diff options
Diffstat (limited to 'sway/tree/container.c')
-rw-r--r-- | sway/tree/container.c | 1306 |
1 files changed, 661 insertions, 645 deletions
diff --git a/sway/tree/container.c b/sway/tree/container.c index 04ef965f..9224b4fb 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -1,25 +1,20 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <drm_fourcc.h> | 2 | #include <drm_fourcc.h> |
4 | #include <stdint.h> | 3 | #include <stdint.h> |
5 | #include <stdlib.h> | 4 | #include <stdlib.h> |
6 | #include <string.h> | ||
7 | #include <strings.h> | ||
8 | #include <sys/stat.h> | ||
9 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
10 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | 7 | #include <wlr/types/wlr_linux_dmabuf_v1.h> |
11 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
12 | #include <wlr/types/wlr_subcompositor.h> | 9 | #include <wlr/types/wlr_subcompositor.h> |
13 | #include <wlr/render/drm_format_set.h> | ||
14 | #include "linux-dmabuf-unstable-v1-protocol.h" | 10 | #include "linux-dmabuf-unstable-v1-protocol.h" |
15 | #include "cairo_util.h" | ||
16 | #include "pango.h" | ||
17 | #include "sway/config.h" | 11 | #include "sway/config.h" |
18 | #include "sway/desktop.h" | ||
19 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
20 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
21 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
22 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
16 | #include "sway/scene_descriptor.h" | ||
17 | #include "sway/sway_text_node.h" | ||
23 | #include "sway/output.h" | 18 | #include "sway/output.h" |
24 | #include "sway/server.h" | 19 | #include "sway/server.h" |
25 | #include "sway/tree/arrange.h" | 20 | #include "sway/tree/arrange.h" |
@@ -30,6 +25,53 @@ | |||
30 | #include "log.h" | 25 | #include "log.h" |
31 | #include "stringop.h" | 26 | #include "stringop.h" |
32 | 27 | ||
28 | static void handle_output_enter( | ||
29 | struct wl_listener *listener, void *data) { | ||
30 | struct sway_container *con = wl_container_of( | ||
31 | listener, con, output_enter); | ||
32 | struct wlr_scene_output *output = data; | ||
33 | |||
34 | if (con->view->foreign_toplevel) { | ||
35 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
36 | con->view->foreign_toplevel, output->output); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | static void handle_output_leave( | ||
41 | struct wl_listener *listener, void *data) { | ||
42 | struct sway_container *con = wl_container_of( | ||
43 | listener, con, output_leave); | ||
44 | struct wlr_scene_output *output = data; | ||
45 | |||
46 | if (con->view->foreign_toplevel) { | ||
47 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
48 | con->view->foreign_toplevel, output->output); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | static bool handle_point_accepts_input( | ||
53 | struct wlr_scene_buffer *buffer, double *x, double *y) { | ||
54 | return false; | ||
55 | } | ||
56 | |||
57 | static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, | ||
58 | bool *failed) { | ||
59 | if (*failed) { | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | // just pass in random values. These will be overwritten when | ||
64 | // they need to be used. | ||
65 | struct wlr_scene_rect *rect = wlr_scene_rect_create( | ||
66 | parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); | ||
67 | if (!rect) { | ||
68 | sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect"); | ||
69 | *failed = true; | ||
70 | } | ||
71 | |||
72 | return rect; | ||
73 | } | ||
74 | |||
33 | struct sway_container *container_create(struct sway_view *view) { | 75 | struct sway_container *container_create(struct sway_view *view) { |
34 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); | 76 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); |
35 | if (!c) { | 77 | if (!c) { |
@@ -37,23 +79,411 @@ struct sway_container *container_create(struct sway_view *view) { | |||
37 | return NULL; | 79 | return NULL; |
38 | } | 80 | } |
39 | node_init(&c->node, N_CONTAINER, c); | 81 | node_init(&c->node, N_CONTAINER, c); |
40 | c->pending.layout = L_NONE; | 82 | |
41 | c->view = view; | 83 | // Container tree structure |
42 | c->alpha = 1.0f; | 84 | // - scene tree |
85 | // - title bar | ||
86 | // - border | ||
87 | // - background | ||
88 | // - title text | ||
89 | // - marks text | ||
90 | // - border | ||
91 | // - border top/bottom/left/right | ||
92 | // - content_tree (we put the content node here so when we disable the | ||
93 | // border everything gets disabled. We only render the content iff there | ||
94 | // is a border as well) | ||
95 | // - buffer used for output enter/leave events for foreign_toplevel | ||
96 | bool failed = false; | ||
97 | c->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
98 | |||
99 | c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed); | ||
100 | c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed); | ||
101 | c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed); | ||
102 | |||
103 | // for opacity purposes we need to carfully create the scene such that | ||
104 | // none of our rect nodes as well as text buffers don't overlap. To do | ||
105 | // this we have to create rects such that they go around text buffers | ||
106 | for (int i = 0; i < 4; i++) { | ||
107 | alloc_rect_node(c->title_bar.border, &failed); | ||
108 | } | ||
109 | |||
110 | for (int i = 0; i < 5; i++) { | ||
111 | alloc_rect_node(c->title_bar.background, &failed); | ||
112 | } | ||
113 | |||
114 | c->border.tree = alloc_scene_tree(c->scene_tree, &failed); | ||
115 | c->content_tree = alloc_scene_tree(c->border.tree, &failed); | ||
116 | |||
117 | if (view) { | ||
118 | // only containers with views can have borders | ||
119 | c->border.top = alloc_rect_node(c->border.tree, &failed); | ||
120 | c->border.bottom = alloc_rect_node(c->border.tree, &failed); | ||
121 | c->border.left = alloc_rect_node(c->border.tree, &failed); | ||
122 | c->border.right = alloc_rect_node(c->border.tree, &failed); | ||
123 | |||
124 | c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); | ||
125 | if (!c->output_handler) { | ||
126 | sway_log(SWAY_ERROR, "Failed to allocate a scene node"); | ||
127 | failed = true; | ||
128 | } | ||
129 | |||
130 | if (!failed) { | ||
131 | c->output_enter.notify = handle_output_enter; | ||
132 | wl_signal_add(&c->output_handler->events.output_enter, | ||
133 | &c->output_enter); | ||
134 | c->output_leave.notify = handle_output_leave; | ||
135 | wl_signal_add(&c->output_handler->events.output_leave, | ||
136 | &c->output_leave); | ||
137 | c->output_handler->point_accepts_input = handle_point_accepts_input; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | if (!failed && !scene_descriptor_assign(&c->scene_tree->node, | ||
142 | SWAY_SCENE_DESC_CONTAINER, c)) { | ||
143 | failed = true; | ||
144 | } | ||
145 | |||
146 | if (failed) { | ||
147 | wlr_scene_node_destroy(&c->scene_tree->node); | ||
148 | free(c); | ||
149 | return NULL; | ||
150 | } | ||
43 | 151 | ||
44 | if (!view) { | 152 | if (!view) { |
45 | c->pending.children = create_list(); | 153 | c->pending.children = create_list(); |
46 | c->current.children = create_list(); | 154 | c->current.children = create_list(); |
47 | } | 155 | } |
156 | |||
157 | c->pending.layout = L_NONE; | ||
158 | c->view = view; | ||
159 | c->alpha = 1.0f; | ||
48 | c->marks = create_list(); | 160 | c->marks = create_list(); |
49 | c->outputs = create_list(); | ||
50 | 161 | ||
51 | wl_signal_init(&c->events.destroy); | 162 | wl_signal_init(&c->events.destroy); |
52 | wl_signal_emit(&root->events.new_node, &c->node); | 163 | wl_signal_emit_mutable(&root->events.new_node, &c->node); |
164 | |||
165 | container_update(c); | ||
53 | 166 | ||
54 | return c; | 167 | return c; |
55 | } | 168 | } |
56 | 169 | ||
170 | static bool container_is_focused(struct sway_container *con, void *data) { | ||
171 | return con->current.focused; | ||
172 | } | ||
173 | |||
174 | static bool container_has_focused_child(struct sway_container *con) { | ||
175 | return container_find_child(con, container_is_focused, NULL); | ||
176 | } | ||
177 | |||
178 | static bool container_is_current_parent_focused(struct sway_container *con) { | ||
179 | if (con->current.parent) { | ||
180 | struct sway_container *parent = con->current.parent; | ||
181 | return parent->current.focused || container_is_current_parent_focused(parent); | ||
182 | } else if (con->current.workspace) { | ||
183 | struct sway_workspace *ws = con->current.workspace; | ||
184 | return ws->current.focused; | ||
185 | } | ||
186 | |||
187 | return false; | ||
188 | } | ||
189 | |||
190 | static struct border_colors *container_get_current_colors( | ||
191 | struct sway_container *con) { | ||
192 | struct border_colors *colors; | ||
193 | |||
194 | bool urgent = con->view ? | ||
195 | view_is_urgent(con->view) : container_has_urgent_child(con); | ||
196 | struct sway_container *active_child; | ||
197 | |||
198 | if (con->current.parent) { | ||
199 | active_child = con->current.parent->current.focused_inactive_child; | ||
200 | } else if (con->current.workspace) { | ||
201 | active_child = con->current.workspace->current.focused_inactive_child; | ||
202 | } else { | ||
203 | active_child = NULL; | ||
204 | } | ||
205 | |||
206 | if (urgent) { | ||
207 | colors = &config->border_colors.urgent; | ||
208 | } else if (con->current.focused || container_is_current_parent_focused(con)) { | ||
209 | colors = &config->border_colors.focused; | ||
210 | } else if (config->has_focused_tab_title && container_has_focused_child(con)) { | ||
211 | colors = &config->border_colors.focused_tab_title; | ||
212 | } else if (con == active_child) { | ||
213 | colors = &config->border_colors.focused_inactive; | ||
214 | } else { | ||
215 | colors = &config->border_colors.unfocused; | ||
216 | } | ||
217 | |||
218 | return colors; | ||
219 | } | ||
220 | |||
221 | static bool container_is_current_floating(struct sway_container *container) { | ||
222 | if (!container->current.parent && container->current.workspace && | ||
223 | list_find(container->current.workspace->floating, container) != -1) { | ||
224 | return true; | ||
225 | } | ||
226 | if (container->scratchpad) { | ||
227 | return true; | ||
228 | } | ||
229 | return false; | ||
230 | } | ||
231 | |||
232 | // scene rect wants premultiplied colors | ||
233 | static void scene_rect_set_color(struct wlr_scene_rect *rect, | ||
234 | const float color[4], float opacity) { | ||
235 | const float premultiplied[] = { | ||
236 | color[0] * color[3] * opacity, | ||
237 | color[1] * color[3] * opacity, | ||
238 | color[2] * color[3] * opacity, | ||
239 | color[3] * opacity, | ||
240 | }; | ||
241 | |||
242 | wlr_scene_rect_set_color(rect, premultiplied); | ||
243 | } | ||
244 | |||
245 | void container_update(struct sway_container *con) { | ||
246 | struct border_colors *colors = container_get_current_colors(con); | ||
247 | list_t *siblings = NULL; | ||
248 | enum sway_container_layout layout = L_NONE; | ||
249 | float alpha = con->alpha; | ||
250 | |||
251 | if (con->current.parent) { | ||
252 | siblings = con->current.parent->current.children; | ||
253 | layout = con->current.parent->current.layout; | ||
254 | } else if (con->current.workspace) { | ||
255 | siblings = con->current.workspace->current.tiling; | ||
256 | layout = con->current.workspace->current.layout; | ||
257 | } | ||
258 | |||
259 | float bottom[4], right[4]; | ||
260 | memcpy(bottom, colors->child_border, sizeof(bottom)); | ||
261 | memcpy(right, colors->child_border, sizeof(right)); | ||
262 | |||
263 | if (!container_is_current_floating(con) && siblings && siblings->length == 1) { | ||
264 | if (layout == L_HORIZ) { | ||
265 | memcpy(right, colors->indicator, sizeof(right)); | ||
266 | } else if (layout == L_VERT) { | ||
267 | memcpy(bottom, colors->indicator, sizeof(bottom)); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | struct wlr_scene_node *node; | ||
272 | wl_list_for_each(node, &con->title_bar.border->children, link) { | ||
273 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
274 | scene_rect_set_color(rect, colors->border, alpha); | ||
275 | } | ||
276 | |||
277 | wl_list_for_each(node, &con->title_bar.background->children, link) { | ||
278 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
279 | scene_rect_set_color(rect, colors->background, alpha); | ||
280 | } | ||
281 | |||
282 | if (con->view) { | ||
283 | scene_rect_set_color(con->border.top, colors->child_border, alpha); | ||
284 | scene_rect_set_color(con->border.bottom, bottom, alpha); | ||
285 | scene_rect_set_color(con->border.left, colors->child_border, alpha); | ||
286 | scene_rect_set_color(con->border.right, right, alpha); | ||
287 | } | ||
288 | |||
289 | if (con->title_bar.title_text) { | ||
290 | sway_text_node_set_color(con->title_bar.title_text, colors->text); | ||
291 | sway_text_node_set_background(con->title_bar.title_text, colors->background); | ||
292 | } | ||
293 | |||
294 | if (con->title_bar.marks_text) { | ||
295 | sway_text_node_set_color(con->title_bar.marks_text, colors->text); | ||
296 | sway_text_node_set_background(con->title_bar.marks_text, colors->background); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | void container_update_itself_and_parents(struct sway_container *con) { | ||
301 | container_update(con); | ||
302 | |||
303 | if (con->current.parent) { | ||
304 | container_update_itself_and_parents(con->current.parent); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) { | ||
309 | int len; | ||
310 | const pixman_box32_t *rects = pixman_region32_rectangles(region, &len); | ||
311 | |||
312 | wlr_scene_node_set_enabled(&tree->node, len > 0); | ||
313 | if (len == 0) { | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | int i = 0; | ||
318 | struct wlr_scene_node *node; | ||
319 | wl_list_for_each(node, &tree->children, link) { | ||
320 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
321 | wlr_scene_node_set_enabled(&rect->node, i < len); | ||
322 | |||
323 | if (i < len) { | ||
324 | const pixman_box32_t *box = &rects[i++]; | ||
325 | wlr_scene_node_set_position(&rect->node, box->x1, box->y1); | ||
326 | wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1); | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | void container_arrange_title_bar(struct sway_container *con) { | ||
332 | enum alignment title_align = config->title_align; | ||
333 | int marks_buffer_width = 0; | ||
334 | int width = con->title_width; | ||
335 | int height = container_titlebar_height(); | ||
336 | |||
337 | pixman_region32_t text_area; | ||
338 | pixman_region32_init(&text_area); | ||
339 | |||
340 | if (con->title_bar.marks_text) { | ||
341 | struct sway_text_node *node = con->title_bar.marks_text; | ||
342 | marks_buffer_width = node->width; | ||
343 | |||
344 | int h_padding; | ||
345 | if (title_align == ALIGN_RIGHT) { | ||
346 | h_padding = config->titlebar_h_padding; | ||
347 | } else { | ||
348 | h_padding = width - config->titlebar_h_padding - marks_buffer_width; | ||
349 | } | ||
350 | |||
351 | h_padding = MAX(h_padding, 0); | ||
352 | |||
353 | int alloc_width = MIN((int)node->width, | ||
354 | width - h_padding - config->titlebar_h_padding); | ||
355 | sway_text_node_set_max_width(node, alloc_width); | ||
356 | wlr_scene_node_set_position(node->node, | ||
357 | h_padding, (height - node->height) >> 1); | ||
358 | |||
359 | pixman_region32_union_rect(&text_area, &text_area, | ||
360 | node->node->x, node->node->y, alloc_width, node->height); | ||
361 | } | ||
362 | |||
363 | if (con->title_bar.title_text) { | ||
364 | struct sway_text_node *node = con->title_bar.title_text; | ||
365 | |||
366 | int h_padding; | ||
367 | if (title_align == ALIGN_RIGHT) { | ||
368 | h_padding = width - config->titlebar_h_padding - node->width; | ||
369 | } else if (title_align == ALIGN_CENTER) { | ||
370 | h_padding = ((int)width - marks_buffer_width - node->width) >> 1; | ||
371 | } else { | ||
372 | h_padding = config->titlebar_h_padding; | ||
373 | } | ||
374 | |||
375 | h_padding = MAX(h_padding, 0); | ||
376 | |||
377 | int alloc_width = MIN((int) node->width, | ||
378 | width - h_padding - config->titlebar_h_padding); | ||
379 | sway_text_node_set_max_width(node, alloc_width); | ||
380 | wlr_scene_node_set_position(node->node, | ||
381 | h_padding, (height - node->height) >> 1); | ||
382 | |||
383 | pixman_region32_union_rect(&text_area, &text_area, | ||
384 | node->node->x, node->node->y, alloc_width, node->height); | ||
385 | } | ||
386 | |||
387 | // silence pixman errors | ||
388 | if (width <= 0 || height <= 0) { | ||
389 | pixman_region32_fini(&text_area); | ||
390 | return; | ||
391 | } | ||
392 | |||
393 | pixman_region32_t background, border; | ||
394 | |||
395 | int thickness = config->titlebar_border_thickness; | ||
396 | pixman_region32_init_rect(&background, | ||
397 | thickness, thickness, | ||
398 | width - thickness * 2, height - thickness * 2); | ||
399 | pixman_region32_init_rect(&border, 0, 0, width, height); | ||
400 | pixman_region32_subtract(&border, &border, &background); | ||
401 | |||
402 | pixman_region32_subtract(&background, &background, &text_area); | ||
403 | pixman_region32_fini(&text_area); | ||
404 | |||
405 | update_rect_list(con->title_bar.background, &background); | ||
406 | pixman_region32_fini(&background); | ||
407 | |||
408 | update_rect_list(con->title_bar.border, &border); | ||
409 | pixman_region32_fini(&border); | ||
410 | |||
411 | container_update(con); | ||
412 | } | ||
413 | |||
414 | void container_update_marks(struct sway_container *con) { | ||
415 | char *buffer = NULL; | ||
416 | |||
417 | if (config->show_marks && con->marks->length) { | ||
418 | size_t len = 0; | ||
419 | for (int i = 0; i < con->marks->length; ++i) { | ||
420 | char *mark = con->marks->items[i]; | ||
421 | if (mark[0] != '_') { | ||
422 | len += strlen(mark) + 2; | ||
423 | } | ||
424 | } | ||
425 | buffer = calloc(len + 1, 1); | ||
426 | char *part = malloc(len + 1); | ||
427 | |||
428 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
429 | free(buffer); | ||
430 | return; | ||
431 | } | ||
432 | |||
433 | for (int i = 0; i < con->marks->length; ++i) { | ||
434 | char *mark = con->marks->items[i]; | ||
435 | if (mark[0] != '_') { | ||
436 | snprintf(part, len + 1, "[%s]", mark); | ||
437 | strcat(buffer, part); | ||
438 | } | ||
439 | } | ||
440 | free(part); | ||
441 | } | ||
442 | |||
443 | if (!buffer) { | ||
444 | if (con->title_bar.marks_text) { | ||
445 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
446 | con->title_bar.marks_text = NULL; | ||
447 | } | ||
448 | } else if (!con->title_bar.marks_text) { | ||
449 | struct border_colors *colors = container_get_current_colors(con); | ||
450 | |||
451 | con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, | ||
452 | buffer, colors->text, false); | ||
453 | } else { | ||
454 | sway_text_node_set_text(con->title_bar.marks_text, buffer); | ||
455 | } | ||
456 | |||
457 | container_arrange_title_bar(con); | ||
458 | free(buffer); | ||
459 | } | ||
460 | |||
461 | void container_update_title_bar(struct sway_container *con) { | ||
462 | if (!con->formatted_title) { | ||
463 | return; | ||
464 | } | ||
465 | |||
466 | struct border_colors *colors = container_get_current_colors(con); | ||
467 | |||
468 | if (con->title_bar.title_text) { | ||
469 | wlr_scene_node_destroy(con->title_bar.title_text->node); | ||
470 | con->title_bar.title_text = NULL; | ||
471 | } | ||
472 | |||
473 | con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, | ||
474 | con->formatted_title, colors->text, config->pango_markup); | ||
475 | |||
476 | // we always have to remake these text buffers completely for text font | ||
477 | // changes etc... | ||
478 | if (con->title_bar.marks_text) { | ||
479 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
480 | con->title_bar.marks_text = NULL; | ||
481 | } | ||
482 | |||
483 | container_update_marks(con); | ||
484 | container_arrange_title_bar(con); | ||
485 | } | ||
486 | |||
57 | void container_destroy(struct sway_container *con) { | 487 | void container_destroy(struct sway_container *con) { |
58 | if (!sway_assert(con->node.destroying, | 488 | if (!sway_assert(con->node.destroying, |
59 | "Tried to free container which wasn't marked as destroying")) { | 489 | "Tried to free container which wasn't marked as destroying")) { |
@@ -65,29 +495,21 @@ void container_destroy(struct sway_container *con) { | |||
65 | } | 495 | } |
66 | free(con->title); | 496 | free(con->title); |
67 | free(con->formatted_title); | 497 | free(con->formatted_title); |
68 | wlr_texture_destroy(con->title_focused); | ||
69 | wlr_texture_destroy(con->title_focused_inactive); | ||
70 | wlr_texture_destroy(con->title_unfocused); | ||
71 | wlr_texture_destroy(con->title_urgent); | ||
72 | wlr_texture_destroy(con->title_focused_tab_title); | ||
73 | list_free(con->pending.children); | 498 | list_free(con->pending.children); |
74 | list_free(con->current.children); | 499 | list_free(con->current.children); |
75 | list_free(con->outputs); | ||
76 | 500 | ||
77 | list_free_items_and_destroy(con->marks); | 501 | list_free_items_and_destroy(con->marks); |
78 | wlr_texture_destroy(con->marks_focused); | ||
79 | wlr_texture_destroy(con->marks_focused_inactive); | ||
80 | wlr_texture_destroy(con->marks_unfocused); | ||
81 | wlr_texture_destroy(con->marks_urgent); | ||
82 | wlr_texture_destroy(con->marks_focused_tab_title); | ||
83 | 502 | ||
84 | if (con->view && con->view->container == con) { | 503 | if (con->view && con->view->container == con) { |
85 | con->view->container = NULL; | 504 | con->view->container = NULL; |
505 | wlr_scene_node_destroy(&con->output_handler->node); | ||
86 | if (con->view->destroying) { | 506 | if (con->view->destroying) { |
87 | view_destroy(con->view); | 507 | view_destroy(con->view); |
88 | } | 508 | } |
89 | } | 509 | } |
90 | 510 | ||
511 | scene_node_disown_children(con->content_tree); | ||
512 | wlr_scene_node_destroy(&con->scene_tree->node); | ||
91 | free(con); | 513 | free(con); |
92 | } | 514 | } |
93 | 515 | ||
@@ -104,7 +526,7 @@ void container_begin_destroy(struct sway_container *con) { | |||
104 | container_fullscreen_disable(con); | 526 | container_fullscreen_disable(con); |
105 | } | 527 | } |
106 | 528 | ||
107 | wl_signal_emit(&con->node.events.destroy, &con->node); | 529 | wl_signal_emit_mutable(&con->node.events.destroy, &con->node); |
108 | 530 | ||
109 | container_end_mouse_operation(con); | 531 | container_end_mouse_operation(con); |
110 | 532 | ||
@@ -174,265 +596,6 @@ struct sway_container *container_find_child(struct sway_container *container, | |||
174 | return NULL; | 596 | return NULL; |
175 | } | 597 | } |
176 | 598 | ||
177 | static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, | ||
178 | struct wlr_surface **surface, double *sx, double *sy) { | ||
179 | if (!sway_assert(con->view, "Expected a view")) { | ||
180 | return NULL; | ||
181 | } | ||
182 | struct sway_view *view = con->view; | ||
183 | double view_sx = lx - con->surface_x + view->geometry.x; | ||
184 | double view_sy = ly - con->surface_y + view->geometry.y; | ||
185 | |||
186 | double _sx, _sy; | ||
187 | struct wlr_surface *_surface = NULL; | ||
188 | switch (view->type) { | ||
189 | #if HAVE_XWAYLAND | ||
190 | case SWAY_VIEW_XWAYLAND: | ||
191 | _surface = wlr_surface_surface_at(view->surface, | ||
192 | view_sx, view_sy, &_sx, &_sy); | ||
193 | break; | ||
194 | #endif | ||
195 | case SWAY_VIEW_XDG_SHELL: | ||
196 | _surface = wlr_xdg_surface_surface_at( | ||
197 | view->wlr_xdg_toplevel->base, | ||
198 | view_sx, view_sy, &_sx, &_sy); | ||
199 | break; | ||
200 | } | ||
201 | if (_surface) { | ||
202 | *sx = _sx; | ||
203 | *sy = _sy; | ||
204 | *surface = _surface; | ||
205 | return con; | ||
206 | } | ||
207 | return NULL; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * container_at for a container with layout L_TABBED. | ||
212 | */ | ||
213 | static struct sway_container *container_at_tabbed(struct sway_node *parent, | ||
214 | double lx, double ly, | ||
215 | struct wlr_surface **surface, double *sx, double *sy) { | ||
216 | struct wlr_box box; | ||
217 | node_get_box(parent, &box); | ||
218 | if (lx < box.x || lx > box.x + box.width || | ||
219 | ly < box.y || ly > box.y + box.height) { | ||
220 | return NULL; | ||
221 | } | ||
222 | struct sway_seat *seat = input_manager_current_seat(); | ||
223 | list_t *children = node_get_children(parent); | ||
224 | if (!children->length) { | ||
225 | return NULL; | ||
226 | } | ||
227 | |||
228 | // Tab titles | ||
229 | int title_height = container_titlebar_height(); | ||
230 | if (ly < box.y + title_height) { | ||
231 | int tab_width = box.width / children->length; | ||
232 | int child_index = (lx - box.x) / tab_width; | ||
233 | if (child_index >= children->length) { | ||
234 | child_index = children->length - 1; | ||
235 | } | ||
236 | struct sway_container *child = children->items[child_index]; | ||
237 | return child; | ||
238 | } | ||
239 | |||
240 | // Surfaces | ||
241 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
242 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * container_at for a container with layout L_STACKED. | ||
247 | */ | ||
248 | static struct sway_container *container_at_stacked(struct sway_node *parent, | ||
249 | double lx, double ly, | ||
250 | struct wlr_surface **surface, double *sx, double *sy) { | ||
251 | struct wlr_box box; | ||
252 | node_get_box(parent, &box); | ||
253 | if (lx < box.x || lx > box.x + box.width || | ||
254 | ly < box.y || ly > box.y + box.height) { | ||
255 | return NULL; | ||
256 | } | ||
257 | struct sway_seat *seat = input_manager_current_seat(); | ||
258 | list_t *children = node_get_children(parent); | ||
259 | |||
260 | // Title bars | ||
261 | int title_height = container_titlebar_height(); | ||
262 | if (title_height > 0) { | ||
263 | int child_index = (ly - box.y) / title_height; | ||
264 | if (child_index < children->length) { | ||
265 | struct sway_container *child = children->items[child_index]; | ||
266 | return child; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | // Surfaces | ||
271 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
272 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * container_at for a container with layout L_HORIZ or L_VERT. | ||
277 | */ | ||
278 | static struct sway_container *container_at_linear(struct sway_node *parent, | ||
279 | double lx, double ly, | ||
280 | struct wlr_surface **surface, double *sx, double *sy) { | ||
281 | list_t *children = node_get_children(parent); | ||
282 | for (int i = 0; i < children->length; ++i) { | ||
283 | struct sway_container *child = children->items[i]; | ||
284 | struct sway_container *container = | ||
285 | tiling_container_at(&child->node, lx, ly, surface, sx, sy); | ||
286 | if (container) { | ||
287 | return container; | ||
288 | } | ||
289 | } | ||
290 | return NULL; | ||
291 | } | ||
292 | |||
293 | static struct sway_container *floating_container_at(double lx, double ly, | ||
294 | struct wlr_surface **surface, double *sx, double *sy) { | ||
295 | // For outputs with floating containers that overhang the output bounds, | ||
296 | // those at the end of the output list appear on top of floating | ||
297 | // containers from other outputs, so iterate the list in reverse. | ||
298 | for (int i = root->outputs->length - 1; i >= 0; --i) { | ||
299 | struct sway_output *output = root->outputs->items[i]; | ||
300 | for (int j = 0; j < output->workspaces->length; ++j) { | ||
301 | struct sway_workspace *ws = output->workspaces->items[j]; | ||
302 | if (!workspace_is_visible(ws)) { | ||
303 | continue; | ||
304 | } | ||
305 | // Items at the end of the list are on top, so iterate the list in | ||
306 | // reverse. | ||
307 | for (int k = ws->floating->length - 1; k >= 0; --k) { | ||
308 | struct sway_container *floater = ws->floating->items[k]; | ||
309 | struct sway_container *container = | ||
310 | tiling_container_at(&floater->node, lx, ly, surface, sx, sy); | ||
311 | if (container) { | ||
312 | return container; | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | } | ||
317 | return NULL; | ||
318 | } | ||
319 | |||
320 | static struct sway_container *view_container_content_at(struct sway_node *parent, | ||
321 | double lx, double ly, | ||
322 | struct wlr_surface **surface, double *sx, double *sy) { | ||
323 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
324 | return NULL; | ||
325 | } | ||
326 | |||
327 | struct sway_container *container = parent->sway_container; | ||
328 | struct wlr_box box = { | ||
329 | .x = container->pending.content_x, | ||
330 | .y = container->pending.content_y, | ||
331 | .width = container->pending.content_width, | ||
332 | .height = container->pending.content_height, | ||
333 | }; | ||
334 | |||
335 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
336 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
337 | return container; | ||
338 | } | ||
339 | |||
340 | return NULL; | ||
341 | } | ||
342 | |||
343 | static struct sway_container *view_container_at(struct sway_node *parent, | ||
344 | double lx, double ly, | ||
345 | struct wlr_surface **surface, double *sx, double *sy) { | ||
346 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
347 | return NULL; | ||
348 | } | ||
349 | |||
350 | struct sway_container *container = parent->sway_container; | ||
351 | struct wlr_box box = { | ||
352 | .x = container->pending.x, | ||
353 | .y = container->pending.y, | ||
354 | .width = container->pending.width, | ||
355 | .height = container->pending.height, | ||
356 | }; | ||
357 | |||
358 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
359 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
360 | return container; | ||
361 | } | ||
362 | |||
363 | return NULL; | ||
364 | } | ||
365 | |||
366 | struct sway_container *tiling_container_at(struct sway_node *parent, | ||
367 | double lx, double ly, | ||
368 | struct wlr_surface **surface, double *sx, double *sy) { | ||
369 | if (node_is_view(parent)) { | ||
370 | return view_container_at(parent, lx, ly, surface, sx, sy); | ||
371 | } | ||
372 | if (!node_get_children(parent)) { | ||
373 | return NULL; | ||
374 | } | ||
375 | switch (node_get_layout(parent)) { | ||
376 | case L_HORIZ: | ||
377 | case L_VERT: | ||
378 | return container_at_linear(parent, lx, ly, surface, sx, sy); | ||
379 | case L_TABBED: | ||
380 | return container_at_tabbed(parent, lx, ly, surface, sx, sy); | ||
381 | case L_STACKED: | ||
382 | return container_at_stacked(parent, lx, ly, surface, sx, sy); | ||
383 | case L_NONE: | ||
384 | return NULL; | ||
385 | } | ||
386 | return NULL; | ||
387 | } | ||
388 | |||
389 | static bool surface_is_popup(struct wlr_surface *surface) { | ||
390 | while (!wlr_surface_is_xdg_surface(surface)) { | ||
391 | if (!wlr_surface_is_subsurface(surface)) { | ||
392 | return false; | ||
393 | } | ||
394 | struct wlr_subsurface *subsurface = | ||
395 | wlr_subsurface_from_wlr_surface(surface); | ||
396 | surface = subsurface->parent; | ||
397 | } | ||
398 | struct wlr_xdg_surface *xdg_surface = | ||
399 | wlr_xdg_surface_from_wlr_surface(surface); | ||
400 | return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; | ||
401 | } | ||
402 | |||
403 | struct sway_container *container_at(struct sway_workspace *workspace, | ||
404 | double lx, double ly, | ||
405 | struct wlr_surface **surface, double *sx, double *sy) { | ||
406 | struct sway_container *c; | ||
407 | |||
408 | struct sway_seat *seat = input_manager_current_seat(); | ||
409 | struct sway_container *focus = seat_get_focused_container(seat); | ||
410 | bool is_floating = focus && container_is_floating_or_child(focus); | ||
411 | // Focused view's popups | ||
412 | if (focus && focus->view) { | ||
413 | c = surface_at_view(focus, lx, ly, surface, sx, sy); | ||
414 | if (c && surface_is_popup(*surface)) { | ||
415 | return c; | ||
416 | } | ||
417 | *surface = NULL; | ||
418 | } | ||
419 | // Floating | ||
420 | if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { | ||
421 | return c; | ||
422 | } | ||
423 | // Tiling (focused) | ||
424 | if (focus && focus->view && !is_floating) { | ||
425 | if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { | ||
426 | return c; | ||
427 | } | ||
428 | } | ||
429 | // Tiling (non-focused) | ||
430 | if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { | ||
431 | return c; | ||
432 | } | ||
433 | return NULL; | ||
434 | } | ||
435 | |||
436 | void container_for_each_child(struct sway_container *container, | 599 | void container_for_each_child(struct sway_container *container, |
437 | void (*f)(struct sway_container *container, void *data), | 600 | void (*f)(struct sway_container *container, void *data), |
438 | void *data) { | 601 | void *data) { |
@@ -478,127 +641,6 @@ bool container_has_ancestor(struct sway_container *descendant, | |||
478 | return false; | 641 | return false; |
479 | } | 642 | } |
480 | 643 | ||
481 | void container_damage_whole(struct sway_container *container) { | ||
482 | for (int i = 0; i < root->outputs->length; ++i) { | ||
483 | struct sway_output *output = root->outputs->items[i]; | ||
484 | output_damage_whole_container(output, container); | ||
485 | } | ||
486 | } | ||
487 | |||
488 | /** | ||
489 | * Return the output which will be used for scale purposes. | ||
490 | * This is the most recently entered output. | ||
491 | */ | ||
492 | struct sway_output *container_get_effective_output(struct sway_container *con) { | ||
493 | if (con->outputs->length == 0) { | ||
494 | return NULL; | ||
495 | } | ||
496 | return con->outputs->items[con->outputs->length - 1]; | ||
497 | } | ||
498 | |||
499 | static void render_titlebar_text_texture(struct sway_output *output, | ||
500 | struct sway_container *con, struct wlr_texture **texture, | ||
501 | struct border_colors *class, bool pango_markup, char *text) { | ||
502 | double scale = output->wlr_output->scale; | ||
503 | int width = 0; | ||
504 | int height = config->font_height * scale; | ||
505 | int baseline; | ||
506 | |||
507 | // We must use a non-nil cairo_t for cairo_set_font_options to work. | ||
508 | // Therefore, we cannot use cairo_create(NULL). | ||
509 | cairo_surface_t *dummy_surface = cairo_image_surface_create( | ||
510 | CAIRO_FORMAT_ARGB32, 0, 0); | ||
511 | cairo_t *c = cairo_create(dummy_surface); | ||
512 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
513 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
514 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
515 | if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | ||
516 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
517 | } else { | ||
518 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
519 | cairo_font_options_set_subpixel_order(fo, | ||
520 | to_cairo_subpixel_order(output->wlr_output->subpixel)); | ||
521 | } | ||
522 | cairo_set_font_options(c, fo); | ||
523 | get_text_size(c, config->font_description, &width, NULL, &baseline, scale, | ||
524 | config->pango_markup, "%s", text); | ||
525 | cairo_surface_destroy(dummy_surface); | ||
526 | cairo_destroy(c); | ||
527 | |||
528 | if (width == 0 || height == 0) { | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | if (height > config->font_height * scale) { | ||
533 | height = config->font_height * scale; | ||
534 | } | ||
535 | |||
536 | cairo_surface_t *surface = cairo_image_surface_create( | ||
537 | CAIRO_FORMAT_ARGB32, width, height); | ||
538 | cairo_status_t status = cairo_surface_status(surface); | ||
539 | if (status != CAIRO_STATUS_SUCCESS) { | ||
540 | sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", | ||
541 | cairo_status_to_string(status)); | ||
542 | return; | ||
543 | } | ||
544 | |||
545 | cairo_t *cairo = cairo_create(surface); | ||
546 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
547 | cairo_set_font_options(cairo, fo); | ||
548 | cairo_font_options_destroy(fo); | ||
549 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
550 | class->background[2], class->background[3]); | ||
551 | cairo_paint(cairo); | ||
552 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
553 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
554 | class->text[2], class->text[3]); | ||
555 | cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); | ||
556 | |||
557 | render_text(cairo, config->font_description, scale, pango_markup, "%s", text); | ||
558 | |||
559 | cairo_surface_flush(surface); | ||
560 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
561 | int stride = cairo_image_surface_get_stride(surface); | ||
562 | struct wlr_renderer *renderer = output->wlr_output->renderer; | ||
563 | *texture = wlr_texture_from_pixels( | ||
564 | renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); | ||
565 | cairo_surface_destroy(surface); | ||
566 | g_object_unref(pango); | ||
567 | cairo_destroy(cairo); | ||
568 | } | ||
569 | |||
570 | static void update_title_texture(struct sway_container *con, | ||
571 | struct wlr_texture **texture, struct border_colors *class) { | ||
572 | struct sway_output *output = container_get_effective_output(con); | ||
573 | if (!output) { | ||
574 | return; | ||
575 | } | ||
576 | if (*texture) { | ||
577 | wlr_texture_destroy(*texture); | ||
578 | *texture = NULL; | ||
579 | } | ||
580 | if (!con->formatted_title) { | ||
581 | return; | ||
582 | } | ||
583 | |||
584 | render_titlebar_text_texture(output, con, texture, class, | ||
585 | config->pango_markup, con->formatted_title); | ||
586 | } | ||
587 | |||
588 | void container_update_title_textures(struct sway_container *container) { | ||
589 | update_title_texture(container, &container->title_focused, | ||
590 | &config->border_colors.focused); | ||
591 | update_title_texture(container, &container->title_focused_inactive, | ||
592 | &config->border_colors.focused_inactive); | ||
593 | update_title_texture(container, &container->title_unfocused, | ||
594 | &config->border_colors.unfocused); | ||
595 | update_title_texture(container, &container->title_urgent, | ||
596 | &config->border_colors.urgent); | ||
597 | update_title_texture(container, &container->title_focused_tab_title, | ||
598 | &config->border_colors.focused_tab_title); | ||
599 | container_damage_whole(container); | ||
600 | } | ||
601 | |||
602 | /** | 644 | /** |
603 | * Calculate and return the length of the tree representation. | 645 | * Calculate and return the length of the tree representation. |
604 | * An example tree representation is: V[Terminal, Firefox] | 646 | * An example tree representation is: V[Terminal, Firefox] |
@@ -664,7 +706,13 @@ void container_update_representation(struct sway_container *con) { | |||
664 | } | 706 | } |
665 | container_build_representation(con->pending.layout, con->pending.children, | 707 | container_build_representation(con->pending.layout, con->pending.children, |
666 | con->formatted_title); | 708 | con->formatted_title); |
667 | container_update_title_textures(con); | 709 | |
710 | if (con->title_bar.title_text) { | ||
711 | sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); | ||
712 | container_arrange_title_bar(con); | ||
713 | } else { | ||
714 | container_update_title_bar(con); | ||
715 | } | ||
668 | } | 716 | } |
669 | if (con->pending.parent) { | 717 | if (con->pending.parent) { |
670 | container_update_representation(con->pending.parent); | 718 | container_update_representation(con->pending.parent); |
@@ -716,6 +764,21 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
716 | 764 | ||
717 | } | 765 | } |
718 | 766 | ||
767 | void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) { | ||
768 | if (!old->width || !old->height) { | ||
769 | // Fall back to centering on the workspace. | ||
770 | container_floating_move_to_center(con); | ||
771 | } else { | ||
772 | int rel_x = con->pending.x - old->x + (con->pending.width / 2); | ||
773 | int rel_y = con->pending.y - old->y + (con->pending.height / 2); | ||
774 | |||
775 | con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); | ||
776 | con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); | ||
777 | |||
778 | sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); | ||
779 | } | ||
780 | } | ||
781 | |||
719 | static void floating_natural_resize(struct sway_container *con) { | 782 | static void floating_natural_resize(struct sway_container *con) { |
720 | int min_width, max_width, min_height, max_height; | 783 | int min_width, max_width, min_height, max_height; |
721 | floating_calculate_constraints(&min_width, &max_width, | 784 | floating_calculate_constraints(&min_width, &max_width, |
@@ -787,11 +850,11 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
787 | int min_width, max_width, min_height, max_height; | 850 | int min_width, max_width, min_height, max_height; |
788 | floating_calculate_constraints(&min_width, &max_width, | 851 | floating_calculate_constraints(&min_width, &max_width, |
789 | &min_height, &max_height); | 852 | &min_height, &max_height); |
790 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); | 853 | struct wlr_box box; |
791 | workspace_get_box(con->pending.workspace, box); | 854 | workspace_get_box(con->pending.workspace, &box); |
792 | 855 | ||
793 | double width = fmax(min_width, fmin(box->width * 0.5, max_width)); | 856 | double width = fmax(min_width, fmin(box.width * 0.5, max_width)); |
794 | double height = fmax(min_height, fmin(box->height * 0.75, max_height)); | 857 | double height = fmax(min_height, fmin(box.height * 0.75, max_height)); |
795 | if (!con->view) { | 858 | if (!con->view) { |
796 | con->pending.width = width; | 859 | con->pending.width = width; |
797 | con->pending.height = height; | 860 | con->pending.height = height; |
@@ -800,8 +863,6 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
800 | con->pending.content_height = height; | 863 | con->pending.content_height = height; |
801 | container_set_geometry_from_content(con); | 864 | container_set_geometry_from_content(con); |
802 | } | 865 | } |
803 | |||
804 | free(box); | ||
805 | } | 866 | } |
806 | 867 | ||
807 | 868 | ||
@@ -937,17 +998,6 @@ bool container_is_floating(struct sway_container *container) { | |||
937 | return false; | 998 | return false; |
938 | } | 999 | } |
939 | 1000 | ||
940 | bool container_is_current_floating(struct sway_container *container) { | ||
941 | if (!container->current.parent && container->current.workspace && | ||
942 | list_find(container->current.workspace->floating, container) != -1) { | ||
943 | return true; | ||
944 | } | ||
945 | if (container->scratchpad) { | ||
946 | return true; | ||
947 | } | ||
948 | return false; | ||
949 | } | ||
950 | |||
951 | void container_get_box(struct sway_container *container, struct wlr_box *box) { | 1001 | void container_get_box(struct sway_container *container, struct wlr_box *box) { |
952 | box->x = container->pending.x; | 1002 | box->x = container->pending.x; |
953 | box->y = container->pending.y; | 1003 | box->y = container->pending.y; |
@@ -1031,6 +1081,13 @@ void container_floating_move_to(struct sway_container *con, | |||
1031 | workspace_add_floating(new_workspace, con); | 1081 | workspace_add_floating(new_workspace, con); |
1032 | arrange_workspace(old_workspace); | 1082 | arrange_workspace(old_workspace); |
1033 | arrange_workspace(new_workspace); | 1083 | arrange_workspace(new_workspace); |
1084 | // If the moved container was a visible scratchpad container, then | ||
1085 | // update its transform. | ||
1086 | if (con->scratchpad) { | ||
1087 | struct wlr_box output_box; | ||
1088 | output_get_box(new_output, &output_box); | ||
1089 | con->transform = output_box; | ||
1090 | } | ||
1034 | workspace_detect_urgent(old_workspace); | 1091 | workspace_detect_urgent(old_workspace); |
1035 | workspace_detect_urgent(new_workspace); | 1092 | workspace_detect_urgent(new_workspace); |
1036 | } | 1093 | } |
@@ -1062,16 +1119,6 @@ void container_end_mouse_operation(struct sway_container *container) { | |||
1062 | } | 1119 | } |
1063 | } | 1120 | } |
1064 | 1121 | ||
1065 | static bool devid_from_fd(int fd, dev_t *devid) { | ||
1066 | struct stat stat; | ||
1067 | if (fstat(fd, &stat) != 0) { | ||
1068 | sway_log_errno(SWAY_ERROR, "fstat failed"); | ||
1069 | return false; | ||
1070 | } | ||
1071 | *devid = stat.st_rdev; | ||
1072 | return true; | ||
1073 | } | ||
1074 | |||
1075 | static void set_fullscreen(struct sway_container *con, bool enable) { | 1122 | static void set_fullscreen(struct sway_container *con, bool enable) { |
1076 | if (!con->view) { | 1123 | if (!con->view) { |
1077 | return; | 1124 | return; |
@@ -1083,75 +1130,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) { | |||
1083 | con->view->foreign_toplevel, enable); | 1130 | con->view->foreign_toplevel, enable); |
1084 | } | 1131 | } |
1085 | } | 1132 | } |
1086 | |||
1087 | if (!server.linux_dmabuf_v1 || !con->view->surface) { | ||
1088 | return; | ||
1089 | } | ||
1090 | if (!enable) { | ||
1091 | wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, | ||
1092 | con->view->surface, NULL); | ||
1093 | return; | ||
1094 | } | ||
1095 | |||
1096 | if (!con->pending.workspace || !con->pending.workspace->output) { | ||
1097 | return; | ||
1098 | } | ||
1099 | |||
1100 | struct sway_output *output = con->pending.workspace->output; | ||
1101 | struct wlr_output *wlr_output = output->wlr_output; | ||
1102 | |||
1103 | // TODO: add wlroots helpers for all of this stuff | ||
1104 | |||
1105 | const struct wlr_drm_format_set *renderer_formats = | ||
1106 | wlr_renderer_get_dmabuf_texture_formats(server.renderer); | ||
1107 | assert(renderer_formats); | ||
1108 | |||
1109 | int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer); | ||
1110 | int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend); | ||
1111 | if (renderer_drm_fd < 0 || backend_drm_fd < 0) { | ||
1112 | return; | ||
1113 | } | ||
1114 | |||
1115 | dev_t render_dev, scanout_dev; | ||
1116 | if (!devid_from_fd(renderer_drm_fd, &render_dev) || | ||
1117 | !devid_from_fd(backend_drm_fd, &scanout_dev)) { | ||
1118 | return; | ||
1119 | } | ||
1120 | |||
1121 | const struct wlr_drm_format_set *output_formats = | ||
1122 | wlr_output_get_primary_formats(output->wlr_output, | ||
1123 | WLR_BUFFER_CAP_DMABUF); | ||
1124 | if (!output_formats) { | ||
1125 | return; | ||
1126 | } | ||
1127 | |||
1128 | struct wlr_drm_format_set scanout_formats = {0}; | ||
1129 | if (!wlr_drm_format_set_intersect(&scanout_formats, | ||
1130 | output_formats, renderer_formats)) { | ||
1131 | return; | ||
1132 | } | ||
1133 | |||
1134 | struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = { | ||
1135 | { | ||
1136 | .target_device = scanout_dev, | ||
1137 | .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, | ||
1138 | .formats = &scanout_formats, | ||
1139 | }, | ||
1140 | { | ||
1141 | .target_device = render_dev, | ||
1142 | .formats = renderer_formats, | ||
1143 | }, | ||
1144 | }; | ||
1145 | |||
1146 | const struct wlr_linux_dmabuf_feedback_v1 feedback = { | ||
1147 | .main_device = render_dev, | ||
1148 | .tranches = tranches, | ||
1149 | .tranches_len = sizeof(tranches) / sizeof(tranches[0]), | ||
1150 | }; | ||
1151 | wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, | ||
1152 | con->view->surface, &feedback); | ||
1153 | |||
1154 | wlr_drm_format_set_finish(&scanout_formats); | ||
1155 | } | 1133 | } |
1156 | 1134 | ||
1157 | static void container_fullscreen_workspace(struct sway_container *con) { | 1135 | static void container_fullscreen_workspace(struct sway_container *con) { |
@@ -1321,72 +1299,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { | |||
1321 | return false; | 1299 | return false; |
1322 | } | 1300 | } |
1323 | 1301 | ||
1324 | static void surface_send_enter_iterator(struct wlr_surface *surface, | ||
1325 | int x, int y, void *data) { | ||
1326 | struct wlr_output *wlr_output = data; | ||
1327 | wlr_surface_send_enter(surface, wlr_output); | ||
1328 | } | ||
1329 | |||
1330 | static void surface_send_leave_iterator(struct wlr_surface *surface, | ||
1331 | int x, int y, void *data) { | ||
1332 | struct wlr_output *wlr_output = data; | ||
1333 | wlr_surface_send_leave(surface, wlr_output); | ||
1334 | } | ||
1335 | |||
1336 | void container_discover_outputs(struct sway_container *con) { | ||
1337 | struct wlr_box con_box = { | ||
1338 | .x = con->current.x, | ||
1339 | .y = con->current.y, | ||
1340 | .width = con->current.width, | ||
1341 | .height = con->current.height, | ||
1342 | }; | ||
1343 | struct sway_output *old_output = container_get_effective_output(con); | ||
1344 | |||
1345 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1346 | struct sway_output *output = root->outputs->items[i]; | ||
1347 | struct wlr_box output_box; | ||
1348 | output_get_box(output, &output_box); | ||
1349 | struct wlr_box intersection; | ||
1350 | bool intersects = | ||
1351 | wlr_box_intersection(&intersection, &con_box, &output_box); | ||
1352 | int index = list_find(con->outputs, output); | ||
1353 | |||
1354 | if (intersects && index == -1) { | ||
1355 | // Send enter | ||
1356 | sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); | ||
1357 | if (con->view) { | ||
1358 | view_for_each_surface(con->view, | ||
1359 | surface_send_enter_iterator, output->wlr_output); | ||
1360 | if (con->view->foreign_toplevel) { | ||
1361 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
1362 | con->view->foreign_toplevel, output->wlr_output); | ||
1363 | } | ||
1364 | } | ||
1365 | list_add(con->outputs, output); | ||
1366 | } else if (!intersects && index != -1) { | ||
1367 | // Send leave | ||
1368 | sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); | ||
1369 | if (con->view) { | ||
1370 | view_for_each_surface(con->view, | ||
1371 | surface_send_leave_iterator, output->wlr_output); | ||
1372 | if (con->view->foreign_toplevel) { | ||
1373 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
1374 | con->view->foreign_toplevel, output->wlr_output); | ||
1375 | } | ||
1376 | } | ||
1377 | list_del(con->outputs, index); | ||
1378 | } | ||
1379 | } | ||
1380 | struct sway_output *new_output = container_get_effective_output(con); | ||
1381 | double old_scale = old_output && old_output->enabled ? | ||
1382 | old_output->wlr_output->scale : -1; | ||
1383 | double new_scale = new_output ? new_output->wlr_output->scale : -1; | ||
1384 | if (old_scale != new_scale) { | ||
1385 | container_update_title_textures(con); | ||
1386 | container_update_marks_textures(con); | ||
1387 | } | ||
1388 | } | ||
1389 | |||
1390 | enum sway_container_layout container_parent_layout(struct sway_container *con) { | 1302 | enum sway_container_layout container_parent_layout(struct sway_container *con) { |
1391 | if (con->pending.parent) { | 1303 | if (con->pending.parent) { |
1392 | return con->pending.parent->pending.layout; | 1304 | return con->pending.parent->pending.layout; |
@@ -1397,19 +1309,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { | |||
1397 | return L_NONE; | 1309 | return L_NONE; |
1398 | } | 1310 | } |
1399 | 1311 | ||
1400 | enum sway_container_layout container_current_parent_layout( | ||
1401 | struct sway_container *con) { | ||
1402 | if (con->current.parent) { | ||
1403 | return con->current.parent->current.layout; | ||
1404 | } | ||
1405 | return con->current.workspace->current.layout; | ||
1406 | } | ||
1407 | |||
1408 | list_t *container_get_siblings(struct sway_container *container) { | 1312 | list_t *container_get_siblings(struct sway_container *container) { |
1409 | if (container->pending.parent) { | 1313 | if (container->pending.parent) { |
1410 | return container->pending.parent->pending.children; | 1314 | return container->pending.parent->pending.children; |
1411 | } | 1315 | } |
1412 | if (container_is_scratchpad_hidden(container)) { | 1316 | if (!container->pending.workspace) { |
1413 | return NULL; | 1317 | return NULL; |
1414 | } | 1318 | } |
1415 | if (list_find(container->pending.workspace->tiling, container) != -1) { | 1319 | if (list_find(container->pending.workspace->tiling, container) != -1) { |
@@ -1422,13 +1326,6 @@ int container_sibling_index(struct sway_container *child) { | |||
1422 | return list_find(container_get_siblings(child), child); | 1326 | return list_find(container_get_siblings(child), child); |
1423 | } | 1327 | } |
1424 | 1328 | ||
1425 | list_t *container_get_current_siblings(struct sway_container *container) { | ||
1426 | if (container->current.parent) { | ||
1427 | return container->current.parent->current.children; | ||
1428 | } | ||
1429 | return container->current.workspace->current.tiling; | ||
1430 | } | ||
1431 | |||
1432 | void container_handle_fullscreen_reparent(struct sway_container *con) { | 1329 | void container_handle_fullscreen_reparent(struct sway_container *con) { |
1433 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || | 1330 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || |
1434 | con->pending.workspace->fullscreen == con) { | 1331 | con->pending.workspace->fullscreen == con) { |
@@ -1643,7 +1540,7 @@ bool container_find_and_unmark(char *mark) { | |||
1643 | if (strcmp(con_mark, mark) == 0) { | 1540 | if (strcmp(con_mark, mark) == 0) { |
1644 | free(con_mark); | 1541 | free(con_mark); |
1645 | list_del(con->marks, i); | 1542 | list_del(con->marks, i); |
1646 | container_update_marks_textures(con); | 1543 | container_update_marks(con); |
1647 | ipc_event_window(con, "mark"); | 1544 | ipc_event_window(con, "mark"); |
1648 | return true; | 1545 | return true; |
1649 | } | 1546 | } |
@@ -1674,70 +1571,15 @@ void container_add_mark(struct sway_container *con, char *mark) { | |||
1674 | ipc_event_window(con, "mark"); | 1571 | ipc_event_window(con, "mark"); |
1675 | } | 1572 | } |
1676 | 1573 | ||
1677 | static void update_marks_texture(struct sway_container *con, | ||
1678 | struct wlr_texture **texture, struct border_colors *class) { | ||
1679 | struct sway_output *output = container_get_effective_output(con); | ||
1680 | if (!output) { | ||
1681 | return; | ||
1682 | } | ||
1683 | if (*texture) { | ||
1684 | wlr_texture_destroy(*texture); | ||
1685 | *texture = NULL; | ||
1686 | } | ||
1687 | if (!con->marks->length) { | ||
1688 | return; | ||
1689 | } | ||
1690 | |||
1691 | size_t len = 0; | ||
1692 | for (int i = 0; i < con->marks->length; ++i) { | ||
1693 | char *mark = con->marks->items[i]; | ||
1694 | if (mark[0] != '_') { | ||
1695 | len += strlen(mark) + 2; | ||
1696 | } | ||
1697 | } | ||
1698 | char *buffer = calloc(len + 1, 1); | ||
1699 | char *part = malloc(len + 1); | ||
1700 | |||
1701 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
1702 | free(buffer); | ||
1703 | return; | ||
1704 | } | ||
1705 | |||
1706 | for (int i = 0; i < con->marks->length; ++i) { | ||
1707 | char *mark = con->marks->items[i]; | ||
1708 | if (mark[0] != '_') { | ||
1709 | snprintf(part, len + 1, "[%s]", mark); | ||
1710 | strcat(buffer, part); | ||
1711 | } | ||
1712 | } | ||
1713 | free(part); | ||
1714 | |||
1715 | render_titlebar_text_texture(output, con, texture, class, false, buffer); | ||
1716 | |||
1717 | free(buffer); | ||
1718 | } | ||
1719 | |||
1720 | void container_update_marks_textures(struct sway_container *con) { | ||
1721 | if (!config->show_marks) { | ||
1722 | return; | ||
1723 | } | ||
1724 | update_marks_texture(con, &con->marks_focused, | ||
1725 | &config->border_colors.focused); | ||
1726 | update_marks_texture(con, &con->marks_focused_inactive, | ||
1727 | &config->border_colors.focused_inactive); | ||
1728 | update_marks_texture(con, &con->marks_unfocused, | ||
1729 | &config->border_colors.unfocused); | ||
1730 | update_marks_texture(con, &con->marks_urgent, | ||
1731 | &config->border_colors.urgent); | ||
1732 | update_marks_texture(con, &con->marks_focused_tab_title, | ||
1733 | &config->border_colors.focused_tab_title); | ||
1734 | container_damage_whole(con); | ||
1735 | } | ||
1736 | |||
1737 | void container_raise_floating(struct sway_container *con) { | 1574 | void container_raise_floating(struct sway_container *con) { |
1738 | // Bring container to front by putting it at the end of the floating list. | 1575 | // Bring container to front by putting it at the end of the floating list. |
1739 | struct sway_container *floater = container_toplevel_ancestor(con); | 1576 | struct sway_container *floater = container_toplevel_ancestor(con); |
1740 | if (container_is_floating(floater) && floater->pending.workspace) { | 1577 | if (container_is_floating(floater) && floater->pending.workspace) { |
1578 | // it's okay to just raise the scene directly instead of waiting | ||
1579 | // for the transaction to go through. We won't be reconfiguring | ||
1580 | // surfaces | ||
1581 | wlr_scene_node_raise_to_top(&floater->scene_tree->node); | ||
1582 | |||
1741 | list_move_to_end(floater->pending.workspace->floating, floater); | 1583 | list_move_to_end(floater->pending.workspace->floating, floater); |
1742 | node_set_dirty(&floater->pending.workspace->node); | 1584 | node_set_dirty(&floater->pending.workspace->node); |
1743 | } | 1585 | } |
@@ -1821,3 +1663,177 @@ int container_squash(struct sway_container *con) { | |||
1821 | } | 1663 | } |
1822 | return change; | 1664 | return change; |
1823 | } | 1665 | } |
1666 | |||
1667 | static void swap_places(struct sway_container *con1, | ||
1668 | struct sway_container *con2) { | ||
1669 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | ||
1670 | temp->pending.x = con1->pending.x; | ||
1671 | temp->pending.y = con1->pending.y; | ||
1672 | temp->pending.width = con1->pending.width; | ||
1673 | temp->pending.height = con1->pending.height; | ||
1674 | temp->width_fraction = con1->width_fraction; | ||
1675 | temp->height_fraction = con1->height_fraction; | ||
1676 | temp->pending.parent = con1->pending.parent; | ||
1677 | temp->pending.workspace = con1->pending.workspace; | ||
1678 | bool temp_floating = container_is_floating(con1); | ||
1679 | |||
1680 | con1->pending.x = con2->pending.x; | ||
1681 | con1->pending.y = con2->pending.y; | ||
1682 | con1->pending.width = con2->pending.width; | ||
1683 | con1->pending.height = con2->pending.height; | ||
1684 | con1->width_fraction = con2->width_fraction; | ||
1685 | con1->height_fraction = con2->height_fraction; | ||
1686 | |||
1687 | con2->pending.x = temp->pending.x; | ||
1688 | con2->pending.y = temp->pending.y; | ||
1689 | con2->pending.width = temp->pending.width; | ||
1690 | con2->pending.height = temp->pending.height; | ||
1691 | con2->width_fraction = temp->width_fraction; | ||
1692 | con2->height_fraction = temp->height_fraction; | ||
1693 | |||
1694 | int temp_index = container_sibling_index(con1); | ||
1695 | if (con2->pending.parent) { | ||
1696 | container_insert_child(con2->pending.parent, con1, | ||
1697 | container_sibling_index(con2)); | ||
1698 | } else if (container_is_floating(con2)) { | ||
1699 | workspace_add_floating(con2->pending.workspace, con1); | ||
1700 | } else { | ||
1701 | workspace_insert_tiling(con2->pending.workspace, con1, | ||
1702 | container_sibling_index(con2)); | ||
1703 | } | ||
1704 | if (temp->pending.parent) { | ||
1705 | container_insert_child(temp->pending.parent, con2, temp_index); | ||
1706 | } else if (temp_floating) { | ||
1707 | workspace_add_floating(temp->pending.workspace, con2); | ||
1708 | } else { | ||
1709 | workspace_insert_tiling(temp->pending.workspace, con2, temp_index); | ||
1710 | } | ||
1711 | |||
1712 | free(temp); | ||
1713 | } | ||
1714 | |||
1715 | static void swap_focus(struct sway_container *con1, | ||
1716 | struct sway_container *con2, struct sway_seat *seat, | ||
1717 | struct sway_container *focus) { | ||
1718 | if (focus == con1 || focus == con2) { | ||
1719 | struct sway_workspace *ws1 = con1->pending.workspace; | ||
1720 | struct sway_workspace *ws2 = con2->pending.workspace; | ||
1721 | enum sway_container_layout layout1 = container_parent_layout(con1); | ||
1722 | enum sway_container_layout layout2 = container_parent_layout(con2); | ||
1723 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { | ||
1724 | if (workspace_is_visible(ws2)) { | ||
1725 | seat_set_focus(seat, &con2->node); | ||
1726 | } | ||
1727 | seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); | ||
1728 | } else if (focus == con2 && (layout1 == L_TABBED | ||
1729 | || layout1 == L_STACKED)) { | ||
1730 | if (workspace_is_visible(ws1)) { | ||
1731 | seat_set_focus(seat, &con1->node); | ||
1732 | } | ||
1733 | seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); | ||
1734 | } else if (ws1 != ws2) { | ||
1735 | seat_set_focus_container(seat, focus == con1 ? con2 : con1); | ||
1736 | } else { | ||
1737 | seat_set_focus_container(seat, focus); | ||
1738 | } | ||
1739 | } else { | ||
1740 | seat_set_focus_container(seat, focus); | ||
1741 | } | ||
1742 | |||
1743 | if (root->fullscreen_global) { | ||
1744 | seat_set_focus(seat, | ||
1745 | seat_get_focus_inactive(seat, &root->fullscreen_global->node)); | ||
1746 | } | ||
1747 | } | ||
1748 | |||
1749 | void container_swap(struct sway_container *con1, struct sway_container *con2) { | ||
1750 | if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { | ||
1751 | return; | ||
1752 | } | ||
1753 | if (!sway_assert(!container_has_ancestor(con1, con2) | ||
1754 | && !container_has_ancestor(con2, con1), | ||
1755 | "Cannot swap ancestor and descendant")) { | ||
1756 | return; | ||
1757 | } | ||
1758 | |||
1759 | sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", | ||
1760 | con1->node.id, con2->node.id); | ||
1761 | |||
1762 | bool scratch1 = con1->scratchpad; | ||
1763 | bool hidden1 = container_is_scratchpad_hidden(con1); | ||
1764 | bool scratch2 = con2->scratchpad; | ||
1765 | bool hidden2 = container_is_scratchpad_hidden(con2); | ||
1766 | if (scratch1) { | ||
1767 | if (hidden1) { | ||
1768 | root_scratchpad_show(con1); | ||
1769 | } | ||
1770 | root_scratchpad_remove_container(con1); | ||
1771 | } | ||
1772 | if (scratch2) { | ||
1773 | if (hidden2) { | ||
1774 | root_scratchpad_show(con2); | ||
1775 | } | ||
1776 | root_scratchpad_remove_container(con2); | ||
1777 | } | ||
1778 | |||
1779 | enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; | ||
1780 | if (fs1) { | ||
1781 | container_fullscreen_disable(con1); | ||
1782 | } | ||
1783 | enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; | ||
1784 | if (fs2) { | ||
1785 | container_fullscreen_disable(con2); | ||
1786 | } | ||
1787 | |||
1788 | struct sway_seat *seat = input_manager_current_seat(); | ||
1789 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1790 | struct sway_workspace *vis1 = | ||
1791 | output_get_active_workspace(con1->pending.workspace->output); | ||
1792 | struct sway_workspace *vis2 = | ||
1793 | output_get_active_workspace(con2->pending.workspace->output); | ||
1794 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | ||
1795 | "workspace. This should not happen")) { | ||
1796 | return; | ||
1797 | } | ||
1798 | |||
1799 | char *stored_prev_name = NULL; | ||
1800 | if (seat->prev_workspace_name) { | ||
1801 | stored_prev_name = strdup(seat->prev_workspace_name); | ||
1802 | } | ||
1803 | |||
1804 | swap_places(con1, con2); | ||
1805 | |||
1806 | if (!workspace_is_visible(vis1)) { | ||
1807 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); | ||
1808 | } | ||
1809 | if (!workspace_is_visible(vis2)) { | ||
1810 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); | ||
1811 | } | ||
1812 | |||
1813 | swap_focus(con1, con2, seat, focus); | ||
1814 | |||
1815 | if (stored_prev_name) { | ||
1816 | free(seat->prev_workspace_name); | ||
1817 | seat->prev_workspace_name = stored_prev_name; | ||
1818 | } | ||
1819 | |||
1820 | if (scratch1) { | ||
1821 | root_scratchpad_add_container(con2, NULL); | ||
1822 | if (!hidden1) { | ||
1823 | root_scratchpad_show(con2); | ||
1824 | } | ||
1825 | } | ||
1826 | if (scratch2) { | ||
1827 | root_scratchpad_add_container(con1, NULL); | ||
1828 | if (!hidden2) { | ||
1829 | root_scratchpad_show(con1); | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | if (fs1) { | ||
1834 | container_set_fullscreen(con2, fs1); | ||
1835 | } | ||
1836 | if (fs2) { | ||
1837 | container_set_fullscreen(con1, fs2); | ||
1838 | } | ||
1839 | } | ||