From e18d3671205c7716cbdda6df04014d6a906a1f7d Mon Sep 17 00:00:00 2001 From: Campbell Jones Date: Fri, 3 Nov 2023 16:46:15 -0400 Subject: [PATCH] Add support for wlr-output-management-v1 --- src/foreign_toplevel.cpp | 4 +- src/output.cpp | 14 +++--- src/output.hpp | 2 +- src/server.cpp | 97 +++++++++++++++++++++++++++++++++++++--- src/server.hpp | 6 ++- src/surface/view.cpp | 8 ++-- 6 files changed, 110 insertions(+), 21 deletions(-) diff --git a/src/foreign_toplevel.cpp b/src/foreign_toplevel.cpp index a51b7743c..facab2f20 100644 --- a/src/foreign_toplevel.cpp +++ b/src/foreign_toplevel.cpp @@ -106,9 +106,9 @@ void ForeignToplevelHandle::set_fullscreen(const bool fullscreen) { } void ForeignToplevelHandle::output_enter(const Output& output) { - wlr_foreign_toplevel_handle_v1_output_enter(&handle, output.output); + wlr_foreign_toplevel_handle_v1_output_enter(&handle, output.wlr); } void ForeignToplevelHandle::output_leave(const Output& output) { - wlr_foreign_toplevel_handle_v1_output_leave(&handle, output.output); + wlr_foreign_toplevel_handle_v1_output_leave(&handle, output.wlr); } diff --git a/src/output.cpp b/src/output.cpp index e8ab42e61..6c3f45af5 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -14,7 +14,7 @@ static void output_enable_notify(wl_listener* listener, void* data) { Output& output = magpie_container_of(listener, output, enable); (void) data; - output.scene_output = wlr_scene_get_scene_output(output.server.scene, output.output); + output.scene_output = wlr_scene_get_scene_output(output.server.scene, output.wlr); } /* This function is called every time an output is ready to display a frame, @@ -32,12 +32,12 @@ static void output_frame_notify(wl_listener* listener, void* data) { Output& output = magpie_container_of(listener, output, frame); (void) data; - if (output.scene_output == nullptr || output.is_leased || !output.output->enabled) { + if (output.scene_output == nullptr || output.is_leased || !output.wlr->enabled) { return; } wlr_scene* scene = output.server.scene; - wlr_scene_output* scene_output = wlr_scene_get_scene_output(scene, output.output); + wlr_scene_output* scene_output = wlr_scene_get_scene_output(scene, output.wlr); /* Render the scene if needed and commit the output */ wlr_scene_output_commit(scene_output); @@ -60,7 +60,7 @@ static void output_destroy_notify(wl_listener* listener, void* data) { } Output::Output(Server& server, wlr_output* output) noexcept : listeners(*this), server(server) { - this->output = output; + this->wlr = output; output->data = this; is_leased = false; @@ -82,11 +82,11 @@ Output::~Output() noexcept { } void Output::update_layout() { - wlr_scene_output* scene_output = wlr_scene_get_scene_output(server.scene, output); + wlr_scene_output* scene_output = wlr_scene_get_scene_output(server.scene, wlr); full_area.x = scene_output->x; full_area.y = scene_output->y; - wlr_output_effective_resolution(output, &full_area.width, &full_area.height); + wlr_output_effective_resolution(wlr, &full_area.width, &full_area.height); usable_area = full_area; @@ -97,7 +97,7 @@ void Output::update_layout() { wlr_box Output::usable_area_in_layout_coords() const { double layout_x = 0, layout_y = 0; - wlr_output_layout_output_coords(server.output_layout, output, &layout_x, &layout_y); + wlr_output_layout_output_coords(server.output_layout, wlr, &layout_x, &layout_y); wlr_box box = usable_area; box.x += layout_x; diff --git a/src/output.hpp b/src/output.hpp index 9f32b2d2d..52578d37e 100644 --- a/src/output.hpp +++ b/src/output.hpp @@ -28,7 +28,7 @@ class Output { public: Server& server; - wlr_output* output; + wlr_output* wlr; wlr_scene_output* scene_output; wlr_box full_area; wlr_box usable_area; diff --git a/src/server.cpp b/src/server.cpp index 5e5f04904..3e97b7b4b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "wlr-wrap-end.hpp" void Server::focus_view(View& view, wlr_surface* surface) { @@ -185,7 +187,7 @@ static void new_layer_surface_notify(wl_listener* listener, void* data) { Output* output; if (layer_surface.output == nullptr) { output = static_cast(wlr_output_layout_get_center_output(server.output_layout)->data); - layer_surface.output = output->output; + layer_surface.output = output->wlr; } else { output = static_cast(layer_surface.output->data); } @@ -223,14 +225,92 @@ static void drm_lease_notify(wl_listener* listener, void* data) { if (output == nullptr) continue; - wlr_output_enable(output->output, false); - wlr_output_commit(output->output); - wlr_output_layout_remove(server.output_layout, output->output); + wlr_output_enable(output->wlr, false); + wlr_output_commit(output->wlr); + wlr_output_layout_remove(server.output_layout, output->wlr); output->is_leased = true; output->scene_output = nullptr; } } +void output_layout_change_notify(wl_listener* listener, void* data) { + Server& server = magpie_container_of(listener, server, output_layout_change); + (void) data; + + if (server.num_pending_output_layout_changes > 0) { + return; + } + + wlr_output_configuration_v1* config = wlr_output_configuration_v1_create(); + + for (auto* output : server.outputs) { + wlr_output_configuration_head_v1* head = wlr_output_configuration_head_v1_create(config, output->wlr); + + wlr_box box; + wlr_output_layout_get_box(server.output_layout, output->wlr, &box); + if (!wlr_box_empty(&box)) { + head->state.x = box.x; + head->state.y = box.y; + } + } + + wlr_output_manager_v1_set_configuration(server.output_manager, config); +} + +void output_manager_apply_notify(wl_listener* listener, void* data) { + Server& server = magpie_container_of(listener, server, output_manager_apply); + auto& config = *static_cast(data); + + server.num_pending_output_layout_changes++; + + wlr_output_configuration_head_v1* head; + wl_list_for_each(head, &config.heads, link) { + Output& output = *static_cast(head->state.output->data); + bool enabled = head->state.enabled && !output.is_leased; + bool adding = enabled && !output.wlr->enabled; + bool removing = !enabled && output.wlr->enabled; + + wlr_output_enable(output.wlr, enabled); + if (enabled) { + if (head->state.mode) { + wlr_output_set_mode(output.wlr, head->state.mode); + } else { + int32_t width = head->state.custom_mode.width; + int32_t height = head->state.custom_mode.height; + int32_t refresh = head->state.custom_mode.refresh; + wlr_output_set_custom_mode(output.wlr, width, height, refresh); + } + + wlr_output_set_scale(output.wlr, head->state.scale); + wlr_output_set_transform(output.wlr, head->state.transform); + } + + if (!wlr_output_commit(output.wlr)) { + wlr_log(WLR_ERROR, "Output config commit failed"); + continue; + } + + if (adding) { + wlr_output_layout_add_auto(server.output_layout, output.wlr); + output.scene_output = wlr_scene_get_scene_output(server.scene, output.wlr); + } + + if (enabled) { + wlr_box box; + wlr_output_layout_get_box(server.output_layout, output.wlr, &box); + if (box.x != head->state.x || box.y != head->state.y) { + /* This overrides the automatic layout */ + wlr_output_layout_move(server.output_layout, output.wlr, head->state.x, head->state.y); + } + } + + if (removing) { + wlr_output_layout_remove(server.output_layout, output.wlr); + output.scene_output = nullptr; + } + } +} + Server::Server() : listeners(*this) { /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ @@ -276,9 +356,14 @@ Server::Server() : listeners(*this) { /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ output_layout = wlr_output_layout_create(); - assert(output_layout); + listeners.output_layout_change.notify = output_layout_change_notify; + wl_signal_add(&output_layout->events.change, &listeners.output_layout_change); + + wlr_xdg_output_manager_v1_create(display, output_layout); - output_manager = wlr_xdg_output_manager_v1_create(display, output_layout); + output_manager = wlr_output_manager_v1_create(display); + listeners.output_manager_apply.notify = output_manager_apply_notify; + wl_signal_add(&output_manager->events.apply, &listeners.output_manager_apply); output_power_manager = wlr_output_power_manager_v1_create(display); listeners.output_power_manager_set_mode.notify = output_power_manager_set_mode_notify; diff --git a/src/server.hpp b/src/server.hpp index bdab00ae6..822d26cab 100644 --- a/src/server.hpp +++ b/src/server.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,8 @@ class Server { wl_listener activation_request_activation; wl_listener backend_new_output; wl_listener drm_lease_request; + wl_listener output_layout_change; + wl_listener output_manager_apply; wl_listener output_power_manager_set_mode; Listeners(Server& parent) noexcept : parent(parent) {} }; @@ -75,10 +78,11 @@ class Server { wlr_box grab_geobox; uint32_t resize_edges; - wlr_xdg_output_manager_v1* output_manager; + wlr_output_manager_v1* output_manager; wlr_output_power_manager_v1* output_power_manager; wlr_output_layout* output_layout; std::set outputs; + uint8_t num_pending_output_layout_changes = 0; wlr_idle_notifier_v1* idle_notifier; wlr_idle_inhibit_manager_v1* idle_inhibit_manager; diff --git a/src/surface/view.cpp b/src/surface/view.cpp index 97ddf9805..e7d742082 100644 --- a/src/surface/view.cpp +++ b/src/surface/view.cpp @@ -26,12 +26,12 @@ const std::optional View::find_output_for_maximize() { long best_area = 0; for (auto* output : server.outputs) { - if (!wlr_output_layout_intersects(server.output_layout, output->output, &previous)) { + if (!wlr_output_layout_intersects(server.output_layout, output->wlr, &previous)) { continue; } wlr_box output_box; - wlr_output_layout_get_box(server.output_layout, output->output, &output_box); + wlr_output_layout_get_box(server.output_layout, output->wlr, &output_box); wlr_box intersection; wlr_box_intersection(&intersection, &previous, &output_box); long intersection_area = intersection.width * intersection.height; @@ -43,9 +43,9 @@ const std::optional View::find_output_for_maximize() { } // if it's outside of all outputs, just use the pointer position - if (best_output == NULL) { + if (best_output == nullptr) { for (auto* output : server.outputs) { - if (wlr_output_layout_contains_point(server.output_layout, output->output, cursor.cursor->x, cursor.cursor->y)) { + if (wlr_output_layout_contains_point(server.output_layout, output->wlr, cursor.cursor->x, cursor.cursor->y)) { best_output = output; break; }