Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
rwm: add and implement river_window_v1.dimensions_hint
protocol/river-window-management-v1.xml | 23 ++++++++++++++++++++++-
river/Window.zig | 33 ++++++++++++++++++++++++---------
river/XdgToplevel.zig | 17 +++++++----------
rivercompat/Window.zig | 1 +
4 files changed, 54 insertions(+), 20 deletions(-)
diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
index 1b320fa..2268a7b 100644
--- a/protocol/river-window-management-v1.xml
+++ b/protocol/river-window-management-v1.xml
@@ -206,7 +206,6 @@
a river_window_manager_v1.commit request.
TODO:
- - size hints (including unset state)
- window cropping
</description>
@@ -260,6 +259,28 @@
<arg name="id" type="new_id" interface="river_node_v1"/>
</request>
+ <event name="dimensions_hint">
+ <description summary="the window's preferred min/max dimensions">
+ This event informs the window manager of the window's preferred min/max
+ dimensions. These preferences are a hint, and the window manager is free
+ to propose dimensions outside of these bounds.
+
+ All min/max width/height values must be strictly greater than or equal
+ to 0. A value of 0 indicates that the window has no preference for that
+ value.
+
+ The min_width/min_height must be strictly less than or equal to the
+ max_width/max_height.
+
+ This event is double-buffered state and will be followed by a
+ river_window_manager_v1.update event.
+ </description>
+ <arg name="min_width" type="int"/>
+ <arg name="min_height" type="int"/>
+ <arg name="max_width" type="int"/>
+ <arg name="max_height" type="int"/>
+ </event>
+
<event name="dimensions">
<description summary="window dimensions">
This event indicates the dimensions of the window in the compositor's
diff --git a/river/Window.zig b/river/Window.zig
index b9622fa..462ebfb 100644
--- a/river/Window.zig
+++ b/river/Window.zig
@@ -20,6 +20,7 @@ const build_options = @import("build_options");
const std = @import("std");
const assert = std.debug.assert;
const math = std.math;
+const meta = std.meta;
const posix = std.posix;
const wlr = @import("wlroots");
const wl = @import("wayland").server.wl;
@@ -38,11 +39,11 @@ const XwaylandWindow = @import("XwaylandWindow.zig");
const log = std.log.scoped(.wm);
-pub const Constraints = struct {
- min_width: u31 = 1,
- max_width: u31 = math.maxInt(u31),
- min_height: u31 = 1,
- max_height: u31 = math.maxInt(u31),
+pub const DimensionsHint = struct {
+ min_width: u31 = 0,
+ max_width: u31 = 0,
+ min_height: u31 = 0,
+ max_height: u31 = 0,
};
const Impl = union(enum) {
@@ -123,9 +124,6 @@ saved_surface_tree: *wlr.SceneTree,
borders: [4]*wlr.SceneRect,
popup_tree: *wlr.SceneTree,
-/// Bounds on the width/height of the window, set by the toplevel/xwindow implementation.
-constraints: Constraints = .{},
-
/// Set to true once the window manager client has made its first commit
/// proposing dimensions for a new river_window_v1 object.
initialized: bool = false,
@@ -146,6 +144,7 @@ pending: struct {
/// Indicates that the closed event will be sent in the next update sequence.
closing,
} = .init,
+ dimensions_hint: DimensionsHint = .{},
box: wlr.Box = .{ .x = 0, .y = 0, .width = 0, .height = 0 },
decoration_hint: river.WindowV1.DecorationHint = .only_supports_csd,
/// Set back to no_request at the end of each update sequence
@@ -161,6 +160,7 @@ pending: struct {
/// This state is only kept around in order to avoid sending redundant events
/// to the window manager client.
sent: struct {
+ dimensions_hint: DimensionsHint = .{},
box: wlr.Box = .{ .x = 0, .y = 0, .width = 0, .height = 0 },
decoration_hint: river.WindowV1.DecorationHint = .only_supports_csd,
} = .{},
@@ -279,6 +279,13 @@ pub fn closing(window: *Window) void {
window.dirtyPending();
}
+pub fn setDimensionsHint(window: *Window, hint: DimensionsHint) void {
+ window.pending.dimensions_hint = hint;
+ if (!meta.eql(window.sent.dimensions_hint, hint)) {
+ window.dirtyPending();
+ }
+}
+
pub fn setDimensions(window: *Window, width: i32, height: i32) void {
window.pending.box.width = width;
window.pending.box.height = height;
@@ -357,7 +364,15 @@ pub fn sendDirty(window: *Window) void {
const sent = &window.sent;
// XXX send all dirty pending state
- log.debug("XXXXXXXXXXXXXX pending {any} sent {any}", .{ pending.box, sent.box });
+ if (new or !meta.eql(pending.dimensions_hint, sent.dimensions_hint)) {
+ window_v1.sendDimensionsHint(
+ pending.dimensions_hint.min_width,
+ pending.dimensions_hint.min_height,
+ pending.dimensions_hint.max_width,
+ pending.dimensions_hint.max_height,
+ );
+ sent.dimensions_hint = pending.dimensions_hint;
+ }
if ((new or pending.box.width != sent.box.width or
pending.box.height != sent.box.height) and !pending.box.empty())
{
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index db36d11..840eed7 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -298,6 +298,13 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const toplevel: *XdgToplevel = @fieldParentPtr("commit", listener);
const window = toplevel.window;
+ window.setDimensionsHint(.{
+ .min_width = @intCast(toplevel.wlr_toplevel.current.min_width),
+ .min_height = @intCast(toplevel.wlr_toplevel.current.min_height),
+ .max_width = @intCast(toplevel.wlr_toplevel.current.max_width),
+ .max_height = @intCast(toplevel.wlr_toplevel.current.max_height),
+ });
+
if (toplevel.wlr_toplevel.base.initial_commit) {
window.ready();
return;
@@ -307,16 +314,6 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
return;
}
- {
- const state = &toplevel.wlr_toplevel.current;
- window.constraints = .{
- .min_width = @max(state.min_width, 1),
- .max_width = if (state.max_width > 0) @intCast(state.max_width) else math.maxInt(u31),
- .min_height = @max(state.min_height, 1),
- .max_height = if (state.max_height > 0) @intCast(state.max_height) else math.maxInt(u31),
- };
- }
-
switch (toplevel.configure_state) {
.idle, .committed, .timed_out => {
const old_geometry = toplevel.geometry;
diff --git a/rivercompat/Window.zig b/rivercompat/Window.zig
index a5659e3..dc3df1f 100644
--- a/rivercompat/Window.zig
+++ b/rivercompat/Window.zig
@@ -63,6 +63,7 @@ fn handleEvent(window_v1: *river.WindowV1, event: river.WindowV1.Event, window:
gpa.destroy(window);
},
+ .dimensions_hint => {},
.dimensions => {},
.app_id => {},
.title => {},