commit 5936cf5aff5a2cf781c339b7ae0e245a18dda8a8
parent a22ca29561af5b1c9f6da39f2646d47727fb0757
Author: deurzen <max@deurzen.net>
Date:   Wed,  1 Jun 2022 06:32:08 +0200
implements layer shell popup stacking
Diffstat:
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;
+}