Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
rwm: add river_output_v1.wl_output
protocol/river-window-management-v1.xml | 24 ++++++++++++++++++++++++
river/Output.zig | 32 ++++++++++++++++++++++++++++++++
rivercompat/Output.zig | 1 +
3 files changed, 57 insertions(+)
diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
index d200375..86130ac 100644
--- a/protocol/river-window-management-v1.xml
+++ b/protocol/river-window-management-v1.xml
@@ -1133,6 +1133,30 @@
</description>
</event>
+ <event name="wl_output">
+ <description summary="corresponding wl_output">
+ The wl_output object corresponding to the river_output_v1.
+
+ This event is sent exactly once. The wl_output associated with a
+ river_output_v1 cannot change. It is guaranteed that there is a 1-to-1
+ mapping between wl_output and river_output_v1 objects.
+
+ Note that this event will only be sent after the client binds the
+ wl_output global. It is guaranteed that the corresponding wl_output is
+ advertised before the river_window_manager_v1.output event is sent.
+
+ The global_remove event for the corresponding wl_output may be sent
+ before the river_output_v1.remove event. This is due to the fact that
+ river_output_v1 state changes are synced to the river window management
+ update sequence while changes to globals are not.
+
+ Rationale: The window manager may need information provided by the
+ wl_output interface such as the name/description. It also may need the
+ wl_output object to start screencopy for example.
+ </description>
+ <arg name="wl_output" type="object" interface="wl_output"/>
+ </event>
+
<event name="position">
<description summary="output position">
This event indicates the position of the output in the compositor's
diff --git a/river/Output.zig b/river/Output.zig
index 52f5b50..0c9215f 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -132,6 +132,7 @@ wlr_output: ?*wlr.Output,
scene_output: ?*wlr.SceneOutput,
object: ?*river.OutputV1 = null,
+sent_wl_output: bool = false,
/// Tracks the currently presented frame on the output as it pertains to ext-session-lock.
/// The output is initially considered blanked:
@@ -171,6 +172,7 @@ link_sent: wl.list.Link,
current: State,
destroy: wl.Listener(*wlr.Output) = .init(handleDestroy),
+bind: wl.Listener(*wlr.Output.event.Bind) = .init(handleBind),
request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequestState),
frame: wl.Listener(*wlr.Output) = .init(handleFrame),
present: wl.Listener(*wlr.Output.event.Present) = .init(handlePresent),
@@ -210,6 +212,7 @@ pub fn create(wlr_output: *wlr.Output) !void {
output.link_sent.init();
wlr_output.events.destroy.add(&output.destroy);
+ wlr_output.events.bind.add(&output.bind);
wlr_output.events.request_state.add(&output.request_state);
wlr_output.events.frame.add(&output.frame);
wlr_output.events.present.add(&output.present);
@@ -230,6 +233,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) v
output.link.remove();
output.destroy.link.remove();
+ output.bind.link.remove();
output.request_state.link.remove();
output.frame.link.remove();
output.present.link.remove();
@@ -243,6 +247,20 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) v
server.wm.dirtyWindowing();
}
+fn handleBind(listener: *wl.Listener(*wlr.Output.event.Bind), event: *wlr.Output.event.Bind) void {
+ const output: *Output = @fieldParentPtr("bind", listener);
+ // Guard against a client binding the same wl_output global more than once.
+ if (output.sent_wl_output) {
+ return;
+ }
+ if (output.object) |output_v1| {
+ if (output_v1.getClient() == event.resource.getClient()) {
+ output_v1.sendWlOutput(event.resource);
+ output.sent_wl_output = true;
+ }
+ }
+}
+
pub fn updateWindowingStart(output: *Output) void {
switch (output.scheduled.state) {
.enabled, .disabled_soft => {
@@ -265,6 +283,20 @@ pub fn updateWindowingStart(output: *Output) void {
const pending = &output.scheduled;
const sent = &output.sent;
+ if (new) {
+ const client = output_v1.getClient();
+ var it = output.wlr_output.?.resources.iterator(.forward);
+ while (it.next()) |wl_output| {
+ if (client == wl_output.getClient()) {
+ output_v1.sendWlOutput(wl_output);
+ output.sent_wl_output = true;
+ break;
+ }
+ } else {
+ output.sent_wl_output = false;
+ }
+ }
+
if (new or pending.width() != sent.width() or pending.height() != sent.height()) {
output_v1.sendDimensions(pending.width(), pending.height());
}
diff --git a/rivercompat/Output.zig b/rivercompat/Output.zig
index 7480e32..fe6d26d 100644
--- a/rivercompat/Output.zig
+++ b/rivercompat/Output.zig
@@ -64,6 +64,7 @@ fn handleEvent(output_v1: *river.OutputV1, event: river.OutputV1.Event, output:
assert(output.output_v1 == output_v1);
switch (event) {
.removed => output.pending.removed = true,
+ .wl_output => {},
.position => |args| {
output.x = args.x;
output.y = args.y;