Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
protocol: add river_seat_v1.pointer_warp
protocol/river-window-management-v1.xml | 15 +++++++++++++++
river/Cursor.zig | 1 -
river/Seat.zig | 14 +++++++++++---
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
index d005671..f433a48 100644
--- a/protocol/river-window-management-v1.xml
+++ b/protocol/river-window-management-v1.xml
@@ -1594,6 +1594,21 @@
<arg name="x" type="int"/>
<arg name="y" type="int"/>
</event>
+
+ <request name="pointer_warp" since="3">
+ <description summary="warp the pointer to a given position">
+ Warp the pointer to the given position in the compositor's logical
+ coordinate space.
+
+ If the given position is outside the bounds of all outputs, the pointer
+ will be warped to the closest point inside an output instead.
+
+ This request modifies window management state and may only be made as
+ part of a manage sequence, see the river_window_manager_v1 description.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ </request>
</interface>
<interface name="river_pointer_binding_v1" version="3">
diff --git a/river/Cursor.zig b/river/Cursor.zig
index b3679da..e9e91ae 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -705,7 +705,6 @@ pub fn updateState(cursor: *Cursor) void {
@divTrunc(now.nsec, std.time.ns_per_ms));
cursor.passthrough(msec);
},
- // TODO: Leave down mode if the target surface is no longer visible.
.ignore, .down, .op => {},
}
}
diff --git a/river/Seat.zig b/river/Seat.zig
index dab563d..27ad75d 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -134,6 +134,7 @@ wm_requested: struct {
start_pointer,
end,
} = .none,
+ pointer_warp: ?struct { x: i32, y: i32 } = null,
} = .{},
xkb_bindings: wl.list.Head(XkbBinding, .link),
@@ -484,7 +485,6 @@ fn handleRequest(
.destroy => {
seat_v1.destroy();
},
-
.focus_window => |args| {
if (!server.wm.ensureWindowing()) return;
const data = args.window.getUserData() orelse return;
@@ -507,7 +507,6 @@ fn handleRequest(
if (!server.wm.ensureWindowing()) return;
seat.wm_requested.op = .end;
},
-
.get_pointer_binding => |args| {
PointerBinding.create(
seat,
@@ -522,7 +521,6 @@ fn handleRequest(
return;
};
},
-
.set_xcursor_theme => |args| {
seat.cursor.setTheme(args.name, args.size) catch |err| switch (err) {
error.OutOfMemory => {
@@ -532,6 +530,10 @@ fn handleRequest(
},
};
},
+ .pointer_warp => |args| {
+ if (!server.wm.ensureWindowing()) return;
+ seat.wm_requested.pointer_warp = .{ .x = args.x, .y = args.y };
+ },
}
}
@@ -581,6 +583,12 @@ pub fn manageFinish(seat: *Seat) void {
.end => seat.opEnd(),
}
seat.wm_requested.op = .none;
+
+ if (seat.wm_requested.pointer_warp) |target| {
+ seat.wm_requested.pointer_warp = null;
+ seat.cursor.wlr_cursor.warpClosest(null, @floatFromInt(target.x), @floatFromInt(target.y));
+ seat.cursor.updateState();
+ }
}
pub fn focus(seat: *Seat, new_focus: Focus) void {