From 69507ee640b479769a91724553d5d94639ee497f Mon Sep 17 00:00:00 2001 From: Uks2 Date: Wed, 22 Jun 2022 07:27:17 +0100 Subject: [PATCH] Add wlr_output_manager support This allows things like wlr-randr to work. wlr-randr or similar can send in two new events: `output_manager_apply' and `output_manager_test'. In output.c, their handler both call an new `output_manager_configure' function which loops through the list of outputs twice. The first loop applies all the requested configuration ad checks that its not all messed up. The second loop either commits that configuration or reverts it depending on whether it worked and whether we're responding to a test event. There's also now an output_manager_update function, called whenever the output layout is changed, which copies changes from the wlr_output_layout to the wlr_output_manager. --- include/desktop/desktop.h | 3 + include/desktop/output.h | 4 ++ kiwmi/desktop/desktop.c | 9 +++ kiwmi/desktop/output.c | 145 ++++++++++++++++++++++++++++++++++++++ kiwmi/luak/kiwmi_output.c | 1 + 5 files changed, 162 insertions(+) diff --git a/include/desktop/desktop.h b/include/desktop/desktop.h index 6fbdd35..56c20b6 100644 --- a/include/desktop/desktop.h +++ b/include/desktop/desktop.h @@ -17,6 +17,7 @@ struct kiwmi_desktop { struct wlr_layer_shell_v1 *layer_shell; struct wlr_data_device_manager *data_device_manager; struct wlr_output_layout *output_layout; + struct wlr_output_manager_v1 *output_manager; struct wl_list outputs; // struct kiwmi_output::link struct wl_list views; // struct kiwmi_view::link @@ -26,6 +27,8 @@ struct kiwmi_desktop { struct wl_listener xdg_toplevel_new_decoration; struct wl_listener layer_shell_new_surface; struct wl_listener new_output; + struct wl_listener output_manager_apply; + struct wl_listener output_manager_test; struct { struct wl_signal new_output; diff --git a/include/desktop/output.h b/include/desktop/output.h index 3f8c034..64b793a 100644 --- a/include/desktop/output.h +++ b/include/desktop/output.h @@ -42,6 +42,10 @@ struct kiwmi_render_data { }; void new_output_notify(struct wl_listener *listener, void *data); +void output_layout_change_notify(struct wl_listener *listener, void *data); +void output_manager_apply_notify(struct wl_listener *listener, void *data); +void output_manager_test_notify(struct wl_listener *listener, void *data); +void output_manager_update(struct kiwmi_desktop *desktop); void output_damage(struct kiwmi_output *output); diff --git a/kiwmi/desktop/desktop.c b/kiwmi/desktop/desktop.c index dd9dc83..9a1ceb3 100644 --- a/kiwmi/desktop/desktop.c +++ b/kiwmi/desktop/desktop.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,14 @@ desktop_init(struct kiwmi_desktop *desktop) wl_signal_init(&desktop->events.view_map); wl_signal_init(&desktop->events.request_active_output); + desktop->output_manager = wlr_output_manager_v1_create(server->wl_display); + desktop->output_manager_apply.notify = output_manager_apply_notify; + wl_signal_add( + &desktop->output_manager->events.apply, &desktop->output_manager_apply); + desktop->output_manager_test.notify = output_manager_test_notify; + wl_signal_add( + &desktop->output_manager->events.test, &desktop->output_manager_test); + return true; } diff --git a/kiwmi/desktop/output.c b/kiwmi/desktop/output.c index dda2a49..868452f 100644 --- a/kiwmi/desktop/output.c +++ b/kiwmi/desktop/output.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -242,6 +243,31 @@ output_frame_notify(struct wl_listener *listener, void *data) wlr_output_commit(wlr_output); } +void +output_manager_update(struct kiwmi_desktop *desktop) +{ + struct wlr_output_configuration_v1 *configuration = + wlr_output_configuration_v1_create(); + + struct kiwmi_output *output; + wl_list_for_each (output, &desktop->outputs, link) { + struct wlr_output_configuration_head_v1 *head = + wlr_output_configuration_head_v1_create( + configuration, output->wlr_output); + + struct wlr_box *area = wlr_output_layout_get_box( + desktop->output_layout, output->wlr_output); + + if (area != NULL) { + head->state.x = area->x; + head->state.y = area->y; + } + } + + wlr_output_manager_v1_set_configuration( + desktop->output_manager, configuration); +} + static void output_commit_notify(struct wl_listener *listener, void *data) { @@ -264,6 +290,7 @@ output_destroy_notify(struct wl_listener *listener, void *UNUSED(data)) if (output->desktop->output_layout) { wlr_output_layout_remove( output->desktop->output_layout, output->wlr_output); + output_manager_update(output->desktop); } wl_signal_emit(&output->events.destroy, output); @@ -297,6 +324,7 @@ output_mode_notify(struct wl_listener *listener, void *UNUSED(data)) arrange_layers(output); output_damage(output); + output_manager_update(output->desktop); wl_signal_emit(&output->events.resize, output); } @@ -377,6 +405,7 @@ new_output_notify(struct wl_listener *listener, void *data) wl_list_insert(&desktop->outputs, &output->link); wlr_output_layout_add_auto(desktop->output_layout, wlr_output); + output_manager_update(desktop); wlr_output_create_global(wlr_output); @@ -392,6 +421,122 @@ new_output_notify(struct wl_listener *listener, void *data) wl_signal_emit(&desktop->events.new_output, output); } +static void +output_manager_configure( + struct kiwmi_server *server, + struct wlr_output_configuration_v1 *configuration, + bool test) +{ + struct wlr_output_configuration_head_v1 *head; + struct wlr_output_configuration_head_v1 *bad_head = NULL; + wl_list_for_each (head, &configuration->heads, link) { + struct wlr_output *wlr_output = head->state.output; + + wlr_output_enable(wlr_output, head->state.enabled); + if (head->state.enabled) { + if (head->state.mode) { + wlr_output_set_mode(wlr_output, head->state.mode); + } else { + wlr_output_set_custom_mode( + wlr_output, + head->state.custom_mode.width, + head->state.custom_mode.height, + head->state.custom_mode.refresh); + } + + wlr_output_set_transform(wlr_output, head->state.transform); + wlr_output_set_scale(wlr_output, head->state.scale); + } + + if (!wlr_output_test(wlr_output)) { + bad_head = head; + break; + } + } + + wl_list_for_each (head, &configuration->heads, link) { + if (bad_head != NULL || test) { + wlr_output_rollback(head->state.output); + if (head == bad_head) { + break; + } else { + continue; + } + } + struct kiwmi_output *output = head->state.output->data; + bool enable_changed = WLR_OUTPUT_STATE_ENABLED + == (output->wlr_output->pending.committed + & WLR_OUTPUT_STATE_ENABLED); + if (head->state.enabled) { + if (enable_changed) { + wlr_output_layout_add( + server->desktop.output_layout, + output->wlr_output, + head->state.x, + head->state.y); + wl_signal_emit(&output->desktop->events.new_output, output); + } else { + bool resized = output->wlr_output->pending.committed + & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE + | WLR_OUTPUT_STATE_TRANSFORM); + struct wlr_output_layout_output *layout_output = + wlr_output_layout_get( + server->desktop.output_layout, output->wlr_output); + bool moved = head->state.x != layout_output->x + || head->state.y != layout_output->y; + if (moved || resized) { + wlr_output_layout_move( + server->desktop.output_layout, + output->wlr_output, + head->state.x, + head->state.y); + wl_signal_emit(&output->events.resize, output); + } + } + } else if (enable_changed) { + wl_signal_emit(&output->events.destroy, output); + wlr_output_layout_remove( + server->desktop.output_layout, output->wlr_output); + } + wlr_output_commit(head->state.output); + } + + if (bad_head == NULL) { + wlr_output_configuration_v1_send_succeeded(configuration); + if (!test) { + wlr_output_manager_v1_set_configuration( + server->desktop.output_manager, configuration); + } else { + wlr_output_configuration_v1_destroy(configuration); + } + } else { + wlr_output_configuration_v1_send_failed(configuration); + wlr_output_configuration_v1_destroy(configuration); + } +} + +void +output_manager_apply_notify(struct wl_listener *listener, void *data) +{ + struct kiwmi_desktop *desktop = + wl_container_of(listener, desktop, output_manager_apply); + struct kiwmi_server *server = wl_container_of(desktop, server, desktop); + struct wlr_output_configuration_v1 *configuration = data; + + output_manager_configure(server, configuration, false); +} + +void +output_manager_test_notify(struct wl_listener *listener, void *data) +{ + struct kiwmi_desktop *desktop = + wl_container_of(listener, desktop, output_manager_test); + struct kiwmi_server *server = wl_container_of(desktop, server, desktop); + struct wlr_output_configuration_v1 *configuration = data; + + output_manager_configure(server, configuration, true); +} + void output_damage(struct kiwmi_output *output) { diff --git a/kiwmi/luak/kiwmi_output.c b/kiwmi/luak/kiwmi_output.c index e76c61c..e2df6d4 100644 --- a/kiwmi/luak/kiwmi_output.c +++ b/kiwmi/luak/kiwmi_output.c @@ -55,6 +55,7 @@ l_kiwmi_output_move(lua_State *L) int ly = lua_tonumber(L, 3); wlr_output_layout_move(output_layout, output->wlr_output, lx, ly); + output_manager_update(output->desktop); return 0; }