diff options
Diffstat (limited to 'sway/tree/root.c')
-rw-r--r-- | sway/tree/root.c | 271 |
1 files changed, 75 insertions, 196 deletions
diff --git a/sway/tree/root.c b/sway/tree/root.c index 8508e9eb..ae3c3cb2 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 | |
45 | bool failed = false; | ||
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); | ||
35 | #if HAVE_XWAYLAND | 56 | #if HAVE_XWAYLAND |
36 | wl_list_init(&root->xwayland_unmanaged); | 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 | } | ||