aboutsummaryrefslogtreecommitdiffstats
path: root/sway/tree
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/arrange.c15
-rw-r--r--sway/tree/container.c1310
-rw-r--r--sway/tree/node.c38
-rw-r--r--sway/tree/output.c98
-rw-r--r--sway/tree/root.c273
-rw-r--r--sway/tree/view.c593
-rw-r--r--sway/tree/workspace.c53
7 files changed, 1102 insertions, 1278 deletions
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 9c1a11e5..d4003fe6 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -264,6 +263,9 @@ void arrange_workspace(struct sway_workspace *workspace) {
264 area->width, area->height, area->x, area->y); 263 area->width, area->height, area->x, area->y);
265 264
266 bool first_arrange = workspace->width == 0 && workspace->height == 0; 265 bool first_arrange = workspace->width == 0 && workspace->height == 0;
266 struct wlr_box prev_box;
267 workspace_get_box(workspace, &prev_box);
268
267 double prev_x = workspace->x - workspace->current_gaps.left; 269 double prev_x = workspace->x - workspace->current_gaps.left;
268 double prev_y = workspace->y - workspace->current_gaps.top; 270 double prev_y = workspace->y - workspace->current_gaps.top;
269 workspace->width = area->width; 271 workspace->width = area->width;
@@ -277,13 +279,14 @@ void arrange_workspace(struct sway_workspace *workspace) {
277 if (!first_arrange && (diff_x != 0 || diff_y != 0)) { 279 if (!first_arrange && (diff_x != 0 || diff_y != 0)) {
278 for (int i = 0; i < workspace->floating->length; ++i) { 280 for (int i = 0; i < workspace->floating->length; ++i) {
279 struct sway_container *floater = workspace->floating->items[i]; 281 struct sway_container *floater = workspace->floating->items[i];
280 container_floating_translate(floater, diff_x, diff_y);
281 double center_x = floater->pending.x + floater->pending.width / 2;
282 double center_y = floater->pending.y + floater->pending.height / 2;
283 struct wlr_box workspace_box; 282 struct wlr_box workspace_box;
284 workspace_get_box(workspace, &workspace_box); 283 workspace_get_box(workspace, &workspace_box);
285 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 284 floating_fix_coordinates(floater, &prev_box, &workspace_box);
286 container_floating_move_to_center(floater); 285 // Set transformation for scratchpad windows.
286 if (floater->scratchpad) {
287 struct wlr_box output_box;
288 output_get_box(output, &output_box);
289 floater->transform = output_box;
287 } 290 }
288 } 291 }
289 } 292 }
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 09766ce5..80ef34fe 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,25 +1,20 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdint.h> 3#include <stdint.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <string.h>
7#include <strings.h>
8#include <sys/stat.h>
9#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
10#include <wlr/types/wlr_linux_dmabuf_v1.h> 7#include <wlr/types/wlr_linux_dmabuf_v1.h>
11#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
13#include <wlr/render/drm_format_set.h>
14#include "linux-dmabuf-unstable-v1-protocol.h" 10#include "linux-dmabuf-unstable-v1-protocol.h"
15#include "cairo_util.h"
16#include "pango.h"
17#include "sway/config.h" 11#include "sway/config.h"
18#include "sway/desktop.h"
19#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
20#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
21#include "sway/input/seat.h" 14#include "sway/input/seat.h"
22#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/scene_descriptor.h"
17#include "sway/sway_text_node.h"
23#include "sway/output.h" 18#include "sway/output.h"
24#include "sway/server.h" 19#include "sway/server.h"
25#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
@@ -30,6 +25,53 @@
30#include "log.h" 25#include "log.h"
31#include "stringop.h" 26#include "stringop.h"
32 27
28static void handle_output_enter(
29 struct wl_listener *listener, void *data) {
30 struct sway_container *con = wl_container_of(
31 listener, con, output_enter);
32 struct wlr_scene_output *output = data;
33
34 if (con->view->foreign_toplevel) {
35 wlr_foreign_toplevel_handle_v1_output_enter(
36 con->view->foreign_toplevel, output->output);
37 }
38}
39
40static void handle_output_leave(
41 struct wl_listener *listener, void *data) {
42 struct sway_container *con = wl_container_of(
43 listener, con, output_leave);
44 struct wlr_scene_output *output = data;
45
46 if (con->view->foreign_toplevel) {
47 wlr_foreign_toplevel_handle_v1_output_leave(
48 con->view->foreign_toplevel, output->output);
49 }
50}
51
52static bool handle_point_accepts_input(
53 struct wlr_scene_buffer *buffer, double *x, double *y) {
54 return false;
55}
56
57static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent,
58 bool *failed) {
59 if (*failed) {
60 return NULL;
61 }
62
63 // just pass in random values. These will be overwritten when
64 // they need to be used.
65 struct wlr_scene_rect *rect = wlr_scene_rect_create(
66 parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});
67 if (!rect) {
68 sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect");
69 *failed = true;
70 }
71
72 return rect;
73}
74
33struct sway_container *container_create(struct sway_view *view) { 75struct sway_container *container_create(struct sway_view *view) {
34 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 76 struct sway_container *c = calloc(1, sizeof(struct sway_container));
35 if (!c) { 77 if (!c) {
@@ -37,23 +79,415 @@ struct sway_container *container_create(struct sway_view *view) {
37 return NULL; 79 return NULL;
38 } 80 }
39 node_init(&c->node, N_CONTAINER, c); 81 node_init(&c->node, N_CONTAINER, c);
40 c->pending.layout = L_NONE; 82
41 c->view = view; 83 // Container tree structure
42 c->alpha = 1.0f; 84 // - scene tree
85 // - title bar
86 // - border
87 // - background
88 // - title text
89 // - marks text
90 // - border
91 // - border top/bottom/left/right
92 // - content_tree (we put the content node here so when we disable the
93 // border everything gets disabled. We only render the content iff there
94 // is a border as well)
95 // - buffer used for output enter/leave events for foreign_toplevel
96 bool failed = false;
97 c->scene_tree = alloc_scene_tree(root->staging, &failed);
98
99 c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed);
100 c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed);
101 c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed);
102
103 // for opacity purposes we need to carfully create the scene such that
104 // none of our rect nodes as well as text buffers don't overlap. To do
105 // this we have to create rects such that they go around text buffers
106 for (int i = 0; i < 4; i++) {
107 alloc_rect_node(c->title_bar.border, &failed);
108 }
109
110 for (int i = 0; i < 5; i++) {
111 alloc_rect_node(c->title_bar.background, &failed);
112 }
113
114 c->border.tree = alloc_scene_tree(c->scene_tree, &failed);
115 c->content_tree = alloc_scene_tree(c->border.tree, &failed);
116
117 if (view) {
118 // only containers with views can have borders
119 c->border.top = alloc_rect_node(c->border.tree, &failed);
120 c->border.bottom = alloc_rect_node(c->border.tree, &failed);
121 c->border.left = alloc_rect_node(c->border.tree, &failed);
122 c->border.right = alloc_rect_node(c->border.tree, &failed);
123
124 c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL);
125 if (!c->output_handler) {
126 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
127 failed = true;
128 }
129
130 if (!failed) {
131 c->output_enter.notify = handle_output_enter;
132 wl_signal_add(&c->output_handler->events.output_enter,
133 &c->output_enter);
134 c->output_leave.notify = handle_output_leave;
135 wl_signal_add(&c->output_handler->events.output_leave,
136 &c->output_leave);
137 c->output_handler->point_accepts_input = handle_point_accepts_input;
138 }
139 }
140
141 if (!failed && !scene_descriptor_assign(&c->scene_tree->node,
142 SWAY_SCENE_DESC_CONTAINER, c)) {
143 failed = true;
144 }
145
146 if (failed) {
147 wlr_scene_node_destroy(&c->scene_tree->node);
148 free(c);
149 return NULL;
150 }
43 151
44 if (!view) { 152 if (!view) {
45 c->pending.children = create_list(); 153 c->pending.children = create_list();
46 c->current.children = create_list(); 154 c->current.children = create_list();
47 } 155 }
156
157 c->pending.layout = L_NONE;
158 c->view = view;
159 c->alpha = 1.0f;
48 c->marks = create_list(); 160 c->marks = create_list();
49 c->outputs = create_list();
50 161
51 wl_signal_init(&c->events.destroy); 162 wl_signal_init(&c->events.destroy);
52 wl_signal_emit(&root->events.new_node, &c->node); 163 wl_signal_emit_mutable(&root->events.new_node, &c->node);
164
165 container_update(c);
53 166
54 return c; 167 return c;
55} 168}
56 169
170static bool container_is_focused(struct sway_container *con, void *data) {
171 return con->current.focused;
172}
173
174static bool container_has_focused_child(struct sway_container *con) {
175 return container_find_child(con, container_is_focused, NULL);
176}
177
178static bool container_is_current_parent_focused(struct sway_container *con) {
179 if (con->current.parent) {
180 struct sway_container *parent = con->current.parent;
181 return parent->current.focused || container_is_current_parent_focused(parent);
182 } else if (con->current.workspace) {
183 struct sway_workspace *ws = con->current.workspace;
184 return ws->current.focused;
185 }
186
187 return false;
188}
189
190static struct border_colors *container_get_current_colors(
191 struct sway_container *con) {
192 struct border_colors *colors;
193
194 bool urgent = con->view ?
195 view_is_urgent(con->view) : container_has_urgent_child(con);
196 struct sway_container *active_child;
197
198 if (con->current.parent) {
199 active_child = con->current.parent->current.focused_inactive_child;
200 } else if (con->current.workspace) {
201 active_child = con->current.workspace->current.focused_inactive_child;
202 } else {
203 active_child = NULL;
204 }
205
206 if (urgent) {
207 colors = &config->border_colors.urgent;
208 } else if (con->current.focused || container_is_current_parent_focused(con)) {
209 colors = &config->border_colors.focused;
210 } else if (config->has_focused_tab_title && container_has_focused_child(con)) {
211 colors = &config->border_colors.focused_tab_title;
212 } else if (con == active_child) {
213 colors = &config->border_colors.focused_inactive;
214 } else {
215 colors = &config->border_colors.unfocused;
216 }
217
218 return colors;
219}
220
221static bool container_is_current_floating(struct sway_container *container) {
222 if (!container->current.parent && container->current.workspace &&
223 list_find(container->current.workspace->floating, container) != -1) {
224 return true;
225 }
226 if (container->scratchpad) {
227 return true;
228 }
229 return false;
230}
231
232// scene rect wants premultiplied colors
233static void scene_rect_set_color(struct wlr_scene_rect *rect,
234 const float color[4], float opacity) {
235 const float premultiplied[] = {
236 color[0] * color[3] * opacity,
237 color[1] * color[3] * opacity,
238 color[2] * color[3] * opacity,
239 color[3] * opacity,
240 };
241
242 wlr_scene_rect_set_color(rect, premultiplied);
243}
244
245void container_update(struct sway_container *con) {
246 struct border_colors *colors = container_get_current_colors(con);
247 list_t *siblings = NULL;
248 enum sway_container_layout layout = L_NONE;
249 float alpha = con->alpha;
250
251 if (con->current.parent) {
252 siblings = con->current.parent->current.children;
253 layout = con->current.parent->current.layout;
254 } else if (con->current.workspace) {
255 siblings = con->current.workspace->current.tiling;
256 layout = con->current.workspace->current.layout;
257 }
258
259 float bottom[4], right[4];
260 memcpy(bottom, colors->child_border, sizeof(bottom));
261 memcpy(right, colors->child_border, sizeof(right));
262
263 if (!container_is_current_floating(con) && siblings && siblings->length == 1) {
264 if (layout == L_HORIZ) {
265 memcpy(right, colors->indicator, sizeof(right));
266 } else if (layout == L_VERT) {
267 memcpy(bottom, colors->indicator, sizeof(bottom));
268 }
269 }
270
271 struct wlr_scene_node *node;
272 wl_list_for_each(node, &con->title_bar.border->children, link) {
273 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
274 scene_rect_set_color(rect, colors->border, alpha);
275 }
276
277 wl_list_for_each(node, &con->title_bar.background->children, link) {
278 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
279 scene_rect_set_color(rect, colors->background, alpha);
280 }
281
282 if (con->view) {
283 scene_rect_set_color(con->border.top, colors->child_border, alpha);
284 scene_rect_set_color(con->border.bottom, bottom, alpha);
285 scene_rect_set_color(con->border.left, colors->child_border, alpha);
286 scene_rect_set_color(con->border.right, right, alpha);
287 }
288
289 if (con->title_bar.title_text) {
290 sway_text_node_set_color(con->title_bar.title_text, colors->text);
291 sway_text_node_set_background(con->title_bar.title_text, colors->background);
292 }
293
294 if (con->title_bar.marks_text) {
295 sway_text_node_set_color(con->title_bar.marks_text, colors->text);
296 sway_text_node_set_background(con->title_bar.marks_text, colors->background);
297 }
298}
299
300void container_update_itself_and_parents(struct sway_container *con) {
301 container_update(con);
302
303 if (con->current.parent) {
304 container_update_itself_and_parents(con->current.parent);
305 }
306}
307
308static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) {
309 int len;
310 const pixman_box32_t *rects = pixman_region32_rectangles(region, &len);
311
312 wlr_scene_node_set_enabled(&tree->node, len > 0);
313 if (len == 0) {
314 return;
315 }
316
317 int i = 0;
318 struct wlr_scene_node *node;
319 wl_list_for_each(node, &tree->children, link) {
320 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
321 wlr_scene_node_set_enabled(&rect->node, i < len);
322
323 if (i < len) {
324 const pixman_box32_t *box = &rects[i++];
325 wlr_scene_node_set_position(&rect->node, box->x1, box->y1);
326 wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1);
327 }
328 }
329}
330
331void container_arrange_title_bar(struct sway_container *con) {
332 enum alignment title_align = config->title_align;
333 int marks_buffer_width = 0;
334 int width = con->title_width;
335 int height = container_titlebar_height();
336
337 pixman_region32_t text_area;
338 pixman_region32_init(&text_area);
339
340 if (con->title_bar.marks_text) {
341 struct sway_text_node *node = con->title_bar.marks_text;
342 marks_buffer_width = node->width;
343
344 int h_padding;
345 if (title_align == ALIGN_RIGHT) {
346 h_padding = config->titlebar_h_padding;
347 } else {
348 h_padding = width - config->titlebar_h_padding - marks_buffer_width;
349 }
350
351 h_padding = MAX(h_padding, 0);
352
353 int alloc_width = MIN((int)node->width,
354 width - h_padding - config->titlebar_h_padding);
355 alloc_width = MAX(alloc_width, 0);
356
357 sway_text_node_set_max_width(node, alloc_width);
358 wlr_scene_node_set_position(node->node,
359 h_padding, (height - node->height) >> 1);
360
361 pixman_region32_union_rect(&text_area, &text_area,
362 node->node->x, node->node->y, alloc_width, node->height);
363 }
364
365 if (con->title_bar.title_text) {
366 struct sway_text_node *node = con->title_bar.title_text;
367
368 int h_padding;
369 if (title_align == ALIGN_RIGHT) {
370 h_padding = width - config->titlebar_h_padding - node->width;
371 } else if (title_align == ALIGN_CENTER) {
372 h_padding = ((int)width - marks_buffer_width - node->width) >> 1;
373 } else {
374 h_padding = config->titlebar_h_padding;
375 }
376
377 h_padding = MAX(h_padding, 0);
378
379 int alloc_width = MIN((int) node->width,
380 width - h_padding - config->titlebar_h_padding);
381 alloc_width = MAX(alloc_width, 0);
382
383 sway_text_node_set_max_width(node, alloc_width);
384 wlr_scene_node_set_position(node->node,
385 h_padding, (height - node->height) >> 1);
386
387 pixman_region32_union_rect(&text_area, &text_area,
388 node->node->x, node->node->y, alloc_width, node->height);
389 }
390
391 // silence pixman errors
392 if (width <= 0 || height <= 0) {
393 pixman_region32_fini(&text_area);
394 return;
395 }
396
397 pixman_region32_t background, border;
398
399 int thickness = config->titlebar_border_thickness;
400 pixman_region32_init_rect(&background,
401 thickness, thickness,
402 width - thickness * 2, height - thickness * 2);
403 pixman_region32_init_rect(&border, 0, 0, width, height);
404 pixman_region32_subtract(&border, &border, &background);
405
406 pixman_region32_subtract(&background, &background, &text_area);
407 pixman_region32_fini(&text_area);
408
409 update_rect_list(con->title_bar.background, &background);
410 pixman_region32_fini(&background);
411
412 update_rect_list(con->title_bar.border, &border);
413 pixman_region32_fini(&border);
414
415 container_update(con);
416}
417
418void container_update_marks(struct sway_container *con) {
419 char *buffer = NULL;
420
421 if (config->show_marks && con->marks->length) {
422 size_t len = 0;
423 for (int i = 0; i < con->marks->length; ++i) {
424 char *mark = con->marks->items[i];
425 if (mark[0] != '_') {
426 len += strlen(mark) + 2;
427 }
428 }
429 buffer = calloc(len + 1, 1);
430 char *part = malloc(len + 1);
431
432 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
433 free(buffer);
434 return;
435 }
436
437 for (int i = 0; i < con->marks->length; ++i) {
438 char *mark = con->marks->items[i];
439 if (mark[0] != '_') {
440 snprintf(part, len + 1, "[%s]", mark);
441 strcat(buffer, part);
442 }
443 }
444 free(part);
445 }
446
447 if (!buffer) {
448 if (con->title_bar.marks_text) {
449 wlr_scene_node_destroy(con->title_bar.marks_text->node);
450 con->title_bar.marks_text = NULL;
451 }
452 } else if (!con->title_bar.marks_text) {
453 struct border_colors *colors = container_get_current_colors(con);
454
455 con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree,
456 buffer, colors->text, false);
457 } else {
458 sway_text_node_set_text(con->title_bar.marks_text, buffer);
459 }
460
461 container_arrange_title_bar(con);
462 free(buffer);
463}
464
465void container_update_title_bar(struct sway_container *con) {
466 if (!con->formatted_title) {
467 return;
468 }
469
470 struct border_colors *colors = container_get_current_colors(con);
471
472 if (con->title_bar.title_text) {
473 wlr_scene_node_destroy(con->title_bar.title_text->node);
474 con->title_bar.title_text = NULL;
475 }
476
477 con->title_bar.title_text = sway_text_node_create(con->title_bar.tree,
478 con->formatted_title, colors->text, config->pango_markup);
479
480 // we always have to remake these text buffers completely for text font
481 // changes etc...
482 if (con->title_bar.marks_text) {
483 wlr_scene_node_destroy(con->title_bar.marks_text->node);
484 con->title_bar.marks_text = NULL;
485 }
486
487 container_update_marks(con);
488 container_arrange_title_bar(con);
489}
490
57void container_destroy(struct sway_container *con) { 491void container_destroy(struct sway_container *con) {
58 if (!sway_assert(con->node.destroying, 492 if (!sway_assert(con->node.destroying,
59 "Tried to free container which wasn't marked as destroying")) { 493 "Tried to free container which wasn't marked as destroying")) {
@@ -65,29 +499,21 @@ void container_destroy(struct sway_container *con) {
65 } 499 }
66 free(con->title); 500 free(con->title);
67 free(con->formatted_title); 501 free(con->formatted_title);
68 wlr_texture_destroy(con->title_focused);
69 wlr_texture_destroy(con->title_focused_inactive);
70 wlr_texture_destroy(con->title_unfocused);
71 wlr_texture_destroy(con->title_urgent);
72 wlr_texture_destroy(con->title_focused_tab_title);
73 list_free(con->pending.children); 502 list_free(con->pending.children);
74 list_free(con->current.children); 503 list_free(con->current.children);
75 list_free(con->outputs);
76 504
77 list_free_items_and_destroy(con->marks); 505 list_free_items_and_destroy(con->marks);
78 wlr_texture_destroy(con->marks_focused);
79 wlr_texture_destroy(con->marks_focused_inactive);
80 wlr_texture_destroy(con->marks_unfocused);
81 wlr_texture_destroy(con->marks_urgent);
82 wlr_texture_destroy(con->marks_focused_tab_title);
83 506
84 if (con->view && con->view->container == con) { 507 if (con->view && con->view->container == con) {
85 con->view->container = NULL; 508 con->view->container = NULL;
509 wlr_scene_node_destroy(&con->output_handler->node);
86 if (con->view->destroying) { 510 if (con->view->destroying) {
87 view_destroy(con->view); 511 view_destroy(con->view);
88 } 512 }
89 } 513 }
90 514
515 scene_node_disown_children(con->content_tree);
516 wlr_scene_node_destroy(&con->scene_tree->node);
91 free(con); 517 free(con);
92} 518}
93 519
@@ -104,7 +530,7 @@ void container_begin_destroy(struct sway_container *con) {
104 container_fullscreen_disable(con); 530 container_fullscreen_disable(con);
105 } 531 }
106 532
107 wl_signal_emit(&con->node.events.destroy, &con->node); 533 wl_signal_emit_mutable(&con->node.events.destroy, &con->node);
108 534
109 container_end_mouse_operation(con); 535 container_end_mouse_operation(con);
110 536
@@ -174,265 +600,6 @@ struct sway_container *container_find_child(struct sway_container *container,
174 return NULL; 600 return NULL;
175} 601}
176 602
177static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly,
178 struct wlr_surface **surface, double *sx, double *sy) {
179 if (!sway_assert(con->view, "Expected a view")) {
180 return NULL;
181 }
182 struct sway_view *view = con->view;
183 double view_sx = lx - con->surface_x + view->geometry.x;
184 double view_sy = ly - con->surface_y + view->geometry.y;
185
186 double _sx, _sy;
187 struct wlr_surface *_surface = NULL;
188 switch (view->type) {
189#if HAVE_XWAYLAND
190 case SWAY_VIEW_XWAYLAND:
191 _surface = wlr_surface_surface_at(view->surface,
192 view_sx, view_sy, &_sx, &_sy);
193 break;
194#endif
195 case SWAY_VIEW_XDG_SHELL:
196 _surface = wlr_xdg_surface_surface_at(
197 view->wlr_xdg_toplevel->base,
198 view_sx, view_sy, &_sx, &_sy);
199 break;
200 }
201 if (_surface) {
202 *sx = _sx;
203 *sy = _sy;
204 *surface = _surface;
205 return con;
206 }
207 return NULL;
208}
209
210/**
211 * container_at for a container with layout L_TABBED.
212 */
213static struct sway_container *container_at_tabbed(struct sway_node *parent,
214 double lx, double ly,
215 struct wlr_surface **surface, double *sx, double *sy) {
216 struct wlr_box box;
217 node_get_box(parent, &box);
218 if (lx < box.x || lx > box.x + box.width ||
219 ly < box.y || ly > box.y + box.height) {
220 return NULL;
221 }
222 struct sway_seat *seat = input_manager_current_seat();
223 list_t *children = node_get_children(parent);
224 if (!children->length) {
225 return NULL;
226 }
227
228 // Tab titles
229 int title_height = container_titlebar_height();
230 if (ly < box.y + title_height) {
231 int tab_width = box.width / children->length;
232 int child_index = (lx - box.x) / tab_width;
233 if (child_index >= children->length) {
234 child_index = children->length - 1;
235 }
236 struct sway_container *child = children->items[child_index];
237 return child;
238 }
239
240 // Surfaces
241 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
242 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
243}
244
245/**
246 * container_at for a container with layout L_STACKED.
247 */
248static struct sway_container *container_at_stacked(struct sway_node *parent,
249 double lx, double ly,
250 struct wlr_surface **surface, double *sx, double *sy) {
251 struct wlr_box box;
252 node_get_box(parent, &box);
253 if (lx < box.x || lx > box.x + box.width ||
254 ly < box.y || ly > box.y + box.height) {
255 return NULL;
256 }
257 struct sway_seat *seat = input_manager_current_seat();
258 list_t *children = node_get_children(parent);
259
260 // Title bars
261 int title_height = container_titlebar_height();
262 if (title_height > 0) {
263 int child_index = (ly - box.y) / title_height;
264 if (child_index < children->length) {
265 struct sway_container *child = children->items[child_index];
266 return child;
267 }
268 }
269
270 // Surfaces
271 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
272 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
273}
274
275/**
276 * container_at for a container with layout L_HORIZ or L_VERT.
277 */
278static struct sway_container *container_at_linear(struct sway_node *parent,
279 double lx, double ly,
280 struct wlr_surface **surface, double *sx, double *sy) {
281 list_t *children = node_get_children(parent);
282 for (int i = 0; i < children->length; ++i) {
283 struct sway_container *child = children->items[i];
284 struct sway_container *container =
285 tiling_container_at(&child->node, lx, ly, surface, sx, sy);
286 if (container) {
287 return container;
288 }
289 }
290 return NULL;
291}
292
293static struct sway_container *floating_container_at(double lx, double ly,
294 struct wlr_surface **surface, double *sx, double *sy) {
295 // For outputs with floating containers that overhang the output bounds,
296 // those at the end of the output list appear on top of floating
297 // containers from other outputs, so iterate the list in reverse.
298 for (int i = root->outputs->length - 1; i >= 0; --i) {
299 struct sway_output *output = root->outputs->items[i];
300 for (int j = 0; j < output->workspaces->length; ++j) {
301 struct sway_workspace *ws = output->workspaces->items[j];
302 if (!workspace_is_visible(ws)) {
303 continue;
304 }
305 // Items at the end of the list are on top, so iterate the list in
306 // reverse.
307 for (int k = ws->floating->length - 1; k >= 0; --k) {
308 struct sway_container *floater = ws->floating->items[k];
309 struct sway_container *container =
310 tiling_container_at(&floater->node, lx, ly, surface, sx, sy);
311 if (container) {
312 return container;
313 }
314 }
315 }
316 }
317 return NULL;
318}
319
320static struct sway_container *view_container_content_at(struct sway_node *parent,
321 double lx, double ly,
322 struct wlr_surface **surface, double *sx, double *sy) {
323 if (!sway_assert(node_is_view(parent), "Expected a view")) {
324 return NULL;
325 }
326
327 struct sway_container *container = parent->sway_container;
328 struct wlr_box box = {
329 .x = container->pending.content_x,
330 .y = container->pending.content_y,
331 .width = container->pending.content_width,
332 .height = container->pending.content_height,
333 };
334
335 if (wlr_box_contains_point(&box, lx, ly)) {
336 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
337 return container;
338 }
339
340 return NULL;
341}
342
343static struct sway_container *view_container_at(struct sway_node *parent,
344 double lx, double ly,
345 struct wlr_surface **surface, double *sx, double *sy) {
346 if (!sway_assert(node_is_view(parent), "Expected a view")) {
347 return NULL;
348 }
349
350 struct sway_container *container = parent->sway_container;
351 struct wlr_box box = {
352 .x = container->pending.x,
353 .y = container->pending.y,
354 .width = container->pending.width,
355 .height = container->pending.height,
356 };
357
358 if (wlr_box_contains_point(&box, lx, ly)) {
359 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
360 return container;
361 }
362
363 return NULL;
364}
365
366struct sway_container *tiling_container_at(struct sway_node *parent,
367 double lx, double ly,
368 struct wlr_surface **surface, double *sx, double *sy) {
369 if (node_is_view(parent)) {
370 return view_container_at(parent, lx, ly, surface, sx, sy);
371 }
372 if (!node_get_children(parent)) {
373 return NULL;
374 }
375 switch (node_get_layout(parent)) {
376 case L_HORIZ:
377 case L_VERT:
378 return container_at_linear(parent, lx, ly, surface, sx, sy);
379 case L_TABBED:
380 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
381 case L_STACKED:
382 return container_at_stacked(parent, lx, ly, surface, sx, sy);
383 case L_NONE:
384 return NULL;
385 }
386 return NULL;
387}
388
389static bool surface_is_popup(struct wlr_surface *surface) {
390 while (!wlr_surface_is_xdg_surface(surface)) {
391 if (!wlr_surface_is_subsurface(surface)) {
392 return false;
393 }
394 struct wlr_subsurface *subsurface =
395 wlr_subsurface_from_wlr_surface(surface);
396 surface = subsurface->parent;
397 }
398 struct wlr_xdg_surface *xdg_surface =
399 wlr_xdg_surface_from_wlr_surface(surface);
400 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
401}
402
403struct sway_container *container_at(struct sway_workspace *workspace,
404 double lx, double ly,
405 struct wlr_surface **surface, double *sx, double *sy) {
406 struct sway_container *c;
407
408 struct sway_seat *seat = input_manager_current_seat();
409 struct sway_container *focus = seat_get_focused_container(seat);
410 bool is_floating = focus && container_is_floating_or_child(focus);
411 // Focused view's popups
412 if (focus && focus->view) {
413 c = surface_at_view(focus, lx, ly, surface, sx, sy);
414 if (c && surface_is_popup(*surface)) {
415 return c;
416 }
417 *surface = NULL;
418 }
419 // Floating
420 if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) {
421 return c;
422 }
423 // Tiling (focused)
424 if (focus && focus->view && !is_floating) {
425 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
426 return c;
427 }
428 }
429 // Tiling (non-focused)
430 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
431 return c;
432 }
433 return NULL;
434}
435
436void container_for_each_child(struct sway_container *container, 603void container_for_each_child(struct sway_container *container,
437 void (*f)(struct sway_container *container, void *data), 604 void (*f)(struct sway_container *container, void *data),
438 void *data) { 605 void *data) {
@@ -478,127 +645,6 @@ bool container_has_ancestor(struct sway_container *descendant,
478 return false; 645 return false;
479} 646}
480 647
481void container_damage_whole(struct sway_container *container) {
482 for (int i = 0; i < root->outputs->length; ++i) {
483 struct sway_output *output = root->outputs->items[i];
484 output_damage_whole_container(output, container);
485 }
486}
487
488/**
489 * Return the output which will be used for scale purposes.
490 * This is the most recently entered output.
491 */
492struct sway_output *container_get_effective_output(struct sway_container *con) {
493 if (con->outputs->length == 0) {
494 return NULL;
495 }
496 return con->outputs->items[con->outputs->length - 1];
497}
498
499static void render_titlebar_text_texture(struct sway_output *output,
500 struct sway_container *con, struct wlr_texture **texture,
501 struct border_colors *class, bool pango_markup, char *text) {
502 double scale = output->wlr_output->scale;
503 int width = 0;
504 int height = config->font_height * scale;
505 int baseline;
506
507 // We must use a non-nil cairo_t for cairo_set_font_options to work.
508 // Therefore, we cannot use cairo_create(NULL).
509 cairo_surface_t *dummy_surface = cairo_image_surface_create(
510 CAIRO_FORMAT_ARGB32, 0, 0);
511 cairo_t *c = cairo_create(dummy_surface);
512 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
513 cairo_font_options_t *fo = cairo_font_options_create();
514 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
515 if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
516 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
517 } else {
518 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
519 cairo_font_options_set_subpixel_order(fo,
520 to_cairo_subpixel_order(output->wlr_output->subpixel));
521 }
522 cairo_set_font_options(c, fo);
523 get_text_size(c, config->font, &width, NULL, &baseline, scale,
524 config->pango_markup, "%s", text);
525 cairo_surface_destroy(dummy_surface);
526 cairo_destroy(c);
527
528 if (width == 0 || height == 0) {
529 return;
530 }
531
532 if (height > config->font_height * scale) {
533 height = config->font_height * scale;
534 }
535
536 cairo_surface_t *surface = cairo_image_surface_create(
537 CAIRO_FORMAT_ARGB32, width, height);
538 cairo_status_t status = cairo_surface_status(surface);
539 if (status != CAIRO_STATUS_SUCCESS) {
540 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
541 cairo_status_to_string(status));
542 return;
543 }
544
545 cairo_t *cairo = cairo_create(surface);
546 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
547 cairo_set_font_options(cairo, fo);
548 cairo_font_options_destroy(fo);
549 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
550 class->background[2], class->background[3]);
551 cairo_paint(cairo);
552 PangoContext *pango = pango_cairo_create_context(cairo);
553 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
554 class->text[2], class->text[3]);
555 cairo_move_to(cairo, 0, config->font_baseline * scale - baseline);
556
557 render_text(cairo, config->font, scale, pango_markup, "%s", text);
558
559 cairo_surface_flush(surface);
560 unsigned char *data = cairo_image_surface_get_data(surface);
561 int stride = cairo_image_surface_get_stride(surface);
562 struct wlr_renderer *renderer = output->wlr_output->renderer;
563 *texture = wlr_texture_from_pixels(
564 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
565 cairo_surface_destroy(surface);
566 g_object_unref(pango);
567 cairo_destroy(cairo);
568}
569
570static void update_title_texture(struct sway_container *con,
571 struct wlr_texture **texture, struct border_colors *class) {
572 struct sway_output *output = container_get_effective_output(con);
573 if (!output) {
574 return;
575 }
576 if (*texture) {
577 wlr_texture_destroy(*texture);
578 *texture = NULL;
579 }
580 if (!con->formatted_title) {
581 return;
582 }
583
584 render_titlebar_text_texture(output, con, texture, class,
585 config->pango_markup, con->formatted_title);
586}
587
588void container_update_title_textures(struct sway_container *container) {
589 update_title_texture(container, &container->title_focused,
590 &config->border_colors.focused);
591 update_title_texture(container, &container->title_focused_inactive,
592 &config->border_colors.focused_inactive);
593 update_title_texture(container, &container->title_unfocused,
594 &config->border_colors.unfocused);
595 update_title_texture(container, &container->title_urgent,
596 &config->border_colors.urgent);
597 update_title_texture(container, &container->title_focused_tab_title,
598 &config->border_colors.focused_tab_title);
599 container_damage_whole(container);
600}
601
602/** 648/**
603 * Calculate and return the length of the tree representation. 649 * Calculate and return the length of the tree representation.
604 * An example tree representation is: V[Terminal, Firefox] 650 * An example tree representation is: V[Terminal, Firefox]
@@ -664,7 +710,13 @@ void container_update_representation(struct sway_container *con) {
664 } 710 }
665 container_build_representation(con->pending.layout, con->pending.children, 711 container_build_representation(con->pending.layout, con->pending.children,
666 con->formatted_title); 712 con->formatted_title);
667 container_update_title_textures(con); 713
714 if (con->title_bar.title_text) {
715 sway_text_node_set_text(con->title_bar.title_text, con->formatted_title);
716 container_arrange_title_bar(con);
717 } else {
718 container_update_title_bar(con);
719 }
668 } 720 }
669 if (con->pending.parent) { 721 if (con->pending.parent) {
670 container_update_representation(con->pending.parent); 722 container_update_representation(con->pending.parent);
@@ -716,6 +768,21 @@ void floating_calculate_constraints(int *min_width, int *max_width,
716 768
717} 769}
718 770
771void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) {
772 if (!old->width || !old->height) {
773 // Fall back to centering on the workspace.
774 container_floating_move_to_center(con);
775 } else {
776 int rel_x = con->pending.x - old->x + (con->pending.width / 2);
777 int rel_y = con->pending.y - old->y + (con->pending.height / 2);
778
779 con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2);
780 con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2);
781
782 sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y);
783 }
784}
785
719static void floating_natural_resize(struct sway_container *con) { 786static void floating_natural_resize(struct sway_container *con) {
720 int min_width, max_width, min_height, max_height; 787 int min_width, max_width, min_height, max_height;
721 floating_calculate_constraints(&min_width, &max_width, 788 floating_calculate_constraints(&min_width, &max_width,
@@ -787,11 +854,11 @@ void container_floating_set_default_size(struct sway_container *con) {
787 int min_width, max_width, min_height, max_height; 854 int min_width, max_width, min_height, max_height;
788 floating_calculate_constraints(&min_width, &max_width, 855 floating_calculate_constraints(&min_width, &max_width,
789 &min_height, &max_height); 856 &min_height, &max_height);
790 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 857 struct wlr_box box;
791 workspace_get_box(con->pending.workspace, box); 858 workspace_get_box(con->pending.workspace, &box);
792 859
793 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 860 double width = fmax(min_width, fmin(box.width * 0.5, max_width));
794 double height = fmax(min_height, fmin(box->height * 0.75, max_height)); 861 double height = fmax(min_height, fmin(box.height * 0.75, max_height));
795 if (!con->view) { 862 if (!con->view) {
796 con->pending.width = width; 863 con->pending.width = width;
797 con->pending.height = height; 864 con->pending.height = height;
@@ -800,8 +867,6 @@ void container_floating_set_default_size(struct sway_container *con) {
800 con->pending.content_height = height; 867 con->pending.content_height = height;
801 container_set_geometry_from_content(con); 868 container_set_geometry_from_content(con);
802 } 869 }
803
804 free(box);
805} 870}
806 871
807 872
@@ -937,17 +1002,6 @@ bool container_is_floating(struct sway_container *container) {
937 return false; 1002 return false;
938} 1003}
939 1004
940bool container_is_current_floating(struct sway_container *container) {
941 if (!container->current.parent && container->current.workspace &&
942 list_find(container->current.workspace->floating, container) != -1) {
943 return true;
944 }
945 if (container->scratchpad) {
946 return true;
947 }
948 return false;
949}
950
951void container_get_box(struct sway_container *container, struct wlr_box *box) { 1005void container_get_box(struct sway_container *container, struct wlr_box *box) {
952 box->x = container->pending.x; 1006 box->x = container->pending.x;
953 box->y = container->pending.y; 1007 box->y = container->pending.y;
@@ -1031,6 +1085,13 @@ void container_floating_move_to(struct sway_container *con,
1031 workspace_add_floating(new_workspace, con); 1085 workspace_add_floating(new_workspace, con);
1032 arrange_workspace(old_workspace); 1086 arrange_workspace(old_workspace);
1033 arrange_workspace(new_workspace); 1087 arrange_workspace(new_workspace);
1088 // If the moved container was a visible scratchpad container, then
1089 // update its transform.
1090 if (con->scratchpad) {
1091 struct wlr_box output_box;
1092 output_get_box(new_output, &output_box);
1093 con->transform = output_box;
1094 }
1034 workspace_detect_urgent(old_workspace); 1095 workspace_detect_urgent(old_workspace);
1035 workspace_detect_urgent(new_workspace); 1096 workspace_detect_urgent(new_workspace);
1036 } 1097 }
@@ -1062,16 +1123,6 @@ void container_end_mouse_operation(struct sway_container *container) {
1062 } 1123 }
1063} 1124}
1064 1125
1065static bool devid_from_fd(int fd, dev_t *devid) {
1066 struct stat stat;
1067 if (fstat(fd, &stat) != 0) {
1068 sway_log_errno(SWAY_ERROR, "fstat failed");
1069 return false;
1070 }
1071 *devid = stat.st_rdev;
1072 return true;
1073}
1074
1075static void set_fullscreen(struct sway_container *con, bool enable) { 1126static void set_fullscreen(struct sway_container *con, bool enable) {
1076 if (!con->view) { 1127 if (!con->view) {
1077 return; 1128 return;
@@ -1083,75 +1134,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) {
1083 con->view->foreign_toplevel, enable); 1134 con->view->foreign_toplevel, enable);
1084 } 1135 }
1085 } 1136 }
1086
1087 if (!server.linux_dmabuf_v1 || !con->view->surface) {
1088 return;
1089 }
1090 if (!enable) {
1091 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1092 con->view->surface, NULL);
1093 return;
1094 }
1095
1096 if (!con->pending.workspace || !con->pending.workspace->output) {
1097 return;
1098 }
1099
1100 struct sway_output *output = con->pending.workspace->output;
1101 struct wlr_output *wlr_output = output->wlr_output;
1102
1103 // TODO: add wlroots helpers for all of this stuff
1104
1105 const struct wlr_drm_format_set *renderer_formats =
1106 wlr_renderer_get_dmabuf_texture_formats(server.renderer);
1107 assert(renderer_formats);
1108
1109 int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
1110 int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
1111 if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
1112 return;
1113 }
1114
1115 dev_t render_dev, scanout_dev;
1116 if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
1117 !devid_from_fd(backend_drm_fd, &scanout_dev)) {
1118 return;
1119 }
1120
1121 const struct wlr_drm_format_set *output_formats =
1122 wlr_output_get_primary_formats(output->wlr_output,
1123 WLR_BUFFER_CAP_DMABUF);
1124 if (!output_formats) {
1125 return;
1126 }
1127
1128 struct wlr_drm_format_set scanout_formats = {0};
1129 if (!wlr_drm_format_set_intersect(&scanout_formats,
1130 output_formats, renderer_formats)) {
1131 return;
1132 }
1133
1134 struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
1135 {
1136 .target_device = scanout_dev,
1137 .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
1138 .formats = &scanout_formats,
1139 },
1140 {
1141 .target_device = render_dev,
1142 .formats = renderer_formats,
1143 },
1144 };
1145
1146 const struct wlr_linux_dmabuf_feedback_v1 feedback = {
1147 .main_device = render_dev,
1148 .tranches = tranches,
1149 .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
1150 };
1151 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1152 con->view->surface, &feedback);
1153
1154 wlr_drm_format_set_finish(&scanout_formats);
1155} 1137}
1156 1138
1157static void container_fullscreen_workspace(struct sway_container *con) { 1139static void container_fullscreen_workspace(struct sway_container *con) {
@@ -1321,72 +1303,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1321 return false; 1303 return false;
1322} 1304}
1323 1305
1324static void surface_send_enter_iterator(struct wlr_surface *surface,
1325 int x, int y, void *data) {
1326 struct wlr_output *wlr_output = data;
1327 wlr_surface_send_enter(surface, wlr_output);
1328}
1329
1330static void surface_send_leave_iterator(struct wlr_surface *surface,
1331 int x, int y, void *data) {
1332 struct wlr_output *wlr_output = data;
1333 wlr_surface_send_leave(surface, wlr_output);
1334}
1335
1336void container_discover_outputs(struct sway_container *con) {
1337 struct wlr_box con_box = {
1338 .x = con->current.x,
1339 .y = con->current.y,
1340 .width = con->current.width,
1341 .height = con->current.height,
1342 };
1343 struct sway_output *old_output = container_get_effective_output(con);
1344
1345 for (int i = 0; i < root->outputs->length; ++i) {
1346 struct sway_output *output = root->outputs->items[i];
1347 struct wlr_box output_box;
1348 output_get_box(output, &output_box);
1349 struct wlr_box intersection;
1350 bool intersects =
1351 wlr_box_intersection(&intersection, &con_box, &output_box);
1352 int index = list_find(con->outputs, output);
1353
1354 if (intersects && index == -1) {
1355 // Send enter
1356 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1357 if (con->view) {
1358 view_for_each_surface(con->view,
1359 surface_send_enter_iterator, output->wlr_output);
1360 if (con->view->foreign_toplevel) {
1361 wlr_foreign_toplevel_handle_v1_output_enter(
1362 con->view->foreign_toplevel, output->wlr_output);
1363 }
1364 }
1365 list_add(con->outputs, output);
1366 } else if (!intersects && index != -1) {
1367 // Send leave
1368 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1369 if (con->view) {
1370 view_for_each_surface(con->view,
1371 surface_send_leave_iterator, output->wlr_output);
1372 if (con->view->foreign_toplevel) {
1373 wlr_foreign_toplevel_handle_v1_output_leave(
1374 con->view->foreign_toplevel, output->wlr_output);
1375 }
1376 }
1377 list_del(con->outputs, index);
1378 }
1379 }
1380 struct sway_output *new_output = container_get_effective_output(con);
1381 double old_scale = old_output && old_output->enabled ?
1382 old_output->wlr_output->scale : -1;
1383 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1384 if (old_scale != new_scale) {
1385 container_update_title_textures(con);
1386 container_update_marks_textures(con);
1387 }
1388}
1389
1390enum sway_container_layout container_parent_layout(struct sway_container *con) { 1306enum sway_container_layout container_parent_layout(struct sway_container *con) {
1391 if (con->pending.parent) { 1307 if (con->pending.parent) {
1392 return con->pending.parent->pending.layout; 1308 return con->pending.parent->pending.layout;
@@ -1397,19 +1313,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) {
1397 return L_NONE; 1313 return L_NONE;
1398} 1314}
1399 1315
1400enum sway_container_layout container_current_parent_layout(
1401 struct sway_container *con) {
1402 if (con->current.parent) {
1403 return con->current.parent->current.layout;
1404 }
1405 return con->current.workspace->current.layout;
1406}
1407
1408list_t *container_get_siblings(struct sway_container *container) { 1316list_t *container_get_siblings(struct sway_container *container) {
1409 if (container->pending.parent) { 1317 if (container->pending.parent) {
1410 return container->pending.parent->pending.children; 1318 return container->pending.parent->pending.children;
1411 } 1319 }
1412 if (container_is_scratchpad_hidden(container)) { 1320 if (!container->pending.workspace) {
1413 return NULL; 1321 return NULL;
1414 } 1322 }
1415 if (list_find(container->pending.workspace->tiling, container) != -1) { 1323 if (list_find(container->pending.workspace->tiling, container) != -1) {
@@ -1422,13 +1330,6 @@ int container_sibling_index(struct sway_container *child) {
1422 return list_find(container_get_siblings(child), child); 1330 return list_find(container_get_siblings(child), child);
1423} 1331}
1424 1332
1425list_t *container_get_current_siblings(struct sway_container *container) {
1426 if (container->current.parent) {
1427 return container->current.parent->current.children;
1428 }
1429 return container->current.workspace->current.tiling;
1430}
1431
1432void container_handle_fullscreen_reparent(struct sway_container *con) { 1333void container_handle_fullscreen_reparent(struct sway_container *con) {
1433 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || 1334 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1434 con->pending.workspace->fullscreen == con) { 1335 con->pending.workspace->fullscreen == con) {
@@ -1643,7 +1544,7 @@ bool container_find_and_unmark(char *mark) {
1643 if (strcmp(con_mark, mark) == 0) { 1544 if (strcmp(con_mark, mark) == 0) {
1644 free(con_mark); 1545 free(con_mark);
1645 list_del(con->marks, i); 1546 list_del(con->marks, i);
1646 container_update_marks_textures(con); 1547 container_update_marks(con);
1647 ipc_event_window(con, "mark"); 1548 ipc_event_window(con, "mark");
1648 return true; 1549 return true;
1649 } 1550 }
@@ -1674,70 +1575,15 @@ void container_add_mark(struct sway_container *con, char *mark) {
1674 ipc_event_window(con, "mark"); 1575 ipc_event_window(con, "mark");
1675} 1576}
1676 1577
1677static void update_marks_texture(struct sway_container *con,
1678 struct wlr_texture **texture, struct border_colors *class) {
1679 struct sway_output *output = container_get_effective_output(con);
1680 if (!output) {
1681 return;
1682 }
1683 if (*texture) {
1684 wlr_texture_destroy(*texture);
1685 *texture = NULL;
1686 }
1687 if (!con->marks->length) {
1688 return;
1689 }
1690
1691 size_t len = 0;
1692 for (int i = 0; i < con->marks->length; ++i) {
1693 char *mark = con->marks->items[i];
1694 if (mark[0] != '_') {
1695 len += strlen(mark) + 2;
1696 }
1697 }
1698 char *buffer = calloc(len + 1, 1);
1699 char *part = malloc(len + 1);
1700
1701 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1702 free(buffer);
1703 return;
1704 }
1705
1706 for (int i = 0; i < con->marks->length; ++i) {
1707 char *mark = con->marks->items[i];
1708 if (mark[0] != '_') {
1709 snprintf(part, len + 1, "[%s]", mark);
1710 strcat(buffer, part);
1711 }
1712 }
1713 free(part);
1714
1715 render_titlebar_text_texture(output, con, texture, class, false, buffer);
1716
1717 free(buffer);
1718}
1719
1720void container_update_marks_textures(struct sway_container *con) {
1721 if (!config->show_marks) {
1722 return;
1723 }
1724 update_marks_texture(con, &con->marks_focused,
1725 &config->border_colors.focused);
1726 update_marks_texture(con, &con->marks_focused_inactive,
1727 &config->border_colors.focused_inactive);
1728 update_marks_texture(con, &con->marks_unfocused,
1729 &config->border_colors.unfocused);
1730 update_marks_texture(con, &con->marks_urgent,
1731 &config->border_colors.urgent);
1732 update_marks_texture(con, &con->marks_focused_tab_title,
1733 &config->border_colors.focused_tab_title);
1734 container_damage_whole(con);
1735}
1736
1737void container_raise_floating(struct sway_container *con) { 1578void container_raise_floating(struct sway_container *con) {
1738 // Bring container to front by putting it at the end of the floating list. 1579 // Bring container to front by putting it at the end of the floating list.
1739 struct sway_container *floater = container_toplevel_ancestor(con); 1580 struct sway_container *floater = container_toplevel_ancestor(con);
1740 if (container_is_floating(floater) && floater->pending.workspace) { 1581 if (container_is_floating(floater) && floater->pending.workspace) {
1582 // it's okay to just raise the scene directly instead of waiting
1583 // for the transaction to go through. We won't be reconfiguring
1584 // surfaces
1585 wlr_scene_node_raise_to_top(&floater->scene_tree->node);
1586
1741 list_move_to_end(floater->pending.workspace->floating, floater); 1587 list_move_to_end(floater->pending.workspace->floating, floater);
1742 node_set_dirty(&floater->pending.workspace->node); 1588 node_set_dirty(&floater->pending.workspace->node);
1743 } 1589 }
@@ -1821,3 +1667,177 @@ int container_squash(struct sway_container *con) {
1821 } 1667 }
1822 return change; 1668 return change;
1823} 1669}
1670
1671static void swap_places(struct sway_container *con1,
1672 struct sway_container *con2) {
1673 struct sway_container *temp = malloc(sizeof(struct sway_container));
1674 temp->pending.x = con1->pending.x;
1675 temp->pending.y = con1->pending.y;
1676 temp->pending.width = con1->pending.width;
1677 temp->pending.height = con1->pending.height;
1678 temp->width_fraction = con1->width_fraction;
1679 temp->height_fraction = con1->height_fraction;
1680 temp->pending.parent = con1->pending.parent;
1681 temp->pending.workspace = con1->pending.workspace;
1682 bool temp_floating = container_is_floating(con1);
1683
1684 con1->pending.x = con2->pending.x;
1685 con1->pending.y = con2->pending.y;
1686 con1->pending.width = con2->pending.width;
1687 con1->pending.height = con2->pending.height;
1688 con1->width_fraction = con2->width_fraction;
1689 con1->height_fraction = con2->height_fraction;
1690
1691 con2->pending.x = temp->pending.x;
1692 con2->pending.y = temp->pending.y;
1693 con2->pending.width = temp->pending.width;
1694 con2->pending.height = temp->pending.height;
1695 con2->width_fraction = temp->width_fraction;
1696 con2->height_fraction = temp->height_fraction;
1697
1698 int temp_index = container_sibling_index(con1);
1699 if (con2->pending.parent) {
1700 container_insert_child(con2->pending.parent, con1,
1701 container_sibling_index(con2));
1702 } else if (container_is_floating(con2)) {
1703 workspace_add_floating(con2->pending.workspace, con1);
1704 } else {
1705 workspace_insert_tiling(con2->pending.workspace, con1,
1706 container_sibling_index(con2));
1707 }
1708 if (temp->pending.parent) {
1709 container_insert_child(temp->pending.parent, con2, temp_index);
1710 } else if (temp_floating) {
1711 workspace_add_floating(temp->pending.workspace, con2);
1712 } else {
1713 workspace_insert_tiling(temp->pending.workspace, con2, temp_index);
1714 }
1715
1716 free(temp);
1717}
1718
1719static void swap_focus(struct sway_container *con1,
1720 struct sway_container *con2, struct sway_seat *seat,
1721 struct sway_container *focus) {
1722 if (focus == con1 || focus == con2) {
1723 struct sway_workspace *ws1 = con1->pending.workspace;
1724 struct sway_workspace *ws2 = con2->pending.workspace;
1725 enum sway_container_layout layout1 = container_parent_layout(con1);
1726 enum sway_container_layout layout2 = container_parent_layout(con2);
1727 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
1728 if (workspace_is_visible(ws2)) {
1729 seat_set_focus(seat, &con2->node);
1730 }
1731 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
1732 } else if (focus == con2 && (layout1 == L_TABBED
1733 || layout1 == L_STACKED)) {
1734 if (workspace_is_visible(ws1)) {
1735 seat_set_focus(seat, &con1->node);
1736 }
1737 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
1738 } else if (ws1 != ws2) {
1739 seat_set_focus_container(seat, focus == con1 ? con2 : con1);
1740 } else {
1741 seat_set_focus_container(seat, focus);
1742 }
1743 } else {
1744 seat_set_focus_container(seat, focus);
1745 }
1746
1747 if (root->fullscreen_global) {
1748 seat_set_focus(seat,
1749 seat_get_focus_inactive(seat, &root->fullscreen_global->node));
1750 }
1751}
1752
1753void container_swap(struct sway_container *con1, struct sway_container *con2) {
1754 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
1755 return;
1756 }
1757 if (!sway_assert(!container_has_ancestor(con1, con2)
1758 && !container_has_ancestor(con2, con1),
1759 "Cannot swap ancestor and descendant")) {
1760 return;
1761 }
1762
1763 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
1764 con1->node.id, con2->node.id);
1765
1766 bool scratch1 = con1->scratchpad;
1767 bool hidden1 = container_is_scratchpad_hidden(con1);
1768 bool scratch2 = con2->scratchpad;
1769 bool hidden2 = container_is_scratchpad_hidden(con2);
1770 if (scratch1) {
1771 if (hidden1) {
1772 root_scratchpad_show(con1);
1773 }
1774 root_scratchpad_remove_container(con1);
1775 }
1776 if (scratch2) {
1777 if (hidden2) {
1778 root_scratchpad_show(con2);
1779 }
1780 root_scratchpad_remove_container(con2);
1781 }
1782
1783 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
1784 if (fs1) {
1785 container_fullscreen_disable(con1);
1786 }
1787 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
1788 if (fs2) {
1789 container_fullscreen_disable(con2);
1790 }
1791
1792 struct sway_seat *seat = input_manager_current_seat();
1793 struct sway_container *focus = seat_get_focused_container(seat);
1794 struct sway_workspace *vis1 =
1795 output_get_active_workspace(con1->pending.workspace->output);
1796 struct sway_workspace *vis2 =
1797 output_get_active_workspace(con2->pending.workspace->output);
1798 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
1799 "workspace. This should not happen")) {
1800 return;
1801 }
1802
1803 char *stored_prev_name = NULL;
1804 if (seat->prev_workspace_name) {
1805 stored_prev_name = strdup(seat->prev_workspace_name);
1806 }
1807
1808 swap_places(con1, con2);
1809
1810 if (!workspace_is_visible(vis1)) {
1811 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
1812 }
1813 if (!workspace_is_visible(vis2)) {
1814 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
1815 }
1816
1817 swap_focus(con1, con2, seat, focus);
1818
1819 if (stored_prev_name) {
1820 free(seat->prev_workspace_name);
1821 seat->prev_workspace_name = stored_prev_name;
1822 }
1823
1824 if (scratch1) {
1825 root_scratchpad_add_container(con2, NULL);
1826 if (!hidden1) {
1827 root_scratchpad_show(con2);
1828 }
1829 }
1830 if (scratch2) {
1831 root_scratchpad_add_container(con1, NULL);
1832 if (!hidden2) {
1833 root_scratchpad_show(con1);
1834 }
1835 }
1836
1837 if (fs1) {
1838 container_set_fullscreen(con2, fs1);
1839 }
1840 if (fs2) {
1841 container_set_fullscreen(con1, fs2);
1842 }
1843}
diff --git a/sway/tree/node.c b/sway/tree/node.c
index bc7e2aa5..7aaf9762 100644
--- a/sway/tree/node.c
+++ b/sway/tree/node.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/output.h" 1#include "sway/output.h"
3#include "sway/server.h" 2#include "sway/server.h"
4#include "sway/tree/container.h" 3#include "sway/tree/container.h"
@@ -18,13 +17,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) {
18const char *node_type_to_str(enum sway_node_type type) { 17const char *node_type_to_str(enum sway_node_type type) {
19 switch (type) { 18 switch (type) {
20 case N_ROOT: 19 case N_ROOT:
21 return "N_ROOT"; 20 return "root";
22 case N_OUTPUT: 21 case N_OUTPUT:
23 return "N_OUTPUT"; 22 return "output";
24 case N_WORKSPACE: 23 case N_WORKSPACE:
25 return "N_WORKSPACE"; 24 return "workspace";
26 case N_CONTAINER: 25 case N_CONTAINER:
27 return "N_CONTAINER"; 26 return "container";
28 } 27 }
29 return ""; 28 return "";
30} 29}
@@ -159,3 +158,32 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
159 } 158 }
160 return false; 159 return false;
161} 160}
161
162void scene_node_disown_children(struct wlr_scene_tree *tree) {
163 // this function can be called as part of destruction code that will be invoked
164 // upon an allocation failure. Let's not crash on NULL due to an allocation error.
165 if (!tree) {
166 return;
167 }
168
169 struct wlr_scene_node *child, *tmp_child;
170 wl_list_for_each_safe(child, tmp_child, &tree->children, link) {
171 wlr_scene_node_reparent(child, root->staging);
172 }
173}
174
175struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent,
176 bool *failed) {
177 // fallthrough
178 if (*failed) {
179 return NULL;
180 }
181
182 struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
183 if (!tree) {
184 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
185 *failed = true;
186 }
187
188 return tree;
189}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 52826c91..2d11195e 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -1,14 +1,13 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <ctype.h> 2#include <ctype.h>
4#include <string.h> 3#include <string.h>
5#include <strings.h> 4#include <strings.h>
6#include <wlr/types/wlr_output_damage.h>
7#include "sway/ipc-server.h" 5#include "sway/ipc-server.h"
8#include "sway/layers.h" 6#include "sway/layers.h"
9#include "sway/output.h" 7#include "sway/output.h"
10#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
11#include "sway/tree/workspace.h" 9#include "sway/tree/workspace.h"
10#include "sway/server.h"
12#include "log.h" 11#include "log.h"
13#include "util.h" 12#include "util.h"
14 13
@@ -87,9 +86,51 @@ static void restore_workspaces(struct sway_output *output) {
87 output_sort_workspaces(output); 86 output_sort_workspaces(output);
88} 87}
89 88
89static void destroy_scene_layers(struct sway_output *output) {
90 wlr_scene_node_destroy(&output->fullscreen_background->node);
91
92 scene_node_disown_children(output->layers.tiling);
93 scene_node_disown_children(output->layers.fullscreen);
94
95 wlr_scene_node_destroy(&output->layers.shell_background->node);
96 wlr_scene_node_destroy(&output->layers.shell_bottom->node);
97 wlr_scene_node_destroy(&output->layers.tiling->node);
98 wlr_scene_node_destroy(&output->layers.fullscreen->node);
99 wlr_scene_node_destroy(&output->layers.shell_top->node);
100 wlr_scene_node_destroy(&output->layers.shell_overlay->node);
101 wlr_scene_node_destroy(&output->layers.session_lock->node);
102}
103
90struct sway_output *output_create(struct wlr_output *wlr_output) { 104struct sway_output *output_create(struct wlr_output *wlr_output) {
91 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 105 struct sway_output *output = calloc(1, sizeof(struct sway_output));
92 node_init(&output->node, N_OUTPUT, output); 106 node_init(&output->node, N_OUTPUT, output);
107
108 bool failed = false;
109 output->layers.shell_background = alloc_scene_tree(root->staging, &failed);
110 output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed);
111 output->layers.tiling = alloc_scene_tree(root->staging, &failed);
112 output->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
113 output->layers.shell_top = alloc_scene_tree(root->staging, &failed);
114 output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed);
115 output->layers.session_lock = alloc_scene_tree(root->staging, &failed);
116
117 if (!failed) {
118 output->fullscreen_background = wlr_scene_rect_create(
119 output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});
120
121 if (!output->fullscreen_background) {
122 sway_log(SWAY_ERROR, "Unable to allocate a background rect");
123 failed = true;
124 }
125 }
126
127 if (failed) {
128 destroy_scene_layers(output);
129 wlr_scene_output_destroy(output->scene_output);
130 free(output);
131 return NULL;
132 }
133
93 output->wlr_output = wlr_output; 134 output->wlr_output = wlr_output;
94 wlr_output->data = output; 135 wlr_output->data = output;
95 output->detected_subpixel = wlr_output->subpixel; 136 output->detected_subpixel = wlr_output->subpixel;
@@ -102,11 +143,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
102 output->workspaces = create_list(); 143 output->workspaces = create_list();
103 output->current.workspaces = create_list(); 144 output->current.workspaces = create_list();
104 145
105 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
106 for (size_t i = 0; i < len; ++i) {
107 wl_list_init(&output->layers[i]);
108 }
109
110 return output; 146 return output;
111} 147}
112 148
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) {
146 182
147 input_manager_configure_xcursor(); 183 input_manager_configure_xcursor();
148 184
149 wl_signal_emit(&root->events.new_node, &output->node); 185 wl_signal_emit_mutable(&root->events.new_node, &output->node);
150 186
151 arrange_layers(output); 187 arrange_layers(output);
152 arrange_root(); 188 arrange_root();
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) {
238 "which is still referenced by transactions")) { 274 "which is still referenced by transactions")) {
239 return; 275 return;
240 } 276 }
277
278 destroy_scene_layers(output);
241 list_free(output->workspaces); 279 list_free(output->workspaces);
242 list_free(output->current.workspaces); 280 list_free(output->current.workspaces);
243 wl_event_source_remove(output->repaint_timer); 281 wl_event_source_remove(output->repaint_timer);
244 free(output); 282 free(output);
245} 283}
246 284
247static void untrack_output(struct sway_container *con, void *data) {
248 struct sway_output *output = data;
249 int index = list_find(con->outputs, output);
250 if (index != -1) {
251 list_del(con->outputs, index);
252 }
253}
254
255void output_disable(struct sway_output *output) { 285void output_disable(struct sway_output *output) {
256 if (!sway_assert(output->enabled, "Expected an enabled output")) { 286 if (!sway_assert(output->enabled, "Expected an enabled output")) {
257 return; 287 return;
@@ -262,23 +292,20 @@ void output_disable(struct sway_output *output) {
262 } 292 }
263 293
264 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); 294 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name);
265 wl_signal_emit(&output->events.disable, output); 295 wl_signal_emit_mutable(&output->events.disable, output);
266 296
267 output_evacuate(output); 297 output_evacuate(output);
268 298
269 root_for_each_container(untrack_output, output);
270
271 list_del(root->outputs, index); 299 list_del(root->outputs, index);
272 300
273 output->enabled = false; 301 output->enabled = false;
274 output->current_mode = NULL;
275 302
276 arrange_root(); 303 arrange_root();
277 304
278 // Reconfigure all devices, since devices with map_to_output directives for 305 // Reconfigure all devices, since devices with map_to_output directives for
279 // an output that goes offline should stop sending events as long as the 306 // an output that goes offline should stop sending events as long as the
280 // output remains offline. 307 // output remains offline.
281 input_manager_configure_all_inputs(); 308 input_manager_configure_all_input_mappings();
282} 309}
283 310
284void output_begin_destroy(struct sway_output *output) { 311void output_begin_destroy(struct sway_output *output) {
@@ -286,7 +313,7 @@ void output_begin_destroy(struct sway_output *output) {
286 return; 313 return;
287 } 314 }
288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); 315 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name);
289 wl_signal_emit(&output->node.events.destroy, &output->node); 316 wl_signal_emit_mutable(&output->node.events.destroy, &output->node);
290 317
291 output->node.destroying = true; 318 output->node.destroying = true;
292 node_set_dirty(&output->node); 319 node_set_dirty(&output->node);
@@ -390,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) {
390 box->height = output->height; 417 box->height = output->height;
391} 418}
392 419
420static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) {
421 struct sway_output_non_desktop *output =
422 wl_container_of(listener, output, destroy);
423
424 sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name);
425
426 int index = list_find(root->non_desktop_outputs, output);
427 list_del(root->non_desktop_outputs, index);
428
429 wl_list_remove(&output->destroy.link);
430
431 free(output);
432}
433
434struct sway_output_non_desktop *output_non_desktop_create(
435 struct wlr_output *wlr_output) {
436 struct sway_output_non_desktop *output =
437 calloc(1, sizeof(struct sway_output_non_desktop));
438
439 output->wlr_output = wlr_output;
440
441 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
442 output->destroy.notify = handle_destroy_non_desktop;
443
444 return output;
445}
446
393enum sway_container_layout output_get_default_layout( 447enum sway_container_layout output_get_default_layout(
394 struct sway_output *output) { 448 struct sway_output *output) {
395 if (config->default_orientation != L_NONE) { 449 if (config->default_orientation != L_NONE) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index 8508e9eb..20fcfa59 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -1,12 +1,14 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <wlr/types/wlr_output_layout.h> 4#include <wlr/types/wlr_output_layout.h>
5#include <wlr/types/wlr_scene.h>
6#include <wlr/util/transform.h>
6#include "sway/desktop/transaction.h" 7#include "sway/desktop/transaction.h"
7#include "sway/input/seat.h" 8#include "sway/input/seat.h"
8#include "sway/ipc-server.h" 9#include "sway/ipc-server.h"
9#include "sway/output.h" 10#include "sway/output.h"
11#include "sway/scene_descriptor.h"
10#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
11#include "sway/tree/container.h" 13#include "sway/tree/container.h"
12#include "sway/tree/root.h" 14#include "sway/tree/root.h"
@@ -23,21 +25,60 @@ static void output_layout_handle_change(struct wl_listener *listener,
23 transaction_commit_dirty(); 25 transaction_commit_dirty();
24} 26}
25 27
26struct sway_root *root_create(void) { 28struct sway_root *root_create(struct wl_display *wl_display) {
27 struct sway_root *root = calloc(1, sizeof(struct sway_root)); 29 struct sway_root *root = calloc(1, sizeof(struct sway_root));
28 if (!root) { 30 if (!root) {
29 sway_log(SWAY_ERROR, "Unable to allocate sway_root"); 31 sway_log(SWAY_ERROR, "Unable to allocate sway_root");
30 return NULL; 32 return NULL;
31 } 33 }
34
35 struct wlr_scene *root_scene = wlr_scene_create();
36 if (!root_scene) {
37 sway_log(SWAY_ERROR, "Unable to allocate root scene node");
38 free(root);
39 return NULL;
40 }
41
32 node_init(&root->node, N_ROOT, root); 42 node_init(&root->node, N_ROOT, root);
33 root->output_layout = wlr_output_layout_create(); 43 root->root_scene = root_scene;
34 wl_list_init(&root->all_outputs); 44
35#if HAVE_XWAYLAND 45 bool failed = false;
36 wl_list_init(&root->xwayland_unmanaged); 46 root->staging = alloc_scene_tree(&root_scene->tree, &failed);
47 root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed);
48
49 root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed);
50 root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed);
51 root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed);
52 root->layers.floating = alloc_scene_tree(root->layer_tree, &failed);
53 root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed);
54 root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed);
55 root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed);
56#if WLR_HAS_XWAYLAND
57 root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed);
37#endif 58#endif
38 wl_list_init(&root->drag_icons); 59 root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed);
60 root->layers.popup = alloc_scene_tree(root->layer_tree, &failed);
61 root->layers.seat = alloc_scene_tree(root->layer_tree, &failed);
62 root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed);
63
64 if (!failed && !scene_descriptor_assign(&root->layers.seat->node,
65 SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) {
66 failed = true;
67 }
68
69 if (failed) {
70 wlr_scene_node_destroy(&root_scene->tree.node);
71 free(root);
72 return NULL;
73 }
74
75 wlr_scene_node_set_enabled(&root->staging->node, false);
76
77 root->output_layout = wlr_output_layout_create(wl_display);
78 wl_list_init(&root->all_outputs);
39 wl_signal_init(&root->events.new_node); 79 wl_signal_init(&root->events.new_node);
40 root->outputs = create_list(); 80 root->outputs = create_list();
81 root->non_desktop_outputs = create_list();
41 root->scratchpad = create_list(); 82 root->scratchpad = create_list();
42 83
43 root->output_layout_change.notify = output_layout_handle_change; 84 root->output_layout_change.notify = output_layout_handle_change;
@@ -49,11 +90,22 @@ struct sway_root *root_create(void) {
49void root_destroy(struct sway_root *root) { 90void root_destroy(struct sway_root *root) {
50 wl_list_remove(&root->output_layout_change.link); 91 wl_list_remove(&root->output_layout_change.link);
51 list_free(root->scratchpad); 92 list_free(root->scratchpad);
93 list_free(root->non_desktop_outputs);
52 list_free(root->outputs); 94 list_free(root->outputs);
53 wlr_output_layout_destroy(root->output_layout); 95 wlr_scene_node_destroy(&root->root_scene->tree.node);
54 free(root); 96 free(root);
55} 97}
56 98
99static void set_container_transform(struct sway_workspace *ws,
100 struct sway_container *con) {
101 struct sway_output *output = ws->output;
102 struct wlr_box box = {0};
103 if (output) {
104 output_get_box(output, &box);
105 }
106 con->transform = box;
107}
108
57void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { 109void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) {
58 if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { 110 if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) {
59 return; 111 return;
@@ -62,6 +114,8 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works
62 struct sway_container *parent = con->pending.parent; 114 struct sway_container *parent = con->pending.parent;
63 struct sway_workspace *workspace = con->pending.workspace; 115 struct sway_workspace *workspace = con->pending.workspace;
64 116
117 set_container_transform(workspace, con);
118
65 // Clear the fullscreen mode when sending to the scratchpad 119 // Clear the fullscreen mode when sending to the scratchpad
66 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { 120 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) {
67 container_fullscreen_disable(con); 121 container_fullscreen_disable(con);
@@ -131,7 +185,10 @@ void root_scratchpad_show(struct sway_container *con) {
131 // Show the container 185 // Show the container
132 if (old_ws) { 186 if (old_ws) {
133 container_detach(con); 187 container_detach(con);
134 workspace_consider_destroy(old_ws); 188 // Make sure the last inactive container on the old workspace is above
189 // the workspace itself in the focus stack.
190 struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node);
191 seat_set_raw_focus(seat, node);
135 } else { 192 } else {
136 // Act on the ancestor of scratchpad hidden split containers 193 // Act on the ancestor of scratchpad hidden split containers
137 while (con->pending.parent) { 194 while (con->pending.parent) {
@@ -140,18 +197,18 @@ void root_scratchpad_show(struct sway_container *con) {
140 } 197 }
141 workspace_add_floating(new_ws, con); 198 workspace_add_floating(new_ws, con);
142 199
143 // Make sure the container's center point overlaps this workspace 200 if (new_ws->output) {
144 double center_lx = con->pending.x + con->pending.width / 2; 201 struct wlr_box output_box;
145 double center_ly = con->pending.y + con->pending.height / 2; 202 output_get_box(new_ws->output, &output_box);
146 203 floating_fix_coordinates(con, &con->transform, &output_box);
147 struct wlr_box workspace_box;
148 workspace_get_box(new_ws, &workspace_box);
149 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
150 container_floating_resize_and_center(con);
151 } 204 }
205 set_container_transform(new_ws, con);
152 206
153 arrange_workspace(new_ws); 207 arrange_workspace(new_ws);
154 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); 208 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
209 if (old_ws) {
210 workspace_consider_destroy(old_ws);
211 }
155} 212}
156 213
157static void disable_fullscreen(struct sway_container *con, void *data) { 214static void disable_fullscreen(struct sway_container *con, void *data) {
@@ -171,6 +228,8 @@ void root_scratchpad_hide(struct sway_container *con) {
171 return; 228 return;
172 } 229 }
173 230
231 set_container_transform(con->pending.workspace, con);
232
174 disable_fullscreen(con, NULL); 233 disable_fullscreen(con, NULL);
175 container_for_each_child(con, disable_fullscreen, NULL); 234 container_for_each_child(con, disable_fullscreen, NULL);
176 container_detach(con); 235 container_detach(con);
@@ -183,172 +242,6 @@ void root_scratchpad_hide(struct sway_container *con) {
183 ipc_event_window(con, "move"); 242 ipc_event_window(con, "move");
184} 243}
185 244
186struct pid_workspace {
187 pid_t pid;
188 char *workspace;
189 struct timespec time_added;
190
191 struct sway_output *output;
192 struct wl_listener output_destroy;
193
194 struct wl_list link;
195};
196
197static struct wl_list pid_workspaces;
198
199/**
200 * Get the pid of a parent process given the pid of a child process.
201 *
202 * Returns the parent pid or NULL if the parent pid cannot be determined.
203 */
204static pid_t get_parent_pid(pid_t child) {
205 pid_t parent = -1;
206 char file_name[100];
207 char *buffer = NULL;
208 const char *sep = " ";
209 FILE *stat = NULL;
210 size_t buf_size = 0;
211
212 snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
213
214 if ((stat = fopen(file_name, "r"))) {
215 if (getline(&buffer, &buf_size, stat) != -1) {
216 strtok(buffer, sep); // pid
217 strtok(NULL, sep); // executable name
218 strtok(NULL, sep); // state
219 char *token = strtok(NULL, sep); // parent pid
220 parent = strtol(token, NULL, 10);
221 }
222 free(buffer);
223 fclose(stat);
224 }
225
226 if (parent) {
227 return (parent == child) ? -1 : parent;
228 }
229
230 return -1;
231}
232
233static void pid_workspace_destroy(struct pid_workspace *pw) {
234 wl_list_remove(&pw->output_destroy.link);
235 wl_list_remove(&pw->link);
236 free(pw->workspace);
237 free(pw);
238}
239
240struct sway_workspace *root_workspace_for_pid(pid_t pid) {
241 if (!pid_workspaces.prev && !pid_workspaces.next) {
242 wl_list_init(&pid_workspaces);
243 return NULL;
244 }
245
246 struct sway_workspace *ws = NULL;
247 struct pid_workspace *pw = NULL;
248
249 sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
250
251 do {
252 struct pid_workspace *_pw = NULL;
253 wl_list_for_each(_pw, &pid_workspaces, link) {
254 if (pid == _pw->pid) {
255 pw = _pw;
256 sway_log(SWAY_DEBUG,
257 "found pid_workspace for pid %d, workspace %s",
258 pid, pw->workspace);
259 goto found;
260 }
261 }
262 pid = get_parent_pid(pid);
263 } while (pid > 1);
264found:
265
266 if (pw && pw->workspace) {
267 ws = workspace_by_name(pw->workspace);
268
269 if (!ws) {
270 sway_log(SWAY_DEBUG,
271 "Creating workspace %s for pid %d because it disappeared",
272 pw->workspace, pid);
273
274 struct sway_output *output = pw->output;
275 if (pw->output && !pw->output->enabled) {
276 sway_log(SWAY_DEBUG,
277 "Workspace output %s is disabled, trying another one",
278 pw->output->wlr_output->name);
279 output = NULL;
280 }
281
282 ws = workspace_create(output, pw->workspace);
283 }
284
285 pid_workspace_destroy(pw);
286 }
287
288 return ws;
289}
290
291static void pw_handle_output_destroy(struct wl_listener *listener, void *data) {
292 struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy);
293 pw->output = NULL;
294 wl_list_remove(&pw->output_destroy.link);
295 wl_list_init(&pw->output_destroy.link);
296}
297
298void root_record_workspace_pid(pid_t pid) {
299 sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid);
300 if (!pid_workspaces.prev && !pid_workspaces.next) {
301 wl_list_init(&pid_workspaces);
302 }
303
304 struct sway_seat *seat = input_manager_current_seat();
305 struct sway_workspace *ws = seat_get_focused_workspace(seat);
306 if (!ws) {
307 sway_log(SWAY_DEBUG, "Bailing out, no workspace");
308 return;
309 }
310 struct sway_output *output = ws->output;
311 if (!output) {
312 sway_log(SWAY_DEBUG, "Bailing out, no output");
313 return;
314 }
315
316 struct timespec now;
317 clock_gettime(CLOCK_MONOTONIC, &now);
318
319 // Remove expired entries
320 static const int timeout = 60;
321 struct pid_workspace *old, *_old;
322 wl_list_for_each_safe(old, _old, &pid_workspaces, link) {
323 if (now.tv_sec - old->time_added.tv_sec >= timeout) {
324 pid_workspace_destroy(old);
325 }
326 }
327
328 struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace));
329 pw->workspace = strdup(ws->name);
330 pw->output = output;
331 pw->pid = pid;
332 memcpy(&pw->time_added, &now, sizeof(struct timespec));
333 pw->output_destroy.notify = pw_handle_output_destroy;
334 wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy);
335 wl_list_insert(&pid_workspaces, &pw->link);
336}
337
338void root_remove_workspace_pid(pid_t pid) {
339 if (!pid_workspaces.prev || !pid_workspaces.next) {
340 return;
341 }
342
343 struct pid_workspace *pw, *tmp;
344 wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) {
345 if (pid == pw->pid) {
346 pid_workspace_destroy(pw);
347 return;
348 }
349 }
350}
351
352void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), 245void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
353 void *data) { 246 void *data) {
354 for (int i = 0; i < root->outputs->length; ++i) { 247 for (int i = 0; i < root->outputs->length; ++i) {
@@ -443,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) {
443 box->width = root->width; 336 box->width = root->width;
444 box->height = root->height; 337 box->height = root->height;
445} 338}
446
447void root_rename_pid_workspaces(const char *old_name, const char *new_name) {
448 if (!pid_workspaces.prev && !pid_workspaces.next) {
449 wl_list_init(&pid_workspaces);
450 }
451
452 struct pid_workspace *pw = NULL;
453 wl_list_for_each(pw, &pid_workspaces, link) {
454 if (strcmp(pw->workspace, old_name) == 0) {
455 free(pw->workspace);
456 pw->workspace = strdup(new_name);
457 }
458 }
459}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7d9e038d..1c1c8ee8 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,29 +1,32 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <strings.h> 2#include <strings.h>
4#include <wayland-server-core.h> 3#include <wayland-server-core.h>
4#include <wlr/config.h>
5#include <wlr/render/wlr_renderer.h> 5#include <wlr/render/wlr_renderer.h>
6#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
8#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_output_layout.h> 9#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 10#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_subcompositor.h> 11#include <wlr/types/wlr_subcompositor.h>
10#include <wlr/types/wlr_xdg_decoration_v1.h> 12#include <wlr/types/wlr_xdg_decoration_v1.h>
11#include "config.h" 13#if WLR_HAS_XWAYLAND
12#if HAVE_XWAYLAND
13#include <wlr/xwayland.h> 14#include <wlr/xwayland.h>
14#endif 15#endif
15#include "list.h" 16#include "list.h"
16#include "log.h" 17#include "log.h"
17#include "sway/criteria.h" 18#include "sway/criteria.h"
18#include "sway/commands.h" 19#include "sway/commands.h"
19#include "sway/desktop.h"
20#include "sway/desktop/transaction.h" 20#include "sway/desktop/transaction.h"
21#include "sway/desktop/idle_inhibit_v1.h" 21#include "sway/desktop/idle_inhibit_v1.h"
22#include "sway/desktop/launcher.h"
22#include "sway/input/cursor.h" 23#include "sway/input/cursor.h"
23#include "sway/ipc-server.h" 24#include "sway/ipc-server.h"
24#include "sway/output.h" 25#include "sway/output.h"
25#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/scene_descriptor.h"
26#include "sway/server.h" 28#include "sway/server.h"
29#include "sway/sway_text_node.h"
27#include "sway/tree/arrange.h" 30#include "sway/tree/arrange.h"
28#include "sway/tree/container.h" 31#include "sway/tree/container.h"
29#include "sway/tree/view.h" 32#include "sway/tree/view.h"
@@ -33,15 +36,29 @@
33#include "pango.h" 36#include "pango.h"
34#include "stringop.h" 37#include "stringop.h"
35 38
36void view_init(struct sway_view *view, enum sway_view_type type, 39bool view_init(struct sway_view *view, enum sway_view_type type,
37 const struct sway_view_impl *impl) { 40 const struct sway_view_impl *impl) {
41 bool failed = false;
42 view->scene_tree = alloc_scene_tree(root->staging, &failed);
43 view->content_tree = alloc_scene_tree(view->scene_tree, &failed);
44
45 if (!failed && !scene_descriptor_assign(&view->scene_tree->node,
46 SWAY_SCENE_DESC_VIEW, view)) {
47 failed = true;
48 }
49
50 if (failed) {
51 wlr_scene_node_destroy(&view->scene_tree->node);
52 return false;
53 }
54
38 view->type = type; 55 view->type = type;
39 view->impl = impl; 56 view->impl = impl;
40 view->executed_criteria = create_list(); 57 view->executed_criteria = create_list();
41 wl_list_init(&view->saved_buffers);
42 view->allow_request_urgent = true; 58 view->allow_request_urgent = true;
43 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; 59 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
44 wl_signal_init(&view->events.unmap); 60 wl_signal_init(&view->events.unmap);
61 return true;
45} 62}
46 63
47void view_destroy(struct sway_view *view) { 64void view_destroy(struct sway_view *view) {
@@ -58,11 +75,10 @@ void view_destroy(struct sway_view *view) {
58 return; 75 return;
59 } 76 }
60 wl_list_remove(&view->events.unmap.listener_list); 77 wl_list_remove(&view->events.unmap.listener_list);
61 if (!wl_list_empty(&view->saved_buffers)) {
62 view_remove_saved_buffer(view);
63 }
64 list_free(view->executed_criteria); 78 list_free(view->executed_criteria);
65 79
80 view_assign_ctx(view, NULL);
81 wlr_scene_node_destroy(&view->scene_tree->node);
66 free(view->title_format); 82 free(view->title_format);
67 83
68 if (view->impl->destroy) { 84 if (view->impl->destroy) {
@@ -110,7 +126,7 @@ const char *view_get_instance(struct sway_view *view) {
110 } 126 }
111 return NULL; 127 return NULL;
112} 128}
113#if HAVE_XWAYLAND 129#if WLR_HAS_XWAYLAND
114uint32_t view_get_x11_window_id(struct sway_view *view) { 130uint32_t view_get_x11_window_id(struct sway_view *view) {
115 if (view->impl->get_int_prop) { 131 if (view->impl->get_int_prop) {
116 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); 132 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);
@@ -143,7 +159,7 @@ const char *view_get_shell(struct sway_view *view) {
143 switch(view->type) { 159 switch(view->type) {
144 case SWAY_VIEW_XDG_SHELL: 160 case SWAY_VIEW_XDG_SHELL:
145 return "xdg_shell"; 161 return "xdg_shell";
146#if HAVE_XWAYLAND 162#if WLR_HAS_XWAYLAND
147 case SWAY_VIEW_XWAYLAND: 163 case SWAY_VIEW_XWAYLAND:
148 return "xwayland"; 164 return "xwayland";
149#endif 165#endif
@@ -363,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) {
363 } 379 }
364} 380}
365 381
366void view_request_activate(struct sway_view *view) { 382void view_request_activate(struct sway_view *view, struct sway_seat *seat) {
367 struct sway_workspace *ws = view->container->pending.workspace; 383 struct sway_workspace *ws = view->container->pending.workspace;
368 if (!ws) { // hidden scratchpad container 384 if (!seat) {
369 return; 385 seat = input_manager_current_seat();
370 } 386 }
371 struct sway_seat *seat = input_manager_current_seat();
372 387
373 switch (config->focus_on_window_activation) { 388 switch (config->focus_on_window_activation) {
374 case FOWA_SMART: 389 case FOWA_SMART:
375 if (workspace_is_visible(ws)) { 390 if (ws && workspace_is_visible(ws)) {
376 seat_set_focus_container(seat, view->container); 391 seat_set_focus_container(seat, view->container);
392 container_raise_floating(view->container);
377 } else { 393 } else {
378 view_set_urgent(view, true); 394 view_set_urgent(view, true);
379 } 395 }
@@ -382,11 +398,23 @@ void view_request_activate(struct sway_view *view) {
382 view_set_urgent(view, true); 398 view_set_urgent(view, true);
383 break; 399 break;
384 case FOWA_FOCUS: 400 case FOWA_FOCUS:
385 seat_set_focus_container(seat, view->container); 401 if (container_is_scratchpad_hidden_or_child(view->container)) {
402 root_scratchpad_show(view->container);
403 } else {
404 seat_set_focus_container(seat, view->container);
405 container_raise_floating(view->container);
406 }
386 break; 407 break;
387 case FOWA_NONE: 408 case FOWA_NONE:
388 break; 409 break;
389 } 410 }
411 transaction_commit_dirty();
412}
413
414void view_request_urgent(struct sway_view *view) {
415 if (config->focus_on_window_activation != FOWA_NONE) {
416 view_set_urgent(view, true);
417 }
390} 418}
391 419
392void view_set_csd_from_server(struct sway_view *view, bool enabled) { 420void view_set_csd_from_server(struct sway_view *view, bool enabled) {
@@ -433,52 +461,6 @@ void view_close_popups(struct sway_view *view) {
433 } 461 }
434} 462}
435 463
436void view_damage_from(struct sway_view *view) {
437 for (int i = 0; i < root->outputs->length; ++i) {
438 struct sway_output *output = root->outputs->items[i];
439 output_damage_from_view(output, view);
440 }
441}
442
443void view_for_each_surface(struct sway_view *view,
444 wlr_surface_iterator_func_t iterator, void *user_data) {
445 if (!view->surface) {
446 return;
447 }
448 if (view->impl->for_each_surface) {
449 view->impl->for_each_surface(view, iterator, user_data);
450 } else {
451 wlr_surface_for_each_surface(view->surface, iterator, user_data);
452 }
453}
454
455void view_for_each_popup_surface(struct sway_view *view,
456 wlr_surface_iterator_func_t iterator, void *user_data) {
457 if (!view->surface) {
458 return;
459 }
460 if (view->impl->for_each_popup_surface) {
461 view->impl->for_each_popup_surface(view, iterator, user_data);
462 }
463}
464
465static void view_subsurface_create(struct sway_view *view,
466 struct wlr_subsurface *subsurface);
467
468static void view_init_subsurfaces(struct sway_view *view,
469 struct wlr_surface *surface);
470
471static void view_child_init_subsurfaces(struct sway_view_child *view_child,
472 struct wlr_surface *surface);
473
474static void view_handle_surface_new_subsurface(struct wl_listener *listener,
475 void *data) {
476 struct sway_view *view =
477 wl_container_of(listener, view, surface_new_subsurface);
478 struct wlr_subsurface *subsurface = data;
479 view_subsurface_create(view, subsurface);
480}
481
482static bool view_has_executed_criteria(struct sway_view *view, 464static bool view_has_executed_criteria(struct sway_view *view,
483 struct criteria *criteria) { 465 struct criteria *criteria) {
484 for (int i = 0; i < view->executed_criteria->length; ++i) { 466 for (int i = 0; i < view->executed_criteria->length; ++i) {
@@ -517,10 +499,10 @@ void view_execute_criteria(struct sway_view *view) {
517static void view_populate_pid(struct sway_view *view) { 499static void view_populate_pid(struct sway_view *view) {
518 pid_t pid; 500 pid_t pid;
519 switch (view->type) { 501 switch (view->type) {
520#if HAVE_XWAYLAND 502#if WLR_HAS_XWAYLAND
521 case SWAY_VIEW_XWAYLAND:; 503 case SWAY_VIEW_XWAYLAND:;
522 struct wlr_xwayland_surface *surf = 504 struct wlr_xwayland_surface *surf =
523 wlr_xwayland_surface_from_wlr_surface(view->surface); 505 wlr_xwayland_surface_try_from_wlr_surface(view->surface);
524 pid = surf->pid; 506 pid = surf->pid;
525 break; 507 break;
526#endif 508#endif
@@ -533,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) {
533 view->pid = pid; 515 view->pid = pid;
534} 516}
535 517
518void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) {
519 if (view->ctx) {
520 // This ctx has been replaced
521 launcher_ctx_destroy(view->ctx);
522 view->ctx = NULL;
523 }
524 if (ctx == NULL) {
525 return;
526 }
527 launcher_ctx_consume(ctx);
528
529 view->ctx = ctx;
530}
531
536static struct sway_workspace *select_workspace(struct sway_view *view) { 532static struct sway_workspace *select_workspace(struct sway_view *view) {
537 struct sway_seat *seat = input_manager_current_seat(); 533 struct sway_seat *seat = input_manager_current_seat();
538 534
@@ -568,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
568 } 564 }
569 list_free(criterias); 565 list_free(criterias);
570 if (ws) { 566 if (ws) {
571 root_remove_workspace_pid(view->pid); 567 view_assign_ctx(view, NULL);
572 return ws; 568 return ws;
573 } 569 }
574 570
575 // Check if there's a PID mapping 571 // Check if there's a PID mapping
576 ws = root_workspace_for_pid(view->pid); 572 ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;
577 if (ws) { 573 if (ws) {
574 view_assign_ctx(view, NULL);
578 return ws; 575 return ws;
579 } 576 }
580 577
@@ -592,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
592 return NULL; 589 return NULL;
593} 590}
594 591
592static void update_ext_foreign_toplevel(struct sway_view *view) {
593 struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = {
594 .app_id = view_get_app_id(view),
595 .title = view_get_title(view),
596 };
597 wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state);
598}
599
595static bool should_focus(struct sway_view *view) { 600static bool should_focus(struct sway_view *view) {
596 struct sway_seat *seat = input_manager_current_seat(); 601 struct sway_seat *seat = input_manager_current_seat();
597 struct sway_container *prev_con = seat_get_focused_container(seat); 602 struct sway_container *prev_con = seat_get_focused_container(seat);
@@ -717,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
717 view_populate_pid(view); 722 view_populate_pid(view);
718 view->container = container_create(view); 723 view->container = container_create(view);
719 724
725 if (view->ctx == NULL) {
726 struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid);
727 if (ctx != NULL) {
728 view_assign_ctx(view, ctx);
729 }
730 }
731
720 // If there is a request to be opened fullscreen on a specific output, try 732 // If there is a request to be opened fullscreen on a specific output, try
721 // to honor that request. Otherwise, fallback to assigns, pid mappings, 733 // to honor that request. Otherwise, fallback to assigns, pid mappings,
722 // focused workspace, etc 734 // focused workspace, etc
@@ -754,6 +766,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
754 } 766 }
755 } 767 }
756 768
769 struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
770 .app_id = view_get_app_id(view),
771 .title = view_get_title(view),
772 };
773 view->ext_foreign_toplevel =
774 wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state);
775
757 view->foreign_toplevel = 776 view->foreign_toplevel =
758 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 777 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
759 view->foreign_activate_request.notify = handle_foreign_activate_request; 778 view->foreign_activate_request.notify = handle_foreign_activate_request;
@@ -777,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
777 } 796 }
778 ipc_event_window(view->container, "new"); 797 ipc_event_window(view->container, "new");
779 798
780 view_init_subsurfaces(view, wlr_surface);
781 wl_signal_add(&wlr_surface->events.new_subsurface,
782 &view->surface_new_subsurface);
783 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
784
785 if (decoration) { 799 if (decoration) {
786 view_update_csd_from_client(view, decoration); 800 view_update_csd_from_client(view, decoration);
787 } 801 }
@@ -824,10 +838,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
824 838
825 bool set_focus = should_focus(view); 839 bool set_focus = should_focus(view);
826 840
827#if HAVE_XWAYLAND 841#if WLR_HAS_XWAYLAND
828 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 842 struct wlr_xwayland_surface *xsurface;
829 struct wlr_xwayland_surface *xsurface = 843 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
830 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
831 set_focus &= wlr_xwayland_icccm_input_model(xsurface) != 844 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
832 WLR_ICCCM_INPUT_MODEL_NONE; 845 WLR_ICCCM_INPUT_MODEL_NONE;
833 } 846 }
@@ -837,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
837 input_manager_set_focus(&view->container->node); 850 input_manager_set_focus(&view->container->node);
838 } 851 }
839 852
853 if (view->ext_foreign_toplevel) {
854 update_ext_foreign_toplevel(view);
855 }
856
840 const char *app_id; 857 const char *app_id;
841 const char *class; 858 const char *class;
842 if ((app_id = view_get_app_id(view)) != NULL) { 859 if ((app_id = view_get_app_id(view)) != NULL) {
@@ -847,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
847} 864}
848 865
849void view_unmap(struct sway_view *view) { 866void view_unmap(struct sway_view *view) {
850 wl_signal_emit(&view->events.unmap, view); 867 wl_signal_emit_mutable(&view->events.unmap, view);
851 868
852 wl_list_remove(&view->surface_new_subsurface.link); 869 view->executed_criteria->length = 0;
853 870
854 if (view->urgent_timer) { 871 if (view->urgent_timer) {
855 wl_event_source_remove(view->urgent_timer); 872 wl_event_source_remove(view->urgent_timer);
856 view->urgent_timer = NULL; 873 view->urgent_timer = NULL;
857 } 874 }
858 875
876 if (view->ext_foreign_toplevel) {
877 wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel);
878 view->ext_foreign_toplevel = NULL;
879 }
880
859 if (view->foreign_toplevel) { 881 if (view->foreign_toplevel) {
860 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); 882 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);
861 view->foreign_toplevel = NULL; 883 view->foreign_toplevel = NULL;
@@ -902,291 +924,54 @@ void view_update_size(struct sway_view *view) {
902 container_set_geometry_from_content(con); 924 container_set_geometry_from_content(con);
903} 925}
904 926
905void view_center_surface(struct sway_view *view) { 927void view_center_and_clip_surface(struct sway_view *view) {
906 struct sway_container *con = view->container; 928 struct sway_container *con = view->container;
907 // We always center the current coordinates rather than the next, as the
908 // geometry immediately affects the currently active rendering.
909 con->surface_x = fmax(con->current.content_x, con->current.content_x +
910 (con->current.content_width - view->geometry.width) / 2);
911 con->surface_y = fmax(con->current.content_y, con->current.content_y +
912 (con->current.content_height - view->geometry.height) / 2);
913}
914 929
915static const struct sway_view_child_impl subsurface_impl; 930 bool clip_to_geometry = true;
916 931
917static void subsurface_get_view_coords(struct sway_view_child *child, 932 if (container_is_floating(con)) {
918 int *sx, int *sy) { 933 // We always center the current coordinates rather than the next, as the
919 struct wlr_surface *surface = child->surface; 934 // geometry immediately affects the currently active rendering.
920 if (child->parent && child->parent->impl && 935 int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);
921 child->parent->impl->get_view_coords) { 936 int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);
922 child->parent->impl->get_view_coords(child->parent, sx, sy); 937 clip_to_geometry = !view->using_csd;
923 } else {
924 *sx = *sy = 0;
925 }
926 struct wlr_subsurface *subsurface =
927 wlr_subsurface_from_wlr_surface(surface);
928 *sx += subsurface->current.x;
929 *sy += subsurface->current.y;
930}
931 938
932static void subsurface_destroy(struct sway_view_child *child) { 939 wlr_scene_node_set_position(&view->content_tree->node, x, y);
933 if (!sway_assert(child->impl == &subsurface_impl,
934 "Expected a subsurface")) {
935 return;
936 }
937 struct sway_subsurface *subsurface = (struct sway_subsurface *)child;
938 wl_list_remove(&subsurface->destroy.link);
939 free(subsurface);
940}
941
942static const struct sway_view_child_impl subsurface_impl = {
943 .get_view_coords = subsurface_get_view_coords,
944 .destroy = subsurface_destroy,
945};
946
947static void subsurface_handle_destroy(struct wl_listener *listener,
948 void *data) {
949 struct sway_subsurface *subsurface =
950 wl_container_of(listener, subsurface, destroy);
951 struct sway_view_child *child = &subsurface->child;
952 view_child_destroy(child);
953}
954
955static void view_child_damage(struct sway_view_child *child, bool whole);
956
957static void view_subsurface_create(struct sway_view *view,
958 struct wlr_subsurface *wlr_subsurface) {
959 struct sway_subsurface *subsurface =
960 calloc(1, sizeof(struct sway_subsurface));
961 if (subsurface == NULL) {
962 sway_log(SWAY_ERROR, "Allocation failed");
963 return;
964 }
965 view_child_init(&subsurface->child, &subsurface_impl, view,
966 wlr_subsurface->surface);
967
968 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
969 subsurface->destroy.notify = subsurface_handle_destroy;
970
971 subsurface->child.mapped = true;
972
973 view_child_damage(&subsurface->child, true);
974}
975
976static void view_child_subsurface_create(struct sway_view_child *child,
977 struct wlr_subsurface *wlr_subsurface) {
978 struct sway_subsurface *subsurface =
979 calloc(1, sizeof(struct sway_subsurface));
980 if (subsurface == NULL) {
981 sway_log(SWAY_ERROR, "Allocation failed");
982 return;
983 }
984 subsurface->child.parent = child;
985 wl_list_insert(&child->children, &subsurface->child.link);
986 view_child_init(&subsurface->child, &subsurface_impl, child->view,
987 wlr_subsurface->surface);
988
989 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
990 subsurface->destroy.notify = subsurface_handle_destroy;
991
992 subsurface->child.mapped = true;
993
994 view_child_damage(&subsurface->child, true);
995}
996
997static bool view_child_is_mapped(struct sway_view_child *child) {
998 while (child) {
999 if (!child->mapped) {
1000 return false;
1001 }
1002 child = child->parent;
1003 }
1004 return true;
1005}
1006
1007static void view_child_damage(struct sway_view_child *child, bool whole) {
1008 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
1009 return;
1010 }
1011 int sx, sy;
1012 child->impl->get_view_coords(child, &sx, &sy);
1013 desktop_damage_surface(child->surface,
1014 child->view->container->pending.content_x -
1015 child->view->geometry.x + sx,
1016 child->view->container->pending.content_y -
1017 child->view->geometry.y + sy, whole);
1018}
1019
1020static void view_child_handle_surface_commit(struct wl_listener *listener,
1021 void *data) {
1022 struct sway_view_child *child =
1023 wl_container_of(listener, child, surface_commit);
1024 view_child_damage(child, false);
1025}
1026
1027static void view_child_handle_surface_new_subsurface(
1028 struct wl_listener *listener, void *data) {
1029 struct sway_view_child *child =
1030 wl_container_of(listener, child, surface_new_subsurface);
1031 struct wlr_subsurface *subsurface = data;
1032 view_child_subsurface_create(child, subsurface);
1033}
1034
1035static void view_child_handle_surface_destroy(struct wl_listener *listener,
1036 void *data) {
1037 struct sway_view_child *child =
1038 wl_container_of(listener, child, surface_destroy);
1039 view_child_destroy(child);
1040}
1041
1042static void view_init_subsurfaces(struct sway_view *view,
1043 struct wlr_surface *surface) {
1044 struct wlr_subsurface *subsurface;
1045 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1046 current.link) {
1047 view_subsurface_create(view, subsurface);
1048 }
1049 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1050 current.link) {
1051 view_subsurface_create(view, subsurface);
1052 }
1053}
1054
1055static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1056 struct wlr_surface *surface) {
1057 struct wlr_subsurface *subsurface;
1058 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1059 current.link) {
1060 view_child_subsurface_create(view_child, subsurface);
1061 }
1062 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1063 current.link) {
1064 view_child_subsurface_create(view_child, subsurface);
1065 }
1066}
1067
1068static void view_child_handle_surface_map(struct wl_listener *listener,
1069 void *data) {
1070 struct sway_view_child *child =
1071 wl_container_of(listener, child, surface_map);
1072 child->mapped = true;
1073 view_child_damage(child, true);
1074}
1075
1076static void view_child_handle_surface_unmap(struct wl_listener *listener,
1077 void *data) {
1078 struct sway_view_child *child =
1079 wl_container_of(listener, child, surface_unmap);
1080 view_child_damage(child, true);
1081 child->mapped = false;
1082}
1083
1084static void view_child_handle_view_unmap(struct wl_listener *listener,
1085 void *data) {
1086 struct sway_view_child *child =
1087 wl_container_of(listener, child, view_unmap);
1088 view_child_damage(child, true);
1089 child->mapped = false;
1090}
1091
1092void view_child_init(struct sway_view_child *child,
1093 const struct sway_view_child_impl *impl, struct sway_view *view,
1094 struct wlr_surface *surface) {
1095 child->impl = impl;
1096 child->view = view;
1097 child->surface = surface;
1098 wl_list_init(&child->children);
1099
1100 wl_signal_add(&surface->events.commit, &child->surface_commit);
1101 child->surface_commit.notify = view_child_handle_surface_commit;
1102 wl_signal_add(&surface->events.new_subsurface,
1103 &child->surface_new_subsurface);
1104 child->surface_new_subsurface.notify =
1105 view_child_handle_surface_new_subsurface;
1106 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
1107 child->surface_destroy.notify = view_child_handle_surface_destroy;
1108
1109 // Not all child views have a map/unmap event
1110 child->surface_map.notify = view_child_handle_surface_map;
1111 wl_list_init(&child->surface_map.link);
1112 child->surface_unmap.notify = view_child_handle_surface_unmap;
1113 wl_list_init(&child->surface_unmap.link);
1114
1115 wl_signal_add(&view->events.unmap, &child->view_unmap);
1116 child->view_unmap.notify = view_child_handle_view_unmap;
1117
1118 struct sway_container *container = child->view->container;
1119 if (container != NULL) {
1120 struct sway_workspace *workspace = container->pending.workspace;
1121 if (workspace) {
1122 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1123 }
1124 }
1125
1126 view_child_init_subsurfaces(child, surface);
1127}
1128
1129void view_child_destroy(struct sway_view_child *child) {
1130 if (view_child_is_mapped(child) && child->view->container != NULL) {
1131 view_child_damage(child, true);
1132 }
1133
1134 if (child->parent != NULL) {
1135 wl_list_remove(&child->link);
1136 child->parent = NULL;
1137 }
1138
1139 struct sway_view_child *subchild, *tmpchild;
1140 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1141 wl_list_remove(&subchild->link);
1142 subchild->parent = NULL;
1143 // The subchild lost its parent link, so it cannot see that the parent
1144 // is unmapped. Unmap it directly.
1145 subchild->mapped = false;
1146 }
1147
1148 wl_list_remove(&child->surface_commit.link);
1149 wl_list_remove(&child->surface_destroy.link);
1150 wl_list_remove(&child->surface_map.link);
1151 wl_list_remove(&child->surface_unmap.link);
1152 wl_list_remove(&child->view_unmap.link);
1153 wl_list_remove(&child->surface_new_subsurface.link);
1154
1155 if (child->impl && child->impl->destroy) {
1156 child->impl->destroy(child);
1157 } else { 940 } else {
1158 free(child); 941 wlr_scene_node_set_position(&view->content_tree->node, 0, 0);
942 }
943
944 // only make sure to clip the content if there is content to clip
945 if (!wl_list_empty(&con->view->content_tree->children)) {
946 struct wlr_box clip = {0};
947 if (clip_to_geometry) {
948 clip = (struct wlr_box){
949 .x = con->view->geometry.x,
950 .y = con->view->geometry.y,
951 .width = con->current.content_width,
952 .height = con->current.content_height,
953 };
954 }
955 wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip);
1159 } 956 }
1160} 957}
1161 958
1162struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { 959struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1163 if (wlr_surface_is_xdg_surface(wlr_surface)) { 960 struct wlr_xdg_surface *xdg_surface;
1164 struct wlr_xdg_surface *xdg_surface = 961 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) {
1165 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1166 if (xdg_surface == NULL) {
1167 return NULL;
1168 }
1169 return view_from_wlr_xdg_surface(xdg_surface); 962 return view_from_wlr_xdg_surface(xdg_surface);
1170 } 963 }
1171#if HAVE_XWAYLAND 964#if WLR_HAS_XWAYLAND
1172 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 965 struct wlr_xwayland_surface *xsurface;
1173 struct wlr_xwayland_surface *xsurface = 966 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
1174 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1175 if (xsurface == NULL) {
1176 return NULL;
1177 }
1178 return view_from_wlr_xwayland_surface(xsurface); 967 return view_from_wlr_xwayland_surface(xsurface);
1179 } 968 }
1180#endif 969#endif
1181 if (wlr_surface_is_subsurface(wlr_surface)) { 970 struct wlr_subsurface *subsurface;
1182 struct wlr_subsurface *subsurface = 971 if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) {
1183 wlr_subsurface_from_wlr_surface(wlr_surface);
1184 if (subsurface == NULL) {
1185 return NULL;
1186 }
1187 return view_from_wlr_surface(subsurface->parent); 972 return view_from_wlr_surface(subsurface->parent);
1188 } 973 }
1189 if (wlr_surface_is_layer_surface(wlr_surface)) { 974 if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) {
1190 return NULL; 975 return NULL;
1191 } 976 }
1192 977
@@ -1267,6 +1052,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
1267 return len; 1052 return len;
1268} 1053}
1269 1054
1055void view_update_app_id(struct sway_view *view) {
1056 const char *app_id = view_get_app_id(view);
1057
1058 if (view->foreign_toplevel && app_id) {
1059 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);
1060 }
1061
1062 if (view->ext_foreign_toplevel) {
1063 update_ext_foreign_toplevel(view);
1064 }
1065}
1066
1270void view_update_title(struct sway_view *view, bool force) { 1067void view_update_title(struct sway_view *view, bool force) {
1271 const char *title = view_get_title(view); 1068 const char *title = view_get_title(view);
1272 1069
@@ -1282,29 +1079,41 @@ void view_update_title(struct sway_view *view, bool force) {
1282 1079
1283 free(view->container->title); 1080 free(view->container->title);
1284 free(view->container->formatted_title); 1081 free(view->container->formatted_title);
1285 if (title) { 1082
1286 size_t len = parse_title_format(view, NULL); 1083 size_t len = parse_title_format(view, NULL);
1084
1085 if (len) {
1287 char *buffer = calloc(len + 1, sizeof(char)); 1086 char *buffer = calloc(len + 1, sizeof(char));
1288 if (!sway_assert(buffer, "Unable to allocate title string")) { 1087 if (!sway_assert(buffer, "Unable to allocate title string")) {
1289 return; 1088 return;
1290 } 1089 }
1291 parse_title_format(view, buffer);
1292 1090
1293 view->container->title = strdup(title); 1091 parse_title_format(view, buffer);
1294 view->container->formatted_title = buffer; 1092 view->container->formatted_title = buffer;
1295 } else { 1093 } else {
1296 view->container->title = NULL;
1297 view->container->formatted_title = NULL; 1094 view->container->formatted_title = NULL;
1298 } 1095 }
1299 1096
1097 view->container->title = title ? strdup(title) : NULL;
1098
1300 // Update title after the global font height is updated 1099 // Update title after the global font height is updated
1301 container_update_title_textures(view->container); 1100 if (view->container->title_bar.title_text && len) {
1101 sway_text_node_set_text(view->container->title_bar.title_text,
1102 view->container->formatted_title);
1103 container_arrange_title_bar(view->container);
1104 } else {
1105 container_update_title_bar(view->container);
1106 }
1302 1107
1303 ipc_event_window(view->container, "title"); 1108 ipc_event_window(view->container, "title");
1304 1109
1305 if (view->foreign_toplevel && title) { 1110 if (view->foreign_toplevel && title) {
1306 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); 1111 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1307 } 1112 }
1113
1114 if (view->ext_foreign_toplevel) {
1115 update_ext_foreign_toplevel(view);
1116 }
1308} 1117}
1309 1118
1310bool view_is_visible(struct sway_view *view) { 1119bool view_is_visible(struct sway_view *view) {
@@ -1365,6 +1174,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1365 return; 1174 return;
1366 } 1175 }
1367 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1176 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1177 container_update_itself_and_parents(view->container);
1368 } else { 1178 } else {
1369 view->urgent = (struct timespec){ 0 }; 1179 view->urgent = (struct timespec){ 0 };
1370 if (view->urgent_timer) { 1180 if (view->urgent_timer) {
@@ -1372,7 +1182,6 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1372 view->urgent_timer = NULL; 1182 view->urgent_timer = NULL;
1373 } 1183 }
1374 } 1184 }
1375 container_damage_whole(view->container);
1376 1185
1377 ipc_event_window(view->container, "urgent"); 1186 ipc_event_window(view->container, "urgent");
1378 1187
@@ -1386,40 +1195,54 @@ bool view_is_urgent(struct sway_view *view) {
1386} 1195}
1387 1196
1388void view_remove_saved_buffer(struct sway_view *view) { 1197void view_remove_saved_buffer(struct sway_view *view) {
1389 if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { 1198 if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) {
1390 return; 1199 return;
1391 } 1200 }
1392 struct sway_saved_buffer *saved_buf, *tmp; 1201
1393 wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { 1202 wlr_scene_node_destroy(&view->saved_surface_tree->node);
1394 wlr_buffer_unlock(&saved_buf->buffer->base); 1203 view->saved_surface_tree = NULL;
1395 wl_list_remove(&saved_buf->link); 1204 wlr_scene_node_set_enabled(&view->content_tree->node, true);
1396 free(saved_buf);
1397 }
1398} 1205}
1399 1206
1400static void view_save_buffer_iterator(struct wlr_surface *surface, 1207static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,
1401 int sx, int sy, void *data) { 1208 int sx, int sy, void *data) {
1402 struct sway_view *view = data; 1209 struct wlr_scene_tree *tree = data;
1403 1210
1404 if (surface && wlr_surface_has_buffer(surface)) { 1211 struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL);
1405 wlr_buffer_lock(&surface->buffer->base); 1212 if (!sbuf) {
1406 struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); 1213 sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface");
1407 saved_buffer->buffer = surface->buffer; 1214 return;
1408 saved_buffer->width = surface->current.width;
1409 saved_buffer->height = surface->current.height;
1410 saved_buffer->x = view->container->surface_x + sx;
1411 saved_buffer->y = view->container->surface_y + sy;
1412 saved_buffer->transform = surface->current.transform;
1413 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1414 wl_list_insert(view->saved_buffers.prev, &saved_buffer->link);
1415 } 1215 }
1216
1217 wlr_scene_buffer_set_dest_size(sbuf,
1218 buffer->dst_width, buffer->dst_height);
1219 wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region);
1220 wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box);
1221 wlr_scene_node_set_position(&sbuf->node, sx, sy);
1222 wlr_scene_buffer_set_transform(sbuf, buffer->transform);
1223 wlr_scene_buffer_set_buffer(sbuf, buffer->buffer);
1416} 1224}
1417 1225
1418void view_save_buffer(struct sway_view *view) { 1226void view_save_buffer(struct sway_view *view) {
1419 if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { 1227 if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) {
1420 view_remove_saved_buffer(view); 1228 view_remove_saved_buffer(view);
1421 } 1229 }
1422 view_for_each_surface(view, view_save_buffer_iterator, view); 1230
1231 view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree);
1232 if (!view->saved_surface_tree) {
1233 sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface");
1234 return;
1235 }
1236
1237 // Enable and disable the saved surface tree like so to atomitaclly update
1238 // the tree. This will prevent over damaging or other weirdness.
1239 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false);
1240
1241 wlr_scene_node_for_each_buffer(&view->content_tree->node,
1242 view_save_buffer_iterator, view->saved_surface_tree);
1243
1244 wlr_scene_node_set_enabled(&view->content_tree->node, false);
1245 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true);
1423} 1246}
1424 1247
1425bool view_is_transient_for(struct sway_view *child, 1248bool view_is_transient_for(struct sway_view *child,
@@ -1427,3 +1250,19 @@ bool view_is_transient_for(struct sway_view *child,
1427 return child->impl->is_transient_for && 1250 return child->impl->is_transient_for &&
1428 child->impl->is_transient_for(child, ancestor); 1251 child->impl->is_transient_for(child, ancestor);
1429} 1252}
1253
1254static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,
1255 int x, int y, void *data) {
1256 struct timespec *when = data;
1257 wl_signal_emit_mutable(&scene_buffer->events.frame_done, when);
1258}
1259
1260void view_send_frame_done(struct sway_view *view) {
1261 struct timespec when;
1262 clock_gettime(CLOCK_MONOTONIC, &when);
1263
1264 struct wlr_scene_node *node;
1265 wl_list_for_each(node, &view->content_tree->children, link) {
1266 wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when);
1267 }
1268}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index c84320bd..52e48ad5 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -11,6 +10,7 @@
11#include "sway/input/seat.h" 10#include "sway/input/seat.h"
12#include "sway/ipc-server.h" 11#include "sway/ipc-server.h"
13#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/server.h"
14#include "sway/tree/arrange.h" 14#include "sway/tree/arrange.h"
15#include "sway/tree/container.h" 15#include "sway/tree/container.h"
16#include "sway/tree/node.h" 16#include "sway/tree/node.h"
@@ -56,6 +56,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
56 56
57struct sway_workspace *workspace_create(struct sway_output *output, 57struct sway_workspace *workspace_create(struct sway_output *output,
58 const char *name) { 58 const char *name) {
59 sway_assert(name, "NULL name given to workspace_create");
60
59 if (output == NULL) { 61 if (output == NULL) {
60 output = workspace_get_initial_output(name); 62 output = workspace_get_initial_output(name);
61 } 63 }
@@ -69,7 +71,19 @@ struct sway_workspace *workspace_create(struct sway_output *output,
69 return NULL; 71 return NULL;
70 } 72 }
71 node_init(&ws->node, N_WORKSPACE, ws); 73 node_init(&ws->node, N_WORKSPACE, ws);
72 ws->name = name ? strdup(name) : NULL; 74
75 bool failed = false;
76 ws->layers.tiling = alloc_scene_tree(root->staging, &failed);
77 ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
78
79 if (failed) {
80 wlr_scene_node_destroy(&ws->layers.tiling->node);
81 wlr_scene_node_destroy(&ws->layers.fullscreen->node);
82 free(ws);
83 return NULL;
84 }
85
86 ws->name = strdup(name);
73 ws->prev_split_layout = L_NONE; 87 ws->prev_split_layout = L_NONE;
74 ws->layout = output_get_default_layout(output); 88 ws->layout = output_get_default_layout(output);
75 ws->floating = create_list(); 89 ws->floating = create_list();
@@ -114,7 +128,7 @@ struct sway_workspace *workspace_create(struct sway_output *output,
114 output_sort_workspaces(output); 128 output_sort_workspaces(output);
115 129
116 ipc_event_workspace(NULL, ws, "init"); 130 ipc_event_workspace(NULL, ws, "init");
117 wl_signal_emit(&root->events.new_node, &ws->node); 131 wl_signal_emit_mutable(&root->events.new_node, &ws->node);
118 132
119 return ws; 133 return ws;
120} 134}
@@ -129,6 +143,11 @@ void workspace_destroy(struct sway_workspace *workspace) {
129 return; 143 return;
130 } 144 }
131 145
146 scene_node_disown_children(workspace->layers.tiling);
147 scene_node_disown_children(workspace->layers.fullscreen);
148 wlr_scene_node_destroy(&workspace->layers.tiling->node);
149 wlr_scene_node_destroy(&workspace->layers.fullscreen->node);
150
132 free(workspace->name); 151 free(workspace->name);
133 free(workspace->representation); 152 free(workspace->representation);
134 list_free_items_and_destroy(workspace->output_priority); 153 list_free_items_and_destroy(workspace->output_priority);
@@ -142,7 +161,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
142void workspace_begin_destroy(struct sway_workspace *workspace) { 161void workspace_begin_destroy(struct sway_workspace *workspace) {
143 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); 162 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name);
144 ipc_event_workspace(NULL, workspace, "empty"); // intentional 163 ipc_event_workspace(NULL, workspace, "empty"); // intentional
145 wl_signal_emit(&workspace->node.events.destroy, &workspace->node); 164 wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node);
146 165
147 if (workspace->output) { 166 if (workspace->output) {
148 workspace_detach(workspace); 167 workspace_detach(workspace);
@@ -174,22 +193,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) {
174static bool workspace_valid_on_output(const char *output_name, 193static bool workspace_valid_on_output(const char *output_name,
175 const char *ws_name) { 194 const char *ws_name) {
176 struct workspace_config *wsc = workspace_find_config(ws_name); 195 struct workspace_config *wsc = workspace_find_config(ws_name);
177 char identifier[128];
178 struct sway_output *output = output_by_name_or_id(output_name); 196 struct sway_output *output = output_by_name_or_id(output_name);
179 if (!output) { 197 if (!output) {
180 return false; 198 return false;
181 } 199 }
182 output_name = output->wlr_output->name;
183 output_get_identifier(identifier, sizeof(identifier), output);
184
185 if (!wsc) { 200 if (!wsc) {
186 return true; 201 return true;
187 } 202 }
188 203
189 for (int i = 0; i < wsc->outputs->length; i++) { 204 for (int i = 0; i < wsc->outputs->length; i++) {
190 if (strcmp(wsc->outputs->items[i], "*") == 0 || 205 if (output_match_name_or_id(output, wsc->outputs->items[i])) {
191 strcmp(wsc->outputs->items[i], output_name) == 0 ||
192 strcmp(wsc->outputs->items[i], identifier) == 0) {
193 return true; 206 return true;
194 } 207 }
195 } 208 }
@@ -284,13 +297,10 @@ char *workspace_next_name(const char *output_name) {
284 // assignments primarily, falling back to bindings and numbers. 297 // assignments primarily, falling back to bindings and numbers.
285 struct sway_mode *mode = config->current_mode; 298 struct sway_mode *mode = config->current_mode;
286 299
287 char identifier[128];
288 struct sway_output *output = output_by_name_or_id(output_name); 300 struct sway_output *output = output_by_name_or_id(output_name);
289 if (!output) { 301 if (!output) {
290 return NULL; 302 return NULL;
291 } 303 }
292 output_name = output->wlr_output->name;
293 output_get_identifier(identifier, sizeof(identifier), output);
294 304
295 int order = INT_MAX; 305 int order = INT_MAX;
296 char *target = NULL; 306 char *target = NULL;
@@ -310,9 +320,7 @@ char *workspace_next_name(const char *output_name) {
310 } 320 }
311 bool found = false; 321 bool found = false;
312 for (int j = 0; j < wsc->outputs->length; ++j) { 322 for (int j = 0; j < wsc->outputs->length; ++j) {
313 if (strcmp(wsc->outputs->items[j], "*") == 0 || 323 if (output_match_name_or_id(output, wsc->outputs->items[j])) {
314 strcmp(wsc->outputs->items[j], output_name) == 0 ||
315 strcmp(wsc->outputs->items[j], identifier) == 0) {
316 found = true; 324 found = true;
317 free(target); 325 free(target);
318 target = strdup(wsc->workspace); 326 target = strdup(wsc->workspace);
@@ -652,15 +660,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
652 660
653struct sway_output *workspace_output_get_highest_available( 661struct sway_output *workspace_output_get_highest_available(
654 struct sway_workspace *ws, struct sway_output *exclude) { 662 struct sway_workspace *ws, struct sway_output *exclude) {
655 char exclude_id[128] = {'\0'};
656 if (exclude) {
657 output_get_identifier(exclude_id, sizeof(exclude_id), exclude);
658 }
659
660 for (int i = 0; i < ws->output_priority->length; i++) { 663 for (int i = 0; i < ws->output_priority->length; i++) {
661 char *name = ws->output_priority->items[i]; 664 const char *name = ws->output_priority->items[i];
662 if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 665 if (exclude && output_match_name_or_id(exclude, name)) {
663 || strcmp(name, exclude_id) == 0)) {
664 continue; 666 continue;
665 } 667 }
666 668
@@ -684,7 +686,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) {
684 if (workspace->urgent != new_urgent) { 686 if (workspace->urgent != new_urgent) {
685 workspace->urgent = new_urgent; 687 workspace->urgent = new_urgent;
686 ipc_event_workspace(NULL, workspace, "urgent"); 688 ipc_event_workspace(NULL, workspace, "urgent");
687 output_damage_whole(workspace->output);
688 } 689 }
689} 690}
690 691