xwayland-unmanaged: handle focus on map/cursor button

This implements keyboard focus on map for unmanaged Xwayland views
using wlroot's heuristic approach as well as keyboard focus on cursor
button click.
This commit is contained in:
Zakariyya Von Forslun 2022-05-28 22:19:43 +10:00 committed by Isaac Freund
parent 0dd5ad032c
commit 7b554bde68
3 changed files with 33 additions and 7 deletions

View File

@ -315,7 +315,13 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
self.seat.focus(null); 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); _ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);

View File

@ -38,12 +38,14 @@ const Output = @import("Output.zig");
const SeatStatus = @import("SeatStatus.zig"); const SeatStatus = @import("SeatStatus.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const ViewStack = @import("view_stack.zig").ViewStack; const ViewStack = @import("view_stack.zig").ViewStack;
const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig");
const log = std.log.scoped(.seat); const log = std.log.scoped(.seat);
const PointerConstraint = @import("PointerConstraint.zig"); const PointerConstraint = @import("PointerConstraint.zig");
const FocusTarget = union(enum) { const FocusTarget = union(enum) {
view: *View, view: *View,
xwayland_unmanaged: *XwaylandUnmanaged,
layer: *LayerSurface, layer: *LayerSurface,
none: void, none: void,
}; };
@ -206,7 +208,8 @@ fn pendingFilter(view: *View, filter_tags: u32) bool {
} }
/// Switch focus to the target, handling unfocus and input inhibition /// 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 { pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
// If the target is already focused, do nothing // If the target is already focused, do nothing
if (std.meta.eql(new_focus, self.focused)) return; 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 // Obtain the target surface
const target_surface = switch (new_focus) { const target_surface = switch (new_focus) {
.view => |target_view| target_view.surface.?, .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, .layer => |target_layer| target_layer.wlr_layer_surface.surface,
.none => null, .none => null,
}; };
@ -228,7 +235,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
view.pending.focus -= 1; view.pending.focus -= 1;
if (view.pending.focus == 0) view.setActivated(false); if (view.pending.focus == 0) view.setActivated(false);
}, },
.layer, .none => {}, .xwayland_unmanaged, .layer, .none => {},
} }
// Set the new focus // Set the new focus
@ -240,7 +247,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
target_view.pending.urgent = false; target_view.pending.urgent = false;
}, },
.layer => |target_layer| assert(self.focused_output == target_layer.output), .layer => |target_layer| assert(self.focused_output == target_layer.output),
.none => {}, .xwayland_unmanaged, .none => {},
} }
self.focused = new_focus; self.focused = new_focus;

View File

@ -78,19 +78,32 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl
xwayland_surface.surface.?.events.commit.add(&self.commit); xwayland_surface.surface.?.events.commit.add(&self.commit);
// TODO: handle keyboard focus if (self.xwayland_surface.overrideRedirectWantsFocus()) {
// if (wlr_xwayland_or_surface_wants_focus(self.xwayland_surface)) { ... server.input_manager.defaultSeat().setFocusRaw(.{ .xwayland_unmanaged = self });
}
} }
/// Called when the surface is unmapped and will no longer be displayed. /// Called when the surface is unmapped and will no longer be displayed.
fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void { fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
const self = @fieldParentPtr(Self, "unmap", listener); 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); const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
server.root.xwayland_unmanaged_views.remove(node); server.root.xwayland_unmanaged_views.remove(node);
self.commit.link.remove(); 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 { fn handleCommit(_: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {