diff --git a/cage.c b/cage.c index a98467f..bce4b65 100644 --- a/cage.c +++ b/cage.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -340,6 +341,8 @@ main(int argc, char *argv[]) ret = 1; goto end; } + server.output_layout_change.notify = handle_output_layout_change; + wl_signal_add(&server.output_layout->events.change, &server.output_layout_change); server.scene = wlr_scene_create(); if (!server.scene) { @@ -472,6 +475,17 @@ main(int argc, char *argv[]) goto end; } + server.output_manager_v1 = wlr_output_manager_v1_create(server.wl_display); + if (!server.output_manager_v1) { + wlr_log(WLR_ERROR, "Unable to create the output manager"); + ret = 1; + goto end; + } + server.output_manager_apply.notify = handle_output_manager_apply; + wl_signal_add(&server.output_manager_v1->events.apply, &server.output_manager_apply); + server.output_manager_test.notify = handle_output_manager_test; + wl_signal_add(&server.output_manager_v1->events.test, &server.output_manager_test); + gamma_control_manager = wlr_gamma_control_manager_v1_create(server.wl_display); if (!gamma_control_manager) { wlr_log(WLR_ERROR, "Unable to create the gamma control manager"); diff --git a/output.c b/output.c index cdf1b54..30f8c56 100644 --- a/output.c +++ b/output.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,32 @@ #include "xwayland.h" #endif +#define OUTPUT_CONFIG_UPDATED \ + (WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | \ + WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) + +static void +update_output_manager_config(struct cg_server *server) +{ + struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); + + struct cg_output *output; + wl_list_for_each (output, &server->outputs, link) { + struct wlr_output *wlr_output = output->wlr_output; + struct wlr_output_configuration_head_v1 *config_head = + wlr_output_configuration_head_v1_create(config, wlr_output); + struct wlr_box output_box; + + wlr_output_layout_get_box(server->output_layout, wlr_output, &output_box); + if (!wlr_box_empty(&output_box)) { + config_head->state.x = output_box.x; + config_head->state.y = output_box.y; + } + } + + wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); +} + static void output_enable(struct cg_output *output) { @@ -57,6 +84,8 @@ output_enable(struct cg_output *output) output->scene_output = wlr_scene_get_scene_output(output->server->scene, wlr_output); assert(output->scene_output != NULL); + + update_output_manager_config(output->server); } static void @@ -77,6 +106,44 @@ output_disable(struct cg_output *output) wlr_output_commit(wlr_output); } +static bool +output_apply_config(struct cg_output *output, struct wlr_output_configuration_head_v1 *head, bool test_only) +{ + wlr_output_enable(output->wlr_output, head->state.enabled); + + if (head->state.enabled) { + /* Do not mess with these parameters for output to be disabled */ + wlr_output_set_scale(output->wlr_output, head->state.scale); + wlr_output_set_transform(output->wlr_output, head->state.transform); + + if (head->state.mode) { + wlr_output_set_mode(output->wlr_output, head->state.mode); + } else { + wlr_output_set_custom_mode(output->wlr_output, head->state.custom_mode.width, + head->state.custom_mode.height, head->state.custom_mode.refresh); + } + } + + if (test_only) { + bool ret = wlr_output_test(output->wlr_output); + wlr_output_rollback(output->wlr_output); + return ret; + } + + /* Apply output configuration */ + if (!wlr_output_commit(output->wlr_output)) { + return false; + } + + if (head->state.enabled) { + wlr_output_layout_add(output->server->output_layout, head->state.output, head->state.x, head->state.y); + } else { + wlr_output_layout_remove(output->server->output_layout, output->wlr_output); + } + + return true; +} + static void handle_output_frame(struct wl_listener *listener, void *data) { @@ -99,15 +166,21 @@ handle_output_commit(struct wl_listener *listener, void *data) struct cg_output *output = wl_container_of(listener, output, commit); struct wlr_output_event_commit *event = data; - if (!output->wlr_output->enabled) { - return; + /* Notes: + * - output layout change will also be called if needed to position the views + * - always update output manager configuration even if the output is now disabled */ + + if (event->committed & WLR_OUTPUT_STATE_ENABLED) { + if (output->wlr_output->enabled) { + output->scene_output = wlr_scene_get_scene_output(output->server->scene, output->wlr_output); + assert(output->scene_output != NULL); + } else { + output->scene_output = NULL; + } } - if (event->committed & WLR_OUTPUT_STATE_TRANSFORM) { - struct cg_view *view; - wl_list_for_each (view, &output->server->views, link) { - view_position(view); - } + if (event->committed & OUTPUT_CONFIG_UPDATED) { + update_output_manager_config(output->server); } } @@ -120,10 +193,17 @@ handle_output_mode(struct wl_listener *listener, void *data) return; } - struct cg_view *view; - wl_list_for_each (view, &output->server->views, link) { - view_position(view); - } + view_position_all(output->server); + update_output_manager_config(output->server); +} + +void +handle_output_layout_change(struct wl_listener *listener, void *data) +{ + struct cg_server *server = wl_container_of(listener, server, output_layout_change); + + view_position_all(server); + update_output_manager_config(server); } static void @@ -149,11 +229,7 @@ output_destroy(struct cg_output *output) struct cg_output *prev = wl_container_of(server->outputs.next, prev, link); if (prev) { output_enable(prev); - - struct cg_view *view; - wl_list_for_each (view, &server->views, link) { - view_position(view); - } + view_position_all(server); } } } @@ -230,11 +306,7 @@ handle_new_output(struct wl_listener *listener, void *data) } output_enable(output); - - struct cg_view *view; - wl_list_for_each (view, &output->server->views, link) { - view_position(view); - } + view_position_all(output->server); } void @@ -255,3 +327,49 @@ output_set_window_title(struct cg_output *output, const char *title) #endif } } + +static bool +output_config_apply(struct cg_server *server, struct wlr_output_configuration_v1 *config, bool test_only) +{ + struct wlr_output_configuration_head_v1 *head; + + wl_list_for_each (head, &config->heads, link) { + struct cg_output *output = head->state.output->data; + + if (!output_apply_config(output, head, test_only)) { + return false; + } + } + + return true; +} + +void +handle_output_manager_apply(struct wl_listener *listener, void *data) +{ + struct cg_server *server = wl_container_of(listener, server, output_manager_apply); + struct wlr_output_configuration_v1 *config = data; + + if (output_config_apply(server, config, false)) { + wlr_output_configuration_v1_send_succeeded(config); + } else { + wlr_output_configuration_v1_send_failed(config); + } + + wlr_output_configuration_v1_destroy(config); +} + +void +handle_output_manager_test(struct wl_listener *listener, void *data) +{ + struct cg_server *server = wl_container_of(listener, server, output_manager_test); + struct wlr_output_configuration_v1 *config = data; + + if (output_config_apply(server, config, true)) { + wlr_output_configuration_v1_send_succeeded(config); + } else { + wlr_output_configuration_v1_send_failed(config); + } + + wlr_output_configuration_v1_destroy(config); +} diff --git a/output.h b/output.h index ced06f6..4783fd1 100644 --- a/output.h +++ b/output.h @@ -21,6 +21,9 @@ struct cg_output { struct wl_list link; // cg_server::outputs }; +void handle_output_manager_apply(struct wl_listener *listener, void *data); +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); diff --git a/server.h b/server.h index 0e808c1..6e2fddf 100644 --- a/server.h +++ b/server.h @@ -37,6 +37,7 @@ struct cg_server { * some outputs may be disabled. */ struct wl_list outputs; // cg_output::link struct wl_listener new_output; + struct wl_listener output_layout_change; struct wl_listener xdg_toplevel_decoration; struct wl_listener new_xdg_shell_surface; @@ -46,6 +47,9 @@ struct cg_server { #if CAGE_HAS_XWAYLAND struct wl_listener new_xwayland_surface; #endif + struct wlr_output_manager_v1 *output_manager_v1; + struct wl_listener output_manager_apply; + struct wl_listener output_manager_test; bool xdg_decoration; bool allow_vt_switch; diff --git a/view.c b/view.c index b4a3eca..8948580 100644 --- a/view.c +++ b/view.c @@ -97,6 +97,15 @@ view_position(struct cg_view *view) } } +void +view_position_all(struct cg_server *server) +{ + struct cg_view *view; + wl_list_for_each (view, &server->views, link) { + view_position(view); + } +} + void view_unmap(struct cg_view *view) { diff --git a/view.h b/view.h index 5ae13dd..9b2ab75 100644 --- a/view.h +++ b/view.h @@ -49,6 +49,7 @@ bool view_is_primary(struct cg_view *view); bool view_is_transient_for(struct cg_view *child, struct cg_view *parent); void view_activate(struct cg_view *view, bool activate); void view_position(struct cg_view *view); +void view_position_all(struct cg_server *server); void view_unmap(struct cg_view *view); void view_map(struct cg_view *view, struct wlr_surface *surface); void view_destroy(struct cg_view *view);