]> git.mdlowis.com Git - proto/labwc.git/commitdiff
layers: support popups
authorJohan Malm <jgm323@gmail.com>
Wed, 20 Oct 2021 21:32:46 +0000 (22:32 +0100)
committerJohan Malm <jgm323@gmail.com>
Wed, 20 Oct 2021 21:32:46 +0000 (22:32 +0100)
include/layers.h
src/layers.c
src/output.c

index 6ed53234739cf0836249a55df74b8c872289b583..d01a410231dcb03e2b9aefbb167e12153c6f68d0 100644 (file)
@@ -1,13 +1,18 @@
 #ifndef __LABWC_LAYERS_H
 #define __LABWC_LAYERS_H
 #include <wayland-server.h>
+#include <wlr/types/wlr_surface.h>
 #include <wlr/types/wlr_layer_shell_v1.h>
 
 struct server;
 
+enum layer_parent {
+       LAYER_PARENT_LAYER,
+       LAYER_PARENT_POPUP,
+};
+
 struct lab_layer_surface {
        struct wlr_layer_surface_v1 *layer_surface;
-       struct server *server;
        struct wl_list link;
 
        struct wl_listener destroy;
@@ -15,9 +20,37 @@ struct lab_layer_surface {
        struct wl_listener unmap;
        struct wl_listener surface_commit;
        struct wl_listener output_destroy;
+       struct wl_listener new_popup;
+       struct wl_listener new_subsurface;
 
        struct wlr_box geo;
        bool mapped;
+       /* TODO: add extent and layer */
+       struct server *server;
+};
+
+struct lab_layer_popup {
+       struct wlr_xdg_popup *wlr_popup;
+       enum layer_parent parent_type;
+       union {
+               struct lab_layer_surface *parent_layer;
+               struct lab_layer_popup *parent_popup;
+       };
+       struct wl_listener map;
+       struct wl_listener unmap;
+       struct wl_listener destroy;
+       struct wl_listener commit;
+       struct wl_listener new_popup;
+};
+
+struct lab_layer_subsurface {
+       struct wlr_subsurface *wlr_subsurface;
+       struct lab_layer_surface *layer_surface;
+
+       struct wl_listener map;
+       struct wl_listener unmap;
+       struct wl_listener destroy;
+       struct wl_listener commit;
 };
 
 void layers_init(struct server *server);
index b8a0727958d532c30c02993978a844365501f083..9d3168b63bc9b17027cfa5caa61b62769165fa6d 100644 (file)
@@ -312,6 +312,236 @@ map_notify(struct wl_listener *listener, void *data)
        wlr_surface_send_enter(l->surface, l->output);
 }
 
