diff options
Diffstat (limited to 'sway/tree/view.c')
-rw-r--r-- | sway/tree/view.c | 764 |
1 files changed, 309 insertions, 455 deletions
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7afcdf31..d6984178 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
7 | #include <wlr/types/wlr_output_layout.h> | 7 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_server_decoration.h> | 8 | #include <wlr/types/wlr_server_decoration.h> |
9 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 10 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
10 | #include "config.h" | 11 | #include "config.h" |
11 | #if HAVE_XWAYLAND | 12 | #if HAVE_XWAYLAND |
@@ -15,14 +16,16 @@ | |||
15 | #include "log.h" | 16 | #include "log.h" |
16 | #include "sway/criteria.h" | 17 | #include "sway/criteria.h" |
17 | #include "sway/commands.h" | 18 | #include "sway/commands.h" |
18 | #include "sway/desktop.h" | ||
19 | #include "sway/desktop/transaction.h" | 19 | #include "sway/desktop/transaction.h" |
20 | #include "sway/desktop/idle_inhibit_v1.h" | 20 | #include "sway/desktop/idle_inhibit_v1.h" |
21 | #include "sway/desktop/launcher.h" | ||
21 | #include "sway/input/cursor.h" | 22 | #include "sway/input/cursor.h" |
22 | #include "sway/ipc-server.h" | 23 | #include "sway/ipc-server.h" |
23 | #include "sway/output.h" | 24 | #include "sway/output.h" |
24 | #include "sway/input/seat.h" | 25 | #include "sway/input/seat.h" |
26 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 27 | #include "sway/server.h" |
28 | #include "sway/sway_text_node.h" | ||
26 | #include "sway/tree/arrange.h" | 29 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/container.h" | 30 | #include "sway/tree/container.h" |
28 | #include "sway/tree/view.h" | 31 | #include "sway/tree/view.h" |
@@ -32,15 +35,29 @@ | |||
32 | #include "pango.h" | 35 | #include "pango.h" |
33 | #include "stringop.h" | 36 | #include "stringop.h" |
34 | 37 | ||
35 | void view_init(struct sway_view *view, enum sway_view_type type, | 38 | bool view_init(struct sway_view *view, enum sway_view_type type, |
36 | const struct sway_view_impl *impl) { | 39 | const struct sway_view_impl *impl) { |
40 | bool failed = false; | ||
41 | view->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
42 | view->content_tree = alloc_scene_tree(view->scene_tree, &failed); | ||
43 | |||
44 | if (!failed && !scene_descriptor_assign(&view->scene_tree->node, | ||
45 | SWAY_SCENE_DESC_VIEW, view)) { | ||
46 | failed = true; | ||
47 | } | ||
48 | |||
49 | if (failed) { | ||
50 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
51 | return false; | ||
52 | } | ||
53 | |||
37 | view->type = type; | 54 | view->type = type; |
38 | view->impl = impl; | 55 | view->impl = impl; |
39 | view->executed_criteria = create_list(); | 56 | view->executed_criteria = create_list(); |
40 | wl_list_init(&view->saved_buffers); | ||
41 | view->allow_request_urgent = true; | 57 | view->allow_request_urgent = true; |
42 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 58 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
43 | wl_signal_init(&view->events.unmap); | 59 | wl_signal_init(&view->events.unmap); |
60 | return true; | ||
44 | } | 61 | } |
45 | 62 | ||
46 | void view_destroy(struct sway_view *view) { | 63 | void view_destroy(struct sway_view *view) { |
@@ -56,11 +73,11 @@ void view_destroy(struct sway_view *view) { | |||
56 | "(might have a pending transaction?)")) { | 73 | "(might have a pending transaction?)")) { |
57 | return; | 74 | return; |
58 | } | 75 | } |
59 | if (!wl_list_empty(&view->saved_buffers)) { | 76 | wl_list_remove(&view->events.unmap.listener_list); |
60 | view_remove_saved_buffer(view); | ||
61 | } | ||
62 | list_free(view->executed_criteria); | 77 | list_free(view->executed_criteria); |
63 | 78 | ||
79 | view_assign_ctx(view, NULL); | ||
80 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
64 | free(view->title_format); | 81 | free(view->title_format); |
65 | 82 | ||
66 | if (view->impl->destroy) { | 83 | if (view->impl->destroy) { |
@@ -206,7 +223,7 @@ bool view_ancestor_is_only_visible(struct sway_view *view) { | |||
206 | } else { | 223 | } else { |
207 | only_visible = true; | 224 | only_visible = true; |
208 | } | 225 | } |
209 | con = con->parent; | 226 | con = con->pending.parent; |
210 | } | 227 | } |
211 | return only_visible; | 228 | return only_visible; |
212 | } | 229 | } |
@@ -222,72 +239,73 @@ static bool view_is_only_visible(struct sway_view *view) { | |||
222 | } | 239 | } |
223 | } | 240 | } |
224 | 241 | ||
225 | con = con->parent; | 242 | con = con->pending.parent; |
226 | } | 243 | } |
227 | 244 | ||
228 | return true; | 245 | return true; |
229 | } | 246 | } |
230 | 247 | ||
231 | static bool gaps_to_edge(struct sway_view *view) { | 248 | static bool gaps_to_edge(struct sway_view *view) { |
232 | struct side_gaps gaps = view->container->workspace->current_gaps; | 249 | struct side_gaps gaps = view->container->pending.workspace->current_gaps; |
233 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; | 250 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; |
234 | } | 251 | } |
235 | 252 | ||
236 | void view_autoconfigure(struct sway_view *view) { | 253 | void view_autoconfigure(struct sway_view *view) { |
237 | struct sway_container *con = view->container; | 254 | struct sway_container *con = view->container; |
238 | struct sway_workspace *ws = con->workspace; | 255 | struct sway_workspace *ws = con->pending.workspace; |
239 | 256 | ||
240 | if (container_is_scratchpad_hidden(con) && | 257 | if (container_is_scratchpad_hidden(con) && |
241 | con->fullscreen_mode != FULLSCREEN_GLOBAL) { | 258 | con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
242 | return; | 259 | return; |
243 | } | 260 | } |
244 | struct sway_output *output = ws ? ws->output : NULL; | 261 | struct sway_output *output = ws ? ws->output : NULL; |
245 | 262 | ||
246 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 263 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
247 | con->content_x = output->lx; | 264 | con->pending.content_x = output->lx; |
248 | con->content_y = output->ly; | 265 | con->pending.content_y = output->ly; |
249 | con->content_width = output->width; | 266 | con->pending.content_width = output->width; |
250 | con->content_height = output->height; | 267 | con->pending.content_height = output->height; |
251 | return; | 268 | return; |
252 | } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 269 | } else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
253 | con->content_x = root->x; | 270 | con->pending.content_x = root->x; |
254 | con->content_y = root->y; | 271 | con->pending.content_y = root->y; |
255 | con->content_width = root->width; | 272 | con->pending.content_width = root->width; |
256 | con->content_height = root->height; | 273 | con->pending.content_height = root->height; |
257 | return; | 274 | return; |
258 | } | 275 | } |
259 | 276 | ||
260 | con->border_top = con->border_bottom = true; | 277 | con->pending.border_top = con->pending.border_bottom = true; |
261 | con->border_left = con->border_right = true; | 278 | con->pending.border_left = con->pending.border_right = true; |
262 | double y_offset = 0; | 279 | double y_offset = 0; |
263 | 280 | ||
264 | if (!container_is_floating(con) && ws) { | 281 | if (!container_is_floating_or_child(con) && ws) { |
265 | if (config->hide_edge_borders == E_BOTH | 282 | if (config->hide_edge_borders == E_BOTH |
266 | || config->hide_edge_borders == E_VERTICAL) { | 283 | || config->hide_edge_borders == E_VERTICAL) { |
267 | con->border_left = con->x != ws->x; | 284 | con->pending.border_left = con->pending.x != ws->x; |
268 | int right_x = con->x + con->width; | 285 | int right_x = con->pending.x + con->pending.width; |
269 | con->border_right = right_x != ws->x + ws->width; | 286 | con->pending.border_right = right_x != ws->x + ws->width; |
270 | } | 287 | } |
271 | 288 | ||
272 | if (config->hide_edge_borders == E_BOTH | 289 | if (config->hide_edge_borders == E_BOTH |
273 | || config->hide_edge_borders == E_HORIZONTAL) { | 290 | || config->hide_edge_borders == E_HORIZONTAL) { |
274 | con->border_top = con->y != ws->y; | 291 | con->pending.border_top = con->pending.y != ws->y; |
275 | int bottom_y = con->y + con->height; | 292 | int bottom_y = con->pending.y + con->pending.height; |
276 | con->border_bottom = bottom_y != ws->y + ws->height; | 293 | con->pending.border_bottom = bottom_y != ws->y + ws->height; |
277 | } | 294 | } |
278 | 295 | ||
279 | bool smart = config->hide_edge_borders_smart == ESMART_ON || | 296 | bool smart = config->hide_edge_borders_smart == ESMART_ON || |
280 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && | 297 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && |
281 | !gaps_to_edge(view)); | 298 | !gaps_to_edge(view)); |
282 | if (smart) { | 299 | if (smart) { |
283 | bool show_border = container_is_floating_or_child(con) || | 300 | bool show_border = !view_is_only_visible(view); |
284 | !view_is_only_visible(view); | 301 | con->pending.border_left &= show_border; |
285 | con->border_left &= show_border; | 302 | con->pending.border_right &= show_border; |
286 | con->border_right &= show_border; | 303 | con->pending.border_top &= show_border; |
287 | con->border_top &= show_border; | 304 | con->pending.border_bottom &= show_border; |
288 | con->border_bottom &= show_border; | ||
289 | } | 305 | } |
306 | } | ||
290 | 307 | ||
308 | if (!container_is_floating(con)) { | ||
291 | // In a tabbed or stacked container, the container's y is the top of the | 309 | // In a tabbed or stacked container, the container's y is the top of the |
292 | // title area. We have to offset the surface y by the height of the title, | 310 | // title area. We have to offset the surface y by the height of the title, |
293 | // bar, and disable any top border because we'll always have the title bar. | 311 | // bar, and disable any top border because we'll always have the title bar. |
@@ -298,56 +316,56 @@ void view_autoconfigure(struct sway_view *view) { | |||
298 | enum sway_container_layout layout = container_parent_layout(con); | 316 | enum sway_container_layout layout = container_parent_layout(con); |
299 | if (layout == L_TABBED) { | 317 | if (layout == L_TABBED) { |
300 | y_offset = container_titlebar_height(); | 318 | y_offset = container_titlebar_height(); |
301 | con->border_top = false; | 319 | con->pending.border_top = false; |
302 | } else if (layout == L_STACKED) { | 320 | } else if (layout == L_STACKED) { |
303 | y_offset = container_titlebar_height() * siblings->length; | 321 | y_offset = container_titlebar_height() * siblings->length; |
304 | con->border_top = false; | 322 | con->pending.border_top = false; |
305 | } | 323 | } |
306 | } | 324 | } |
307 | } | 325 | } |
308 | 326 | ||
309 | double x, y, width, height; | 327 | double x, y, width, height; |
310 | switch (con->border) { | 328 | switch (con->pending.border) { |
311 | default: | 329 | default: |
312 | case B_CSD: | 330 | case B_CSD: |
313 | case B_NONE: | 331 | case B_NONE: |
314 | x = con->x; | 332 | x = con->pending.x; |
315 | y = con->y + y_offset; | 333 | y = con->pending.y + y_offset; |
316 | width = con->width; | 334 | width = con->pending.width; |
317 | height = con->height - y_offset; | 335 | height = con->pending.height - y_offset; |
318 | break; | 336 | break; |
319 | case B_PIXEL: | 337 | case B_PIXEL: |
320 | x = con->x + con->border_thickness * con->border_left; | 338 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
321 | y = con->y + con->border_thickness * con->border_top + y_offset; | 339 | y = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset; |
322 | width = con->width | 340 | width = con->pending.width |
323 | - con->border_thickness * con->border_left | 341 | - con->pending.border_thickness * con->pending.border_left |
324 | - con->border_thickness * con->border_right; | 342 | - con->pending.border_thickness * con->pending.border_right; |
325 | height = con->height - y_offset | 343 | height = con->pending.height - y_offset |
326 | - con->border_thickness * con->border_top | 344 | - con->pending.border_thickness * con->pending.border_top |
327 | - con->border_thickness * con->border_bottom; | 345 | - con->pending.border_thickness * con->pending.border_bottom; |
328 | break; | 346 | break; |
329 | case B_NORMAL: | 347 | case B_NORMAL: |
330 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border | 348 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border |
331 | x = con->x + con->border_thickness * con->border_left; | 349 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
332 | width = con->width | 350 | width = con->pending.width |
333 | - con->border_thickness * con->border_left | 351 | - con->pending.border_thickness * con->pending.border_left |
334 | - con->border_thickness * con->border_right; | 352 | - con->pending.border_thickness * con->pending.border_right; |
335 | if (y_offset) { | 353 | if (y_offset) { |
336 | y = con->y + y_offset; | 354 | y = con->pending.y + y_offset; |
337 | height = con->height - y_offset | 355 | height = con->pending.height - y_offset |
338 | - con->border_thickness * con->border_bottom; | 356 | - con->pending.border_thickness * con->pending.border_bottom; |
339 | } else { | 357 | } else { |
340 | y = con->y + container_titlebar_height(); | 358 | y = con->pending.y + container_titlebar_height(); |
341 | height = con->height - container_titlebar_height() | 359 | height = con->pending.height - container_titlebar_height() |
342 | - con->border_thickness * con->border_bottom; | 360 | - con->pending.border_thickness * con->pending.border_bottom; |
343 | } | 361 | } |
344 | break; | 362 | break; |
345 | } | 363 | } |
346 | 364 | ||
347 | con->content_x = x; | 365 | con->pending.content_x = x; |
348 | con->content_y = y; | 366 | con->pending.content_y = y; |
349 | con->content_width = width; | 367 | con->pending.content_width = width; |
350 | con->content_height = height; | 368 | con->pending.content_height = height; |
351 | } | 369 | } |
352 | 370 | ||
353 | void view_set_activated(struct sway_view *view, bool activated) { | 371 | void view_set_activated(struct sway_view *view, bool activated) { |
@@ -360,17 +378,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
360 | } | 378 | } |
361 | } | 379 | } |
362 | 380 | ||
363 | void view_request_activate(struct sway_view *view) { | 381 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
364 | struct sway_workspace *ws = view->container->workspace; | 382 | struct sway_workspace *ws = view->container->pending.workspace; |
365 | if (!ws) { // hidden scratchpad container | 383 | if (!seat) { |
366 | return; | 384 | seat = input_manager_current_seat(); |
367 | } | 385 | } |
368 | struct sway_seat *seat = input_manager_current_seat(); | ||
369 | 386 | ||
370 | switch (config->focus_on_window_activation) { | 387 | switch (config->focus_on_window_activation) { |
371 | case FOWA_SMART: | 388 | case FOWA_SMART: |
372 | if (workspace_is_visible(ws)) { | 389 | if (ws && workspace_is_visible(ws)) { |
373 | seat_set_focus_container(seat, view->container); | 390 | seat_set_focus_container(seat, view->container); |
391 | container_raise_floating(view->container); | ||
374 | } else { | 392 | } else { |
375 | view_set_urgent(view, true); | 393 | view_set_urgent(view, true); |
376 | } | 394 | } |
@@ -379,11 +397,17 @@ void view_request_activate(struct sway_view *view) { | |||
379 | view_set_urgent(view, true); | 397 | view_set_urgent(view, true); |
380 | break; | 398 | break; |
381 | case FOWA_FOCUS: | 399 | case FOWA_FOCUS: |
382 | seat_set_focus_container(seat, view->container); | 400 | if (container_is_scratchpad_hidden_or_child(view->container)) { |
401 | root_scratchpad_show(view->container); | ||
402 | } else { | ||
403 | seat_set_focus_container(seat, view->container); | ||
404 | container_raise_floating(view->container); | ||
405 | } | ||
383 | break; | 406 | break; |
384 | case FOWA_NONE: | 407 | case FOWA_NONE: |
385 | break; | 408 | break; |
386 | } | 409 | } |
410 | transaction_commit_dirty(); | ||
387 | } | 411 | } |
388 | 412 | ||
389 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { | 413 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { |
@@ -401,13 +425,13 @@ void view_set_csd_from_server(struct sway_view *view, bool enabled) { | |||
401 | void view_update_csd_from_client(struct sway_view *view, bool enabled) { | 425 | void view_update_csd_from_client(struct sway_view *view, bool enabled) { |
402 | sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); | 426 | sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); |
403 | struct sway_container *con = view->container; | 427 | struct sway_container *con = view->container; |
404 | if (enabled && con && con->border != B_CSD) { | 428 | if (enabled && con && con->pending.border != B_CSD) { |
405 | con->saved_border = con->border; | 429 | con->saved_border = con->pending.border; |
406 | if (container_is_floating(con)) { | 430 | if (container_is_floating(con)) { |
407 | con->border = B_CSD; | 431 | con->pending.border = B_CSD; |
408 | } | 432 | } |
409 | } else if (!enabled && con && con->border == B_CSD) { | 433 | } else if (!enabled && con && con->pending.border == B_CSD) { |
410 | con->border = con->saved_border; | 434 | con->pending.border = con->saved_border; |
411 | } | 435 | } |
412 | view->using_csd = enabled; | 436 | view->using_csd = enabled; |
413 | } | 437 | } |
@@ -430,49 +454,6 @@ void view_close_popups(struct sway_view *view) { | |||
430 | } | 454 | } |
431 | } | 455 | } |
432 | 456 | ||
433 | void view_damage_from(struct sway_view *view) { | ||
434 | for (int i = 0; i < root->outputs->length; ++i) { | ||
435 | struct sway_output *output = root->outputs->items[i]; | ||
436 | output_damage_from_view(output, view); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | void view_for_each_surface(struct sway_view *view, | ||
441 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
442 | if (!view->surface) { | ||
443 | return; | ||
444 | } | ||
445 | if (view->impl->for_each_surface) { | ||
446 | view->impl->for_each_surface(view, iterator, user_data); | ||
447 | } else { | ||
448 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
449 | } | ||
450 | } | ||
451 | |||
452 | void view_for_each_popup_surface(struct sway_view *view, | ||
453 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
454 | if (!view->surface) { | ||
455 | return; | ||
456 | } | ||
457 | if (view->impl->for_each_popup_surface) { | ||
458 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
459 | } | ||
460 | } | ||
461 | |||
462 | static void view_subsurface_create(struct sway_view *view, | ||
463 | struct wlr_subsurface *subsurface); | ||
464 | |||
465 | static void view_init_subsurfaces(struct sway_view *view, | ||
466 | struct wlr_surface *surface); | ||
467 | |||
468 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
469 | void *data) { | ||
470 | struct sway_view *view = | ||
471 | wl_container_of(listener, view, surface_new_subsurface); | ||
472 | struct wlr_subsurface *subsurface = data; | ||
473 | view_subsurface_create(view, subsurface); | ||
474 | } | ||
475 | |||
476 | static bool view_has_executed_criteria(struct sway_view *view, | 457 | static bool view_has_executed_criteria(struct sway_view *view, |
477 | struct criteria *criteria) { | 458 | struct criteria *criteria) { |
478 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 459 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -514,7 +495,7 @@ static void view_populate_pid(struct sway_view *view) { | |||
514 | #if HAVE_XWAYLAND | 495 | #if HAVE_XWAYLAND |
515 | case SWAY_VIEW_XWAYLAND:; | 496 | case SWAY_VIEW_XWAYLAND:; |
516 | struct wlr_xwayland_surface *surf = | 497 | struct wlr_xwayland_surface *surf = |
517 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 498 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
518 | pid = surf->pid; | 499 | pid = surf->pid; |
519 | break; | 500 | break; |
520 | #endif | 501 | #endif |
@@ -527,6 +508,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
527 | view->pid = pid; | 508 | view->pid = pid; |
528 | } | 509 | } |
529 | 510 | ||
511 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { | ||
512 | if (view->ctx) { | ||
513 | // This ctx has been replaced | ||
514 | launcher_ctx_destroy(view->ctx); | ||
515 | view->ctx = NULL; | ||
516 | } | ||
517 | if (ctx == NULL) { | ||
518 | return; | ||
519 | } | ||
520 | launcher_ctx_consume(ctx); | ||
521 | |||
522 | view->ctx = ctx; | ||
523 | } | ||
524 | |||
530 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 525 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
531 | struct sway_seat *seat = input_manager_current_seat(); | 526 | struct sway_seat *seat = input_manager_current_seat(); |
532 | 527 | ||
@@ -562,13 +557,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
562 | } | 557 | } |
563 | list_free(criterias); | 558 | list_free(criterias); |
564 | if (ws) { | 559 | if (ws) { |
565 | root_remove_workspace_pid(view->pid); | 560 | view_assign_ctx(view, NULL); |
566 | return ws; | 561 | return ws; |
567 | } | 562 | } |
568 | 563 | ||
569 | // Check if there's a PID mapping | 564 | // Check if there's a PID mapping |
570 | ws = root_workspace_for_pid(view->pid); | 565 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
571 | if (ws) { | 566 | if (ws) { |
567 | view_assign_ctx(view, NULL); | ||
572 | return ws; | 568 | return ws; |
573 | } | 569 | } |
574 | 570 | ||
@@ -577,7 +573,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
577 | if (node && node->type == N_WORKSPACE) { | 573 | if (node && node->type == N_WORKSPACE) { |
578 | return node->sway_workspace; | 574 | return node->sway_workspace; |
579 | } else if (node && node->type == N_CONTAINER) { | 575 | } else if (node && node->type == N_CONTAINER) { |
580 | return node->sway_container->workspace; | 576 | return node->sway_container->pending.workspace; |
581 | } | 577 | } |
582 | 578 | ||
583 | // When there's no outputs connected, the above should match a workspace on | 579 | // When there's no outputs connected, the above should match a workspace on |
@@ -590,12 +586,17 @@ static bool should_focus(struct sway_view *view) { | |||
590 | struct sway_seat *seat = input_manager_current_seat(); | 586 | struct sway_seat *seat = input_manager_current_seat(); |
591 | struct sway_container *prev_con = seat_get_focused_container(seat); | 587 | struct sway_container *prev_con = seat_get_focused_container(seat); |
592 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); | 588 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); |
593 | struct sway_workspace *map_ws = view->container->workspace; | 589 | struct sway_workspace *map_ws = view->container->pending.workspace; |
594 | 590 | ||
595 | if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 591 | if (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
596 | return true; | 592 | return true; |
597 | } | 593 | } |
598 | 594 | ||
595 | // View opened "under" fullscreen view should not be given focus. | ||
596 | if (root->fullscreen_global || !map_ws || map_ws->fullscreen) { | ||
597 | return false; | ||
598 | } | ||
599 | |||
599 | // Views can only take focus if they are mapped into the active workspace | 600 | // Views can only take focus if they are mapped into the active workspace |
600 | if (prev_ws != map_ws) { | 601 | if (prev_ws != map_ws) { |
601 | return false; | 602 | return false; |
@@ -603,9 +604,9 @@ static bool should_focus(struct sway_view *view) { | |||
603 | 604 | ||
604 | // If the view is the only one in the focused workspace, it'll get focus | 605 | // If the view is the only one in the focused workspace, it'll get focus |
605 | // regardless of any no_focus criteria. | 606 | // regardless of any no_focus criteria. |
606 | if (!view->container->parent && !prev_con) { | 607 | if (!view->container->pending.parent && !prev_con) { |
607 | size_t num_children = view->container->workspace->tiling->length + | 608 | size_t num_children = view->container->pending.workspace->tiling->length + |
608 | view->container->workspace->floating->length; | 609 | view->container->pending.workspace->floating->length; |
609 | if (num_children == 1) { | 610 | if (num_children == 1) { |
610 | return true; | 611 | return true; |
611 | } | 612 | } |
@@ -635,6 +636,7 @@ static void handle_foreign_activate_request( | |||
635 | break; | 636 | break; |
636 | } | 637 | } |
637 | } | 638 | } |
639 | transaction_commit_dirty(); | ||
638 | } | 640 | } |
639 | 641 | ||
640 | static void handle_foreign_fullscreen_request( | 642 | static void handle_foreign_fullscreen_request( |
@@ -645,9 +647,21 @@ static void handle_foreign_fullscreen_request( | |||
645 | 647 | ||
646 | // Match fullscreen command behavior for scratchpad hidden views | 648 | // Match fullscreen command behavior for scratchpad hidden views |
647 | struct sway_container *container = view->container; | 649 | struct sway_container *container = view->container; |
648 | if (!container->workspace) { | 650 | if (!container->pending.workspace) { |
649 | while (container->parent) { | 651 | while (container->pending.parent) { |
650 | container = container->parent; | 652 | container = container->pending.parent; |
653 | } | ||
654 | } | ||
655 | |||
656 | if (event->fullscreen && event->output && event->output->data) { | ||
657 | struct sway_output *output = event->output->data; | ||
658 | struct sway_workspace *ws = output_get_active_workspace(output); | ||
659 | if (ws && !container_is_scratchpad_hidden(view->container)) { | ||
660 | if (container_is_floating(view->container)) { | ||
661 | workspace_add_floating(ws, view->container); | ||
662 | } else { | ||
663 | workspace_add_tiling(ws, view->container); | ||
664 | } | ||
651 | } | 665 | } |
652 | } | 666 | } |
653 | 667 | ||
@@ -656,12 +670,13 @@ static void handle_foreign_fullscreen_request( | |||
656 | if (event->fullscreen) { | 670 | if (event->fullscreen) { |
657 | arrange_root(); | 671 | arrange_root(); |
658 | } else { | 672 | } else { |
659 | if (container->parent) { | 673 | if (container->pending.parent) { |
660 | arrange_container(container->parent); | 674 | arrange_container(container->pending.parent); |
661 | } else if (container->workspace) { | 675 | } else if (container->pending.workspace) { |
662 | arrange_workspace(container->workspace); | 676 | arrange_workspace(container->pending.workspace); |
663 | } | 677 | } |
664 | } | 678 | } |
679 | transaction_commit_dirty(); | ||
665 | } | 680 | } |
666 | 681 | ||
667 | static void handle_foreign_close_request( | 682 | static void handle_foreign_close_request( |
@@ -692,6 +707,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
692 | view_populate_pid(view); | 707 | view_populate_pid(view); |
693 | view->container = container_create(view); | 708 | view->container = container_create(view); |
694 | 709 | ||
710 | if (view->ctx == NULL) { | ||
711 | struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid); | ||
712 | if (ctx != NULL) { | ||
713 | view_assign_ctx(view, ctx); | ||
714 | } | ||
715 | } | ||
716 | |||
695 | // If there is a request to be opened fullscreen on a specific output, try | 717 | // If there is a request to be opened fullscreen on a specific output, try |
696 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 718 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
697 | // focused workspace, etc | 719 | // focused workspace, etc |
@@ -705,10 +727,29 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
705 | } | 727 | } |
706 | 728 | ||
707 | struct sway_seat *seat = input_manager_current_seat(); | 729 | struct sway_seat *seat = input_manager_current_seat(); |
708 | struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) | 730 | struct sway_node *node = |
709 | : seat_get_focus_inactive(seat, &root->node); | 731 | seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); |
710 | struct sway_container *target_sibling = node->type == N_CONTAINER ? | 732 | struct sway_container *target_sibling = NULL; |
711 | node->sway_container : NULL; | 733 | if (node && node->type == N_CONTAINER) { |
734 | if (container_is_floating(node->sway_container)) { | ||
735 | // If we're about to launch the view into the floating container, then | ||
736 | // launch it as a tiled view instead. | ||
737 | if (ws) { | ||
738 | target_sibling = seat_get_focus_inactive_tiling(seat, ws); | ||
739 | if (target_sibling) { | ||
740 | struct sway_container *con = | ||
741 | seat_get_focus_inactive_view(seat, &target_sibling->node); | ||
742 | if (con) { | ||
743 | target_sibling = con; | ||
744 | } | ||
745 | } | ||
746 | } else { | ||
747 | ws = seat_get_last_known_workspace(seat); | ||
748 | } | ||
749 | } else { | ||
750 | target_sibling = node->sway_container; | ||
751 | } | ||
752 | } | ||
712 | 753 | ||
713 | view->foreign_toplevel = | 754 | view->foreign_toplevel = |
714 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 755 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
@@ -725,13 +766,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
725 | wl_signal_add(&view->foreign_toplevel->events.destroy, | 766 | wl_signal_add(&view->foreign_toplevel->events.destroy, |
726 | &view->foreign_destroy); | 767 | &view->foreign_destroy); |
727 | 768 | ||
728 | // If we're about to launch the view into the floating container, then | ||
729 | // launch it as a tiled view in the root of the workspace instead. | ||
730 | if (target_sibling && container_is_floating(target_sibling)) { | ||
731 | target_sibling = NULL; | ||
732 | ws = seat_get_last_known_workspace(seat); | ||
733 | } | ||
734 | |||
735 | struct sway_container *container = view->container; | 769 | struct sway_container *container = view->container; |
736 | if (target_sibling) { | 770 | if (target_sibling) { |
737 | container_add_sibling(target_sibling, container, 1); | 771 | container_add_sibling(target_sibling, container, 1); |
@@ -740,30 +774,25 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
740 | } | 774 | } |
741 | ipc_event_window(view->container, "new"); | 775 | ipc_event_window(view->container, "new"); |
742 | 776 | ||
743 | view_init_subsurfaces(view, wlr_surface); | ||
744 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
745 | &view->surface_new_subsurface); | ||
746 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
747 | |||
748 | if (decoration) { | 777 | if (decoration) { |
749 | view_update_csd_from_client(view, decoration); | 778 | view_update_csd_from_client(view, decoration); |
750 | } | 779 | } |
751 | 780 | ||
752 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 781 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
753 | view->container->border = config->floating_border; | 782 | view->container->pending.border = config->floating_border; |
754 | view->container->border_thickness = config->floating_border_thickness; | 783 | view->container->pending.border_thickness = config->floating_border_thickness; |
755 | container_set_floating(view->container, true); | 784 | container_set_floating(view->container, true); |
756 | } else { | 785 | } else { |
757 | view->container->border = config->border; | 786 | view->container->pending.border = config->border; |
758 | view->container->border_thickness = config->border_thickness; | 787 | view->container->pending.border_thickness = config->border_thickness; |
759 | view_set_tiled(view, true); | 788 | view_set_tiled(view, true); |
760 | } | 789 | } |
761 | 790 | ||
762 | if (config->popup_during_fullscreen == POPUP_LEAVE && | 791 | if (config->popup_during_fullscreen == POPUP_LEAVE && |
763 | container->workspace && | 792 | container->pending.workspace && |
764 | container->workspace->fullscreen && | 793 | container->pending.workspace->fullscreen && |
765 | container->workspace->fullscreen->view) { | 794 | container->pending.workspace->fullscreen->view) { |
766 | struct sway_container *fs = container->workspace->fullscreen; | 795 | struct sway_container *fs = container->pending.workspace->fullscreen; |
767 | if (view_is_transient_for(view, fs->view)) { | 796 | if (view_is_transient_for(view, fs->view)) { |
768 | container_set_fullscreen(fs, false); | 797 | container_set_fullscreen(fs, false); |
769 | } | 798 | } |
@@ -774,12 +803,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
774 | 803 | ||
775 | if (fullscreen) { | 804 | if (fullscreen) { |
776 | container_set_fullscreen(view->container, true); | 805 | container_set_fullscreen(view->container, true); |
777 | arrange_workspace(view->container->workspace); | 806 | arrange_workspace(view->container->pending.workspace); |
778 | } else { | 807 | } else { |
779 | if (container->parent) { | 808 | if (container->pending.parent) { |
780 | arrange_container(container->parent); | 809 | arrange_container(container->pending.parent); |
781 | } else if (container->workspace) { | 810 | } else if (container->pending.workspace) { |
782 | arrange_workspace(container->workspace); | 811 | arrange_workspace(container->pending.workspace); |
783 | } | 812 | } |
784 | } | 813 | } |
785 | 814 | ||
@@ -788,11 +817,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
788 | bool set_focus = should_focus(view); | 817 | bool set_focus = should_focus(view); |
789 | 818 | ||
790 | #if HAVE_XWAYLAND | 819 | #if HAVE_XWAYLAND |
791 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 820 | struct wlr_xwayland_surface *xsurface; |
792 | struct wlr_xwayland_surface *xsurface = | 821 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
793 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | 822 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
794 | set_focus = (wlr_xwayland_icccm_input_model(xsurface) != | 823 | WLR_ICCCM_INPUT_MODEL_NONE; |
795 | WLR_ICCCM_INPUT_MODEL_NONE) && set_focus; | ||
796 | } | 824 | } |
797 | #endif | 825 | #endif |
798 | 826 | ||
@@ -803,18 +831,16 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
803 | const char *app_id; | 831 | const char *app_id; |
804 | const char *class; | 832 | const char *class; |
805 | if ((app_id = view_get_app_id(view)) != NULL) { | 833 | if ((app_id = view_get_app_id(view)) != NULL) { |
806 | wlr_foreign_toplevel_handle_v1_set_app_id( | 834 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); |
807 | view->foreign_toplevel, app_id); | ||
808 | } else if ((class = view_get_class(view)) != NULL) { | 835 | } else if ((class = view_get_class(view)) != NULL) { |
809 | wlr_foreign_toplevel_handle_v1_set_app_id( | 836 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class); |
810 | view->foreign_toplevel, class); | ||
811 | } | 837 | } |
812 | } | 838 | } |
813 | 839 | ||
814 | void view_unmap(struct sway_view *view) { | 840 | void view_unmap(struct sway_view *view) { |
815 | wl_signal_emit(&view->events.unmap, view); | 841 | wl_signal_emit_mutable(&view->events.unmap, view); |
816 | 842 | ||
817 | wl_list_remove(&view->surface_new_subsurface.link); | 843 | view->executed_criteria->length = 0; |
818 | 844 | ||
819 | if (view->urgent_timer) { | 845 | if (view->urgent_timer) { |
820 | wl_event_source_remove(view->urgent_timer); | 846 | wl_event_source_remove(view->urgent_timer); |
@@ -826,8 +852,8 @@ void view_unmap(struct sway_view *view) { | |||
826 | view->foreign_toplevel = NULL; | 852 | view->foreign_toplevel = NULL; |
827 | } | 853 | } |
828 | 854 | ||
829 | struct sway_container *parent = view->container->parent; | 855 | struct sway_container *parent = view->container->pending.parent; |
830 | struct sway_workspace *ws = view->container->workspace; | 856 | struct sway_workspace *ws = view->container->pending.workspace; |
831 | container_begin_destroy(view->container); | 857 | container_begin_destroy(view->container); |
832 | if (parent) { | 858 | if (parent) { |
833 | container_reap_empty(parent); | 859 | container_reap_empty(parent); |
@@ -860,262 +886,54 @@ void view_unmap(struct sway_view *view) { | |||
860 | view->surface = NULL; | 886 | view->surface = NULL; |
861 | } | 887 | } |
862 | 888 | ||
863 | void view_update_size(struct sway_view *view, int width, int height) { | 889 | void view_update_size(struct sway_view *view) { |
864 | struct sway_container *con = view->container; | 890 | struct sway_container *con = view->container; |
865 | 891 | con->pending.content_width = view->geometry.width; | |
866 | if (container_is_floating(con)) { | 892 | con->pending.content_height = view->geometry.height; |
867 | con->content_width = width; | 893 | container_set_geometry_from_content(con); |
868 | con->content_height = height; | ||
869 | container_set_geometry_from_content(con); | ||
870 | } else { | ||
871 | con->surface_x = con->content_x + (con->content_width - width) / 2; | ||
872 | con->surface_y = con->content_y + (con->content_height - height) / 2; | ||
873 | con->surface_x = fmax(con->surface_x, con->content_x); | ||
874 | con->surface_y = fmax(con->surface_y, con->content_y); | ||
875 | } | ||
876 | } | 894 | } |
877 | 895 | ||
878 | static const struct sway_view_child_impl subsurface_impl; | 896 | void view_center_and_clip_surface(struct sway_view *view) { |
897 | struct sway_container *con = view->container; | ||
879 | 898 | ||
880 | static void subsurface_get_root_coords(struct sway_view_child *child, | 899 | if (container_is_floating(con)) { |
881 | int *root_sx, int *root_sy) { | 900 | // We always center the current coordinates rather than the next, as the |
882 | struct wlr_surface *surface = child->surface; | 901 | // geometry immediately affects the currently active rendering. |
883 | *root_sx = -child->view->geometry.x; | 902 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); |
884 | *root_sy = -child->view->geometry.y; | 903 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); |
885 | 904 | ||
886 | if (child->parent && child->parent->impl && | 905 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
887 | child->parent->impl->get_root_coords) { | ||
888 | int sx, sy; | ||
889 | child->parent->impl->get_root_coords(child->parent, &sx, &sy); | ||
890 | *root_sx += sx; | ||
891 | *root_sy += sy; | ||
892 | } else { | 906 | } else { |
893 | while (surface && wlr_surface_is_subsurface(surface)) { | 907 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
894 | struct wlr_subsurface *subsurface = | ||
895 | wlr_subsurface_from_wlr_surface(surface); | ||
896 | if (subsurface == NULL) { | ||
897 | break; | ||
898 | } | ||
899 | *root_sx += subsurface->current.x; | ||
900 | *root_sy += subsurface->current.y; | ||
901 | surface = subsurface->parent; | ||
902 | } | ||
903 | } | ||
904 | } | ||
905 | |||
906 | static void subsurface_destroy(struct sway_view_child *child) { | ||
907 | if (!sway_assert(child->impl == &subsurface_impl, | ||
908 | "Expected a subsurface")) { | ||
909 | return; | ||
910 | } | ||
911 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | ||
912 | wl_list_remove(&subsurface->destroy.link); | ||
913 | free(subsurface); | ||
914 | } | ||
915 | |||
916 | static const struct sway_view_child_impl subsurface_impl = { | ||
917 | .get_root_coords = subsurface_get_root_coords, | ||
918 | .destroy = subsurface_destroy, | ||
919 | }; | ||
920 | |||
921 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
922 | void *data) { | ||
923 | struct sway_subsurface *subsurface = | ||
924 | wl_container_of(listener, subsurface, destroy); | ||
925 | struct sway_view_child *child = &subsurface->child; | ||
926 | view_child_destroy(child); | ||
927 | } | ||
928 | |||
929 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
930 | |||
931 | static void view_subsurface_create(struct sway_view *view, | ||
932 | struct wlr_subsurface *wlr_subsurface) { | ||
933 | struct sway_subsurface *subsurface = | ||
934 | calloc(1, sizeof(struct sway_subsurface)); | ||
935 | if (subsurface == NULL) { | ||
936 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
937 | return; | ||
938 | } | ||
939 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
940 | wlr_subsurface->surface); | ||
941 | |||
942 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
943 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
944 | |||
945 | subsurface->child.mapped = true; | ||
946 | |||
947 | view_child_damage(&subsurface->child, true); | ||
948 | } | ||
949 | |||
950 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
951 | struct wlr_subsurface *wlr_subsurface) { | ||
952 | struct sway_subsurface *subsurface = | ||
953 | calloc(1, sizeof(struct sway_subsurface)); | ||
954 | if (subsurface == NULL) { | ||
955 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
956 | return; | ||
957 | } | ||
958 | subsurface->child.parent = child; | ||
959 | wl_list_insert(&child->children, &subsurface->child.link); | ||
960 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
961 | wlr_subsurface->surface); | ||
962 | |||
963 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
964 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
965 | |||
966 | subsurface->child.mapped = true; | ||
967 | |||
968 | view_child_damage(&subsurface->child, true); | ||
969 | } | ||
970 | |||
971 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
972 | if (!child || !child->mapped || !child->view || !child->view->container) { | ||
973 | return; | ||
974 | } | 908 | } |
975 | int sx, sy; | ||
976 | child->impl->get_root_coords(child, &sx, &sy); | ||
977 | desktop_damage_surface(child->surface, | ||
978 | child->view->container->content_x + sx, | ||
979 | child->view->container->content_y + sy, whole); | ||
980 | } | ||
981 | |||
982 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
983 | void *data) { | ||
984 | struct sway_view_child *child = | ||
985 | wl_container_of(listener, child, surface_commit); | ||
986 | view_child_damage(child, false); | ||
987 | } | ||
988 | 909 | ||
989 | static void view_child_handle_surface_new_subsurface( | 910 | // only make sure to clip the content if there is content to clip |
990 | struct wl_listener *listener, void *data) { | 911 | if (!wl_list_empty(&con->view->content_tree->children)) { |
991 | struct sway_view_child *child = | 912 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ |
992 | wl_container_of(listener, child, surface_new_subsurface); | 913 | .x = con->view->geometry.x, |
993 | struct wlr_subsurface *subsurface = data; | 914 | .y = con->view->geometry.y, |
994 | view_child_subsurface_create(child, subsurface); | 915 | .width = con->current.content_width, |
995 | } | 916 | .height = con->current.content_height, |
996 | 917 | }); | |
997 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
998 | void *data) { | ||
999 | struct sway_view_child *child = | ||
1000 | wl_container_of(listener, child, surface_destroy); | ||
1001 | view_child_destroy(child); | ||
1002 | } | ||
1003 | |||
1004 | static void view_init_subsurfaces(struct sway_view *view, | ||
1005 | struct wlr_surface *surface) { | ||
1006 | struct wlr_subsurface *subsurface; | ||
1007 | wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { | ||
1008 | view_subsurface_create(view, subsurface); | ||
1009 | } | ||
1010 | } | ||
1011 | |||
1012 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1013 | void *data) { | ||
1014 | struct sway_view_child *child = | ||
1015 | wl_container_of(listener, child, surface_map); | ||
1016 | child->mapped = true; | ||
1017 | view_child_damage(child, true); | ||
1018 | } | ||
1019 | |||
1020 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1021 | void *data) { | ||
1022 | struct sway_view_child *child = | ||
1023 | wl_container_of(listener, child, surface_unmap); | ||
1024 | view_child_damage(child, true); | ||
1025 | child->mapped = false; | ||
1026 | } | ||
1027 | |||
1028 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1029 | void *data) { | ||
1030 | struct sway_view_child *child = | ||
1031 | wl_container_of(listener, child, view_unmap); | ||
1032 | view_child_damage(child, true); | ||
1033 | child->mapped = false; | ||
1034 | } | ||
1035 | |||
1036 | void view_child_init(struct sway_view_child *child, | ||
1037 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1038 | struct wlr_surface *surface) { | ||
1039 | child->impl = impl; | ||
1040 | child->view = view; | ||
1041 | child->surface = surface; | ||
1042 | wl_list_init(&child->children); | ||
1043 | |||
1044 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1045 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1046 | wl_signal_add(&surface->events.new_subsurface, | ||
1047 | &child->surface_new_subsurface); | ||
1048 | child->surface_new_subsurface.notify = | ||
1049 | view_child_handle_surface_new_subsurface; | ||
1050 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1051 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1052 | |||
1053 | // Not all child views have a map/unmap event | ||
1054 | child->surface_map.notify = view_child_handle_surface_map; | ||
1055 | wl_list_init(&child->surface_map.link); | ||
1056 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1057 | wl_list_init(&child->surface_unmap.link); | ||
1058 | |||
1059 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1060 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1061 | |||
1062 | struct sway_workspace *workspace = child->view->container->workspace; | ||
1063 | if (workspace) { | ||
1064 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1065 | } | ||
1066 | |||
1067 | view_init_subsurfaces(child->view, surface); | ||
1068 | } | ||
1069 | |||
1070 | void view_child_destroy(struct sway_view_child *child) { | ||
1071 | if (child->mapped && child->view->container != NULL) { | ||
1072 | view_child_damage(child, true); | ||
1073 | } | ||
1074 | |||
1075 | if (child->parent != NULL) { | ||
1076 | wl_list_remove(&child->link); | ||
1077 | child->parent = NULL; | ||
1078 | } | ||
1079 | |||
1080 | struct sway_view_child *subchild, *tmpchild; | ||
1081 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | ||
1082 | wl_list_remove(&subchild->link); | ||
1083 | subchild->parent = NULL; | ||
1084 | } | ||
1085 | |||
1086 | wl_list_remove(&child->surface_commit.link); | ||
1087 | wl_list_remove(&child->surface_destroy.link); | ||
1088 | wl_list_remove(&child->surface_map.link); | ||
1089 | wl_list_remove(&child->surface_unmap.link); | ||
1090 | wl_list_remove(&child->view_unmap.link); | ||
1091 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1092 | |||
1093 | if (child->impl && child->impl->destroy) { | ||
1094 | child->impl->destroy(child); | ||
1095 | } else { | ||
1096 | free(child); | ||
1097 | } | 918 | } |
1098 | } | 919 | } |
1099 | 920 | ||
1100 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | 921 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { |
1101 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 922 | struct wlr_xdg_surface *xdg_surface; |
1102 | struct wlr_xdg_surface *xdg_surface = | 923 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1103 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1104 | return view_from_wlr_xdg_surface(xdg_surface); | 924 | return view_from_wlr_xdg_surface(xdg_surface); |
1105 | } | 925 | } |
1106 | #if HAVE_XWAYLAND | 926 | #if HAVE_XWAYLAND |
1107 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 927 | struct wlr_xwayland_surface *xsurface; |
1108 | struct wlr_xwayland_surface *xsurface = | 928 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1109 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1110 | return view_from_wlr_xwayland_surface(xsurface); | 929 | return view_from_wlr_xwayland_surface(xsurface); |
1111 | } | 930 | } |
1112 | #endif | 931 | #endif |
1113 | if (wlr_surface_is_subsurface(wlr_surface)) { | 932 | struct wlr_subsurface *subsurface; |
1114 | struct wlr_subsurface *subsurface = | 933 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1115 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1116 | return view_from_wlr_surface(subsurface->parent); | 934 | return view_from_wlr_surface(subsurface->parent); |
1117 | } | 935 | } |
1118 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 936 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1119 | return NULL; | 937 | return NULL; |
1120 | } | 938 | } |
1121 | 939 | ||
@@ -1211,25 +1029,31 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1211 | 1029 | ||
1212 | free(view->container->title); | 1030 | free(view->container->title); |
1213 | free(view->container->formatted_title); | 1031 | free(view->container->formatted_title); |
1214 | if (title) { | 1032 | |
1215 | size_t len = parse_title_format(view, NULL); | 1033 | size_t len = parse_title_format(view, NULL); |
1034 | |||
1035 | if (len) { | ||
1216 | char *buffer = calloc(len + 1, sizeof(char)); | 1036 | char *buffer = calloc(len + 1, sizeof(char)); |
1217 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1037 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1218 | return; | 1038 | return; |
1219 | } | 1039 | } |
1220 | parse_title_format(view, buffer); | ||
1221 | 1040 | ||
1222 | view->container->title = strdup(title); | 1041 | parse_title_format(view, buffer); |
1223 | view->container->formatted_title = buffer; | 1042 | view->container->formatted_title = buffer; |
1224 | } else { | 1043 | } else { |
1225 | view->container->title = NULL; | ||
1226 | view->container->formatted_title = NULL; | 1044 | view->container->formatted_title = NULL; |
1227 | } | 1045 | } |
1228 | container_calculate_title_height(view->container); | 1046 | |
1229 | config_update_font_height(false); | 1047 | view->container->title = title ? strdup(title) : NULL; |
1230 | 1048 | ||
1231 | // Update title after the global font height is updated | 1049 | // Update title after the global font height is updated |
1232 | container_update_title_textures(view->container); | 1050 | if (view->container->title_bar.title_text && len) { |
1051 | sway_text_node_set_text(view->container->title_bar.title_text, | ||
1052 | view->container->formatted_title); | ||
1053 | container_arrange_title_bar(view->container); | ||
1054 | } else { | ||
1055 | container_update_title_bar(view->container); | ||
1056 | } | ||
1233 | 1057 | ||
1234 | ipc_event_window(view->container, "title"); | 1058 | ipc_event_window(view->container, "title"); |
1235 | 1059 | ||
@@ -1242,15 +1066,15 @@ bool view_is_visible(struct sway_view *view) { | |||
1242 | if (view->container->node.destroying) { | 1066 | if (view->container->node.destroying) { |
1243 | return false; | 1067 | return false; |
1244 | } | 1068 | } |
1245 | struct sway_workspace *workspace = view->container->workspace; | 1069 | struct sway_workspace *workspace = view->container->pending.workspace; |
1246 | if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { | 1070 | if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
1247 | bool fs_global_descendant = false; | 1071 | bool fs_global_descendant = false; |
1248 | struct sway_container *parent = view->container->parent; | 1072 | struct sway_container *parent = view->container->pending.parent; |
1249 | while (parent) { | 1073 | while (parent) { |
1250 | if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1074 | if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1251 | fs_global_descendant = true; | 1075 | fs_global_descendant = true; |
1252 | } | 1076 | } |
1253 | parent = parent->parent; | 1077 | parent = parent->pending.parent; |
1254 | } | 1078 | } |
1255 | if (!fs_global_descendant) { | 1079 | if (!fs_global_descendant) { |
1256 | return false; | 1080 | return false; |
@@ -1268,13 +1092,13 @@ bool view_is_visible(struct sway_view *view) { | |||
1268 | enum sway_container_layout layout = container_parent_layout(con); | 1092 | enum sway_container_layout layout = container_parent_layout(con); |
1269 | if ((layout == L_TABBED || layout == L_STACKED) | 1093 | if ((layout == L_TABBED || layout == L_STACKED) |
1270 | && !container_is_floating(con)) { | 1094 | && !container_is_floating(con)) { |
1271 | struct sway_node *parent = con->parent ? | 1095 | struct sway_node *parent = con->pending.parent ? |
1272 | &con->parent->node : &con->workspace->node; | 1096 | &con->pending.parent->node : &con->pending.workspace->node; |
1273 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { | 1097 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { |
1274 | return false; | 1098 | return false; |
1275 | } | 1099 | } |
1276 | } | 1100 | } |
1277 | con = con->parent; | 1101 | con = con->pending.parent; |
1278 | } | 1102 | } |
1279 | // Check view isn't hidden by another fullscreen view | 1103 | // Check view isn't hidden by another fullscreen view |
1280 | struct sway_container *fs = root->fullscreen_global ? | 1104 | struct sway_container *fs = root->fullscreen_global ? |
@@ -1296,6 +1120,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1296 | return; | 1120 | return; |
1297 | } | 1121 | } |
1298 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1122 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1123 | container_update_itself_and_parents(view->container); | ||
1299 | } else { | 1124 | } else { |
1300 | view->urgent = (struct timespec){ 0 }; | 1125 | view->urgent = (struct timespec){ 0 }; |
1301 | if (view->urgent_timer) { | 1126 | if (view->urgent_timer) { |
@@ -1303,12 +1128,11 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1303 | view->urgent_timer = NULL; | 1128 | view->urgent_timer = NULL; |
1304 | } | 1129 | } |
1305 | } | 1130 | } |
1306 | container_damage_whole(view->container); | ||
1307 | 1131 | ||
1308 | ipc_event_window(view->container, "urgent"); | 1132 | ipc_event_window(view->container, "urgent"); |
1309 | 1133 | ||
1310 | if (!container_is_scratchpad_hidden(view->container)) { | 1134 | if (!container_is_scratchpad_hidden(view->container)) { |
1311 | workspace_detect_urgent(view->container->workspace); | 1135 | workspace_detect_urgent(view->container->pending.workspace); |
1312 | } | 1136 | } |
1313 | } | 1137 | } |
1314 | 1138 | ||
@@ -1317,40 +1141,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1317 | } | 1141 | } |
1318 | 1142 | ||
1319 | void view_remove_saved_buffer(struct sway_view *view) { | 1143 | void view_remove_saved_buffer(struct sway_view *view) { |
1320 | if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { | 1144 | if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { |
1321 | return; | 1145 | return; |
1322 | } | 1146 | } |
1323 | struct sway_saved_buffer *saved_buf, *tmp; | 1147 | |
1324 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1148 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1325 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1149 | view->saved_surface_tree = NULL; |
1326 | wl_list_remove(&saved_buf->link); | 1150 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1327 | free(saved_buf); | ||
1328 | } | ||
1329 | } | 1151 | } |
1330 | 1152 | ||
1331 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1153 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1332 | int sx, int sy, void *data) { | 1154 | int sx, int sy, void *data) { |
1333 | struct sway_view *view = data; | 1155 | struct wlr_scene_tree *tree = data; |
1334 | 1156 | ||
1335 | if (surface && wlr_surface_has_buffer(surface)) { | 1157 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1336 | wlr_buffer_lock(&surface->buffer->base); | 1158 | if (!sbuf) { |
1337 | struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); | 1159 | sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); |
1338 | saved_buffer->buffer = surface->buffer; | 1160 | return; |
1339 | saved_buffer->width = surface->current.width; | ||
1340 | saved_buffer->height = surface->current.height; | ||
1341 | saved_buffer->x = sx; | ||
1342 | saved_buffer->y = sy; | ||
1343 | saved_buffer->transform = surface->current.transform; | ||
1344 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1345 | wl_list_insert(&view->saved_buffers, &saved_buffer->link); | ||
1346 | } | 1161 | } |
1162 | |||
1163 | wlr_scene_buffer_set_dest_size(sbuf, | ||
1164 | buffer->dst_width, buffer->dst_height); | ||
1165 | wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); | ||
1166 | wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); | ||
1167 | wlr_scene_node_set_position(&sbuf->node, sx, sy); | ||
1168 | wlr_scene_buffer_set_transform(sbuf, buffer->transform); | ||
1169 | wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); | ||
1347 | } | 1170 | } |
1348 | 1171 | ||
1349 | void view_save_buffer(struct sway_view *view) { | 1172 | void view_save_buffer(struct sway_view *view) { |
1350 | if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { | 1173 | if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { |
1351 | view_remove_saved_buffer(view); | 1174 | view_remove_saved_buffer(view); |
1352 | } | 1175 | } |
1353 | view_for_each_surface(view, view_save_buffer_iterator, view); | 1176 | |
1177 | view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); | ||
1178 | if (!view->saved_surface_tree) { | ||
1179 | sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); | ||
1180 | return; | ||
1181 | } | ||
1182 | |||
1183 | // Enable and disable the saved surface tree like so to atomitaclly update | ||
1184 | // the tree. This will prevent over damaging or other weirdness. | ||
1185 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); | ||
1186 | |||
1187 | wlr_scene_node_for_each_buffer(&view->content_tree->node, | ||
1188 | view_save_buffer_iterator, view->saved_surface_tree); | ||
1189 | |||
1190 | wlr_scene_node_set_enabled(&view->content_tree->node, false); | ||
1191 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); | ||
1354 | } | 1192 | } |
1355 | 1193 | ||
1356 | bool view_is_transient_for(struct sway_view *child, | 1194 | bool view_is_transient_for(struct sway_view *child, |
@@ -1358,3 +1196,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1358 | return child->impl->is_transient_for && | 1196 | return child->impl->is_transient_for && |
1359 | child->impl->is_transient_for(child, ancestor); | 1197 | child->impl->is_transient_for(child, ancestor); |
1360 | } | 1198 | } |
1199 | |||
1200 | static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, | ||
1201 | int x, int y, void *data) { | ||
1202 | struct timespec *when = data; | ||
1203 | wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); | ||
1204 | } | ||
1205 | |||
1206 | void view_send_frame_done(struct sway_view *view) { | ||
1207 | struct timespec when; | ||
1208 | clock_gettime(CLOCK_MONOTONIC, &when); | ||
1209 | |||
1210 | struct wlr_scene_node *node; | ||
1211 | wl_list_for_each(node, &view->content_tree->children, link) { | ||
1212 | wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); | ||
1213 | } | ||
1214 | } | ||