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.c1306
1 files changed, 661 insertions, 645 deletions
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 04ef965f..9224b4fb 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,25 +1,20 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdint.h> 3#include <stdint.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <string.h>
7#include <strings.h>
8#include <sys/stat.h>
9#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
10#include <wlr/types/wlr_linux_dmabuf_v1.h> 7#include <wlr/types/wlr_linux_dmabuf_v1.h>
11#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
13#include <wlr/render/drm_format_set.h>
14#include "linux-dmabuf-unstable-v1-protocol.h" 10#include "linux-dmabuf-unstable-v1-protocol.h"
15#include "cairo_util.h"
16#include "pango.h"
17#include "sway/config.h" 11#include "sway/config.h"
18#include "sway/desktop.h"
19#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
20#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
21#include "sway/input/seat.h" 14#include "sway/input/seat.h"
22#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/scene_descriptor.h"
17#include "sway/sway_text_node.h"
23#include "sway/output.h" 18#include "sway/output.h"
24#include "sway/server.h" 19#include "sway/server.h"
25#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
@@ -30,6 +25,53 @@
30#include "log.h" 25#include "log.h"
31#include "stringop.h" 26#include "stringop.h"
32 27
28static void handle_output_enter(
29 struct wl_listener *listener, void *data) {
30 struct sway_container *con = wl_container_of(
31 listener, con, output_enter);
32 struct wlr_scene_output *output = data;
33
34 if (con->view->foreign_toplevel) {
35 wlr_foreign_toplevel_handle_v1_output_enter(
36 con->view->foreign_toplevel, output->output);
37 }
38}
39
40static void handle_output_leave(
41 struct wl_listener *listener, void *data) {
42 struct sway_container *con = wl_container_of(
43 listener, con, output_leave);
44 struct wlr_scene_output *output = data;
45
46 if (con->view->foreign_toplevel) {
47 wlr_foreign_toplevel_handle_v1_output_leave(
48 con->view->foreign_toplevel, output->output);
49 }
50}
51
52static bool handle_point_accepts_input(
53 struct wlr_scene_buffer *buffer, double *x, double *y) {
54 return false;
55}
56
57static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent,
58 bool *failed) {
59 if (*failed) {
60 return NULL;
61 }
62
63 // just pass in random values. These will be overwritten when
64 // they need to be used.
65 struct wlr_scene_rect *rect = wlr_scene_rect_create(
66 parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});
67 if (!rect) {
68 sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect");
69 *failed = true;
70 }
71
72 return rect;
73}
74
33struct sway_container *container_create(struct sway_view *view) { 75struct sway_container *container_create(struct sway_view *view) {
34 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 76 struct sway_container *c = calloc(1, sizeof(struct sway_container));
35 if (!c) { 77 if (!c) {
@@ -37,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) {
37 return NULL; 79 return NULL;
38 } 80 }
39 node_init(&c->node, N_CONTAINER, c); 81 node_init(&c->node, N_CONTAINER, c);
40 c->pending.layout = L_NONE; 82
41 c->view = view; 83 // Container tree structure
42 c->alpha = 1.0f; 84 // - scene tree
85 // - title bar
86 // - border
87 // - background
88 // - title text
89 // - marks text
90 // - border
91 // - border top/bottom/left/right
92 // - content_tree (we put the content node here so when we disable the
93 // border everything gets disabled. We only render the content iff there
94 // is a border as well)
95 // - buffer used for output enter/leave events for foreign_toplevel
96 bool failed = false;
97 c->scene_tree = alloc_scene_tree(root->staging, &failed);
98
99 c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed);
100 c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed);
101 c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed);
102
103 // for opacity purposes we need to carfully create the scene such that
104 // none of our rect nodes as well as text buffers don't overlap. To do
105 // this we have to create rects such that they go around text buffers
106 for (int i = 0; i < 4; i++) {
107 alloc_rect_node(c->title_bar.border, &failed);
108 }
109
110 for (int i = 0; i < 5; i++) {
111 alloc_rect_node(c->title_bar.background, &failed);
112 }
113
114 c->border.tree = alloc_scene_tree(c->scene_tree, &failed);
115 c->content_tree = alloc_scene_tree(c->border.tree, &failed);
116
117 if (view) {
118 // only containers with views can have borders
119 c->border.top = alloc_rect_node(c->border.tree, &failed);
120 c->border.bottom = alloc_rect_node(c->border.tree, &failed);
121 c->border.left = alloc_rect_node(c->border.tree, &failed);
122 c->border.right = alloc_rect_node(c->border.tree, &failed);
123
124 c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL);
125 if (!c->output_handler) {
126 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
127 failed = true;
128 }
129
130 if (!failed) {
131 c->output_enter.notify = handle_output_enter;
132 wl_signal_add(&c->output_handler->events.output_enter,
133 &c->output_enter);
134 c->output_leave.notify = handle_output_leave;
135 wl_signal_add(&c->output_handler->events.output_leave,
136 &c->output_leave);
137 c->output_handler->point_accepts_input = handle_point_accepts_input;
138 }
139 }
140
141 if (!failed && !scene_descriptor_assign(&c->scene_tree->node,
142 SWAY_SCENE_DESC_CONTAINER, c)) {
143 failed = true;
144 }
145
146 if (failed) {
147 wlr_scene_node_destroy(&c->scene_tree->node);
148 free(c);
149 return NULL;
150 }
43 151
44 if (!view) { 152 if (!view) {
45 c->pending.children = create_list(); 153 c->pending.children = create_list();
46 c->current.children = create_list(); 154 c->current.children = create_list();
47 } 155 }
156
157 c->pending.layout = L_NONE;
158 c->view = view;
159 c->alpha = 1.0f;
48 c->marks = create_list(); 160 c->marks = create_list();
49 c->outputs = create_list();
50 161
51 wl_signal_init(&c->events.destroy); 162 wl_signal_init(&c->events.destroy);
52 wl_signal_emit(&root->events.new_node, &c->node); 163 wl_signal_emit_mutable(&root->events.new_node, &c->node);
164
165 container_update(c);
53 166
54 return c; 167 return c;
55} 168}
56 169
170static bool container_is_focused(struct sway_container *con, void *data) {
171 return con->current.focused;
172}
173
174static bool container_has_focused_child(struct sway_container *con) {
175 return container_find_child(con, container_is_focused, NULL);
176}
177
178static bool container_is_current_parent_focused(struct sway_container *con) {
179 if (con->current.parent) {
180 struct sway_container *parent = con->current.parent;
181 return parent->current.focused || container_is_current_parent_focused(parent);
182 } else if (con->current.workspace) {
183 struct sway_workspace *ws = con->current.workspace;
184 return ws->current.focused;
185 }
186
187 return false;
188}
189
190static struct border_colors *container_get_current_colors(
191 struct sway_container *con) {
192 struct border_colors *colors;
193
194 bool urgent = con->view ?
195 view_is_urgent(con->view) : container_has_urgent_child(con);
196 struct sway_container *active_child;
197
198 if (con->current.parent) {
199 active_child = con->current.parent->current.focused_inactive_child;
200 } else if (con->current.workspace) {
201 active_child = con->current.workspace->current.focused_inactive_child;
202 } else {
203 active_child = NULL;
204 }
205
206 if (urgent) {
207 colors = &config->border_colors.urgent;
208 } else if (con->current.focused || container_is_current_parent_focused(con)) {
209 colors = &config->border_colors.focused;
210 } else if (config->has_focused_tab_title && container_has_focused_child(con)) {
211 colors = &config->border_colors.focused_tab_title;
212 } else if (con == active_child) {
213 colors = &config->border_colors.focused_inactive;
214 } else {
215 colors = &config->border_colors.unfocused;
216 }
217
218 return colors;
219}
220
221static bool container_is_current_floating(struct sway_container *container) {
222 if (!container->current.parent && container->current.workspace &&
223 list_find(container->current.workspace->floating, container) != -1) {
224 return true;
225 }
226 if (container->scratchpad) {
227 return true;
228 }
229 return false;
230}
231
232// scene rect wants premultiplied colors
233static void scene_rect_set_color(struct wlr_scene_rect *rect,
234 const float color[4], float opacity) {
235 const float premultiplied[] = {
236 color[0] * color[3] * opacity,
237 color[1] * color[3] * opacity,
238 color[2] * color[3] * opacity,
239 color[3] * opacity,
240 };
241
242 wlr_scene_rect_set_color(rect, premultiplied);
243}
244
245void container_update(struct sway_container *con) {
246 struct border_colors *colors = container_get_current_colors(con);
247 list_t *siblings = NULL;
248 enum sway_container_layout layout = L_NONE;
249 float alpha = con->alpha;
250
251 if (con->current.parent) {
252 siblings = con->current.parent->current.children;
253 layout = con->current.parent->current.layout;
254 } else if (con->current.workspace) {
255 siblings = con->current.workspace->current.tiling;
256 layout = con->current.workspace->current.layout;
257 }
258
259 float bottom[4], right[4];
260 memcpy(bottom, colors->child_border, sizeof(bottom));
261 memcpy(right, colors->child_border, sizeof(right));
262
263 if (!container_is_current_floating(con) && siblings && siblings->length == 1) {
264 if (layout == L_HORIZ) {
265 memcpy(right, colors->indicator, sizeof(right));
266 } else if (layout == L_VERT) {
267 memcpy(bottom, colors->indicator, sizeof(bottom));
268 }
269 }
270
271 struct wlr_scene_node *node;
272 wl_list_for_each(node, &con->title_bar.border->children, link) {
273 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
274 scene_rect_set_color(rect, colors->border, alpha);
275 }
276
277 wl_list_for_each(node, &con->title_bar.background->children, link) {
278 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
279 scene_rect_set_color(rect, colors->background, alpha);
280 }
281
282 if (con->view) {
283 scene_rect_set_color(con->border.top, colors->child_border, alpha);
284 scene_rect_set_color(con->border.bottom, bottom, alpha);
285 scene_rect_set_color(con->border.left, colors->child_border, alpha);
286 scene_rect_set_color(con->border.right, right, alpha);
287 }
288
289 if (con->title_bar.title_text) {
290 sway_text_node_set_color(con->title_bar.title_text, colors->text);
291 sway_text_node_set_background(con->title_bar.title_text, colors->background);
292 }
293
294 if (con->title_bar.marks_text) {
295 sway_text_node_set_color(con->title_bar.marks_text, colors->text);
296 sway_text_node_set_background(con->title_bar.marks_text, colors->background);
297 }
298}
299
300void container_update_itself_and_parents(struct sway_container *con) {
301 container_update(con);
302
303 if (con->current.parent) {
304 container_update_itself_and_parents(con->current.parent);
305 }
306}
307
308static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) {
309 int len;
310 const pixman_box32_t *rects = pixman_region32_rectangles(region, &len);
311
312 wlr_scene_node_set_enabled(&tree->node, len > 0);
313 if (len == 0) {
314 return;
315 }
316
317 int i = 0;
318 struct wlr_scene_node *node;
319 wl_list_for_each(node, &tree->children, link) {
320 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
321 wlr_scene_node_set_enabled(&rect->node, i < len);
322
323 if (i < len) {
324 const pixman_box32_t *box = &rects[i++];
325 wlr_scene_node_set_position(&rect->node, box->x1, box->y1);
326 wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1);
327 }
328 }
329}
330
331void container_arrange_title_bar(struct sway_container *con) {
332 enum alignment title_align = config->title_align;
333 int marks_buffer_width = 0;
334 int width = con->title_width;
335 int height = container_titlebar_height();
336
337 pixman_region32_t text_area;
338 pixman_region32_init(&text_area);
339
340 if (con->title_bar.marks_text) {
341 struct sway_text_node *node = con->title_bar.marks_text;
342 marks_buffer_width = node->width;
343
344 int h_padding;
345 if (title_align == ALIGN_RIGHT) {
346 h_padding = config->titlebar_h_padding;
347 } else {
348 h_padding = width - config->titlebar_h_padding - marks_buffer_width;
349 }
350
351 h_padding = MAX(h_padding, 0);
352
353 int alloc_width = MIN((int)node->width,
354 width - h_padding - config->titlebar_h_padding);
355 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
57void container_destroy(struct sway_container *con) { 487void container_destroy(struct sway_container *con) {
58 if (!sway_assert(con->node.destroying, 488 if (!sway_assert(con->node.destroying,
59 "Tried to free container which wasn't marked as destroying")) { 489 "Tried to free container which wasn't marked as destroying")) {
@@ -65,29 +495,21 @@ void container_destroy(struct sway_container *con) {
65 } 495 }
66 free(con->title); 496 free(con->title);
67 free(con->formatted_title); 497 free(con->formatted_title);
68 wlr_texture_destroy(con->title_focused);
69 wlr_texture_destroy(con->title_focused_inactive);
70 wlr_texture_destroy(con->title_unfocused);
71 wlr_texture_destroy(con->title_urgent);
72 wlr_texture_destroy(con->title_focused_tab_title);
73 list_free(con->pending.children); 498 list_free(con->pending.children);
74 list_free(con->current.children); 499 list_free(con->current.children);
75 list_free(con->outputs);
76 500
77 list_free_items_and_destroy(con->marks); 501 list_free_items_and_destroy(con->marks);
78 wlr_texture_destroy(con->marks_focused);
79 wlr_texture_destroy(con->marks_focused_inactive);
80 wlr_texture_destroy(con->marks_unfocused);
81 wlr_texture_destroy(con->marks_urgent);
82 wlr_texture_destroy(con->marks_focused_tab_title);
83 502
84 if (con->view && con->view->container == con) { 503 if (con->view && con->view->container == con) {
85 con->view->container = NULL; 504 con->view->container = NULL;
505 wlr_scene_node_destroy(&con->output_handler->node);
86 if (con->view->destroying) { 506 if (con->view->destroying) {
87 view_destroy(con->view); 507 view_destroy(con->view);
88 } 508 }
89 } 509 }
90 510
511 scene_node_disown_children(con->content_tree);
512 wlr_scene_node_destroy(&con->scene_tree->node);
91 free(con); 513 free(con);
92} 514}
93 515
@@ -104,7 +526,7 @@ void container_begin_destroy(struct sway_container *con) {
104 container_fullscreen_disable(con); 526 container_fullscreen_disable(con);
105 } 527 }
106 528
107 wl_signal_emit(&con->node.events.destroy, &con->node); 529 wl_signal_emit_mutable(&con->node.events.destroy, &con->node);
108 530
109 container_end_mouse_operation(con); 531 container_end_mouse_operation(con);
110 532
@@ -174,265 +596,6 @@ struct sway_container *container_find_child(struct sway_container *container,
174 return NULL; 596 return NULL;
175} 597}
176 598
177static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly,
178 struct wlr_surface **surface, double *sx, double *sy) {
179 if (!sway_assert(con->view, "Expected a view")) {
180 return NULL;
181 }
182 struct sway_view *view = con->view;
183 double view_sx = lx - con->surface_x + view->geometry.x;
184 double view_sy = ly - con->surface_y + view->geometry.y;
185
186 double _sx, _sy;
187 struct wlr_surface *_surface = NULL;
188 switch (view->type) {
189#if HAVE_XWAYLAND
190 case SWAY_VIEW_XWAYLAND:
191 _surface = wlr_surface_surface_at(view->surface,
192 view_sx, view_sy, &_sx, &_sy);
193 break;
194#endif
195 case SWAY_VIEW_XDG_SHELL:
196 _surface = wlr_xdg_surface_surface_at(
197 view->wlr_xdg_toplevel->base,
198 view_sx, view_sy, &_sx, &_sy);
199 break;
200 }
201 if (_surface) {
202 *sx = _sx;
203 *sy = _sy;
204 *surface = _surface;
205 return con;
206 }
207 return NULL;
208}
209
210/**
211 * container_at for a container with layout L_TABBED.
212 */
213static struct sway_container *container_at_tabbed(struct sway_node *parent,
214 double lx, double ly,
215 struct wlr_surface **surface, double *sx, double *sy) {
216 struct wlr_box box;
217 node_get_box(parent, &box);
218 if (lx < box.x || lx > box.x + box.width ||
219 ly < box.y || ly > box.y + box.height) {
220 return NULL;
221 }
222 struct sway_seat *seat = input_manager_current_seat();
223 list_t *children = node_get_children(parent);
224 if (!children->length) {
225 return NULL;
226 }
227
228 // Tab titles
229 int title_height = container_titlebar_height();
230 if (ly < box.y + title_height) {
231 int tab_width = box.width / children->length;
232 int child_index = (lx - box.x) / tab_width;
233 if (child_index >= children->length) {
234 child_index = children->length - 1;
235 }
236 struct sway_container *child = children->items[child_index];
237 return child;
238 }
239
240 // Surfaces
241 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
242 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
243}
244
245/**
246 * container_at for a container with layout L_STACKED.
247 */
248static struct sway_container *container_at_stacked(struct sway_node *parent,
249 double lx, double ly,
250 struct wlr_surface **surface, double *sx, double *sy) {
251 struct wlr_box box;
252 node_get_box(parent, &box);
253 if (lx < box.x || lx > box.x + box.width ||
254 ly < box.y || ly > box.y + box.height) {
255 return NULL;
256 }
257 struct sway_seat *seat = input_manager_current_seat();
258 list_t *children = node_get_children(parent);
259
260 // Title bars
261 int title_height = container_titlebar_height();
262 if (title_height > 0) {
263 int child_index = (ly - box.y) / title_height;
264 if (child_index < children->length) {
265 struct sway_container *child = children->items[child_index];
266 return child;
267 }
268 }
269
270 // Surfaces
271 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
272 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
273}
274
275/**
276 * container_at for a container with layout L_HORIZ or L_VERT.
277 */
278static struct sway_container *container_at_linear(struct sway_node *parent,
279 double lx, double ly,
280 struct wlr_surface **surface, double *sx, double *sy) {
281 list_t *children = node_get_children(parent);
282 for (int i = 0; i < children->length; ++i) {
283 struct sway_container *child = children->items[i];
284 struct sway_container *container =
285 tiling_container_at(&child->node, lx, ly, surface, sx, sy);
286 if (container) {
287 return container;
288 }
289 }
290 return NULL;
291}
292
293static struct sway_container *floating_container_at(double lx, double ly,
294 struct wlr_surface **surface, double *sx, double *sy) {
295 // For outputs with floating containers that overhang the output bounds,
296 // those at the end of the output list appear on top of floating
297 // containers from other outputs, so iterate the list in reverse.
298 for (int i = root->outputs->length - 1; i >= 0; --i) {
299 struct sway_output *output = root->outputs->items[i];
300 for (int j = 0; j < output->workspaces->length; ++j) {
301 struct sway_workspace *ws = output->workspaces->items[j];
302 if (!workspace_is_visible(ws)) {
303 continue;
304 }
305 // Items at the end of the list are on top, so iterate the list in
306 // reverse.
307 for (int k = ws->floating->length - 1; k >= 0; --k) {
308 struct sway_container *floater = ws->floating->items[k];
309 struct sway_container *container =
310 tiling_container_at(&floater->node, lx, ly, surface, sx, sy);
311 if (container) {
312 return container;
313 }
314 }
315 }
316 }
317 return NULL;
318}
319
320static struct sway_container *view_container_content_at(struct sway_node *parent,
321 double lx, double ly,
322 struct wlr_surface **surface, double *sx, double *sy) {
323 if (!sway_assert(node_is_view(parent), "Expected a view")) {
324 return NULL;
325 }
326
327 struct sway_container *container = parent->sway_container;
328 struct wlr_box box = {
329 .x = container->pending.content_x,
330 .y = container->pending.content_y,
331 .width = container->pending.content_width,
332 .height = container->pending.content_height,
333 };
334
335 if (wlr_box_contains_point(&box, lx, ly)) {
336 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
337 return container;
338 }
339
340 return NULL;
341}
342
343static struct sway_container *view_container_at(struct sway_node *parent,
344 double lx, double ly,
345 struct wlr_surface **surface, double *sx, double *sy) {
346 if (!sway_assert(node_is_view(parent), "Expected a view")) {
347 return NULL;
348 }
349
350 struct sway_container *container = parent->sway_container;
351 struct wlr_box box = {
352 .x = container->pending.x,
353 .y = container->pending.y,
354 .width = container->pending.width,
355 .height = container->pending.height,
356 };
357
358 if (wlr_box_contains_point(&box, lx, ly)) {
359 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
360 return container;
361 }
362
363 return NULL;
364}
365
366struct sway_container *tiling_container_at(struct sway_node *parent,
367 double lx, double ly,
368 struct wlr_surface **surface, double *sx, double *sy) {
369 if (node_is_view(parent)) {
370 return view_container_at(parent, lx, ly, surface, sx, sy);
371 }
372 if (!node_get_children(parent)) {
373 return NULL;
374 }
375 switch (node_get_layout(parent)) {
376 case L_HORIZ:
377 case L_VERT:
378 return container_at_linear(parent, lx, ly, surface, sx, sy);
379 case L_TABBED:
380 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
381 case L_STACKED:
382 return container_at_stacked(parent, lx, ly, surface, sx, sy);
383 case L_NONE:
384 return NULL;
385 }
386 return NULL;
387}
388
389static bool surface_is_popup(struct wlr_surface *surface) {
390 while (!wlr_surface_is_xdg_surface(surface)) {
391 if (!wlr_surface_is_subsurface(surface)) {
392 return false;
393 }
394 struct wlr_subsurface *subsurface =
395 wlr_subsurface_from_wlr_surface(surface);
396 surface = subsurface->parent;
397 }
398 struct wlr_xdg_surface *xdg_surface =
399 wlr_xdg_surface_from_wlr_surface(surface);
400 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
401}
402
403struct sway_container *container_at(struct sway_workspace *workspace,
404 double lx, double ly,
405 struct wlr_surface **surface, double *sx, double *sy) {
406 struct sway_container *c;
407
408 struct sway_seat *seat = input_manager_current_seat();
409 struct sway_container *focus = seat_get_focused_container(seat);
410 bool is_floating = focus && container_is_floating_or_child(focus);
411 // Focused view's popups
412 if (focus && focus->view) {
413 c = surface_at_view(focus, lx, ly, surface, sx, sy);
414 if (c && surface_is_popup(*surface)) {
415 return c;
416 }
417 *surface = NULL;
418 }
419 // Floating
420 if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) {
421 return c;
422 }
423 // Tiling (focused)
424 if (focus && focus->view && !is_floating) {
425 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
426 return c;
427 }
428 }
429 // Tiling (non-focused)
430 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
431 return c;
432 }
433 return NULL;
434}
435
436void container_for_each_child(struct sway_container *container, 599void container_for_each_child(struct sway_container *container,
437 void (*f)(struct sway_container *container, void *data), 600 void (*f)(struct sway_container *container, void *data),
438 void *data) { 601 void *data) {
@@ -478,127 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant,
478 return false; 641 return false;
479} 642}
480 643
481void container_damage_whole(struct sway_container *container) {
482 for (int i = 0; i < root->outputs->length; ++i) {
483 struct sway_output *output = root->outputs->items[i];
484 output_damage_whole_container(output, container);
485 }
486}
487
488/**
489 * Return the output which will be used for scale purposes.
490 * This is the most recently entered output.
491 */
492struct sway_output *container_get_effective_output(struct sway_container *con) {
493 if (con->outputs->length == 0) {
494 return NULL;
495 }
496 return con->outputs->items[con->outputs->length - 1];
497}
498
499static void render_titlebar_text_texture(struct sway_output *output,
500 struct sway_container *con, struct wlr_texture **texture,
501 struct border_colors *class, bool pango_markup, char *text) {
502 double scale = output->wlr_output->scale;
503 int width = 0;
504 int height = config->font_height * scale;
505 int baseline;
506
507 // We must use a non-nil cairo_t for cairo_set_font_options to work.
508 // Therefore, we cannot use cairo_create(NULL).
509 cairo_surface_t *dummy_surface = cairo_image_surface_create(
510 CAIRO_FORMAT_ARGB32, 0, 0);
511 cairo_t *c = cairo_create(dummy_surface);
512 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
513 cairo_font_options_t *fo = cairo_font_options_create();
514 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
515 if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
516 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
517 } else {
518 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
519 cairo_font_options_set_subpixel_order(fo,
520 to_cairo_subpixel_order(output->wlr_output->subpixel));
521 }
522 cairo_set_font_options(c, fo);
523 get_text_size(c, config->font_description, &width, NULL, &baseline, scale,
524 config->pango_markup, "%s", text);
525 cairo_surface_destroy(dummy_surface);
526 cairo_destroy(c);
527
528 if (width == 0 || height == 0) {
529 return;
530 }
531
532 if (height > config->font_height * scale) {
533 height = config->font_height * scale;
534 }
535
536 cairo_surface_t *surface = cairo_image_surface_create(
537 CAIRO_FORMAT_ARGB32, width, height);
538 cairo_status_t status = cairo_surface_status(surface);
539 if (status != CAIRO_STATUS_SUCCESS) {
540 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
541 cairo_status_to_string(status));
542 return;
543 }
544
545 cairo_t *cairo = cairo_create(surface);
546 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
547 cairo_set_font_options(cairo, fo);
548 cairo_font_options_destroy(fo);
549 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
550 class->background[2], class->background[3]);
551 cairo_paint(cairo);
552 PangoContext *pango = pango_cairo_create_context(cairo);
553 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
554 class->text[2], class->text[3]);
555 cairo_move_to(cairo, 0, config->font_baseline * scale - baseline);
556
557 render_text(cairo, config->font_description, scale, pango_markup, "%s", text);
558
559 cairo_surface_flush(surface);
560 unsigned char *data = cairo_image_surface_get_data(surface);
561 int stride = cairo_image_surface_get_stride(surface);
562 struct wlr_renderer *renderer = output->wlr_output->renderer;
563 *texture = wlr_texture_from_pixels(
564 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
565 cairo_surface_destroy(surface);
566 g_object_unref(pango);
567 cairo_destroy(cairo);
568}
569
570static void update_title_texture(struct sway_container *con,
571 struct wlr_texture **texture, struct border_colors *class) {
572 struct sway_output *output = container_get_effective_output(con);
573 if (!output) {
574 return;
575 }
576 if (*texture) {
577 wlr_texture_destroy(*texture);
578 *texture = NULL;
579 }
580 if (!con->formatted_title) {
581 return;
582 }
583
584 render_titlebar_text_texture(output, con, texture, class,
585 config->pango_markup, con->formatted_title);
586}
587
588void container_update_title_textures(struct sway_container *container) {
589 update_title_texture(container, &container->title_focused,
590 &config->border_colors.focused);
591 update_title_texture(container, &container->title_focused_inactive,
592 &config->border_colors.focused_inactive);
593 update_title_texture(container, &container->title_unfocused,
594 &config->border_colors.unfocused);
595 update_title_texture(container, &container->title_urgent,
596 &config->border_colors.urgent);
597 update_title_texture(container, &container->title_focused_tab_title,
598 &config->border_colors.focused_tab_title);
599 container_damage_whole(container);
600}
601
602/** 644/**
603 * Calculate and return the length of the tree representation. 645 * Calculate and return the length of the tree representation.
604 * An example tree representation is: V[Terminal, Firefox] 646 * An example tree representation is: V[Terminal, Firefox]
@@ -664,7 +706,13 @@ void container_update_representation(struct sway_container *con) {
664 } 706 }
665 container_build_representation(con->pending.layout, con->pending.children, 707 container_build_representation(con->pending.layout, con->pending.children,
666 con->formatted_title); 708 con->formatted_title);
667 container_update_title_textures(con); 709
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 }
668 } 716 }
669 if (con->pending.parent) { 717 if (con->pending.parent) {
670 container_update_representation(con->pending.parent); 718 container_update_representation(con->pending.parent);
@@ -716,6 +764,21 @@ void floating_calculate_constraints(int *min_width, int *max_width,
716 764
717} 765}
718 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
719static void floating_natural_resize(struct sway_container *con) { 782static void floating_natural_resize(struct sway_container *con) {
720 int min_width, max_width, min_height, max_height; 783 int min_width, max_width, min_height, max_height;
721 floating_calculate_constraints(&min_width, &max_width, 784 floating_calculate_constraints(&min_width, &max_width,
@@ -787,11 +850,11 @@ void container_floating_set_default_size(struct sway_container *con) {
787 int min_width, max_width, min_height, max_height; 850 int min_width, max_width, min_height, max_height;
788 floating_calculate_constraints(&min_width, &max_width, 851 floating_calculate_constraints(&min_width, &max_width,
789 &min_height, &max_height); 852 &min_height, &max_height);
790 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 853 struct wlr_box box;
791 workspace_get_box(con->pending.workspace, box); 854 workspace_get_box(con->pending.workspace, &box);
792 855
793 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));
794 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));
795 if (!con->view) { 858 if (!con->view) {
796 con->pending.width = width; 859 con->pending.width = width;
797 con->pending.height = height; 860 con->pending.height = height;
@@ -800,8 +863,6 @@ void container_floating_set_default_size(struct sway_container *con) {
800 con->pending.content_height = height; 863 con->pending.content_height = height;
801 container_set_geometry_from_content(con); 864 container_set_geometry_from_content(con);
802 } 865 }
803
804 free(box);
805} 866}
806 867
807 868
@@ -937,17 +998,6 @@ bool container_is_floating(struct sway_container *container) {
937 return false; 998 return false;
938} 999}
939 1000
940bool container_is_current_floating(struct sway_container *container) {
941 if (!container->current.parent && container->current.workspace &&
942 list_find(container->current.workspace->floating, container) != -1) {
943 return true;
944 }
945 if (container->scratchpad) {
946 return true;
947 }
948 return false;
949}
950
951void container_get_box(struct sway_container *container, struct wlr_box *box) { 1001void container_get_box(struct sway_container *container, struct wlr_box *box) {
952 box->x = container->pending.x; 1002 box->x = container->pending.x;
953 box->y = container->pending.y; 1003 box->y = container->pending.y;
@@ -1031,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con,
1031 workspace_add_floating(new_workspace, con); 1081 workspace_add_floating(new_workspace, con);
1032 arrange_workspace(old_workspace); 1082 arrange_workspace(old_workspace);
1033 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 }
1034 workspace_detect_urgent(old_workspace); 1091 workspace_detect_urgent(old_workspace);
1035 workspace_detect_urgent(new_workspace); 1092 workspace_detect_urgent(new_workspace);
1036 } 1093 }
@@ -1062,16 +1119,6 @@ void container_end_mouse_operation(struct sway_container *container) {
1062 } 1119 }
1063} 1120}
1064 1121
1065static bool devid_from_fd(int fd, dev_t *devid) {
1066 struct stat stat;
1067 if (fstat(fd, &stat) != 0) {
1068 sway_log_errno(SWAY_ERROR, "fstat failed");
1069 return false;
1070 }
1071 *devid = stat.st_rdev;
1072 return true;
1073}
1074
1075static void set_fullscreen(struct sway_container *con, bool enable) { 1122static void set_fullscreen(struct sway_container *con, bool enable) {
1076 if (!con->view) { 1123 if (!con->view) {
1077 return; 1124 return;
@@ -1083,75 +1130,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) {
1083 con->view->foreign_toplevel, enable); 1130 con->view->foreign_toplevel, enable);
1084 } 1131 }
1085 } 1132 }
1086
1087 if (!server.linux_dmabuf_v1 || !con->view->surface) {
1088 return;
1089 }
1090 if (!enable) {
1091 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1092 con->view->surface, NULL);
1093 return;
1094 }
1095
1096 if (!con->pending.workspace || !con->pending.workspace->output) {
1097 return;
1098 }
1099
1100 struct sway_output *output = con->pending.workspace->output;
1101 struct wlr_output *wlr_output = output->wlr_output;
1102
1103 // TODO: add wlroots helpers for all of this stuff
1104
1105 const struct wlr_drm_format_set *renderer_formats =
1106 wlr_renderer_get_dmabuf_texture_formats(server.renderer);
1107 assert(renderer_formats);
1108
1109 int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
1110 int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
1111 if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
1112 return;
1113 }
1114
1115 dev_t render_dev, scanout_dev;
1116 if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
1117 !devid_from_fd(backend_drm_fd, &scanout_dev)) {
1118 return;
1119 }
1120
1121 const struct wlr_drm_format_set *output_formats =
1122 wlr_output_get_primary_formats(output->wlr_output,
1123 WLR_BUFFER_CAP_DMABUF);
1124 if (!output_formats) {
1125 return;
1126 }
1127
1128 struct wlr_drm_format_set scanout_formats = {0};
1129 if (!wlr_drm_format_set_intersect(&scanout_formats,
1130 output_formats, renderer_formats)) {
1131 return;
1132 }
1133
1134 struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
1135 {
1136 .target_device = scanout_dev,
1137 .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
1138 .formats = &scanout_formats,
1139 },
1140 {
1141 .target_device = render_dev,
1142 .formats = renderer_formats,
1143 },
1144 };
1145
1146 const struct wlr_linux_dmabuf_feedback_v1 feedback = {
1147 .main_device = render_dev,
1148 .tranches = tranches,
1149 .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
1150 };
1151 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1152 con->view->surface, &feedback);
1153
1154 wlr_drm_format_set_finish(&scanout_formats);
1155} 1133}
1156 1134
1157static void container_fullscreen_workspace(struct sway_container *con) { 1135static void container_fullscreen_workspace(struct sway_container *con) {
@@ -1321,72 +1299,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1321 return false; 1299 return false;
1322} 1300}
1323 1301
1324static void surface_send_enter_iterator(struct wlr_surface *surface,
1325 int x, int y, void *data) {
1326 struct wlr_output *wlr_output = data;
1327 wlr_surface_send_enter(surface, wlr_output);
1328}
1329
1330static void surface_send_leave_iterator(struct wlr_surface *surface,
1331 int x, int y, void *data) {
1332 struct wlr_output *wlr_output = data;
1333 wlr_surface_send_leave(surface, wlr_output);
1334}
1335
1336void container_discover_outputs(struct sway_container *con) {
1337 struct wlr_box con_box = {
1338 .x = con->current.x,
1339 .y = con->current.y,
1340 .width = con->current.width,
1341 .height = con->current.height,
1342 };
1343 struct sway_output *old_output = container_get_effective_output(con);
1344
1345 for (int i = 0; i < root->outputs->length; ++i) {
1346 struct sway_output *output = root->outputs->items[i];
1347 struct wlr_box output_box;
1348 output_get_box(output, &output_box);
1349 struct wlr_box intersection;
1350 bool intersects =
1351 wlr_box_intersection(&intersection, &con_box, &output_box);
1352 int index = list_find(con->outputs, output);
1353
1354 if (intersects && index == -1) {
1355 // Send enter
1356 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1357 if (con->view) {
1358 view_for_each_surface(con->view,
1359 surface_send_enter_iterator, output->wlr_output);
1360 if (con->view->foreign_toplevel) {
1361 wlr_foreign_toplevel_handle_v1_output_enter(
1362 con->view->foreign_toplevel, output->wlr_output);
1363 }
1364 }
1365 list_add(con->outputs, output);
1366 } else if (!intersects && index != -1) {
1367 // Send leave
1368 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1369 if (con->view) {
1370 view_for_each_surface(con->view,
1371 surface_send_leave_iterator, output->wlr_output);
1372 if (con->view->foreign_toplevel) {
1373 wlr_foreign_toplevel_handle_v1_output_leave(
1374 con->view->foreign_toplevel, output->wlr_output);
1375 }
1376 }
1377 list_del(con->outputs, index);
1378 }
1379 }
1380 struct sway_output *new_output = container_get_effective_output(con);
1381 double old_scale = old_output && old_output->enabled ?
1382 old_output->wlr_output->scale : -1;
1383 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1384 if (old_scale != new_scale) {
1385 container_update_title_textures(con);
1386 container_update_marks_textures(con);
1387 }
1388}
1389
1390enum sway_container_layout container_parent_layout(struct sway_container *con) { 1302enum sway_container_layout container_parent_layout(struct sway_container *con) {
1391 if (con->pending.parent) { 1303 if (con->pending.parent) {
1392 return con->pending.parent->pending.layout; 1304 return con->pending.parent->pending.layout;
@@ -1397,19 +1309,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) {
1397 return L_NONE; 1309 return L_NONE;
1398} 1310}
1399 1311
1400enum sway_container_layout container_current_parent_layout(
1401 struct sway_container *con) {
1402 if (con->current.parent) {
1403 return con->current.parent->current.layout;
1404 }
1405 return con->current.workspace->current.layout;
1406}
1407
1408list_t *container_get_siblings(struct sway_container *container) { 1312list_t *container_get_siblings(struct sway_container *container) {
1409 if (container->pending.parent) { 1313 if (container->pending.parent) {
1410 return container->pending.parent->pending.children; 1314 return container->pending.parent->pending.children;
1411 } 1315 }
1412 if (container_is_scratchpad_hidden(container)) { 1316 if (!container->pending.workspace) {
1413 return NULL; 1317 return NULL;
1414 } 1318 }
1415 if (list_find(container->pending.workspace->tiling, container) != -1) { 1319 if (list_find(container->pending.workspace->tiling, container) != -1) {
@@ -1422,13 +1326,6 @@ int container_sibling_index(struct sway_container *child) {
1422 return list_find(container_get_siblings(child), child); 1326 return list_find(container_get_siblings(child), child);
1423} 1327}
1424 1328
1425list_t *container_get_current_siblings(struct sway_container *container) {
1426 if (container->current.parent) {
1427 return container->current.parent->current.children;
1428 }
1429 return container->current.workspace->current.tiling;
1430}
1431
1432void container_handle_fullscreen_reparent(struct sway_container *con) { 1329void container_handle_fullscreen_reparent(struct sway_container *con) {
1433 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || 1330 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1434 con->pending.workspace->fullscreen == con) { 1331 con->pending.workspace->fullscreen == con) {
@@ -1643,7 +1540,7 @@ bool container_find_and_unmark(char *mark) {
1643 if (strcmp(con_mark, mark) == 0) { 1540 if (strcmp(con_mark, mark) == 0) {
1644 free(con_mark); 1541 free(con_mark);
1645 list_del(con->marks, i); 1542 list_del(con->marks, i);
1646 container_update_marks_textures(con); 1543 container_update_marks(con);
1647 ipc_event_window(con, "mark"); 1544 ipc_event_window(con, "mark");
1648 return true; 1545 return true;
1649 } 1546 }
@@ -1674,70 +1571,15 @@ void container_add_mark(struct sway_container *con, char *mark) {
1674 ipc_event_window(con, "mark"); 1571 ipc_event_window(con, "mark");
1675} 1572}
1676 1573
1677static void update_marks_texture(struct sway_container *con,
1678 struct wlr_texture **texture, struct border_colors *class) {
1679 struct sway_output *output = container_get_effective_output(con);
1680 if (!output) {
1681 return;
1682 }
1683 if (*texture) {
1684 wlr_texture_destroy(*texture);
1685 *texture = NULL;
1686 }
1687 if (!con->marks->length) {
1688 return;
1689 }
1690
1691 size_t len = 0;
1692 for (int i = 0; i < con->marks->length; ++i) {
1693 char *mark = con->marks->items[i];
1694 if (mark[0] != '_') {
1695 len += strlen(mark) + 2;
1696 }
1697 }
1698 char *buffer = calloc(len + 1, 1);
1699 char *part = malloc(len + 1);
1700
1701 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1702 free(buffer);
1703 return;
1704 }
1705
1706 for (int i = 0; i < con->marks->length; ++i) {
1707 char *mark = con->marks->items[i];
1708 if (mark[0] != '_') {
1709 snprintf(part, len + 1, "[%s]", mark);
1710 strcat(buffer, part);
1711 }
1712 }
1713 free(part);
1714
1715 render_titlebar_text_texture(output, con, texture, class, false, buffer);
1716
1717 free(buffer);
1718}
1719
1720void container_update_marks_textures(struct sway_container *con) {
1721 if (!config->show_marks) {
1722 return;
1723 }
1724 update_marks_texture(con, &con->marks_focused,
1725 &config->border_colors.focused);
1726 update_marks_texture(con, &con->marks_focused_inactive,
1727 &config->border_colors.focused_inactive);
1728 update_marks_texture(con, &con->marks_unfocused,
1729 &config->border_colors.unfocused);
1730 update_marks_texture(con, &con->marks_urgent,
1731 &config->border_colors.urgent);
1732 update_marks_texture(con, &con->marks_focused_tab_title,
1733 &config->border_colors.focused_tab_title);
1734 container_damage_whole(con);
1735}
1736
1737void container_raise_floating(struct sway_container *con) { 1574void container_raise_floating(struct sway_container *con) {
1738 // 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.
1739 struct sway_container *floater = container_toplevel_ancestor(con); 1576 struct sway_container *floater = container_toplevel_ancestor(con);
1740 if (container_is_floating(floater) && floater->pending.workspace) { 1577 if (container_is_floating(floater) && floater->pending.workspace) {
1578 // it's okay to just raise the scene directly instead of waiting
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
1741 list_move_to_end(floater->pending.workspace->floating, floater); 1583 list_move_to_end(floater->pending.workspace->floating, floater);
1742 node_set_dirty(&floater->pending.workspace->node); 1584 node_set_dirty(&floater->pending.workspace->node);
1743 } 1585 }
@@ -1821,3 +1663,177 @@ int container_squash(struct sway_container *con) {
1821 } 1663 }
1822 return change; 1664 return change;
1823} 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}