+static void
+subsurface_damage(struct lab_layer_subsurface *subsurface, bool whole)
+{
+       struct lab_layer_surface *layer = subsurface->layer_surface;
+       struct wlr_output *wlr_output = layer->layer_surface->output;
+       if (!wlr_output) {
+               return;
+       }
+//     struct output *output = wlr_output->data;
+//     int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
+//     int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
+       damage_all_outputs(layer->server);
+}
+
+static void
+subsurface_handle_unmap(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_subsurface *subsurface =
+                       wl_container_of(listener, subsurface, unmap);
+       subsurface_damage(subsurface, true);
+}
+
+static void
+subsurface_handle_map(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_subsurface *subsurface =
+                       wl_container_of(listener, subsurface, map);
+       subsurface_damage(subsurface, true);
+}
+
+static void
+subsurface_handle_commit(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_subsurface *subsurface =
+                       wl_container_of(listener, subsurface, commit);
+       subsurface_damage(subsurface, false);
+}
+
+static void
+subsurface_handle_destroy(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_subsurface *subsurface =
+                       wl_container_of(listener, subsurface, destroy);
+
+       wl_list_remove(&subsurface->map.link);
+       wl_list_remove(&subsurface->unmap.link);
+       wl_list_remove(&subsurface->destroy.link);
+       wl_list_remove(&subsurface->commit.link);
+       free(subsurface);
+}
+
+static struct
+lab_layer_subsurface *create_subsurface(struct wlr_subsurface *wlr_subsurface,
+               struct lab_layer_surface *layer_surface)
+{
+       struct lab_layer_subsurface *subsurface =
+                       calloc(1, sizeof(struct lab_layer_subsurface));
+       if (!subsurface) {
+               return NULL;
+       }
+
+       subsurface->wlr_subsurface = wlr_subsurface;
+       subsurface->layer_surface = layer_surface;
+
+       subsurface->map.notify = subsurface_handle_map;
+       wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
+       subsurface->unmap.notify = subsurface_handle_unmap;
+       wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
+       subsurface->destroy.notify = subsurface_handle_destroy;
+       wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
+       subsurface->commit.notify = subsurface_handle_commit;
+       wl_signal_add(&wlr_subsurface->surface->events.commit,
+               &subsurface->commit);
+
+       return subsurface;
+}
+
+static void
+new_subsurface_notify(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_surface *lab_layer_surface =
+               wl_container_of(listener, lab_layer_surface, new_subsurface);
+       struct wlr_subsurface *wlr_subsurface = data;
+       create_subsurface(wlr_subsurface, lab_layer_surface);
+}
+
+
+static struct
+lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup)
+{
+       while (popup->parent_type == LAYER_PARENT_POPUP) {
+               popup = popup->parent_popup;
+       }
+       return popup->parent_layer;
+}
+
+static void
+popup_damage(struct lab_layer_popup *layer_popup, bool whole)
+{
+       struct wlr_xdg_popup *popup = layer_popup->wlr_popup;
+//     struct wlr_surface *surface = popup->base->surface;
+       int popup_sx = popup->geometry.x - popup->base->current.geometry.x;
+       int popup_sy = popup->geometry.y - popup->base->current.geometry.y;
+       int ox = popup_sx, oy = popup_sy;
+       struct lab_layer_surface *layer;
+       while (true) {
+               if (layer_popup->parent_type == LAYER_PARENT_POPUP) {
+                       layer_popup = layer_popup->parent_popup;
+                       ox += layer_popup->wlr_popup->geometry.x;
+                       oy += layer_popup->wlr_popup->geometry.y;
+               } else {
+                       layer = layer_popup->parent_layer;
+                       ox += layer->geo.x;
+                       oy += layer->geo.y;
+                       break;
+               }
+       }
+//     struct wlr_output *wlr_output = layer->layer_surface->output;
+//     struct output *output = wlr_output->data;
+       damage_all_outputs(layer->server);
+}
+
+static void
+popup_handle_map(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_popup *popup = wl_container_of(listener, popup, map);
+       struct lab_layer_surface *layer = popup_get_layer(popup);
+       struct wlr_output *wlr_output = layer->layer_surface->output;
+       wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
+       popup_damage(popup, true);
+}
+
+static void
+popup_handle_unmap(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_popup *popup = wl_container_of(listener, popup, unmap);
+       popup_damage(popup, true);
+}
+
+static void
+popup_handle_commit(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_popup *popup = wl_container_of(listener, popup, commit);
+       popup_damage(popup, false);
+}
+
+static void
+popup_handle_destroy(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_popup *popup =
+               wl_container_of(listener, popup, destroy);
+
+       wl_list_remove(&popup->map.link);
+       wl_list_remove(&popup->unmap.link);
+       wl_list_remove(&popup->destroy.link);
+       wl_list_remove(&popup->commit.link);
+       free(popup);
+}
+
+static void
+popup_unconstrain(struct lab_layer_popup *popup)
+{
+       struct lab_layer_surface *layer = popup_get_layer(popup);
+       struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
+       struct output *output = layer->layer_surface->output->data;
+
+       struct wlr_box output_box = { 0 };
+       wlr_output_effective_resolution(output->wlr_output, &output_box.width,
+               &output_box.height);
+
+       struct wlr_box output_toplevel_sx_box = {
+               .x = -layer->geo.x,
+               .y = -layer->geo.y,
+               .width = output_box.width,
+               .height = output_box.height,
+       };
+
+       wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
+}
+
+static void popup_handle_new_popup(struct wl_listener *listener, void *data);
+
+static struct lab_layer_popup *
+create_popup(struct wlr_xdg_popup *wlr_popup,
+               enum layer_parent parent_type, void *parent)
+{
+       struct lab_layer_popup *popup =
+               calloc(1, sizeof(struct lab_layer_popup));
+       if (!popup) {
+               return NULL;
+       }
+
+       popup->wlr_popup = wlr_popup;
+       popup->parent_type = parent_type;
+       popup->parent_layer = parent;
+
+       popup->map.notify = popup_handle_map;
+       wl_signal_add(&wlr_popup->base->events.map, &popup->map);
+       popup->unmap.notify = popup_handle_unmap;
+       wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
+       popup->destroy.notify = popup_handle_destroy;
+       wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
+       popup->commit.notify = popup_handle_commit;
+       wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
+       popup->new_popup.notify = popup_handle_new_popup;
+       wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
+
+       popup_unconstrain(popup);
+
+       return popup;
+}
+
+static void
+popup_handle_new_popup(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_popup *lab_layer_popup =
+               wl_container_of(listener, lab_layer_popup, new_popup);
+       struct wlr_xdg_popup *wlr_popup = data;
+       create_popup(wlr_popup, LAYER_PARENT_POPUP, lab_layer_popup);
+}
+
+static void
+new_popup_notify(struct wl_listener *listener, void *data)
+{
+       struct lab_layer_surface *lab_layer_surface =
+               wl_container_of(listener, lab_layer_surface, new_popup);
+       struct wlr_xdg_popup *wlr_popup = data;
+       create_popup(wlr_popup, LAYER_PARENT_LAYER, lab_layer_surface);
+}
+
 static void
 new_layer_surface_notify(struct wl_listener *listener, void *data)
 {
@@ -326,26 +556,16 @@ new_layer_surface_notify(struct wl_listener *listener, void *data)
                layer_surface->output = output;
        }
 
-       struct output *output =
-               output_from_wlr_output(server, layer_surface->output);
-
        struct lab_layer_surface *surface =
                calloc(1, sizeof(struct lab_layer_surface));
        if (!surface) {
                return;
        }
