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; }