kranewl

A wlroots-based dynamic Wayland compositor, written in C++, configurable with Lua
git clone git://git.deurzen.net/kranewl
Log | Files | Refs | LICENSE

commit 5936cf5aff5a2cf781c339b7ae0e245a18dda8a8
parent a22ca29561af5b1c9f6da39f2646d47727fb0757
Author: deurzen <max@deurzen.net>
Date:   Wed,  1 Jun 2022 06:32:08 +0200

implements layer shell popup stacking

Diffstat:
Minclude/kranewl/scene-layer.hh | 3++-
Minclude/kranewl/server.hh | 2+-
Minclude/kranewl/tree/layer.hh | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/kranewl/server.cc | 1+
Msrc/kranewl/tree/layer.cc | 280+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 333 insertions(+), 16 deletions(-)

diff --git a/include/kranewl/scene-layer.hh b/include/kranewl/scene-layer.hh @@ -10,5 +10,6 @@ enum SceneLayer : unsigned short { SCENE_LAYER_FREE = 3, SCENE_LAYER_TOP = 4, SCENE_LAYER_OVERLAY = 5, - SCENE_LAYER_NOFOCUS = 6, + SCENE_LAYER_POPUP = 6, + SCENE_LAYER_NOFOCUS = 7, }; diff --git a/include/kranewl/server.hh b/include/kranewl/server.hh @@ -65,7 +65,7 @@ public: struct wlr_data_device_manager* mp_data_device_manager; struct wlr_output_layout* mp_output_layout; struct wlr_scene* mp_scene; - std::array<struct wlr_scene_node*, 7> m_scene_layers; + std::array<struct wlr_scene_node*, 8> m_scene_layers; Seat m_seat; private: diff --git a/include/kranewl/tree/layer.hh b/include/kranewl/tree/layer.hh @@ -5,16 +5,18 @@ #include <kranewl/scene-layer.hh> #include <kranewl/tree/node.hh> -#include <chrono> - extern "C" { #include <wayland-server-core.h> } +#include <chrono> +#include <vector> + typedef class Server* Server_ptr; typedef class Model* Model_ptr; typedef class Seat* Seat_ptr; typedef class Output* Output_ptr; +typedef struct LayerPopup* LayerPopup_ptr; typedef struct Layer final : public Node { Layer( @@ -35,6 +37,7 @@ typedef struct Layer final : public Node { static void handle_map(struct wl_listener*, void*); static void handle_unmap(struct wl_listener*, void*); static void handle_surface_commit(struct wl_listener*, void*); + static void handle_new_popup(struct wl_listener*, void*); static void handle_destroy(struct wl_listener*, void*); bool mapped() const { return m_mapped; } @@ -53,11 +56,14 @@ typedef struct Layer final : public Node { SceneLayer m_scene_layer; struct wlr_layer_surface_v1* mp_layer_surface; + std::vector<LayerPopup_ptr> m_popups; + pid_t m_pid; struct wl_listener ml_map; struct wl_listener ml_unmap; struct wl_listener ml_surface_commit; + struct wl_listener ml_new_popup; struct wl_listener ml_destroy; private: @@ -66,3 +72,56 @@ private: std::chrono::time_point<std::chrono::steady_clock> m_managed_since; }* Layer_ptr; + +typedef struct LayerPopup final { + LayerPopup( + struct wlr_xdg_popup*, + Layer_ptr, + Server_ptr, + Model_ptr, + Seat_ptr + ); + + LayerPopup( + struct wlr_xdg_popup*, + LayerPopup_ptr, + Layer_ptr, + Server_ptr, + Model_ptr, + Seat_ptr + ); + + ~LayerPopup(); + + Region const& region() { return m_region; } + void set_region(Region const&); + + static void handle_map(struct wl_listener*, void*); + static void handle_unmap(struct wl_listener*, void*); + static void handle_surface_commit(struct wl_listener*, void*); + static void handle_new_popup(struct wl_listener*, void*); + static void handle_destroy(struct wl_listener*, void*); + + Server_ptr mp_server; + Model_ptr mp_model; + Seat_ptr mp_seat; + + Layer_ptr mp_root; + LayerPopup_ptr mp_parent; + + struct wlr_scene_node* mp_scene; + SceneLayer m_scene_layer; + struct wlr_xdg_popup* mp_wlr_popup; + + std::vector<LayerPopup_ptr> m_popups; + + struct wl_listener ml_map; + struct wl_listener ml_unmap; + struct wl_listener ml_surface_commit; + struct wl_listener ml_new_popup; + struct wl_listener ml_destroy; + +private: + Region m_region; + +}* LayerPopup_ptr; diff --git a/src/kranewl/server.cc b/src/kranewl/server.cc @@ -110,6 +110,7 @@ Server::Server(Model_ptr model) &wlr_scene_tree_create(&mp_scene->node)->node, &wlr_scene_tree_create(&mp_scene->node)->node, &wlr_scene_tree_create(&mp_scene->node)->node, + &wlr_scene_tree_create(&mp_scene->node)->node, &wlr_scene_tree_create(&mp_scene->node)->node }, m_seat([this]() { diff --git a/src/kranewl/tree/layer.cc b/src/kranewl/tree/layer.cc @@ -14,6 +14,7 @@ extern "C" { #include <wlr/types/wlr_layer_shell_v1.h> #include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_seat.h> +#include <wlr/types/wlr_xdg_shell.h> } #undef static #undef namespace @@ -37,6 +38,7 @@ Layer::Layer( ml_map({ .notify = Layer::handle_map }), ml_unmap({ .notify = Layer::handle_unmap }), ml_surface_commit({ .notify = Layer::handle_surface_commit }), + ml_new_popup({ .notify = Layer::handle_new_popup }), ml_destroy({ .notify = Layer::handle_destroy }), m_mapped(0), m_managed_since(std::chrono::steady_clock::now()) @@ -44,6 +46,7 @@ Layer::Layer( wl_signal_add(&mp_layer_surface->events.map, &ml_map); wl_signal_add(&mp_layer_surface->events.unmap, &ml_unmap); wl_signal_add(&mp_layer_surface->surface->events.commit, &ml_surface_commit); + wl_signal_add(&mp_layer_surface->events.new_popup, &ml_new_popup); wl_signal_add(&mp_layer_surface->events.destroy, &ml_destroy); } @@ -97,10 +100,10 @@ Layer::handle_map(struct wl_listener* listener, void*) layer->mp_model->register_layer(layer); - struct wlr_layer_surface_v1_state initial_state = layer->mp_layer_surface->current; - layer->mp_layer_surface->current = layer->mp_layer_surface->pending; + struct wlr_layer_surface_v1_state initial_state = layer->mp_layer_surface->current; + layer->mp_layer_surface->current = layer->mp_layer_surface->pending; layer->mp_output->arrange_layers(); - layer->mp_layer_surface->current = initial_state; + layer->mp_layer_surface->current = initial_state; wlr_surface_send_enter( layer->mp_layer_surface->surface, @@ -145,30 +148,29 @@ Layer::handle_surface_commit(struct wl_listener* listener, void*) struct wlr_layer_surface_v1* layer_surface = layer->mp_layer_surface; struct wlr_output* wlr_output = layer_surface->output; - SceneLayer scene_layer; switch (layer_surface->current.layer) { case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: - scene_layer = SCENE_LAYER_BACKGROUND; + layer->m_scene_layer = SCENE_LAYER_BACKGROUND; break; case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: - scene_layer = SCENE_LAYER_BOTTOM; + layer->m_scene_layer = SCENE_LAYER_BOTTOM; break; case ZWLR_LAYER_SHELL_V1_LAYER_TOP: - scene_layer = SCENE_LAYER_TOP; + layer->m_scene_layer = SCENE_LAYER_TOP; break; case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: - scene_layer = SCENE_LAYER_OVERLAY; + layer->m_scene_layer = SCENE_LAYER_OVERLAY; break; default: spdlog::error("No applicable scene layer found for layer surface"); spdlog::warn("Not committing surface"); - scene_layer = SCENE_LAYER_NONE; + layer->m_scene_layer = SCENE_LAYER_NONE; return; } wlr_scene_node_reparent( layer->mp_scene, - layer->mp_server->m_scene_layers[scene_layer] + layer->mp_server->m_scene_layers[layer->m_scene_layer] ); Output_ptr output; @@ -180,12 +182,70 @@ Layer::handle_surface_commit(struct wl_listener* listener, void*) layer->m_mapped = layer_surface->mapped; - if (layer->mp_server->m_scene_layers[scene_layer] != layer->mp_scene) - output->relayer_layer(layer, layer->m_scene_layer, scene_layer); + if (layer->mp_server->m_scene_layers[layer->m_scene_layer] != layer->mp_scene) + output->relayer_layer(layer, layer->m_scene_layer, layer->m_scene_layer); output->arrange_layers(); } +static inline LayerPopup_ptr +create_layer_popup( + struct wlr_xdg_popup* wlr_popup, + LayerPopup_ptr parent, + Layer_ptr root, + Server_ptr server, + Model_ptr model, + Seat_ptr seat +) +{ + LayerPopup_ptr popup = parent + ? new LayerPopup(wlr_popup, parent, root, server, model, seat) + : new LayerPopup(wlr_popup, root, server, model, seat); + + if (parent) + parent->m_popups.push_back(popup); + else + root->m_popups.push_back(popup); + + struct wlr_output* wlr_output = root->mp_layer_surface->output; + Output_ptr output = reinterpret_cast<Output_ptr>(wlr_output->data); + + Region const& output_region = output->full_region(); + Region const& relative_region = parent + ? parent->region() + : root->region(); + + struct wlr_box mappable_region = { + .x = -relative_region.pos.x, + .y = -relative_region.pos.y, + .width = output_region.dim.w, + .height = output_region.dim.h + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &mappable_region); + popup->set_region(Region::from_box(wlr_popup->geometry)); + + return popup; +} + +void +Layer::handle_new_popup(struct wl_listener* listener, void* data) +{ + TRACE(); + + Layer_ptr layer = wl_container_of(listener, layer, ml_new_popup); + struct wlr_xdg_popup* wlr_popup = reinterpret_cast<struct wlr_xdg_popup*>(data); + + create_layer_popup( + wlr_popup, + nullptr, + layer, + layer->mp_server, + layer->mp_model, + layer->mp_seat + ); +} + void Layer::handle_destroy(struct wl_listener* listener, void*) { @@ -212,3 +272,199 @@ Layer::handle_destroy(struct wl_listener* listener, void*) spdlog::info("Destroyed layer {}", layer->m_uid_formatted); delete layer; } + +static inline void +init_layer_popup(LayerPopup_ptr popup) +{ + TRACE(); + + wl_signal_add(&popup->mp_wlr_popup->base->events.map, &popup->ml_map); + wl_signal_add(&popup->mp_wlr_popup->base->events.unmap, &popup->ml_unmap); + wl_signal_add(&popup->mp_wlr_popup->base->surface->events.commit, &popup->ml_surface_commit); + wl_signal_add(&popup->mp_wlr_popup->base->events.new_popup, &popup->ml_new_popup); + wl_signal_add(&popup->mp_wlr_popup->base->events.destroy, &popup->ml_destroy); +} + +LayerPopup::LayerPopup( + struct wlr_xdg_popup* wlr_popup, + Layer_ptr parent, + Server_ptr server, + Model_ptr model, + Seat_ptr seat +) + : mp_server(server), + mp_model(model), + mp_seat(seat), + mp_wlr_popup(wlr_popup), + mp_parent(nullptr), + mp_root(parent), + m_popups({}), + ml_map({ .notify = LayerPopup::handle_map }), + ml_unmap({ .notify = LayerPopup::handle_unmap }), + ml_surface_commit({ .notify = LayerPopup::handle_surface_commit }), + ml_new_popup({ .notify = LayerPopup::handle_new_popup }), + ml_destroy({ .notify = LayerPopup::handle_destroy }) +{ + TRACE(); + init_layer_popup(this); +} + +LayerPopup::LayerPopup( + struct wlr_xdg_popup* wlr_popup, + LayerPopup_ptr parent, + Layer_ptr root, + Server_ptr server, + Model_ptr model, + Seat_ptr seat +) + : mp_server(server), + mp_model(model), + mp_seat(seat), + mp_wlr_popup(wlr_popup), + mp_parent(parent), + mp_root(root), + m_popups({}), + ml_map({ .notify = LayerPopup::handle_map }), + ml_unmap({ .notify = LayerPopup::handle_unmap }), + ml_surface_commit({ .notify = LayerPopup::handle_surface_commit }), + ml_new_popup({ .notify = LayerPopup::handle_new_popup }), + ml_destroy({ .notify = LayerPopup::handle_destroy }) +{ + TRACE(); + init_layer_popup(this); +} + +LayerPopup::~LayerPopup() +{ + +} + +void +LayerPopup::set_region(Region const& region) +{ + m_region = region; +} + +void +LayerPopup::handle_map(struct wl_listener* listener, void*) +{ + TRACE(); + + LayerPopup_ptr popup = wl_container_of(listener, popup, ml_map); + struct wlr_xdg_popup* wlr_popup = popup->mp_wlr_popup; + Layer_ptr root = popup->mp_root; + LayerPopup_ptr parent = popup->mp_parent; + Server_ptr server = root->mp_server; + + SceneLayer reference_scene_layer = parent + ? parent->m_scene_layer + : root->m_scene_layer; + + popup->m_scene_layer = SceneLayer::SCENE_LAYER_POPUP; + popup->mp_scene = &wlr_scene_tree_create( + server->m_scene_layers[popup->m_scene_layer] + )->node; + wlr_popup->base->surface->data = popup->mp_scene + = wlr_scene_xdg_surface_create( + server->m_scene_layers[popup->m_scene_layer], + wlr_popup->base + ); + popup->mp_scene->data = popup; + + wlr_scene_node_reparent( + popup->mp_scene, + server->m_scene_layers[popup->m_scene_layer] + ); + + Region const& region = popup->region(); + wlr_scene_node_set_position( + popup->mp_scene, + region.pos.x, + region.pos.y + ); + + if (popup->m_scene_layer == reference_scene_layer) + wlr_scene_node_place_above( + popup->mp_scene, + parent ? parent->mp_scene : root->mp_scene + ); +} + +void +LayerPopup::handle_unmap(struct wl_listener*, void*) +{ + TRACE(); + +} + +void +LayerPopup::handle_surface_commit(struct wl_listener* listener, void*) +{ + TRACE(); + + LayerPopup_ptr popup = wl_container_of(listener, popup, ml_surface_commit); + + struct wlr_xdg_popup* wlr_popup = popup->mp_wlr_popup; + Layer_ptr root = popup->mp_root; + LayerPopup_ptr parent = popup->mp_parent; + + /* wlr_scene_node_reparent( */ + /* layer->mp_scene, */ + /* layer->mp_server->m_scene_layers[scene_layer] */ + /* ); */ + + /* Output_ptr output; */ + /* if (!wlr_output || !(output = reinterpret_cast<Output_ptr>(wlr_output->data))) */ + /* return; */ + + /* if (layer_surface->current.committed == 0 && layer->m_mapped == layer_surface->mapped) */ + /* return; */ + + /* layer->m_mapped = layer_surface->mapped; */ + + /* if (layer->mp_server->m_scene_layers[scene_layer] != layer->mp_scene) */ + /* output->relayer_layer(layer, layer->m_scene_layer, scene_layer); */ + + /* output->arrange_layers(); */ +} + +void +LayerPopup::handle_new_popup(struct wl_listener* listener, void* data) +{ + TRACE(); + + LayerPopup_ptr popup = wl_container_of(listener, popup, ml_new_popup); + struct wlr_xdg_popup* wlr_popup = reinterpret_cast<struct wlr_xdg_popup*>(data); + + create_layer_popup( + wlr_popup, + popup, + popup->mp_root, + popup->mp_server, + popup->mp_model, + popup->mp_seat + ); +} + +void +LayerPopup::handle_destroy(struct wl_listener* listener, void*) +{ + TRACE(); + + LayerPopup_ptr popup = wl_container_of(listener, popup, ml_destroy); + + wl_list_remove(&popup->ml_map.link); + wl_list_remove(&popup->ml_unmap.link); + wl_list_remove(&popup->ml_surface_commit.link); + wl_list_remove(&popup->ml_new_popup.link); + wl_list_remove(&popup->ml_destroy.link); + + Util::erase_remove( + popup->mp_parent + ? popup->mp_parent->m_popups + : popup->mp_root->m_popups, + popup + ); + + delete popup; +}