add support to protocol: output-power-manager + output-manager

This commit is contained in:
DanyLE
2025-07-09 20:32:33 +02:00
parent 194b7894cd
commit 9cc2408dc8
6 changed files with 779 additions and 99 deletions

28
diyac.c
View File

@ -8,7 +8,6 @@
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_data_control_v1.h>
@ -75,7 +74,7 @@ int main(int argc, char *argv[])
switch (c)
{
case 'v':
if(log_level < WLR_DEBUG)
if (log_level < WLR_DEBUG)
{
log_level++;
}
@ -95,7 +94,7 @@ int main(int argc, char *argv[])
}
if (optind < argc)
{
if(optind != argc - 1)
if (optind != argc - 1)
{
help();
return 1;
@ -167,15 +166,6 @@ int main(int argc, char *argv[])
*/
wlr_primary_selection_v1_device_manager_create(server.wl_display);
/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
server.output_layout = wlr_output_layout_create(server.wl_display);
/* Configure a listener to be notified when new outputs are available on the
* backend. */
wl_list_init(&server.outputs);
server.new_output.notify = diyac_server_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
/* Create a scene graph. This is a wlroots abstraction that handles all
* rendering and damage tracking. All the compositor author needs to do
@ -184,14 +174,14 @@ int main(int argc, char *argv[])
* necessary.
*/
server.scene = wlr_scene_create();
server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout);
wlr_xdg_output_manager_v1_create(server.wl_display,server.output_layout);
diyac_output_init(&server);
wlr_export_dmabuf_manager_v1_create(server.wl_display);
wlr_data_control_manager_v1_create(server.wl_display);
wlr_screencopy_manager_v1_create(server.wl_display);
wlr_single_pixel_buffer_manager_v1_create(server.wl_display);
wlr_fractional_scale_manager_v1_create(server.wl_display,1);
wlr_fractional_scale_manager_v1_create(server.wl_display, 1);
diya_init_idle_manager(server.wl_display);
/* Set up xdg-shell version 6 The xdg-shell is a Wayland protocol which is
* used for application windows. For more detail on shells, refer to
* https://drewdevault.com/2018/07/29/Wayland-shells.html.
@ -263,7 +253,7 @@ int main(int argc, char *argv[])
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
socket);
server.proc_mon = NULL;
if( startup_cmd && exit_with_session)
if (startup_cmd && exit_with_session)
{
if (server.session_pid == -1)
{
@ -283,13 +273,17 @@ int main(int argc, char *argv[])
/* Once wl_display_run returns, we destroy all clients then shut down the
* server. */
if(server.proc_mon)
if (server.proc_mon)
{
wl_event_source_remove(server.proc_mon);
}
wl_list_remove(&server.new_xdg_toplevel.link);
wl_list_remove(&server.new_layer_surface.link);
wl_list_remove(&server.new_output.link);
wl_list_remove(&server.output_power_manager_set_mode.link);
wl_list_remove(&server.output_manager_apply.link);
wl_list_remove(&server.output_manager_test.link);
wl_list_remove(&server.output_layout_change.link);
wl_display_destroy_clients(server.wl_display);
// wlr_scene_node_destroy(&server.scene->tree.node);
wlr_xcursor_manager_destroy(server.seat.cursor_mgr);

30
diyac.h
View File

@ -18,6 +18,8 @@
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_output_management_v1.h>
#define LAYER_TREE_SZ 4
@ -110,13 +112,13 @@ struct diyac_node_descriptor
struct diyac_session_lock
{
bool abandoned;
struct wlr_surface * focused;
struct wlr_surface *focused;
struct wlr_session_lock_v1* wlr_session_lock;
struct wlr_session_lock_v1 *wlr_session_lock;
struct wl_listener new_surface;
struct wl_listener unlock;
struct wl_listener destroy;
//struct wl_listener new_output;
// struct wl_listener new_output;
};
struct diyac_server
@ -136,7 +138,7 @@ struct diyac_server
struct wl_listener new_layer_surface;
struct wl_list views;
struct diyac_view * active_view;
struct diyac_view *active_view;
struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
/*
* Popups need to be rendered above always-on-top views, so we reparent
@ -156,9 +158,17 @@ struct diyac_server
struct wl_list outputs;
struct wl_listener new_output;
struct diyac_session_lock * lock;
struct wlr_output_power_manager_v1 *output_power_manager;
struct wl_listener output_power_manager_set_mode;
struct wl_event_source* proc_mon;
struct wlr_output_manager_v1 *output_manager;
struct wl_listener output_manager_test;
struct wl_listener output_manager_apply;
struct wl_listener output_layout_change;
struct diyac_session_lock *lock;
struct wl_event_source *proc_mon;
pid_t session_pid;
};
@ -181,7 +191,7 @@ struct diyac_output_lock_handle
struct wlr_scene_rect *background;
struct wlr_session_lock_surface_v1 *surface;
struct diyac_output * output;
struct diyac_output *output;
struct wl_listener surface_destroy;
struct wl_listener surface_map;
@ -197,13 +207,15 @@ struct diyac_output
struct wl_listener request_state;
struct wl_listener destroy;
struct wlr_box usable_area;
struct wlr_output_state pending_state;
struct wlr_scene_output* scene_output;
// layer output
struct diyac_output_scenes scenes;
// alias to diyac_output_scenes elements
struct wlr_scene_tree *layer_tree[LAYER_TREE_SZ];
// lock handle
struct diyac_output_lock_handle * lock_handle;
struct diyac_output_lock_handle *lock_handle;
};
struct foreign_toplevel
@ -227,7 +239,7 @@ struct diyac_view
struct diyac_view_state state;
struct diyac_view_state requested;
uint32_t configuration_serial;
struct wl_event_source * configuration_timeout;
struct wl_event_source *configuration_timeout;
/*
* Geometry of the wlr_surface contained within the view, as
* currently displayed. Should be kept in sync with the

View File

@ -1,6 +1,6 @@
project('diyac',
['c'],
version: '0.1.1',
version: '0.1.2',
license: 'MIT',
meson_version: '>=0.58.0',
default_options: ['c_std=gnu11', 'warning_level=3'])
@ -34,6 +34,7 @@ wayland_targets=[]
wl_protocols = [
wl_protocol_dir / 'stable/xdg-shell/xdg-shell',
'protocol/wlr-layer-shell-unstable-v1',
'protocol/wlr-output-power-management-unstable-v1',
]
foreach proto : wl_protocols

672
output.c
View File

@ -3,15 +3,66 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <wlr/backend/wayland.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <assert.h>
#include "output.h"
#include "layer.h"
#include "node.h"
#include "view.h"
#include "session.h"
/*
* While an output layout change is in process, this counter is
* non-zero and causes change-events from the wlr_output_layout
* to be ignored (to prevent, for example, moving views in a
* transitory layout state). Once the counter reaches zero,
* do_output_layout_change() must be called explicitly.
*/
static int g_pending_output_layout_change = 0;
static void output_state_init(struct diyac_output *output)
{
wlr_output_state_init(&output->pending_state);
/*
* As there is no direct way to convert an existing output
* configuration to an output_state we first convert it
* to a temporary output-management config and then apply
* it to an empty wlr_output_state.
*/
struct wlr_output_configuration_v1 *backup_config =
wlr_output_configuration_v1_create();
struct wlr_output_configuration_head_v1 *backup_head =
wlr_output_configuration_head_v1_create(
backup_config, output->wlr_output);
wlr_output_head_v1_state_apply(&backup_head->state, &output->pending_state);
wlr_output_configuration_v1_destroy(backup_config);
}
static bool output_state_commit(struct diyac_output *output)
{
bool committed =
wlr_output_commit_state(output->wlr_output, &output->pending_state);
if (committed)
{
wlr_output_state_finish(&output->pending_state);
wlr_output_state_init(&output->pending_state);
}
else
{
wlr_log(WLR_ERROR, "Failed to commit frame");
}
return committed;
}
static void output_frame(struct wl_listener *listener, void *data)
{
(void) data;
(void)data;
/* This function is called every time an output is ready to display a frame,
* generally at the output's refresh rate (e.g. 60Hz). */
struct diyac_output *output = wl_container_of(listener, output, frame);
@ -28,21 +79,126 @@ static void output_frame(struct wl_listener *listener, void *data)
wlr_scene_output_send_frame_done(scene_output, &now);
}
static struct wlr_output_configuration_v1 *create_output_config(struct diyac_server *server)
{
struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create();
if (!config)
{
wlr_log(WLR_ERROR, "wlr_output_configuration_v1_create()");
return NULL;
}
struct diyac_output *output;
wl_list_for_each(output, &server->outputs, link)
{
struct wlr_output_configuration_head_v1 *head =
wlr_output_configuration_head_v1_create(config,
output->wlr_output);
if (!head)
{
wlr_log(WLR_ERROR,
"wlr_output_configuration_head_v1_create()");
wlr_output_configuration_v1_destroy(config);
return NULL;
}
struct wlr_box box;
wlr_output_layout_get_box(server->output_layout,
output->wlr_output, &box);
if (!wlr_box_empty(&box))
{
head->state.x = box.x;
head->state.y = box.y;
}
else
{
wlr_log(WLR_ERROR, "failed to get output layout box");
}
}
return config;
}
/* returns true if usable area changed */
static bool update_usable_area(struct diyac_output *output)
{
struct wlr_box old = output->usable_area;
diyac_layers_arrange(output);
return !wlr_box_equal(&old, &output->usable_area);
}
static void do_output_layout_change(struct diyac_server *server)
{
if (!g_pending_output_layout_change)
{
struct wlr_output_configuration_v1 *config =
create_output_config(server);
if (config)
{
wlr_output_manager_v1_set_configuration(
server->output_manager, config);
}
else
{
wlr_log(WLR_ERROR,
"wlr_output_manager_v1_set_configuration()");
}
struct diyac_output *output;
wl_list_for_each(output, &server->outputs, link)
{
diyac_output_update_usable_area(output);
}
wlr_cursor_move(server->seat.cursor, NULL, 0, 0);
}
}
static void output_request_state(struct wl_listener *listener, void *data)
{
/* This function is called when the backend requests a new state for
* the output. For example, Wayland and X11 backends request a new mode
* when the output window is resized. */
/* This ensures nested backends can be resized */
struct diyac_output *output = wl_container_of(listener, output, request_state);
const struct wlr_output_event_request_state *event = data;
wlr_output_commit_state(output->wlr_output, event->state);
/*
* If wlroots ever requests other state changes here we could
* restore more of ddc9047a67cd53b2948f71fde1bbe9118000dd3f.
*/
if (event->state->committed == WLR_OUTPUT_STATE_MODE)
{
/* Only the mode has changed */
switch (event->state->mode_type)
{
case WLR_OUTPUT_STATE_MODE_FIXED:
wlr_output_state_set_mode(&output->pending_state,
event->state->mode);
break;
case WLR_OUTPUT_STATE_MODE_CUSTOM:
wlr_output_state_set_custom_mode(&output->pending_state,
event->state->custom_mode.width,
event->state->custom_mode.height,
event->state->custom_mode.refresh);
break;
}
wlr_output_schedule_frame(output->wlr_output);
return;
}
/*
* Fallback path for everything that we didn't handle above.
* The commit will cause a black frame injection so this
* path causes flickering during resize of nested outputs.
*/
if (!wlr_output_commit_state(output->wlr_output, event->state))
{
wlr_log(WLR_ERROR, "Backend requested a new state that could not be applied");
}
}
static void output_destroy(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_output *output = wl_container_of(listener, output, destroy);
if(output->lock_handle)
if (output->lock_handle)
{
diyac_session_unlock_output(output);
}
@ -53,31 +209,141 @@ static void output_destroy(struct wl_listener *listener, void *data)
wlr_scene_node_destroy(&output->scenes.overlay->node);
wlr_scene_node_destroy(&output->scenes.session->node);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->request_state.link);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->link);
struct diyac_server* server = output->server;
struct diyac_view * view;
wl_list_for_each(view, &server->views, link) {
if (view->output == output) {
struct diyac_server *server = output->server;
struct diyac_view *view;
wl_list_for_each(view, &server->views, link)
{
if (view->output == output)
{
/**
* TODO: testing this case
*/
view->output = NULL;
if(&server->outputs != server->outputs.next)
if (&server->outputs != server->outputs.next)
{
view->output = wl_container_of(server->outputs.next, view->output, link);
diyac_view_update_geometry(view, false);
}
}
}
wlr_output_state_finish(&output->pending_state);
free(output);
}
void diyac_server_new_output(struct wl_listener *listener, void *data)
static bool output_test_auto(struct wlr_output *wlr_output, struct wlr_output_state *state,
bool is_client_request)
{
wlr_log(WLR_DEBUG, "testing modes for %s", wlr_output->name);
/*
* When a client requests a specific mode, test only that mode. Here
* we interpret a custom_mode of all zeroes as "none/any"; this is
* seen e.g. with kanshi configs containing no "mode" field. In
* theory, (state->committed & WLR_OUTPUT_STATE_MODE) should be zero
* in this case, but this is not seen in practice.
*
* If the wlr_output_state did not come from a client request, then
* ignore the mode/custom_mode fields which are not meaningful.
*/
if (is_client_request && (state->mode || state->custom_mode.width || state->custom_mode.height || state->custom_mode.refresh))
{
if (state->mode)
{
wlr_log(WLR_DEBUG, "testing requested mode %dx%d@%d",
state->mode->width, state->mode->height,
state->mode->refresh);
}
else
{
wlr_log(WLR_DEBUG, "testing custom mode %dx%d@%d",
state->custom_mode.width,
state->custom_mode.height,
state->custom_mode.refresh);
}
return wlr_output_test_state(wlr_output, state);
}
struct wlr_output_mode *preferred_mode =
wlr_output_preferred_mode(wlr_output);
if (preferred_mode)
{
wlr_log(WLR_DEBUG, "testing preferred mode %dx%d@%d",
preferred_mode->width, preferred_mode->height,
preferred_mode->refresh);
wlr_output_state_set_mode(state, preferred_mode);
if (wlr_output_test_state(wlr_output, state))
{
return true;
}
}
/*
* Sometimes the preferred mode is not available due to hardware
* constraints (e.g. GPU or cable bandwidth limitations). In these
* cases it's better to fallback to lower modes than to end up with
* a black screen. See sway@4cdc4ac6
*/
struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link)
{
if (mode == preferred_mode)
{
continue;
}
wlr_log(WLR_DEBUG, "testing fallback mode %dx%d@%d",
mode->width, mode->height, mode->refresh);
wlr_output_state_set_mode(state, mode);
if (wlr_output_test_state(wlr_output, state))
{
return true;
}
}
/* Reset mode if none worked (we may still try to commit) */
wlr_output_state_set_mode(state, NULL);
return false;
}
static void add_output_to_layout(struct diyac_server *server, struct diyac_output *output)
{
struct wlr_output *wlr_output = output->wlr_output;
struct wlr_output_layout_output *layout_output =
wlr_output_layout_add_auto(server->output_layout, wlr_output);
if (!layout_output)
{
wlr_log(WLR_ERROR, "unable to add output to layout");
return;
}
if (!output->scene_output)
{
output->scene_output =
wlr_scene_output_create(server->scene, wlr_output);
if (!output->scene_output)
{
wlr_log(WLR_ERROR, "unable to create scene output");
return;
}
/*
* Note: wlr_scene_output_layout_add_output() is not
* safe to call twice, so we call it only when initially
* creating the scene_output.
*/
wlr_scene_output_layout_add_output(server->scene_layout,
layout_output, output->scene_output);
}
/* Create lock surface if needed */
if (server->lock)
{
diyac_session_lock_output(output);
}
}
static void diyac_server_new_output(struct wl_listener *listener, void *data)
{
/* This event is raised by the backend when a new output (aka a display or
* monitor) becomes available. */
@ -89,26 +355,6 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
* and our renderer. Must be done once, before commiting the output */
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
/* The output may be disabled, switch it on. */
struct wlr_output_state state;
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, true);
/* Some backends don't have modes. DRM+KMS does, and we need to set a mode
* before we can use the output. The mode is a tuple of (width, height,
* refresh rate), and each monitor supports only a specific set of modes. We
* just pick the monitor's preferred mode, a more sophisticated compositor
* would let the user configure it. */
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
if (mode != NULL)
{
wlr_output_state_set_mode(&state, mode);
}
/* Atomically applies the new output state. */
wlr_output_commit_state(wlr_output, &state);
wlr_output_state_finish(&state);
/* Allocates and configures our state for this output */
struct diyac_output *output = calloc(1, sizeof(*output));
output->wlr_output = wlr_output;
@ -118,6 +364,9 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
output->usable_area.y = 0;
output->usable_area.width = wlr_output->width;
output->usable_area.height = wlr_output->height;
output_state_init(output);
/* Sets up a listener for the frame event. */
output->frame.notify = output_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
@ -156,10 +405,6 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY] = output->scenes.overlay;
output->lock_handle = NULL;
if(server->lock)
{
diyac_session_lock_output(output);
}
/*
* Set the z-positions to achieve the following order (from top to
* bottom):
@ -178,28 +423,25 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
wlr_scene_node_raise_to_top(&output->scenes.popup->node);
wlr_scene_node_raise_to_top(&output->scenes.session->node);
/* Adds this to the output layout. The add_auto function arranges outputs
* from left-to-right in the order they appear. A more sophisticated
* compositor would let the user configure the arrangement of outputs in the
* layout.
*
* The output layout utility automatically adds a wl_output global to the
* display, which Wayland clients can see to find out information about the
* output (such as DPI, scale factor, manufacturer, etc).
wlr_output_state_set_enabled(&output->pending_state, true);
if (!output_test_auto(wlr_output, &output->pending_state,
/* is_client_request */ false))
{
wlr_log(WLR_INFO, "mode test failed for output %s",
wlr_output->name);
/*
* Continue anyway. For some reason, the test fails when
* running nested, yet the following commit succeeds.
*/
struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout,
wlr_output);
struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output);
wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output);
}
/* returns true if usable area changed */
static bool update_usable_area(struct diyac_output *output)
{
struct wlr_box old = output->usable_area;
diyac_layers_arrange(output);
return !wlr_box_equal(&old, &output->usable_area);
}
output_state_commit(output);
wlr_output_effective_resolution(wlr_output,
&output->usable_area.width, &output->usable_area.height);
g_pending_output_layout_change++;
add_output_to_layout(server, output);
g_pending_output_layout_change--;
do_output_layout_change(server);
}
void diyac_output_update_usable_area(struct diyac_output *output)
@ -225,9 +467,9 @@ struct diyac_output *diyac_output_from_cursor(struct diyac_server *server)
return output->data;
}
void diyac_output_usable_area(struct diyac_output *output,struct wlr_box* area)
void diyac_output_usable_area(struct diyac_output *output, struct wlr_box *area)
{
if(!area | !output)
if (!area | !output)
{
return;
}
@ -237,12 +479,12 @@ void diyac_output_usable_area(struct diyac_output *output,struct wlr_box* area)
output->wlr_output, &ox, &oy);
box.x -= ox;
box.y -= oy;
memcpy(area,&box,sizeof(box));
memcpy(area, &box, sizeof(box));
}
void diyac_output_full_area(struct diyac_output *output,struct wlr_box* area)
void diyac_output_full_area(struct diyac_output *output, struct wlr_box *area)
{
if(!area | !output)
if (!area | !output)
{
return;
}
@ -254,5 +496,307 @@ void diyac_output_full_area(struct diyac_output *output,struct wlr_box* area)
output->wlr_output, &ox, &oy);
box.x -= ox;
box.y -= oy;
memcpy(area,&box,sizeof(box));
memcpy(area, &box, sizeof(box));
}
static void diyac_output_power_manager_set_mode(struct wl_listener *listener, void *data)
{
struct diyac_server *server = wl_container_of(listener, server,
output_power_manager_set_mode);
struct wlr_output_power_v1_set_mode_event *event = data;
struct diyac_output *output = event->output->data;
assert(output);
switch (event->mode)
{
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
if (!event->output->enabled)
{
return;
}
wlr_output_state_set_enabled(&output->pending_state, false);
output_state_commit(output);
break;
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
if (event->output->enabled)
{
return;
}
wlr_output_state_set_enabled(&output->pending_state, true);
output_state_commit(output);
/*
* Re-set the cursor image so that the cursor
* isn't invisible on the newly enabled output.
*/
// cursor_update_image(&server->seat);
break;
}
}
static void handle_output_layout_change(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_server *server =
wl_container_of(listener, server, output_layout_change);
do_output_layout_change(server);
}
static bool verify_output_config_v1(const struct wlr_output_configuration_v1 *config)
{
const char *err_msg = NULL;
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each(head, &config->heads, link)
{
if (!head->state.enabled)
{
continue;
}
/* Handle custom modes */
if (!head->state.mode)
{
int32_t refresh = head->state.custom_mode.refresh;
if (wlr_output_is_wl(head->state.output) && refresh != 0)
{
/* Wayland backend does not support refresh rates */
err_msg = "Wayland backend refresh rates unsupported";
goto custom_mode_failed;
}
}
if (wlr_output_is_wl(head->state.output) && !head->state.adaptive_sync_enabled)
{
err_msg = "Wayland backend requires adaptive sync";
goto custom_mode_failed;
}
/*
* Ensure the new output state can be applied on
* its own and inform the client when it can not.
*
* Applying the changes may still fail later when
* getting mixed with wlr_output->pending which
* may contain further unrelated changes.
*/
struct wlr_output_state output_state;
wlr_output_state_init(&output_state);
wlr_output_head_v1_state_apply(&head->state, &output_state);
if (!output_test_auto(head->state.output, &output_state,
/* is_client_request */ true))
{
wlr_output_state_finish(&output_state);
return false;
}
wlr_output_state_finish(&output_state);
}
return true;
custom_mode_failed:
assert(err_msg);
wlr_log(WLR_INFO, "%s (%s: %dx%d@%d)",
err_msg,
head->state.output->name,
head->state.custom_mode.width,
head->state.custom_mode.height,
head->state.custom_mode.refresh);
return false;
}
void diyac_output_enable_adaptive_sync(struct diyac_output *output, bool enabled)
{
wlr_output_state_set_adaptive_sync_enabled(&output->pending_state, enabled);
if (!wlr_output_test_state(output->wlr_output, &output->pending_state))
{
wlr_output_state_set_adaptive_sync_enabled(&output->pending_state, false);
wlr_log(WLR_DEBUG,
"failed to enable adaptive sync for output %s",
output->wlr_output->name);
}
else
{
wlr_log(WLR_INFO, "adaptive sync %sabled for output %s",
enabled ? "en" : "dis", output->wlr_output->name);
}
}
static bool output_config_apply(struct diyac_server *server, struct wlr_output_configuration_v1 *config)
{
bool success = true;
g_pending_output_layout_change++;
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each(head, &config->heads, link)
{
struct wlr_output *o = head->state.output;
struct diyac_output *output = o->data;
struct wlr_output_state *os = &output->pending_state;
bool output_enabled = head->state.enabled;
wlr_output_state_set_enabled(os, output_enabled);
if (output_enabled)
{
/* Output specific actions only */
if (head->state.mode)
{
wlr_output_state_set_mode(os, head->state.mode);
}
else
{
wlr_output_state_set_custom_mode(os,
head->state.custom_mode.width,
head->state.custom_mode.height,
head->state.custom_mode.refresh);
}
/*
* Try to ensure a valid mode. Ignore failures
* here and just check the commit below.
*/
(void)output_test_auto(o, os,
/* is_client_request */ true);
wlr_output_state_set_scale(os, head->state.scale);
wlr_output_state_set_transform(os, head->state.transform);
diyac_output_enable_adaptive_sync(output,
head->state.adaptive_sync_enabled);
}
if (!output_state_commit(output))
{
/*
* FIXME: This is only part of the story, we should revert
* all previously committed outputs as well here.
*
* See https://github.com/labwc/labwc/pull/1528
*/
wlr_log(WLR_INFO, "Output config commit failed: %s", o->name);
success = false;
break;
}
/*
* Add or remove output from layout only if the commit went
* through. Note that at startup, the output may have already
* been enabled but not yet been added to the layout.
*/
bool was_in_layout =
!!wlr_output_layout_get(server->output_layout, o);
if (output_enabled)
{
if (!was_in_layout)
{
add_output_to_layout(server, output);
}
struct wlr_box pos = {0};
wlr_output_layout_get_box(server->output_layout, o, &pos);
if (pos.x != head->state.x || pos.y != head->state.y)
{
/*
* This overrides the automatic layout
*
* wlr_output_layout_add() in fact means _move()
*/
wlr_output_layout_add(server->output_layout, o,
head->state.x, head->state.y);
}
}
else if (was_in_layout)
{
/*
* At time of writing, wlr_output_layout_remove()
* indirectly destroys the wlr_scene_output, but
* this behavior may change in future. To remove
* doubt and avoid either a leak or double-free,
* explicitly destroy the wlr_scene_output before
* calling wlr_output_layout_remove().
*/
wlr_scene_output_destroy(output->scene_output);
wlr_output_layout_remove(server->output_layout, o);
output->scene_output = NULL;
}
}
g_pending_output_layout_change--;
do_output_layout_change(server);
return success;
}
static void handle_output_manager_apply(struct wl_listener *listener, void *data)
{
struct diyac_server *server =
wl_container_of(listener, server, output_manager_apply);
struct wlr_output_configuration_v1 *config = data;
bool config_is_good = verify_output_config_v1(config);
if (config_is_good && output_config_apply(server, config))
{
wlr_output_configuration_v1_send_succeeded(config);
}
else
{
wlr_output_configuration_v1_send_failed(config);
}
wlr_output_configuration_v1_destroy(config);
/*
struct output *output;
wl_list_for_each(output, &server->outputs, link)
{
wlr_xcursor_manager_load(server->seat.xcursor_manager,
output->wlr_output->scale);
}
cursor_update_focus(server);
cursor_update_image(&server->seat);
*/
}
static void handle_output_manager_test(struct wl_listener *listener, void *data)
{
(void) listener;
struct wlr_output_configuration_v1 *config = data;
if (verify_output_config_v1(config))
{
wlr_output_configuration_v1_send_succeeded(config);
}
else
{
wlr_output_configuration_v1_send_failed(config);
}
wlr_output_configuration_v1_destroy(config);
}
void diyac_output_init(struct diyac_server *server)
{
/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
server->output_layout = wlr_output_layout_create(server->wl_display);
/* Configure a listener to be notified when new outputs are available on the
* backend. */
wl_list_init(&server->outputs);
server->new_output.notify = diyac_server_new_output;
wl_signal_add(&server->backend->events.new_output, &server->new_output);
server->scene_layout = wlr_scene_attach_output_layout(server->scene, server->output_layout);
wlr_xdg_output_manager_v1_create(server->wl_display, server->output_layout);
server->output_layout_change.notify = handle_output_layout_change;
wl_signal_add(&server->output_layout->events.change,
&server->output_layout_change);
server->output_manager = wlr_output_manager_v1_create(server->wl_display);
server->output_manager_apply.notify = handle_output_manager_apply;
wl_signal_add(&server->output_manager->events.apply,
&server->output_manager_apply);
server->output_manager_test.notify = handle_output_manager_test;
wl_signal_add(&server->output_manager->events.test,
&server->output_manager_test);
server->output_power_manager =
wlr_output_power_manager_v1_create(server->wl_display);
server->output_power_manager_set_mode.notify =
diyac_output_power_manager_set_mode;
wl_signal_add(&server->output_power_manager->events.set_mode,
&server->output_power_manager_set_mode);
}

