git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit56a3e8dbcb65b83bd0dd3062b17f354f68b7d1f0
parentc6cdc45bdf
authorIsaac Freund <[email protected]>
date2025-03-23 15:50
Seat: send rwm pointer_enter/leave events

 river/Cursor.zig     | 32 ++++++++++++++++++++++++++++++++
 river/Window.zig     |  6 ++++++
 rivercompat/Seat.zig | 20 +++++++++++++++-----
 3 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/river/Cursor.zig b/river/Cursor.zig
index fec0416..6ede6b0 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -329,6 +329,7 @@ pub fn processMotionRelative(cursor: *Cursor, event: *const wlr.Pointer.event.Mo
 
             switch (cursor.mode) {
                 .passthrough => {
+                    cursor.updateHovered();
                     cursor.passthrough(event.time_msec);
                 },
                 .ignore => {},
@@ -360,6 +361,35 @@ pub fn processMotionRelative(cursor: *Cursor, event: *const wlr.Pointer.event.Mo
     }
 }
 
+fn updateHovered(cursor: *Cursor) void {
+    if (server.scene.at(cursor.wlr_cursor.x, cursor.wlr_cursor.y)) |result| {
+        switch (result.data) {
+            .window => |window| {
+                switch (window.impl) {
+                    .toplevel => |toplevel| {
+                        // Exclude input regions of the toplevel that extend beyond the window
+                        if (result.surface != null and result.surface.?.getRootSurface() == toplevel.wlr_toplevel.base.surface) {
+                            if (window.rendering_sent.box.containsPoint(cursor.wlr_cursor.x, cursor.wlr_cursor.y)) {
+                                cursor.seat.windowing_scheduled.window = window;
+                            }
+                        } else {
+                            cursor.seat.windowing_scheduled.window = window;
+                        }
+                    },
+                    .xwayland => cursor.seat.windowing_scheduled.window = window,
+                    .none => {},
+                }
+            },
+            .shell_surface, .lock_surface => cursor.seat.windowing_scheduled.window = null,
+            .override_redirect => {
+                assert(build_options.xwayland);
+                assert(server.xwayland != null);
+                cursor.seat.windowing_scheduled.window = null;
+            },
+        }
+    }
+}
+
 pub fn processMotionAbsolute(cursor: *Cursor, event: *const wlr.Pointer.event.MotionAbsolute) void {
     var lx: f64 = undefined;
     var ly: f64 = undefined;
@@ -652,6 +682,8 @@ pub fn updateState(cursor: *Cursor) void {
 
     switch (cursor.mode) {
         .passthrough => {
+            cursor.updateHovered();
+
             var now: posix.timespec = undefined;
             posix.clock_gettime(posix.CLOCK.MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
             const msec: u32 = @intCast(now.tv_sec * std.time.ms_per_s +
diff --git a/river/Window.zig b/river/Window.zig
index da57a10..76b9a6d 100644
--- a/river/Window.zig
+++ b/river/Window.zig
@@ -301,6 +301,12 @@ pub fn destroy(window: *Window) void {
             if (seat.focused == .window and seat.focused.window == window) {
                 seat.focus(.none);
             }
+            if (seat.windowing_scheduled.window == window) {
+                seat.windowing_scheduled.window = null;
+            }
+            if (seat.windowing_sent.window == window) {
+                seat.windowing_sent.window = null;
+            }
         }
     }
 
diff --git a/rivercompat/Seat.zig b/rivercompat/Seat.zig
index 8098241..9263f2e 100644
--- a/rivercompat/Seat.zig
+++ b/rivercompat/Seat.zig
@@ -44,6 +44,7 @@ seat_v1: *river.SeatV1,
 pending: State = .{},
 focused: ?*Window = null,
 focused_output: ?*Output = null,
+hovered: ?*Window = null,
 link: wl.list.Link,
 
 pub fn create(seat_v1: *river.SeatV1) void {
@@ -65,8 +66,18 @@ fn handleEvent(seat_v1: *river.SeatV1, event: river.SeatV1.Event, seat: *Seat) v
             seat_v1.destroy();
             gpa.destroy(seat);
         },
-        .pointer_enter => {},
-        .pointer_leave => {},
+        .pointer_enter => |args| {
+            const window_v1 = args.window orelse return;
+            const window: *Window = @ptrCast(@alignCast(window_v1.getUserData()));
+            seat.hovered = window;
+        },
+        .pointer_leave => |args| {
+            const window_v1 = args.window orelse return;
+            const window: *Window = @ptrCast(@alignCast(window_v1.getUserData()));
+            if (seat.hovered == window) {
+                seat.hovered = null;
+            }
+        },
         .pointer_activity => {},
         .window_interaction => |args| {
             const window_v1 = args.window orelse return;
@@ -142,9 +153,8 @@ pub fn execute(seat: *Seat, action: Action) void {
             }
         },
         .resize_start => {
-            seat.seat_v1.opStartPointer();
-            var it = wm.windows.iterator(.forward);
-            while (it.next()) |window| {
+            if (seat.hovered) |window| {
+                seat.seat_v1.opStartPointer();
                 seat.seat_v1.opAddResizeWindow(window.window_v1, .{
                     .top = true,
                     .left = true,