Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
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,