add support to protocol: output-power-manager + output-manager
This commit is contained in:
28
diyac.c
28
diyac.c
@ -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
30
diyac.h
@ -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
|
||||
|
@ -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
672
output.c
@ -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);
|
||||
}
|
3
output.h
3
output.h
@ -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
|
128
protocol/wlr-output-power-management-unstable-v1.xml
Normal file
128
protocol/wlr-output-power-management-unstable-v1.xml
Normal 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>
|
Reference in New Issue
Block a user