aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop/xdg_shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop/xdg_shell.c')
-rw-r--r--sway/desktop/xdg_shell.c252
1 files changed, 149 insertions, 103 deletions
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 8da922d5..7c417891 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,7 +6,7 @@
7#include <wlr/util/edges.h> 6#include <wlr/util/edges.h>
8#include "log.h" 7#include "log.h"
9#include "sway/decoration.h" 8#include "sway/decoration.h"
10#include "sway/desktop.h" 9#include "sway/scene_descriptor.h"
11#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
12#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
13#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
@@ -19,55 +18,39 @@
19#include "sway/tree/workspace.h" 18#include "sway/tree/workspace.h"
20#include "sway/xdg_decoration.h" 19#include "sway/xdg_decoration.h"
21 20
22static const struct sway_view_child_impl popup_impl;
23
24static void popup_get_view_coords(struct sway_view_child *child,
25 int *sx, int *sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
28
29 wlr_xdg_popup_get_toplevel_coords(wlr_popup,
30 wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x,
31 wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y,
32 sx, sy);
33}
34
35static void popup_destroy(struct sway_view_child *child) {
36 if (!sway_assert(child->impl == &popup_impl,
37 "Expected an xdg_shell popup")) {
38 return;
39 }
40 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
41 wl_list_remove(&popup->new_popup.link);
42 wl_list_remove(&popup->destroy.link);
43 free(popup);
44}
45
46static const struct sway_view_child_impl popup_impl = {
47 .get_view_coords = popup_get_view_coords,
48 .destroy = popup_destroy,
49};
50
51static struct sway_xdg_popup *popup_create( 21static struct sway_xdg_popup *popup_create(
52 struct wlr_xdg_popup *wlr_popup, struct sway_view *view); 22 struct wlr_xdg_popup *wlr_popup, struct sway_view *view,
23 struct wlr_scene_tree *parent);
53 24
54static void popup_handle_new_popup(struct wl_listener *listener, void *data) { 25static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
55 struct sway_xdg_popup *popup = 26 struct sway_xdg_popup *popup =
56 wl_container_of(listener, popup, new_popup); 27 wl_container_of(listener, popup, new_popup);
57 struct wlr_xdg_popup *wlr_popup = data; 28 struct wlr_xdg_popup *wlr_popup = data;
58 popup_create(wlr_popup, popup->child.view); 29 popup_create(wlr_popup, popup->view, popup->xdg_surface_tree);
59} 30}
60 31
61static void popup_handle_destroy(struct wl_listener *listener, void *data) { 32static void popup_handle_destroy(struct wl_listener *listener, void *data) {
62 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); 33 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
63 view_child_destroy(&popup->child); 34
35 wl_list_remove(&popup->new_popup.link);
36 wl_list_remove(&popup->destroy.link);
37 wl_list_remove(&popup->surface_commit.link);
38 wl_list_remove(&popup->reposition.link);
39 wlr_scene_node_destroy(&popup->scene_tree->node);
40 free(popup);
64} 41}
65 42
66static void popup_unconstrain(struct sway_xdg_popup *popup) { 43static void popup_unconstrain(struct sway_xdg_popup *popup) {
67 struct sway_view *view = popup->child.view; 44 struct sway_view *view = popup->view;
68 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; 45 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
69 46
70 struct sway_output *output = view->container->pending.workspace->output; 47 struct sway_workspace *workspace = view->container->pending.workspace;
48 if (!workspace) {
49 // is null if in the scratchpad
50 return;
51 }
52
53 struct sway_output *output = workspace->output;
71 54
72 // the output box expressed in the coordinate system of the toplevel parent 55 // the output box expressed in the coordinate system of the toplevel parent
73 // of the popup 56 // of the popup
@@ -81,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
81 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 64 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
82} 65}
83 66
84static struct sway_xdg_popup *popup_create( 67static void popup_handle_surface_commit(struct wl_listener *listener, void *data) {
85 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { 68 struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit);
69 if (popup->wlr_xdg_popup->base->initial_commit) {
70 popup_unconstrain(popup);
71 }
72}
73
74static void popup_handle_reposition(struct wl_listener *listener, void *data) {
75 struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition);
76 popup_unconstrain(popup);
77}
78
79static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,
80 struct sway_view *view, struct wlr_scene_tree *parent) {
86 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 81 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
87 82
88 struct sway_xdg_popup *popup = 83 struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));
89 calloc(1, sizeof(struct sway_xdg_popup)); 84 if (!popup) {
90 if (popup == NULL) { 85 return NULL;
86 }
87
88 popup->wlr_xdg_popup = wlr_popup;
89 popup->view = view;
90
91 popup->scene_tree = wlr_scene_tree_create(parent);
92 if (!popup->scene_tree) {
93 free(popup);
91 return NULL; 94 return NULL;
92 } 95 }
93 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 96
97 popup->xdg_surface_tree = wlr_scene_xdg_surface_create(
98 popup->scene_tree, xdg_surface);
99 if (!popup->xdg_surface_tree) {
100 wlr_scene_node_destroy(&popup->scene_tree->node);
101 free(popup);
102 return NULL;
103 }
104
105 popup->desc.relative = &view->content_tree->node;
106 popup->desc.view = view;
107
108 if (!scene_descriptor_assign(&popup->scene_tree->node,
109 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
110 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
111 wlr_scene_node_destroy(&popup->scene_tree->node);
112 free(popup);
113 return NULL;
114 }
115
94 popup->wlr_xdg_popup = xdg_surface->popup; 116 popup->wlr_xdg_popup = xdg_surface->popup;
117 struct sway_xdg_shell_view *shell_view =
118 wl_container_of(view, shell_view, view);
119 xdg_surface->data = shell_view;
95 120
121 wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);
122 popup->surface_commit.notify = popup_handle_surface_commit;
96 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 123 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
97 popup->new_popup.notify = popup_handle_new_popup; 124 popup->new_popup.notify = popup_handle_new_popup;
98 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 125 wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
126 popup->reposition.notify = popup_handle_reposition;
127 wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
99 popup->destroy.notify = popup_handle_destroy; 128 popup->destroy.notify = popup_handle_destroy;
100 129
101 wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map);
102 wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap);
103
104 popup_unconstrain(popup);
105
106 return popup; 130 return popup;
107} 131}
108 132
109
110static struct sway_xdg_shell_view *xdg_shell_view_from_view( 133static struct sway_xdg_shell_view *xdg_shell_view_from_view(
111 struct sway_view *view) { 134 struct sway_view *view) {
112 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, 135 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,
@@ -163,12 +186,19 @@ static void set_tiled(struct sway_view *view, bool tiled) {
163 if (xdg_shell_view_from_view(view) == NULL) { 186 if (xdg_shell_view_from_view(view) == NULL) {
164 return; 187 return;
165 } 188 }
166 enum wlr_edges edges = WLR_EDGE_NONE; 189 if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=
167 if (tiled) { 190 XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
168 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 191 enum wlr_edges edges = WLR_EDGE_NONE;
169 WLR_EDGE_BOTTOM; 192 if (tiled) {
193 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
194 WLR_EDGE_BOTTOM;
195 }
196 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
197 } else {
198 // The version is too low for the tiled state; configure as maximized instead
199 // to stop the client from drawing decorations outside of the toplevel geometry.
200 wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled);
170 } 201 }
171 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
172} 202}
173 203
174static void set_fullscreen(struct sway_view *view, bool fullscreen) { 204static void set_fullscreen(struct sway_view *view, bool fullscreen) {
@@ -194,24 +224,6 @@ static bool wants_floating(struct sway_view *view) {
194 || toplevel->parent; 224 || toplevel->parent;
195} 225}
196 226
197static void for_each_surface(struct sway_view *view,
198 wlr_surface_iterator_func_t iterator, void *user_data) {
199 if (xdg_shell_view_from_view(view) == NULL) {
200 return;
201 }
202 wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator,
203 user_data);
204}
205
206static void for_each_popup_surface(struct sway_view *view,
207 wlr_surface_iterator_func_t iterator, void *user_data) {
208 if (xdg_shell_view_from_view(view) == NULL) {
209 return;
210 }
211 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base,
212 iterator, user_data);
213}
214
215static bool is_transient_for(struct sway_view *child, 227static bool is_transient_for(struct sway_view *child,
216 struct sway_view *ancestor) { 228 struct sway_view *ancestor) {
217 if (xdg_shell_view_from_view(child) == NULL) { 229 if (xdg_shell_view_from_view(child) == NULL) {
@@ -259,8 +271,6 @@ static const struct sway_view_impl view_impl = {
259 .set_fullscreen = set_fullscreen, 271 .set_fullscreen = set_fullscreen,
260 .set_resizing = set_resizing, 272 .set_resizing = set_resizing,
261 .wants_floating = wants_floating, 273 .wants_floating = wants_floating,
262 .for_each_surface = for_each_surface,
263 .for_each_popup_surface = for_each_popup_surface,
264 .is_transient_for = is_transient_for, 274 .is_transient_for = is_transient_for,
265 .close = _close, 275 .close = _close,
266 .close_popups = close_popups, 276 .close_popups = close_popups,
@@ -273,6 +283,20 @@ static void handle_commit(struct wl_listener *listener, void *data) {
273 struct sway_view *view = &xdg_shell_view->view; 283 struct sway_view *view = &xdg_shell_view->view;
274 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; 284 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
275 285
286 if (xdg_surface->initial_commit) {
287 if (view->xdg_decoration != NULL) {
288 set_xdg_decoration_mode(view->xdg_decoration);
289 }
290 // XXX: https://github.com/swaywm/sway/issues/2176
291 wlr_xdg_surface_schedule_configure(xdg_surface);
292 // TODO: wlr_xdg_toplevel_set_bounds()
293 return;
294 }
295
296 if (!xdg_surface->surface->mapped) {
297 return;
298 }
299
276 struct wlr_box new_geo; 300 struct wlr_box new_geo;
277 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 301 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
278 bool new_size = new_geo.width != view->geometry.width || 302 bool new_size = new_geo.width != view->geometry.width ||
@@ -284,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) {
284 // The client changed its surface size in this commit. For floating 308 // The client changed its surface size in this commit. For floating
285 // containers, we resize the container to match. For tiling containers, 309 // containers, we resize the container to match. For tiling containers,
286 // we only recenter the surface. 310 // we only recenter the surface.
287 desktop_damage_view(view);
288 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 311 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
289 if (container_is_floating(view->container)) { 312 if (container_is_floating(view->container)) {
290 view_update_size(view); 313 view_update_size(view);
314 // Only set the toplevel size the current container actually has a size.
315 if (view->container->current.width) {
316 wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width,
317 view->geometry.height);
318 }
291 transaction_commit_dirty_client(); 319 transaction_commit_dirty_client();
292 } else {
293 view_center_surface(view);
294 } 320 }
295 desktop_damage_view(view); 321
322 view_center_and_clip_surface(view);
296 } 323 }
297 324
298 if (view->container->node.instruction) { 325 if (view->container->node.instruction) {
299 transaction_notify_view_ready_by_serial(view, 326 bool successful = transaction_notify_view_ready_by_serial(view,
300 xdg_surface->current.configure_serial); 327 xdg_surface->current.configure_serial);
301 }
302 328
303 view_damage_from(view); 329 // If we saved the view and this commit isn't what we're looking for
330 // that means the user will never actually see the buffers submitted to
331 // us here. Just send frame done events to these surfaces so they can
332 // commit another time for us.
333 if (view->saved_surface_tree && !successful) {
334 view_send_frame_done(view);
335 }
336 }
304} 337}
305 338
306static void handle_set_title(struct wl_listener *listener, void *data) { 339static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -315,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) {
315 struct sway_xdg_shell_view *xdg_shell_view = 348 struct sway_xdg_shell_view *xdg_shell_view =
316 wl_container_of(listener, xdg_shell_view, set_app_id); 349 wl_container_of(listener, xdg_shell_view, set_app_id);
317 struct sway_view *view = &xdg_shell_view->view; 350 struct sway_view *view = &xdg_shell_view->view;
351 view_update_app_id(view);
318 view_execute_criteria(view); 352 view_execute_criteria(view);
319} 353}
320 354
@@ -322,7 +356,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
322 struct sway_xdg_shell_view *xdg_shell_view = 356 struct sway_xdg_shell_view *xdg_shell_view =
323 wl_container_of(listener, xdg_shell_view, new_popup); 357 wl_container_of(listener, xdg_shell_view, new_popup);
324 struct wlr_xdg_popup *wlr_popup = data; 358 struct wlr_xdg_popup *wlr_popup = data;
325 popup_create(wlr_popup, &xdg_shell_view->view); 359
360 struct sway_xdg_popup *popup = popup_create(wlr_popup,
361 &xdg_shell_view->view, root->layers.popup);
362 if (!popup) {
363 return;
364 }
365
366 int lx, ly;
367 wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly);
368 wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);
326} 369}
327 370
328static void handle_request_maximize(struct wl_listener *listener, void *data) { 371static void handle_request_maximize(struct wl_listener *listener, void *data) {
@@ -338,7 +381,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
338 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; 381 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
339 struct sway_view *view = &xdg_shell_view->view; 382 struct sway_view *view = &xdg_shell_view->view;
340 383
341 if (!toplevel->base->mapped) { 384 if (!toplevel->base->surface->mapped) {
342 return; 385 return;
343 } 386 }
344 387
@@ -403,7 +446,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
403 446
404 view_unmap(view); 447 view_unmap(view);
405 448
406 wl_list_remove(&xdg_shell_view->commit.link);
407 wl_list_remove(&xdg_shell_view->new_popup.link); 449 wl_list_remove(&xdg_shell_view->new_popup.link);
408 wl_list_remove(&xdg_shell_view->request_maximize.link); 450 wl_list_remove(&xdg_shell_view->request_maximize.link);
409 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 451 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
@@ -446,10 +488,6 @@ static void handle_map(struct wl_listener *listener, void *data) {
446 488
447 transaction_commit_dirty(); 489 transaction_commit_dirty();
448 490
449 xdg_shell_view->commit.notify = handle_commit;
450 wl_signal_add(&toplevel->base->surface->events.commit,
451 &xdg_shell_view->commit);
452
453 xdg_shell_view->new_popup.notify = handle_new_popup; 491 xdg_shell_view->new_popup.notify = handle_new_popup;
454 wl_signal_add(&toplevel->base->events.new_popup, 492 wl_signal_add(&toplevel->base->events.new_popup,
455 &xdg_shell_view->new_popup); 493 &xdg_shell_view->new_popup);
@@ -489,6 +527,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
489 wl_list_remove(&xdg_shell_view->destroy.link); 527 wl_list_remove(&xdg_shell_view->destroy.link);
490 wl_list_remove(&xdg_shell_view->map.link); 528 wl_list_remove(&xdg_shell_view->map.link);
491 wl_list_remove(&xdg_shell_view->unmap.link); 529 wl_list_remove(&xdg_shell_view->unmap.link);
530 wl_list_remove(&xdg_shell_view->commit.link);
492 view->wlr_xdg_toplevel = NULL; 531 view->wlr_xdg_toplevel = NULL;
493 if (view->xdg_decoration) { 532 if (view->xdg_decoration) {
494 view->xdg_decoration->view = NULL; 533 view->xdg_decoration->view = NULL;
@@ -501,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface(
501 return xdg_surface->data; 540 return xdg_surface->data;
502} 541}
503 542
504void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 543void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
505 struct wlr_xdg_surface *xdg_surface = data; 544 struct wlr_xdg_toplevel *xdg_toplevel = data;
506
507 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
508 sway_log(SWAY_DEBUG, "New xdg_shell popup");
509 return;
510 }
511 545
512 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 546 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
513 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 547 xdg_toplevel->title, xdg_toplevel->app_id);
514 wlr_xdg_surface_ping(xdg_surface); 548 wlr_xdg_surface_ping(xdg_toplevel->base);
515 549
516 struct sway_xdg_shell_view *xdg_shell_view = 550 struct sway_xdg_shell_view *xdg_shell_view =
517 calloc(1, sizeof(struct sway_xdg_shell_view)); 551 calloc(1, sizeof(struct sway_xdg_shell_view));
@@ -519,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
519 return; 553 return;
520 } 554 }
521 555
522 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 556 if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) {
523 xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; 557 free(xdg_shell_view);
558 return;
559 }
560 xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
524 561
525 xdg_shell_view->map.notify = handle_map; 562 xdg_shell_view->map.notify = handle_map;
526 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 563 wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map);
527 564
528 xdg_shell_view->unmap.notify = handle_unmap; 565 xdg_shell_view->unmap.notify = handle_unmap;
529 wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); 566 wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
567
568 xdg_shell_view->commit.notify = handle_commit;
569 wl_signal_add(&xdg_toplevel->base->surface->events.commit,
570 &xdg_shell_view->commit);
530 571
531 xdg_shell_view->destroy.notify = handle_destroy; 572 xdg_shell_view->destroy.notify = handle_destroy;
532 wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); 573 wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy);
574
575 wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);
576
577 xdg_toplevel->base->data = xdg_shell_view;
533 578
534 xdg_surface->data = xdg_shell_view; 579 wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel,
580 XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
535} 581}