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