diff options
Diffstat (limited to 'sway/tree/root.c')
-rw-r--r-- | sway/tree/root.c | 287 |
1 files changed, 88 insertions, 199 deletions
diff --git a/sway/tree/root.c b/sway/tree/root.c index ebd185ec..e9cea5e2 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c | |||
@@ -3,10 +3,13 @@ | |||
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
6 | #include <wlr/types/wlr_scene.h> | ||
7 | #include <wlr/util/transform.h> | ||
6 | #include "sway/desktop/transaction.h" | 8 | #include "sway/desktop/transaction.h" |
7 | #include "sway/input/seat.h" | 9 | #include "sway/input/seat.h" |
8 | #include "sway/ipc-server.h" | 10 | #include "sway/ipc-server.h" |
9 | #include "sway/output.h" | 11 | #include "sway/output.h" |
12 | #include "sway/scene_descriptor.h" | ||
10 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
12 | #include "sway/tree/root.h" | 15 | #include "sway/tree/root.h" |
@@ -23,21 +26,60 @@ static void output_layout_handle_change(struct wl_listener *listener, | |||
23 | transaction_commit_dirty(); | 26 | transaction_commit_dirty(); |
24 | } | 27 | } |
25 | 28 | ||
26 | struct sway_root *root_create(void) { | 29 | struct sway_root *root_create(struct wl_display *wl_display) { |
27 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); | 30 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); |
28 | if (!root) { | 31 | if (!root) { |
29 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); | 32 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); |
30 | return NULL; | 33 | return NULL; |
31 | } | 34 | } |
35 | |||
36 | struct wlr_scene *root_scene = wlr_scene_create(); | ||
37 | if (!root_scene) { | ||
38 | sway_log(SWAY_ERROR, "Unable to allocate root scene node"); | ||
39 | free(root); | ||
40 | return NULL; | ||
41 | } | ||
42 | |||
32 | node_init(&root->node, N_ROOT, root); | 43 | node_init(&root->node, N_ROOT, root); |
33 | root->output_layout = wlr_output_layout_create(); | 44 | root->root_scene = root_scene; |
34 | wl_list_init(&root->all_outputs); | 45 | |
46 | bool failed = false; | ||
47 | root->staging = alloc_scene_tree(&root_scene->tree, &failed); | ||
48 | root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); | ||
49 | |||
50 | root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed); | ||
51 | root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed); | ||
52 | root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); | ||
53 | root->layers.floating = alloc_scene_tree(root->layer_tree, &failed); | ||
54 | root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); | ||
55 | root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); | ||
56 | root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); | ||
35 | #if HAVE_XWAYLAND | 57 | #if HAVE_XWAYLAND |
36 | wl_list_init(&root->xwayland_unmanaged); | 58 | root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); |
37 | #endif | 59 | #endif |
38 | wl_list_init(&root->drag_icons); | 60 | root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); |
61 | root->layers.popup = alloc_scene_tree(root->layer_tree, &failed); | ||
62 | root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); | ||
63 | root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); | ||
64 | |||
65 | if (!failed && !scene_descriptor_assign(&root->layers.seat->node, | ||
66 | SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) { | ||
67 | failed = true; | ||
68 | } | ||
69 | |||
70 | if (failed) { | ||
71 | wlr_scene_node_destroy(&root_scene->tree.node); | ||
72 | free(root); | ||
73 | return NULL; | ||
74 | } | ||
75 | |||
76 | wlr_scene_node_set_enabled(&root->staging->node, false); | ||
77 | |||
78 | root->output_layout = wlr_output_layout_create(wl_display); | ||
79 | wl_list_init(&root->all_outputs); | ||
39 | wl_signal_init(&root->events.new_node); | 80 | wl_signal_init(&root->events.new_node); |
40 | root->outputs = create_list(); | 81 | root->outputs = create_list(); |
82 | root->non_desktop_outputs = create_list(); | ||
41 | root->scratchpad = create_list(); | 83 | root->scratchpad = create_list(); |
42 | 84 | ||
43 | root->output_layout_change.notify = output_layout_handle_change; | 85 | root->output_layout_change.notify = output_layout_handle_change; |
@@ -49,21 +91,34 @@ struct sway_root *root_create(void) { | |||
49 | void root_destroy(struct sway_root *root) { | 91 | void root_destroy(struct sway_root *root) { |
50 | wl_list_remove(&root->output_layout_change.link); | 92 | wl_list_remove(&root->output_layout_change.link); |
51 | list_free(root->scratchpad); | 93 | list_free(root->scratchpad); |
94 | list_free(root->non_desktop_outputs); | ||
52 | list_free(root->outputs); | 95 | list_free(root->outputs); |
53 | wlr_output_layout_destroy(root->output_layout); | 96 | wlr_scene_node_destroy(&root->root_scene->tree.node); |
54 | free(root); | 97 | free(root); |
55 | } | 98 | } |
56 | 99 | ||
100 | static void set_container_transform(struct sway_workspace *ws, | ||
101 | struct sway_container *con) { | ||
102 | struct sway_output *output = ws->output; | ||
103 | struct wlr_box box = {0}; | ||
104 | if (output) { | ||
105 | output_get_box(output, &box); | ||
106 | } | ||
107 | con->transform = box; | ||
108 | } | ||
109 | |||
57 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { | 110 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { |
58 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { | 111 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { |
59 | return; | 112 | return; |
60 | } | 113 | } |
61 | 114 | ||
62 | struct sway_container *parent = con->parent; | 115 | struct sway_container *parent = con->pending.parent; |
63 | struct sway_workspace *workspace = con->workspace; | 116 | struct sway_workspace *workspace = con->pending.workspace; |
117 | |||
118 | set_container_transform(workspace, con); | ||
64 | 119 | ||
65 | // Clear the fullscreen mode when sending to the scratchpad | 120 | // Clear the fullscreen mode when sending to the scratchpad |
66 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 121 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
67 | container_fullscreen_disable(con); | 122 | container_fullscreen_disable(con); |
68 | } | 123 | } |
69 | 124 | ||
@@ -117,7 +172,7 @@ void root_scratchpad_show(struct sway_container *con) { | |||
117 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); | 172 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); |
118 | return; | 173 | return; |
119 | } | 174 | } |
120 | struct sway_workspace *old_ws = con->workspace; | 175 | struct sway_workspace *old_ws = con->pending.workspace; |
121 | 176 | ||
122 | // If the current con or any of its parents are in fullscreen mode, we | 177 | // If the current con or any of its parents are in fullscreen mode, we |
123 | // first need to disable it before showing the scratchpad con. | 178 | // first need to disable it before showing the scratchpad con. |
@@ -131,31 +186,34 @@ void root_scratchpad_show(struct sway_container *con) { | |||
131 | // Show the container | 186 | // Show the container |
132 | if (old_ws) { | 187 | if (old_ws) { |
133 | container_detach(con); | 188 | container_detach(con); |
134 | workspace_consider_destroy(old_ws); | 189 | // Make sure the last inactive container on the old workspace is above |
190 | // the workspace itself in the focus stack. | ||
191 | struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node); | ||
192 | seat_set_raw_focus(seat, node); | ||
135 | } else { | 193 | } else { |
136 | // Act on the ancestor of scratchpad hidden split containers | 194 | // Act on the ancestor of scratchpad hidden split containers |
137 | while (con->parent) { | 195 | while (con->pending.parent) { |
138 | con = con->parent; | 196 | con = con->pending.parent; |
139 | } | 197 | } |
140 | } | 198 | } |
141 | workspace_add_floating(new_ws, con); | 199 | workspace_add_floating(new_ws, con); |
142 | 200 | ||
143 | // Make sure the container's center point overlaps this workspace | 201 | if (new_ws->output) { |
144 | double center_lx = con->x + con->width / 2; | 202 | struct wlr_box output_box; |
145 | double center_ly = con->y + con->height / 2; | 203 | output_get_box(new_ws->output, &output_box); |
146 | 204 | 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 | } | 205 | } |
206 | set_container_transform(new_ws, con); | ||
152 | 207 | ||
153 | arrange_workspace(new_ws); | 208 | arrange_workspace(new_ws); |
154 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); | 209 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); |
210 | if (old_ws) { | ||
211 | workspace_consider_destroy(old_ws); | ||
212 | } | ||
155 | } | 213 | } |
156 | 214 | ||
157 | static void disable_fullscreen(struct sway_container *con, void *data) { | 215 | static void disable_fullscreen(struct sway_container *con, void *data) { |
158 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 216 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
159 | container_fullscreen_disable(con); | 217 | container_fullscreen_disable(con); |
160 | } | 218 | } |
161 | } | 219 | } |
@@ -163,14 +221,16 @@ static void disable_fullscreen(struct sway_container *con, void *data) { | |||
163 | void root_scratchpad_hide(struct sway_container *con) { | 221 | void root_scratchpad_hide(struct sway_container *con) { |
164 | struct sway_seat *seat = input_manager_current_seat(); | 222 | struct sway_seat *seat = input_manager_current_seat(); |
165 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); | 223 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
166 | struct sway_workspace *ws = con->workspace; | 224 | struct sway_workspace *ws = con->pending.workspace; |
167 | 225 | ||
168 | if (con->fullscreen_mode == FULLSCREEN_GLOBAL && !con->workspace) { | 226 | if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL && !con->pending.workspace) { |
169 | // If the container was made fullscreen global while in the scratchpad, | 227 | // If the container was made fullscreen global while in the scratchpad, |
170 | // it should be shown until fullscreen has been disabled | 228 | // it should be shown until fullscreen has been disabled |
171 | return; | 229 | return; |
172 | } | 230 | } |
173 | 231 | ||
232 | set_container_transform(con->pending.workspace, con); | ||
233 | |||
174 | disable_fullscreen(con, NULL); | 234 | disable_fullscreen(con, NULL); |
175 | container_for_each_child(con, disable_fullscreen, NULL); | 235 | container_for_each_child(con, disable_fullscreen, NULL); |
176 | container_detach(con); | 236 | container_detach(con); |
@@ -183,163 +243,6 @@ void root_scratchpad_hide(struct sway_container *con) { | |||
183 | ipc_event_window(con, "move"); | 243 | ipc_event_window(con, "move"); |
184 | } | 244 | } |
185 | 245 | ||
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 | sprintf(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 | ws = workspace_create(pw->output, pw->workspace); | ||
274 | } | ||
275 | |||
276 | pid_workspace_destroy(pw); | ||
277 | } | ||
278 | |||
279 | return ws; | ||
280 | } | ||
281 | |||
282 | static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | ||
283 | struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); | ||
284 | pw->output = NULL; | ||
285 | wl_list_remove(&pw->output_destroy.link); | ||
286 | wl_list_init(&pw->output_destroy.link); | ||
287 | } | ||
288 | |||
289 | void root_record_workspace_pid(pid_t pid) { | ||
290 | sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid); | ||
291 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
292 | wl_list_init(&pid_workspaces); | ||
293 | } | ||
294 | |||
295 | struct sway_seat *seat = input_manager_current_seat(); | ||
296 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
297 | if (!ws) { | ||
298 | sway_log(SWAY_DEBUG, "Bailing out, no workspace"); | ||
299 | return; | ||
300 | } | ||
301 | struct sway_output *output = ws->output; | ||
302 | if (!output) { | ||
303 | sway_log(SWAY_DEBUG, "Bailing out, no output"); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | struct timespec now; | ||
308 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
309 | |||
310 | // Remove expired entries | ||
311 | static const int timeout = 60; | ||
312 | struct pid_workspace *old, *_old; | ||
313 | wl_list_for_each_safe(old, _old, &pid_workspaces, link) { | ||
314 | if (now.tv_sec - old->time_added.tv_sec >= timeout) { | ||
315 | pid_workspace_destroy(old); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); | ||
320 | pw->workspace = strdup(ws->name); | ||
321 | pw->output = output; | ||
322 | pw->pid = pid; | ||
323 | memcpy(&pw->time_added, &now, sizeof(struct timespec)); | ||
324 | pw->output_destroy.notify = pw_handle_output_destroy; | ||
325 | wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); | ||
326 | wl_list_insert(&pid_workspaces, &pw->link); | ||
327 | } | ||
328 | |||
329 | void root_remove_workspace_pid(pid_t pid) { | ||
330 | if (!pid_workspaces.prev || !pid_workspaces.next) { | ||
331 | return; | ||
332 | } | ||
333 | |||
334 | struct pid_workspace *pw, *tmp; | ||
335 | wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) { | ||
336 | if (pid == pw->pid) { | ||
337 | pid_workspace_destroy(pw); | ||
338 | return; | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), | 246 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), |
344 | void *data) { | 247 | void *data) { |
345 | for (int i = 0; i < root->outputs->length; ++i) { | 248 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -365,8 +268,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), | |||
365 | } | 268 | } |
366 | 269 | ||
367 | // Saved workspaces | 270 | // Saved workspaces |
368 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 271 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
369 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 272 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
370 | workspace_for_each_container(ws, f, data); | 273 | workspace_for_each_container(ws, f, data); |
371 | } | 274 | } |
372 | } | 275 | } |
@@ -418,8 +321,8 @@ struct sway_container *root_find_container( | |||
418 | } | 321 | } |
419 | 322 | ||
420 | // Saved workspaces | 323 | // Saved workspaces |
421 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 324 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
422 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 325 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
423 | if ((result = workspace_find_container(ws, test, data))) { | 326 | if ((result = workspace_find_container(ws, test, data))) { |
424 | return result; | 327 | return result; |
425 | } | 328 | } |
@@ -434,17 +337,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { | |||
434 | box->width = root->width; | 337 | box->width = root->width; |
435 | box->height = root->height; | 338 | box->height = root->height; |
436 | } | 339 | } |
437 | |||
438 | void root_rename_pid_workspaces(const char *old_name, const char *new_name) { | ||
439 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
440 | wl_list_init(&pid_workspaces); | ||
441 | } | ||
442 | |||
443 | struct pid_workspace *pw = NULL; | ||
444 | wl_list_for_each(pw, &pid_workspaces, link) { | ||
445 | if (strcmp(pw->workspace, old_name) == 0) { | ||
446 | free(pw->workspace); | ||
447 | pw->workspace = strdup(new_name); | ||
448 | } | ||
449 | } | ||
450 | } | ||