diff --git a/river/Seat.zig b/river/Seat.zig index 627e2f9..f1d8aa4 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -234,9 +234,28 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { switch (self.focused) { .view => |view| { view.pending.focus -= 1; - if (view.pending.focus == 0) view.setActivated(false); + + if (view.pending.focus == 0) { + if (build_options.xwayland) { + if (new_focus != .xwayland_override_redirect or + new_focus.xwayland_override_redirect.parent_view != view) + { + view.setActivated(false); + } + } else view.setActivated(false); + } + }, + .xwayland_override_redirect => |override_redirect| { + if (override_redirect.parent_view) |pv| { + if (!(new_focus == .view and new_focus.view == pv) and + !(new_focus == .xwayland_override_redirect and new_focus.xwayland_override_redirect.parent_view == pv) and + pv.pending.focus == 0) + { + pv.setActivated(false); + } + } }, - .xwayland_override_redirect, .layer, .lock_surface, .none => {}, + .layer, .lock_surface, .none => {}, } // Set the new focus @@ -244,7 +263,17 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { .view => |target_view| { assert(server.lock_manager.state == .unlocked); assert(self.focused_output == target_view.output); - if (target_view.pending.focus == 0) target_view.setActivated(true); + + if (target_view.pending.focus == 0) { + if (build_options.xwayland) { + if (self.focused != .xwayland_override_redirect or + self.focused.xwayland_override_redirect.parent_view != target_view) + { + target_view.setActivated(true); + } + } else target_view.setActivated(true); + } + target_view.pending.focus += 1; target_view.pending.urgent = false; }, diff --git a/river/XwaylandOverrideRedirect.zig b/river/XwaylandOverrideRedirect.zig index 6b61100..424b50a 100644 --- a/river/XwaylandOverrideRedirect.zig +++ b/river/XwaylandOverrideRedirect.zig @@ -34,6 +34,9 @@ const log = std.log.scoped(.xwayland); /// The corresponding wlroots object xwayland_surface: *wlr.XwaylandSurface, +// Keep track of the parent top-level Xwayland view previously focused +parent_view: ?*View = null, + // Listeners that are always active over the view's lifetime request_configure: wl.Listener(*wlr.XwaylandSurface.event.Configure) = wl.Listener(*wlr.XwaylandSurface.event.Configure).init(handleRequestConfigure), @@ -100,7 +103,23 @@ pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: if (self.xwayland_surface.overrideRedirectWantsFocus() and self.xwayland_surface.icccmInputModel() != .none) { - server.input_manager.defaultSeat().setFocusRaw(.{ .xwayland_override_redirect = self }); + const seat = server.input_manager.defaultSeat(); + + // Get top-level Xwayland view so that the seat can keep it activated, in + // case this is a menu that has not been filtered out by the heuristic + // or ICCCM input model above. + 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) + { + self.parent_view = seat.focused.view; + } else if (seat.focused == .xwayland_override_redirect and + seat.focused.xwayland_override_redirect.xwayland_surface.pid == self.xwayland_surface.pid) + { + self.parent_view = seat.focused.xwayland_override_redirect.parent_view; + } + + seat.setFocusRaw(.{ .xwayland_override_redirect = self }); } }