View File

@ -2,9 +2,10 @@
#define DIYAC_OUTPUT_H
#include "diyac.h"
void diyac_server_new_output(struct wl_listener *listener, void *data);
void diyac_output_update_usable_area(struct diyac_output *output);
struct diyac_output *diyac_output_from_cursor(struct diyac_server *server);
void diyac_output_usable_area(struct diyac_output * output,struct wlr_box* box);
void diyac_output_full_area(struct diyac_output *output,struct wlr_box* area);
void diyac_output_init(struct diyac_server * server);
void diyac_output_enable_adaptive_sync(struct diyac_output *output, bool enabled);
#endif

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_output_power_management_unstable_v1">
<copyright>
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.
</copyright>
<description summary="Control power management modes of outputs">
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.
</description>
<interface name="zwlr_output_power_manager_v1" version="1">
<description summary="manager to create per-output power management">
This interface is a manager that allows creating per-output power
management mode controls.
</description>
<request name="get_output_power">
<description summary="get a power management for an output">
Create an output power management mode control that can be used to
adjust the power management mode for a given output.
</description>
<arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_output_power_v1" version="1">
<description summary="adjust power management mode for an output">
This object offers requests to set the power management mode of
an output.
</description>
<enum name="mode">
<entry name="off" value="0"
summary="Output is turned off."/>
<entry name="on" value="1"
summary="Output is turned on, no power saving"/>
</enum>
<enum name="error">
<entry name="invalid_mode" value="1" summary="nonexistent power save mode"/>
</enum>
<request name="set_mode">
<description summary="Set an outputs power save mode">
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.
</description>
<arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
</request>
<event name="mode">
<description summary="Report a power management mode change">
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.
</description>
<arg name="mode" type="uint" enum="mode"
summary="the output's new power management mode"/>
</event>
<event name="failed">
<description summary="object no longer valid">
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.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy this power management">
Destroys the output power management mode control object.
</description>
</request>
</interface>
</protocol>