Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
rivercompat: implement new rwm update sequence
The client side ergonomics of the update sequence changes seem like they
will be fine in practice. Most of the refactoring needed would have been
necessary anyways to make rivercompat actually useful.
This commit compiles but is untested as the new update sequence has not
yet been implemented in river itself.
rivercompat/Seat.zig | 94 +++++++++++++++++++++++++++----------------
rivercompat/ShellSurface.zig | 45 +++++++++++++--------
rivercompat/Window.zig | 87 ++++++++++++++++++++++-----------------
rivercompat/WindowManager.zig | 38 +++++++++++++----
rivercompat/XkbBinding.zig | 5 ++-
5 files changed, 171 insertions(+), 98 deletions(-)
diff --git a/rivercompat/Seat.zig b/rivercompat/Seat.zig
index 5583d31..388300a 100644
--- a/rivercompat/Seat.zig
+++ b/rivercompat/Seat.zig
@@ -32,8 +32,16 @@ const PointerBinding = @import("PointerBinding.zig");
const gpa = std.heap.c_allocator;
+const State = struct {
+ new: bool = false,
+ action: ?Action = null,
+ window_interaction: ?*Window = null,
+ shell_surface_interaction: ?*river.ShellSurfaceV1 = null,
+};
+
wm: *WindowManager,
seat_v1: *river.SeatV1,
+pending: State = .{},
focused: ?*Window = null,
link: wl.list.Link,
@@ -42,43 +50,12 @@ pub fn create(wm: *WindowManager, seat_v1: *river.SeatV1) void {
seat.* = .{
.wm = wm,
.seat_v1 = seat_v1,
+ .pending = .{ .new = true },
.link = undefined,
};
wm.seats.append(seat);
seat_v1.setListener(*Seat, handleEvent, seat);
-
- XkbBinding.create(seat, xkb.Keysym.n, .{ .mod4 = true }, .focus_next);
- XkbBinding.create(seat, xkb.Keysym.h, .{ .mod4 = true }, .hide_focused);
- XkbBinding.create(seat, xkb.Keysym.k, .{ .mod4 = true }, .close_focused);
- XkbBinding.create(seat, xkb.Keysym.s, .{ .mod4 = true }, .show_all);
- PointerBinding.create(seat, c.BTN_LEFT, .{ .mod4 = true }, .move_start, .op_end);
- PointerBinding.create(seat, c.BTN_RIGHT, .{ .mod4 = true }, .resize_start, .op_end);
- PointerBinding.create(seat, c.BTN_MIDDLE, .{ .mod4 = true }, .close_focused, null);
-}
-
-pub fn focus(seat: *Seat, target: ?*Window) void {
- if (target) |window| {
- seat.seat_v1.focusWindow(window.window_v1);
- seat.focused = window;
-
- window.link.remove();
- seat.wm.windows.prepend(window);
-
- window.node_v1.placeTop();
- } else {
- seat.seat_v1.clearFocus();
- }
-}
-
-pub fn focusNext(seat: *Seat) void {
- if (seat.focused != null) {
- if (seat.wm.windows.length() >= 2) {
- seat.focus(seat.wm.windows.last().?);
- }
- } else {
- seat.focus(seat.wm.windows.first());
- }
}
fn handleEvent(seat_v1: *river.SeatV1, event: river.SeatV1.Event, seat: *Seat) void {
@@ -94,15 +71,38 @@ fn handleEvent(seat_v1: *river.SeatV1, event: river.SeatV1.Event, seat: *Seat) v
.window_interaction => |args| {
const window_v1 = args.window orelse return;
const window: *Window = @ptrCast(@alignCast(window_v1.getUserData()));
- seat.focus(window);
+ assert(seat.pending.window_interaction == null);
+ seat.pending.window_interaction = window;
},
.shell_surface_interaction => |args| {
- const shell_surface_v1 = args.shell_surface orelse return;
- seat_v1.focusShellSurface(shell_surface_v1);
+ assert(seat.pending.shell_surface_interaction == null);
+ seat.pending.shell_surface_interaction = args.shell_surface orelse return;
},
}
}
+pub fn updateWindowing(seat: *Seat) void {
+ if (seat.pending.new) {
+ XkbBinding.create(seat, xkb.Keysym.n, .{ .mod4 = true }, .focus_next);
+ XkbBinding.create(seat, xkb.Keysym.h, .{ .mod4 = true }, .hide_focused);
+ XkbBinding.create(seat, xkb.Keysym.k, .{ .mod4 = true }, .close_focused);
+ XkbBinding.create(seat, xkb.Keysym.s, .{ .mod4 = true }, .show_all);
+ PointerBinding.create(seat, c.BTN_LEFT, .{ .mod4 = true }, .move_start, .op_end);
+ PointerBinding.create(seat, c.BTN_RIGHT, .{ .mod4 = true }, .resize_start, .op_end);
+ PointerBinding.create(seat, c.BTN_MIDDLE, .{ .mod4 = true }, .close_focused, null);
+ }
+ if (seat.pending.window_interaction) |window| {
+ seat.focus(window);
+ }
+ if (seat.pending.shell_surface_interaction) |shell_surface| {
+ seat.seat_v1.focusShellSurface(shell_surface);
+ }
+ if (seat.pending.action) |action| {
+ seat.execute(action);
+ }
+ seat.pending = .{};
+}
+
pub const Action = enum {
focus_next,
close_focused,
@@ -144,3 +144,27 @@ pub fn execute(seat: *Seat, action: Action) void {
.op_end => seat.seat_v1.opEnd(),
}
}
+
+pub fn focus(seat: *Seat, target: ?*Window) void {
+ if (target) |window| {
+ seat.seat_v1.focusWindow(window.window_v1);
+ seat.focused = window;
+
+ window.link.remove();
+ seat.wm.windows.prepend(window);
+
+ window.node_v1.placeTop();
+ } else {
+ seat.seat_v1.clearFocus();
+ }
+}
+
+pub fn focusNext(seat: *Seat) void {
+ if (seat.focused != null) {
+ if (seat.wm.windows.length() >= 2) {
+ seat.focus(seat.wm.windows.last().?);
+ }
+ } else {
+ seat.focus(seat.wm.windows.first());
+ }
+}
diff --git a/rivercompat/ShellSurface.zig b/rivercompat/ShellSurface.zig
index 2e8b425..605e3c7 100644
--- a/rivercompat/ShellSurface.zig
+++ b/rivercompat/ShellSurface.zig
@@ -28,11 +28,16 @@ const WindowManager = @import("WindowManager.zig");
const gpa = std.heap.c_allocator;
-wm: *WindowManager,
+const State = struct {
+ new: bool = false,
+};
+
surface: *wl.Surface,
viewport: *wp.Viewport,
shell_surface_v1: *river.ShellSurfaceV1,
node: *river.NodeV1,
+pending: State,
+link: wl.list.Link,
pub fn create(wm: *WindowManager) void {
const shell_surface = gpa.create(ShellSurface) catch @panic("OOM");
@@ -41,30 +46,36 @@ pub fn create(wm: *WindowManager) void {
const shell_surface_v1 = wm.wm_v1.getShellSurface(surface) catch @panic("OOM");
shell_surface.* = .{
- .wm = wm,
.surface = surface,
.viewport = viewport,
.shell_surface_v1 = shell_surface_v1,
.node = shell_surface_v1.getNode() catch @panic("OOM"),
+ .pending = .{ .new = true },
+ .link = undefined,
};
+ wm.shell_surfaces.append(shell_surface);
+}
- shell_surface.node.placeBottom();
- shell_surface.node.setPosition(0, 0);
- shell_surface_v1.syncNextCommit();
+pub fn updateWindowing(shell_surface: *ShellSurface, wm: *WindowManager) void {
+ if (shell_surface.pending.new) {
+ shell_surface.node.placeBottom();
+ shell_surface.node.setPosition(0, 0);
+ shell_surface.shell_surface_v1.syncNextCommit();
- const rgb = 0xfdf6e3;
+ const rgb = 0xfdf6e3;
- const buffer = wm.single_pixel.createU32RgbaBuffer(
- @as(u32, (rgb >> 16) & 0xff) * (0xffff_ffff / 0xff),
- @as(u32, (rgb >> 8) & 0xff) * (0xffff_ffff / 0xff),
- @as(u32, (rgb >> 0) & 0xff) * (0xffff_ffff / 0xff),
- 0xffff_ffff,
- ) catch @panic("OOM");
- defer buffer.destroy();
+ const buffer = wm.single_pixel.createU32RgbaBuffer(
+ @as(u32, (rgb >> 16) & 0xff) * (0xffff_ffff / 0xff),
+ @as(u32, (rgb >> 8) & 0xff) * (0xffff_ffff / 0xff),
+ @as(u32, (rgb >> 0) & 0xff) * (0xffff_ffff / 0xff),
+ 0xffff_ffff,
+ ) catch @panic("OOM");
+ defer buffer.destroy();
- surface.attach(buffer, 0, 0);
+ shell_surface.surface.attach(buffer, 0, 0);
- surface.damageBuffer(0, 0, math.maxInt(i32), math.maxInt(i32));
- viewport.setDestination(math.maxInt(i32) / 2, math.maxInt(i32) / 2);
- surface.commit();
+ shell_surface.surface.damageBuffer(0, 0, math.maxInt(i32), math.maxInt(i32));
+ shell_surface.viewport.setDestination(math.maxInt(i32) / 2, math.maxInt(i32) / 2);
+ shell_surface.surface.commit();
+ }
}
diff --git a/rivercompat/Window.zig b/rivercompat/Window.zig
index 99f51b7..d25f8e2 100644
--- a/rivercompat/Window.zig
+++ b/rivercompat/Window.zig
@@ -26,61 +26,32 @@ const WindowManager = @import("WindowManager.zig");
const gpa = std.heap.c_allocator;
-wm: *WindowManager,
+const State = struct {
+ new: bool = false,
+ closed: bool = false,
+};
+
window_v1: *river.WindowV1,
node_v1: *river.NodeV1,
+pending: State,
link: wl.list.Link,
pub fn create(window_v1: *river.WindowV1, wm: *WindowManager) void {
const window = gpa.create(Window) catch @panic("OOM");
window.* = .{
- .wm = wm,
.window_v1 = window_v1,
.node_v1 = window_v1.getNode() catch @panic("OOM"),
+ .pending = .{ .new = true },
.link = undefined,
};
wm.windows.append(window);
window_v1.setListener(*Window, handleEvent, window);
- window.node_v1.placeTop();
- window_v1.useSsd();
-
- const rgb = 0x586e75;
- window_v1.setBorders(
- .{ .left = true, .bottom = true, .top = false, .right = true },
- 6, // width
- @as(u32, (rgb >> 16) & 0xff) * (0xffff_ffff / 0xff),
- @as(u32, (rgb >> 8) & 0xff) * (0xffff_ffff / 0xff),
- @as(u32, (rgb >> 0) & 0xff) * (0xffff_ffff / 0xff),
- 0xffff_ffff,
- );
-
- {
- var it = wm.seats.iterator(.forward);
- while (it.next()) |seat| {
- seat.focus(window);
- }
- }
}
fn handleEvent(window_v1: *river.WindowV1, event: river.WindowV1.Event, window: *Window) void {
assert(window.window_v1 == window_v1);
switch (event) {
- .closed => {
- window_v1.destroy();
-
- window.link.remove();
- {
- var it = window.wm.seats.iterator(.forward);
- while (it.next()) |seat| {
- if (seat.focused == window) {
- seat.focused = null;
- seat.focusNext();
- }
- }
- }
-
- gpa.destroy(window);
- },
+ .closed => window.pending.closed = true,
.dimensions_hint => {},
.dimensions => {},
.app_id => {},
@@ -97,3 +68,45 @@ fn handleEvent(window_v1: *river.WindowV1, event: river.WindowV1.Event, window:
.minimize_requested => {},
}
}
+
+pub fn updateWindowing(window: *Window, wm: *WindowManager) void {
+ if (window.pending.closed) {
+ window.window_v1.destroy();
+ window.link.remove();
+ {
+ var it = wm.seats.iterator(.forward);
+ while (it.next()) |seat| {
+ if (seat.focused == window) {
+ seat.focused = null;
+ seat.focusNext();
+ }
+ }
+ }
+ gpa.destroy(window);
+ return;
+ }
+
+ if (window.pending.new) {
+ window.node_v1.placeTop();
+ window.window_v1.useSsd();
+
+ const rgb = 0x586e75;
+ window.window_v1.setBorders(
+ .{ .left = true, .bottom = true, .top = false, .right = true },
+ 6, // width
+ @as(u32, (rgb >> 16) & 0xff) * (0xffff_ffff / 0xff),
+ @as(u32, (rgb >> 8) & 0xff) * (0xffff_ffff / 0xff),
+ @as(u32, (rgb >> 0) & 0xff) * (0xffff_ffff / 0xff),
+ 0xffff_ffff,
+ );
+
+ {
+ var it = wm.seats.iterator(.forward);
+ while (it.next()) |seat| {
+ seat.focus(window);
+ }
+ }
+ }
+
+ window.pending = .{};
+}
diff --git a/rivercompat/WindowManager.zig b/rivercompat/WindowManager.zig
index 6ef5727..83fac77 100644
--- a/rivercompat/WindowManager.zig
+++ b/rivercompat/WindowManager.zig
@@ -35,6 +35,7 @@ single_pixel: *wp.SinglePixelBufferManagerV1,
windows: wl.list.Head(Window, .link),
seats: wl.list.Head(Seat, .link),
+shell_surfaces: wl.list.Head(ShellSurface, .link),
pub fn init(
wm: *WindowManager,
@@ -50,9 +51,11 @@ pub fn init(
.single_pixel = single_pixel,
.windows = undefined,
.seats = undefined,
+ .shell_surfaces = undefined,
};
wm.windows.init();
wm.seats.init();
+ wm.shell_surfaces.init();
wm_v1.setListener(*WindowManager, handleEvent, wm);
@@ -64,16 +67,17 @@ fn handleEvent(wm_v1: *river.WindowManagerV1, event: river.WindowManagerV1.Event
switch (event) {
.unavailable => main.fatal("another window manager is already running", .{}),
.finished => unreachable, // We never send river_window_manager_v1.stop
- .update => |args| {
- wm_v1.ackUpdate(args.serial);
- wm_v1.commit();
+ .update_windowing_start => {
+ wm.updateWindowing();
+ wm_v1.updateWindowingFinish();
+ },
+ .update_rendering_start => {
+ wm.updateRendering();
+ wm_v1.updateWindowingFinish();
},
.session_locked => {},
.session_unlocked => {},
- .window => |args| {
- Window.create(args.id, wm);
- wm.arrange();
- },
+ .window => |args| Window.create(args.id, wm),
.output => |args| {
_ = args;
},
@@ -83,16 +87,34 @@ fn handleEvent(wm_v1: *river.WindowManagerV1, event: river.WindowManagerV1.Event
}
}
-pub fn arrange(wm: *WindowManager) void {
+fn updateWindowing(wm: *WindowManager) void {
+ {
+ var it = wm.seats.iterator(.forward);
+ while (it.next()) |seat| {
+ seat.updateWindowing();
+ }
+ }
{
var x: i32 = 0;
var y: i32 = 0;
var it = wm.windows.iterator(.forward);
while (it.next()) |window| {
+ window.updateWindowing(wm);
+
window.node_v1.setPosition(x, y);
window.window_v1.proposeDimensions(400, 400);
x += 40;
y += 40;
}
}
+ {
+ var it = wm.shell_surfaces.iterator(.forward);
+ while (it.next()) |shell_surface| {
+ shell_surface.updateWindowing(wm);
+ }
+ }
+}
+
+fn updateRendering(wm: *WindowManager) void {
+ _ = wm;
}
diff --git a/rivercompat/XkbBinding.zig b/rivercompat/XkbBinding.zig
index 0abbf2b..0b19e83 100644
--- a/rivercompat/XkbBinding.zig
+++ b/rivercompat/XkbBinding.zig
@@ -51,7 +51,10 @@ pub fn create(
fn handleEvent(xkb_binding_v1: *river.XkbBindingV1, event: river.XkbBindingV1.Event, binding: *XkbBinding) void {
assert(binding.xkb_binding_v1 == xkb_binding_v1);
switch (event) {
- .pressed => binding.seat.execute(binding.action),
+ .pressed => {
+ assert(binding.seat.pending.action == null);
+ binding.seat.pending.action = binding.action;
+ },
.released => {},
}
}