diff --git a/river/Cursor.zig b/river/Cursor.zig index 84cda6a..829afe7 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -337,6 +337,7 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P server.root.applyPending(); } +/// Requires a call to Root.applyPending() fn updateKeyboardFocus(self: Self, result: Root.AtResult) void { switch (result.node) { .view => |view| { @@ -348,8 +349,6 @@ fn updateKeyboardFocus(self: Self, result: Root.AtResult) void { // give it keyboard focus. if (layer_surface.wlr_layer_surface.current.keyboard_interactive != .none) { self.seat.setFocusRaw(.{ .layer = layer_surface }); - } else { - self.seat.focus(null); } }, .lock_surface => |lock_surface| { @@ -364,11 +363,11 @@ fn updateKeyboardFocus(self: Self, result: Root.AtResult) void { } /// Focus the output at the given layout coordinates, if any +/// Requires a call to Root.applyPending() fn updateOutputFocus(self: Self, lx: f64, ly: f64) void { if (server.root.output_layout.outputAt(lx, ly)) |wlr_output| { const output = @intToPtr(*Output, wlr_output.data); self.seat.focusOutput(output); - self.seat.focus(null); } } diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig index b3cdec3..fd294f2 100644 --- a/river/LayerSurface.zig +++ b/river/LayerSurface.zig @@ -134,6 +134,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { } } +/// Requires a call to Root.applyPending() fn handleKeyboardInteractiveExclusive(output: *Output) void { if (server.lock_manager.state != .unlocked) return; @@ -174,7 +175,6 @@ fn handleKeyboardInteractiveExclusive(output: *Output) void { // without keyboard interactivity, stop focusing that layer surface. if (!current_focus.mapped or current_focus.current.keyboard_interactive == .none) { seat.setFocusRaw(.{ .none = {} }); - seat.focus(null); } } } diff --git a/river/LockManager.zig b/river/LockManager.zig index 9757d93..e5467ac 100644 --- a/river/LockManager.zig +++ b/river/LockManager.zig @@ -223,8 +223,6 @@ fn handleUnlock(listener: *wl.Listener(void)) void { while (it) |node| : (it = node.next) { const seat = &node.data; seat.setFocusRaw(.none); - seat.focus(null); - seat.cursor.updateState(); // Exit locked mode seat.enterMode(seat.prev_mode_id); @@ -232,6 +230,8 @@ fn handleUnlock(listener: *wl.Listener(void)) void { } handleDestroy(&manager.destroy); + + server.root.applyPending(); } fn handleDestroy(listener: *wl.Listener(void)) void { diff --git a/river/Output.zig b/river/Output.zig index adbded4..95dde75 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -293,6 +293,7 @@ pub fn layerSurfaceTree(self: Self, layer: zwlr.LayerShellV1.Layer) *wlr.SceneTr /// Arrange all layer surfaces of this output and adjust the usable area. /// Will arrange views as well if the usable area changes. +/// Requires a call to Root.applyPending() pub fn arrangeLayers(self: *Self) void { var full_box: wlr.Box = .{ .x = 0, diff --git a/river/Root.zig b/river/Root.zig index 4f08d38..7603705 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -294,7 +294,6 @@ pub fn removeOutput(root: *Self, output: *Output) void { const seat = &seat_node.data; if (seat.focused_output == output) { seat.focusOutput(fallback_output); - seat.focus(null); } } @@ -340,7 +339,6 @@ pub fn addOutput(root: *Self, output: *Output) void { while (it) |seat_node| : (it = seat_node.next) { const seat = &seat_node.data; seat.focusOutput(output); - seat.focus(null); } } root.applyPending(); @@ -352,6 +350,15 @@ pub fn addOutput(root: *Self, output: *Output) void { /// generates a new layout for all outputs and all affected clients ack a /// configure and commit a new buffer. pub fn applyPending(root: *Self) void { + { + // Changes to the pending state may require a focus update to keep + // state consistent. Instead of having focus(null) calls spread all + // around the codebase and risk forgetting one, always ensure focus + // state is synchronized here. + var it = server.input_manager.seats.first; + while (it) |node| : (it = node.next) node.data.focus(null); + } + // If there is already a transaction inflight, wait until it completes. if (root.inflight_layout_demands > 0 or root.inflight_configures > 0) { root.pending_state_dirty = true; diff --git a/river/Seat.zig b/river/Seat.zig index d3c22d9..802a5ce 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -138,6 +138,7 @@ pub fn deinit(self: *Self) void { /// Set the current focus. If a visible view is passed it will be focused. /// If null is passed, the top view in the stack of the focused output will be focused. +/// Requires a call to Root.applyPending() pub fn focus(self: *Self, _target: ?*View) void { var target = _target; diff --git a/river/View.zig b/river/View.zig index 399dcdd..45d8906 100644 --- a/river/View.zig +++ b/river/View.zig @@ -478,16 +478,6 @@ pub fn unmap(view: *Self) void { server.root.hidden.pending.wm_stack.prepend(view); } - { - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - const seat = &node.data; - if (seat.focused == .view and seat.focused.view == view) { - seat.focus(null); - } - } - } - view.request_activate.link.remove(); server.root.applyPending(); diff --git a/river/XwaylandOverrideRedirect.zig b/river/XwaylandOverrideRedirect.zig index e87ce77..cb0686a 100644 --- a/river/XwaylandOverrideRedirect.zig +++ b/river/XwaylandOverrideRedirect.zig @@ -138,15 +138,11 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSur var seat_it = server.input_manager.seats.first; while (seat_it) |seat_node| : (seat_it = seat_node.next) { const seat = &seat_node.data; - switch (seat.focused) { - .view => |focused| if (focused.impl == .xwayland_view and - focused.impl.xwayland_view.xwayland_surface.pid == self.xwayland_surface.pid and - seat.wlr_seat.keyboard_state.focused_surface == self.xwayland_surface.surface) - { - seat.keyboardEnterOrLeave(focused.rootSurface()); - }, - .xwayland_override_redirect => |focused| if (focused == self) seat.focus(null), - .layer, .lock_surface, .none => {}, + if (seat.focused == .view and seat.focused.view.impl == .xwayland_view and + seat.focused.view.impl.xwayland_view.xwayland_surface.pid == self.xwayland_surface.pid and + seat.wlr_seat.keyboard_state.focused_surface == self.xwayland_surface.surface) + { + seat.keyboardEnterOrLeave(seat.focused.view.rootSurface()); } } diff --git a/river/command/output.zig b/river/command/output.zig index dc72323..64d021e 100644 --- a/river/command/output.zig +++ b/river/command/output.zig @@ -43,7 +43,6 @@ pub fn focusOutput( } seat.focusOutput((try getOutput(seat, args[1])) orelse return); - seat.focus(null); server.root.applyPending(); } @@ -68,8 +67,6 @@ pub fn sendToOutput( if (seat.focused.view.pending.output == destination_output) return; seat.focused.view.setPendingOutput(destination_output); - // Handle the change and focus whatever's next in the focus stack - seat.focus(null); server.root.applyPending(); } } diff --git a/river/command/tags.zig b/river/command/tags.zig index 3eac000..d1ac8da 100644 --- a/river/command/tags.zig +++ b/river/command/tags.zig @@ -34,7 +34,6 @@ pub fn setFocusedTags( if (output.pending.tags != tags) { output.previous_tags = output.pending.tags; output.pending.tags = tags; - seat.focus(null); server.root.applyPending(); } } @@ -58,7 +57,6 @@ pub fn setViewTags( if (seat.focused == .view) { const view = seat.focused.view; view.pending.tags = tags; - seat.focus(null); server.root.applyPending(); } } @@ -75,7 +73,6 @@ pub fn toggleFocusedTags( if (new_focused_tags != 0) { output.previous_tags = output.pending.tags; output.pending.tags = new_focused_tags; - seat.focus(null); server.root.applyPending(); } } @@ -92,7 +89,6 @@ pub fn toggleViewTags( if (new_tags != 0) { const view = seat.focused.view; view.pending.tags = new_tags; - seat.focus(null); server.root.applyPending(); } } @@ -110,7 +106,6 @@ pub fn focusPreviousTags( if (output.pending.tags != previous_tags) { output.previous_tags = output.pending.tags; output.pending.tags = previous_tags; - seat.focus(null); server.root.applyPending(); } } @@ -127,7 +122,6 @@ pub fn sendToPreviousTags( if (seat.focused == .view) { const view = seat.focused.view; view.pending.tags = output.previous_tags; - seat.focus(null); server.root.applyPending(); } } diff --git a/river/command/toggle_fullscreen.zig b/river/command/toggle_fullscreen.zig index 402cce3..5dfef41 100644 --- a/river/command/toggle_fullscreen.zig +++ b/river/command/toggle_fullscreen.zig @@ -33,9 +33,6 @@ pub fn toggleFullscreen( const view = seat.focused.view; view.pending.fullscreen = !view.pending.fullscreen; - // It is possible to end up with multiple fullscreen views in which - // case making one non-fullscreen should switch focus to the next. - seat.focus(null); server.root.applyPending(); } }