Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
layer-shell: send focus_none more consistently
Currently river does not send river_layer_shell_seat_v1.focus_none
when a layer surface that currently has non-exclusive focus is closed or
otherwise looses focus (e.g. because the window manager requested a
window to be focused or cleared focus).
This status quo behavior is undesirable since:
1. It does not comply to the letter of the protocol specification
2. It requires window managers to do more work in practice
With this patch, the focus_none event is sent in the next manage
sequence whenever there is a state transition from a layer surface
having focus to no layer surface having focus.
protocol/river-layer-shell-v1.xml | 4 ++++
river/LayerShell.zig | 2 +-
river/LayerShellSeat.zig | 3 ---
river/LayerSurface.zig | 11 +++++++---
river/Seat.zig | 43 +++++++++++++++++++++++----------------
5 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/protocol/river-layer-shell-v1.xml b/protocol/river-layer-shell-v1.xml
index 7e45cb0..0167e9d 100644
--- a/protocol/river-layer-shell-v1.xml
+++ b/protocol/river-layer-shell-v1.xml
@@ -168,6 +168,10 @@
focus during the same manage sequence in which this event is sent, the
layer surface will not be focused.
+ If the layer surface with non-exclusive focus is closed or the window
+ manager chooses to move focus away from the layer surface, a focus_none
+ event will be sent in the next manage sequence.
+
This event will be followed by a manage_start event after all other new
state has been sent by the server.
</description>
diff --git a/river/LayerShell.zig b/river/LayerShell.zig
index 681bc04..6578f1f 100644
--- a/river/LayerShell.zig
+++ b/river/LayerShell.zig
@@ -159,7 +159,7 @@ fn handleNewSurface(_: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wl
};
}
-pub fn updateFocus(_: *LayerShell) void {
+pub fn checkExclusiveFocus(_: *LayerShell) void {
// Find the topmost layer surface (if any) in the top or overlay layers which
// requests exclusive keyboard interactivity.
const to_focus = blk: {
diff --git a/river/LayerShellSeat.zig b/river/LayerShellSeat.zig
index 9c64fb7..767f759 100644
--- a/river/LayerShellSeat.zig
+++ b/river/LayerShellSeat.zig
@@ -90,7 +90,4 @@ pub fn manageStart(shell_seat: *LayerShellSeat) void {
}
}
shell_seat.sent.focus = shell_seat.scheduled.focus;
- if (shell_seat.scheduled.focus == .non_exclusive) {
- shell_seat.scheduled.focus = .none;
- }
}
diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig
index c8af056..e8557f5 100644
--- a/river/LayerSurface.zig
+++ b/river/LayerSurface.zig
@@ -113,7 +113,7 @@ fn handleMap(listener: *wl.Listener(void)) void {
// Beware: it is possible for arrange() to destroy this LayerSurface!
const output: *Output = @ptrCast(@alignCast(layer_surface.wlr_layer_surface.output.?.data));
output.layer_shell.arrange();
- server.layer_shell.updateFocus();
+ server.layer_shell.checkExclusiveFocus();
server.wm.dirtyWindowing();
}
@@ -128,13 +128,18 @@ fn handleUnmap(listener: *wl.Listener(void)) void {
if (seat.focused == .layer_surface and seat.focused.layer_surface == layer_surface) {
seat.focus(.none);
}
+ if (seat.layer_shell.scheduled.focus == .non_exclusive and
+ seat.layer_shell.scheduled.focus.non_exclusive == layer_surface.ref)
+ {
+ seat.layer_shell.scheduled.focus = .none;
+ }
}
}
// Beware: it is possible for arrange() to destroy this LayerSurface!
const output: *Output = @ptrCast(@alignCast(layer_surface.wlr_layer_surface.output.?.data));
output.layer_shell.arrange();
- server.layer_shell.updateFocus();
+ server.layer_shell.checkExclusiveFocus();
server.wm.dirtyWindowing();
}
@@ -156,7 +161,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
// Beware: it is possible for arrange() to destroy this LayerSurface!
const output: *Output = @ptrCast(@alignCast(layer_surface.wlr_layer_surface.output.?.data));
output.layer_shell.arrange();
- server.layer_shell.updateFocus();
+ server.layer_shell.checkExclusiveFocus();
server.wm.dirtyWindowing();
}
}
diff --git a/river/Seat.zig b/river/Seat.zig
index 5a0f12a..4077e9a 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -558,24 +558,31 @@ pub fn manageFinish(seat: *Seat) void {
.exclusive => |ref| if (ref.get()) |layer_surface| {
seat.focus(.{ .layer_surface = layer_surface });
},
- .non_exclusive, .none => switch (seat.wm_requested.focus) {
- .none => switch (seat.layer_shell.sent.focus) {
- .exclusive => unreachable,
- .non_exclusive => |ref| if (ref.get()) |layer_surface| {
- seat.focus(.{ .layer_surface = layer_surface });
- seat.layer_shell.sent.focus = .none;
- },
- .none => {},
- },
- .clear => {
- seat.focus(.none);
- },
- .window => |ref| if (ref.get()) |window| {
- seat.focus(.{ .window = window });
- },
- .shell_surface => |shell_surface| {
- seat.focus(.{ .shell_surface = shell_surface });
- },
+ .non_exclusive, .none => {
+ if (seat.wm_requested.focus == .none) {
+ switch (seat.layer_shell.sent.focus) {
+ .exclusive => unreachable,
+ .non_exclusive => |ref| if (ref.get()) |layer_surface| {
+ seat.focus(.{ .layer_surface = layer_surface });
+ },
+ .none => {},
+ }
+ } else {
+ switch (seat.wm_requested.focus) {
+ .none => unreachable,
+ .clear => seat.focus(.none),
+ .window => |ref| if (ref.get()) |window| seat.focus(.{ .window = window }),
+ .shell_surface => |shell_surface| seat.focus(.{ .shell_surface = shell_surface }),
+ }
+ switch (seat.layer_shell.sent.focus) {
+ .exclusive => unreachable,
+ .non_exclusive => {
+ seat.layer_shell.scheduled.focus = .none;
+ server.wm.dirtyWindowing();
+ },
+ .none => {},
+ }
+ }
},
}
seat.wm_requested.focus = .none;