1 #define _POSIX_C_SOURCE 200809L
2 #include <stdlib.h>
3 #include <strings.h>
4 #include <wayland-server-core.h>
5 #include <wlr/render/wlr_renderer.h>
6 #include <wlr/types/wlr_buffer.h>
7 #include <wlr/types/wlr_output_layout.h>
8 #include <wlr/types/wlr_server_decoration.h>
9 #include <wlr/types/wlr_xdg_decoration_v1.h>
10 #include "config.h"
11 #if HAVE_XWAYLAND
12 #include <wlr/xwayland.h>
13 #endif
14 #include "list.h"
15 #include "log.h"
16 #include "sway/criteria.h"
17 #include "sway/commands.h"
18 #include "sway/desktop.h"
19 #include "sway/desktop/transaction.h"
20 #include "sway/desktop/idle_inhibit_v1.h"
21 #include "sway/input/cursor.h"
22 #include "sway/ipc-server.h"
23 #include "sway/output.h"
24 #include "sway/input/seat.h"
25 #include "sway/server.h"
26 #include "sway/tree/arrange.h"
27 #include "sway/tree/container.h"
28 #include "sway/tree/view.h"
29 #include "sway/tree/workspace.h"
30 #include "sway/config.h"
31 #include "sway/xdg_decoration.h"
32 #include "pango.h"
33 #include "stringop.h"
34 
view_init(struct sway_view * view,enum sway_view_type type,const struct sway_view_impl * impl)35 void view_init(struct sway_view *view, enum sway_view_type type,
36 		const struct sway_view_impl *impl) {
37 	view->type = type;
38 	view->impl = impl;
39 	view->executed_criteria = create_list();
40 	wl_list_init(&view->saved_buffers);
41 	view->allow_request_urgent = true;
42 	view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
43 	wl_signal_init(&view->events.unmap);
44 }
45 
view_destroy(struct sway_view * view)46 void view_destroy(struct sway_view *view) {
47 	if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) {
48 		return;
49 	}
50 	if (!sway_assert(view->destroying,
51 				"Tried to free view which wasn't marked as destroying")) {
52 		return;
53 	}
54 	if (!sway_assert(view->container == NULL,
55 				"Tried to free view which still has a container "
56 				"(might have a pending transaction?)")) {
57 		return;
58 	}
59 	if (!wl_list_empty(&view->saved_buffers)) {
60 		view_remove_saved_buffer(view);
61 	}
62 	list_free(view->executed_criteria);
63 
64 	free(view->title_format);
65 
66 	if (view->impl->destroy) {
67 		view->impl->destroy(view);
68 	} else {
69 		free(view);
70 	}
71 }
72 
view_begin_destroy(struct sway_view * view)73 void view_begin_destroy(struct sway_view *view) {
74 	if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
75 		return;
76 	}
77 	view->destroying = true;
78 
79 	if (!view->container) {
80 		view_destroy(view);
81 	}
82 }
83 
view_get_title(struct sway_view * view)84 const char *view_get_title(struct sway_view *view) {
85 	if (view->impl->get_string_prop) {
86 		return view->impl->get_string_prop(view, VIEW_PROP_TITLE);
87 	}
88 	return NULL;
89 }
90 
view_get_app_id(struct sway_view * view)91 const char *view_get_app_id(struct sway_view *view) {
92 	if (view->impl->get_string_prop) {
93 		return view->impl->get_string_prop(view, VIEW_PROP_APP_ID);
94 	}
95 	return NULL;
96 }
97 
view_get_class(struct sway_view * view)98 const char *view_get_class(struct sway_view *view) {
99 	if (view->impl->get_string_prop) {
100 		return view->impl->get_string_prop(view, VIEW_PROP_CLASS);
101 	}
102 	return NULL;
103 }
104 
view_get_instance(struct sway_view * view)105 const char *view_get_instance(struct sway_view *view) {
106 	if (view->impl->get_string_prop) {
107 		return view->impl->get_string_prop(view, VIEW_PROP_INSTANCE);
108 	}
109 	return NULL;
110 }
111 #if HAVE_XWAYLAND
view_get_x11_window_id(struct sway_view * view)112 uint32_t view_get_x11_window_id(struct sway_view *view) {
113 	if (view->impl->get_int_prop) {
114 		return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);
115 	}
116 	return 0;
117 }
118 
view_get_x11_parent_id(struct sway_view * view)119 uint32_t view_get_x11_parent_id(struct sway_view *view) {
120 	if (view->impl->get_int_prop) {
121 		return view->impl->get_int_prop(view, VIEW_PROP_X11_PARENT_ID);
122 	}
123 	return 0;
124 }
125 #endif
view_get_window_role(struct sway_view * view)126 const char *view_get_window_role(struct sway_view *view) {
127 	if (view->impl->get_string_prop) {
128 		return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE);
129 	}
130 	return NULL;
131 }
132 
view_get_window_type(struct sway_view * view)133 uint32_t view_get_window_type(struct sway_view *view) {
134 	if (view->impl->get_int_prop) {
135 		return view->impl->get_int_prop(view, VIEW_PROP_WINDOW_TYPE);
136 	}
137 	return 0;
138 }
139 
view_get_shell(struct sway_view * view)140 const char *view_get_shell(struct sway_view *view) {
141 	switch(view->type) {
142 	case SWAY_VIEW_XDG_SHELL:
143 		return "xdg_shell";
144 #if HAVE_XWAYLAND
145 	case SWAY_VIEW_XWAYLAND:
146 		return "xwayland";
147 #endif
148 	}
149 	return "unknown";
150 }
151 
view_get_constraints(struct sway_view * view,double * min_width,double * max_width,double * min_height,double * max_height)152 void view_get_constraints(struct sway_view *view, double *min_width,
153 		double *max_width, double *min_height, double *max_height) {
154 	if (view->impl->get_constraints) {
155 		view->impl->get_constraints(view,
156 				min_width, max_width, min_height, max_height);
157 	} else {
158 		*min_width = DBL_MIN;
159 		*max_width = DBL_MAX;
160 		*min_height = DBL_MIN;
161 		*max_height = DBL_MAX;
162 	}
163 }
164 
view_configure(struct sway_view * view,double lx,double ly,int width,int height)165 uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
166 		int height) {
167 	if (view->impl->configure) {
168 		return view->impl->configure(view, lx, ly, width, height);
169 	}
170 	return 0;
171 }
172 
view_inhibit_idle(struct sway_view * view)173 bool view_inhibit_idle(struct sway_view *view) {
174 	struct sway_idle_inhibitor_v1 *user_inhibitor =
175 		sway_idle_inhibit_v1_user_inhibitor_for_view(view);
176 
177 	struct sway_idle_inhibitor_v1 *application_inhibitor =
178 		sway_idle_inhibit_v1_application_inhibitor_for_view(view);
179 
180 	if (!user_inhibitor && !application_inhibitor) {
181 		return false;
182 	}
183 
184 	if (!user_inhibitor) {
185 		return sway_idle_inhibit_v1_is_active(application_inhibitor);
186 	}
187 
188 	if (!application_inhibitor) {
189 		return sway_idle_inhibit_v1_is_active(user_inhibitor);
190 	}
191 
192 	return sway_idle_inhibit_v1_is_active(user_inhibitor)
193 		|| sway_idle_inhibit_v1_is_active(application_inhibitor);
194 }
195 
view_ancestor_is_only_visible(struct sway_view * view)196 bool view_ancestor_is_only_visible(struct sway_view *view) {
197 	bool only_visible = true;
198 	struct sway_container *con = view->container;
199 	while (con) {
200 		enum sway_container_layout layout = container_parent_layout(con);
201 		if (layout != L_TABBED && layout != L_STACKED) {
202 			list_t *siblings = container_get_siblings(con);
203 			if (siblings && siblings->length > 1) {
204 				only_visible = false;
205 			}
206 		} else {
207 			only_visible = true;
208 		}
209 		con = con->parent;
210 	}
211 	return only_visible;
212 }
213 
view_is_only_visible(struct sway_view * view)214 static bool view_is_only_visible(struct sway_view *view) {
215 	struct sway_container *con = view->container;
216 	while (con) {
217 		enum sway_container_layout layout = container_parent_layout(con);
218 		if (layout != L_TABBED && layout != L_STACKED) {
219 			list_t *siblings = container_get_siblings(con);
220 			if (siblings && siblings->length > 1) {
221 				return false;
222 			}
223 		}
224 
225 		con = con->parent;
226 	}
227 
228 	return true;
229 }
230 
gaps_to_edge(struct sway_view * view)231 static bool gaps_to_edge(struct sway_view *view) {
232 	struct side_gaps gaps = view->container->workspace->current_gaps;
233 	return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0;
234 }
235 
view_autoconfigure(struct sway_view * view)236 void view_autoconfigure(struct sway_view *view) {
237 	struct sway_container *con = view->container;
238 	struct sway_workspace *ws = con->workspace;
239 
240 	if (container_is_scratchpad_hidden(con) &&
241 			con->fullscreen_mode != FULLSCREEN_GLOBAL) {
242 		return;
243 	}
244 	struct sway_output *output = ws ? ws->output : NULL;
245 
246 	if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) {
247 		con->content_x = output->lx;
248 		con->content_y = output->ly;
249 		con->content_width = output->width;
250 		con->content_height = output->height;
251 		return;
252 	} else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) {
253 		con->content_x = root->x;
254 		con->content_y = root->y;
255 		con->content_width = root->width;
256 		con->content_height = root->height;
257 		return;
258 	}
259 
260 	con->border_top = con->border_bottom = true;
261 	con->border_left = con->border_right = true;
262 	double y_offset = 0;
263 
264 	if (!container_is_floating(con) && ws) {
265 		if (config->hide_edge_borders == E_BOTH
266 				|| config->hide_edge_borders == E_VERTICAL) {
267 			con->border_left = con->x != ws->x;
268 			int right_x = con->x + con->width;
269 			con->border_right = right_x != ws->x + ws->width;
270 		}
271 
272 		if (config->hide_edge_borders == E_BOTH
273 				|| config->hide_edge_borders == E_HORIZONTAL) {
274 			con->border_top = con->y != ws->y;
275 			int bottom_y = con->y + con->height;
276 			con->border_bottom = bottom_y != ws->y + ws->height;
277 		}
278 
279 		bool smart = config->hide_edge_borders_smart == ESMART_ON ||
280 			(config->hide_edge_borders_smart == ESMART_NO_GAPS &&
281 			!gaps_to_edge(view));
282 		if (smart) {
283 			bool show_border = !view_is_only_visible(view);
284 			con->border_left &= show_border;
285 			con->border_right &= show_border;
286 			con->border_top &= show_border;
287 			con->border_bottom &= show_border;
288 		}
289 
290 		// In a tabbed or stacked container, the container's y is the top of the
291 		// title area. We have to offset the surface y by the height of the title,
292 		// bar, and disable any top border because we'll always have the title bar.
293 		list_t *siblings = container_get_siblings(con);
294 		bool show_titlebar = (siblings && siblings->length > 1)
295 			|| !config->hide_lone_tab;
296 		if (show_titlebar) {
297 			enum sway_container_layout layout = container_parent_layout(con);
298 			if (layout == L_TABBED) {
299 				y_offset = container_titlebar_height();
300 				con->border_top = false;
301 			} else if (layout == L_STACKED) {
302 				y_offset = container_titlebar_height() * siblings->length;
303 				con->border_top = false;
304 			}
305 		}
306 	}
307 
308 	double x, y, width, height;
309 	switch (con->border) {
310 	default:
311 	case B_CSD:
312 	case B_NONE:
313 		x = con->x;
314 		y = con->y + y_offset;
315 		width = con->width;
316 		height = con->height - y_offset;
317 		break;
318 	case B_PIXEL:
319 		x = con->x + con->border_thickness * con->border_left;
320 		y = con->y + con->border_thickness * con->border_top + y_offset;
321 		width = con->width
322 			- con->border_thickness * con->border_left
323 			- con->border_thickness * con->border_right;
324 		height = con->height - y_offset
325 			- con->border_thickness * con->border_top
326 			- con->border_thickness * con->border_bottom;
327 		break;
328 	case B_NORMAL:
329 		// Height is: 1px border + 3px pad + title height + 3px pad + 1px border
330 		x = con->x + con->border_thickness * con->border_left;
331 		width = con->width
332 			- con->border_thickness * con->border_left
333 			- con->border_thickness * con->border_right;
334 		if (y_offset) {
335 			y = con->y + y_offset;
336 			height = con->height - y_offset
337 				- con->border_thickness * con->border_bottom;
338 		} else {
339 			y = con->y + container_titlebar_height();
340 			height = con->height - container_titlebar_height()
341 				- con->border_thickness * con->border_bottom;
342 		}
343 		break;
344 	}
345 
346 	con->content_x = x;
347 	con->content_y = y;
348 	con->content_width = width;
349 	con->content_height = height;
350 }
351 
view_set_activated(struct sway_view * view,bool activated)352 void view_set_activated(struct sway_view *view, bool activated) {
353 	if (view->impl->set_activated) {
354 		view->impl->set_activated(view, activated);
355 	}
356 	if (view->foreign_toplevel) {
357 		wlr_foreign_toplevel_handle_v1_set_activated(
358 				view->foreign_toplevel, activated);
359 	}
360 }
361 
view_request_activate(struct sway_view * view)362 void view_request_activate(struct sway_view *view) {
363 	struct sway_workspace *ws = view->container->workspace;
364 	if (!ws) { // hidden scratchpad container
365 		return;
366 	}
367 	struct sway_seat *seat = input_manager_current_seat();
368 
369 	switch (config->focus_on_window_activation) {
370 	case FOWA_SMART:
371 		if (workspace_is_visible(ws)) {
372 			seat_set_focus_container(seat, view->container);
373 		} else {
374 			view_set_urgent(view, true);
375 		}
376 		break;
377 	case FOWA_URGENT:
378 		view_set_urgent(view, true);
379 		break;
380 	case FOWA_FOCUS:
381 		seat_set_focus_container(seat, view->container);
382 		break;
383 	case FOWA_NONE:
384 		break;
385 	}
386 }
387 
view_set_csd_from_server(struct sway_view * view,bool enabled)388 void view_set_csd_from_server(struct sway_view *view, bool enabled) {
389 	sway_log(SWAY_DEBUG, "Telling view %p to set CSD to %i", view, enabled);
390 	if (view->xdg_decoration) {
391 		uint32_t mode = enabled ?
392 			WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
393 			WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
394 		wlr_xdg_toplevel_decoration_v1_set_mode(
395 				view->xdg_decoration->wlr_xdg_decoration, mode);
396 	}
397 	view->using_csd = enabled;
398 }
399 
view_update_csd_from_client(struct sway_view * view,bool enabled)400 void view_update_csd_from_client(struct sway_view *view, bool enabled) {
401 	sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled);
402 	struct sway_container *con = view->container;
403 	if (enabled && con && con->border != B_CSD) {
404 		con->saved_border = con->border;
405 		if (container_is_floating(con)) {
406 			con->border = B_CSD;
407 		}
408 	} else if (!enabled && con && con->border == B_CSD) {
409 		con->border = con->saved_border;
410 	}
411 	view->using_csd = enabled;
412 }
413 
view_set_tiled(struct sway_view * view,bool tiled)414 void view_set_tiled(struct sway_view *view, bool tiled) {
415 	if (view->impl->set_tiled) {
416 		view->impl->set_tiled(view, tiled);
417 	}
418 }
419 
view_close(struct sway_view * view)420 void view_close(struct sway_view *view) {
421 	if (view->impl->close) {
422 		view->impl->close(view);
423 	}
424 }
425 
view_close_popups(struct sway_view * view)426 void view_close_popups(struct sway_view *view) {
427 	if (view->impl->close_popups) {
428 		view->impl->close_popups(view);
429 	}
430 }
431 
view_damage_from(struct sway_view * view)432 void view_damage_from(struct sway_view *view) {
433 	for (int i = 0; i < root->outputs->length; ++i) {
434 		struct sway_output *output = root->outputs->items[i];
435 		output_damage_from_view(output, view);
436 	}
437 }
438 
view_for_each_surface(struct sway_view * view,wlr_surface_iterator_func_t iterator,void * user_data)439 void view_for_each_surface(struct sway_view *view,
440 		wlr_surface_iterator_func_t iterator, void *user_data) {
441 	if (!view->surface) {
442 		return;
443 	}
444 	if (view->impl->for_each_surface) {
445 		view->impl->for_each_surface(view, iterator, user_data);
446 	} else {
447 		wlr_surface_for_each_surface(view->surface, iterator, user_data);
448 	}
449 }
450 
view_for_each_popup(struct sway_view * view,wlr_surface_iterator_func_t iterator,void * user_data)451 void view_for_each_popup(struct sway_view *view,
452 		wlr_surface_iterator_func_t iterator, void *user_data) {
453 	if (!view->surface) {
454 		return;
455 	}
456 	if (view->impl->for_each_popup) {
457 		view->impl->for_each_popup(view, iterator, user_data);
458 	}
459 }
460 
461 static void view_subsurface_create(struct sway_view *view,
462 	struct wlr_subsurface *subsurface);
463 
464 static void view_init_subsurfaces(struct sway_view *view,
465 	struct wlr_surface *surface);
466 
467 static void view_child_init_subsurfaces(struct sway_view_child *view_child,
468 	struct wlr_surface *surface);
469 
view_handle_surface_new_subsurface(struct wl_listener * listener,void * data)470 static void view_handle_surface_new_subsurface(struct wl_listener *listener,
471 		void *data) {
472 	struct sway_view *view =
473 		wl_container_of(listener, view, surface_new_subsurface);
474 	struct wlr_subsurface *subsurface = data;
475 	view_subsurface_create(view, subsurface);
476 }
477 
view_has_executed_criteria(struct sway_view * view,struct criteria * criteria)478 static bool view_has_executed_criteria(struct sway_view *view,
479 		struct criteria *criteria) {
480 	for (int i = 0; i < view->executed_criteria->length; ++i) {
481 		struct criteria *item = view->executed_criteria->items[i];
482 		if (item == criteria) {
483 			return true;
484 		}
485 	}
486 	return false;
487 }
488 
view_execute_criteria(struct sway_view * view)489 void view_execute_criteria(struct sway_view *view) {
490 	list_t *criterias = criteria_for_view(view, CT_COMMAND);
491 	for (int i = 0; i < criterias->length; i++) {
492 		struct criteria *criteria = criterias->items[i];
493 		sway_log(SWAY_DEBUG, "Checking criteria %s", criteria->raw);
494 		if (view_has_executed_criteria(view, criteria)) {
495 			sway_log(SWAY_DEBUG, "Criteria already executed");
496 			continue;
497 		}
498 		sway_log(SWAY_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
499 				criteria->raw, view, criteria->cmdlist);
500 		list_add(view->executed_criteria, criteria);
501 		list_t *res_list = execute_command(
502 				criteria->cmdlist, NULL, view->container);
503 		while (res_list->length) {
504 			struct cmd_results *res = res_list->items[0];
505 			free_cmd_results(res);
506 			list_del(res_list, 0);
507 		}
508 		list_free(res_list);
509 	}
510 	list_free(criterias);
511 }
512 
view_populate_pid(struct sway_view * view)513 static void view_populate_pid(struct sway_view *view) {
514 	pid_t pid;
515 	switch (view->type) {
516 #if HAVE_XWAYLAND
517 	case SWAY_VIEW_XWAYLAND:;
518 		struct wlr_xwayland_surface *surf =
519 			wlr_xwayland_surface_from_wlr_surface(view->surface);
520 		pid = surf->pid;
521 		break;
522 #endif
523 	case SWAY_VIEW_XDG_SHELL:;
524 		struct wl_client *client =
525 			wl_resource_get_client(view->surface->resource);
526 		wl_client_get_credentials(client, &pid, NULL, NULL);
527 		break;
528 	}
529 	view->pid = pid;
530 }
531 
select_workspace(struct sway_view * view)532 static struct sway_workspace *select_workspace(struct sway_view *view) {
533 	struct sway_seat *seat = input_manager_current_seat();
534 
535 	// Check if there's any `assign` criteria for the view
536 	list_t *criterias = criteria_for_view(view,
537 			CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT);
538 	struct sway_workspace *ws = NULL;
539 	for (int i = 0; i < criterias->length; ++i) {
540 		struct criteria *criteria = criterias->items[i];
541 		if (criteria->type == CT_ASSIGN_OUTPUT) {
542 			struct sway_output *output = output_by_name_or_id(criteria->target);
543 			if (output) {
544 				ws = output_get_active_workspace(output);
545 				break;
546 			}
547 		} else {
548 			// CT_ASSIGN_WORKSPACE(_NUMBER)
549 			ws = criteria->type == CT_ASSIGN_WORKSPACE_NUMBER ?
550 				workspace_by_number(criteria->target) :
551 				workspace_by_name(criteria->target);
552 
553 			if (!ws) {
554 				if (strcasecmp(criteria->target, "back_and_forth") == 0) {
555 					if (seat->prev_workspace_name) {
556 						ws = workspace_create(NULL, seat->prev_workspace_name);
557 					}
558 				} else {
559 					ws = workspace_create(NULL, criteria->target);
560 				}
561 			}
562 			break;
563 		}
564 	}
565 	list_free(criterias);
566 	if (ws) {
567 		root_remove_workspace_pid(view->pid);
568 		return ws;
569 	}
570 
571 	// Check if there's a PID mapping
572 	ws = root_workspace_for_pid(view->pid);
573 	if (ws) {
574 		return ws;
575 	}
576 
577 	// Use the focused workspace
578 	struct sway_node *node = seat_get_focus_inactive(seat, &root->node);
579 	if (node && node->type == N_WORKSPACE) {
580 		return node->sway_workspace;
581 	} else if (node && node->type == N_CONTAINER) {
582 		return node->sway_container->workspace;
583 	}
584 
585 	// When there's no outputs connected, the above should match a workspace on
586 	// the noop output.
587 	sway_assert(false, "Expected to find a workspace");
588 	return NULL;
589 }
590 
should_focus(struct sway_view * view)591 static bool should_focus(struct sway_view *view) {
592 	struct sway_seat *seat = input_manager_current_seat();
593 	struct sway_container *prev_con = seat_get_focused_container(seat);
594 	struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
595 	struct sway_workspace *map_ws = view->container->workspace;
596 
597 	if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) {
598 		return true;
599 	}
600 
601 	// Views can only take focus if they are mapped into the active workspace
602 	if (prev_ws != map_ws) {
603 		return false;
604 	}
605 
606 	// If the view is the only one in the focused workspace, it'll get focus
607 	// regardless of any no_focus criteria.
608 	if (!view->container->parent && !prev_con) {
609 		size_t num_children = view->container->workspace->tiling->length +
610 			view->container->workspace->floating->length;
611 		if (num_children == 1) {
612 			return true;
613 		}
614 	}
615 
616 	// Check no_focus criteria
617 	list_t *criterias = criteria_for_view(view, CT_NO_FOCUS);
618 	size_t len = criterias->length;
619 	list_free(criterias);
620 	return len == 0;
621 }
622 
handle_foreign_activate_request(struct wl_listener * listener,void * data)623 static void handle_foreign_activate_request(
624 		struct wl_listener *listener, void *data) {
625 	struct sway_view *view = wl_container_of(
626 			listener, view, foreign_activate_request);
627 	struct wlr_foreign_toplevel_handle_v1_activated_event *event = data;
628 	struct sway_seat *seat;
629 	wl_list_for_each(seat, &server.input->seats, link) {
630 		if (seat->wlr_seat == event->seat) {
631 			seat_set_focus_container(seat, view->container);
632 			break;
633 		}
634 	}
635 }
636 
handle_foreign_close_request(struct wl_listener * listener,void * data)637 static void handle_foreign_close_request(
638 		struct wl_listener *listener, void *data) {
639 	struct sway_view *view = wl_container_of(
640 			listener, view, foreign_close_request);
641 	view_close(view);
642 }
643 
view_map(struct sway_view * view,struct wlr_surface * wlr_surface,bool fullscreen,struct wlr_output * fullscreen_output,bool decoration)644 void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
645 			  bool fullscreen, struct wlr_output *fullscreen_output,
646 			  bool decoration) {
647 	if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
648 		return;
649 	}
650 	view->surface = wlr_surface;
651 	view_populate_pid(view);
652 	view->container = container_create(view);
653 
654 	// If there is a request to be opened fullscreen on a specific output, try
655 	// to honor that request. Otherwise, fallback to assigns, pid mappings,
656 	// focused workspace, etc
657 	struct sway_workspace *ws = NULL;
658 	if (fullscreen_output && fullscreen_output->data) {
659 		struct sway_output *output = fullscreen_output->data;
660 		ws = output_get_active_workspace(output);
661 	}
662 	if (!ws) {
663 		ws = select_workspace(view);
664 	}
665 
666 	struct sway_seat *seat = input_manager_current_seat();
667 	struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node)
668 		: seat_get_focus_inactive(seat, &root->node);
669 	struct sway_container *target_sibling = node->type == N_CONTAINER ?
670 		node->sway_container : NULL;
671 
672 	view->foreign_toplevel =
673 		wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
674 	view->foreign_activate_request.notify = handle_foreign_activate_request;
675 	wl_signal_add(&view->foreign_toplevel->events.request_activate,
676 			&view->foreign_activate_request);
677 	view->foreign_close_request.notify = handle_foreign_close_request;
678 	wl_signal_add(&view->foreign_toplevel->events.request_close,
679 			&view->foreign_close_request);
680 
681 	// If we're about to launch the view into the floating container, then
682 	// launch it as a tiled view in the root of the workspace instead.
683 	if (target_sibling && container_is_floating(target_sibling)) {
684 		target_sibling = NULL;
685 		ws = seat_get_last_known_workspace(seat);
686 	}
687 
688 	if (target_sibling) {
689 		container_add_sibling(target_sibling, view->container, 1);
690 	} else if (ws) {
691 		workspace_add_tiling(ws, view->container);
692 	}
693 	ipc_event_window(view->container, "new");
694 
695 	view_init_subsurfaces(view, wlr_surface);
696 	wl_signal_add(&wlr_surface->events.new_subsurface,
697 		&view->surface_new_subsurface);
698 	view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
699 
700 	if (decoration) {
701 		view_update_csd_from_client(view, decoration);
702 	}
703 
704 	if (view->impl->wants_floating && view->impl->wants_floating(view)) {
705 		view->container->border = config->floating_border;
706 		view->container->border_thickness = config->floating_border_thickness;
707 		container_set_floating(view->container, true);
708 	} else {
709 		view->container->border = config->border;
710 		view->container->border_thickness = config->border_thickness;
711 		view_set_tiled(view, true);
712 	}
713 
714 	if (config->popup_during_fullscreen == POPUP_LEAVE &&
715 			view->container->workspace &&
716 			view->container->workspace->fullscreen &&
717 			view->container->workspace->fullscreen->view) {
718 		struct sway_container *fs = view->container->workspace->fullscreen;
719 		if (view_is_transient_for(view, fs->view)) {
720 			container_set_fullscreen(fs, false);
721 		}
722 	}
723 
724 	view_update_title(view, false);
725 	container_update_representation(view->container);
726 
727 	if (fullscreen) {
728 		container_set_fullscreen(view->container, true);
729 		arrange_workspace(view->container->workspace);
730 	} else {
731 		if (view->container->parent) {
732 			arrange_container(view->container->parent);
733 		} else if (view->container->workspace) {
734 			arrange_workspace(view->container->workspace);
735 		}
736 	}
737 
738 	view_execute_criteria(view);
739 
740 	if (should_focus(view)) {
741 		input_manager_set_focus(&view->container->node);
742 	}
743 
744 	const char *app_id;
745 	const char *class;
746 	if ((app_id = view_get_app_id(view)) != NULL) {
747 		wlr_foreign_toplevel_handle_v1_set_app_id(
748 				view->foreign_toplevel, app_id);
749 	} else if ((class = view_get_class(view)) != NULL) {
750 		wlr_foreign_toplevel_handle_v1_set_app_id(
751 				view->foreign_toplevel, class);
752 	}
753 }
754 
view_unmap(struct sway_view * view)755 void view_unmap(struct sway_view *view) {
756 	wl_signal_emit(&view->events.unmap, view);
757 
758 	wl_list_remove(&view->surface_new_subsurface.link);
759 
760 	if (view->urgent_timer) {
761 		wl_event_source_remove(view->urgent_timer);
762 		view->urgent_timer = NULL;
763 	}
764 
765 	if (view->foreign_toplevel) {
766 		wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);
767 		view->foreign_toplevel = NULL;
768 	}
769 
770 	struct sway_container *parent = view->container->parent;
771 	struct sway_workspace *ws = view->container->workspace;
772 	container_begin_destroy(view->container);
773 	if (parent) {
774 		container_reap_empty(parent);
775 	} else if (ws) {
776 		workspace_consider_destroy(ws);
777 	}
778 
779 	if (root->fullscreen_global) {
780 		// Container may have been a child of the root fullscreen container
781 		arrange_root();
782 	} else if (ws && !ws->node.destroying) {
783 		arrange_workspace(ws);
784 		workspace_detect_urgent(ws);
785 	}
786 
787 	struct sway_seat *seat;
788 	wl_list_for_each(seat, &server.input->seats, link) {
789 		seat->cursor->image_surface = NULL;
790 		if (seat->cursor->active_constraint) {
791 			struct wlr_surface *constrain_surface =
792 				seat->cursor->active_constraint->surface;
793 			if (view_from_wlr_surface(constrain_surface) == view) {
794 				sway_cursor_constrain(seat->cursor, NULL);
795 			}
796 		}
797 		seat_consider_warp_to_focus(seat);
798 	}
799 
800 	transaction_commit_dirty();
801 	view->surface = NULL;
802 }
803 
view_update_size(struct sway_view * view,int width,int height)804 void view_update_size(struct sway_view *view, int width, int height) {
805 	struct sway_container *con = view->container;
806 
807 	if (container_is_floating(con)) {
808 		con->content_width = width;
809 		con->content_height = height;
810 		container_set_geometry_from_content(con);
811 	} else {
812 		con->surface_x = con->content_x + (con->content_width - width) / 2;
813 		con->surface_y = con->content_y + (con->content_height - height) / 2;
814 		con->surface_x = fmax(con->surface_x, con->content_x);
815 		con->surface_y = fmax(con->surface_y, con->content_y);
816 	}
817 }
818 
819 static const struct sway_view_child_impl subsurface_impl;
820 
subsurface_get_root_coords(struct sway_view_child * child,int * root_sx,int * root_sy)821 static void subsurface_get_root_coords(struct sway_view_child *child,
822 		int *root_sx, int *root_sy) {
823 	struct wlr_surface *surface = child->surface;
824 	*root_sx = -child->view->geometry.x;
825 	*root_sy = -child->view->geometry.y;
826 
827 	if (child->parent && child->parent->impl &&
828 			child->parent->impl->get_root_coords) {
829 		int sx, sy;
830 		child->parent->impl->get_root_coords(child->parent, &sx, &sy);
831 		*root_sx += sx;
832 		*root_sy += sy;
833 	} else {
834 		while (surface && wlr_surface_is_subsurface(surface)) {
835 			struct wlr_subsurface *subsurface =
836 				wlr_subsurface_from_wlr_surface(surface);
837 			if (subsurface == NULL) {
838 				break;
839 			}
840 			*root_sx += subsurface->current.x;
841 			*root_sy += subsurface->current.y;
842 			surface = subsurface->parent;
843 		}
844 	}
845 }
846 
subsurface_destroy(struct sway_view_child * child)847 static void subsurface_destroy(struct sway_view_child *child) {
848 	if (!sway_assert(child->impl == &subsurface_impl,
849 			"Expected a subsurface")) {
850 		return;
851 	}
852 	struct sway_subsurface *subsurface = (struct sway_subsurface *)child;
853 	wl_list_remove(&subsurface->destroy.link);
854 	free(subsurface);
855 }
856 
857 static const struct sway_view_child_impl subsurface_impl = {
858 	.get_root_coords = subsurface_get_root_coords,
859 	.destroy = subsurface_destroy,
860 };
861 
subsurface_handle_destroy(struct wl_listener * listener,void * data)862 static void subsurface_handle_destroy(struct wl_listener *listener,
863 		void *data) {
864 	struct sway_subsurface *subsurface =
865 		wl_container_of(listener, subsurface, destroy);
866 	struct sway_view_child *child = &subsurface->child;
867 	view_child_destroy(child);
868 }
869 
870 static void view_child_damage(struct sway_view_child *child, bool whole);
871 
view_subsurface_create(struct sway_view * view,struct wlr_subsurface * wlr_subsurface)872 static void view_subsurface_create(struct sway_view *view,
873 		struct wlr_subsurface *wlr_subsurface) {
874 	struct sway_subsurface *subsurface =
875 		calloc(1, sizeof(struct sway_subsurface));
876 	if (subsurface == NULL) {
877 		sway_log(SWAY_ERROR, "Allocation failed");
878 		return;
879 	}
880 	view_child_init(&subsurface->child, &subsurface_impl, view,
881 		wlr_subsurface->surface);
882 
883 	wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
884 	subsurface->destroy.notify = subsurface_handle_destroy;
885 
886 	subsurface->child.mapped = true;
887 
888 	view_child_damage(&subsurface->child, true);
889 }
890 
view_child_subsurface_create(struct sway_view_child * child,struct wlr_subsurface * wlr_subsurface)891 static void view_child_subsurface_create(struct sway_view_child *child,
892 		struct wlr_subsurface *wlr_subsurface) {
893 	struct sway_subsurface *subsurface =
894 		calloc(1, sizeof(struct sway_subsurface));
895 	if (subsurface == NULL) {
896 		sway_log(SWAY_ERROR, "Allocation failed");
897 		return;
898 	}
899 	subsurface->child.parent = child;
900 	wl_list_insert(&child->children, &subsurface->child.link);
901 	view_child_init(&subsurface->child, &subsurface_impl, child->view,
902 		wlr_subsurface->surface);
903 
904 	wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
905 	subsurface->destroy.notify = subsurface_handle_destroy;
906 
907 	subsurface->child.mapped = true;
908 
909 	view_child_damage(&subsurface->child, true);
910 }
911 
view_child_is_mapped(struct sway_view_child * child)912 static bool view_child_is_mapped(struct sway_view_child *child) {
913 	while (child) {
914 		if (!child->mapped) {
915 			return false;
916 		}
917 		child = child->parent;
918 	}
919 	return true;
920 }
921 
view_child_damage(struct sway_view_child * child,bool whole)922 static void view_child_damage(struct sway_view_child *child, bool whole) {
923 	if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
924 		return;
925 	}
926 	int sx, sy;
927 	child->impl->get_root_coords(child, &sx, &sy);
928 	desktop_damage_surface(child->surface,
929 			child->view->container->content_x + sx,
930 			child->view->container->content_y + sy, whole);
931 }
932 
view_child_handle_surface_commit(struct wl_listener * listener,void * data)933 static void view_child_handle_surface_commit(struct wl_listener *listener,
934 		void *data) {
935 	struct sway_view_child *child =
936 		wl_container_of(listener, child, surface_commit);
937 	view_child_damage(child, false);
938 }
939 
view_child_handle_surface_new_subsurface(struct wl_listener * listener,void * data)940 static void view_child_handle_surface_new_subsurface(
941 		struct wl_listener *listener, void *data) {
942 	struct sway_view_child *child =
943 		wl_container_of(listener, child, surface_new_subsurface);
944 	struct wlr_subsurface *subsurface = data;
945 	view_child_subsurface_create(child, subsurface);
946 }
947 
view_child_handle_surface_destroy(struct wl_listener * listener,void * data)948 static void view_child_handle_surface_destroy(struct wl_listener *listener,
949 		void *data) {
950 	struct sway_view_child *child =
951 		wl_container_of(listener, child, surface_destroy);
952 	view_child_destroy(child);
953 }
954 
view_init_subsurfaces(struct sway_view * view,struct wlr_surface * surface)955 static void view_init_subsurfaces(struct sway_view *view,
956 		struct wlr_surface *surface) {
957 	struct wlr_subsurface *subsurface;
958 	wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) {
959 		view_subsurface_create(view, subsurface);
960 	}
961 }
962 
view_child_init_subsurfaces(struct sway_view_child * view_child,struct wlr_surface * surface)963 static void view_child_init_subsurfaces(struct sway_view_child *view_child,
964 		struct wlr_surface *surface) {
965 	struct wlr_subsurface *subsurface;
966 	wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) {
967 		view_child_subsurface_create(view_child, subsurface);
968 	}
969 }
970 
view_child_handle_surface_map(struct wl_listener * listener,void * data)971 static void view_child_handle_surface_map(struct wl_listener *listener,
972 		void *data) {
973 	struct sway_view_child *child =
974 		wl_container_of(listener, child, surface_map);
975 	child->mapped = true;
976 	view_child_damage(child, true);
977 }
978 
view_child_handle_surface_unmap(struct wl_listener * listener,void * data)979 static void view_child_handle_surface_unmap(struct wl_listener *listener,
980 		void *data) {
981 	struct sway_view_child *child =
982 		wl_container_of(listener, child, surface_unmap);
983 	view_child_damage(child, true);
984 	child->mapped = false;
985 }
986 
view_child_handle_view_unmap(struct wl_listener * listener,void * data)987 static void view_child_handle_view_unmap(struct wl_listener *listener,
988 		void *data) {
989 	struct sway_view_child *child =
990 		wl_container_of(listener, child, view_unmap);
991 	view_child_damage(child, true);
992 	child->mapped = false;
993 }
994 
view_child_init(struct sway_view_child * child,const struct sway_view_child_impl * impl,struct sway_view * view,struct wlr_surface * surface)995 void view_child_init(struct sway_view_child *child,
996 		const struct sway_view_child_impl *impl, struct sway_view *view,
997 		struct wlr_surface *surface) {
998 	child->impl = impl;
999 	child->view = view;
1000 	child->surface = surface;
1001 	wl_list_init(&child->children);
1002 
1003 	wl_signal_add(&surface->events.commit, &child->surface_commit);
1004 	child->surface_commit.notify = view_child_handle_surface_commit;
1005 	wl_signal_add(&surface->events.new_subsurface,
1006 		&child->surface_new_subsurface);
1007 	child->surface_new_subsurface.notify =
1008 		view_child_handle_surface_new_subsurface;
1009 	wl_signal_add(&surface->events.destroy, &child->surface_destroy);
1010 	child->surface_destroy.notify = view_child_handle_surface_destroy;
1011 
1012 	// Not all child views have a map/unmap event
1013 	child->surface_map.notify = view_child_handle_surface_map;
1014 	wl_list_init(&child->surface_map.link);
1015 	child->surface_unmap.notify = view_child_handle_surface_unmap;
1016 	wl_list_init(&child->surface_unmap.link);
1017 
1018 	wl_signal_add(&view->events.unmap, &child->view_unmap);
1019 	child->view_unmap.notify = view_child_handle_view_unmap;
1020 
1021 	struct sway_workspace *workspace = child->view->container->workspace;
1022 	if (workspace) {
1023 		wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1024 	}
1025 
1026 	view_child_init_subsurfaces(child, surface);
1027 }
1028 
view_child_destroy(struct sway_view_child * child)1029 void view_child_destroy(struct sway_view_child *child) {
1030 	if (view_child_is_mapped(child) && child->view->container != NULL) {
1031 		view_child_damage(child, true);
1032 	}
1033 
1034 	if (child->parent != NULL) {
1035 		wl_list_remove(&child->link);
1036 		child->parent = NULL;
1037 	}
1038 
1039 	struct sway_view_child *subchild, *tmpchild;
1040 	wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1041 		wl_list_remove(&subchild->link);
1042 		subchild->parent = NULL;
1043 		// The subchild lost its parent link, so it cannot see that the parent
1044 		// is unmapped. Unmap it directly.
1045 		subchild->mapped = false;
1046 	}
1047 
1048 	wl_list_remove(&child->surface_commit.link);
1049 	wl_list_remove(&child->surface_destroy.link);
1050 	wl_list_remove(&child->surface_map.link);
1051 	wl_list_remove(&child->surface_unmap.link);
1052 	wl_list_remove(&child->view_unmap.link);
1053 	wl_list_remove(&child->surface_new_subsurface.link);
1054 
1055 	if (child->impl && child->impl->destroy) {
1056 		child->impl->destroy(child);
1057 	} else {
1058 		free(child);
1059 	}
1060 }
1061 
view_from_wlr_surface(struct wlr_surface * wlr_surface)1062 struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1063 	if (wlr_surface_is_xdg_surface(wlr_surface)) {
1064 		struct wlr_xdg_surface *xdg_surface =
1065 			wlr_xdg_surface_from_wlr_surface(wlr_surface);
1066 		return view_from_wlr_xdg_surface(xdg_surface);
1067 	}
1068 #if HAVE_XWAYLAND
1069 	if (wlr_surface_is_xwayland_surface(wlr_surface)) {
1070 		struct wlr_xwayland_surface *xsurface =
1071 			wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1072 		return view_from_wlr_xwayland_surface(xsurface);
1073 	}
1074 #endif
1075 	if (wlr_surface_is_subsurface(wlr_surface)) {
1076 		struct wlr_subsurface *subsurface =
1077 			wlr_subsurface_from_wlr_surface(wlr_surface);
1078 		return view_from_wlr_surface(subsurface->parent);
1079 	}
1080 	if (wlr_surface_is_layer_surface(wlr_surface)) {
1081 		return NULL;
1082 	}
1083 
1084 	const char *role = wlr_surface->role ? wlr_surface->role->name : NULL;
1085 	sway_log(SWAY_DEBUG, "Surface of unknown type (role %s): %p",
1086 		role, wlr_surface);
1087 	return NULL;
1088 }
1089 
escape_pango_markup(const char * buffer)1090 static char *escape_pango_markup(const char *buffer) {
1091 	size_t length = escape_markup_text(buffer, NULL);
1092 	char *escaped_title = calloc(length + 1, sizeof(char));
1093 	escape_markup_text(buffer, escaped_title);
1094 	return escaped_title;
1095 }
1096 
append_prop(char * buffer,const char * value)1097 static size_t append_prop(char *buffer, const char *value) {
1098 	if (!value) {
1099 		return 0;
1100 	}
1101 	// If using pango_markup in font, we need to escape all markup chars
1102 	// from values to make sure tags are not inserted by clients
1103 	if (config->pango_markup) {
1104 		char *escaped_value = escape_pango_markup(value);
1105 		lenient_strcat(buffer, escaped_value);
1106 		size_t len = strlen(escaped_value);
1107 		free(escaped_value);
1108 		return len;
1109 	} else {
1110 		lenient_strcat(buffer, value);
1111 		return strlen(value);
1112 	}
1113 }
1114 
1115 /**
1116  * Calculate and return the length of the formatted title.
1117  * If buffer is not NULL, also populate the buffer with the formatted title.
1118  */
parse_title_format(struct sway_view * view,char * buffer)1119 static size_t parse_title_format(struct sway_view *view, char *buffer) {
1120 	if (!view->title_format || strcmp(view->title_format, "%title") == 0) {
1121 		return append_prop(buffer, view_get_title(view));
1122 	}
1123 
1124 	size_t len = 0;
1125 	char *format = view->title_format;
1126 	char *next = strchr(format, '%');
1127 	while (next) {
1128 		// Copy everything up to the %
1129 		lenient_strncat(buffer, format, next - format);
1130 		len += next - format;
1131 		format = next;
1132 
1133 		if (strncmp(next, "%title", 6) == 0) {
1134 			len += append_prop(buffer, view_get_title(view));
1135 			format += 6;
1136 		} else if (strncmp(next, "%app_id", 7) == 0) {
1137 			len += append_prop(buffer, view_get_app_id(view));
1138 			format += 7;
1139 		} else if (strncmp(next, "%class", 6) == 0) {
1140 			len += append_prop(buffer, view_get_class(view));
1141 			format += 6;
1142 		} else if (strncmp(next, "%instance", 9) == 0) {
1143 			len += append_prop(buffer, view_get_instance(view));
1144 			format += 9;
1145 		} else if (strncmp(next, "%shell", 6) == 0) {
1146 			len += append_prop(buffer, view_get_shell(view));
1147 			format += 6;
1148 		} else {
1149 			lenient_strcat(buffer, "%");
1150 			++format;
1151 			++len;
1152 		}
1153 		next = strchr(format, '%');
1154 	}
1155 	lenient_strcat(buffer, format);
1156 	len += strlen(format);
1157 
1158 	return len;
1159 }
1160 
view_update_title(struct sway_view * view,bool force)1161 void view_update_title(struct sway_view *view, bool force) {
1162 	const char *title = view_get_title(view);
1163 
1164 	if (!force) {
1165 		if (title && view->container->title &&
1166 				strcmp(title, view->container->title) == 0) {
1167 			return;
1168 		}
1169 		if (!title && !view->container->title) {
1170 			return;
1171 		}
1172 	}
1173 
1174 	free(view->container->title);
1175 	free(view->container->formatted_title);
1176 	if (title) {
1177 		size_t len = parse_title_format(view, NULL);
1178 		char *buffer = calloc(len + 1, sizeof(char));
1179 		if (!sway_assert(buffer, "Unable to allocate title string")) {
1180 			return;
1181 		}
1182 		parse_title_format(view, buffer);
1183 
1184 		view->container->title = strdup(title);
1185 		view->container->formatted_title = buffer;
1186 	} else {
1187 		view->container->title = NULL;
1188 		view->container->formatted_title = NULL;
1189 	}
1190 	container_calculate_title_height(view->container);
1191 	config_update_font_height(false);
1192 
1193 	// Update title after the global font height is updated
1194 	container_update_title_textures(view->container);
1195 
1196 	ipc_event_window(view->container, "title");
1197 
1198 	if (view->foreign_toplevel && title) {
1199 		wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1200 	}
1201 }
1202 
view_is_visible(struct sway_view * view)1203 bool view_is_visible(struct sway_view *view) {
1204 	if (view->container->node.destroying) {
1205 		return false;
1206 	}
1207 	struct sway_workspace *workspace = view->container->workspace;
1208 	if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) {
1209 		bool fs_global_descendant = false;
1210 		struct sway_container *parent = view->container->parent;
1211 		while (parent) {
1212 			if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) {
1213 				fs_global_descendant = true;
1214 			}
1215 			parent = parent->parent;
1216 		}
1217 		if (!fs_global_descendant) {
1218 			return false;
1219 		}
1220 	}
1221 	// Determine if view is nested inside a floating container which is sticky
1222 	struct sway_container *floater = view->container;
1223 	while (floater->parent) {
1224 		floater = floater->parent;
1225 	}
1226 	bool is_sticky = container_is_floating(floater) && floater->is_sticky;
1227 	if (!is_sticky && workspace && !workspace_is_visible(workspace)) {
1228 		return false;
1229 	}
1230 	// Check view isn't in a tabbed or stacked container on an inactive tab
1231 	struct sway_seat *seat = input_manager_current_seat();
1232 	struct sway_container *con = view->container;
1233 	while (con) {
1234 		enum sway_container_layout layout = container_parent_layout(con);
1235 		if ((layout == L_TABBED || layout == L_STACKED)
1236 				&& !container_is_floating(con)) {
1237 			struct sway_node *parent = con->parent ?
1238 				&con->parent->node : &con->workspace->node;
1239 			if (seat_get_active_tiling_child(seat, parent) != &con->node) {
1240 				return false;
1241 			}
1242 		}
1243 		con = con->parent;
1244 	}
1245 	// Check view isn't hidden by another fullscreen view
1246 	struct sway_container *fs = root->fullscreen_global ?
1247 		root->fullscreen_global : workspace->fullscreen;
1248 	if (fs && !container_is_fullscreen_or_child(view->container) &&
1249 			!container_is_transient_for(view->container, fs)) {
1250 		return false;
1251 	}
1252 	return true;
1253 }
1254 
view_set_urgent(struct sway_view * view,bool enable)1255 void view_set_urgent(struct sway_view *view, bool enable) {
1256 	if (view_is_urgent(view) == enable) {
1257 		return;
1258 	}
1259 	if (enable) {
1260 		struct sway_seat *seat = input_manager_current_seat();
1261 		if (seat_get_focused_container(seat) == view->container) {
1262 			return;
1263 		}
1264 		clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1265 	} else {
1266 		view->urgent = (struct timespec){ 0 };
1267 		if (view->urgent_timer) {
1268 			wl_event_source_remove(view->urgent_timer);
1269 			view->urgent_timer = NULL;
1270 		}
1271 	}
1272 	container_damage_whole(view->container);
1273 
1274 	ipc_event_window(view->container, "urgent");
1275 
1276 	if (!container_is_scratchpad_hidden(view->container)) {
1277 		workspace_detect_urgent(view->container->workspace);
1278 	}
1279 }
1280 
view_is_urgent(struct sway_view * view)1281 bool view_is_urgent(struct sway_view *view) {
1282 	return view->urgent.tv_sec || view->urgent.tv_nsec;
1283 }
1284 
view_remove_saved_buffer(struct sway_view * view)1285 void view_remove_saved_buffer(struct sway_view *view) {
1286 	if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) {
1287 		return;
1288 	}
1289 	struct sway_saved_buffer *saved_buf, *tmp;
1290 	wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) {
1291 		wlr_buffer_unlock(&saved_buf->buffer->base);
1292 		wl_list_remove(&saved_buf->link);
1293 		free(saved_buf);
1294 	}
1295 }
1296 
view_save_buffer_iterator(struct wlr_surface * surface,int sx,int sy,void * data)1297 static void view_save_buffer_iterator(struct wlr_surface *surface,
1298 		int sx, int sy, void *data) {
1299 	struct sway_view *view = data;
1300 
1301 	if (surface && wlr_surface_has_buffer(surface)) {
1302 		wlr_buffer_lock(&surface->buffer->base);
1303 		struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer));
1304 		saved_buffer->buffer = surface->buffer;
1305 		saved_buffer->width = surface->current.width;
1306 		saved_buffer->height = surface->current.height;
1307 		saved_buffer->x = sx;
1308 		saved_buffer->y = sy;
1309 		saved_buffer->transform = surface->current.transform;
1310 		wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1311 		wl_list_insert(&view->saved_buffers, &saved_buffer->link);
1312 	}
1313 }
1314 
view_save_buffer(struct sway_view * view)1315 void view_save_buffer(struct sway_view *view) {
1316 	if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) {
1317 		view_remove_saved_buffer(view);
1318 	}
1319 	view_for_each_surface(view, view_save_buffer_iterator, view);
1320 }
1321 
view_is_transient_for(struct sway_view * child,struct sway_view * ancestor)1322 bool view_is_transient_for(struct sway_view *child,
1323 		struct sway_view *ancestor) {
1324 	return child->impl->is_transient_for &&
1325 		child->impl->is_transient_for(child, ancestor);
1326 }
1327