diff options
| -rw-r--r-- | river/Seat.zig | 36 | ||||
| -rw-r--r-- | river/XwaylandView.zig | 10 |
2 files changed, 44 insertions, 2 deletions
diff --git a/river/Seat.zig b/river/Seat.zig index 627e2f9..441ede3 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -80,6 +80,9 @@ focused_output: *Output, focused: FocusTarget = .none, +/// Currently activated Xwayland view (used to handle override redirect menus) +activated_xwayland_view: if (build_options.xwayland) ?*View else void = if (build_options.xwayland) null else {}, + /// Stack of views in most recently focused order /// If there is a currently focused view, it is on top. focus_stack: ViewStack(*View) = .{}, @@ -230,11 +233,36 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { .none => null, }; + if (build_options.xwayland) { + // Keep the parent top-level Xwayland view of any override redirect surface + // activated while that override redirect surface is focused. This ensures + // override redirect menus do not disappear as a result of deactivating + // their parent window. + if (new_focus == .xwayland_override_redirect and + self.focused == .view and + self.focused.view.impl == .xwayland_view and + self.focused.view.impl.xwayland_view.xwayland_surface.pid == new_focus.xwayland_override_redirect.xwayland_surface.pid) + { + self.activated_xwayland_view = self.focused.view; + } else if (self.activated_xwayland_view) |active_view| { + if (!(new_focus == .view and new_focus.view == active_view) and + !(new_focus == .xwayland_override_redirect and new_focus.xwayland_override_redirect.xwayland_surface.pid == active_view.impl.xwayland_view.xwayland_surface.pid)) + { + if (active_view.pending.focus == 0) active_view.setActivated(false); + self.activated_xwayland_view = null; + } + } + } + // First clear the current focus switch (self.focused) { .view => |view| { view.pending.focus -= 1; - if (view.pending.focus == 0) view.setActivated(false); + if (view.pending.focus == 0 and + (!build_options.xwayland or view != self.activated_xwayland_view)) + { + view.setActivated(false); + } }, .xwayland_override_redirect, .layer, .lock_surface, .none => {}, } @@ -244,7 +272,11 @@ 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 and + (!build_options.xwayland or target_view != self.activated_xwayland_view)) + { + target_view.setActivated(true); + } target_view.pending.focus += 1; target_view.pending.urgent = false; }, diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index 0eef7a4..55681e6 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -175,6 +175,16 @@ pub fn getConstraints(self: Self) View.Constraints { /// Called when the xwayland surface is destroyed fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void { const self = @fieldParentPtr(Self, "destroy", listener); + const view = self.view; + + // Ensure no seat will attempt to access this view after it is destroyed. + 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.activated_xwayland_view == view) { + seat.activated_xwayland_view = null; + } + } // Remove listeners that are active for the entire lifetime of the view self.destroy.link.remove(); |
