aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2019-07-05 14:45:11 -0400
committerSimon Ser <contact@emersion.fr>2019-08-14 22:10:05 +0300
commitcb8f68d74b6c9f0b9690c44a34b8a8f1c46986be (patch)
treea49f7022c65a5a11f6fbdb4c164ac3a4c9b3c2cd
parentf36240208c57124a88cdee18fb81fa3fc69e0f0a (diff)
layer-shell: add support for popups
-rw-r--r--include/sway/layers.h20
-rw-r--r--sway/desktop/layer_shell.c130
-rw-r--r--sway/desktop/output.c30
-rw-r--r--sway/input/cursor.c11
4 files changed, 182 insertions, 9 deletions
diff --git a/include/sway/layers.h b/include/sway/layers.h
index 51878fc9..01d066d5 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -5,6 +5,11 @@
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_layer_shell_v1.h>
+enum layer_parent {
+ LAYER_PARENT_LAYER,
+ LAYER_PARENT_POPUP,
+};
+
struct sway_layer_surface {
struct wlr_layer_surface_v1 *layer_surface;
struct wl_list link;
@@ -14,11 +19,26 @@ struct sway_layer_surface {
struct wl_listener unmap;
struct wl_listener surface_commit;
struct wl_listener output_destroy;
+ struct wl_listener new_popup;
bool configured;
struct wlr_box geo;
};
+struct sway_layer_popup {
+ struct wlr_xdg_popup *wlr_popup;
+ enum layer_parent parent_type;
+ union {
+ struct sway_layer_surface *parent_layer;
+ struct sway_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 sway_output;
void arrange_layers(struct sway_output *output);
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index 34ba5592..f1f79c1b 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -320,6 +320,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&sway_layer->map.link);
wl_list_remove(&sway_layer->unmap.link);
wl_list_remove(&sway_layer->surface_commit.link);
+ wl_list_remove(&sway_layer->new_popup.link);
if (sway_layer->layer_surface->output != NULL) {
struct sway_output *output = sway_layer->layer_surface->output->data;
if (output != NULL) {
@@ -338,7 +339,6 @@ static void handle_map(struct wl_listener *listener, void *data) {
struct sway_output *output = sway_layer->layer_surface->output->data;
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
sway_layer->layer_surface->surface, true);
- // TODO: send enter to subsurfaces and popups
wlr_surface_send_enter(sway_layer->layer_surface->surface,
sway_layer->layer_surface->output);
cursor_rebase_all();
@@ -350,6 +350,131 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
unmap(sway_layer);
}
+static struct sway_layer_surface *popup_get_layer(
+ struct sway_layer_popup *popup) {
+ while (popup->parent_type == LAYER_PARENT_POPUP) {
+ popup = popup->parent_popup;
+ }
+ return popup->parent_layer;
+}
+
+static void popup_damage(struct sway_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->geometry.x;
+ int popup_sy = popup->geometry.y - popup->base->geometry.y;
+ int ox = popup_sx, oy = popup_sy;
+ struct sway_layer_surface *layer;
+ while (true) {
+ if (layer_popup->parent_type == LAYER_PARENT_POPUP) {
+ layer_popup = layer_popup->parent_popup;
+ ox += layer_popup->wlr_popup->base->geometry.x +
+ layer_popup->wlr_popup->geometry.x;
+ oy += layer_popup->wlr_popup->base->geometry.y +
+ 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 sway_output *output = wlr_output->data;
+ output_damage_surface(output, ox, oy, surface, whole);
+}
+
+static void popup_handle_map(struct wl_listener *listener, void *data) {
+ struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
+ struct sway_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 sway_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 sway_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 sway_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 sway_layer_popup *popup) {
+ struct sway_layer_surface *layer = popup_get_layer(popup);
+ struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
+
+ struct sway_output *output = layer->layer_surface->output->data;
+
+ // the output box expressed in the coordinate system of the toplevel parent
+ // of the popup
+ struct wlr_box output_toplevel_sx_box = {
+ .x = -layer->geo.x,
+ .y = -layer->geo.y,
+ .width = output->width,
+ .height = output->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 sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup,
+ enum layer_parent parent_type, void *parent) {
+ struct sway_layer_popup *popup =
+ calloc(1, sizeof(struct sway_layer_popup));
+ if (popup == NULL) {
+ 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 sway_layer_popup *sway_layer_popup =
+ wl_container_of(listener, sway_layer_popup, new_popup);
+ struct wlr_xdg_popup *wlr_popup = data;
+ create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup);
+}
+
+static void handle_new_popup(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer_surface =
+ wl_container_of(listener, sway_layer_surface, new_popup);
+ struct wlr_xdg_popup *wlr_popup = data;
+ create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface);
+}
+
struct sway_layer_surface *layer_from_wlr_layer_surface_v1(
struct wlr_layer_surface_v1 *layer_surface) {
return layer_surface->data;
@@ -406,7 +531,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
wl_signal_add(&layer_surface->events.map, &sway_layer->map);
sway_layer->unmap.notify = handle_unmap;
wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
- // TODO: Listen for subsurfaces
+ sway_layer->new_popup.notify = handle_new_popup;
+ wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup);
sway_layer->layer_surface = layer_surface;
layer_surface->data = sway_layer;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 7dcc8e51..1f3cc938 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -204,6 +204,36 @@ void output_layer_for_each_surface(struct sway_output *output,
output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
layer_surface->geo.x, layer_surface->geo.y, iterator,
user_data);
+
+ struct wlr_xdg_popup *state;
+ wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) {
+ struct wlr_xdg_surface *popup = state->base;
+ if (!popup->configured) {
+ continue;
+ }
+
+ double popup_sx, popup_sy;
+ popup_sx = layer_surface->geo.x +
+ popup->popup->geometry.x - popup->geometry.x;
+ popup_sy = layer_surface->geo.y +
+ popup->popup->geometry.y - popup->geometry.y;
+
+ struct wlr_surface *surface = popup->surface;
+
+ struct surface_iterator_data data = {
+ .user_iterator = iterator,
+ .user_data = user_data,
+ .output = output,
+ .ox = popup_sx,
+ .oy = popup_sy,
+ .width = surface->current.width,
+ .height = surface->current.height,
+ .rotation = 0,
+ };
+
+ wlr_xdg_surface_for_each_surface(
+ popup, output_for_each_surface_iterator, &data);
+ }
}
}
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 5739aafb..c6a332b8 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -39,15 +39,12 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
struct sway_layer_surface *sway_layer;
wl_list_for_each_reverse(sway_layer, layer, link) {
- struct wlr_surface *wlr_surface =
- sway_layer->layer_surface->surface;
double _sx = ox - sway_layer->geo.x;
double _sy = oy - sway_layer->geo.y;
- // TODO: Test popups/subsurfaces
- if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) {
- *sx = _sx;
- *sy = _sy;
- return wlr_surface;
+ struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
+ sway_layer->layer_surface, _sx, _sy, sx, sy);
+ if (sub) {
+ return sub;
}
}
return NULL;