diff options
Diffstat (limited to 'sway/tree')
-rw-r--r-- | sway/tree/arrange.c | 15 | ||||
-rw-r--r-- | sway/tree/container.c | 1310 | ||||
-rw-r--r-- | sway/tree/node.c | 38 | ||||
-rw-r--r-- | sway/tree/output.c | 98 | ||||
-rw-r--r-- | sway/tree/root.c | 273 | ||||
-rw-r--r-- | sway/tree/view.c | 593 | ||||
-rw-r--r-- | sway/tree/workspace.c | 53 |
7 files changed, 1102 insertions, 1278 deletions
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 9c1a11e5..d4003fe6 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -264,6 +263,9 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
264 | area->width, area->height, area->x, area->y); | 263 | area->width, area->height, area->x, area->y); |
265 | 264 | ||
266 | bool first_arrange = workspace->width == 0 && workspace->height == 0; | 265 | bool first_arrange = workspace->width == 0 && workspace->height == 0; |
266 | struct wlr_box prev_box; | ||
267 | workspace_get_box(workspace, &prev_box); | ||
268 | |||
267 | double prev_x = workspace->x - workspace->current_gaps.left; | 269 | double prev_x = workspace->x - workspace->current_gaps.left; |
268 | double prev_y = workspace->y - workspace->current_gaps.top; | 270 | double prev_y = workspace->y - workspace->current_gaps.top; |
269 | workspace->width = area->width; | 271 | workspace->width = area->width; |
@@ -277,13 +279,14 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
277 | if (!first_arrange && (diff_x != 0 || diff_y != 0)) { | 279 | if (!first_arrange && (diff_x != 0 || diff_y != 0)) { |
278 | for (int i = 0; i < workspace->floating->length; ++i) { | 280 | for (int i = 0; i < workspace->floating->length; ++i) { |
279 | struct sway_container *floater = workspace->floating->items[i]; | 281 | struct sway_container *floater = workspace->floating->items[i]; |
280 | container_floating_translate(floater, diff_x, diff_y); | ||
281 | double center_x = floater->pending.x + floater->pending.width / 2; | ||
282 | double center_y = floater->pending.y + floater->pending.height / 2; | ||
283 | struct wlr_box workspace_box; | 282 | struct wlr_box workspace_box; |
284 | workspace_get_box(workspace, &workspace_box); | 283 | workspace_get_box(workspace, &workspace_box); |
285 | if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { | 284 | floating_fix_coordinates(floater, &prev_box, &workspace_box); |
286 | container_floating_move_to_center(floater); | 285 | // Set transformation for scratchpad windows. |
286 | if (floater->scratchpad) { | ||
287 | struct wlr_box output_box; | ||
288 | output_get_box(output, &output_box); | ||
289 | floater->transform = output_box; | ||
287 | } | 290 | } |
288 | } | 291 | } |
289 | } | 292 | } |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 09766ce5..80ef34fe 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,415 @@ 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 | alloc_width = MAX(alloc_width, 0); | ||
356 | |||
357 | sway_text_node_set_max_width(node, alloc_width); | ||
358 | wlr_scene_node_set_position(node->node, | ||
359 | h_padding, (height - node->height) >> 1); | ||
360 | |||
361 | pixman_region32_union_rect(&text_area, &text_area, | ||
362 | node->node->x, node->node->y, alloc_width, node->height); | ||
363 | } | ||
364 | |||
365 | if (con->title_bar.title_text) { | ||
366 | struct sway_text_node *node = con->title_bar.title_text; | ||
367 | |||
368 | int h_padding; | ||
369 | if (title_align == ALIGN_RIGHT) { | ||
370 | h_padding = width - config->titlebar_h_padding - node->width; | ||
371 | } else if (title_align == ALIGN_CENTER) { | ||
372 | h_padding = ((int)width - marks_buffer_width - node->width) >> 1; | ||
373 | } else { | ||
374 | h_padding = config->titlebar_h_padding; | ||
375 | } | ||
376 | |||
377 | h_padding = MAX(h_padding, 0); | ||
378 | |||
379 | int alloc_width = MIN((int) node->width, | ||
380 | width - h_padding - config->titlebar_h_padding); | ||
381 | alloc_width = MAX(alloc_width, 0); | ||
382 | |||
383 | sway_text_node_set_max_width(node, alloc_width); | ||
384 | wlr_scene_node_set_position(node->node, | ||
385 | h_padding, (height - node->height) >> 1); | ||
386 | |||
387 | pixman_region32_union_rect(&text_area, &text_area, | ||
388 | node->node->x, node->node->y, alloc_width, node->height); | ||
389 | } | ||
390 | |||
391 | // silence pixman errors | ||
392 | if (width <= 0 || height <= 0) { | ||
393 | pixman_region32_fini(&text_area); | ||
394 | return; | ||
395 | } | ||
396 | |||
397 | pixman_region32_t background, border; | ||
398 | |||
399 | int thickness = config->titlebar_border_thickness; | ||
400 | pixman_region32_init_rect(&background, | ||
401 | thickness, thickness, | ||
402 | width - thickness * 2, height - thickness * 2); | ||
403 | pixman_region32_init_rect(&border, 0, 0, width, height); | ||
404 | pixman_region32_subtract(&border, &border, &background); | ||
405 | |||
406 | pixman_region32_subtract(&background, &background, &text_area); | ||
407 | pixman_region32_fini(&text_area); | ||
408 | |||
409 | update_rect_list(con->title_bar.background, &background); | ||
410 | pixman_region32_fini(&background); | ||
411 | |||
412 | update_rect_list(con->title_bar.border, &border); | ||
413 | pixman_region32_fini(&border); | ||
414 | |||
415 | container_update(con); | ||
416 | } | ||
417 | |||
418 | void container_update_marks(struct sway_container *con) { | ||
419 | char *buffer = NULL; | ||
420 | |||
421 | if (config->show_marks && con->marks->length) { | ||
422 | size_t len = 0; | ||
423 | for (int i = 0; i < con->marks->length; ++i) { | ||
424 | char *mark = con->marks->items[i]; | ||
425 | if (mark[0] != '_') { | ||
426 | len += strlen(mark) + 2; | ||
427 | } | ||
428 | } | ||
429 | buffer = calloc(len + 1, 1); | ||
430 | char *part = malloc(len + 1); | ||
431 | |||
432 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
433 | free(buffer); | ||
434 | return; | ||
435 | } | ||
436 | |||
437 | for (int i = 0; i < con->marks->length; ++i) { | ||
438 | char *mark = con->marks->items[i]; | ||
439 | if (mark[0] != '_') { | ||
440 | snprintf(part, len + 1, "[%s]", mark); | ||
441 | strcat(buffer, part); | ||
442 | } | ||
443 | } | ||
444 | free(part); | ||
445 | } | ||
446 | |||
447 | if (!buffer) { | ||
448 | if (con->title_bar.marks_text) { | ||
449 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
450 | con->title_bar.marks_text = NULL; | ||
451 | } | ||
452 | } else if (!con->title_bar.marks_text) { | ||
453 | struct border_colors *colors = container_get_current_colors(con); | ||
454 | |||
455 | con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, | ||
456 | buffer, colors->text, false); | ||
457 | } else { | ||
458 | sway_text_node_set_text(con->title_bar.marks_text, buffer); | ||
459 | } | ||
460 | |||
461 | container_arrange_title_bar(con); | ||
462 | free(buffer); | ||
463 | } | ||
464 | |||
465 | void container_update_title_bar(struct sway_container *con) { | ||
466 | if (!con->formatted_title) { | ||
467 | return; | ||
468 | } | ||
469 | |||
470 | struct border_colors *colors = container_get_current_colors(con); | ||
471 | |||
472 | if (con->title_bar.title_text) { | ||
473 | wlr_scene_node_destroy(con->title_bar.title_text->node); | ||
474 | con->title_bar.title_text = NULL; | ||
475 | } | ||
476 | |||
477 | con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, | ||
478 | con->formatted_title, colors->text, config->pango_markup); | ||
479 | |||
480 | // we always have to remake these text buffers completely for text font | ||
481 | // changes etc... | ||
482 | if (con->title_bar.marks_text) { | ||
483 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
484 | con->title_bar.marks_text = NULL; | ||
485 | } | ||
486 | |||
487 | container_update_marks(con); | ||
488 | container_arrange_title_bar(con); | ||
489 | } | ||
490 | |||
57 | void container_destroy(struct sway_container *con) { | 491 | void container_destroy(struct sway_container *con) { |
58 | if (!sway_assert(con->node.destroying, | 492 | if (!sway_assert(con->node.destroying, |
59 | "Tried to free container which wasn't marked as destroying")) { | 493 | "Tried to free container which wasn't marked as destroying")) { |
@@ -65,29 +499,21 @@ void container_destroy(struct sway_container *con) { | |||
65 | } | 499 | } |
66 | free(con->title); | 500 | free(con->title); |
67 | free(con->formatted_title); | 501 | 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); | 502 | list_free(con->pending.children); |
74 | list_free(con->current.children); | 503 | list_free(con->current.children); |
75 | list_free(con->outputs); | ||
76 | 504 | ||
77 | list_free_items_and_destroy(con->marks); | 505 | 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 | 506 | ||
84 | if (con->view && con->view->container == con) { | 507 | if (con->view && con->view->container == con) { |
85 | con->view->container = NULL; | 508 | con->view->container = NULL; |
509 | wlr_scene_node_destroy(&con->output_handler->node); | ||
86 | if (con->view->destroying) { | 510 | if (con->view->destroying) { |
87 | view_destroy(con->view); | 511 | view_destroy(con->view); |
88 | } | 512 | } |
89 | } | 513 | } |
90 | 514 | ||
515 | scene_node_disown_children(con->content_tree); | ||
516 | wlr_scene_node_destroy(&con->scene_tree->node); | ||
91 | free(con); | 517 | free(con); |
92 | } | 518 | } |
93 | 519 | ||
@@ -104,7 +530,7 @@ void container_begin_destroy(struct sway_container *con) { | |||
104 | container_fullscreen_disable(con); | 530 | container_fullscreen_disable(con); |
105 | } | 531 | } |
106 | 532 | ||
107 | wl_signal_emit(&con->node.events.destroy, &con->node); | 533 | wl_signal_emit_mutable(&con->node.events.destroy, &con->node); |
108 | 534 | ||
109 | container_end_mouse_operation(con); | 535 | container_end_mouse_operation(con); |
110 | 536 | ||
@@ -174,265 +600,6 @@ struct sway_container *container_find_child(struct sway_container *container, | |||
174 | return NULL; | 600 | return NULL; |
175 | } | 601 | } |
176 | 602 | ||
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, | 603 | void container_for_each_child(struct sway_container *container, |
437 | void (*f)(struct sway_container *container, void *data), | 604 | void (*f)(struct sway_container *container, void *data), |
438 | void *data) { | 605 | void *data) { |
@@ -478,127 +645,6 @@ bool container_has_ancestor(struct sway_container *descendant, | |||
478 | return false; | 645 | return false; |
479 | } | 646 | } |
480 | 647 | ||
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, &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, 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 | /** | 648 | /** |
603 | * Calculate and return the length of the tree representation. | 649 | * Calculate and return the length of the tree representation. |
604 | * An example tree representation is: V[Terminal, Firefox] | 650 | * An example tree representation is: V[Terminal, Firefox] |
@@ -664,7 +710,13 @@ void container_update_representation(struct sway_container *con) { | |||
664 | } | 710 | } |
665 | container_build_representation(con->pending.layout, con->pending.children, | 711 | container_build_representation(con->pending.layout, con->pending.children, |
666 | con->formatted_title); | 712 | con->formatted_title); |
667 | container_update_title_textures(con); | 713 | |
714 | if (con->title_bar.title_text) { | ||
715 | sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); | ||
716 | container_arrange_title_bar(con); | ||
717 | } else { | ||
718 | container_update_title_bar(con); | ||
719 | } | ||
668 | } | 720 | } |
669 | if (con->pending.parent) { | 721 | if (con->pending.parent) { |
670 | container_update_representation(con->pending.parent); | 722 | container_update_representation(con->pending.parent); |
@@ -716,6 +768,21 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
716 | 768 | ||
717 | } | 769 | } |
718 | 770 | ||
771 | void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) { | ||
772 | if (!old->width || !old->height) { | ||
773 | // Fall back to centering on the workspace. | ||
774 | container_floating_move_to_center(con); | ||
775 | } else { | ||
776 | int rel_x = con->pending.x - old->x + (con->pending.width / 2); | ||
777 | int rel_y = con->pending.y - old->y + (con->pending.height / 2); | ||
778 | |||
779 | con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); | ||
780 | con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); | ||
781 | |||
782 | sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); | ||
783 | } | ||
784 | } | ||
785 | |||
719 | static void floating_natural_resize(struct sway_container *con) { | 786 | static void floating_natural_resize(struct sway_container *con) { |
720 | int min_width, max_width, min_height, max_height; | 787 | int min_width, max_width, min_height, max_height; |
721 | floating_calculate_constraints(&min_width, &max_width, | 788 | floating_calculate_constraints(&min_width, &max_width, |
@@ -787,11 +854,11 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
787 | int min_width, max_width, min_height, max_height; | 854 | int min_width, max_width, min_height, max_height; |
788 | floating_calculate_constraints(&min_width, &max_width, | 855 | floating_calculate_constraints(&min_width, &max_width, |
789 | &min_height, &max_height); | 856 | &min_height, &max_height); |
790 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); | 857 | struct wlr_box box; |
791 | workspace_get_box(con->pending.workspace, box); | 858 | workspace_get_box(con->pending.workspace, &box); |
792 | 859 | ||
793 | double width = fmax(min_width, fmin(box->width * 0.5, max_width)); | 860 | 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)); | 861 | double height = fmax(min_height, fmin(box.height * 0.75, max_height)); |
795 | if (!con->view) { | 862 | if (!con->view) { |
796 | con->pending.width = width; | 863 | con->pending.width = width; |
797 | con->pending.height = height; | 864 | con->pending.height = height; |
@@ -800,8 +867,6 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
800 | con->pending.content_height = height; | 867 | con->pending.content_height = height; |
801 | container_set_geometry_from_content(con); | 868 | container_set_geometry_from_content(con); |
802 | } | 869 | } |
803 | |||
804 | free(box); | ||
805 | } | 870 | } |
806 | 871 | ||
807 | 872 | ||
@@ -937,17 +1002,6 @@ bool container_is_floating(struct sway_container *container) { | |||
937 | return false; | 1002 | return false; |
938 | } | 1003 | } |
939 | 1004 | ||
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) { | 1005 | void container_get_box(struct sway_container *container, struct wlr_box *box) { |
952 | box->x = container->pending.x; | 1006 | box->x = container->pending.x; |
953 | box->y = container->pending.y; | 1007 | box->y = container->pending.y; |
@@ -1031,6 +1085,13 @@ void container_floating_move_to(struct sway_container *con, | |||
1031 | workspace_add_floating(new_workspace, con); | 1085 | workspace_add_floating(new_workspace, con); |
1032 | arrange_workspace(old_workspace); | 1086 | arrange_workspace(old_workspace); |
1033 | arrange_workspace(new_workspace); | 1087 | arrange_workspace(new_workspace); |
1088 | // If the moved container was a visible scratchpad container, then | ||
1089 | // update its transform. | ||
1090 | if (con->scratchpad) { | ||
1091 | struct wlr_box output_box; | ||
1092 | output_get_box(new_output, &output_box); | ||
1093 | con->transform = output_box; | ||
1094 | } | ||
1034 | workspace_detect_urgent(old_workspace); | 1095 | workspace_detect_urgent(old_workspace); |
1035 | workspace_detect_urgent(new_workspace); | 1096 | workspace_detect_urgent(new_workspace); |
1036 | } | 1097 | } |
@@ -1062,16 +1123,6 @@ void container_end_mouse_operation(struct sway_container *container) { | |||
1062 | } | 1123 | } |
1063 | } | 1124 | } |
1064 | 1125 | ||
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) { | 1126 | static void set_fullscreen(struct sway_container *con, bool enable) { |
1076 | if (!con->view) { | 1127 | if (!con->view) { |
1077 | return; | 1128 | return; |
@@ -1083,75 +1134,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) { | |||
1083 | con->view->foreign_toplevel, enable); | 1134 | con->view->foreign_toplevel, enable); |
1084 | } | 1135 | } |
1085 | } | 1136 | } |
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 | } | 1137 | } |
1156 | 1138 | ||
1157 | static void container_fullscreen_workspace(struct sway_container *con) { | 1139 | static void container_fullscreen_workspace(struct sway_container *con) { |
@@ -1321,72 +1303,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { | |||
1321 | return false; | 1303 | return false; |
1322 | } | 1304 | } |
1323 | 1305 | ||
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) { | 1306 | enum sway_container_layout container_parent_layout(struct sway_container *con) { |
1391 | if (con->pending.parent) { | 1307 | if (con->pending.parent) { |
1392 | return con->pending.parent->pending.layout; | 1308 | return con->pending.parent->pending.layout; |
@@ -1397,19 +1313,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { | |||
1397 | return L_NONE; | 1313 | return L_NONE; |
1398 | } | 1314 | } |
1399 | 1315 | ||
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) { | 1316 | list_t *container_get_siblings(struct sway_container *container) { |
1409 | if (container->pending.parent) { | 1317 | if (container->pending.parent) { |
1410 | return container->pending.parent->pending.children; | 1318 | return container->pending.parent->pending.children; |
1411 | } | 1319 | } |
1412 | if (container_is_scratchpad_hidden(container)) { | 1320 | if (!container->pending.workspace) { |
1413 | return NULL; | 1321 | return NULL; |
1414 | } | 1322 | } |
1415 | if (list_find(container->pending.workspace->tiling, container) != -1) { | 1323 | if (list_find(container->pending.workspace->tiling, container) != -1) { |
@@ -1422,13 +1330,6 @@ int container_sibling_index(struct sway_container *child) { | |||
1422 | return list_find(container_get_siblings(child), child); | 1330 | return list_find(container_get_siblings(child), child); |
1423 | } | 1331 | } |
1424 | 1332 | ||
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) { | 1333 | void container_handle_fullscreen_reparent(struct sway_container *con) { |
1433 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || | 1334 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || |
1434 | con->pending.workspace->fullscreen == con) { | 1335 | con->pending.workspace->fullscreen == con) { |
@@ -1643,7 +1544,7 @@ bool container_find_and_unmark(char *mark) { | |||
1643 | if (strcmp(con_mark, mark) == 0) { | 1544 | if (strcmp(con_mark, mark) == 0) { |
1644 | free(con_mark); | 1545 | free(con_mark); |
1645 | list_del(con->marks, i); | 1546 | list_del(con->marks, i); |
1646 | container_update_marks_textures(con); | 1547 | container_update_marks(con); |
1647 | ipc_event_window(con, "mark"); | 1548 | ipc_event_window(con, "mark"); |
1648 | return true; | 1549 | return true; |
1649 | } | 1550 | } |
@@ -1674,70 +1575,15 @@ void container_add_mark(struct sway_container *con, char *mark) { | |||
1674 | ipc_event_window(con, "mark"); | 1575 | ipc_event_window(con, "mark"); |
1675 | } | 1576 | } |
1676 | 1577 | ||
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) { | 1578 | void container_raise_floating(struct sway_container *con) { |
1738 | // Bring container to front by putting it at the end of the floating list. | 1579 | // Bring container to front by putting it at the end of the floating list. |
1739 | struct sway_container *floater = container_toplevel_ancestor(con); | 1580 | struct sway_container *floater = container_toplevel_ancestor(con); |
1740 | if (container_is_floating(floater) && floater->pending.workspace) { | 1581 | if (container_is_floating(floater) && floater->pending.workspace) { |
1582 | // it's okay to just raise the scene directly instead of waiting | ||
1583 | // for the transaction to go through. We won't be reconfiguring | ||
1584 | // surfaces | ||
1585 | wlr_scene_node_raise_to_top(&floater->scene_tree->node); | ||
1586 | |||
1741 | list_move_to_end(floater->pending.workspace->floating, floater); | 1587 | list_move_to_end(floater->pending.workspace->floating, floater); |
1742 | node_set_dirty(&floater->pending.workspace->node); | 1588 | node_set_dirty(&floater->pending.workspace->node); |
1743 | } | 1589 | } |
@@ -1821,3 +1667,177 @@ int container_squash(struct sway_container *con) { | |||
1821 | } | 1667 | } |
1822 | return change; | 1668 | return change; |
1823 | } | 1669 | } |
1670 | |||
1671 | static void swap_places(struct sway_container *con1, | ||
1672 | struct sway_container *con2) { | ||
1673 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | ||
1674 | temp->pending.x = con1->pending.x; | ||
1675 | temp->pending.y = con1->pending.y; | ||
1676 | temp->pending.width = con1->pending.width; | ||
1677 | temp->pending.height = con1->pending.height; | ||
1678 | temp->width_fraction = con1->width_fraction; | ||
1679 | temp->height_fraction = con1->height_fraction; | ||
1680 | temp->pending.parent = con1->pending.parent; | ||
1681 | temp->pending.workspace = con1->pending.workspace; | ||
1682 | bool temp_floating = container_is_floating(con1); | ||
1683 | |||
1684 | con1->pending.x = con2->pending.x; | ||
1685 | con1->pending.y = con2->pending.y; | ||
1686 | con1->pending.width = con2->pending.width; | ||
1687 | con1->pending.height = con2->pending.height; | ||
1688 | con1->width_fraction = con2->width_fraction; | ||
1689 | con1->height_fraction = con2->height_fraction; | ||
1690 | |||
1691 | con2->pending.x = temp->pending.x; | ||
1692 | con2->pending.y = temp->pending.y; | ||
1693 | con2->pending.width = temp->pending.width; | ||
1694 | con2->pending.height = temp->pending.height; | ||
1695 | con2->width_fraction = temp->width_fraction; | ||
1696 | con2->height_fraction = temp->height_fraction; | ||
1697 | |||
1698 | int temp_index = container_sibling_index(con1); | ||
1699 | if (con2->pending.parent) { | ||
1700 | container_insert_child(con2->pending.parent, con1, | ||
1701 | container_sibling_index(con2)); | ||
1702 | } else if (container_is_floating(con2)) { | ||
1703 | workspace_add_floating(con2->pending.workspace, con1); | ||
1704 | } else { | ||
1705 | workspace_insert_tiling(con2->pending.workspace, con1, | ||
1706 | container_sibling_index(con2)); | ||
1707 | } | ||
1708 | if (temp->pending.parent) { | ||
1709 | container_insert_child(temp->pending.parent, con2, temp_index); | ||
1710 | } else if (temp_floating) { | ||
1711 | workspace_add_floating(temp->pending.workspace, con2); | ||
1712 | } else { | ||
1713 | workspace_insert_tiling(temp->pending.workspace, con2, temp_index); | ||
1714 | } | ||
1715 | |||
1716 | free(temp); | ||
1717 | } | ||
1718 | |||
1719 | static void swap_focus(struct sway_container *con1, | ||
1720 | struct sway_container *con2, struct sway_seat *seat, | ||
1721 | struct sway_container *focus) { | ||
1722 | if (focus == con1 || focus == con2) { | ||
1723 | struct sway_workspace *ws1 = con1->pending.workspace; | ||
1724 | struct sway_workspace *ws2 = con2->pending.workspace; | ||
1725 | enum sway_container_layout layout1 = container_parent_layout(con1); | ||
1726 | enum sway_container_layout layout2 = container_parent_layout(con2); | ||
1727 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { | ||
1728 | if (workspace_is_visible(ws2)) { | ||
1729 | seat_set_focus(seat, &con2->node); | ||
1730 | } | ||
1731 | seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); | ||
1732 | } else if (focus == con2 && (layout1 == L_TABBED | ||
1733 | || layout1 == L_STACKED)) { | ||
1734 | if (workspace_is_visible(ws1)) { | ||
1735 | seat_set_focus(seat, &con1->node); | ||
1736 | } | ||
1737 | seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); | ||
1738 | } else if (ws1 != ws2) { | ||
1739 | seat_set_focus_container(seat, focus == con1 ? con2 : con1); | ||
1740 | } else { | ||
1741 | seat_set_focus_container(seat, focus); | ||
1742 | } | ||
1743 | } else { | ||
1744 | seat_set_focus_container(seat, focus); | ||
1745 | } | ||
1746 | |||
1747 | if (root->fullscreen_global) { | ||
1748 | seat_set_focus(seat, | ||
1749 | seat_get_focus_inactive(seat, &root->fullscreen_global->node)); | ||
1750 | } | ||
1751 | } | ||
1752 | |||
1753 | void container_swap(struct sway_container *con1, struct sway_container *con2) { | ||
1754 | if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { | ||
1755 | return; | ||
1756 | } | ||
1757 | if (!sway_assert(!container_has_ancestor(con1, con2) | ||
1758 | && !container_has_ancestor(con2, con1), | ||
1759 | "Cannot swap ancestor and descendant")) { | ||
1760 | return; | ||
1761 | } | ||
1762 | |||
1763 | sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", | ||
1764 | con1->node.id, con2->node.id); | ||
1765 | |||
1766 | bool scratch1 = con1->scratchpad; | ||
1767 | bool hidden1 = container_is_scratchpad_hidden(con1); | ||
1768 | bool scratch2 = con2->scratchpad; | ||
1769 | bool hidden2 = container_is_scratchpad_hidden(con2); | ||
1770 | if (scratch1) { | ||
1771 | if (hidden1) { | ||
1772 | root_scratchpad_show(con1); | ||
1773 | } | ||
1774 | root_scratchpad_remove_container(con1); | ||
1775 | } | ||
1776 | if (scratch2) { | ||
1777 | if (hidden2) { | ||
1778 | root_scratchpad_show(con2); | ||
1779 | } | ||
1780 | root_scratchpad_remove_container(con2); | ||
1781 | } | ||
1782 | |||
1783 | enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; | ||
1784 | if (fs1) { | ||
1785 | container_fullscreen_disable(con1); | ||
1786 | } | ||
1787 | enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; | ||
1788 | if (fs2) { | ||
1789 | container_fullscreen_disable(con2); | ||
1790 | } | ||
1791 | |||
1792 | struct sway_seat *seat = input_manager_current_seat(); | ||
1793 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1794 | struct sway_workspace *vis1 = | ||
1795 | output_get_active_workspace(con1->pending.workspace->output); | ||
1796 | struct sway_workspace *vis2 = | ||
1797 | output_get_active_workspace(con2->pending.workspace->output); | ||
1798 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | ||
1799 | "workspace. This should not happen")) { | ||
1800 | return; | ||
1801 | } | ||
1802 | |||
1803 | char *stored_prev_name = NULL; | ||
1804 | if (seat->prev_workspace_name) { | ||
1805 | stored_prev_name = strdup(seat->prev_workspace_name); | ||
1806 | } | ||
1807 | |||
1808 | swap_places(con1, con2); | ||
1809 | |||
1810 | if (!workspace_is_visible(vis1)) { | ||
1811 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); | ||
1812 | } | ||
1813 | if (!workspace_is_visible(vis2)) { | ||
1814 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); | ||
1815 | } | ||
1816 | |||
1817 | swap_focus(con1, con2, seat, focus); | ||
1818 | |||
1819 | if (stored_prev_name) { | ||
1820 | free(seat->prev_workspace_name); | ||
1821 | seat->prev_workspace_name = stored_prev_name; | ||
1822 | } | ||
1823 | |||
1824 | if (scratch1) { | ||
1825 | root_scratchpad_add_container(con2, NULL); | ||
1826 | if (!hidden1) { | ||
1827 | root_scratchpad_show(con2); | ||
1828 | } | ||
1829 | } | ||
1830 | if (scratch2) { | ||
1831 | root_scratchpad_add_container(con1, NULL); | ||
1832 | if (!hidden2) { | ||
1833 | root_scratchpad_show(con1); | ||
1834 | } | ||
1835 | } | ||
1836 | |||
1837 | if (fs1) { | ||
1838 | container_set_fullscreen(con2, fs1); | ||
1839 | } | ||
1840 | if (fs2) { | ||
1841 | container_set_fullscreen(con1, fs2); | ||
1842 | } | ||
1843 | } | ||
diff --git a/sway/tree/node.c b/sway/tree/node.c index bc7e2aa5..7aaf9762 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/output.h" | 1 | #include "sway/output.h" |
3 | #include "sway/server.h" | 2 | #include "sway/server.h" |
4 | #include "sway/tree/container.h" | 3 | #include "sway/tree/container.h" |
@@ -18,13 +17,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) { | |||
18 | const char *node_type_to_str(enum sway_node_type type) { | 17 | const char *node_type_to_str(enum sway_node_type type) { |
19 | switch (type) { | 18 | switch (type) { |
20 | case N_ROOT: | 19 | case N_ROOT: |
21 | return "N_ROOT"; | 20 | return "root"; |
22 | case N_OUTPUT: | 21 | case N_OUTPUT: |
23 | return "N_OUTPUT"; | 22 | return "output"; |
24 | case N_WORKSPACE: | 23 | case N_WORKSPACE: |
25 | return "N_WORKSPACE"; | 24 | return "workspace"; |
26 | case N_CONTAINER: | 25 | case N_CONTAINER: |
27 | return "N_CONTAINER"; | 26 | return "container"; |
28 | } | 27 | } |
29 | return ""; | 28 | return ""; |
30 | } | 29 | } |
@@ -159,3 +158,32 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | |||
159 | } | 158 | } |
160 | return false; | 159 | return false; |
161 | } | 160 | } |
161 | |||
162 | void scene_node_disown_children(struct wlr_scene_tree *tree) { | ||
163 | // this function can be called as part of destruction code that will be invoked | ||
164 | // upon an allocation failure. Let's not crash on NULL due to an allocation error. | ||
165 | if (!tree) { | ||
166 | return; | ||
167 | } | ||
168 | |||
169 | struct wlr_scene_node *child, *tmp_child; | ||
170 | wl_list_for_each_safe(child, tmp_child, &tree->children, link) { | ||
171 | wlr_scene_node_reparent(child, root->staging); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, | ||
176 | bool *failed) { | ||
177 | // fallthrough | ||
178 | if (*failed) { | ||
179 | return NULL; | ||
180 | } | ||
181 | |||
182 | struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); | ||
183 | if (!tree) { | ||
184 | sway_log(SWAY_ERROR, "Failed to allocate a scene node"); | ||
185 | *failed = true; | ||
186 | } | ||
187 | |||
188 | return tree; | ||
189 | } | ||
diff --git a/sway/tree/output.c b/sway/tree/output.c index 52826c91..2d11195e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -1,14 +1,13 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <ctype.h> | 2 | #include <ctype.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <wlr/types/wlr_output_damage.h> | ||
7 | #include "sway/ipc-server.h" | 5 | #include "sway/ipc-server.h" |
8 | #include "sway/layers.h" | 6 | #include "sway/layers.h" |
9 | #include "sway/output.h" | 7 | #include "sway/output.h" |
10 | #include "sway/tree/arrange.h" | 8 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/workspace.h" | 9 | #include "sway/tree/workspace.h" |
10 | #include "sway/server.h" | ||
12 | #include "log.h" | 11 | #include "log.h" |
13 | #include "util.h" | 12 | #include "util.h" |
14 | 13 | ||
@@ -87,9 +86,51 @@ static void restore_workspaces(struct sway_output *output) { | |||
87 | output_sort_workspaces(output); | 86 | output_sort_workspaces(output); |
88 | } | 87 | } |
89 | 88 | ||
89 | static void destroy_scene_layers(struct sway_output *output) { | ||
90 | wlr_scene_node_destroy(&output->fullscreen_background->node); | ||
91 | |||
92 | scene_node_disown_children(output->layers.tiling); | ||
93 | scene_node_disown_children(output->layers.fullscreen); | ||
94 | |||
95 | wlr_scene_node_destroy(&output->layers.shell_background->node); | ||
96 | wlr_scene_node_destroy(&output->layers.shell_bottom->node); | ||
97 | wlr_scene_node_destroy(&output->layers.tiling->node); | ||
98 | wlr_scene_node_destroy(&output->layers.fullscreen->node); | ||
99 | wlr_scene_node_destroy(&output->layers.shell_top->node); | ||
100 | wlr_scene_node_destroy(&output->layers.shell_overlay->node); | ||
101 | wlr_scene_node_destroy(&output->layers.session_lock->node); | ||
102 | } | ||
103 | |||
90 | struct sway_output *output_create(struct wlr_output *wlr_output) { | 104 | struct sway_output *output_create(struct wlr_output *wlr_output) { |
91 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); | 105 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); |
92 | node_init(&output->node, N_OUTPUT, output); | 106 | node_init(&output->node, N_OUTPUT, output); |
107 | |||
108 | bool failed = false; | ||
109 | output->layers.shell_background = alloc_scene_tree(root->staging, &failed); | ||
110 | output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed); | ||
111 | output->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
112 | output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
113 | output->layers.shell_top = alloc_scene_tree(root->staging, &failed); | ||
114 | output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed); | ||
115 | output->layers.session_lock = alloc_scene_tree(root->staging, &failed); | ||
116 | |||
117 | if (!failed) { | ||
118 | output->fullscreen_background = wlr_scene_rect_create( | ||
119 | output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); | ||
120 | |||
121 | if (!output->fullscreen_background) { | ||
122 | sway_log(SWAY_ERROR, "Unable to allocate a background rect"); | ||
123 | failed = true; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (failed) { | ||
128 | destroy_scene_layers(output); | ||
129 | wlr_scene_output_destroy(output->scene_output); | ||
130 | free(output); | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
93 | output->wlr_output = wlr_output; | 134 | output->wlr_output = wlr_output; |
94 | wlr_output->data = output; | 135 | wlr_output->data = output; |
95 | output->detected_subpixel = wlr_output->subpixel; | 136 | output->detected_subpixel = wlr_output->subpixel; |
@@ -102,11 +143,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { | |||
102 | output->workspaces = create_list(); | 143 | output->workspaces = create_list(); |
103 | output->current.workspaces = create_list(); | 144 | output->current.workspaces = create_list(); |
104 | 145 | ||
105 | size_t len = sizeof(output->layers) / sizeof(output->layers[0]); | ||
106 | for (size_t i = 0; i < len; ++i) { | ||
107 | wl_list_init(&output->layers[i]); | ||
108 | } | ||
109 | |||
110 | return output; | 146 | return output; |
111 | } | 147 | } |
112 | 148 | ||
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) { | |||
146 | 182 | ||
147 | input_manager_configure_xcursor(); | 183 | input_manager_configure_xcursor(); |
148 | 184 | ||
149 | wl_signal_emit(&root->events.new_node, &output->node); | 185 | wl_signal_emit_mutable(&root->events.new_node, &output->node); |
150 | 186 | ||
151 | arrange_layers(output); | 187 | arrange_layers(output); |
152 | arrange_root(); | 188 | arrange_root(); |
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) { | |||
238 | "which is still referenced by transactions")) { | 274 | "which is still referenced by transactions")) { |
239 | return; | 275 | return; |
240 | } | 276 | } |
277 | |||
278 | destroy_scene_layers(output); | ||
241 | list_free(output->workspaces); | 279 | list_free(output->workspaces); |
242 | list_free(output->current.workspaces); | 280 | list_free(output->current.workspaces); |
243 | wl_event_source_remove(output->repaint_timer); | 281 | wl_event_source_remove(output->repaint_timer); |
244 | free(output); | 282 | free(output); |
245 | } | 283 | } |
246 | 284 | ||
247 | static void untrack_output(struct sway_container *con, void *data) { | ||
248 | struct sway_output *output = data; | ||
249 | int index = list_find(con->outputs, output); | ||
250 | if (index != -1) { | ||
251 | list_del(con->outputs, index); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | void output_disable(struct sway_output *output) { | 285 | void output_disable(struct sway_output *output) { |
256 | if (!sway_assert(output->enabled, "Expected an enabled output")) { | 286 | if (!sway_assert(output->enabled, "Expected an enabled output")) { |
257 | return; | 287 | return; |
@@ -262,23 +292,20 @@ void output_disable(struct sway_output *output) { | |||
262 | } | 292 | } |
263 | 293 | ||
264 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); | 294 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); |
265 | wl_signal_emit(&output->events.disable, output); | 295 | wl_signal_emit_mutable(&output->events.disable, output); |
266 | 296 | ||
267 | output_evacuate(output); | 297 | output_evacuate(output); |
268 | 298 | ||
269 | root_for_each_container(untrack_output, output); | ||
270 | |||
271 | list_del(root->outputs, index); | 299 | list_del(root->outputs, index); |
272 | 300 | ||
273 | output->enabled = false; | 301 | output->enabled = false; |
274 | output->current_mode = NULL; | ||
275 | 302 | ||
276 | arrange_root(); | 303 | arrange_root(); |
277 | 304 | ||
278 | // Reconfigure all devices, since devices with map_to_output directives for | 305 | // Reconfigure all devices, since devices with map_to_output directives for |
279 | // an output that goes offline should stop sending events as long as the | 306 | // an output that goes offline should stop sending events as long as the |
280 | // output remains offline. | 307 | // output remains offline. |
281 | input_manager_configure_all_inputs(); | 308 | input_manager_configure_all_input_mappings(); |
282 | } | 309 | } |
283 | 310 | ||
284 | void output_begin_destroy(struct sway_output *output) { | 311 | void output_begin_destroy(struct sway_output *output) { |
@@ -286,7 +313,7 @@ void output_begin_destroy(struct sway_output *output) { | |||
286 | return; | 313 | return; |
287 | } | 314 | } |
288 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); | 315 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); |
289 | wl_signal_emit(&output->node.events.destroy, &output->node); | 316 | wl_signal_emit_mutable(&output->node.events.destroy, &output->node); |
290 | 317 | ||
291 | output->node.destroying = true; | 318 | output->node.destroying = true; |
292 | node_set_dirty(&output->node); | 319 | node_set_dirty(&output->node); |
@@ -390,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) { | |||
390 | box->height = output->height; | 417 | box->height = output->height; |
391 | } | 418 | } |
392 | 419 | ||
420 | static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) { | ||
421 | struct sway_output_non_desktop *output = | ||
422 | wl_container_of(listener, output, destroy); | ||
423 | |||
424 | sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name); | ||
425 | |||
426 | int index = list_find(root->non_desktop_outputs, output); | ||
427 | list_del(root->non_desktop_outputs, index); | ||
428 | |||
429 | wl_list_remove(&output->destroy.link); | ||
430 | |||
431 | free(output); | ||
432 | } | ||
433 | |||
434 | struct sway_output_non_desktop *output_non_desktop_create( | ||
435 | struct wlr_output *wlr_output) { | ||
436 | struct sway_output_non_desktop *output = | ||
437 | calloc(1, sizeof(struct sway_output_non_desktop)); | ||
438 | |||
439 | output->wlr_output = wlr_output; | ||
440 | |||
441 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | ||
442 | output->destroy.notify = handle_destroy_non_desktop; | ||
443 | |||
444 | return output; | ||
445 | } | ||
446 | |||
393 | enum sway_container_layout output_get_default_layout( | 447 | enum sway_container_layout output_get_default_layout( |
394 | struct sway_output *output) { | 448 | struct sway_output *output) { |
395 | if (config->default_orientation != L_NONE) { | 449 | if (config->default_orientation != L_NONE) { |
diff --git a/sway/tree/root.c b/sway/tree/root.c index 8508e9eb..20fcfa59 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c | |||
@@ -1,12 +1,14 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 4 | #include <wlr/types/wlr_output_layout.h> |
5 | #include <wlr/types/wlr_scene.h> | ||
6 | #include <wlr/util/transform.h> | ||
6 | #include "sway/desktop/transaction.h" | 7 | #include "sway/desktop/transaction.h" |
7 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
8 | #include "sway/ipc-server.h" | 9 | #include "sway/ipc-server.h" |
9 | #include "sway/output.h" | 10 | #include "sway/output.h" |
11 | #include "sway/scene_descriptor.h" | ||
10 | #include "sway/tree/arrange.h" | 12 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/container.h" | 13 | #include "sway/tree/container.h" |
12 | #include "sway/tree/root.h" | 14 | #include "sway/tree/root.h" |
@@ -23,21 +25,60 @@ static void output_layout_handle_change(struct wl_listener *listener, | |||
23 | transaction_commit_dirty(); | 25 | transaction_commit_dirty(); |
24 | } | 26 | } |
25 | 27 | ||
26 | struct sway_root *root_create(void) { | 28 | struct sway_root *root_create(struct wl_display *wl_display) { |
27 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); | 29 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); |
28 | if (!root) { | 30 | if (!root) { |
29 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); | 31 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); |
30 | return NULL; | 32 | return NULL; |
31 | } | 33 | } |
34 | |||
35 | struct wlr_scene *root_scene = wlr_scene_create(); | ||
36 | if (!root_scene) { | ||
37 | sway_log(SWAY_ERROR, "Unable to allocate root scene node"); | ||
38 | free(root); | ||
39 | return NULL; | ||
40 | } | ||
41 | |||
32 | node_init(&root->node, N_ROOT, root); | 42 | node_init(&root->node, N_ROOT, root); |
33 | root->output_layout = wlr_output_layout_create(); | 43 | root->root_scene = root_scene; |
34 | wl_list_init(&root->all_outputs); | 44 | |
35 | #if HAVE_XWAYLAND | 45 | bool failed = false; |
36 | wl_list_init(&root->xwayland_unmanaged); | 46 | root->staging = alloc_scene_tree(&root_scene->tree, &failed); |
47 | root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); | ||
48 | |||
49 | root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed); | ||
50 | root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed); | ||
51 | root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); | ||
52 | root->layers.floating = alloc_scene_tree(root->layer_tree, &failed); | ||
53 | root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); | ||
54 | root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); | ||
55 | root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); | ||
56 | #if WLR_HAS_XWAYLAND | ||
57 | root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); | ||
37 | #endif | 58 | #endif |
38 | wl_list_init(&root->drag_icons); | 59 | root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); |
60 | root->layers.popup = alloc_scene_tree(root->layer_tree, &failed); | ||
61 | root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); | ||
62 | root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); | ||
63 | |||
64 | if (!failed && !scene_descriptor_assign(&root->layers.seat->node, | ||
65 | SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) { | ||
66 | failed = true; | ||
67 | } | ||
68 | |||
69 | if (failed) { | ||
70 | wlr_scene_node_destroy(&root_scene->tree.node); | ||
71 | free(root); | ||
72 | return NULL; | ||
73 | } | ||
74 | |||
75 | wlr_scene_node_set_enabled(&root->staging->node, false); | ||
76 | |||
77 | root->output_layout = wlr_output_layout_create(wl_display); | ||
78 | wl_list_init(&root->all_outputs); | ||
39 | wl_signal_init(&root->events.new_node); | 79 | wl_signal_init(&root->events.new_node); |
40 | root->outputs = create_list(); | 80 | root->outputs = create_list(); |
81 | root->non_desktop_outputs = create_list(); | ||
41 | root->scratchpad = create_list(); | 82 | root->scratchpad = create_list(); |
42 | 83 | ||
43 | root->output_layout_change.notify = output_layout_handle_change; | 84 | root->output_layout_change.notify = output_layout_handle_change; |
@@ -49,11 +90,22 @@ struct sway_root *root_create(void) { | |||
49 | void root_destroy(struct sway_root *root) { | 90 | void root_destroy(struct sway_root *root) { |
50 | wl_list_remove(&root->output_layout_change.link); | 91 | wl_list_remove(&root->output_layout_change.link); |
51 | list_free(root->scratchpad); | 92 | list_free(root->scratchpad); |
93 | list_free(root->non_desktop_outputs); | ||
52 | list_free(root->outputs); | 94 | list_free(root->outputs); |
53 | wlr_output_layout_destroy(root->output_layout); | 95 | wlr_scene_node_destroy(&root->root_scene->tree.node); |
54 | free(root); | 96 | free(root); |
55 | } | 97 | } |
56 | 98 | ||
99 | static void set_container_transform(struct sway_workspace *ws, | ||
100 | struct sway_container *con) { | ||
101 | struct sway_output *output = ws->output; | ||
102 | struct wlr_box box = {0}; | ||
103 | if (output) { | ||
104 | output_get_box(output, &box); | ||
105 | } | ||
106 | con->transform = box; | ||
107 | } | ||
108 | |||
57 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { | 109 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { |
58 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { | 110 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { |
59 | return; | 111 | return; |
@@ -62,6 +114,8 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works | |||
62 | struct sway_container *parent = con->pending.parent; | 114 | struct sway_container *parent = con->pending.parent; |
63 | struct sway_workspace *workspace = con->pending.workspace; | 115 | struct sway_workspace *workspace = con->pending.workspace; |
64 | 116 | ||
117 | set_container_transform(workspace, con); | ||
118 | |||
65 | // Clear the fullscreen mode when sending to the scratchpad | 119 | // Clear the fullscreen mode when sending to the scratchpad |
66 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { | 120 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
67 | container_fullscreen_disable(con); | 121 | container_fullscreen_disable(con); |
@@ -131,7 +185,10 @@ void root_scratchpad_show(struct sway_container *con) { | |||
131 | // Show the container | 185 | // Show the container |
132 | if (old_ws) { | 186 | if (old_ws) { |
133 | container_detach(con); | 187 | container_detach(con); |
134 | workspace_consider_destroy(old_ws); | 188 | // Make sure the last inactive container on the old workspace is above |
189 | // the workspace itself in the focus stack. | ||
190 | struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node); | ||
191 | seat_set_raw_focus(seat, node); | ||
135 | } else { | 192 | } else { |
136 | // Act on the ancestor of scratchpad hidden split containers | 193 | // Act on the ancestor of scratchpad hidden split containers |
137 | while (con->pending.parent) { | 194 | while (con->pending.parent) { |
@@ -140,18 +197,18 @@ void root_scratchpad_show(struct sway_container *con) { | |||
140 | } | 197 | } |
141 | workspace_add_floating(new_ws, con); | 198 | workspace_add_floating(new_ws, con); |
142 | 199 | ||
143 | // Make sure the container's center point overlaps this workspace | 200 | if (new_ws->output) { |
144 | double center_lx = con->pending.x + con->pending.width / 2; | 201 | struct wlr_box output_box; |
145 | double center_ly = con->pending.y + con->pending.height / 2; | 202 | output_get_box(new_ws->output, &output_box); |
146 | 203 | floating_fix_coordinates(con, &con->transform, &output_box); | |
147 | struct wlr_box workspace_box; | ||
148 | workspace_get_box(new_ws, &workspace_box); | ||
149 | if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { | ||
150 | container_floating_resize_and_center(con); | ||
151 | } | 204 | } |
205 | set_container_transform(new_ws, con); | ||
152 | 206 | ||
153 | arrange_workspace(new_ws); | 207 | arrange_workspace(new_ws); |
154 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); | 208 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); |
209 | if (old_ws) { | ||
210 | workspace_consider_destroy(old_ws); | ||
211 | } | ||
155 | } | 212 | } |
156 | 213 | ||
157 | static void disable_fullscreen(struct sway_container *con, void *data) { | 214 | static void disable_fullscreen(struct sway_container *con, void *data) { |
@@ -171,6 +228,8 @@ void root_scratchpad_hide(struct sway_container *con) { | |||
171 | return; | 228 | return; |
172 | } | 229 | } |
173 | 230 | ||
231 | set_container_transform(con->pending.workspace, con); | ||
232 | |||
174 | disable_fullscreen(con, NULL); | 233 | disable_fullscreen(con, NULL); |
175 | container_for_each_child(con, disable_fullscreen, NULL); | 234 | container_for_each_child(con, disable_fullscreen, NULL); |
176 | container_detach(con); | 235 | container_detach(con); |
@@ -183,172 +242,6 @@ void root_scratchpad_hide(struct sway_container *con) { | |||
183 | ipc_event_window(con, "move"); | 242 | ipc_event_window(con, "move"); |
184 | } | 243 | } |
185 | 244 | ||
186 | struct pid_workspace { | ||
187 | pid_t pid; | ||
188 | char *workspace; | ||
189 | struct timespec time_added; | ||
190 | |||
191 | struct sway_output *output; | ||
192 | struct wl_listener output_destroy; | ||
193 | |||
194 | struct wl_list link; | ||
195 | }; | ||
196 | |||
197 | static struct wl_list pid_workspaces; | ||
198 | |||
199 | /** | ||
200 | * Get the pid of a parent process given the pid of a child process. | ||
201 | * | ||
202 | * Returns the parent pid or NULL if the parent pid cannot be determined. | ||
203 | */ | ||
204 | static pid_t get_parent_pid(pid_t child) { | ||
205 | pid_t parent = -1; | ||
206 | char file_name[100]; | ||
207 | char *buffer = NULL; | ||
208 | const char *sep = " "; | ||
209 | FILE *stat = NULL; | ||
210 | size_t buf_size = 0; | ||
211 | |||
212 | snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); | ||
213 | |||
214 | if ((stat = fopen(file_name, "r"))) { | ||
215 | if (getline(&buffer, &buf_size, stat) != -1) { | ||
216 | strtok(buffer, sep); // pid | ||
217 | strtok(NULL, sep); // executable name | ||
218 | strtok(NULL, sep); // state | ||
219 | char *token = strtok(NULL, sep); // parent pid | ||
220 | parent = strtol(token, NULL, 10); | ||
221 | } | ||
222 | free(buffer); | ||
223 | fclose(stat); | ||
224 | } | ||
225 | |||
226 | if (parent) { | ||
227 | return (parent == child) ? -1 : parent; | ||
228 | } | ||
229 | |||
230 | return -1; | ||
231 | } | ||
232 | |||
233 | static void pid_workspace_destroy(struct pid_workspace *pw) { | ||
234 | wl_list_remove(&pw->output_destroy.link); | ||
235 | wl_list_remove(&pw->link); | ||
236 | free(pw->workspace); | ||
237 | free(pw); | ||
238 | } | ||
239 | |||
240 | struct sway_workspace *root_workspace_for_pid(pid_t pid) { | ||
241 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
242 | wl_list_init(&pid_workspaces); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | struct sway_workspace *ws = NULL; | ||
247 | struct pid_workspace *pw = NULL; | ||
248 | |||
249 | sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); | ||
250 | |||
251 | do { | ||
252 | struct pid_workspace *_pw = NULL; | ||
253 | wl_list_for_each(_pw, &pid_workspaces, link) { | ||
254 | if (pid == _pw->pid) { | ||
255 | pw = _pw; | ||
256 | sway_log(SWAY_DEBUG, | ||
257 | "found pid_workspace for pid %d, workspace %s", | ||
258 | pid, pw->workspace); | ||
259 | goto found; | ||
260 | } | ||
261 | } | ||
262 | pid = get_parent_pid(pid); | ||
263 | } while (pid > 1); | ||
264 | found: | ||
265 | |||
266 | if (pw && pw->workspace) { | ||
267 | ws = workspace_by_name(pw->workspace); | ||
268 | |||
269 | if (!ws) { | ||
270 | sway_log(SWAY_DEBUG, | ||
271 | "Creating workspace %s for pid %d because it disappeared", | ||
272 | pw->workspace, pid); | ||
273 | |||
274 | struct sway_output *output = pw->output; | ||
275 | if (pw->output && !pw->output->enabled) { | ||
276 | sway_log(SWAY_DEBUG, | ||
277 | "Workspace output %s is disabled, trying another one", | ||
278 | pw->output->wlr_output->name); | ||
279 | output = NULL; | ||
280 | } | ||
281 | |||
282 | ws = workspace_create(output, pw->workspace); | ||
283 | } | ||
284 | |||
285 | pid_workspace_destroy(pw); | ||
286 | } | ||
287 | |||
288 | return ws; | ||
289 | } | ||
290 | |||
291 | static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | ||
292 | struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); | ||
293 | pw->output = NULL; | ||
294 | wl_list_remove(&pw->output_destroy.link); | ||
295 | wl_list_init(&pw->output_destroy.link); | ||
296 | } | ||
297 | |||
298 | void root_record_workspace_pid(pid_t pid) { | ||
299 | sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid); | ||
300 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
301 | wl_list_init(&pid_workspaces); | ||
302 | } | ||
303 | |||
304 | struct sway_seat *seat = input_manager_current_seat(); | ||
305 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
306 | if (!ws) { | ||
307 | sway_log(SWAY_DEBUG, "Bailing out, no workspace"); | ||
308 | return; | ||
309 | } | ||
310 | struct sway_output *output = ws->output; | ||
311 | if (!output) { | ||
312 | sway_log(SWAY_DEBUG, "Bailing out, no output"); | ||
313 | return; | ||
314 | } | ||
315 | |||
316 | struct timespec now; | ||
317 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
318 | |||
319 | // Remove expired entries | ||
320 | static const int timeout = 60; | ||
321 | struct pid_workspace *old, *_old; | ||
322 | wl_list_for_each_safe(old, _old, &pid_workspaces, link) { | ||
323 | if (now.tv_sec - old->time_added.tv_sec >= timeout) { | ||
324 | pid_workspace_destroy(old); | ||
325 | } | ||
326 | } | ||
327 | |||
328 | struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); | ||
329 | pw->workspace = strdup(ws->name); | ||
330 | pw->output = output; | ||
331 | pw->pid = pid; | ||
332 | memcpy(&pw->time_added, &now, sizeof(struct timespec)); | ||
333 | pw->output_destroy.notify = pw_handle_output_destroy; | ||
334 | wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); | ||
335 | wl_list_insert(&pid_workspaces, &pw->link); | ||
336 | } | ||
337 | |||
338 | void root_remove_workspace_pid(pid_t pid) { | ||
339 | if (!pid_workspaces.prev || !pid_workspaces.next) { | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | struct pid_workspace *pw, *tmp; | ||
344 | wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) { | ||
345 | if (pid == pw->pid) { | ||
346 | pid_workspace_destroy(pw); | ||
347 | return; | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | |||
352 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), | 245 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), |
353 | void *data) { | 246 | void *data) { |
354 | for (int i = 0; i < root->outputs->length; ++i) { | 247 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -443,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { | |||
443 | box->width = root->width; | 336 | box->width = root->width; |
444 | box->height = root->height; | 337 | box->height = root->height; |
445 | } | 338 | } |
446 | |||
447 | void root_rename_pid_workspaces(const char *old_name, const char *new_name) { | ||
448 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
449 | wl_list_init(&pid_workspaces); | ||
450 | } | ||
451 | |||
452 | struct pid_workspace *pw = NULL; | ||
453 | wl_list_for_each(pw, &pid_workspaces, link) { | ||
454 | if (strcmp(pw->workspace, old_name) == 0) { | ||
455 | free(pw->workspace); | ||
456 | pw->workspace = strdup(new_name); | ||
457 | } | ||
458 | } | ||
459 | } | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7d9e038d..1c1c8ee8 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,29 +1,32 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include <wlr/config.h> | ||
5 | #include <wlr/render/wlr_renderer.h> | 5 | #include <wlr/render/wlr_renderer.h> |
6 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
7 | #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> | ||
8 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_output_layout.h> | 9 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_server_decoration.h> | 10 | #include <wlr/types/wlr_server_decoration.h> |
9 | #include <wlr/types/wlr_subcompositor.h> | 11 | #include <wlr/types/wlr_subcompositor.h> |
10 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 12 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
11 | #include "config.h" | 13 | #if WLR_HAS_XWAYLAND |
12 | #if HAVE_XWAYLAND | ||
13 | #include <wlr/xwayland.h> | 14 | #include <wlr/xwayland.h> |
14 | #endif | 15 | #endif |
15 | #include "list.h" | 16 | #include "list.h" |
16 | #include "log.h" | 17 | #include "log.h" |
17 | #include "sway/criteria.h" | 18 | #include "sway/criteria.h" |
18 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
19 | #include "sway/desktop.h" | ||
20 | #include "sway/desktop/transaction.h" | 20 | #include "sway/desktop/transaction.h" |
21 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | #include "sway/desktop/launcher.h" | ||
22 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
23 | #include "sway/ipc-server.h" | 24 | #include "sway/ipc-server.h" |
24 | #include "sway/output.h" | 25 | #include "sway/output.h" |
25 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/scene_descriptor.h" | ||
26 | #include "sway/server.h" | 28 | #include "sway/server.h" |
29 | #include "sway/sway_text_node.h" | ||
27 | #include "sway/tree/arrange.h" | 30 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/container.h" | 31 | #include "sway/tree/container.h" |
29 | #include "sway/tree/view.h" | 32 | #include "sway/tree/view.h" |
@@ -33,15 +36,29 @@ | |||
33 | #include "pango.h" | 36 | #include "pango.h" |
34 | #include "stringop.h" | 37 | #include "stringop.h" |
35 | 38 | ||
36 | void view_init(struct sway_view *view, enum sway_view_type type, | 39 | bool view_init(struct sway_view *view, enum sway_view_type type, |
37 | const struct sway_view_impl *impl) { | 40 | const struct sway_view_impl *impl) { |
41 | bool failed = false; | ||
42 | view->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
43 | view->content_tree = alloc_scene_tree(view->scene_tree, &failed); | ||
44 | |||
45 | if (!failed && !scene_descriptor_assign(&view->scene_tree->node, | ||
46 | SWAY_SCENE_DESC_VIEW, view)) { | ||
47 | failed = true; | ||
48 | } | ||
49 | |||
50 | if (failed) { | ||
51 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
52 | return false; | ||
53 | } | ||
54 | |||
38 | view->type = type; | 55 | view->type = type; |
39 | view->impl = impl; | 56 | view->impl = impl; |
40 | view->executed_criteria = create_list(); | 57 | view->executed_criteria = create_list(); |
41 | wl_list_init(&view->saved_buffers); | ||
42 | view->allow_request_urgent = true; | 58 | view->allow_request_urgent = true; |
43 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 59 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
44 | wl_signal_init(&view->events.unmap); | 60 | wl_signal_init(&view->events.unmap); |
61 | return true; | ||
45 | } | 62 | } |
46 | 63 | ||
47 | void view_destroy(struct sway_view *view) { | 64 | void view_destroy(struct sway_view *view) { |
@@ -58,11 +75,10 @@ void view_destroy(struct sway_view *view) { | |||
58 | return; | 75 | return; |
59 | } | 76 | } |
60 | wl_list_remove(&view->events.unmap.listener_list); | 77 | wl_list_remove(&view->events.unmap.listener_list); |
61 | if (!wl_list_empty(&view->saved_buffers)) { | ||
62 | view_remove_saved_buffer(view); | ||
63 | } | ||
64 | list_free(view->executed_criteria); | 78 | list_free(view->executed_criteria); |
65 | 79 | ||
80 | view_assign_ctx(view, NULL); | ||
81 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
66 | free(view->title_format); | 82 | free(view->title_format); |
67 | 83 | ||
68 | if (view->impl->destroy) { | 84 | if (view->impl->destroy) { |
@@ -110,7 +126,7 @@ const char *view_get_instance(struct sway_view *view) { | |||
110 | } | 126 | } |
111 | return NULL; | 127 | return NULL; |
112 | } | 128 | } |
113 | #if HAVE_XWAYLAND | 129 | #if WLR_HAS_XWAYLAND |
114 | uint32_t view_get_x11_window_id(struct sway_view *view) { | 130 | uint32_t view_get_x11_window_id(struct sway_view *view) { |
115 | if (view->impl->get_int_prop) { | 131 | if (view->impl->get_int_prop) { |
116 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); | 132 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); |
@@ -143,7 +159,7 @@ const char *view_get_shell(struct sway_view *view) { | |||
143 | switch(view->type) { | 159 | switch(view->type) { |
144 | case SWAY_VIEW_XDG_SHELL: | 160 | case SWAY_VIEW_XDG_SHELL: |
145 | return "xdg_shell"; | 161 | return "xdg_shell"; |
146 | #if HAVE_XWAYLAND | 162 | #if WLR_HAS_XWAYLAND |
147 | case SWAY_VIEW_XWAYLAND: | 163 | case SWAY_VIEW_XWAYLAND: |
148 | return "xwayland"; | 164 | return "xwayland"; |
149 | #endif | 165 | #endif |
@@ -363,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
363 | } | 379 | } |
364 | } | 380 | } |
365 | 381 | ||
366 | void view_request_activate(struct sway_view *view) { | 382 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
367 | struct sway_workspace *ws = view->container->pending.workspace; | 383 | struct sway_workspace *ws = view->container->pending.workspace; |
368 | if (!ws) { // hidden scratchpad container | 384 | if (!seat) { |
369 | return; | 385 | seat = input_manager_current_seat(); |
370 | } | 386 | } |
371 | struct sway_seat *seat = input_manager_current_seat(); | ||
372 | 387 | ||
373 | switch (config->focus_on_window_activation) { | 388 | switch (config->focus_on_window_activation) { |
374 | case FOWA_SMART: | 389 | case FOWA_SMART: |
375 | if (workspace_is_visible(ws)) { | 390 | if (ws && workspace_is_visible(ws)) { |
376 | seat_set_focus_container(seat, view->container); | 391 | seat_set_focus_container(seat, view->container); |
392 | container_raise_floating(view->container); | ||
377 | } else { | 393 | } else { |
378 | view_set_urgent(view, true); | 394 | view_set_urgent(view, true); |
379 | } | 395 | } |
@@ -382,11 +398,23 @@ void view_request_activate(struct sway_view *view) { | |||
382 | view_set_urgent(view, true); | 398 | view_set_urgent(view, true); |
383 | break; | 399 | break; |
384 | case FOWA_FOCUS: | 400 | case FOWA_FOCUS: |
385 | seat_set_focus_container(seat, view->container); | 401 | if (container_is_scratchpad_hidden_or_child(view->container)) { |
402 | root_scratchpad_show(view->container); | ||
403 | } else { | ||
404 | seat_set_focus_container(seat, view->container); | ||
405 | container_raise_floating(view->container); | ||
406 | } | ||
386 | break; | 407 | break; |
387 | case FOWA_NONE: | 408 | case FOWA_NONE: |
388 | break; | 409 | break; |
389 | } | 410 | } |
411 | transaction_commit_dirty(); | ||
412 | } | ||
413 | |||
414 | void view_request_urgent(struct sway_view *view) { | ||
415 | if (config->focus_on_window_activation != FOWA_NONE) { | ||
416 | view_set_urgent(view, true); | ||
417 | } | ||
390 | } | 418 | } |
391 | 419 | ||
392 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { | 420 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { |
@@ -433,52 +461,6 @@ void view_close_popups(struct sway_view *view) { | |||
433 | } | 461 | } |
434 | } | 462 | } |
435 | 463 | ||
436 | void view_damage_from(struct sway_view *view) { | ||
437 | for (int i = 0; i < root->outputs->length; ++i) { | ||
438 | struct sway_output *output = root->outputs->items[i]; | ||
439 | output_damage_from_view(output, view); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | void view_for_each_surface(struct sway_view *view, | ||
444 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
445 | if (!view->surface) { | ||
446 | return; | ||
447 | } | ||
448 | if (view->impl->for_each_surface) { | ||
449 | view->impl->for_each_surface(view, iterator, user_data); | ||
450 | } else { | ||
451 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | void view_for_each_popup_surface(struct sway_view *view, | ||
456 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
457 | if (!view->surface) { | ||
458 | return; | ||
459 | } | ||
460 | if (view->impl->for_each_popup_surface) { | ||
461 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | static void view_subsurface_create(struct sway_view *view, | ||
466 | struct wlr_subsurface *subsurface); | ||
467 | |||
468 | static void view_init_subsurfaces(struct sway_view *view, | ||
469 | struct wlr_surface *surface); | ||
470 | |||
471 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
472 | struct wlr_surface *surface); | ||
473 | |||
474 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
475 | void *data) { | ||
476 | struct sway_view *view = | ||
477 | wl_container_of(listener, view, surface_new_subsurface); | ||
478 | struct wlr_subsurface *subsurface = data; | ||
479 | view_subsurface_create(view, subsurface); | ||
480 | } | ||
481 | |||
482 | static bool view_has_executed_criteria(struct sway_view *view, | 464 | static bool view_has_executed_criteria(struct sway_view *view, |
483 | struct criteria *criteria) { | 465 | struct criteria *criteria) { |
484 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 466 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -517,10 +499,10 @@ void view_execute_criteria(struct sway_view *view) { | |||
517 | static void view_populate_pid(struct sway_view *view) { | 499 | static void view_populate_pid(struct sway_view *view) { |
518 | pid_t pid; | 500 | pid_t pid; |
519 | switch (view->type) { | 501 | switch (view->type) { |
520 | #if HAVE_XWAYLAND | 502 | #if WLR_HAS_XWAYLAND |
521 | case SWAY_VIEW_XWAYLAND:; | 503 | case SWAY_VIEW_XWAYLAND:; |
522 | struct wlr_xwayland_surface *surf = | 504 | struct wlr_xwayland_surface *surf = |
523 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 505 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
524 | pid = surf->pid; | 506 | pid = surf->pid; |
525 | break; | 507 | break; |
526 | #endif | 508 | #endif |
@@ -533,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
533 | view->pid = pid; | 515 | view->pid = pid; |
534 | } | 516 | } |
535 | 517 | ||
518 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { | ||
519 | if (view->ctx) { | ||
520 | // This ctx has been replaced | ||
521 | launcher_ctx_destroy(view->ctx); | ||
522 | view->ctx = NULL; | ||
523 | } | ||
524 | if (ctx == NULL) { | ||
525 | return; | ||
526 | } | ||
527 | launcher_ctx_consume(ctx); | ||
528 | |||
529 | view->ctx = ctx; | ||
530 | } | ||
531 | |||
536 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 532 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
537 | struct sway_seat *seat = input_manager_current_seat(); | 533 | struct sway_seat *seat = input_manager_current_seat(); |
538 | 534 | ||
@@ -568,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
568 | } | 564 | } |
569 | list_free(criterias); | 565 | list_free(criterias); |
570 | if (ws) { | 566 | if (ws) { |
571 | root_remove_workspace_pid(view->pid); | 567 | view_assign_ctx(view, NULL); |
572 | return ws; | 568 | return ws; |
573 | } | 569 | } |
574 | 570 | ||
575 | // Check if there's a PID mapping | 571 | // Check if there's a PID mapping |
576 | ws = root_workspace_for_pid(view->pid); | 572 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
577 | if (ws) { | 573 | if (ws) { |
574 | view_assign_ctx(view, NULL); | ||
578 | return ws; | 575 | return ws; |
579 | } | 576 | } |
580 | 577 | ||
@@ -592,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
592 | return NULL; | 589 | return NULL; |
593 | } | 590 | } |
594 | 591 | ||
592 | static void update_ext_foreign_toplevel(struct sway_view *view) { | ||
593 | struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = { | ||
594 | .app_id = view_get_app_id(view), | ||
595 | .title = view_get_title(view), | ||
596 | }; | ||
597 | wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state); | ||
598 | } | ||
599 | |||
595 | static bool should_focus(struct sway_view *view) { | 600 | static bool should_focus(struct sway_view *view) { |
596 | struct sway_seat *seat = input_manager_current_seat(); | 601 | struct sway_seat *seat = input_manager_current_seat(); |
597 | struct sway_container *prev_con = seat_get_focused_container(seat); | 602 | struct sway_container *prev_con = seat_get_focused_container(seat); |
@@ -717,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
717 | view_populate_pid(view); | 722 | view_populate_pid(view); |
718 | view->container = container_create(view); | 723 | view->container = container_create(view); |
719 | 724 | ||
725 | if (view->ctx == NULL) { | ||
726 | struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid); | ||
727 | if (ctx != NULL) { | ||
728 | view_assign_ctx(view, ctx); | ||
729 | } | ||
730 | } | ||
731 | |||
720 | // If there is a request to be opened fullscreen on a specific output, try | 732 | // If there is a request to be opened fullscreen on a specific output, try |
721 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 733 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
722 | // focused workspace, etc | 734 | // focused workspace, etc |
@@ -754,6 +766,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
754 | } | 766 | } |
755 | } | 767 | } |
756 | 768 | ||
769 | struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { | ||
770 | .app_id = view_get_app_id(view), | ||
771 | .title = view_get_title(view), | ||
772 | }; | ||
773 | view->ext_foreign_toplevel = | ||
774 | wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); | ||
775 | |||
757 | view->foreign_toplevel = | 776 | view->foreign_toplevel = |
758 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 777 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
759 | view->foreign_activate_request.notify = handle_foreign_activate_request; | 778 | view->foreign_activate_request.notify = handle_foreign_activate_request; |
@@ -777,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
777 | } | 796 | } |
778 | ipc_event_window(view->container, "new"); | 797 | ipc_event_window(view->container, "new"); |
779 | 798 | ||
780 | view_init_subsurfaces(view, wlr_surface); | ||
781 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
782 | &view->surface_new_subsurface); | ||
783 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
784 | |||
785 | if (decoration) { | 799 | if (decoration) { |
786 | view_update_csd_from_client(view, decoration); | 800 | view_update_csd_from_client(view, decoration); |
787 | } | 801 | } |
@@ -824,10 +838,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
824 | 838 | ||
825 | bool set_focus = should_focus(view); | 839 | bool set_focus = should_focus(view); |
826 | 840 | ||
827 | #if HAVE_XWAYLAND | 841 | #if WLR_HAS_XWAYLAND |
828 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 842 | struct wlr_xwayland_surface *xsurface; |
829 | struct wlr_xwayland_surface *xsurface = | 843 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
830 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
831 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != | 844 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
832 | WLR_ICCCM_INPUT_MODEL_NONE; | 845 | WLR_ICCCM_INPUT_MODEL_NONE; |
833 | } | 846 | } |
@@ -837,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
837 | input_manager_set_focus(&view->container->node); | 850 | input_manager_set_focus(&view->container->node); |
838 | } | 851 | } |
839 | 852 | ||
853 | if (view->ext_foreign_toplevel) { | ||
854 | update_ext_foreign_toplevel(view); | ||
855 | } | ||
856 | |||
840 | const char *app_id; | 857 | const char *app_id; |
841 | const char *class; | 858 | const char *class; |
842 | if ((app_id = view_get_app_id(view)) != NULL) { | 859 | if ((app_id = view_get_app_id(view)) != NULL) { |
@@ -847,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
847 | } | 864 | } |
848 | 865 | ||
849 | void view_unmap(struct sway_view *view) { | 866 | void view_unmap(struct sway_view *view) { |
850 | wl_signal_emit(&view->events.unmap, view); | 867 | wl_signal_emit_mutable(&view->events.unmap, view); |
851 | 868 | ||
852 | wl_list_remove(&view->surface_new_subsurface.link); | 869 | view->executed_criteria->length = 0; |
853 | 870 | ||
854 | if (view->urgent_timer) { | 871 | if (view->urgent_timer) { |
855 | wl_event_source_remove(view->urgent_timer); | 872 | wl_event_source_remove(view->urgent_timer); |
856 | view->urgent_timer = NULL; | 873 | view->urgent_timer = NULL; |
857 | } | 874 | } |
858 | 875 | ||
876 | if (view->ext_foreign_toplevel) { | ||
877 | wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel); | ||
878 | view->ext_foreign_toplevel = NULL; | ||
879 | } | ||
880 | |||
859 | if (view->foreign_toplevel) { | 881 | if (view->foreign_toplevel) { |
860 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); | 882 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); |
861 | view->foreign_toplevel = NULL; | 883 | view->foreign_toplevel = NULL; |
@@ -902,291 +924,54 @@ void view_update_size(struct sway_view *view) { | |||
902 | container_set_geometry_from_content(con); | 924 | container_set_geometry_from_content(con); |
903 | } | 925 | } |
904 | 926 | ||
905 | void view_center_surface(struct sway_view *view) { | 927 | void view_center_and_clip_surface(struct sway_view *view) { |
906 | struct sway_container *con = view->container; | 928 | struct sway_container *con = view->container; |
907 | // We always center the current coordinates rather than the next, as the | ||
908 | // geometry immediately affects the currently active rendering. | ||
909 | con->surface_x = fmax(con->current.content_x, con->current.content_x + | ||
910 | (con->current.content_width - view->geometry.width) / 2); | ||
911 | con->surface_y = fmax(con->current.content_y, con->current.content_y + | ||
912 | (con->current.content_height - view->geometry.height) / 2); | ||
913 | } | ||
914 | 929 | ||
915 | static const struct sway_view_child_impl subsurface_impl; | 930 | bool clip_to_geometry = true; |
916 | 931 | ||
917 | static void subsurface_get_view_coords(struct sway_view_child *child, | 932 | if (container_is_floating(con)) { |
918 | int *sx, int *sy) { | 933 | // We always center the current coordinates rather than the next, as the |
919 | struct wlr_surface *surface = child->surface; | 934 | // geometry immediately affects the currently active rendering. |
920 | if (child->parent && child->parent->impl && | 935 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); |
921 | child->parent->impl->get_view_coords) { | 936 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); |
922 | child->parent->impl->get_view_coords(child->parent, sx, sy); | 937 | clip_to_geometry = !view->using_csd; |
923 | } else { | ||
924 | *sx = *sy = 0; | ||
925 | } | ||
926 | struct wlr_subsurface *subsurface = | ||
927 | wlr_subsurface_from_wlr_surface(surface); | ||
928 | *sx += subsurface->current.x; | ||
929 | *sy += subsurface->current.y; | ||
930 | } | ||
931 | 938 | ||
932 | static void subsurface_destroy(struct sway_view_child *child) { | 939 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
933 | if (!sway_assert(child->impl == &subsurface_impl, | ||
934 | "Expected a subsurface")) { | ||
935 | return; | ||
936 | } | ||
937 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | ||
938 | wl_list_remove(&subsurface->destroy.link); | ||
939 | free(subsurface); | ||
940 | } | ||
941 | |||
942 | static const struct sway_view_child_impl subsurface_impl = { | ||
943 | .get_view_coords = subsurface_get_view_coords, | ||
944 | .destroy = subsurface_destroy, | ||
945 | }; | ||
946 | |||
947 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
948 | void *data) { | ||
949 | struct sway_subsurface *subsurface = | ||
950 | wl_container_of(listener, subsurface, destroy); | ||
951 | struct sway_view_child *child = &subsurface->child; | ||
952 | view_child_destroy(child); | ||
953 | } | ||
954 | |||
955 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
956 | |||
957 | static void view_subsurface_create(struct sway_view *view, | ||
958 | struct wlr_subsurface *wlr_subsurface) { | ||
959 | struct sway_subsurface *subsurface = | ||
960 | calloc(1, sizeof(struct sway_subsurface)); | ||
961 | if (subsurface == NULL) { | ||
962 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
963 | return; | ||
964 | } | ||
965 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
966 | wlr_subsurface->surface); | ||
967 | |||
968 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
969 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
970 | |||
971 | subsurface->child.mapped = true; | ||
972 | |||
973 | view_child_damage(&subsurface->child, true); | ||
974 | } | ||
975 | |||
976 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
977 | struct wlr_subsurface *wlr_subsurface) { | ||
978 | struct sway_subsurface *subsurface = | ||
979 | calloc(1, sizeof(struct sway_subsurface)); | ||
980 | if (subsurface == NULL) { | ||
981 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
982 | return; | ||
983 | } | ||
984 | subsurface->child.parent = child; | ||
985 | wl_list_insert(&child->children, &subsurface->child.link); | ||
986 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
987 | wlr_subsurface->surface); | ||
988 | |||
989 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
990 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
991 | |||
992 | subsurface->child.mapped = true; | ||
993 | |||
994 | view_child_damage(&subsurface->child, true); | ||
995 | } | ||
996 | |||
997 | static bool view_child_is_mapped(struct sway_view_child *child) { | ||
998 | while (child) { | ||
999 | if (!child->mapped) { | ||
1000 | return false; | ||
1001 | } | ||
1002 | child = child->parent; | ||
1003 | } | ||
1004 | return true; | ||
1005 | } | ||
1006 | |||
1007 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
1008 | if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { | ||
1009 | return; | ||
1010 | } | ||
1011 | int sx, sy; | ||
1012 | child->impl->get_view_coords(child, &sx, &sy); | ||
1013 | desktop_damage_surface(child->surface, | ||
1014 | child->view->container->pending.content_x - | ||
1015 | child->view->geometry.x + sx, | ||
1016 | child->view->container->pending.content_y - | ||
1017 | child->view->geometry.y + sy, whole); | ||
1018 | } | ||
1019 | |||
1020 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
1021 | void *data) { | ||
1022 | struct sway_view_child *child = | ||
1023 | wl_container_of(listener, child, surface_commit); | ||
1024 | view_child_damage(child, false); | ||
1025 | } | ||
1026 | |||
1027 | static void view_child_handle_surface_new_subsurface( | ||
1028 | struct wl_listener *listener, void *data) { | ||
1029 | struct sway_view_child *child = | ||
1030 | wl_container_of(listener, child, surface_new_subsurface); | ||
1031 | struct wlr_subsurface *subsurface = data; | ||
1032 | view_child_subsurface_create(child, subsurface); | ||
1033 | } | ||
1034 | |||
1035 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
1036 | void *data) { | ||
1037 | struct sway_view_child *child = | ||
1038 | wl_container_of(listener, child, surface_destroy); | ||
1039 | view_child_destroy(child); | ||
1040 | } | ||
1041 | |||
1042 | static void view_init_subsurfaces(struct sway_view *view, | ||
1043 | struct wlr_surface *surface) { | ||
1044 | struct wlr_subsurface *subsurface; | ||
1045 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, | ||
1046 | current.link) { | ||
1047 | view_subsurface_create(view, subsurface); | ||
1048 | } | ||
1049 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1050 | current.link) { | ||
1051 | view_subsurface_create(view, subsurface); | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
1056 | struct wlr_surface *surface) { | ||
1057 | struct wlr_subsurface *subsurface; | ||
1058 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, | ||
1059 | current.link) { | ||
1060 | view_child_subsurface_create(view_child, subsurface); | ||
1061 | } | ||
1062 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1063 | current.link) { | ||
1064 | view_child_subsurface_create(view_child, subsurface); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1069 | void *data) { | ||
1070 | struct sway_view_child *child = | ||
1071 | wl_container_of(listener, child, surface_map); | ||
1072 | child->mapped = true; | ||
1073 | view_child_damage(child, true); | ||
1074 | } | ||
1075 | |||
1076 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1077 | void *data) { | ||
1078 | struct sway_view_child *child = | ||
1079 | wl_container_of(listener, child, surface_unmap); | ||
1080 | view_child_damage(child, true); | ||
1081 | child->mapped = false; | ||
1082 | } | ||
1083 | |||
1084 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1085 | void *data) { | ||
1086 | struct sway_view_child *child = | ||
1087 | wl_container_of(listener, child, view_unmap); | ||
1088 | view_child_damage(child, true); | ||
1089 | child->mapped = false; | ||
1090 | } | ||
1091 | |||
1092 | void view_child_init(struct sway_view_child *child, | ||
1093 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1094 | struct wlr_surface *surface) { | ||
1095 | child->impl = impl; | ||
1096 | child->view = view; | ||
1097 | child->surface = surface; | ||
1098 | wl_list_init(&child->children); | ||
1099 | |||
1100 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1101 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1102 | wl_signal_add(&surface->events.new_subsurface, | ||
1103 | &child->surface_new_subsurface); | ||
1104 | child->surface_new_subsurface.notify = | ||
1105 | view_child_handle_surface_new_subsurface; | ||
1106 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1107 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1108 | |||
1109 | // Not all child views have a map/unmap event | ||
1110 | child->surface_map.notify = view_child_handle_surface_map; | ||
1111 | wl_list_init(&child->surface_map.link); | ||
1112 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1113 | wl_list_init(&child->surface_unmap.link); | ||
1114 | |||
1115 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1116 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1117 | |||
1118 | struct sway_container *container = child->view->container; | ||
1119 | if (container != NULL) { | ||
1120 | struct sway_workspace *workspace = container->pending.workspace; | ||
1121 | if (workspace) { | ||
1122 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1123 | } | ||
1124 | } | ||
1125 | |||
1126 | view_child_init_subsurfaces(child, surface); | ||
1127 | } | ||
1128 | |||
1129 | void view_child_destroy(struct sway_view_child *child) { | ||
1130 | if (view_child_is_mapped(child) && child->view->container != NULL) { | ||
1131 | view_child_damage(child, true); | ||
1132 | } | ||
1133 | |||
1134 | if (child->parent != NULL) { | ||
1135 | wl_list_remove(&child->link); | ||
1136 | child->parent = NULL; | ||
1137 | } | ||
1138 | |||
1139 | struct sway_view_child *subchild, *tmpchild; | ||
1140 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | ||
1141 | wl_list_remove(&subchild->link); | ||
1142 | subchild->parent = NULL; | ||
1143 | // The subchild lost its parent link, so it cannot see that the parent | ||
1144 | // is unmapped. Unmap it directly. | ||
1145 | subchild->mapped = false; | ||
1146 | } | ||
1147 | |||
1148 | wl_list_remove(&child->surface_commit.link); | ||
1149 | wl_list_remove(&child->surface_destroy.link); | ||
1150 | wl_list_remove(&child->surface_map.link); | ||
1151 | wl_list_remove(&child->surface_unmap.link); | ||
1152 | wl_list_remove(&child->view_unmap.link); | ||
1153 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1154 | |||
1155 | if (child->impl && child->impl->destroy) { | ||
1156 | child->impl->destroy(child); | ||
1157 | } else { | 940 | } else { |
1158 | free(child); | 941 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
942 | } | ||
943 | |||
944 | // only make sure to clip the content if there is content to clip | ||
945 | if (!wl_list_empty(&con->view->content_tree->children)) { | ||
946 | struct wlr_box clip = {0}; | ||
947 | if (clip_to_geometry) { | ||
948 | clip = (struct wlr_box){ | ||
949 | .x = con->view->geometry.x, | ||
950 | .y = con->view->geometry.y, | ||
951 | .width = con->current.content_width, | ||
952 | .height = con->current.content_height, | ||
953 | }; | ||
954 | } | ||
955 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip); | ||
1159 | } | 956 | } |
1160 | } | 957 | } |
1161 | 958 | ||
1162 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | 959 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { |
1163 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 960 | struct wlr_xdg_surface *xdg_surface; |
1164 | struct wlr_xdg_surface *xdg_surface = | 961 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1165 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1166 | if (xdg_surface == NULL) { | ||
1167 | return NULL; | ||
1168 | } | ||
1169 | return view_from_wlr_xdg_surface(xdg_surface); | 962 | return view_from_wlr_xdg_surface(xdg_surface); |
1170 | } | 963 | } |
1171 | #if HAVE_XWAYLAND | 964 | #if WLR_HAS_XWAYLAND |
1172 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 965 | struct wlr_xwayland_surface *xsurface; |
1173 | struct wlr_xwayland_surface *xsurface = | 966 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1174 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1175 | if (xsurface == NULL) { | ||
1176 | return NULL; | ||
1177 | } | ||
1178 | return view_from_wlr_xwayland_surface(xsurface); | 967 | return view_from_wlr_xwayland_surface(xsurface); |
1179 | } | 968 | } |
1180 | #endif | 969 | #endif |
1181 | if (wlr_surface_is_subsurface(wlr_surface)) { | 970 | struct wlr_subsurface *subsurface; |
1182 | struct wlr_subsurface *subsurface = | 971 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1183 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1184 | if (subsurface == NULL) { | ||
1185 | return NULL; | ||
1186 | } | ||
1187 | return view_from_wlr_surface(subsurface->parent); | 972 | return view_from_wlr_surface(subsurface->parent); |
1188 | } | 973 | } |
1189 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 974 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1190 | return NULL; | 975 | return NULL; |
1191 | } | 976 | } |
1192 | 977 | ||
@@ -1267,6 +1052,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
1267 | return len; | 1052 | return len; |
1268 | } | 1053 | } |
1269 | 1054 | ||
1055 | void view_update_app_id(struct sway_view *view) { | ||
1056 | const char *app_id = view_get_app_id(view); | ||
1057 | |||
1058 | if (view->foreign_toplevel && app_id) { | ||
1059 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); | ||
1060 | } | ||
1061 | |||
1062 | if (view->ext_foreign_toplevel) { | ||
1063 | update_ext_foreign_toplevel(view); | ||
1064 | } | ||
1065 | } | ||
1066 | |||
1270 | void view_update_title(struct sway_view *view, bool force) { | 1067 | void view_update_title(struct sway_view *view, bool force) { |
1271 | const char *title = view_get_title(view); | 1068 | const char *title = view_get_title(view); |
1272 | 1069 | ||
@@ -1282,29 +1079,41 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1282 | 1079 | ||
1283 | free(view->container->title); | 1080 | free(view->container->title); |
1284 | free(view->container->formatted_title); | 1081 | free(view->container->formatted_title); |
1285 | if (title) { | 1082 | |
1286 | size_t len = parse_title_format(view, NULL); | 1083 | size_t len = parse_title_format(view, NULL); |
1084 | |||
1085 | if (len) { | ||
1287 | char *buffer = calloc(len + 1, sizeof(char)); | 1086 | char *buffer = calloc(len + 1, sizeof(char)); |
1288 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1087 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1289 | return; | 1088 | return; |
1290 | } | 1089 | } |
1291 | parse_title_format(view, buffer); | ||
1292 | 1090 | ||
1293 | view->container->title = strdup(title); | 1091 | parse_title_format(view, buffer); |
1294 | view->container->formatted_title = buffer; | 1092 | view->container->formatted_title = buffer; |
1295 | } else { | 1093 | } else { |
1296 | view->container->title = NULL; | ||
1297 | view->container->formatted_title = NULL; | 1094 | view->container->formatted_title = NULL; |
1298 | } | 1095 | } |
1299 | 1096 | ||
1097 | view->container->title = title ? strdup(title) : NULL; | ||
1098 | |||
1300 | // Update title after the global font height is updated | 1099 | // Update title after the global font height is updated |
1301 | container_update_title_textures(view->container); | 1100 | if (view->container->title_bar.title_text && len) { |
1101 | sway_text_node_set_text(view->container->title_bar.title_text, | ||
1102 | view->container->formatted_title); | ||
1103 | container_arrange_title_bar(view->container); | ||
1104 | } else { | ||
1105 | container_update_title_bar(view->container); | ||
1106 | } | ||
1302 | 1107 | ||
1303 | ipc_event_window(view->container, "title"); | 1108 | ipc_event_window(view->container, "title"); |
1304 | 1109 | ||
1305 | if (view->foreign_toplevel && title) { | 1110 | if (view->foreign_toplevel && title) { |
1306 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); | 1111 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); |
1307 | } | 1112 | } |
1113 | |||
1114 | if (view->ext_foreign_toplevel) { | ||
1115 | update_ext_foreign_toplevel(view); | ||
1116 | } | ||
1308 | } | 1117 | } |
1309 | 1118 | ||
1310 | bool view_is_visible(struct sway_view *view) { | 1119 | bool view_is_visible(struct sway_view *view) { |
@@ -1365,6 +1174,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1365 | return; | 1174 | return; |
1366 | } | 1175 | } |
1367 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1176 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1177 | container_update_itself_and_parents(view->container); | ||
1368 | } else { | 1178 | } else { |
1369 | view->urgent = (struct timespec){ 0 }; | 1179 | view->urgent = (struct timespec){ 0 }; |
1370 | if (view->urgent_timer) { | 1180 | if (view->urgent_timer) { |
@@ -1372,7 +1182,6 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1372 | view->urgent_timer = NULL; | 1182 | view->urgent_timer = NULL; |
1373 | } | 1183 | } |
1374 | } | 1184 | } |
1375 | container_damage_whole(view->container); | ||
1376 | 1185 | ||
1377 | ipc_event_window(view->container, "urgent"); | 1186 | ipc_event_window(view->container, "urgent"); |
1378 | 1187 | ||
@@ -1386,40 +1195,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1386 | } | 1195 | } |
1387 | 1196 | ||
1388 | void view_remove_saved_buffer(struct sway_view *view) { | 1197 | void view_remove_saved_buffer(struct sway_view *view) { |
1389 | if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { | 1198 | if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { |
1390 | return; | 1199 | return; |
1391 | } | 1200 | } |
1392 | struct sway_saved_buffer *saved_buf, *tmp; | 1201 | |
1393 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1202 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1394 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1203 | view->saved_surface_tree = NULL; |
1395 | wl_list_remove(&saved_buf->link); | 1204 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1396 | free(saved_buf); | ||
1397 | } | ||
1398 | } | 1205 | } |
1399 | 1206 | ||
1400 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1207 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1401 | int sx, int sy, void *data) { | 1208 | int sx, int sy, void *data) { |
1402 | struct sway_view *view = data; | 1209 | struct wlr_scene_tree *tree = data; |
1403 | 1210 | ||
1404 | if (surface && wlr_surface_has_buffer(surface)) { | 1211 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1405 | wlr_buffer_lock(&surface->buffer->base); | 1212 | if (!sbuf) { |
1406 | struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); | 1213 | sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); |
1407 | saved_buffer->buffer = surface->buffer; | 1214 | return; |
1408 | saved_buffer->width = surface->current.width; | ||
1409 | saved_buffer->height = surface->current.height; | ||
1410 | saved_buffer->x = view->container->surface_x + sx; | ||
1411 | saved_buffer->y = view->container->surface_y + sy; | ||
1412 | saved_buffer->transform = surface->current.transform; | ||
1413 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1414 | wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); | ||
1415 | } | 1215 | } |
1216 | |||
1217 | wlr_scene_buffer_set_dest_size(sbuf, | ||
1218 | buffer->dst_width, buffer->dst_height); | ||
1219 | wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); | ||
1220 | wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); | ||
1221 | wlr_scene_node_set_position(&sbuf->node, sx, sy); | ||
1222 | wlr_scene_buffer_set_transform(sbuf, buffer->transform); | ||
1223 | wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); | ||
1416 | } | 1224 | } |
1417 | 1225 | ||
1418 | void view_save_buffer(struct sway_view *view) { | 1226 | void view_save_buffer(struct sway_view *view) { |
1419 | if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { | 1227 | if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { |
1420 | view_remove_saved_buffer(view); | 1228 | view_remove_saved_buffer(view); |
1421 | } | 1229 | } |
1422 | view_for_each_surface(view, view_save_buffer_iterator, view); | 1230 | |
1231 | view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); | ||
1232 | if (!view->saved_surface_tree) { | ||
1233 | sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); | ||
1234 | return; | ||
1235 | } | ||
1236 | |||
1237 | // Enable and disable the saved surface tree like so to atomitaclly update | ||
1238 | // the tree. This will prevent over damaging or other weirdness. | ||
1239 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); | ||
1240 | |||
1241 | wlr_scene_node_for_each_buffer(&view->content_tree->node, | ||
1242 | view_save_buffer_iterator, view->saved_surface_tree); | ||
1243 | |||
1244 | wlr_scene_node_set_enabled(&view->content_tree->node, false); | ||
1245 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); | ||
1423 | } | 1246 | } |
1424 | 1247 | ||
1425 | bool view_is_transient_for(struct sway_view *child, | 1248 | bool view_is_transient_for(struct sway_view *child, |
@@ -1427,3 +1250,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1427 | return child->impl->is_transient_for && | 1250 | return child->impl->is_transient_for && |
1428 | child->impl->is_transient_for(child, ancestor); | 1251 | child->impl->is_transient_for(child, ancestor); |
1429 | } | 1252 | } |
1253 | |||
1254 | static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, | ||
1255 | int x, int y, void *data) { | ||
1256 | struct timespec *when = data; | ||
1257 | wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); | ||
1258 | } | ||
1259 | |||
1260 | void view_send_frame_done(struct sway_view *view) { | ||
1261 | struct timespec when; | ||
1262 | clock_gettime(CLOCK_MONOTONIC, &when); | ||
1263 | |||
1264 | struct wlr_scene_node *node; | ||
1265 | wl_list_for_each(node, &view->content_tree->children, link) { | ||
1266 | wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); | ||
1267 | } | ||
1268 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index c84320bd..52e48ad5 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
@@ -11,6 +10,7 @@ | |||
11 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
12 | #include "sway/ipc-server.h" | 11 | #include "sway/ipc-server.h" |
13 | #include "sway/output.h" | 12 | #include "sway/output.h" |
13 | #include "sway/server.h" | ||
14 | #include "sway/tree/arrange.h" | 14 | #include "sway/tree/arrange.h" |
15 | #include "sway/tree/container.h" | 15 | #include "sway/tree/container.h" |
16 | #include "sway/tree/node.h" | 16 | #include "sway/tree/node.h" |
@@ -56,6 +56,8 @@ struct sway_output *workspace_get_initial_output(const char *name) { | |||
56 | 56 | ||
57 | struct sway_workspace *workspace_create(struct sway_output *output, | 57 | struct sway_workspace *workspace_create(struct sway_output *output, |
58 | const char *name) { | 58 | const char *name) { |
59 | sway_assert(name, "NULL name given to workspace_create"); | ||
60 | |||
59 | if (output == NULL) { | 61 | if (output == NULL) { |
60 | output = workspace_get_initial_output(name); | 62 | output = workspace_get_initial_output(name); |
61 | } | 63 | } |
@@ -69,7 +71,19 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
69 | return NULL; | 71 | return NULL; |
70 | } | 72 | } |
71 | node_init(&ws->node, N_WORKSPACE, ws); | 73 | node_init(&ws->node, N_WORKSPACE, ws); |
72 | ws->name = name ? strdup(name) : NULL; | 74 | |
75 | bool failed = false; | ||
76 | ws->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
77 | ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
78 | |||
79 | if (failed) { | ||
80 | wlr_scene_node_destroy(&ws->layers.tiling->node); | ||
81 | wlr_scene_node_destroy(&ws->layers.fullscreen->node); | ||
82 | free(ws); | ||
83 | return NULL; | ||
84 | } | ||
85 | |||
86 | ws->name = strdup(name); | ||
73 | ws->prev_split_layout = L_NONE; | 87 | ws->prev_split_layout = L_NONE; |
74 | ws->layout = output_get_default_layout(output); | 88 | ws->layout = output_get_default_layout(output); |
75 | ws->floating = create_list(); | 89 | ws->floating = create_list(); |
@@ -114,7 +128,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
114 | output_sort_workspaces(output); | 128 | output_sort_workspaces(output); |
115 | 129 | ||
116 | ipc_event_workspace(NULL, ws, "init"); | 130 | ipc_event_workspace(NULL, ws, "init"); |
117 | wl_signal_emit(&root->events.new_node, &ws->node); | 131 | wl_signal_emit_mutable(&root->events.new_node, &ws->node); |
118 | 132 | ||
119 | return ws; | 133 | return ws; |
120 | } | 134 | } |
@@ -129,6 +143,11 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
129 | return; | 143 | return; |
130 | } | 144 | } |
131 | 145 | ||
146 | scene_node_disown_children(workspace->layers.tiling); | ||
147 | scene_node_disown_children(workspace->layers.fullscreen); | ||
148 | wlr_scene_node_destroy(&workspace->layers.tiling->node); | ||
149 | wlr_scene_node_destroy(&workspace->layers.fullscreen->node); | ||
150 | |||
132 | free(workspace->name); | 151 | free(workspace->name); |
133 | free(workspace->representation); | 152 | free(workspace->representation); |
134 | list_free_items_and_destroy(workspace->output_priority); | 153 | list_free_items_and_destroy(workspace->output_priority); |
@@ -142,7 +161,7 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
142 | void workspace_begin_destroy(struct sway_workspace *workspace) { | 161 | void workspace_begin_destroy(struct sway_workspace *workspace) { |
143 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); | 162 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); |
144 | ipc_event_workspace(NULL, workspace, "empty"); // intentional | 163 | ipc_event_workspace(NULL, workspace, "empty"); // intentional |
145 | wl_signal_emit(&workspace->node.events.destroy, &workspace->node); | 164 | wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); |
146 | 165 | ||
147 | if (workspace->output) { | 166 | if (workspace->output) { |
148 | workspace_detach(workspace); | 167 | workspace_detach(workspace); |
@@ -174,22 +193,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) { | |||
174 | static bool workspace_valid_on_output(const char *output_name, | 193 | static bool workspace_valid_on_output(const char *output_name, |
175 | const char *ws_name) { | 194 | const char *ws_name) { |
176 | struct workspace_config *wsc = workspace_find_config(ws_name); | 195 | struct workspace_config *wsc = workspace_find_config(ws_name); |
177 | char identifier[128]; | ||
178 | struct sway_output *output = output_by_name_or_id(output_name); | 196 | struct sway_output *output = output_by_name_or_id(output_name); |
179 | if (!output) { | 197 | if (!output) { |
180 | return false; | 198 | return false; |
181 | } | 199 | } |
182 | output_name = output->wlr_output->name; | ||
183 | output_get_identifier(identifier, sizeof(identifier), output); | ||
184 | |||
185 | if (!wsc) { | 200 | if (!wsc) { |
186 | return true; | 201 | return true; |
187 | } | 202 | } |
188 | 203 | ||
189 | for (int i = 0; i < wsc->outputs->length; i++) { | 204 | for (int i = 0; i < wsc->outputs->length; i++) { |
190 | if (strcmp(wsc->outputs->items[i], "*") == 0 || | 205 | if (output_match_name_or_id(output, wsc->outputs->items[i])) { |
191 | strcmp(wsc->outputs->items[i], output_name) == 0 || | ||
192 | strcmp(wsc->outputs->items[i], identifier) == 0) { | ||
193 | return true; | 206 | return true; |
194 | } | 207 | } |
195 | } | 208 | } |
@@ -284,13 +297,10 @@ char *workspace_next_name(const char *output_name) { | |||
284 | // assignments primarily, falling back to bindings and numbers. | 297 | // assignments primarily, falling back to bindings and numbers. |
285 | struct sway_mode *mode = config->current_mode; | 298 | struct sway_mode *mode = config->current_mode; |
286 | 299 | ||
287 | char identifier[128]; | ||
288 | struct sway_output *output = output_by_name_or_id(output_name); | 300 | struct sway_output *output = output_by_name_or_id(output_name); |
289 | if (!output) { | 301 | if (!output) { |
290 | return NULL; | 302 | return NULL; |
291 | } | 303 | } |
292 | output_name = output->wlr_output->name; | ||
293 | output_get_identifier(identifier, sizeof(identifier), output); | ||
294 | 304 | ||
295 | int order = INT_MAX; | 305 | int order = INT_MAX; |
296 | char *target = NULL; | 306 | char *target = NULL; |
@@ -310,9 +320,7 @@ char *workspace_next_name(const char *output_name) { | |||
310 | } | 320 | } |
311 | bool found = false; | 321 | bool found = false; |
312 | for (int j = 0; j < wsc->outputs->length; ++j) { | 322 | for (int j = 0; j < wsc->outputs->length; ++j) { |
313 | if (strcmp(wsc->outputs->items[j], "*") == 0 || | 323 | if (output_match_name_or_id(output, wsc->outputs->items[j])) { |
314 | strcmp(wsc->outputs->items[j], output_name) == 0 || | ||
315 | strcmp(wsc->outputs->items[j], identifier) == 0) { | ||
316 | found = true; | 324 | found = true; |
317 | free(target); | 325 | free(target); |
318 | target = strdup(wsc->workspace); | 326 | target = strdup(wsc->workspace); |
@@ -652,15 +660,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace, | |||
652 | 660 | ||
653 | struct sway_output *workspace_output_get_highest_available( | 661 | struct sway_output *workspace_output_get_highest_available( |
654 | struct sway_workspace *ws, struct sway_output *exclude) { | 662 | struct sway_workspace *ws, struct sway_output *exclude) { |
655 | char exclude_id[128] = {'\0'}; | ||
656 | if (exclude) { | ||
657 | output_get_identifier(exclude_id, sizeof(exclude_id), exclude); | ||
658 | } | ||
659 | |||
660 | for (int i = 0; i < ws->output_priority->length; i++) { | 663 | for (int i = 0; i < ws->output_priority->length; i++) { |
661 | char *name = ws->output_priority->items[i]; | 664 | const char *name = ws->output_priority->items[i]; |
662 | if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 | 665 | if (exclude && output_match_name_or_id(exclude, name)) { |
663 | || strcmp(name, exclude_id) == 0)) { | ||
664 | continue; | 666 | continue; |
665 | } | 667 | } |
666 | 668 | ||
@@ -684,7 +686,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { | |||
684 | if (workspace->urgent != new_urgent) { | 686 | if (workspace->urgent != new_urgent) { |
685 | workspace->urgent = new_urgent; | 687 | workspace->urgent = new_urgent; |
686 | ipc_event_workspace(NULL, workspace, "urgent"); | 688 | ipc_event_workspace(NULL, workspace, "urgent"); |
687 | output_damage_whole(workspace->output); | ||
688 | } | 689 | } |
689 | } | 690 | } |
690 | 691 | ||