aboutsummaryrefslogtreecommitdiffstats
path: root/sway/tree/container.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree/container.c')
-rw-r--r--sway/tree/container.c1685
1 files changed, 915 insertions, 770 deletions
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 6a9ce1c4..30cb97ba 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,28 +1,77 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <drm_fourcc.h>
3#include <stdint.h> 4#include <stdint.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <string.h>
6#include <strings.h>
7#include <wayland-server-core.h> 6#include <wayland-server-core.h>
7#include <wlr/types/wlr_linux_dmabuf_v1.h>
8#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
9#include "cairo.h" 9#include <wlr/types/wlr_subcompositor.h>
10#include "pango.h" 10#include "linux-dmabuf-unstable-v1-protocol.h"
11#include "sway/config.h" 11#include "sway/config.h"
12#include "sway/desktop.h"
13#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
14#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
15#include "sway/input/seat.h" 14#include "sway/input/seat.h"
16#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/scene_descriptor.h"
17#include "sway/sway_text_node.h"
17#include "sway/output.h" 18#include "sway/output.h"
18#include "sway/server.h" 19#include "sway/server.h"
19#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
20#include "sway/tree/view.h" 21#include "sway/tree/view.h"
21#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
23#include "sway/xdg_decoration.h"
22#include "list.h" 24#include "list.h"
23#include "log.h" 25#include "log.h"
24#include "stringop.h" 26#include "stringop.h"
25 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
26struct sway_container *container_create(struct sway_view *view) { 75struct sway_container *container_create(struct sway_view *view) {
27 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 76 struct sway_container *c = calloc(1, sizeof(struct sway_container));
28 if (!c) { 77 if (!c) {
@@ -30,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) {
30 return NULL; 79 return NULL;
31 } 80 }
32 node_init(&c->node, N_CONTAINER, c); 81 node_init(&c->node, N_CONTAINER, c);
33 c->layout = L_NONE; 82
34 c->view = view; 83 // Container tree structure
35 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 }
36 151
37 if (!view) { 152 if (!view) {
38 c->children = create_list(); 153 c->pending.children = create_list();
39 c->current.children = create_list(); 154 c->current.children = create_list();
40 } 155 }
156
157 c->pending.layout = L_NONE;
158 c->view = view;
159 c->alpha = 1.0f;
41 c->marks = create_list(); 160 c->marks = create_list();
42 c->outputs = create_list();
43 161
44 wl_signal_init(&c->events.destroy); 162 wl_signal_init(&c->events.destroy);
45 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);
46 166
47 return c; 167 return c;
48} 168}
49 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 sway_text_node_set_max_width(node, alloc_width);
356 wlr_scene_node_set_position(node->node,
357 h_padding, (height - node->height) >> 1);
358
359 pixman_region32_union_rect(&text_area, &text_area,
360 node->node->x, node->node->y, alloc_width, node->height);
361 }
362
363 if (con->title_bar.title_text) {
364 struct sway_text_node *node = con->title_bar.title_text;
365
366 int h_padding;
367 if (title_align == ALIGN_RIGHT) {
368 h_padding = width - config->titlebar_h_padding - node->width;
369 } else if (title_align == ALIGN_CENTER) {
370 h_padding = ((int)width - marks_buffer_width - node->width) >> 1;
371 } else {
372 h_padding = config->titlebar_h_padding;
373 }
374
375 h_padding = MAX(h_padding, 0);
376
377 int alloc_width = MIN((int) node->width,
378 width - h_padding - config->titlebar_h_padding);
379 sway_text_node_set_max_width(node, alloc_width);
380 wlr_scene_node_set_position(node->node,
381 h_padding, (height - node->height) >> 1);
382
383 pixman_region32_union_rect(&text_area, &text_area,
384 node->node->x, node->node->y, alloc_width, node->height);
385 }
386
387 // silence pixman errors
388 if (width <= 0 || height <= 0) {
389 pixman_region32_fini(&text_area);
390 return;
391 }
392
393 pixman_region32_t background, border;
394
395 int thickness = config->titlebar_border_thickness;
396 pixman_region32_init_rect(&background,
397 thickness, thickness,
398 width - thickness * 2, height - thickness * 2);
399 pixman_region32_init_rect(&border, 0, 0, width, height);
400 pixman_region32_subtract(&border, &border, &background);
401
402 pixman_region32_subtract(&background, &background, &text_area);
403 pixman_region32_fini(&text_area);
404
405 update_rect_list(con->title_bar.background, &background);
406 pixman_region32_fini(&background);
407
408 update_rect_list(con->title_bar.border, &border);
409 pixman_region32_fini(&border);
410
411 container_update(con);
412}
413
414void container_update_marks(struct sway_container *con) {
415 char *buffer = NULL;
416
417 if (config->show_marks && con->marks->length) {
418 size_t len = 0;
419 for (int i = 0; i < con->marks->length; ++i) {
420 char *mark = con->marks->items[i];
421 if (mark[0] != '_') {
422 len += strlen(mark) + 2;
423 }
424 }
425 buffer = calloc(len + 1, 1);
426 char *part = malloc(len + 1);
427
428 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
429 free(buffer);
430 return;
431 }
432
433 for (int i = 0; i < con->marks->length; ++i) {
434 char *mark = con->marks->items[i];
435 if (mark[0] != '_') {
436 snprintf(part, len + 1, "[%s]", mark);
437 strcat(buffer, part);
438 }
439 }
440 free(part);
441 }
442
443 if (!buffer) {
444 if (con->title_bar.marks_text) {
445 wlr_scene_node_destroy(con->title_bar.marks_text->node);
446 con->title_bar.marks_text = NULL;
447 }
448 } else if (!con->title_bar.marks_text) {
449 struct border_colors *colors = container_get_current_colors(con);
450
451 con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree,
452 buffer, colors->text, false);
453 } else {
454 sway_text_node_set_text(con->title_bar.marks_text, buffer);
455 }
456
457 container_arrange_title_bar(con);
458 free(buffer);
459}
460
461void container_update_title_bar(struct sway_container *con) {
462 if (!con->formatted_title) {
463 return;
464 }
465
466 struct border_colors *colors = container_get_current_colors(con);
467
468 if (con->title_bar.title_text) {
469 wlr_scene_node_destroy(con->title_bar.title_text->node);
470 con->title_bar.title_text = NULL;
471 }
472
473 con->title_bar.title_text = sway_text_node_create(con->title_bar.tree,
474 con->formatted_title, colors->text, config->pango_markup);
475
476 // we always have to remake these text buffers completely for text font
477 // changes etc...
478 if (con->title_bar.marks_text) {
479 wlr_scene_node_destroy(con->title_bar.marks_text->node);
480 con->title_bar.marks_text = NULL;
481 }
482
483 container_update_marks(con);
484 container_arrange_title_bar(con);
485}
486
50void container_destroy(struct sway_container *con) { 487void container_destroy(struct sway_container *con) {
51 if (!sway_assert(con->node.destroying, 488 if (!sway_assert(con->node.destroying,
52 "Tried to free container which wasn't marked as destroying")) { 489 "Tried to free container which wasn't marked as destroying")) {
@@ -58,29 +495,21 @@ void container_destroy(struct sway_container *con) {
58 } 495 }
59 free(con->title); 496 free(con->title);
60 free(con->formatted_title); 497 free(con->formatted_title);
61 wlr_texture_destroy(con->title_focused); 498 list_free(con->pending.children);
62 wlr_texture_destroy(con->title_focused_inactive);
63 wlr_texture_destroy(con->title_unfocused);
64 wlr_texture_destroy(con->title_urgent);
65 list_free(con->children);
66 list_free(con->current.children); 499 list_free(con->current.children);
67 list_free(con->outputs);
68 500
69 list_free_items_and_destroy(con->marks); 501 list_free_items_and_destroy(con->marks);
70 wlr_texture_destroy(con->marks_focused);
71 wlr_texture_destroy(con->marks_focused_inactive);
72 wlr_texture_destroy(con->marks_unfocused);
73 wlr_texture_destroy(con->marks_urgent);
74 502
75 if (con->view) { 503 if (con->view && con->view->container == con) {
76 if (con->view->container == con) { 504 con->view->container = NULL;
77 con->view->container = NULL; 505 wlr_scene_node_destroy(&con->output_handler->node);
78 }
79 if (con->view->destroying) { 506 if (con->view->destroying) {
80 view_destroy(con->view); 507 view_destroy(con->view);
81 } 508 }
82 } 509 }
83 510
511 scene_node_disown_children(con->content_tree);
512 wlr_scene_node_destroy(&con->scene_tree->node);
84 free(con); 513 free(con);
85} 514}
86 515
@@ -90,14 +519,14 @@ void container_begin_destroy(struct sway_container *con) {
90 } 519 }
91 // The workspace must have the fullscreen pointer cleared so that the 520 // The workspace must have the fullscreen pointer cleared so that the
92 // seat code can find an appropriate new focus. 521 // seat code can find an appropriate new focus.
93 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { 522 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) {
94 con->workspace->fullscreen = NULL; 523 con->pending.workspace->fullscreen = NULL;
95 } 524 }
96 if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { 525 if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
97 container_fullscreen_disable(con); 526 container_fullscreen_disable(con);
98 } 527 }
99 528
100 wl_signal_emit(&con->node.events.destroy, &con->node); 529 wl_signal_emit_mutable(&con->node.events.destroy, &con->node);
101 530
102 container_end_mouse_operation(con); 531 container_end_mouse_operation(con);
103 532
@@ -108,11 +537,11 @@ void container_begin_destroy(struct sway_container *con) {
108 root_scratchpad_remove_container(con); 537 root_scratchpad_remove_container(con);
109 } 538 }
110 539
111 if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { 540 if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
112 container_fullscreen_disable(con); 541 container_fullscreen_disable(con);
113 } 542 }
114 543
115 if (con->parent || con->workspace) { 544 if (con->pending.parent || con->pending.workspace) {
116 container_detach(con); 545 container_detach(con);
117 } 546 }
118} 547}
@@ -121,12 +550,12 @@ void container_reap_empty(struct sway_container *con) {
121 if (con->view) { 550 if (con->view) {
122 return; 551 return;
123 } 552 }
124 struct sway_workspace *ws = con->workspace; 553 struct sway_workspace *ws = con->pending.workspace;
125 while (con) { 554 while (con) {
126 if (con->children->length) { 555 if (con->pending.children->length) {
127 return; 556 return;
128 } 557 }
129 struct sway_container *parent = con->parent; 558 struct sway_container *parent = con->pending.parent;
130 container_begin_destroy(con); 559 container_begin_destroy(con);
131 con = parent; 560 con = parent;
132 } 561 }
@@ -139,9 +568,9 @@ struct sway_container *container_flatten(struct sway_container *container) {
139 if (container->view) { 568 if (container->view) {
140 return NULL; 569 return NULL;
141 } 570 }
142 while (container && container->children->length == 1) { 571 while (container && container->pending.children->length == 1) {
143 struct sway_container *child = container->children->items[0]; 572 struct sway_container *child = container->pending.children->items[0];
144 struct sway_container *parent = container->parent; 573 struct sway_container *parent = container->pending.parent;
145 container_replace(container, child); 574 container_replace(container, child);
146 container_begin_destroy(container); 575 container_begin_destroy(container);
147 container = parent; 576 container = parent;
@@ -151,11 +580,11 @@ struct sway_container *container_flatten(struct sway_container *container) {
151 580
152struct sway_container *container_find_child(struct sway_container *container, 581struct sway_container *container_find_child(struct sway_container *container,
153 bool (*test)(struct sway_container *con, void *data), void *data) { 582 bool (*test)(struct sway_container *con, void *data), void *data) {
154 if (!container->children) { 583 if (!container->pending.children) {
155 return NULL; 584 return NULL;
156 } 585 }
157 for (int i = 0; i < container->children->length; ++i) { 586 for (int i = 0; i < container->pending.children->length; ++i) {
158 struct sway_container *child = container->children->items[i]; 587 struct sway_container *child = container->pending.children->items[i];
159 if (test(child, data)) { 588 if (test(child, data)) {
160 return child; 589 return child;
161 } 590 }
@@ -167,260 +596,44 @@ struct sway_container *container_find_child(struct sway_container *container,
167 return NULL; 596 return NULL;
168} 597}
169 598
170static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, 599void container_for_each_child(struct sway_container *container,
171 struct wlr_surface **surface, double *sx, double *sy) { 600 void (*f)(struct sway_container *container, void *data),
172 if (!sway_assert(con->view, "Expected a view")) { 601 void *data) {
173 return NULL; 602 if (container->pending.children) {
174 } 603 for (int i = 0; i < container->pending.children->length; ++i) {
175 struct sway_view *view = con->view; 604 struct sway_container *child = container->pending.children->items[i];
176 double view_sx = lx - con->surface_x + view->geometry.x; 605 f(child, data);
177 double view_sy = ly - con->surface_y + view->geometry.y; 606 container_for_each_child(child, f, data);
178
179 double _sx, _sy;
180 struct wlr_surface *_surface = NULL;
181 switch (view->type) {
182#if HAVE_XWAYLAND
183 case SWAY_VIEW_XWAYLAND:
184 _surface = wlr_surface_surface_at(view->surface,
185 view_sx, view_sy, &_sx, &_sy);
186 break;
187#endif
188 case SWAY_VIEW_XDG_SHELL:
189 _surface = wlr_xdg_surface_surface_at(
190 view->wlr_xdg_surface,
191 view_sx, view_sy, &_sx, &_sy);
192 break;
193 }
194 if (_surface) {
195 *sx = _sx;
196 *sy = _sy;
197 *surface = _surface;
198 return con;
199 }
200 return NULL;
201}
202
203/**
204 * container_at for a container with layout L_TABBED.
205 */
206static struct sway_container *container_at_tabbed(struct sway_node *parent,
207 double lx, double ly,
208 struct wlr_surface **surface, double *sx, double *sy) {
209 struct wlr_box box;
210 node_get_box(parent, &box);
211 if (lx < box.x || lx > box.x + box.width ||
212 ly < box.y || ly > box.y + box.height) {
213 return NULL;
214 }
215 struct sway_seat *seat = input_manager_current_seat();
216 list_t *children = node_get_children(parent);
217 if (!children->length) {
218 return NULL;
219 }
220
221 // Tab titles
222 int title_height = container_titlebar_height();
223 if (ly < box.y + title_height) {
224 int tab_width = box.width / children->length;
225 int child_index = (lx - box.x) / tab_width;
226 if (child_index >= children->length) {
227 child_index = children->length - 1;
228 }
229 struct sway_container *child = children->items[child_index];
230 return child;
231 }
232
233 // Surfaces
234 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
235 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
236}
237
238/**
239 * container_at for a container with layout L_STACKED.
240 */
241static struct sway_container *container_at_stacked(struct sway_node *parent,
242 double lx, double ly,
243 struct wlr_surface **surface, double *sx, double *sy) {
244 struct wlr_box box;
245 node_get_box(parent, &box);
246 if (lx < box.x || lx > box.x + box.width ||
247 ly < box.y || ly > box.y + box.height) {
248 return NULL;
249 }
250 struct sway_seat *seat = input_manager_current_seat();
251 list_t *children = node_get_children(parent);
252
253 // Title bars
254 int title_height = container_titlebar_height();
255 if (title_height > 0) {
256 int child_index = (ly - box.y) / title_height;
257 if (child_index < children->length) {
258 struct sway_container *child = children->items[child_index];
259 return child;
260 } 607 }
261 } 608 }
262
263 // Surfaces
264 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
265 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
266} 609}
267 610
268/** 611struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container)
269 * container_at for a container with layout L_HORIZ or L_VERT. 612{
270 */ 613 struct sway_workspace *workspace = container->pending.workspace;
271static struct sway_container *container_at_linear(struct sway_node *parent,
272 double lx, double ly,
273 struct wlr_surface **surface, double *sx, double *sy) {
274 list_t *children = node_get_children(parent);
275 for (int i = 0; i < children->length; ++i) {
276 struct sway_container *child = children->items[i];
277 struct sway_container *container =
278 tiling_container_at(&child->node, lx, ly, surface, sx, sy);
279 if (container) {
280 return container;
281 }
282 }
283 return NULL;
284}
285 614
286static struct sway_container *floating_container_at(double lx, double ly, 615 if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) {
287 struct wlr_surface **surface, double *sx, double *sy) { 616 if (container_is_transient_for(container, workspace->fullscreen)) {
288 // For outputs with floating containers that overhang the output bounds, 617 return NULL;
289 // those at the end of the output list appear on top of floating
290 // containers from other outputs, so iterate the list in reverse.
291 for (int i = root->outputs->length - 1; i >= 0; --i) {
292 struct sway_output *output = root->outputs->items[i];
293 for (int j = 0; j < output->workspaces->length; ++j) {
294 struct sway_workspace *ws = output->workspaces->items[j];
295 if (!workspace_is_visible(ws)) {
296 continue;
297 }
298 // Items at the end of the list are on top, so iterate the list in
299 // reverse.
300 for (int k = ws->floating->length - 1; k >= 0; --k) {
301 struct sway_container *floater = ws->floating->items[k];
302 struct sway_container *container =
303 tiling_container_at(&floater->node, lx, ly, surface, sx, sy);
304 if (container) {
305 return container;
306 }
307 }
308 } 618 }
309 } 619 return workspace->fullscreen;
310 return NULL;
311}
312
313struct sway_container *view_container_at(struct sway_node *parent,
314 double lx, double ly,
315 struct wlr_surface **surface, double *sx, double *sy) {
316 if (!sway_assert(node_is_view(parent), "Expected a view")) {
317 return NULL;
318 }
319
320 struct sway_container *container = parent->sway_container;
321 struct wlr_box box = {
322 .x = container->x,
323 .y = container->y,
324 .width = container->width,
325 .height = container->height,
326 };
327
328 if (wlr_box_contains_point(&box, lx, ly)) {
329 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
330 return container;
331 } 620 }
332 621
333 return NULL; 622 struct sway_container *fullscreen_global = root->fullscreen_global;
334} 623 if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) {
335 624 if (container_is_transient_for(container, fullscreen_global)) {
336struct sway_container *tiling_container_at(struct sway_node *parent, 625 return NULL;
337 double lx, double ly,
338 struct wlr_surface **surface, double *sx, double *sy) {
339 if (node_is_view(parent)) {
340 return view_container_at(parent, lx, ly, surface, sx, sy);
341 }
342 if (!node_get_children(parent)) {
343 return NULL;
344 }
345 switch (node_get_layout(parent)) {
346 case L_HORIZ:
347 case L_VERT:
348 return container_at_linear(parent, lx, ly, surface, sx, sy);
349 case L_TABBED:
350 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
351 case L_STACKED:
352 return container_at_stacked(parent, lx, ly, surface, sx, sy);
353 case L_NONE:
354 return NULL;
355 }
356 return NULL;
357}
358
359static bool surface_is_popup(struct wlr_surface *surface) {
360 if (wlr_surface_is_xdg_surface(surface)) {
361 struct wlr_xdg_surface *xdg_surface =
362 wlr_xdg_surface_from_wlr_surface(surface);
363 while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) {
364 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
365 return true;
366 }
367 xdg_surface = xdg_surface->toplevel->parent;
368 } 626 }
369 return false; 627 return fullscreen_global;
370 } 628 }
371 629
372 return false;
373}
374
375struct sway_container *container_at(struct sway_workspace *workspace,
376 double lx, double ly,
377 struct wlr_surface **surface, double *sx, double *sy) {
378 struct sway_container *c;
379
380 struct sway_seat *seat = input_manager_current_seat();
381 struct sway_container *focus = seat_get_focused_container(seat);
382 bool is_floating = focus && container_is_floating_or_child(focus);
383 // Focused view's popups
384 if (focus && focus->view) {
385 c = surface_at_view(focus, lx, ly, surface, sx, sy);
386 if (c && surface_is_popup(*surface)) {
387 return c;
388 }
389 *surface = NULL;
390 }
391 // Floating
392 if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) {
393 return c;
394 }
395 // Tiling (focused)
396 if (focus && focus->view && !is_floating) {
397 if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) {
398 return c;
399 }
400 }
401 // Tiling (non-focused)
402 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
403 return c;
404 }
405 return NULL; 630 return NULL;
406} 631}
407 632
408void container_for_each_child(struct sway_container *container,
409 void (*f)(struct sway_container *container, void *data),
410 void *data) {
411 if (container->children) {
412 for (int i = 0; i < container->children->length; ++i) {
413 struct sway_container *child = container->children->items[i];
414 f(child, data);
415 container_for_each_child(child, f, data);
416 }
417 }
418}
419
420bool container_has_ancestor(struct sway_container *descendant, 633bool container_has_ancestor(struct sway_container *descendant,
421 struct sway_container *ancestor) { 634 struct sway_container *ancestor) {
422 while (descendant) { 635 while (descendant) {
423 descendant = descendant->parent; 636 descendant = descendant->pending.parent;
424 if (descendant == ancestor) { 637 if (descendant == ancestor) {
425 return true; 638 return true;
426 } 639 }
@@ -428,119 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant,
428 return false; 641 return false;
429} 642}
430 643
431void container_damage_whole(struct sway_container *container) {
432 for (int i = 0; i < root->outputs->length; ++i) {
433 struct sway_output *output = root->outputs->items[i];
434 output_damage_whole_container(output, container);
435 }
436}
437
438/**
439 * Return the output which will be used for scale purposes.
440 * This is the most recently entered output.
441 */
442struct sway_output *container_get_effective_output(struct sway_container *con) {
443 if (con->outputs->length == 0) {
444 return NULL;
445 }
446 return con->outputs->items[con->outputs->length - 1];
447}
448
449static void update_title_texture(struct sway_container *con,
450 struct wlr_texture **texture, struct border_colors *class) {
451 struct sway_output *output = container_get_effective_output(con);
452 if (!output) {
453 return;
454 }
455 if (*texture) {
456 wlr_texture_destroy(*texture);
457 *texture = NULL;
458 }
459 if (!con->formatted_title) {
460 return;
461 }
462
463 double scale = output->wlr_output->scale;
464 int width = 0;
465 int height = con->title_height * scale;
466
467 // We must use a non-nil cairo_t for cairo_set_font_options to work.
468 // Therefore, we cannot use cairo_create(NULL).
469 cairo_surface_t *dummy_surface = cairo_image_surface_create(
470 CAIRO_FORMAT_ARGB32, 0, 0);
471 cairo_t *c = cairo_create(dummy_surface);
472 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
473 cairo_font_options_t *fo = cairo_font_options_create();
474 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
475 if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
476 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
477 } else {
478 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
479 cairo_font_options_set_subpixel_order(fo,
480 to_cairo_subpixel_order(output->wlr_output->subpixel));
481 }
482 cairo_set_font_options(c, fo);
483 get_text_size(c, config->font, &width, NULL, NULL, scale,
484 config->pango_markup, "%s", con->formatted_title);
485 cairo_surface_destroy(dummy_surface);
486 cairo_destroy(c);
487
488 cairo_surface_t *surface = cairo_image_surface_create(
489 CAIRO_FORMAT_ARGB32, width, height);
490 cairo_t *cairo = cairo_create(surface);
491 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
492 cairo_set_font_options(cairo, fo);
493 cairo_font_options_destroy(fo);
494 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
495 class->background[2], class->background[3]);
496 cairo_paint(cairo);
497 PangoContext *pango = pango_cairo_create_context(cairo);
498 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
499 class->text[2], class->text[3]);
500 cairo_move_to(cairo, 0, 0);
501
502 pango_printf(cairo, config->font, scale, config->pango_markup,
503 "%s", con->formatted_title);
504
505 cairo_surface_flush(surface);
506 unsigned char *data = cairo_image_surface_get_data(surface);
507 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
508 struct wlr_renderer *renderer = wlr_backend_get_renderer(
509 output->wlr_output->backend);
510 *texture = wlr_texture_from_pixels(
511 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
512 cairo_surface_destroy(surface);
513 g_object_unref(pango);
514 cairo_destroy(cairo);
515}
516
517void container_update_title_textures(struct sway_container *container) {
518 update_title_texture(container, &container->title_focused,
519 &config->border_colors.focused);
520 update_title_texture(container, &container->title_focused_inactive,
521 &config->border_colors.focused_inactive);
522 update_title_texture(container, &container->title_unfocused,
523 &config->border_colors.unfocused);
524 update_title_texture(container, &container->title_urgent,
525 &config->border_colors.urgent);
526 container_damage_whole(container);
527}
528
529void container_calculate_title_height(struct sway_container *container) {
530 if (!container->formatted_title) {
531 container->title_height = 0;
532 return;
533 }
534 cairo_t *cairo = cairo_create(NULL);
535 int height;
536 int baseline;
537 get_text_size(cairo, config->font, NULL, &height, &baseline, 1,
538 config->pango_markup, "%s", container->formatted_title);
539 cairo_destroy(cairo);
540 container->title_height = height;
541 container->title_baseline = baseline;
542}
543
544/** 644/**
545 * Calculate and return the length of the tree representation. 645 * Calculate and return the length of the tree representation.
546 * An example tree representation is: V[Terminal, Firefox] 646 * An example tree representation is: V[Terminal, Firefox]
@@ -596,23 +696,28 @@ size_t container_build_representation(enum sway_container_layout layout,
596 696
597void container_update_representation(struct sway_container *con) { 697void container_update_representation(struct sway_container *con) {
598 if (!con->view) { 698 if (!con->view) {
599 size_t len = container_build_representation(con->layout, 699 size_t len = container_build_representation(con->pending.layout,
600 con->children, NULL); 700 con->pending.children, NULL);
601 free(con->formatted_title); 701 free(con->formatted_title);
602 con->formatted_title = calloc(len + 1, sizeof(char)); 702 con->formatted_title = calloc(len + 1, sizeof(char));
603 if (!sway_assert(con->formatted_title, 703 if (!sway_assert(con->formatted_title,
604 "Unable to allocate title string")) { 704 "Unable to allocate title string")) {
605 return; 705 return;
606 } 706 }
607 container_build_representation(con->layout, con->children, 707 container_build_representation(con->pending.layout, con->pending.children,
608 con->formatted_title); 708 con->formatted_title);
609 container_calculate_title_height(con); 709
610 container_update_title_textures(con); 710 if (con->title_bar.title_text) {
711 sway_text_node_set_text(con->title_bar.title_text, con->formatted_title);
712 container_arrange_title_bar(con);
713 } else {
714 container_update_title_bar(con);
715 }
611 } 716 }
612 if (con->parent) { 717 if (con->pending.parent) {
613 container_update_representation(con->parent); 718 container_update_representation(con->pending.parent);
614 } else if (con->workspace) { 719 } else if (con->pending.workspace) {
615 workspace_update_representation(con->workspace); 720 workspace_update_representation(con->pending.workspace);
616 } 721 }
617} 722}
618 723
@@ -638,12 +743,13 @@ void floating_calculate_constraints(int *min_width, int *max_width,
638 *min_height = config->floating_minimum_height; 743 *min_height = config->floating_minimum_height;
639 } 744 }
640 745
641 struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); 746 struct wlr_box box;
747 wlr_output_layout_get_box(root->output_layout, NULL, &box);
642 748
643 if (config->floating_maximum_width == -1) { // no maximum 749 if (config->floating_maximum_width == -1) { // no maximum
644 *max_width = INT_MAX; 750 *max_width = INT_MAX;
645 } else if (config->floating_maximum_width == 0) { // automatic 751 } else if (config->floating_maximum_width == 0) { // automatic
646 *max_width = box->width; 752 *max_width = box.width;
647 } else { 753 } else {
648 *max_width = config->floating_maximum_width; 754 *max_width = config->floating_maximum_width;
649 } 755 }
@@ -651,99 +757,112 @@ void floating_calculate_constraints(int *min_width, int *max_width,
651 if (config->floating_maximum_height == -1) { // no maximum 757 if (config->floating_maximum_height == -1) { // no maximum
652 *max_height = INT_MAX; 758 *max_height = INT_MAX;
653 } else if (config->floating_maximum_height == 0) { // automatic 759 } else if (config->floating_maximum_height == 0) { // automatic
654 *max_height = box->height; 760 *max_height = box.height;
655 } else { 761 } else {
656 *max_height = config->floating_maximum_height; 762 *max_height = config->floating_maximum_height;
657 } 763 }
658 764
659} 765}
660 766
767void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) {
768 if (!old->width || !old->height) {
769 // Fall back to centering on the workspace.
770 container_floating_move_to_center(con);
771 } else {
772 int rel_x = con->pending.x - old->x + (con->pending.width / 2);
773 int rel_y = con->pending.y - old->y + (con->pending.height / 2);
774
775 con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2);
776 con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2);
777
778 sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y);
779 }
780}
781
661static void floating_natural_resize(struct sway_container *con) { 782static void floating_natural_resize(struct sway_container *con) {
662 int min_width, max_width, min_height, max_height; 783 int min_width, max_width, min_height, max_height;
663 floating_calculate_constraints(&min_width, &max_width, 784 floating_calculate_constraints(&min_width, &max_width,
664 &min_height, &max_height); 785 &min_height, &max_height);
665 if (!con->view) { 786 if (!con->view) {
666 con->width = fmax(min_width, fmin(con->width, max_width)); 787 con->pending.width = fmax(min_width, fmin(con->pending.width, max_width));
667 con->height = fmax(min_height, fmin(con->height, max_height)); 788 con->pending.height = fmax(min_height, fmin(con->pending.height, max_height));
668 } else { 789 } else {
669 struct sway_view *view = con->view; 790 struct sway_view *view = con->view;
670 con->content_width = 791 con->pending.content_width =
671 fmax(min_width, fmin(view->natural_width, max_width)); 792 fmax(min_width, fmin(view->natural_width, max_width));
672 con->content_height = 793 con->pending.content_height =
673 fmax(min_height, fmin(view->natural_height, max_height)); 794 fmax(min_height, fmin(view->natural_height, max_height));
674 container_set_geometry_from_content(con); 795 container_set_geometry_from_content(con);
675 } 796 }
676} 797}
677 798
678void container_floating_resize_and_center(struct sway_container *con) { 799void container_floating_resize_and_center(struct sway_container *con) {
679 struct sway_workspace *ws = con->workspace; 800 struct sway_workspace *ws = con->pending.workspace;
680 if (!ws) { 801 if (!ws) {
681 // On scratchpad, just resize 802 // On scratchpad, just resize
682 floating_natural_resize(con); 803 floating_natural_resize(con);
683 return; 804 return;
684 } 805 }
685 806
686 struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, 807 struct wlr_box ob;
687 ws->output->wlr_output); 808 wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob);
688 if (!ob) { 809 if (wlr_box_empty(&ob)) {
689 // On NOOP output. Will be called again when moved to an output 810 // On NOOP output. Will be called again when moved to an output
690 con->x = 0; 811 con->pending.x = 0;
691 con->y = 0; 812 con->pending.y = 0;
692 con->width = 0; 813 con->pending.width = 0;
693 con->height = 0; 814 con->pending.height = 0;
694 return; 815 return;
695 } 816 }
696 817
697 floating_natural_resize(con); 818 floating_natural_resize(con);
698 if (!con->view) { 819 if (!con->view) {
699 if (con->width > ws->width || con->height > ws->height) { 820 if (con->pending.width > ws->width || con->pending.height > ws->height) {
700 con->x = ob->x + (ob->width - con->width) / 2; 821 con->pending.x = ob.x + (ob.width - con->pending.width) / 2;
701 con->y = ob->y + (ob->height - con->height) / 2; 822 con->pending.y = ob.y + (ob.height - con->pending.height) / 2;
702 } else { 823 } else {
703 con->x = ws->x + (ws->width - con->width) / 2; 824 con->pending.x = ws->x + (ws->width - con->pending.width) / 2;
704 con->y = ws->y + (ws->height - con->height) / 2; 825 con->pending.y = ws->y + (ws->height - con->pending.height) / 2;
705 } 826 }
706 } else { 827 } else {
707 if (con->content_width > ws->width 828 if (con->pending.content_width > ws->width
708 || con->content_height > ws->height) { 829 || con->pending.content_height > ws->height) {
709 con->content_x = ob->x + (ob->width - con->content_width) / 2; 830 con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2;
710 con->content_y = ob->y + (ob->height - con->content_height) / 2; 831 con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2;
711 } else { 832 } else {
712 con->content_x = ws->x + (ws->width - con->content_width) / 2; 833 con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2;
713 con->content_y = ws->y + (ws->height - con->content_height) / 2; 834 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;
714 } 835 }
715 836
716 // If the view's border is B_NONE then these properties are ignored. 837 // If the view's border is B_NONE then these properties are ignored.
717 con->border_top = con->border_bottom = true; 838 con->pending.border_top = con->pending.border_bottom = true;
718 con->border_left = con->border_right = true; 839 con->pending.border_left = con->pending.border_right = true;
719 840
720 container_set_geometry_from_content(con); 841 container_set_geometry_from_content(con);
721 } 842 }
722} 843}
723 844
724void container_floating_set_default_size(struct sway_container *con) { 845void container_floating_set_default_size(struct sway_container *con) {
725 if (!sway_assert(con->workspace, "Expected a container on a workspace")) { 846 if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) {
726 return; 847 return;
727 } 848 }
728 849
729 int min_width, max_width, min_height, max_height; 850 int min_width, max_width, min_height, max_height;
730 floating_calculate_constraints(&min_width, &max_width, 851 floating_calculate_constraints(&min_width, &max_width,
731 &min_height, &max_height); 852 &min_height, &max_height);
732 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 853 struct wlr_box box;
733 workspace_get_box(con->workspace, box); 854 workspace_get_box(con->pending.workspace, &box);
734 855
735 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 856 double width = fmax(min_width, fmin(box.width * 0.5, max_width));
736 double height = fmax(min_height, fmin(box->height * 0.75, max_height)); 857 double height = fmax(min_height, fmin(box.height * 0.75, max_height));
737 if (!con->view) { 858 if (!con->view) {
738 con->width = width; 859 con->pending.width = width;
739 con->height = height; 860 con->pending.height = height;
740 } else { 861 } else {
741 con->content_width = width; 862 con->pending.content_width = width;
742 con->content_height = height; 863 con->pending.content_height = height;
743 container_set_geometry_from_content(con); 864 container_set_geometry_from_content(con);
744 } 865 }
745
746 free(box);
747} 866}
748 867
749 868
@@ -761,8 +880,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) {
761 con->view->impl->set_resizing(con->view, resizing); 880 con->view->impl->set_resizing(con->view, resizing);
762 } 881 }
763 } else { 882 } else {
764 for (int i = 0; i < con->children->length; ++i ) { 883 for (int i = 0; i < con->pending.children->length; ++i ) {
765 struct sway_container *child = con->children->items[i]; 884 struct sway_container *child = con->pending.children->items[i];
766 container_set_resizing(child, resizing); 885 container_set_resizing(child, resizing);
767 } 886 }
768 } 887 }
@@ -774,21 +893,33 @@ void container_set_floating(struct sway_container *container, bool enable) {
774 } 893 }
775 894
776 struct sway_seat *seat = input_manager_current_seat(); 895 struct sway_seat *seat = input_manager_current_seat();
777 struct sway_workspace *workspace = container->workspace; 896 struct sway_workspace *workspace = container->pending.workspace;
897 struct sway_container *focus = seat_get_focused_container(seat);
898 bool set_focus = focus == container;
778 899
779 if (enable) { 900 if (enable) {
780 struct sway_container *old_parent = container->parent; 901 struct sway_container *old_parent = container->pending.parent;
781 container_detach(container); 902 container_detach(container);
782 workspace_add_floating(workspace, container); 903 workspace_add_floating(workspace, container);
783 if (container->view) { 904 if (container->view) {
784 view_set_tiled(container->view, false); 905 view_set_tiled(container->view, false);
785 if (container->view->using_csd) { 906 if (container->view->using_csd) {
786 container->border = B_CSD; 907 container->saved_border = container->pending.border;
908 container->pending.border = B_CSD;
909 if (container->view->xdg_decoration) {
910 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
911 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
912 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
913 }
787 } 914 }
788 } 915 }
789 container_floating_set_default_size(container); 916 container_floating_set_default_size(container);
790 container_floating_resize_and_center(container); 917 container_floating_resize_and_center(container);
791 if (old_parent) { 918 if (old_parent) {
919 if (set_focus) {
920 seat_set_raw_focus(seat, &old_parent->node);
921 seat_set_raw_focus(seat, &container->node);
922 }
792 container_reap_empty(old_parent); 923 container_reap_empty(old_parent);
793 } 924 }
794 } else { 925 } else {
@@ -800,19 +931,28 @@ void container_set_floating(struct sway_container *container, bool enable) {
800 struct sway_container *reference = 931 struct sway_container *reference =
801 seat_get_focus_inactive_tiling(seat, workspace); 932 seat_get_focus_inactive_tiling(seat, workspace);
802 if (reference) { 933 if (reference) {
803 container_add_sibling(reference, container, 1); 934 if (reference->view) {
804 container->width = reference->width; 935 container_add_sibling(reference, container, 1);
805 container->height = reference->height; 936 } else {
937 container_add_child(reference, container);
938 }
939 container->pending.width = reference->pending.width;
940 container->pending.height = reference->pending.height;
806 } else { 941 } else {
807 struct sway_container *other = 942 struct sway_container *other =
808 workspace_add_tiling(workspace, container); 943 workspace_add_tiling(workspace, container);
809 other->width = workspace->width; 944 other->pending.width = workspace->width;
810 other->height = workspace->height; 945 other->pending.height = workspace->height;
811 } 946 }
812 if (container->view) { 947 if (container->view) {
813 view_set_tiled(container->view, true); 948 view_set_tiled(container->view, true);
814 if (container->view->using_csd) { 949 if (container->view->using_csd) {
815 container->border = container->saved_border; 950 container->pending.border = container->saved_border;
951 if (container->view->xdg_decoration) {
952 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
953 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
954 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
955 }
816 } 956 }
817 } 957 }
818 container->width_fraction = 0; 958 container->width_fraction = 0;
@@ -834,22 +974,22 @@ void container_set_geometry_from_content(struct sway_container *con) {
834 size_t border_width = 0; 974 size_t border_width = 0;
835 size_t top = 0; 975 size_t top = 0;
836 976
837 if (con->border != B_CSD) { 977 if (con->pending.border != B_CSD && !con->pending.fullscreen_mode) {
838 border_width = con->border_thickness * (con->border != B_NONE); 978 border_width = con->pending.border_thickness * (con->pending.border != B_NONE);
839 top = con->border == B_NORMAL ? 979 top = con->pending.border == B_NORMAL ?
840 container_titlebar_height() : border_width; 980 container_titlebar_height() : border_width;
841 } 981 }
842 982
843 con->x = con->content_x - border_width; 983 con->pending.x = con->pending.content_x - border_width;
844 con->y = con->content_y - top; 984 con->pending.y = con->pending.content_y - top;
845 con->width = con->content_width + border_width * 2; 985 con->pending.width = con->pending.content_width + border_width * 2;
846 con->height = top + con->content_height + border_width; 986 con->pending.height = top + con->pending.content_height + border_width;
847 node_set_dirty(&con->node); 987 node_set_dirty(&con->node);
848} 988}
849 989
850bool container_is_floating(struct sway_container *container) { 990bool container_is_floating(struct sway_container *container) {
851 if (!container->parent && container->workspace && 991 if (!container->pending.parent && container->pending.workspace &&
852 list_find(container->workspace->floating, container) != -1) { 992 list_find(container->pending.workspace->floating, container) != -1) {
853 return true; 993 return true;
854 } 994 }
855 if (container->scratchpad) { 995 if (container->scratchpad) {
@@ -859,10 +999,10 @@ bool container_is_floating(struct sway_container *container) {
859} 999}
860 1000
861void container_get_box(struct sway_container *container, struct wlr_box *box) { 1001void container_get_box(struct sway_container *container, struct wlr_box *box) {
862 box->x = container->x; 1002 box->x = container->pending.x;
863 box->y = container->y; 1003 box->y = container->pending.y;
864 box->width = container->width; 1004 box->width = container->pending.width;
865 box->height = container->height; 1005 box->height = container->pending.height;
866} 1006}
867 1007
868/** 1008/**
@@ -870,14 +1010,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
870 */ 1010 */
871void container_floating_translate(struct sway_container *con, 1011void container_floating_translate(struct sway_container *con,
872 double x_amount, double y_amount) { 1012 double x_amount, double y_amount) {
873 con->x += x_amount; 1013 con->pending.x += x_amount;
874 con->y += y_amount; 1014 con->pending.y += y_amount;
875 con->content_x += x_amount; 1015 con->pending.content_x += x_amount;
876 con->content_y += y_amount; 1016 con->pending.content_y += y_amount;
877 1017
878 if (con->children) { 1018 if (con->pending.children) {
879 for (int i = 0; i < con->children->length; ++i) { 1019 for (int i = 0; i < con->pending.children->length; ++i) {
880 struct sway_container *child = con->children->items[i]; 1020 struct sway_container *child = con->pending.children->items[i];
881 container_floating_translate(child, x_amount, y_amount); 1021 container_floating_translate(child, x_amount, y_amount);
882 } 1022 }
883 } 1023 }
@@ -893,8 +1033,8 @@ void container_floating_translate(struct sway_container *con,
893 * center. 1033 * center.
894 */ 1034 */
895struct sway_output *container_floating_find_output(struct sway_container *con) { 1035struct sway_output *container_floating_find_output(struct sway_container *con) {
896 double center_x = con->x + con->width / 2; 1036 double center_x = con->pending.x + con->pending.width / 2;
897 double center_y = con->y + con->height / 2; 1037 double center_y = con->pending.y + con->pending.height / 2;
898 struct sway_output *closest_output = NULL; 1038 struct sway_output *closest_output = NULL;
899 double closest_distance = DBL_MAX; 1039 double closest_distance = DBL_MAX;
900 for (int i = 0; i < root->outputs->length; ++i) { 1040 for (int i = 0; i < root->outputs->length; ++i) {
@@ -925,11 +1065,11 @@ void container_floating_move_to(struct sway_container *con,
925 "Expected a floating container")) { 1065 "Expected a floating container")) {
926 return; 1066 return;
927 } 1067 }
928 container_floating_translate(con, lx - con->x, ly - con->y); 1068 container_floating_translate(con, lx - con->pending.x, ly - con->pending.y);
929 if (container_is_scratchpad_hidden(con)) { 1069 if (container_is_scratchpad_hidden(con)) {
930 return; 1070 return;
931 } 1071 }
932 struct sway_workspace *old_workspace = con->workspace; 1072 struct sway_workspace *old_workspace = con->pending.workspace;
933 struct sway_output *new_output = container_floating_find_output(con); 1073 struct sway_output *new_output = container_floating_find_output(con);
934 if (!sway_assert(new_output, "Unable to find any output")) { 1074 if (!sway_assert(new_output, "Unable to find any output")) {
935 return; 1075 return;
@@ -941,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con,
941 workspace_add_floating(new_workspace, con); 1081 workspace_add_floating(new_workspace, con);
942 arrange_workspace(old_workspace); 1082 arrange_workspace(old_workspace);
943 arrange_workspace(new_workspace); 1083 arrange_workspace(new_workspace);
1084 // If the moved container was a visible scratchpad container, then
1085 // update its transform.
1086 if (con->scratchpad) {
1087 struct wlr_box output_box;
1088 output_get_box(new_output, &output_box);
1089 con->transform = output_box;
1090 }
944 workspace_detect_urgent(old_workspace); 1091 workspace_detect_urgent(old_workspace);
945 workspace_detect_urgent(new_workspace); 1092 workspace_detect_urgent(new_workspace);
946 } 1093 }
@@ -951,10 +1098,10 @@ void container_floating_move_to_center(struct sway_container *con) {
951 "Expected a floating container")) { 1098 "Expected a floating container")) {
952 return; 1099 return;
953 } 1100 }
954 struct sway_workspace *ws = con->workspace; 1101 struct sway_workspace *ws = con->pending.workspace;
955 double new_lx = ws->x + (ws->width - con->width) / 2; 1102 double new_lx = ws->x + (ws->width - con->pending.width) / 2;
956 double new_ly = ws->y + (ws->height - con->height) / 2; 1103 double new_ly = ws->y + (ws->height - con->pending.height) / 2;
957 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 1104 container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y);
958} 1105}
959 1106
960static bool find_urgent_iterator(struct sway_container *con, void *data) { 1107static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -972,42 +1119,39 @@ void container_end_mouse_operation(struct sway_container *container) {
972 } 1119 }
973} 1120}
974 1121
975static void set_fullscreen_iterator(struct sway_container *con, void *data) { 1122static void set_fullscreen(struct sway_container *con, bool enable) {
976 if (!con->view) { 1123 if (!con->view) {
977 return; 1124 return;
978 } 1125 }
979 if (con->view->impl->set_fullscreen) { 1126 if (con->view->impl->set_fullscreen) {
980 bool *enable = data; 1127 con->view->impl->set_fullscreen(con->view, enable);
981 con->view->impl->set_fullscreen(con->view, *enable);
982 if (con->view->foreign_toplevel) { 1128 if (con->view->foreign_toplevel) {
983 wlr_foreign_toplevel_handle_v1_set_fullscreen( 1129 wlr_foreign_toplevel_handle_v1_set_fullscreen(
984 con->view->foreign_toplevel, *enable); 1130 con->view->foreign_toplevel, enable);
985 } 1131 }
986 } 1132 }
987} 1133}
988 1134
989static void container_fullscreen_workspace(struct sway_container *con) { 1135static void container_fullscreen_workspace(struct sway_container *con) {
990 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1136 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
991 "Expected a non-fullscreen container")) { 1137 "Expected a non-fullscreen container")) {
992 return; 1138 return;
993 } 1139 }
994 bool enable = true; 1140 set_fullscreen(con, true);
995 set_fullscreen_iterator(con, &enable); 1141 con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE;
996 container_for_each_child(con, set_fullscreen_iterator, &enable);
997 con->fullscreen_mode = FULLSCREEN_WORKSPACE;
998 1142
999 con->saved_x = con->x; 1143 con->saved_x = con->pending.x;
1000 con->saved_y = con->y; 1144 con->saved_y = con->pending.y;
1001 con->saved_width = con->width; 1145 con->saved_width = con->pending.width;
1002 con->saved_height = con->height; 1146 con->saved_height = con->pending.height;
1003 1147
1004 if (con->workspace) { 1148 if (con->pending.workspace) {
1005 con->workspace->fullscreen = con; 1149 con->pending.workspace->fullscreen = con;
1006 struct sway_seat *seat; 1150 struct sway_seat *seat;
1007 struct sway_workspace *focus_ws; 1151 struct sway_workspace *focus_ws;
1008 wl_list_for_each(seat, &server.input->seats, link) { 1152 wl_list_for_each(seat, &server.input->seats, link) {
1009 focus_ws = seat_get_focused_workspace(seat); 1153 focus_ws = seat_get_focused_workspace(seat);
1010 if (focus_ws == con->workspace) { 1154 if (focus_ws == con->pending.workspace) {
1011 seat_set_focus_container(seat, con); 1155 seat_set_focus_container(seat, con);
1012 } else { 1156 } else {
1013 struct sway_node *focus = 1157 struct sway_node *focus =
@@ -1023,19 +1167,17 @@ static void container_fullscreen_workspace(struct sway_container *con) {
1023} 1167}
1024 1168
1025static void container_fullscreen_global(struct sway_container *con) { 1169static void container_fullscreen_global(struct sway_container *con) {
1026 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, 1170 if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
1027 "Expected a non-fullscreen container")) { 1171 "Expected a non-fullscreen container")) {
1028 return; 1172 return;
1029 } 1173 }
1030 bool enable = true; 1174 set_fullscreen(con, true);
1031 set_fullscreen_iterator(con, &enable);
1032 container_for_each_child(con, set_fullscreen_iterator, &enable);
1033 1175
1034 root->fullscreen_global = con; 1176 root->fullscreen_global = con;
1035 con->saved_x = con->x; 1177 con->saved_x = con->pending.x;
1036 con->saved_y = con->y; 1178 con->saved_y = con->pending.y;
1037 con->saved_width = con->width; 1179 con->saved_width = con->pending.width;
1038 con->saved_height = con->height; 1180 con->saved_height = con->pending.height;
1039 1181
1040 struct sway_seat *seat; 1182 struct sway_seat *seat;
1041 wl_list_for_each(seat, &server.input->seats, link) { 1183 wl_list_for_each(seat, &server.input->seats, link) {
@@ -1045,34 +1187,32 @@ static void container_fullscreen_global(struct sway_container *con) {
1045 } 1187 }
1046 } 1188 }
1047 1189
1048 con->fullscreen_mode = FULLSCREEN_GLOBAL; 1190 con->pending.fullscreen_mode = FULLSCREEN_GLOBAL;
1049 container_end_mouse_operation(con); 1191 container_end_mouse_operation(con);
1050 ipc_event_window(con, "fullscreen_mode"); 1192 ipc_event_window(con, "fullscreen_mode");
1051} 1193}
1052 1194
1053void container_fullscreen_disable(struct sway_container *con) { 1195void container_fullscreen_disable(struct sway_container *con) {
1054 if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, 1196 if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE,
1055 "Expected a fullscreen container")) { 1197 "Expected a fullscreen container")) {
1056 return; 1198 return;
1057 } 1199 }
1058 bool enable = false; 1200 set_fullscreen(con, false);
1059 set_fullscreen_iterator(con, &enable);
1060 container_for_each_child(con, set_fullscreen_iterator, &enable);
1061 1201
1062 if (container_is_floating(con)) { 1202 if (container_is_floating(con)) {
1063 con->x = con->saved_x; 1203 con->pending.x = con->saved_x;
1064 con->y = con->saved_y; 1204 con->pending.y = con->saved_y;
1065 con->width = con->saved_width; 1205 con->pending.width = con->saved_width;
1066 con->height = con->saved_height; 1206 con->pending.height = con->saved_height;
1067 } 1207 }
1068 1208
1069 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1209 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1070 if (con->workspace) { 1210 if (con->pending.workspace) {
1071 con->workspace->fullscreen = NULL; 1211 con->pending.workspace->fullscreen = NULL;
1072 if (container_is_floating(con)) { 1212 if (container_is_floating(con)) {
1073 struct sway_output *output = 1213 struct sway_output *output =
1074 container_floating_find_output(con); 1214 container_floating_find_output(con);
1075 if (con->workspace->output != output) { 1215 if (con->pending.workspace->output != output) {
1076 container_floating_move_to_center(con); 1216 container_floating_move_to_center(con);
1077 } 1217 }
1078 } 1218 }
@@ -1084,11 +1224,11 @@ void container_fullscreen_disable(struct sway_container *con) {
1084 // If the container was mapped as fullscreen and set as floating by 1224 // If the container was mapped as fullscreen and set as floating by
1085 // criteria, it needs to be reinitialized as floating to get the proper 1225 // criteria, it needs to be reinitialized as floating to get the proper
1086 // size and location 1226 // size and location
1087 if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { 1227 if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) {
1088 container_floating_resize_and_center(con); 1228 container_floating_resize_and_center(con);
1089 } 1229 }
1090 1230
1091 con->fullscreen_mode = FULLSCREEN_NONE; 1231 con->pending.fullscreen_mode = FULLSCREEN_NONE;
1092 container_end_mouse_operation(con); 1232 container_end_mouse_operation(con);
1093 ipc_event_window(con, "fullscreen_mode"); 1233 ipc_event_window(con, "fullscreen_mode");
1094 1234
@@ -1106,7 +1246,7 @@ void container_fullscreen_disable(struct sway_container *con) {
1106 1246
1107void container_set_fullscreen(struct sway_container *con, 1247void container_set_fullscreen(struct sway_container *con,
1108 enum sway_fullscreen_mode mode) { 1248 enum sway_fullscreen_mode mode) {
1109 if (con->fullscreen_mode == mode) { 1249 if (con->pending.fullscreen_mode == mode) {
1110 return; 1250 return;
1111 } 1251 }
1112 1252
@@ -1118,8 +1258,8 @@ void container_set_fullscreen(struct sway_container *con,
1118 if (root->fullscreen_global) { 1258 if (root->fullscreen_global) {
1119 container_fullscreen_disable(root->fullscreen_global); 1259 container_fullscreen_disable(root->fullscreen_global);
1120 } 1260 }
1121 if (con->workspace && con->workspace->fullscreen) { 1261 if (con->pending.workspace && con->pending.workspace->fullscreen) {
1122 container_fullscreen_disable(con->workspace->fullscreen); 1262 container_fullscreen_disable(con->pending.workspace->fullscreen);
1123 } 1263 }
1124 container_fullscreen_workspace(con); 1264 container_fullscreen_workspace(con);
1125 break; 1265 break;
@@ -1127,7 +1267,7 @@ void container_set_fullscreen(struct sway_container *con,
1127 if (root->fullscreen_global) { 1267 if (root->fullscreen_global) {
1128 container_fullscreen_disable(root->fullscreen_global); 1268 container_fullscreen_disable(root->fullscreen_global);
1129 } 1269 }
1130 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1270 if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1131 container_fullscreen_disable(con); 1271 container_fullscreen_disable(con);
1132 } 1272 }
1133 container_fullscreen_global(con); 1273 container_fullscreen_global(con);
@@ -1137,8 +1277,8 @@ void container_set_fullscreen(struct sway_container *con,
1137 1277
1138struct sway_container *container_toplevel_ancestor( 1278struct sway_container *container_toplevel_ancestor(
1139 struct sway_container *container) { 1279 struct sway_container *container) {
1140 while (container->parent) { 1280 while (container->pending.parent) {
1141 container = container->parent; 1281 container = container->pending.parent;
1142 } 1282 }
1143 1283
1144 return container; 1284 return container;
@@ -1150,148 +1290,67 @@ bool container_is_floating_or_child(struct sway_container *container) {
1150 1290
1151bool container_is_fullscreen_or_child(struct sway_container *container) { 1291bool container_is_fullscreen_or_child(struct sway_container *container) {
1152 do { 1292 do {
1153 if (container->fullscreen_mode) { 1293 if (container->pending.fullscreen_mode) {
1154 return true; 1294 return true;
1155 } 1295 }
1156 container = container->parent; 1296 container = container->pending.parent;
1157 } while (container); 1297 } while (container);
1158 1298
1159 return false; 1299 return false;
1160} 1300}
1161 1301
1162static void surface_send_enter_iterator(struct wlr_surface *surface,
1163 int x, int y, void *data) {
1164 struct wlr_output *wlr_output = data;
1165 wlr_surface_send_enter(surface, wlr_output);
1166}
1167
1168static void surface_send_leave_iterator(struct wlr_surface *surface,
1169 int x, int y, void *data) {
1170 struct wlr_output *wlr_output = data;
1171 wlr_surface_send_leave(surface, wlr_output);
1172}
1173
1174void container_discover_outputs(struct sway_container *con) {
1175 struct wlr_box con_box = {
1176 .x = con->current.x,
1177 .y = con->current.y,
1178 .width = con->current.width,
1179 .height = con->current.height,
1180 };
1181 struct sway_output *old_output = container_get_effective_output(con);
1182
1183 for (int i = 0; i < root->outputs->length; ++i) {
1184 struct sway_output *output = root->outputs->items[i];
1185 struct wlr_box output_box;
1186 output_get_box(output, &output_box);
1187 struct wlr_box intersection;
1188 bool intersects =
1189 wlr_box_intersection(&intersection, &con_box, &output_box);
1190 int index = list_find(con->outputs, output);
1191
1192 if (intersects && index == -1) {
1193 // Send enter
1194 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1195 if (con->view) {
1196 view_for_each_surface(con->view,
1197 surface_send_enter_iterator, output->wlr_output);
1198 if (con->view->foreign_toplevel) {
1199 wlr_foreign_toplevel_handle_v1_output_enter(
1200 con->view->foreign_toplevel, output->wlr_output);
1201 }
1202 }
1203 list_add(con->outputs, output);
1204 } else if (!intersects && index != -1) {
1205 // Send leave
1206 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1207 if (con->view) {
1208 view_for_each_surface(con->view,
1209 surface_send_leave_iterator, output->wlr_output);
1210 if (con->view->foreign_toplevel) {
1211 wlr_foreign_toplevel_handle_v1_output_leave(
1212 con->view->foreign_toplevel, output->wlr_output);
1213 }
1214 }
1215 list_del(con->outputs, index);
1216 }
1217 }
1218 struct sway_output *new_output = container_get_effective_output(con);
1219 double old_scale = old_output && old_output->enabled ?
1220 old_output->wlr_output->scale : -1;
1221 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1222 if (old_scale != new_scale) {
1223 container_update_title_textures(con);
1224 container_update_marks_textures(con);
1225 }
1226}
1227
1228enum sway_container_layout container_parent_layout(struct sway_container *con) { 1302enum sway_container_layout container_parent_layout(struct sway_container *con) {
1229 if (con->parent) { 1303 if (con->pending.parent) {
1230 return con->parent->layout; 1304 return con->pending.parent->pending.layout;
1231 } 1305 }
1232 if (con->workspace) { 1306 if (con->pending.workspace) {
1233 return con->workspace->layout; 1307 return con->pending.workspace->layout;
1234 } 1308 }
1235 return L_NONE; 1309 return L_NONE;
1236} 1310}
1237 1311
1238enum sway_container_layout container_current_parent_layout(
1239 struct sway_container *con) {
1240 if (con->current.parent) {
1241 return con->current.parent->current.layout;
1242 }
1243 return con->current.workspace->current.layout;
1244}
1245
1246list_t *container_get_siblings(struct sway_container *container) { 1312list_t *container_get_siblings(struct sway_container *container) {
1247 if (container->parent) { 1313 if (container->pending.parent) {
1248 return container->parent->children; 1314 return container->pending.parent->pending.children;
1249 } 1315 }
1250 if (container_is_scratchpad_hidden(container)) { 1316 if (!container->pending.workspace) {
1251 return NULL; 1317 return NULL;
1252 } 1318 }
1253 if (list_find(container->workspace->tiling, container) != -1) { 1319 if (list_find(container->pending.workspace->tiling, container) != -1) {
1254 return container->workspace->tiling; 1320 return container->pending.workspace->tiling;
1255 } 1321 }
1256 return container->workspace->floating; 1322 return container->pending.workspace->floating;
1257} 1323}
1258 1324
1259int container_sibling_index(struct sway_container *child) { 1325int container_sibling_index(struct sway_container *child) {
1260 return list_find(container_get_siblings(child), child); 1326 return list_find(container_get_siblings(child), child);
1261} 1327}
1262 1328
1263list_t *container_get_current_siblings(struct sway_container *container) {
1264 if (container->current.parent) {
1265 return container->current.parent->current.children;
1266 }
1267 return container->current.workspace->current.tiling;
1268}
1269
1270void container_handle_fullscreen_reparent(struct sway_container *con) { 1329void container_handle_fullscreen_reparent(struct sway_container *con) {
1271 if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || 1330 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1272 con->workspace->fullscreen == con) { 1331 con->pending.workspace->fullscreen == con) {
1273 return; 1332 return;
1274 } 1333 }
1275 if (con->workspace->fullscreen) { 1334 if (con->pending.workspace->fullscreen) {
1276 container_fullscreen_disable(con->workspace->fullscreen); 1335 container_fullscreen_disable(con->pending.workspace->fullscreen);
1277 } 1336 }
1278 con->workspace->fullscreen = con; 1337 con->pending.workspace->fullscreen = con;
1279 1338
1280 arrange_workspace(con->workspace); 1339 arrange_workspace(con->pending.workspace);
1281} 1340}
1282 1341
1283static void set_workspace(struct sway_container *container, void *data) { 1342static void set_workspace(struct sway_container *container, void *data) {
1284 container->workspace = container->parent->workspace; 1343 container->pending.workspace = container->pending.parent->pending.workspace;
1285} 1344}
1286 1345
1287void container_insert_child(struct sway_container *parent, 1346void container_insert_child(struct sway_container *parent,
1288 struct sway_container *child, int i) { 1347 struct sway_container *child, int i) {
1289 if (child->workspace) { 1348 if (child->pending.workspace) {
1290 container_detach(child); 1349 container_detach(child);
1291 } 1350 }
1292 list_insert(parent->children, i, child); 1351 list_insert(parent->pending.children, i, child);
1293 child->parent = parent; 1352 child->pending.parent = parent;
1294 child->workspace = parent->workspace; 1353 child->pending.workspace = parent->pending.workspace;
1295 container_for_each_child(child, set_workspace, NULL); 1354 container_for_each_child(child, set_workspace, NULL);
1296 container_handle_fullscreen_reparent(child); 1355 container_handle_fullscreen_reparent(child);
1297 container_update_representation(parent); 1356 container_update_representation(parent);
@@ -1299,14 +1358,14 @@ void container_insert_child(struct sway_container *parent,
1299 1358
1300void container_add_sibling(struct sway_container *fixed, 1359void container_add_sibling(struct sway_container *fixed,
1301 struct sway_container *active, bool after) { 1360 struct sway_container *active, bool after) {
1302 if (active->workspace) { 1361 if (active->pending.workspace) {
1303 container_detach(active); 1362 container_detach(active);
1304 } 1363 }
1305 list_t *siblings = container_get_siblings(fixed); 1364 list_t *siblings = container_get_siblings(fixed);
1306 int index = list_find(siblings, fixed); 1365 int index = list_find(siblings, fixed);
1307 list_insert(siblings, index + after, active); 1366 list_insert(siblings, index + after, active);
1308 active->parent = fixed->parent; 1367 active->pending.parent = fixed->pending.parent;
1309 active->workspace = fixed->workspace; 1368 active->pending.workspace = fixed->pending.workspace;
1310 container_for_each_child(active, set_workspace, NULL); 1369 container_for_each_child(active, set_workspace, NULL);
1311 container_handle_fullscreen_reparent(active); 1370 container_handle_fullscreen_reparent(active);
1312 container_update_representation(active); 1371 container_update_representation(active);
@@ -1314,17 +1373,13 @@ void container_add_sibling(struct sway_container *fixed,
1314 1373
1315void container_add_child(struct sway_container *parent, 1374void container_add_child(struct sway_container *parent,
1316 struct sway_container *child) { 1375 struct sway_container *child) {
1317 if (child->workspace) { 1376 if (child->pending.workspace) {
1318 container_detach(child); 1377 container_detach(child);
1319 } 1378 }
1320 list_add(parent->children, child); 1379 list_add(parent->pending.children, child);
1321 child->parent = parent; 1380 child->pending.parent = parent;
1322 child->workspace = parent->workspace; 1381 child->pending.workspace = parent->pending.workspace;
1323 container_for_each_child(child, set_workspace, NULL); 1382 container_for_each_child(child, set_workspace, NULL);
1324 bool fullscreen = child->fullscreen_mode != FULLSCREEN_NONE ||
1325 parent->fullscreen_mode != FULLSCREEN_NONE;
1326 set_fullscreen_iterator(child, &fullscreen);
1327 container_for_each_child(child, set_fullscreen_iterator, &fullscreen);
1328 container_handle_fullscreen_reparent(child); 1383 container_handle_fullscreen_reparent(child);
1329 container_update_representation(parent); 1384 container_update_representation(parent);
1330 node_set_dirty(&child->node); 1385 node_set_dirty(&child->node);
@@ -1332,15 +1387,15 @@ void container_add_child(struct sway_container *parent,
1332} 1387}
1333 1388
1334void container_detach(struct sway_container *child) { 1389void container_detach(struct sway_container *child) {
1335 if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { 1390 if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
1336 child->workspace->fullscreen = NULL; 1391 child->pending.workspace->fullscreen = NULL;
1337 } 1392 }
1338 if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { 1393 if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1339 root->fullscreen_global = NULL; 1394 root->fullscreen_global = NULL;
1340 } 1395 }
1341 1396
1342 struct sway_container *old_parent = child->parent; 1397 struct sway_container *old_parent = child->pending.parent;
1343 struct sway_workspace *old_workspace = child->workspace; 1398 struct sway_workspace *old_workspace = child->pending.workspace;
1344 list_t *siblings = container_get_siblings(child); 1399 list_t *siblings = container_get_siblings(child);
1345 if (siblings) { 1400 if (siblings) {
1346 int index = list_find(siblings, child); 1401 int index = list_find(siblings, child);
@@ -1348,8 +1403,8 @@ void container_detach(struct sway_container *child) {
1348 list_del(siblings, index); 1403 list_del(siblings, index);
1349 } 1404 }
1350 } 1405 }
1351 child->parent = NULL; 1406 child->pending.parent = NULL;
1352 child->workspace = NULL; 1407 child->pending.workspace = NULL;
1353 container_for_each_child(child, set_workspace, NULL); 1408 container_for_each_child(child, set_workspace, NULL);
1354 1409
1355 if (old_parent) { 1410 if (old_parent) {
@@ -1364,18 +1419,18 @@ void container_detach(struct sway_container *child) {
1364 1419
1365void container_replace(struct sway_container *container, 1420void container_replace(struct sway_container *container,
1366 struct sway_container *replacement) { 1421 struct sway_container *replacement) {
1367 enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; 1422 enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode;
1368 bool scratchpad = container->scratchpad; 1423 bool scratchpad = container->scratchpad;
1369 struct sway_workspace *ws = NULL; 1424 struct sway_workspace *ws = NULL;
1370 if (fullscreen != FULLSCREEN_NONE) { 1425 if (fullscreen != FULLSCREEN_NONE) {
1371 container_fullscreen_disable(container); 1426 container_fullscreen_disable(container);
1372 } 1427 }
1373 if (scratchpad) { 1428 if (scratchpad) {
1374 ws = container->workspace; 1429 ws = container->pending.workspace;
1375 root_scratchpad_show(container); 1430 root_scratchpad_show(container);
1376 root_scratchpad_remove_container(container); 1431 root_scratchpad_remove_container(container);
1377 } 1432 }
1378 if (container->parent || container->workspace) { 1433 if (container->pending.parent || container->pending.workspace) {
1379 float width_fraction = container->width_fraction; 1434 float width_fraction = container->width_fraction;
1380 float height_fraction = container->height_fraction; 1435 float height_fraction = container->height_fraction;
1381 container_add_sibling(container, replacement, 1); 1436 container_add_sibling(container, replacement, 1);
@@ -1403,7 +1458,7 @@ struct sway_container *container_split(struct sway_container *child,
1403 enum sway_container_layout layout) { 1458 enum sway_container_layout layout) {
1404 // i3 doesn't split singleton H/V containers 1459 // i3 doesn't split singleton H/V containers
1405 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 1460 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354
1406 if (child->parent || child->workspace) { 1461 if (child->pending.parent || child->pending.workspace) {
1407 list_t *siblings = container_get_siblings(child); 1462 list_t *siblings = container_get_siblings(child);
1408 if (siblings->length == 1) { 1463 if (siblings->length == 1) {
1409 enum sway_container_layout current = container_parent_layout(child); 1464 enum sway_container_layout current = container_parent_layout(child);
@@ -1411,12 +1466,12 @@ struct sway_container *container_split(struct sway_container *child,
1411 current = L_NONE; 1466 current = L_NONE;
1412 } 1467 }
1413 if (current == L_HORIZ || current == L_VERT) { 1468 if (current == L_HORIZ || current == L_VERT) {
1414 if (child->parent) { 1469 if (child->pending.parent) {
1415 child->parent->layout = layout; 1470 child->pending.parent->pending.layout = layout;
1416 container_update_representation(child->parent); 1471 container_update_representation(child->pending.parent);
1417 } else { 1472 } else {
1418 child->workspace->layout = layout; 1473 child->pending.workspace->layout = layout;
1419 workspace_update_representation(child->workspace); 1474 workspace_update_representation(child->pending.workspace);
1420 } 1475 }
1421 return child; 1476 return child;
1422 } 1477 }
@@ -1429,25 +1484,25 @@ struct sway_container *container_split(struct sway_container *child,
1429 if (container_is_floating(child) && child->view) { 1484 if (container_is_floating(child) && child->view) {
1430 view_set_tiled(child->view, true); 1485 view_set_tiled(child->view, true);
1431 if (child->view->using_csd) { 1486 if (child->view->using_csd) {
1432 child->border = child->saved_border; 1487 child->pending.border = child->saved_border;
1433 } 1488 }
1434 } 1489 }
1435 1490
1436 struct sway_container *cont = container_create(NULL); 1491 struct sway_container *cont = container_create(NULL);
1437 cont->width = child->width; 1492 cont->pending.width = child->pending.width;
1438 cont->height = child->height; 1493 cont->pending.height = child->pending.height;
1439 cont->width_fraction = child->width_fraction; 1494 cont->width_fraction = child->width_fraction;
1440 cont->height_fraction = child->height_fraction; 1495 cont->height_fraction = child->height_fraction;
1441 cont->x = child->x; 1496 cont->pending.x = child->pending.x;
1442 cont->y = child->y; 1497 cont->pending.y = child->pending.y;
1443 cont->layout = layout; 1498 cont->pending.layout = layout;
1444 1499
1445 container_replace(child, cont); 1500 container_replace(child, cont);
1446 container_add_child(cont, child); 1501 container_add_child(cont, child);
1447 1502
1448 if (set_focus) { 1503 if (set_focus) {
1449 seat_set_raw_focus(seat, &cont->node); 1504 seat_set_raw_focus(seat, &cont->node);
1450 if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { 1505 if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
1451 seat_set_focus(seat, &child->node); 1506 seat_set_focus(seat, &child->node);
1452 } else { 1507 } else {
1453 seat_set_raw_focus(seat, &child->node); 1508 seat_set_raw_focus(seat, &child->node);
@@ -1485,7 +1540,7 @@ bool container_find_and_unmark(char *mark) {
1485 if (strcmp(con_mark, mark) == 0) { 1540 if (strcmp(con_mark, mark) == 0) {
1486 free(con_mark); 1541 free(con_mark);
1487 list_del(con->marks, i); 1542 list_del(con->marks, i);
1488 container_update_marks_textures(con); 1543 container_update_marks(con);
1489 ipc_event_window(con, "mark"); 1544 ipc_event_window(con, "mark");
1490 return true; 1545 return true;
1491 } 1546 }
@@ -1516,111 +1571,27 @@ void container_add_mark(struct sway_container *con, char *mark) {
1516 ipc_event_window(con, "mark"); 1571 ipc_event_window(con, "mark");
1517} 1572}
1518 1573
1519static void update_marks_texture(struct sway_container *con,
1520 struct wlr_texture **texture, struct border_colors *class) {
1521 struct sway_output *output = container_get_effective_output(con);
1522 if (!output) {
1523 return;
1524 }
1525 if (*texture) {
1526 wlr_texture_destroy(*texture);
1527 *texture = NULL;
1528 }
1529 if (!con->marks->length) {
1530 return;
1531 }
1532
1533 size_t len = 0;
1534 for (int i = 0; i < con->marks->length; ++i) {
1535 char *mark = con->marks->items[i];
1536 if (mark[0] != '_') {
1537 len += strlen(mark) + 2;
1538 }
1539 }
1540 char *buffer = calloc(len + 1, 1);
1541 char *part = malloc(len + 1);
1542
1543 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1544 free(buffer);
1545 return;
1546 }
1547
1548 for (int i = 0; i < con->marks->length; ++i) {
1549 char *mark = con->marks->items[i];
1550 if (mark[0] != '_') {
1551 sprintf(part, "[%s]", mark);
1552 strcat(buffer, part);
1553 }
1554 }
1555 free(part);
1556
1557 double scale = output->wlr_output->scale;
1558 int width = 0;
1559 int height = con->title_height * scale;
1560
1561 cairo_t *c = cairo_create(NULL);
1562 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1563 "%s", buffer);
1564 cairo_destroy(c);
1565
1566 cairo_surface_t *surface = cairo_image_surface_create(
1567 CAIRO_FORMAT_ARGB32, width, height);
1568 cairo_t *cairo = cairo_create(surface);
1569 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1570 class->background[2], class->background[3]);
1571 cairo_paint(cairo);
1572 PangoContext *pango = pango_cairo_create_context(cairo);
1573 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1574 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1575 class->text[2], class->text[3]);
1576 cairo_move_to(cairo, 0, 0);
1577
1578 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1579
1580 cairo_surface_flush(surface);
1581 unsigned char *data = cairo_image_surface_get_data(surface);
1582 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
1583 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1584 output->wlr_output->backend);
1585 *texture = wlr_texture_from_pixels(
1586 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
1587 cairo_surface_destroy(surface);
1588 g_object_unref(pango);
1589 cairo_destroy(cairo);
1590 free(buffer);
1591}
1592
1593void container_update_marks_textures(struct sway_container *con) {
1594 if (!config->show_marks) {
1595 return;
1596 }
1597 update_marks_texture(con, &con->marks_focused,
1598 &config->border_colors.focused);
1599 update_marks_texture(con, &con->marks_focused_inactive,
1600 &config->border_colors.focused_inactive);
1601 update_marks_texture(con, &con->marks_unfocused,
1602 &config->border_colors.unfocused);
1603 update_marks_texture(con, &con->marks_urgent,
1604 &config->border_colors.urgent);
1605 container_damage_whole(con);
1606}
1607
1608void container_raise_floating(struct sway_container *con) { 1574void container_raise_floating(struct sway_container *con) {
1609 // Bring container to front by putting it at the end of the floating list. 1575 // Bring container to front by putting it at the end of the floating list.
1610 struct sway_container *floater = container_toplevel_ancestor(con); 1576 struct sway_container *floater = container_toplevel_ancestor(con);
1611 if (container_is_floating(floater) && floater->workspace) { 1577 if (container_is_floating(floater) && floater->pending.workspace) {
1612 list_move_to_end(floater->workspace->floating, floater); 1578 // it's okay to just raise the scene directly instead of waiting
1613 node_set_dirty(&floater->workspace->node); 1579 // for the transaction to go through. We won't be reconfiguring
1580 // surfaces
1581 wlr_scene_node_raise_to_top(&floater->scene_tree->node);
1582
1583 list_move_to_end(floater->pending.workspace->floating, floater);
1584 node_set_dirty(&floater->pending.workspace->node);
1614 } 1585 }
1615} 1586}
1616 1587
1617bool container_is_scratchpad_hidden(struct sway_container *con) { 1588bool container_is_scratchpad_hidden(struct sway_container *con) {
1618 return con->scratchpad && !con->workspace; 1589 return con->scratchpad && !con->pending.workspace;
1619} 1590}
1620 1591
1621bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { 1592bool container_is_scratchpad_hidden_or_child(struct sway_container *con) {
1622 con = container_toplevel_ancestor(con); 1593 con = container_toplevel_ancestor(con);
1623 return con->scratchpad && !con->workspace; 1594 return con->scratchpad && !con->pending.workspace;
1624} 1595}
1625 1596
1626bool container_is_sticky(struct sway_container *con) { 1597bool container_is_sticky(struct sway_container *con) {
@@ -1648,39 +1619,39 @@ static bool is_parallel(enum sway_container_layout first,
1648static bool container_is_squashable(struct sway_container *con, 1619static bool container_is_squashable(struct sway_container *con,
1649 struct sway_container *child) { 1620 struct sway_container *child) {
1650 enum sway_container_layout gp_layout = container_parent_layout(con); 1621 enum sway_container_layout gp_layout = container_parent_layout(con);
1651 return (con->layout == L_HORIZ || con->layout == L_VERT) && 1622 return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) &&
1652 (child->layout == L_HORIZ || child->layout == L_VERT) && 1623 (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) &&
1653 !is_parallel(con->layout, child->layout) && 1624 !is_parallel(con->pending.layout, child->pending.layout) &&
1654 is_parallel(gp_layout, child->layout); 1625 is_parallel(gp_layout, child->pending.layout);
1655} 1626}
1656 1627
1657static void container_squash_children(struct sway_container *con) { 1628static void container_squash_children(struct sway_container *con) {
1658 for (int i = 0; i < con->children->length; i++) { 1629 for (int i = 0; i < con->pending.children->length; i++) {
1659 struct sway_container *child = con->children->items[i]; 1630 struct sway_container *child = con->pending.children->items[i];
1660 i += container_squash(child); 1631 i += container_squash(child);
1661 } 1632 }
1662} 1633}
1663 1634
1664int container_squash(struct sway_container *con) { 1635int container_squash(struct sway_container *con) {
1665 if (!con->children) { 1636 if (!con->pending.children) {
1666 return 0; 1637 return 0;
1667 } 1638 }
1668 if (con->children->length != 1) { 1639 if (con->pending.children->length != 1) {
1669 container_squash_children(con); 1640 container_squash_children(con);
1670 return 0; 1641 return 0;
1671 } 1642 }
1672 struct sway_container *child = con->children->items[0]; 1643 struct sway_container *child = con->pending.children->items[0];
1673 int idx = container_sibling_index(con); 1644 int idx = container_sibling_index(con);
1674 int change = 0; 1645 int change = 0;
1675 if (container_is_squashable(con, child)) { 1646 if (container_is_squashable(con, child)) {
1676 // con and child are a redundant H/V pair. Destroy them. 1647 // con and child are a redundant H/V pair. Destroy them.
1677 while (child->children->length) { 1648 while (child->pending.children->length) {
1678 struct sway_container *current = child->children->items[0]; 1649 struct sway_container *current = child->pending.children->items[0];
1679 container_detach(current); 1650 container_detach(current);
1680 if (con->parent) { 1651 if (con->pending.parent) {
1681 container_insert_child(con->parent, current, idx); 1652 container_insert_child(con->pending.parent, current, idx);
1682 } else { 1653 } else {
1683 workspace_insert_tiling_direct(con->workspace, current, idx); 1654 workspace_insert_tiling_direct(con->pending.workspace, current, idx);
1684 } 1655 }
1685 change++; 1656 change++;
1686 } 1657 }
@@ -1692,3 +1663,177 @@ int container_squash(struct sway_container *con) {
1692 } 1663 }
1693 return change; 1664 return change;
1694} 1665}
1666
1667static void swap_places(struct sway_container *con1,
1668 struct sway_container *con2) {
1669 struct sway_container *temp = malloc(sizeof(struct sway_container));
1670 temp->pending.x = con1->pending.x;
1671 temp->pending.y = con1->pending.y;
1672 temp->pending.width = con1->pending.width;
1673 temp->pending.height = con1->pending.height;
1674 temp->width_fraction = con1->width_fraction;
1675 temp->height_fraction = con1->height_fraction;
1676 temp->pending.parent = con1->pending.parent;
1677 temp->pending.workspace = con1->pending.workspace;
1678 bool temp_floating = container_is_floating(con1);
1679
1680 con1->pending.x = con2->pending.x;
1681 con1->pending.y = con2->pending.y;
1682 con1->pending.width = con2->pending.width;
1683 con1->pending.height = con2->pending.height;
1684 con1->width_fraction = con2->width_fraction;
1685 con1->height_fraction = con2->height_fraction;
1686
1687 con2->pending.x = temp->pending.x;
1688 con2->pending.y = temp->pending.y;
1689 con2->pending.width = temp->pending.width;
1690 con2->pending.height = temp->pending.height;
1691 con2->width_fraction = temp->width_fraction;
1692 con2->height_fraction = temp->height_fraction;
1693
1694 int temp_index = container_sibling_index(con1);
1695 if (con2->pending.parent) {
1696 container_insert_child(con2->pending.parent, con1,
1697 container_sibling_index(con2));
1698 } else if (container_is_floating(con2)) {
1699 workspace_add_floating(con2->pending.workspace, con1);
1700 } else {
1701 workspace_insert_tiling(con2->pending.workspace, con1,
1702 container_sibling_index(con2));
1703 }
1704 if (temp->pending.parent) {
1705 container_insert_child(temp->pending.parent, con2, temp_index);
1706 } else if (temp_floating) {
1707 workspace_add_floating(temp->pending.workspace, con2);
1708 } else {
1709 workspace_insert_tiling(temp->pending.workspace, con2, temp_index);
1710 }
1711
1712 free(temp);
1713}
1714
1715static void swap_focus(struct sway_container *con1,
1716 struct sway_container *con2, struct sway_seat *seat,
1717 struct sway_container *focus) {
1718 if (focus == con1 || focus == con2) {
1719 struct sway_workspace *ws1 = con1->pending.workspace;
1720 struct sway_workspace *ws2 = con2->pending.workspace;
1721 enum sway_container_layout layout1 = container_parent_layout(con1);
1722 enum sway_container_layout layout2 = container_parent_layout(con2);
1723 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
1724 if (workspace_is_visible(ws2)) {
1725 seat_set_focus(seat, &con2->node);
1726 }
1727 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
1728 } else if (focus == con2 && (layout1 == L_TABBED
1729 || layout1 == L_STACKED)) {
1730 if (workspace_is_visible(ws1)) {
1731 seat_set_focus(seat, &con1->node);
1732 }
1733 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
1734 } else if (ws1 != ws2) {
1735 seat_set_focus_container(seat, focus == con1 ? con2 : con1);
1736 } else {
1737 seat_set_focus_container(seat, focus);
1738 }
1739 } else {
1740 seat_set_focus_container(seat, focus);
1741 }
1742
1743 if (root->fullscreen_global) {
1744 seat_set_focus(seat,
1745 seat_get_focus_inactive(seat, &root->fullscreen_global->node));
1746 }
1747}
1748
1749void container_swap(struct sway_container *con1, struct sway_container *con2) {
1750 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
1751 return;
1752 }
1753 if (!sway_assert(!container_has_ancestor(con1, con2)
1754 && !container_has_ancestor(con2, con1),
1755 "Cannot swap ancestor and descendant")) {
1756 return;
1757 }
1758
1759 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
1760 con1->node.id, con2->node.id);
1761
1762 bool scratch1 = con1->scratchpad;
1763 bool hidden1 = container_is_scratchpad_hidden(con1);
1764 bool scratch2 = con2->scratchpad;
1765 bool hidden2 = container_is_scratchpad_hidden(con2);
1766 if (scratch1) {
1767 if (hidden1) {
1768 root_scratchpad_show(con1);
1769 }
1770 root_scratchpad_remove_container(con1);
1771 }
1772 if (scratch2) {
1773 if (hidden2) {
1774 root_scratchpad_show(con2);
1775 }
1776 root_scratchpad_remove_container(con2);
1777 }
1778
1779 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
1780 if (fs1) {
1781 container_fullscreen_disable(con1);
1782 }
1783 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
1784 if (fs2) {
1785 container_fullscreen_disable(con2);
1786 }
1787
1788 struct sway_seat *seat = input_manager_current_seat();
1789 struct sway_container *focus = seat_get_focused_container(seat);
1790 struct sway_workspace *vis1 =
1791 output_get_active_workspace(con1->pending.workspace->output);
1792 struct sway_workspace *vis2 =
1793 output_get_active_workspace(con2->pending.workspace->output);
1794 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
1795 "workspace. This should not happen")) {
1796 return;
1797 }
1798
1799 char *stored_prev_name = NULL;
1800 if (seat->prev_workspace_name) {
1801 stored_prev_name = strdup(seat->prev_workspace_name);
1802 }
1803
1804 swap_places(con1, con2);
1805
1806 if (!workspace_is_visible(vis1)) {
1807 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
1808 }
1809 if (!workspace_is_visible(vis2)) {
1810 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
1811 }
1812
1813 swap_focus(con1, con2, seat, focus);
1814
1815 if (stored_prev_name) {
1816 free(seat->prev_workspace_name);
1817 seat->prev_workspace_name = stored_prev_name;
1818 }
1819
1820 if (scratch1) {
1821 root_scratchpad_add_container(con2, NULL);
1822 if (!hidden1) {
1823 root_scratchpad_show(con2);
1824 }
1825 }
1826 if (scratch2) {
1827 root_scratchpad_add_container(con1, NULL);
1828 if (!hidden2) {
1829 root_scratchpad_show(con1);
1830 }
1831 }
1832
1833 if (fs1) {
1834 container_set_fullscreen(con2, fs1);
1835 }
1836 if (fs2) {
1837 container_set_fullscreen(con1, fs2);
1838 }
1839}