diff --git a/river/Cursor.zig b/river/Cursor.zig index da1b8d6..73f48a4 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -315,7 +315,13 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P self.seat.focus(null); } }, - .xwayland_unmanaged => assert(build_options.xwayland), + .xwayland_unmanaged => |xwayland_unmanaged| { + if (build_options.xwayland) { + self.seat.setFocusRaw(.{ .xwayland_unmanaged = xwayland_unmanaged }); + } else { + unreachable; + } + }, } _ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state); diff --git a/river/Seat.zig b/river/Seat.zig index ebe23bb..baf1056 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -38,12 +38,14 @@ const Output = @import("Output.zig"); const SeatStatus = @import("SeatStatus.zig"); const View = @import("View.zig"); const ViewStack = @import("view_stack.zig").ViewStack; +const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig"); const log = std.log.scoped(.seat); const PointerConstraint = @import("PointerConstraint.zig"); const FocusTarget = union(enum) { view: *View, + xwayland_unmanaged: *XwaylandUnmanaged, layer: *LayerSurface, none: void, }; @@ -206,7 +208,8 @@ fn pendingFilter(view: *View, filter_tags: u32) bool { } /// Switch focus to the target, handling unfocus and input inhibition -/// properly. This should only be called directly if dealing with layers. +/// properly. This should only be called directly if dealing with layers or +/// unmanaged xwayland views. pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { // If the target is already focused, do nothing if (std.meta.eql(new_focus, self.focused)) return; @@ -214,6 +217,10 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { // Obtain the target surface const target_surface = switch (new_focus) { .view => |target_view| target_view.surface.?, + .xwayland_unmanaged => |target_xwayland_unmanaged| blk: { + assert(build_options.xwayland); + break :blk target_xwayland_unmanaged.xwayland_surface.surface; + }, .layer => |target_layer| target_layer.wlr_layer_surface.surface, .none => null, }; @@ -228,7 +235,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { view.pending.focus -= 1; if (view.pending.focus == 0) view.setActivated(false); }, - .layer, .none => {}, + .xwayland_unmanaged, .layer, .none => {}, } // Set the new focus @@ -240,7 +247,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { target_view.pending.urgent = false; }, .layer => |target_layer| assert(self.focused_output == target_layer.output), - .none => {}, + .xwayland_unmanaged, .none => {}, } self.focused = new_focus; diff --git a/river/XwaylandUnmanaged.zig b/river/XwaylandUnmanaged.zig index e19d353..96f6f3f 100644 --- a/river/XwaylandUnmanaged.zig +++ b/river/XwaylandUnmanaged.zig @@ -78,19 +78,32 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl xwayland_surface.surface.?.events.commit.add(&self.commit); - // TODO: handle keyboard focus - // if (wlr_xwayland_or_surface_wants_focus(self.xwayland_surface)) { ... + if (self.xwayland_surface.overrideRedirectWantsFocus()) { + server.input_manager.defaultSeat().setFocusRaw(.{ .xwayland_unmanaged = self }); + } } /// Called when the surface is unmapped and will no longer be displayed. fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void { const self = @fieldParentPtr(Self, "unmap", listener); - // Remove self from the list of unmanged views in the root + // Remove self from the list of unmanaged views in the root const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self); server.root.xwayland_unmanaged_views.remove(node); self.commit.link.remove(); + + // If the unmapped surface is currently focused, reset focus to the most + // appropriate view. + var seat_it = server.input_manager.seats.first; + while (seat_it) |seat_node| : (seat_it = seat_node.next) { + const seat = &seat_node.data; + if (seat.focused == .xwayland_unmanaged and seat.focused.xwayland_unmanaged == self) { + seat.focus(null); + } + } + + server.root.startTransaction(); } fn handleCommit(_: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {