add multimonitor

This commit is contained in:
2026-08-18 21:30:51 +02:00
parent 7d117f4ae5
commit f60dd06e7e
10 changed files with 470 additions and 34 deletions
+47 -2
View File
@@ -76,6 +76,10 @@ pointer_y: i32 = 0,
/// The window the pointer is currently inside.
hovered: ?*Window = null,
/// A screen to warp the pointer onto in the next manage sequence. Set when
/// focus moves to an output with no window on it to warp to.
pending_warp_output: ?*Output = null,
op: ?Op = null,
/// An operation to start in the next manage sequence.
pending_op: ?Op = null,
@@ -165,12 +169,19 @@ fn createBindings(self: *Seat) !void {
std.log.info("registered {d}/{d} pointer bindings", .{ self.buttons.items.len, config.buttons.len });
}
/// The output this seat is working on: the one holding the focused window,
/// else the one under the pointer.
/// The output this seat is working on: the one holding the focused window, else
/// the one focus was last moved to, else the one under the pointer.
///
/// That middle case is what makes an empty screen a place the user can be.
/// Moving to a screen with nothing on it clears the focused window, and with
/// only the pointer to fall back on the seat would go on reporting the screen it
/// came from — so the tag keys, the layout keys and the next window spawned
/// would all land back on the monitor just left.
pub fn currentOutput(self: *Seat) ?*Output {
if (self.focused) |win| {
if (win.output) |out| return out;
}
if (self.wm.focused_output) |out| return out;
return self.wm.outputAt(self.pointer_x, self.pointer_y) orelse self.wm.firstOutput();
}
@@ -256,6 +267,11 @@ pub fn applyManage(self: *Seat) void {
self.pending_clear_focus = false;
self.wm.ipcDirty();
}
if (self.pending_warp_output) |out| {
self.warpToOutput(out);
self.pending_warp_output = null;
}
}
/// Pull the pointer to the middle of a newly focused window.
@@ -274,6 +290,25 @@ fn warpTo(self: *Seat, win: *Window) void {
);
}
/// Pull the pointer onto a screen the keyboard has just moved to, when there is
/// no window there to warp to instead.
///
/// Without it, moving to an empty screen leaves the cursor on the one before —
/// and with `focus_follows_mouse` on, the first window the pointer then brushes
/// past takes the focus straight back. Manage sequence only.
fn warpToOutput(self: *Seat, out: *Output) void {
if (!config.warp_cursor) return;
if (self.op != null) return;
const area = out.layoutArea();
if (area.width <= 0 or area.height <= 0) return;
if (area.contains(self.pointer_x, self.pointer_y)) return;
self.seat.pointerWarp(
area.x + @divTrunc(area.width, 2),
area.y + @divTrunc(area.height, 2),
);
}
fn onEvent(_: *river.SeatV1, event: river.SeatV1.Event, self: *Seat) void {
switch (event) {
.removed => {
@@ -289,6 +324,16 @@ fn onEvent(_: *river.SeatV1, event: river.SeatV1.Event, self: *Seat) void {
.pointer_position => |ev| {
self.pointer_x = ev.x;
self.pointer_y = ev.y;
// Sloppy focus crosses screens too, as dwm's motion handler does:
// the pointer leaving a monitor is what moves the seat to the next
// one, so the tag and layout keys follow the cursor even over a
// screen with no window on it to focus.
if (config.focus_follows_mouse and self.op == null) {
if (self.wm.outputAt(ev.x, ev.y)) |out| {
if (self.wm.focused_output != out) self.wm.enterOutput(self, out);
}
}
},
.pointer_enter => |ev| {