1 #define _POSIX_C_SOURCE 200809L
2 #include <assert.h>
3 #include <stdint.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <strings.h>
7 #include <wayland-server-core.h>
8 #include <wlr/types/wlr_output_layout.h>
9 #include "cairo.h"
10 #include "pango.h"
11 #include "sway/config.h"
12 #include "sway/desktop.h"
13 #include "sway/desktop/transaction.h"
14 #include "sway/input/input-manager.h"
15 #include "sway/input/seat.h"
16 #include "sway/ipc-server.h"
17 #include "sway/output.h"
18 #include "sway/server.h"
19 #include "sway/tree/arrange.h"
20 #include "sway/tree/view.h"
21 #include "sway/tree/workspace.h"
22 #include "list.h"
23 #include "log.h"
24 #include "stringop.h"
25
container_create(struct sway_view * view)26 struct sway_container *container_create(struct sway_view *view) {
27 struct sway_container *c = calloc(1, sizeof(struct sway_container));
28 if (!c) {
29 sway_log(SWAY_ERROR, "Unable to allocate sway_container");
30 return NULL;
31 }
32 node_init(&c->node, N_CONTAINER, c);
33 c->layout = L_NONE;
34 c->view = view;
35 c->alpha = 1.0f;
36
37 if (!view) {
38 c->children = create_list();
39 c->current.children = create_list();
40 }
41 c->marks = create_list();
42 c->outputs = create_list();
43
44 wl_signal_init(&c->events.destroy);
45 wl_signal_emit(&root->events.new_node, &c->node);
46
47 return c;
48 }
49
container_destroy(struct sway_container * con)50 void container_destroy(struct sway_container *con) {
51 if (!sway_assert(con->node.destroying,
52 "Tried to free container which wasn't marked as destroying")) {
53 return;
54 }
55 if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container "
56 "which is still referenced by transactions")) {
57 return;
58 }
59 free(con->title);
60 free(con->formatted_title);
61 wlr_texture_destroy(con->title_focused);
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);
67 list_free(con->outputs);
68
69 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
75 if (con->view) {
76 if (con->view->container == con) {
77 con->view->container = NULL;
78 }
79 if (con->view->destroying) {
80 view_destroy(con->view);
81 }
82 }
83
84 free(con);
85 }
86
container_begin_destroy(struct sway_container * con)87 void container_begin_destroy(struct sway_container *con) {
88 if (con->view) {
89 ipc_event_window(con, "close");
90 }
91 // The workspace must have the fullscreen pointer cleared so that the
92 // seat code can find an appropriate new focus.
93 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) {
94 con->workspace->fullscreen = NULL;
95 }
96 if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) {
97 container_fullscreen_disable(con);
98 }
99
100 wl_signal_emit(&con->node.events.destroy, &con->node);
101
102 container_end_mouse_operation(con);
103
104 con->node.destroying = true;
105 node_set_dirty(&con->node);
106
107 if (con->scratchpad) {
108 root_scratchpad_remove_container(con);
109 }
110
111 if (con->fullscreen_mode == FULLSCREEN_GLOBAL) {
112 container_fullscreen_disable(con);
113 }
114
115 if (con->parent || con->workspace) {
116 container_detach(con);
117 }
118 }
119
container_reap_empty(struct sway_container * con)120 void container_reap_empty(struct sway_container *con) {
121 if (con->view) {
122 return;
123 }
124 struct sway_workspace *ws = con->workspace;
125 while (con) {
126 if (con->children->length) {
127 return;
128 }
129 struct sway_container *parent = con->parent;
130 container_begin_destroy(con);
131 con = parent;
132 }
133 if (ws) {
134 workspace_consider_destroy(ws);
135 }
136 }
137
container_flatten(struct sway_container * container)138 struct sway_container *container_flatten(struct sway_container *container) {
139 if (container->view) {
140 return NULL;
141 }
142 while (container && container->children->length == 1) {
143 struct sway_container *child = container->children->items[0];
144 struct sway_container *parent = container->parent;
145 container_replace(container, child);
146 container_begin_destroy(container);
147 container = parent;
148 }
149 return container;
150 }
151
container_find_child(struct sway_container * container,bool (* test)(struct sway_container * con,void * data),void * data)152 struct sway_container *container_find_child(struct sway_container *container,
153 bool (*test)(struct sway_container *con, void *data), void *data) {
154 if (!container->children) {
155 return NULL;
156 }
157 for (int i = 0; i < container->children->length; ++i) {
158 struct sway_container *child = container->children->items[i];
159 if (test(child, data)) {
160 return child;
161 }
162 struct sway_container *res = container_find_child(child, test, data);
163 if (res) {
164 return res;
165 }
166 }
167 return NULL;
168 }
169
surface_at_view(struct sway_container * con,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)170 static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly,
171 struct wlr_surface **surface, double *sx, double *sy) {
172 if (!sway_assert(con->view, "Expected a view")) {
173 return NULL;
174 }
175 struct sway_view *view = con->view;
176 double view_sx = lx - con->surface_x + view->geometry.x;
177 double view_sy = ly - con->surface_y + view->geometry.y;
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 */
container_at_tabbed(struct sway_node * parent,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)206 static 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 */
container_at_stacked(struct sway_node * parent,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)241 static 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 }
261 }
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 }
267
268 /**
269 * container_at for a container with layout L_HORIZ or L_VERT.
270 */
container_at_linear(struct sway_node * parent,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)271 static 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
floating_container_at(double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)286 static struct sway_container *floating_container_at(double lx, double ly,
287 struct wlr_surface **surface, double *sx, double *sy) {
288 // For outputs with floating containers that overhang the output bounds,
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 }
309 }
310 return NULL;
311 }
312
view_container_at(struct sway_node * parent,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)313 struct 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 }
332
333 return NULL;
334 }
335
tiling_container_at(struct sway_node * parent,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)336 struct sway_container *tiling_container_at(struct sway_node *parent,
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
surface_is_popup(struct wlr_surface * surface)359 static 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 }
369 return false;
370 }
371
372 return false;
373 }
374
container_at(struct sway_workspace * workspace,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)375 struct 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;
406 }
407
container_for_each_child(struct sway_container * container,void (* f)(struct sway_container * container,void * data),void * data)408 void 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
container_has_ancestor(struct sway_container * descendant,struct sway_container * ancestor)420 bool container_has_ancestor(struct sway_container *descendant,
421 struct sway_container *ancestor) {
422 while (descendant) {
423 descendant = descendant->parent;
424 if (descendant == ancestor) {
425 return true;
426 }
427 }
428 return false;
429 }
430
container_damage_whole(struct sway_container * container)431 void 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 */
container_get_effective_output(struct sway_container * con)442 struct 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
update_title_texture(struct sway_container * con,struct wlr_texture ** texture,struct border_colors * class)449 static 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
container_update_title_textures(struct sway_container * container)517 void 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
container_calculate_title_height(struct sway_container * container)529 void 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 /**
545 * Calculate and return the length of the tree representation.
546 * An example tree representation is: V[Terminal, Firefox]
547 * If buffer is not NULL, also populate the buffer with the representation.
548 */
container_build_representation(enum sway_container_layout layout,list_t * children,char * buffer)549 size_t container_build_representation(enum sway_container_layout layout,
550 list_t *children, char *buffer) {
551 size_t len = 2;
552 switch (layout) {
553 case L_VERT:
554 lenient_strcat(buffer, "V[");
555 break;
556 case L_HORIZ:
557 lenient_strcat(buffer, "H[");
558 break;
559 case L_TABBED:
560 lenient_strcat(buffer, "T[");
561 break;
562 case L_STACKED:
563 lenient_strcat(buffer, "S[");
564 break;
565 case L_NONE:
566 lenient_strcat(buffer, "D[");
567 break;
568 }
569 for (int i = 0; i < children->length; ++i) {
570 if (i != 0) {
571 ++len;
572 lenient_strcat(buffer, " ");
573 }
574 struct sway_container *child = children->items[i];
575 const char *identifier = NULL;
576 if (child->view) {
577 identifier = view_get_class(child->view);
578 if (!identifier) {
579 identifier = view_get_app_id(child->view);
580 }
581 } else {
582 identifier = child->formatted_title;
583 }
584 if (identifier) {
585 len += strlen(identifier);
586 lenient_strcat(buffer, identifier);
587 } else {
588 len += 6;
589 lenient_strcat(buffer, "(null)");
590 }
591 }
592 ++len;
593 lenient_strcat(buffer, "]");
594 return len;
595 }
596
container_update_representation(struct sway_container * con)597 void container_update_representation(struct sway_container *con) {
598 if (!con->view) {
599 size_t len = container_build_representation(con->layout,
600 con->children, NULL);
601 free(con->formatted_title);
602 con->formatted_title = calloc(len + 1, sizeof(char));
603 if (!sway_assert(con->formatted_title,
604 "Unable to allocate title string")) {
605 return;
606 }
607 container_build_representation(con->layout, con->children,
608 con->formatted_title);
609 container_calculate_title_height(con);
610 container_update_title_textures(con);
611 }
612 if (con->parent) {
613 container_update_representation(con->parent);
614 } else if (con->workspace) {
615 workspace_update_representation(con->workspace);
616 }
617 }
618
container_titlebar_height(void)619 size_t container_titlebar_height(void) {
620 return config->font_height + config->titlebar_v_padding * 2;
621 }
622
floating_calculate_constraints(int * min_width,int * max_width,int * min_height,int * max_height)623 void floating_calculate_constraints(int *min_width, int *max_width,
624 int *min_height, int *max_height) {
625 if (config->floating_minimum_width == -1) { // no minimum
626 *min_width = 0;
627 } else if (config->floating_minimum_width == 0) { // automatic
628 *min_width = 75;
629 } else {
630 *min_width = config->floating_minimum_width;
631 }
632
633 if (config->floating_minimum_height == -1) { // no minimum
634 *min_height = 0;
635 } else if (config->floating_minimum_height == 0) { // automatic
636 *min_height = 50;
637 } else {
638 *min_height = config->floating_minimum_height;
639 }
640
641 struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL);
642
643 if (config->floating_maximum_width == -1) { // no maximum
644 *max_width = INT_MAX;
645 } else if (config->floating_maximum_width == 0) { // automatic
646 *max_width = box->width;
647 } else {
648 *max_width = config->floating_maximum_width;
649 }
650
651 if (config->floating_maximum_height == -1) { // no maximum
652 *max_height = INT_MAX;
653 } else if (config->floating_maximum_height == 0) { // automatic
654 *max_height = box->height;
655 } else {
656 *max_height = config->floating_maximum_height;
657 }
658
659 }
660
floating_natural_resize(struct sway_container * con)661 static void floating_natural_resize(struct sway_container *con) {
662 int min_width, max_width, min_height, max_height;
663 floating_calculate_constraints(&min_width, &max_width,
664 &min_height, &max_height);
665 if (!con->view) {
666 con->width = fmax(min_width, fmin(con->width, max_width));
667 con->height = fmax(min_height, fmin(con->height, max_height));
668 } else {
669 struct sway_view *view = con->view;
670 con->content_width =
671 fmax(min_width, fmin(view->natural_width, max_width));
672 con->content_height =
673 fmax(min_height, fmin(view->natural_height, max_height));
674 container_set_geometry_from_content(con);
675 }
676 }
677
container_floating_resize_and_center(struct sway_container * con)678 void container_floating_resize_and_center(struct sway_container *con) {
679 struct sway_workspace *ws = con->workspace;
680 if (!ws) {
681 // On scratchpad, just resize
682 floating_natural_resize(con);
683 return;
684 }
685
686 struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout,
687 ws->output->wlr_output);
688 if (!ob) {
689 // On NOOP output. Will be called again when moved to an output
690 con->x = 0;
691 con->y = 0;
692 con->width = 0;
693 con->height = 0;
694 return;
695 }
696
697 floating_natural_resize(con);
698 if (!con->view) {
699 if (con->width > ws->width || con->height > ws->height) {
700 con->x = ob->x + (ob->width - con->width) / 2;
701 con->y = ob->y + (ob->height - con->height) / 2;
702 } else {
703 con->x = ws->x + (ws->width - con->width) / 2;
704 con->y = ws->y + (ws->height - con->height) / 2;
705 }
706 } else {
707 if (con->content_width > ws->width
708 || con->content_height > ws->height) {
709 con->content_x = ob->x + (ob->width - con->content_width) / 2;
710 con->content_y = ob->y + (ob->height - con->content_height) / 2;
711 } else {
712 con->content_x = ws->x + (ws->width - con->content_width) / 2;
713 con->content_y = ws->y + (ws->height - con->content_height) / 2;
714 }
715
716 // If the view's border is B_NONE then these properties are ignored.
717 con->border_top = con->border_bottom = true;
718 con->border_left = con->border_right = true;
719
720 container_set_geometry_from_content(con);
721 }
722 }
723
container_floating_set_default_size(struct sway_container * con)724 void container_floating_set_default_size(struct sway_container *con) {
725 if (!sway_assert(con->workspace, "Expected a container on a workspace")) {
726 return;
727 }
728
729 int min_width, max_width, min_height, max_height;
730 floating_calculate_constraints(&min_width, &max_width,
731 &min_height, &max_height);
732 struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
733 workspace_get_box(con->workspace, box);
734
735 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));
737 if (!con->view) {
738 con->width = width;
739 con->height = height;
740 } else {
741 con->content_width = width;
742 con->content_height = height;
743 container_set_geometry_from_content(con);
744 }
745
746 free(box);
747 }
748
container_set_floating(struct sway_container * container,bool enable)749 void container_set_floating(struct sway_container *container, bool enable) {
750 if (container_is_floating(container) == enable) {
751 return;
752 }
753
754 struct sway_seat *seat = input_manager_current_seat();
755 struct sway_workspace *workspace = container->workspace;
756
757 if (enable) {
758 struct sway_container *old_parent = container->parent;
759 container_detach(container);
760 workspace_add_floating(workspace, container);
761 if (container->view) {
762 view_set_tiled(container->view, false);
763 if (container->view->using_csd) {
764 container->border = B_CSD;
765 }
766 }
767 container_floating_set_default_size(container);
768 container_floating_resize_and_center(container);
769 if (old_parent) {
770 container_reap_empty(old_parent);
771 }
772 } else {
773 // Returning to tiled
774 if (container->scratchpad) {
775 root_scratchpad_remove_container(container);
776 }
777 container_detach(container);
778 struct sway_container *reference =
779 seat_get_focus_inactive_tiling(seat, workspace);
780 if (reference) {
781 container_add_sibling(reference, container, 1);
782 container->width = reference->width;
783 container->height = reference->height;
784 } else {
785 workspace_add_tiling(workspace, container);
786 container->width = workspace->width;
787 container->height = workspace->height;
788 }
789 if (container->view) {
790 view_set_tiled(container->view, true);
791 if (container->view->using_csd) {
792 container->border = container->saved_border;
793 }
794 }
795 container->width_fraction = 0;
796 container->height_fraction = 0;
797 }
798
799 container_end_mouse_operation(container);
800
801 ipc_event_window(container, "floating");
802 }
803
container_set_geometry_from_content(struct sway_container * con)804 void container_set_geometry_from_content(struct sway_container *con) {
805 if (!sway_assert(con->view, "Expected a view")) {
806 return;
807 }
808 if (!sway_assert(container_is_floating(con), "Expected a floating view")) {
809 return;
810 }
811 size_t border_width = 0;
812 size_t top = 0;
813
814 if (con->border != B_CSD) {
815 border_width = con->border_thickness * (con->border != B_NONE);
816 top = con->border == B_NORMAL ?
817 container_titlebar_height() : border_width;
818 }
819
820 con->x = con->content_x - border_width;
821 con->y = con->content_y - top;
822 con->width = con->content_width + border_width * 2;
823 con->height = top + con->content_height + border_width;
824 node_set_dirty(&con->node);
825 }
826
container_is_floating(struct sway_container * container)827 bool container_is_floating(struct sway_container *container) {
828 if (!container->parent && container->workspace &&
829 list_find(container->workspace->floating, container) != -1) {
830 return true;
831 }
832 if (container->scratchpad) {
833 return true;
834 }
835 return false;
836 }
837
container_get_box(struct sway_container * container,struct wlr_box * box)838 void container_get_box(struct sway_container *container, struct wlr_box *box) {
839 box->x = container->x;
840 box->y = container->y;
841 box->width = container->width;
842 box->height = container->height;
843 }
844
845 /**
846 * Translate the container's position as well as all children.
847 */
container_floating_translate(struct sway_container * con,double x_amount,double y_amount)848 void container_floating_translate(struct sway_container *con,
849 double x_amount, double y_amount) {
850 con->x += x_amount;
851 con->y += y_amount;
852 con->content_x += x_amount;
853 con->content_y += y_amount;
854
855 if (con->children) {
856 for (int i = 0; i < con->children->length; ++i) {
857 struct sway_container *child = con->children->items[i];
858 container_floating_translate(child, x_amount, y_amount);
859 }
860 }
861
862 node_set_dirty(&con->node);
863 }
864
865 /**
866 * Choose an output for the floating container's new position.
867 *
868 * If the center of the container intersects an output then we'll choose that
869 * one, otherwise we'll choose whichever output is closest to the container's
870 * center.
871 */
container_floating_find_output(struct sway_container * con)872 struct sway_output *container_floating_find_output(struct sway_container *con) {
873 double center_x = con->x + con->width / 2;
874 double center_y = con->y + con->height / 2;
875 struct sway_output *closest_output = NULL;
876 double closest_distance = DBL_MAX;
877 for (int i = 0; i < root->outputs->length; ++i) {
878 struct sway_output *output = root->outputs->items[i];
879 struct wlr_box output_box;
880 double closest_x, closest_y;
881 output_get_box(output, &output_box);
882 wlr_box_closest_point(&output_box, center_x, center_y,
883 &closest_x, &closest_y);
884 if (center_x == closest_x && center_y == closest_y) {
885 // The center of the floating container is on this output
886 return output;
887 }
888 double x_dist = closest_x - center_x;
889 double y_dist = closest_y - center_y;
890 double distance = x_dist * x_dist + y_dist * y_dist;
891 if (distance < closest_distance) {
892 closest_output = output;
893 closest_distance = distance;
894 }
895 }
896 return closest_output;
897 }
898
container_floating_move_to(struct sway_container * con,double lx,double ly)899 void container_floating_move_to(struct sway_container *con,
900 double lx, double ly) {
901 if (!sway_assert(container_is_floating(con),
902 "Expected a floating container")) {
903 return;
904 }
905 container_floating_translate(con, lx - con->x, ly - con->y);
906 if (container_is_scratchpad_hidden(con)) {
907 return;
908 }
909 struct sway_workspace *old_workspace = con->workspace;
910 struct sway_output *new_output = container_floating_find_output(con);
911 if (!sway_assert(new_output, "Unable to find any output")) {
912 return;
913 }
914 struct sway_workspace *new_workspace =
915 output_get_active_workspace(new_output);
916 if (new_workspace && old_workspace != new_workspace) {
917 container_detach(con);
918 workspace_add_floating(new_workspace, con);
919 arrange_workspace(old_workspace);
920 arrange_workspace(new_workspace);
921 workspace_detect_urgent(old_workspace);
922 workspace_detect_urgent(new_workspace);
923 }
924 }
925
container_floating_move_to_center(struct sway_container * con)926 void container_floating_move_to_center(struct sway_container *con) {
927 if (!sway_assert(container_is_floating(con),
928 "Expected a floating container")) {
929 return;
930 }
931 struct sway_workspace *ws = con->workspace;
932 double new_lx = ws->x + (ws->width - con->width) / 2;
933 double new_ly = ws->y + (ws->height - con->height) / 2;
934 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
935 }
936
find_urgent_iterator(struct sway_container * con,void * data)937 static bool find_urgent_iterator(struct sway_container *con, void *data) {
938 return con->view && view_is_urgent(con->view);
939 }
940
container_has_urgent_child(struct sway_container * container)941 bool container_has_urgent_child(struct sway_container *container) {
942 return container_find_child(container, find_urgent_iterator, NULL);
943 }
944
container_end_mouse_operation(struct sway_container * container)945 void container_end_mouse_operation(struct sway_container *container) {
946 struct sway_seat *seat;
947 wl_list_for_each(seat, &server.input->seats, link) {
948 seatop_unref(seat, container);
949 }
950 }
951
set_fullscreen_iterator(struct sway_container * con,void * data)952 static void set_fullscreen_iterator(struct sway_container *con, void *data) {
953 if (!con->view) {
954 return;
955 }
956 if (con->view->impl->set_fullscreen) {
957 bool *enable = data;
958 con->view->impl->set_fullscreen(con->view, *enable);
959 }
960 }
961
container_fullscreen_workspace(struct sway_container * con)962 static void container_fullscreen_workspace(struct sway_container *con) {
963 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE,
964 "Expected a non-fullscreen container")) {
965 return;
966 }
967 bool enable = true;
968 set_fullscreen_iterator(con, &enable);
969 container_for_each_child(con, set_fullscreen_iterator, &enable);
970
971 con->saved_x = con->x;
972 con->saved_y = con->y;
973 con->saved_width = con->width;
974 con->saved_height = con->height;
975
976 if (con->workspace) {
977 con->workspace->fullscreen = con;
978 struct sway_seat *seat;
979 struct sway_workspace *focus_ws;
980 wl_list_for_each(seat, &server.input->seats, link) {
981 focus_ws = seat_get_focused_workspace(seat);
982 if (focus_ws == con->workspace) {
983 seat_set_focus_container(seat, con);
984 } else {
985 struct sway_node *focus =
986 seat_get_focus_inactive(seat, &root->node);
987 seat_set_raw_focus(seat, &con->node);
988 seat_set_raw_focus(seat, focus);
989 }
990 }
991 }
992
993 con->fullscreen_mode = FULLSCREEN_WORKSPACE;
994 container_end_mouse_operation(con);
995 ipc_event_window(con, "fullscreen_mode");
996 }
997
container_fullscreen_global(struct sway_container * con)998 static void container_fullscreen_global(struct sway_container *con) {
999 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE,
1000 "Expected a non-fullscreen container")) {
1001 return;
1002 }
1003 bool enable = true;
1004 set_fullscreen_iterator(con, &enable);
1005 container_for_each_child(con, set_fullscreen_iterator, &enable);
1006
1007 root->fullscreen_global = con;
1008 con->saved_x = con->x;
1009 con->saved_y = con->y;
1010 con->saved_width = con->width;
1011 con->saved_height = con->height;
1012
1013 struct sway_seat *seat;
1014 wl_list_for_each(seat, &server.input->seats, link) {
1015 struct sway_container *focus = seat_get_focused_container(seat);
1016 if (focus && focus != con) {
1017 seat_set_focus_container(seat, con);
1018 }
1019 }
1020
1021 con->fullscreen_mode = FULLSCREEN_GLOBAL;
1022 container_end_mouse_operation(con);
1023 ipc_event_window(con, "fullscreen_mode");
1024 }
1025
container_fullscreen_disable(struct sway_container * con)1026 void container_fullscreen_disable(struct sway_container *con) {
1027 if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE,
1028 "Expected a fullscreen container")) {
1029 return;
1030 }
1031 bool enable = false;
1032 set_fullscreen_iterator(con, &enable);
1033 container_for_each_child(con, set_fullscreen_iterator, &enable);
1034
1035 if (container_is_floating(con)) {
1036 con->x = con->saved_x;
1037 con->y = con->saved_y;
1038 con->width = con->saved_width;
1039 con->height = con->saved_height;
1040 }
1041
1042 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) {
1043 if (con->workspace) {
1044 con->workspace->fullscreen = NULL;
1045 if (container_is_floating(con)) {
1046 struct sway_output *output =
1047 container_floating_find_output(con);
1048 if (con->workspace->output != output) {
1049 container_floating_move_to_center(con);
1050 }
1051 }
1052 }
1053 } else {
1054 root->fullscreen_global = NULL;
1055 }
1056
1057 // If the container was mapped as fullscreen and set as floating by
1058 // criteria, it needs to be reinitialized as floating to get the proper
1059 // size and location
1060 if (container_is_floating(con) && (con->width == 0 || con->height == 0)) {
1061 container_floating_resize_and_center(con);
1062 }
1063
1064 con->fullscreen_mode = FULLSCREEN_NONE;
1065 container_end_mouse_operation(con);
1066 ipc_event_window(con, "fullscreen_mode");
1067
1068 if (con->scratchpad) {
1069 struct sway_seat *seat;
1070 wl_list_for_each(seat, &server.input->seats, link) {
1071 struct sway_container *focus = seat_get_focused_container(seat);
1072 if (focus == con || container_has_ancestor(focus, con)) {
1073 seat_set_focus(seat,
1074 seat_get_focus_inactive(seat, &root->node));
1075 }
1076 }
1077 }
1078 }
1079
container_set_fullscreen(struct sway_container * con,enum sway_fullscreen_mode mode)1080 void container_set_fullscreen(struct sway_container *con,
1081 enum sway_fullscreen_mode mode) {
1082 if (con->fullscreen_mode == mode) {
1083 return;
1084 }
1085
1086 switch (mode) {
1087 case FULLSCREEN_NONE:
1088 container_fullscreen_disable(con);
1089 break;
1090 case FULLSCREEN_WORKSPACE:
1091 if (root->fullscreen_global) {
1092 container_fullscreen_disable(root->fullscreen_global);
1093 }
1094 if (con->workspace && con->workspace->fullscreen) {
1095 container_fullscreen_disable(con->workspace->fullscreen);
1096 }
1097 container_fullscreen_workspace(con);
1098 break;
1099 case FULLSCREEN_GLOBAL:
1100 if (root->fullscreen_global) {
1101 container_fullscreen_disable(root->fullscreen_global);
1102 }
1103 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) {
1104 container_fullscreen_disable(con);
1105 }
1106 container_fullscreen_global(con);
1107 break;
1108 }
1109 }
1110
container_toplevel_ancestor(struct sway_container * container)1111 struct sway_container *container_toplevel_ancestor(
1112 struct sway_container *container) {
1113 while (container->parent) {
1114 container = container->parent;
1115 }
1116
1117 return container;
1118 }
1119
container_is_floating_or_child(struct sway_container * container)1120 bool container_is_floating_or_child(struct sway_container *container) {
1121 return container_is_floating(container_toplevel_ancestor(container));
1122 }
1123
container_is_fullscreen_or_child(struct sway_container * container)1124 bool container_is_fullscreen_or_child(struct sway_container *container) {
1125 do {
1126 if (container->fullscreen_mode) {
1127 return true;
1128 }
1129 container = container->parent;
1130 } while (container);
1131
1132 return false;
1133 }
1134
surface_send_enter_iterator(struct wlr_surface * surface,int x,int y,void * data)1135 static void surface_send_enter_iterator(struct wlr_surface *surface,
1136 int x, int y, void *data) {
1137 struct wlr_output *wlr_output = data;
1138 wlr_surface_send_enter(surface, wlr_output);
1139 }
1140
surface_send_leave_iterator(struct wlr_surface * surface,int x,int y,void * data)1141 static void surface_send_leave_iterator(struct wlr_surface *surface,
1142 int x, int y, void *data) {
1143 struct wlr_output *wlr_output = data;
1144 wlr_surface_send_leave(surface, wlr_output);
1145 }
1146
container_discover_outputs(struct sway_container * con)1147 void container_discover_outputs(struct sway_container *con) {
1148 struct wlr_box con_box = {
1149 .x = con->current.x,
1150 .y = con->current.y,
1151 .width = con->current.width,
1152 .height = con->current.height,
1153 };
1154 struct sway_output *old_output = container_get_effective_output(con);
1155
1156 for (int i = 0; i < root->outputs->length; ++i) {
1157 struct sway_output *output = root->outputs->items[i];
1158 struct wlr_box output_box;
1159 output_get_box(output, &output_box);
1160 struct wlr_box intersection;
1161 bool intersects =
1162 wlr_box_intersection(&intersection, &con_box, &output_box);
1163 int index = list_find(con->outputs, output);
1164
1165 if (intersects && index == -1) {
1166 // Send enter
1167 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1168 if (con->view) {
1169 view_for_each_surface(con->view,
1170 surface_send_enter_iterator, output->wlr_output);
1171 if (con->view->foreign_toplevel) {
1172 wlr_foreign_toplevel_handle_v1_output_enter(
1173 con->view->foreign_toplevel, output->wlr_output);
1174 }
1175 }
1176 list_add(con->outputs, output);
1177 } else if (!intersects && index != -1) {
1178 // Send leave
1179 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1180 if (con->view) {
1181 view_for_each_surface(con->view,
1182 surface_send_leave_iterator, output->wlr_output);
1183 if (con->view->foreign_toplevel) {
1184 wlr_foreign_toplevel_handle_v1_output_leave(
1185 con->view->foreign_toplevel, output->wlr_output);
1186 }
1187 }
1188 list_del(con->outputs, index);
1189 }
1190 }
1191 struct sway_output *new_output = container_get_effective_output(con);
1192 double old_scale = old_output && old_output->enabled ?
1193 old_output->wlr_output->scale : -1;
1194 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1195 if (old_scale != new_scale) {
1196 container_update_title_textures(con);
1197 container_update_marks_textures(con);
1198 }
1199 }
1200
container_parent_layout(struct sway_container * con)1201 enum sway_container_layout container_parent_layout(struct sway_container *con) {
1202 if (con->parent) {
1203 return con->parent->layout;
1204 }
1205 if (con->workspace) {
1206 return con->workspace->layout;
1207 }
1208 return L_NONE;
1209 }
1210
container_current_parent_layout(struct sway_container * con)1211 enum sway_container_layout container_current_parent_layout(
1212 struct sway_container *con) {
1213 if (con->current.parent) {
1214 return con->current.parent->current.layout;
1215 }
1216 return con->current.workspace->current.layout;
1217 }
1218
container_get_siblings(struct sway_container * container)1219 list_t *container_get_siblings(struct sway_container *container) {
1220 if (container->parent) {
1221 return container->parent->children;
1222 }
1223 if (container_is_scratchpad_hidden(container)) {
1224 return NULL;
1225 }
1226 if (list_find(container->workspace->tiling, container) != -1) {
1227 return container->workspace->tiling;
1228 }
1229 return container->workspace->floating;
1230 }
1231
container_sibling_index(struct sway_container * child)1232 int container_sibling_index(struct sway_container *child) {
1233 return list_find(container_get_siblings(child), child);
1234 }
1235
container_get_current_siblings(struct sway_container * container)1236 list_t *container_get_current_siblings(struct sway_container *container) {
1237 if (container->current.parent) {
1238 return container->current.parent->current.children;
1239 }
1240 return container->current.workspace->current.tiling;
1241 }
1242
container_handle_fullscreen_reparent(struct sway_container * con)1243 void container_handle_fullscreen_reparent(struct sway_container *con) {
1244 if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace ||
1245 con->workspace->fullscreen == con) {
1246 return;
1247 }
1248 if (con->workspace->fullscreen) {
1249 container_fullscreen_disable(con->workspace->fullscreen);
1250 }
1251 con->workspace->fullscreen = con;
1252
1253 arrange_workspace(con->workspace);
1254 }
1255
set_workspace(struct sway_container * container,void * data)1256 static void set_workspace(struct sway_container *container, void *data) {
1257 container->workspace = container->parent->workspace;
1258 }
1259
container_insert_child(struct sway_container * parent,struct sway_container * child,int i)1260 void container_insert_child(struct sway_container *parent,
1261 struct sway_container *child, int i) {
1262 if (child->workspace) {
1263 container_detach(child);
1264 }
1265 list_insert(parent->children, i, child);
1266 child->parent = parent;
1267 child->workspace = parent->workspace;
1268 container_for_each_child(child, set_workspace, NULL);
1269 container_handle_fullscreen_reparent(child);
1270 container_update_representation(parent);
1271 }
1272
container_add_sibling(struct sway_container * fixed,struct sway_container * active,bool after)1273 void container_add_sibling(struct sway_container *fixed,
1274 struct sway_container *active, bool after) {
1275 if (active->workspace) {
1276 container_detach(active);
1277 }
1278 list_t *siblings = container_get_siblings(fixed);
1279 int index = list_find(siblings, fixed);
1280 list_insert(siblings, index + after, active);
1281 active->parent = fixed->parent;
1282 active->workspace = fixed->workspace;
1283 container_for_each_child(active, set_workspace, NULL);
1284 container_handle_fullscreen_reparent(active);
1285 container_update_representation(active);
1286 }
1287
container_add_child(struct sway_container * parent,struct sway_container * child)1288 void container_add_child(struct sway_container *parent,
1289 struct sway_container *child) {
1290 if (child->workspace) {
1291 container_detach(child);
1292 }
1293 list_add(parent->children, child);
1294 child->parent = parent;
1295 child->workspace = parent->workspace;
1296 container_for_each_child(child, set_workspace, NULL);
1297 bool fullscreen = child->fullscreen_mode != FULLSCREEN_NONE ||
1298 parent->fullscreen_mode != FULLSCREEN_NONE;
1299 set_fullscreen_iterator(child, &fullscreen);
1300 container_for_each_child(child, set_fullscreen_iterator, &fullscreen);
1301 container_handle_fullscreen_reparent(child);
1302 container_update_representation(parent);
1303 node_set_dirty(&child->node);
1304 node_set_dirty(&parent->node);
1305 }
1306
container_detach(struct sway_container * child)1307 void container_detach(struct sway_container *child) {
1308 if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) {
1309 child->workspace->fullscreen = NULL;
1310 }
1311 if (child->fullscreen_mode == FULLSCREEN_GLOBAL) {
1312 root->fullscreen_global = NULL;
1313 }
1314
1315 struct sway_container *old_parent = child->parent;
1316 struct sway_workspace *old_workspace = child->workspace;
1317 list_t *siblings = container_get_siblings(child);
1318 if (siblings) {
1319 int index = list_find(siblings, child);
1320 if (index != -1) {
1321 list_del(siblings, index);
1322 }
1323 }
1324 child->parent = NULL;
1325 child->workspace = NULL;
1326 container_for_each_child(child, set_workspace, NULL);
1327
1328 if (old_parent) {
1329 container_update_representation(old_parent);
1330 node_set_dirty(&old_parent->node);
1331 } else if (old_workspace) {
1332 // We may have removed the last tiling child from the workspace. If the
1333 // workspace layout was e.g. tabbed, then at this point it may be just
1334 // H[]. So, reset it to the default (e.g. T[]) for next time.
1335 if (!old_workspace->tiling->length) {
1336 old_workspace->layout =
1337 output_get_default_layout(old_workspace->output);
1338 }
1339
1340 workspace_update_representation(old_workspace);
1341 node_set_dirty(&old_workspace->node);
1342 }
1343 node_set_dirty(&child->node);
1344 }
1345
container_replace(struct sway_container * container,struct sway_container * replacement)1346 void container_replace(struct sway_container *container,
1347 struct sway_container *replacement) {
1348 enum sway_fullscreen_mode fullscreen = container->fullscreen_mode;
1349 bool scratchpad = container->scratchpad;
1350 struct sway_workspace *ws = NULL;
1351 if (fullscreen != FULLSCREEN_NONE) {
1352 container_fullscreen_disable(container);
1353 }
1354 if (scratchpad) {
1355 ws = container->workspace;
1356 root_scratchpad_show(container);
1357 root_scratchpad_remove_container(container);
1358 }
1359 if (container->parent || container->workspace) {
1360 float width_fraction = container->width_fraction;
1361 float height_fraction = container->height_fraction;
1362 container_add_sibling(container, replacement, 1);
1363 container_detach(container);
1364 replacement->width_fraction = width_fraction;
1365 replacement->height_fraction = height_fraction;
1366 }
1367 if (scratchpad) {
1368 root_scratchpad_add_container(replacement, ws);
1369 }
1370 switch (fullscreen) {
1371 case FULLSCREEN_WORKSPACE:
1372 container_fullscreen_workspace(replacement);
1373 break;
1374 case FULLSCREEN_GLOBAL:
1375 container_fullscreen_global(replacement);
1376 break;
1377 case FULLSCREEN_NONE:
1378 // noop
1379 break;
1380 }
1381 }
1382
container_split(struct sway_container * child,enum sway_container_layout layout)1383 struct sway_container *container_split(struct sway_container *child,
1384 enum sway_container_layout layout) {
1385 struct sway_seat *seat = input_manager_get_default_seat();
1386 bool set_focus = (seat_get_focus(seat) == &child->node);
1387
1388 struct sway_container *cont = container_create(NULL);
1389 cont->width = child->width;
1390 cont->height = child->height;
1391 cont->width_fraction = child->width_fraction;
1392 cont->height_fraction = child->height_fraction;
1393 cont->x = child->x;
1394 cont->y = child->y;
1395 cont->layout = layout;
1396
1397 container_replace(child, cont);
1398 container_add_child(cont, child);
1399
1400 if (set_focus) {
1401 seat_set_raw_focus(seat, &cont->node);
1402 if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) {
1403 seat_set_focus(seat, &child->node);
1404 } else {
1405 seat_set_raw_focus(seat, &child->node);
1406 }
1407 }
1408
1409 return cont;
1410 }
1411
container_is_transient_for(struct sway_container * child,struct sway_container * ancestor)1412 bool container_is_transient_for(struct sway_container *child,
1413 struct sway_container *ancestor) {
1414 return config->popup_during_fullscreen == POPUP_SMART &&
1415 child->view && ancestor->view &&
1416 view_is_transient_for(child->view, ancestor->view);
1417 }
1418
find_by_mark_iterator(struct sway_container * con,void * data)1419 static bool find_by_mark_iterator(struct sway_container *con, void *data) {
1420 char *mark = data;
1421 return container_has_mark(con, mark);
1422 }
1423
container_find_mark(char * mark)1424 struct sway_container *container_find_mark(char *mark) {
1425 return root_find_container(find_by_mark_iterator, mark);
1426 }
1427
container_find_and_unmark(char * mark)1428 bool container_find_and_unmark(char *mark) {
1429 struct sway_container *con = root_find_container(
1430 find_by_mark_iterator, mark);
1431 if (!con) {
1432 return false;
1433 }
1434
1435 for (int i = 0; i < con->marks->length; ++i) {
1436 char *con_mark = con->marks->items[i];
1437 if (strcmp(con_mark, mark) == 0) {
1438 free(con_mark);
1439 list_del(con->marks, i);
1440 container_update_marks_textures(con);
1441 ipc_event_window(con, "mark");
1442 return true;
1443 }
1444 }
1445 return false;
1446 }
1447
container_clear_marks(struct sway_container * con)1448 void container_clear_marks(struct sway_container *con) {
1449 for (int i = 0; i < con->marks->length; ++i) {
1450 free(con->marks->items[i]);
1451 }
1452 con->marks->length = 0;
1453 ipc_event_window(con, "mark");
1454 }
1455
container_has_mark(struct sway_container * con,char * mark)1456 bool container_has_mark(struct sway_container *con, char *mark) {
1457 for (int i = 0; i < con->marks->length; ++i) {
1458 char *item = con->marks->items[i];
1459 if (strcmp(item, mark) == 0) {
1460 return true;
1461 }
1462 }
1463 return false;
1464 }
1465
container_add_mark(struct sway_container * con,char * mark)1466 void container_add_mark(struct sway_container *con, char *mark) {
1467 list_add(con->marks, strdup(mark));
1468 ipc_event_window(con, "mark");
1469 }
1470
update_marks_texture(struct sway_container * con,struct wlr_texture ** texture,struct border_colors * class)1471 static void update_marks_texture(struct sway_container *con,
1472 struct wlr_texture **texture, struct border_colors *class) {
1473 struct sway_output *output = container_get_effective_output(con);
1474 if (!output) {
1475 return;
1476 }
1477 if (*texture) {
1478 wlr_texture_destroy(*texture);
1479 *texture = NULL;
1480 }
1481 if (!con->marks->length) {
1482 return;
1483 }
1484
1485 size_t len = 0;
1486 for (int i = 0; i < con->marks->length; ++i) {
1487 char *mark = con->marks->items[i];
1488 if (mark[0] != '_') {
1489 len += strlen(mark) + 2;
1490 }
1491 }
1492 char *buffer = calloc(len + 1, 1);
1493 char *part = malloc(len + 1);
1494
1495 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1496 free(buffer);
1497 return;
1498 }
1499
1500 for (int i = 0; i < con->marks->length; ++i) {
1501 char *mark = con->marks->items[i];
1502 if (mark[0] != '_') {
1503 sprintf(part, "[%s]", mark);
1504 strcat(buffer, part);
1505 }
1506 }
1507 free(part);
1508
1509 double scale = output->wlr_output->scale;
1510 int width = 0;
1511 int height = con->title_height * scale;
1512
1513 cairo_t *c = cairo_create(NULL);
1514 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1515 "%s", buffer);
1516 cairo_destroy(c);
1517
1518 cairo_surface_t *surface = cairo_image_surface_create(
1519 CAIRO_FORMAT_ARGB32, width, height);
1520 cairo_t *cairo = cairo_create(surface);
1521 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1522 class->background[2], class->background[3]);
1523 cairo_paint(cairo);
1524 PangoContext *pango = pango_cairo_create_context(cairo);
1525 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1526 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1527 class->text[2], class->text[3]);
1528 cairo_move_to(cairo, 0, 0);
1529
1530 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1531
1532 cairo_surface_flush(surface);
1533 unsigned char *data = cairo_image_surface_get_data(surface);
1534 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
1535 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1536 output->wlr_output->backend);
1537 *texture = wlr_texture_from_pixels(
1538 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
1539 cairo_surface_destroy(surface);
1540 g_object_unref(pango);
1541 cairo_destroy(cairo);
1542 free(buffer);
1543 }
1544
container_update_marks_textures(struct sway_container * con)1545 void container_update_marks_textures(struct sway_container *con) {
1546 if (!config->show_marks) {
1547 return;
1548 }
1549 update_marks_texture(con, &con->marks_focused,
1550 &config->border_colors.focused);
1551 update_marks_texture(con, &con->marks_focused_inactive,
1552 &config->border_colors.focused_inactive);
1553 update_marks_texture(con, &con->marks_unfocused,
1554 &config->border_colors.unfocused);
1555 update_marks_texture(con, &con->marks_urgent,
1556 &config->border_colors.urgent);
1557 container_damage_whole(con);
1558 }
1559
container_raise_floating(struct sway_container * con)1560 void container_raise_floating(struct sway_container *con) {
1561 // Bring container to front by putting it at the end of the floating list.
1562 struct sway_container *floater = container_toplevel_ancestor(con);
1563 if (container_is_floating(floater) && floater->workspace) {
1564 list_move_to_end(floater->workspace->floating, floater);
1565 node_set_dirty(&floater->workspace->node);
1566 }
1567 }
1568
container_is_scratchpad_hidden(struct sway_container * con)1569 bool container_is_scratchpad_hidden(struct sway_container *con) {
1570 return con->scratchpad && !con->workspace;
1571 }
1572
container_is_scratchpad_hidden_or_child(struct sway_container * con)1573 bool container_is_scratchpad_hidden_or_child(struct sway_container *con) {
1574 con = container_toplevel_ancestor(con);
1575 return con->scratchpad && !con->workspace;
1576 }
1577