aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--river/Seat.zig36
-rw-r--r--river/XwaylandView.zig10
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();