Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
Seat: implement resize operations
river/Cursor.zig | 2 +-
river/Seat.zig | 57 ++++++++++++++++++++++++++++++++++++++++++++-------
river/Window.zig | 46 ++++++++++++++++++++++++++---------------
river/XdgToplevel.zig | 4 ----
rivercompat/Seat.zig | 18 +++++++++++++---
5 files changed, 95 insertions(+), 32 deletions(-)
diff --git a/river/Cursor.zig b/river/Cursor.zig
index 08e2d4d..826adac 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -355,7 +355,7 @@ pub fn processMotionRelative(cursor: *Cursor, event: *const wlr.Pointer.event.Mo
data.delta_y = dy - @trunc(dy);
cursor.wlr_cursor.move(event.device, dx, dy);
- cursor.seat.updateOp(@intFromFloat(dx), @intFromFloat(dy));
+ cursor.seat.updateOp(@intFromFloat(cursor.wlr_cursor.x), @intFromFloat(cursor.wlr_cursor.y));
},
}
}
diff --git a/river/Seat.zig b/river/Seat.zig
index 679253c..ba7339f 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -448,7 +448,14 @@ fn handleRequest(
.seat = seat,
} };
},
- .op_add_resize_window => {},
+ .op_add_resize_window => |args| {
+ const data = args.window.getUserData() orelse return;
+ const window: *Window = @ptrCast(@alignCast(data));
+ window.uncommitted.op = .{ .resize = .{
+ .seat = seat,
+ .edges = args.edges,
+ } };
+ },
.op_end => seat.uncommitted.op = .end,
.pointer_confine_to_region => {},
@@ -539,7 +546,18 @@ pub fn applyCommitted(seat: *Seat) void {
};
}
},
- .resize => {}, // TODO
+ .resize => |data| {
+ if (data.seat == seat) {
+ assert(window.inflight.op == .none);
+ window.inflight.op = .{
+ .resize = .{
+ .seat = seat,
+ .edges = data.edges,
+ .start_box = window.pending.box,
+ },
+ };
+ }
+ },
}
}
}
@@ -741,8 +759,12 @@ pub fn handleSwitchMapping(
}
}
-pub fn updateOp(seat: *Seat, dx: i32, dy: i32) void {
- assert(seat.op != null);
+pub fn updateOp(seat: *Seat, x: i32, y: i32) void {
+ const op = seat.op.?;
+
+ // Total dx/dy since operation start
+ const dx = x - op.start_x;
+ const dy = y - op.start_y;
{
var it = server.wm.windows.iterator(.forward);
@@ -752,12 +774,33 @@ pub fn updateOp(seat: *Seat, dx: i32, dy: i32) void {
.move => |data| {
if (data.seat != seat) continue;
- window.pending.box.x += dx;
- window.pending.box.y += dy;
+ window.pending.box.x = data.start_x + dx;
+ window.pending.box.y = data.start_y + dy;
+
+ seat.op.?.dirty = true;
+ },
+ .resize => |data| {
+ if (data.seat != seat) continue;
+
+ // For resize, position is not updated until the window has committed
+ // its new dimensions. The client may not commit exactly the dimensions
+ // we request and we need to know the actual committed dimensions to
+ // correctly place the top left corner in the case of a resize from
+ // the top or left edge.
+ if (data.edges.left) {
+ window.pending.box.width = @max(1, data.start_box.width - dx);
+ } else if (data.edges.right) {
+ window.pending.box.width = @max(1, data.start_box.width + dx);
+ }
+
+ if (data.edges.top) {
+ window.pending.box.height = @max(1, data.start_box.height - dy);
+ } else if (data.edges.bottom) {
+ window.pending.box.height = @max(1, data.start_box.height + dy);
+ }
seat.op.?.dirty = true;
},
- .resize => {}, // TODO
}
}
}
diff --git a/river/Window.zig b/river/Window.zig
index 5ad0f3e..3836e7d 100644
--- a/river/Window.zig
+++ b/river/Window.zig
@@ -284,15 +284,6 @@ pub fn setDimensionsHint(window: *Window, hint: DimensionsHint) void {
}
}
-pub fn setPosition(window: *Window, x: i32, y: i32) void {
- window.pending.box.x = x;
- window.pending.box.y = y;
-
- if (x != window.sent.box.x or y != window.sent.box.y) {
- server.wm.dirtyPending();
- }
-}
-
pub fn setDimensions(window: *Window, width: i32, height: i32) void {
window.pending.box.width = width;
window.pending.box.height = height;
@@ -300,8 +291,34 @@ pub fn setDimensions(window: *Window, width: i32, height: i32) void {
window.inflight.box.width = width;
window.inflight.box.height = height;
- if (window.sent.dimensions == null or
- width != window.sent.dimensions.?.width or height != window.sent.dimensions.?.height)
+ switch (window.inflight.op) {
+ .none => {},
+ .move => |data| assert(data.seat.op != null),
+ .resize => |data| {
+ assert(data.seat.op != null);
+
+ if (data.edges.left) {
+ window.pending.box.x = data.start_box.x + data.start_box.width - width;
+ } else if (data.edges.right) {
+ window.pending.box.x = data.start_box.x;
+ }
+
+ if (data.edges.top) {
+ window.pending.box.y = data.start_box.y + data.start_box.height - height;
+ } else if (data.edges.bottom) {
+ window.pending.box.y = data.start_box.y;
+ }
+
+ window.inflight.box.x = window.pending.box.x;
+ window.inflight.box.y = window.pending.box.y;
+ },
+ }
+
+ if (window.sent.dimensions == null or window.sent.position == null or
+ width != window.sent.dimensions.?.width or
+ height != window.sent.dimensions.?.height or
+ window.pending.box.x != window.sent.position.?.x or
+ window.pending.box.y != window.sent.position.?.y)
{
server.wm.dirtyPending();
}
@@ -642,13 +659,8 @@ pub fn commitTransaction(window: *Window) void {
// If we did not use the current geometry of the toplevel at this point
// we would be rendering the SSD border at initial size X but the surface
// would be rendered at size Y.
- if (false and window.inflight.resizing) {
- window.resizeUpdatePosition(toplevel.geometry.width, toplevel.geometry.height);
- }
-
+ window.setDimensions(toplevel.geometry.width, toplevel.geometry.height);
window.current = window.inflight;
- window.current.box.width = toplevel.geometry.width;
- window.current.box.height = toplevel.geometry.height;
},
.idle, .committed => {
toplevel.configure_state = .idle;
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 769993a..f8ab16d 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -362,10 +362,6 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
.acked, .timed_out_acked => {
toplevel.wlr_toplevel.base.getGeometry(&toplevel.geometry);
- if (false and window.inflight.resizing) {
- window.resizeUpdatePosition(toplevel.geometry.width, toplevel.geometry.height);
- }
-
window.setDimensions(toplevel.geometry.width, toplevel.geometry.height);
switch (toplevel.configure_state) {
diff --git a/rivercompat/Seat.zig b/rivercompat/Seat.zig
index 8439975..20094ee 100644
--- a/rivercompat/Seat.zig
+++ b/rivercompat/Seat.zig
@@ -51,7 +51,8 @@ pub fn create(wm: *WindowManager, seat_v1: *river.SeatV1) void {
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.s, .{ .mod4 = true }, .show_all);
- PointerBinding.create(seat, c.BTN_LEFT, .{ .mod4 = true }, .move_start, .move_end);
+ 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);
}
@@ -103,7 +104,8 @@ pub const Action = enum {
hide_focused,
show_all,
move_start,
- move_end,
+ resize_start,
+ op_end,
};
pub fn execute(seat: *Seat, action: Action) void {
@@ -124,6 +126,16 @@ pub fn execute(seat: *Seat, action: Action) void {
seat.seat_v1.opAddMoveWindow(window.window_v1);
}
},
- .move_end => seat.seat_v1.opEnd(),
+ .resize_start => {
+ seat.seat_v1.opStartPointer();
+ var it = seat.wm.windows.iterator(.forward);
+ while (it.next()) |window| {
+ seat.seat_v1.opAddResizeWindow(window.window_v1, .{
+ .top = true,
+ .left = true,
+ });
+ }
+ },
+ .op_end => seat.seat_v1.opEnd(),
}
}