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