-       surface->layer_surface = layer_surface;
-       layer_surface->data = surface;
-       surface->server = server;
 
        surface->surface_commit.notify = surface_commit_notify;
        wl_signal_add(&layer_surface->surface->events.commit,
                &surface->surface_commit);
 
-       surface->output_destroy.notify = output_destroy_notify;
-       wl_signal_add(&layer_surface->output->events.destroy,
-               &surface->output_destroy);
-
        surface->destroy.notify = destroy_notify;
        wl_signal_add(&layer_surface->events.destroy, &surface->destroy);
 
@@ -355,6 +575,27 @@ new_layer_surface_notify(struct wl_listener *listener, void *data)
        surface->unmap.notify = unmap_notify;
        wl_signal_add(&layer_surface->events.unmap, &surface->unmap);
 
+       surface->new_popup.notify = new_popup_notify;
+       wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
+
+       surface->new_subsurface.notify = new_subsurface_notify;
+       wl_signal_add(&layer_surface->surface->events.new_subsurface,
+               &surface->new_subsurface);
+
+       surface->layer_surface = layer_surface;
+       layer_surface->data = surface;
+       surface->server = server;
+
+       struct output *output = layer_surface->output->data;
+       surface->output_destroy.notify = output_destroy_notify;
+       wl_signal_add(&layer_surface->output->events.destroy,
+               &surface->output_destroy);
+
+       if (!output) {
+               wlr_log(WLR_ERROR, "no output for layer");
+               return;
+       }
+
        wl_list_insert(&output->layers[layer_surface->pending.layer],
                &surface->link);
        /*
index e94db2410ec114cc827c172bcfed16014f9c63c3..0e80fa6782513b38808bbbf9a2645400a441142c 100644 (file)
@@ -29,7 +29,9 @@ struct surface_iterator_data {
        surface_iterator_func_t user_iterator;
        void *user_data;
        struct output *output;
+       struct view *view;
        double ox, oy;
+       int width, height;
 };
 
 static bool
@@ -290,10 +292,19 @@ output_layer_for_each_surface(struct output *output,
        wl_list_for_each(layer_surface, layer_surfaces, link) {
                struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
                        layer_surface->layer_surface;
-               output_surface_for_each_surface(output,
-                       wlr_layer_surface_v1->surface, layer_surface->geo.x,
-                       layer_surface->geo.y, iterator, user_data);
-               /* TODO: handle popups */
+               struct wlr_surface *surface = wlr_layer_surface_v1->surface;
+               struct surface_iterator_data data = {
+                       .user_iterator = iterator,
+                       .user_data = user_data,
+                       .output = output,
+                       .view = NULL,
+                       .ox = layer_surface->geo.x,
+                       .oy = layer_surface->geo.y,
+                       .width = surface->current.width,
+                       .height = surface->current.height,
+               };
+               wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
+                       output_for_each_surface_iterator, &data);
        }
 }
 
@@ -636,6 +647,43 @@ render_rootmenu(struct output *output, pixman_region32_t *output_damage)
        }
 }
 
+static void
+output_layer_for_each_popup_surface(struct output *output,
+               struct wl_list *layer_surfaces,
+               surface_iterator_func_t iterator,
+               void *user_data)
+{
+       struct lab_layer_surface *layer_surface;
+       wl_list_for_each(layer_surface, layer_surfaces, link) {
+               struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
+                       layer_surface->layer_surface;
+               struct wlr_surface *surface = wlr_layer_surface_v1->surface;
+               struct surface_iterator_data data = {
+                       .user_iterator = iterator,
+                       .user_data = user_data,
+                       .output = output,
+                       .view = NULL,
+                       .ox = layer_surface->geo.x,
+                       .oy = layer_surface->geo.y,
+                       .width = surface->current.width,
+                       .height = surface->current.height,
+               };
+               wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
+                       output_for_each_surface_iterator, &data);
+       }
+}
+
+static void
+render_layer_popups(struct output *output, pixman_region32_t *damage,
+               struct wl_list *layer_surfaces)
+{
+       struct render_data data = {
+               .damage = damage,
+       };
+       output_layer_for_each_popup_surface(output, layer_surfaces,
+               render_surface_iterator, &data);
+}
+
 void
 output_layer_for_each_surface_toplevel(struct output *output,
                struct wl_list *layer_surfaces, surface_iterator_func_t iterator,
@@ -749,6 +797,13 @@ output_render(struct output *output, pixman_region32_t *damage)
 
        render_layer_toplevel(output, damage,
                &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+       render_layer_popups(output, damage,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
+       render_layer_popups(output, damage,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
+       render_layer_popups(output, damage,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+
        render_layer_toplevel(output, damage,
                &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
 
@@ -915,6 +970,7 @@ new_output_notify(struct wl_listener *listener, void *data)
 
        struct output *output = calloc(1, sizeof(struct output));
        output->wlr_output = wlr_output;
+       wlr_output->data = output;
        output->server = server;
        output->damage = wlr_output_damage_create(wlr_output);
        wlr_output_effective_resolution(wlr_output,