From 7e44e3c83292c526e031eae691a97e3a1cbed266 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Fri, 5 Jun 2026 09:05:21 -0700 Subject: [PATCH] cage: add support for wlr-output-power-management This implements the zwlr_output_power_management_v1 protocol, which allow clients such as swayidle to toggle display power state. Outputs are intentionally not removed per the discussion in #245. A dpms_powered_off flag is tracked on each output to prevent a DPMS "on" request from re-enabling an output that was disabled via wlr-output-management rather than DPMS. The protocol generation/dep additions is heavily inspired by swaywm Fixes #245 --- cage.c | 10 ++ meson.build | 4 +- output.c | 24 ++++ output.h | 3 + protocols/meson.build | 26 ++++ ...lr-output-power-management-unstable-v1.xml | 129 ++++++++++++++++++ server.h | 4 + 7 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 protocols/meson.build create mode 100644 protocols/wlr-output-power-management-unstable-v1.xml diff --git a/cage.c b/cage.c index b2523ca..7b51e48 100644 --- a/cage.c +++ b/cage.c @@ -518,6 +518,15 @@ main(int argc, char *argv[]) server.output_manager_test.notify = handle_output_manager_test; wl_signal_add(&server.output_manager_v1->events.test, &server.output_manager_test); + server.output_power_manager_v1 = wlr_output_power_manager_v1_create(server.wl_display); + if (!server.output_power_manager_v1) { + wlr_log(WLR_ERROR, "Unable to create the output power manager"); + ret = 1; + goto end; + } + server.output_power_manager_set_mode.notify = handle_output_power_manager_set_mode; + wl_signal_add(&server.output_power_manager_v1->events.set_mode, &server.output_power_manager_set_mode); + #if WLR_HAS_DRM_BACKEND server.drm_lease_v1 = wlr_drm_lease_v1_manager_create(server.wl_display, server.backend); if (server.drm_lease_v1) { @@ -662,6 +671,7 @@ main(int argc, char *argv[]) wl_list_remove(&server.new_idle_inhibitor_v1.link); wl_list_remove(&server.new_output.link); wl_list_remove(&server.output_layout_change.link); + wl_list_remove(&server.output_power_manager_set_mode.link); end: if (pid != 0) diff --git a/meson.build b/meson.build index 586e999..3c08d9e 100644 --- a/meson.build +++ b/meson.build @@ -102,9 +102,11 @@ if conf_data.get('CAGE_HAS_XWAYLAND', 0) == 1 cage_sources += 'xwayland.c' endif +subdir('protocols') + executable( meson.project_name(), - cage_sources, + cage_sources + wl_protos_src, dependencies: [ wayland_server, wlroots, diff --git a/output.c b/output.c index 9f2a708..758d933 100644 --- a/output.c +++ b/output.c @@ -112,6 +112,7 @@ output_enable(struct cg_output *output) if (wlr_output_commit_state(wlr_output, &state)) { output_layout_add_auto(output); } + output->dpms_powered_off = false; update_output_manager_config(output->server); } @@ -401,6 +402,29 @@ out: return ok; } +void +handle_output_power_manager_set_mode(struct wl_listener *listener, void *data) +{ + struct wlr_output_power_v1_set_mode_event *event = data; + struct cg_output *output = event->output->data; + + if (event->mode == ZWLR_OUTPUT_POWER_V1_MODE_ON) { + if (!output->dpms_powered_off) { + return; + } + output->dpms_powered_off = false; + } else { + output->dpms_powered_off = true; + } + + struct wlr_output_state state = {0}; + wlr_output_state_set_enabled(&state, event->mode == ZWLR_OUTPUT_POWER_V1_MODE_ON); + + if (!wlr_output_commit_state(output->wlr_output, &state)) { + wlr_log(WLR_ERROR, "Failed to set power mode for output %s", output->wlr_output->name); + } +} + void handle_output_manager_apply(struct wl_listener *listener, void *data) { diff --git a/output.h b/output.h index fa72545..774b673 100644 --- a/output.h +++ b/output.h @@ -18,6 +18,8 @@ struct cg_output { struct wl_listener frame; struct wl_list link; // cg_server::outputs + + bool dpms_powered_off; }; void handle_output_manager_apply(struct wl_listener *listener, void *data); @@ -25,5 +27,6 @@ void handle_output_manager_test(struct wl_listener *listener, void *data); void handle_output_layout_change(struct wl_listener *listener, void *data); void handle_new_output(struct wl_listener *listener, void *data); void output_set_window_title(struct cg_output *output, const char *title); +void handle_output_power_manager_set_mode(struct wl_listener *listener, void *data); #endif diff --git a/protocols/meson.build b/protocols/meson.build new file mode 100644 index 0000000..c6aadfb --- /dev/null +++ b/protocols/meson.build @@ -0,0 +1,26 @@ +wayland_scanner_dep = dependency('wayland-scanner', native: true) +wayland_scanner = find_program( + wayland_scanner_dep.get_variable('wayland_scanner'), + native: true, +) + +wl_protos_src = [] + +protocols = [ + 'wlr-output-power-management-unstable-v1.xml', +] + +foreach xml : protocols + wl_protos_src += custom_target( + xml.underscorify() + '_c', + input: xml, + output: '@BASENAME@-protocol.c', + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + ) + wl_protos_src += custom_target( + xml.underscorify() + '_server_h', + input: xml, + output: '@BASENAME@-protocol.h', + command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], + ) +endforeach diff --git a/protocols/wlr-output-power-management-unstable-v1.xml b/protocols/wlr-output-power-management-unstable-v1.xml new file mode 100644 index 0000000..e45b613 --- /dev/null +++ b/protocols/wlr-output-power-management-unstable-v1.xml @@ -0,0 +1,129 @@ + + + + Copyright © 2019 Purism SPC + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows clients to control power management modes + of outputs that are currently part of the compositor space. The + intent is to allow special clients like desktop shells to power + down outputs when the system is idle. + + To modify outputs not currently part of the compositor space see + wlr-output-management. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-output power + management mode controls. + + + + + Create an output power management mode control that can be used to + adjust the power management mode for a given output. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This object offers requests to set the power management mode of + an output. + + + + + + + + + + + + + + Set an output's power save mode to the given mode. The mode change + is effective immediately. If the output does not support the given + mode a failed event is sent. + + + + + + + Report the power management mode change of an output. + + The mode event is sent after an output changed its power + management mode. The reason can be a client using set_mode or the + compositor deciding to change an output's mode. + This event is also sent immediately when the object is created + so the client is informed about the current power management mode. + + + + + + + This event indicates that the output power management mode control + is no longer valid. This can happen for a number of reasons, + including: + - The output doesn't support power management + - Another client already has exclusive power management mode control + for this output + - The output disappeared + + Upon receiving this event, the client should destroy this object. + + + + + + Destroys the output power management mode control object. + + + + + diff --git a/server.h b/server.h index a5b7138..3f7e92a 100644 --- a/server.h +++ b/server.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,9 @@ struct cg_server { struct wl_listener output_manager_apply; struct wl_listener output_manager_test; + struct wlr_output_power_manager_v1 *output_power_manager_v1; + struct wl_listener output_power_manager_set_mode; + #if WLR_HAS_DRM_BACKEND struct wlr_drm_lease_v1_manager *drm_lease_v1; struct wl_listener drm_lease_request;