diff options
Diffstat (limited to 'sway/tree/workspace.c')
-rw-r--r-- | sway/tree/workspace.c | 493 |
1 files changed, 238 insertions, 255 deletions
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 1957d94f..38ee478e 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -12,128 +12,105 @@ | |||
12 | #include "sway/output.h" | 12 | #include "sway/output.h" |
13 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
14 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
15 | #include "sway/tree/node.h" | ||
15 | #include "sway/tree/view.h" | 16 | #include "sway/tree/view.h" |
16 | #include "sway/tree/workspace.h" | 17 | #include "sway/tree/workspace.h" |
17 | #include "list.h" | 18 | #include "list.h" |
18 | #include "log.h" | 19 | #include "log.h" |
19 | #include "util.h" | 20 | #include "util.h" |
20 | 21 | ||
21 | struct sway_container *workspace_get_initial_output(const char *name) { | 22 | struct sway_output *workspace_get_initial_output(const char *name) { |
22 | struct sway_container *parent; | ||
23 | // Search for workspace<->output pair | 23 | // Search for workspace<->output pair |
24 | int e = config->workspace_outputs->length; | ||
25 | for (int i = 0; i < config->workspace_outputs->length; ++i) { | 24 | for (int i = 0; i < config->workspace_outputs->length; ++i) { |
26 | struct workspace_output *wso = config->workspace_outputs->items[i]; | 25 | struct workspace_output *wso = config->workspace_outputs->items[i]; |
27 | if (strcasecmp(wso->workspace, name) == 0) { | 26 | if (strcasecmp(wso->workspace, name) == 0) { |
28 | // Find output to use if it exists | 27 | // Find output to use if it exists |
29 | e = root_container.children->length; | 28 | struct sway_output *output = output_by_name(wso->output); |
30 | for (i = 0; i < e; ++i) { | 29 | if (output) { |
31 | parent = root_container.children->items[i]; | 30 | return output; |
32 | if (strcmp(parent->name, wso->output) == 0) { | ||
33 | return parent; | ||
34 | } | ||
35 | } | 31 | } |
36 | break; | 32 | break; |
37 | } | 33 | } |
38 | } | 34 | } |
39 | // Otherwise put it on the focused output | 35 | // Otherwise put it on the focused output |
40 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 36 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
41 | struct sway_container *focus = | 37 | struct sway_workspace *focus = seat_get_focused_workspace(seat); |
42 | seat_get_focus_inactive(seat, &root_container); | 38 | return focus->output; |
43 | parent = focus; | ||
44 | parent = container_parent(parent, C_OUTPUT); | ||
45 | return parent; | ||
46 | } | 39 | } |
47 | 40 | ||
48 | struct sway_container *workspace_create(struct sway_container *output, | 41 | struct sway_workspace *workspace_create(struct sway_output *output, |
49 | const char *name) { | 42 | const char *name) { |
50 | if (output == NULL) { | 43 | if (output == NULL) { |
51 | output = workspace_get_initial_output(name); | 44 | output = workspace_get_initial_output(name); |
52 | } | 45 | } |
53 | 46 | ||
54 | wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); | 47 | wlr_log(WLR_DEBUG, "Adding workspace %s for output %s", name, |
55 | struct sway_container *workspace = container_create(C_WORKSPACE); | 48 | output->wlr_output->name); |
56 | |||
57 | workspace->x = output->x; | ||
58 | workspace->y = output->y; | ||
59 | workspace->width = output->width; | ||
60 | workspace->height = output->height; | ||
61 | workspace->name = !name ? NULL : strdup(name); | ||
62 | workspace->prev_split_layout = L_NONE; | ||
63 | workspace->layout = container_get_default_layout(output); | ||
64 | 49 | ||
65 | struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); | 50 | struct sway_workspace *ws = calloc(1, sizeof(struct sway_workspace)); |
66 | if (!swayws) { | 51 | if (!ws) { |
52 | wlr_log(WLR_ERROR, "Unable to allocate sway_workspace"); | ||
67 | return NULL; | 53 | return NULL; |
68 | } | 54 | } |
69 | swayws->swayc = workspace; | 55 | node_init(&ws->node, N_WORKSPACE, ws); |
70 | swayws->floating = create_list(); | 56 | ws->x = output->wlr_output->lx; |
71 | swayws->output_priority = create_list(); | 57 | ws->y = output->wlr_output->ly; |
72 | workspace->sway_workspace = swayws; | 58 | ws->width = output->wlr_output->width; |
73 | workspace_output_add_priority(workspace, output); | 59 | ws->height = output->wlr_output->height; |
74 | 60 | ws->name = name ? strdup(name) : NULL; | |
75 | container_add_child(output, workspace); | 61 | ws->prev_split_layout = L_NONE; |
62 | ws->layout = output_get_default_layout(output); | ||
63 | ws->floating = create_list(); | ||
64 | ws->tiling = create_list(); | ||
65 | ws->output_priority = create_list(); | ||
66 | workspace_output_add_priority(ws, output); | ||
67 | |||
68 | output_add_workspace(output, ws); | ||
76 | output_sort_workspaces(output); | 69 | output_sort_workspaces(output); |
77 | container_create_notify(workspace); | ||
78 | 70 | ||
79 | return workspace; | 71 | ipc_event_workspace(NULL, ws, "init"); |
72 | wl_signal_emit(&root->events.new_node, &ws->node); | ||
73 | |||
74 | return ws; | ||
80 | } | 75 | } |
81 | 76 | ||
82 | void workspace_destroy(struct sway_container *workspace) { | 77 | void workspace_destroy(struct sway_workspace *workspace) { |
83 | if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { | 78 | if (!sway_assert(workspace->node.destroying, |
84 | return; | ||
85 | } | ||
86 | if (!sway_assert(workspace->destroying, | ||
87 | "Tried to free workspace which wasn't marked as destroying")) { | 79 | "Tried to free workspace which wasn't marked as destroying")) { |
88 | return; | 80 | return; |
89 | } | 81 | } |
90 | if (!sway_assert(workspace->ntxnrefs == 0, "Tried to free workspace " | 82 | if (!sway_assert(workspace->node.ntxnrefs == 0, "Tried to free workspace " |
91 | "which is still referenced by transactions")) { | 83 | "which is still referenced by transactions")) { |
92 | return; | 84 | return; |
93 | } | 85 | } |
94 | // sway_workspace | ||
95 | struct sway_workspace *ws = workspace->sway_workspace; | ||
96 | list_foreach(ws->output_priority, free); | ||
97 | list_free(ws->output_priority); | ||
98 | list_free(ws->floating); | ||
99 | free(ws); | ||
100 | 86 | ||
101 | // swayc | ||
102 | free(workspace->name); | 87 | free(workspace->name); |
103 | free(workspace->formatted_title); | 88 | free(workspace->representation); |
104 | wlr_texture_destroy(workspace->title_focused); | 89 | list_foreach(workspace->output_priority, free); |
105 | wlr_texture_destroy(workspace->title_focused_inactive); | 90 | list_free(workspace->output_priority); |
106 | wlr_texture_destroy(workspace->title_unfocused); | 91 | list_free(workspace->floating); |
107 | wlr_texture_destroy(workspace->title_urgent); | 92 | list_free(workspace->tiling); |
108 | list_free(workspace->children); | 93 | list_free(workspace->current.floating); |
109 | list_free(workspace->current.children); | 94 | list_free(workspace->current.tiling); |
110 | list_free(workspace->outputs); | ||
111 | free(workspace); | 95 | free(workspace); |
112 | } | 96 | } |
113 | 97 | ||
114 | void workspace_begin_destroy(struct sway_container *workspace) { | 98 | void workspace_begin_destroy(struct sway_workspace *workspace) { |
115 | if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { | ||
116 | return; | ||
117 | } | ||
118 | wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name); | 99 | wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name); |
119 | wl_signal_emit(&workspace->events.destroy, workspace); | ||
120 | ipc_event_workspace(NULL, workspace, "empty"); // intentional | 100 | ipc_event_workspace(NULL, workspace, "empty"); // intentional |
101 | wl_signal_emit(&workspace->node.events.destroy, &workspace->node); | ||
121 | 102 | ||
122 | workspace->destroying = true; | 103 | if (workspace->output) { |
123 | container_set_dirty(workspace); | 104 | workspace_detach(workspace); |
124 | |||
125 | if (workspace->parent) { | ||
126 | container_remove_child(workspace); | ||
127 | } | 105 | } |
106 | |||
107 | workspace->node.destroying = true; | ||
108 | node_set_dirty(&workspace->node); | ||
128 | } | 109 | } |
129 | 110 | ||
130 | void workspace_consider_destroy(struct sway_container *ws) { | 111 | void workspace_consider_destroy(struct sway_workspace *ws) { |
131 | if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { | 112 | if (ws->tiling->length == 0 && ws->floating->length == 0 |
132 | return; | 113 | && output_get_active_workspace(ws->output) != ws) { |
133 | } | ||
134 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
135 | if (ws->children->length == 0 && ws->sway_workspace->floating->length == 0 | ||
136 | && seat_get_active_child(seat, ws->parent) != ws) { | ||
137 | workspace_begin_destroy(ws); | 114 | workspace_begin_destroy(ws); |
138 | } | 115 | } |
139 | } | 116 | } |
@@ -272,59 +249,49 @@ char *workspace_next_name(const char *output_name) { | |||
272 | } | 249 | } |
273 | // As a fall back, get the current number of active workspaces | 250 | // As a fall back, get the current number of active workspaces |
274 | // and return that + 1 for the next workspace's name | 251 | // and return that + 1 for the next workspace's name |
275 | int ws_num = root_container.children->length; | 252 | int ws_num = root->outputs->length; |
276 | int l = snprintf(NULL, 0, "%d", ws_num); | 253 | int l = snprintf(NULL, 0, "%d", ws_num); |
277 | char *name = malloc(l + 1); | 254 | char *name = malloc(l + 1); |
278 | if (!sway_assert(name, "Cloud not allocate workspace name")) { | 255 | if (!sway_assert(name, "Could not allocate workspace name")) { |
279 | return NULL; | 256 | return NULL; |
280 | } | 257 | } |
281 | sprintf(name, "%d", ws_num++); | 258 | sprintf(name, "%d", ws_num++); |
282 | return name; | 259 | return name; |
283 | } | 260 | } |
284 | 261 | ||
285 | static bool _workspace_by_number(struct sway_container *view, void *data) { | 262 | static bool _workspace_by_number(struct sway_workspace *ws, void *data) { |
286 | if (view->type != C_WORKSPACE) { | ||
287 | return false; | ||
288 | } | ||
289 | char *name = data; | 263 | char *name = data; |
290 | char *view_name = view->name; | 264 | char *ws_name = ws->name; |
291 | while (isdigit(*name)) { | 265 | while (isdigit(*name)) { |
292 | if (*name++ != *view_name++) { | 266 | if (*name++ != *ws_name++) { |
293 | return false; | 267 | return false; |
294 | } | 268 | } |
295 | } | 269 | } |
296 | return !isdigit(*view_name); | 270 | return !isdigit(*ws_name); |
297 | } | 271 | } |
298 | 272 | ||
299 | struct sway_container *workspace_by_number(const char* name) { | 273 | struct sway_workspace *workspace_by_number(const char* name) { |
300 | return root_find_workspace(_workspace_by_number, (void *) name); | 274 | return root_find_workspace(_workspace_by_number, (void *) name); |
301 | } | 275 | } |
302 | 276 | ||
303 | static bool _workspace_by_name(struct sway_container *view, void *data) { | 277 | static bool _workspace_by_name(struct sway_workspace *ws, void *data) { |
304 | return (view->type == C_WORKSPACE) && | 278 | return strcasecmp(ws->name, data) == 0; |
305 | (strcasecmp(view->name, (char *) data) == 0); | ||
306 | } | 279 | } |
307 | 280 | ||
308 | struct sway_container *workspace_by_name(const char *name) { | 281 | struct sway_workspace *workspace_by_name(const char *name) { |
309 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 282 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
310 | struct sway_container *current_workspace = NULL, *current_output = NULL; | 283 | struct sway_workspace *current = seat_get_focused_workspace(seat); |
311 | struct sway_container *focus = seat_get_focus(seat); | ||
312 | if (focus) { | ||
313 | current_workspace = focus->type == C_WORKSPACE ? | ||
314 | focus : container_parent(focus, C_WORKSPACE); | ||
315 | current_output = container_parent(focus, C_OUTPUT); | ||
316 | } | ||
317 | 284 | ||
318 | if (strcmp(name, "prev") == 0) { | 285 | if (strcmp(name, "prev") == 0) { |
319 | return workspace_prev(current_workspace); | 286 | return workspace_prev(current); |
320 | } else if (strcmp(name, "prev_on_output") == 0) { | 287 | } else if (strcmp(name, "prev_on_output") == 0) { |
321 | return workspace_output_prev(current_output); | 288 | return workspace_output_prev(current); |
322 | } else if (strcmp(name, "next") == 0) { | 289 | } else if (strcmp(name, "next") == 0) { |
323 | return workspace_next(current_workspace); | 290 | return workspace_next(current); |
324 | } else if (strcmp(name, "next_on_output") == 0) { | 291 | } else if (strcmp(name, "next_on_output") == 0) { |
325 | return workspace_output_next(current_output); | 292 | return workspace_output_next(current); |
326 | } else if (strcmp(name, "current") == 0) { | 293 | } else if (strcmp(name, "current") == 0) { |
327 | return current_workspace; | 294 | return current; |
328 | } else if (strcasecmp(name, "back_and_forth") == 0) { | 295 | } else if (strcasecmp(name, "back_and_forth") == 0) { |
329 | return prev_workspace_name ? | 296 | return prev_workspace_name ? |
330 | root_find_workspace(_workspace_by_name, (void*)prev_workspace_name) | 297 | root_find_workspace(_workspace_by_name, (void*)prev_workspace_name) |
@@ -339,97 +306,68 @@ struct sway_container *workspace_by_name(const char *name) { | |||
339 | * the end and beginning. If next is false, the previous workspace is returned, | 306 | * the end and beginning. If next is false, the previous workspace is returned, |
340 | * otherwise the next one is returned. | 307 | * otherwise the next one is returned. |
341 | */ | 308 | */ |
342 | static struct sway_container *workspace_output_prev_next_impl( | 309 | static struct sway_workspace *workspace_output_prev_next_impl( |
343 | struct sway_container *output, int dir) { | 310 | struct sway_output *output, int dir) { |
344 | if (!output) { | ||
345 | return NULL; | ||
346 | } | ||
347 | if (!sway_assert(output->type == C_OUTPUT, | ||
348 | "Argument must be an output, is %d", output->type)) { | ||
349 | return NULL; | ||
350 | } | ||
351 | |||
352 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 311 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
353 | struct sway_container *focus = seat_get_focus_inactive(seat, output); | 312 | struct sway_workspace *workspace = seat_get_focused_workspace(seat); |
354 | struct sway_container *workspace = (focus->type == C_WORKSPACE ? | ||
355 | focus : | ||
356 | container_parent(focus, C_WORKSPACE)); | ||
357 | 313 | ||
358 | int index = list_find(output->children, workspace); | 314 | int index = list_find(output->workspaces, workspace); |
359 | size_t new_index = wrap(index + dir, output->children->length); | 315 | size_t new_index = wrap(index + dir, output->workspaces->length); |
360 | return output->children->items[new_index]; | 316 | return output->workspaces->items[new_index]; |
361 | } | 317 | } |
362 | 318 | ||
363 | /** | 319 | /** |
364 | * Get the previous or next workspace. If the first/last workspace on an output | 320 | * Get the previous or next workspace. If the first/last workspace on an output |
365 | * is active, proceed to the previous/next output's previous/next workspace. | 321 | * is active, proceed to the previous/next output's previous/next workspace. |
366 | */ | 322 | */ |
367 | static struct sway_container *workspace_prev_next_impl( | 323 | static struct sway_workspace *workspace_prev_next_impl( |
368 | struct sway_container *workspace, int dir) { | 324 | struct sway_workspace *workspace, int dir) { |
369 | if (!workspace) { | 325 | struct sway_output *output = workspace->output; |
370 | return NULL; | 326 | int index = list_find(output->workspaces, workspace); |
371 | } | ||
372 | if (!sway_assert(workspace->type == C_WORKSPACE, | ||
373 | "Argument must be a workspace, is %d", workspace->type)) { | ||
374 | return NULL; | ||
375 | } | ||
376 | |||
377 | struct sway_container *output = workspace->parent; | ||
378 | int index = list_find(output->children, workspace); | ||
379 | int new_index = index + dir; | 327 | int new_index = index + dir; |
380 | 328 | ||
381 | if (new_index >= 0 && new_index < output->children->length) { | 329 | if (new_index >= 0 && new_index < output->workspaces->length) { |
382 | return output->children->items[index + dir]; | 330 | return output->workspaces->items[new_index]; |
383 | } | 331 | } |
384 | 332 | ||
385 | // Look on a different output | 333 | // Look on a different output |
386 | int output_index = list_find(root_container.children, output); | 334 | int output_index = list_find(root->outputs, output); |
387 | new_index = wrap(output_index + dir, root_container.children->length); | 335 | new_index = wrap(output_index + dir, root->outputs->length); |
388 | output = root_container.children->items[new_index]; | 336 | output = root->outputs->items[new_index]; |
389 | 337 | ||
390 | if (dir == 1) { | 338 | if (dir == 1) { |
391 | return output->children->items[0]; | 339 | return output->workspaces->items[0]; |
392 | } else { | 340 | } else { |
393 | return output->children->items[output->children->length - 1]; | 341 | return output->workspaces->items[output->workspaces->length - 1]; |
394 | } | 342 | } |
395 | } | 343 | } |
396 | 344 | ||
397 | struct sway_container *workspace_output_next(struct sway_container *current) { | 345 | struct sway_workspace *workspace_output_next(struct sway_workspace *current) { |
398 | return workspace_output_prev_next_impl(current, 1); | 346 | return workspace_output_prev_next_impl(current->output, 1); |
399 | } | 347 | } |
400 | 348 | ||
401 | struct sway_container *workspace_next(struct sway_container *current) { | 349 | struct sway_workspace *workspace_next(struct sway_workspace *current) { |
402 | return workspace_prev_next_impl(current, 1); | 350 | return workspace_prev_next_impl(current, 1); |
403 | } | 351 | } |
404 | 352 | ||
405 | struct sway_container *workspace_output_prev(struct sway_container *current) { | 353 | struct sway_workspace *workspace_output_prev(struct sway_workspace *current) { |
406 | return workspace_output_prev_next_impl(current, -1); | 354 | return workspace_output_prev_next_impl(current->output, -1); |
407 | } | 355 | } |
408 | 356 | ||
409 | struct sway_container *workspace_prev(struct sway_container *current) { | 357 | struct sway_workspace *workspace_prev(struct sway_workspace *current) { |
410 | return workspace_prev_next_impl(current, -1); | 358 | return workspace_prev_next_impl(current, -1); |
411 | } | 359 | } |
412 | 360 | ||
413 | bool workspace_switch(struct sway_container *workspace, | 361 | bool workspace_switch(struct sway_workspace *workspace, |
414 | bool no_auto_back_and_forth) { | 362 | bool no_auto_back_and_forth) { |
415 | if (!workspace) { | ||
416 | return false; | ||
417 | } | ||
418 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 363 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
419 | struct sway_container *focus = | 364 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
420 | seat_get_focus_inactive(seat, &root_container); | 365 | struct sway_workspace *active_ws = seat_get_focused_workspace(seat); |
421 | if (!seat || !focus) { | ||
422 | return false; | ||
423 | } | ||
424 | struct sway_container *active_ws = focus; | ||
425 | if (active_ws->type != C_WORKSPACE) { | ||
426 | active_ws = container_parent(focus, C_WORKSPACE); | ||
427 | } | ||
428 | 366 | ||
429 | if (!no_auto_back_and_forth && config->auto_back_and_forth | 367 | if (!no_auto_back_and_forth && config->auto_back_and_forth |
430 | && active_ws == workspace | 368 | && active_ws == workspace |
431 | && prev_workspace_name) { | 369 | && prev_workspace_name) { |
432 | struct sway_container *new_ws = workspace_by_name(prev_workspace_name); | 370 | struct sway_workspace *new_ws = workspace_by_name(prev_workspace_name); |
433 | workspace = new_ws ? | 371 | workspace = new_ws ? |
434 | new_ws : | 372 | new_ws : |
435 | workspace_create(NULL, prev_workspace_name); | 373 | workspace_create(NULL, prev_workspace_name); |
@@ -447,21 +385,21 @@ bool workspace_switch(struct sway_container *workspace, | |||
447 | } | 385 | } |
448 | 386 | ||
449 | // Move sticky containers to new workspace | 387 | // Move sticky containers to new workspace |
450 | struct sway_container *next_output = workspace->parent; | 388 | struct sway_output *next_output = workspace->output; |
451 | struct sway_container *next_output_prev_ws = | 389 | struct sway_workspace *next_output_prev_ws = |
452 | seat_get_active_child(seat, next_output); | 390 | output_get_active_workspace(next_output); |
453 | list_t *floating = next_output_prev_ws->sway_workspace->floating; | ||
454 | bool has_sticky = false; | 391 | bool has_sticky = false; |
455 | if (workspace != next_output_prev_ws) { | 392 | if (workspace != next_output_prev_ws) { |
456 | for (int i = 0; i < floating->length; ++i) { | 393 | for (int i = 0; i < next_output_prev_ws->floating->length; ++i) { |
457 | struct sway_container *floater = floating->items[i]; | 394 | struct sway_container *floater = |
395 | next_output_prev_ws->floating->items[i]; | ||
458 | if (floater->is_sticky) { | 396 | if (floater->is_sticky) { |
459 | has_sticky = true; | 397 | has_sticky = true; |
460 | container_remove_child(floater); | 398 | container_detach(floater); |
461 | workspace_add_floating(workspace, floater); | 399 | workspace_add_floating(workspace, floater); |
462 | if (floater == focus) { | 400 | if (&floater->node == focus) { |
463 | seat_set_focus(seat, NULL); | 401 | seat_set_focus(seat, NULL); |
464 | seat_set_focus(seat, floater); | 402 | seat_set_focus(seat, &floater->node); |
465 | } | 403 | } |
466 | --i; | 404 | --i; |
467 | } | 405 | } |
@@ -470,9 +408,9 @@ bool workspace_switch(struct sway_container *workspace, | |||
470 | 408 | ||
471 | wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", | 409 | wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", |
472 | workspace, workspace->name); | 410 | workspace, workspace->name); |
473 | struct sway_container *next = seat_get_focus_inactive(seat, workspace); | 411 | struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node); |
474 | if (next == NULL) { | 412 | if (next == NULL) { |
475 | next = workspace; | 413 | next = &workspace->node; |
476 | } | 414 | } |
477 | if (has_sticky) { | 415 | if (has_sticky) { |
478 | // If there's a sticky container, we might be setting focus to the same | 416 | // If there's a sticky container, we might be setting focus to the same |
@@ -483,35 +421,24 @@ bool workspace_switch(struct sway_container *workspace, | |||
483 | workspace_consider_destroy(active_ws); | 421 | workspace_consider_destroy(active_ws); |
484 | } | 422 | } |
485 | seat_set_focus(seat, next); | 423 | seat_set_focus(seat, next); |
486 | struct sway_container *output = container_parent(workspace, C_OUTPUT); | 424 | arrange_workspace(workspace); |
487 | arrange_windows(output); | ||
488 | return true; | 425 | return true; |
489 | } | 426 | } |
490 | 427 | ||
491 | bool workspace_is_visible(struct sway_container *ws) { | 428 | bool workspace_is_visible(struct sway_workspace *ws) { |
492 | if (ws->destroying) { | 429 | if (ws->node.destroying) { |
493 | return false; | 430 | return false; |
494 | } | 431 | } |
495 | struct sway_container *output = container_parent(ws, C_OUTPUT); | 432 | return output_get_active_workspace(ws->output) == ws; |
496 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
497 | struct sway_container *focus = seat_get_focus_inactive(seat, output); | ||
498 | if (focus->type != C_WORKSPACE) { | ||
499 | focus = container_parent(focus, C_WORKSPACE); | ||
500 | } | ||
501 | return focus == ws; | ||
502 | } | 433 | } |
503 | 434 | ||
504 | bool workspace_is_empty(struct sway_container *ws) { | 435 | bool workspace_is_empty(struct sway_workspace *ws) { |
505 | if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { | 436 | if (ws->tiling->length) { |
506 | return false; | ||
507 | } | ||
508 | if (ws->children->length) { | ||
509 | return false; | 437 | return false; |
510 | } | 438 | } |
511 | // Sticky views are not considered to be part of this workspace | 439 | // Sticky views are not considered to be part of this workspace |
512 | list_t *floating = ws->sway_workspace->floating; | 440 | for (int i = 0; i < ws->floating->length; ++i) { |
513 | for (int i = 0; i < floating->length; ++i) { | 441 | struct sway_container *floater = ws->floating->items[i]; |
514 | struct sway_container *floater = floating->items[i]; | ||
515 | if (!floater->is_sticky) { | 442 | if (!floater->is_sticky) { |
516 | return false; | 443 | return false; |
517 | } | 444 | } |
@@ -523,20 +450,19 @@ static int find_output(const void *id1, const void *id2) { | |||
523 | return strcmp(id1, id2) ? 0 : 1; | 450 | return strcmp(id1, id2) ? 0 : 1; |
524 | } | 451 | } |
525 | 452 | ||
526 | void workspace_output_raise_priority(struct sway_container *workspace, | 453 | void workspace_output_raise_priority(struct sway_workspace *ws, |
527 | struct sway_container *old_output, struct sway_container *output) { | 454 | struct sway_output *old_output, struct sway_output *output) { |
528 | struct sway_workspace *ws = workspace->sway_workspace; | ||
529 | |||
530 | int old_index = list_seq_find(ws->output_priority, find_output, | 455 | int old_index = list_seq_find(ws->output_priority, find_output, |
531 | old_output->name); | 456 | old_output->wlr_output->name); |
532 | if (old_index < 0) { | 457 | if (old_index < 0) { |
533 | return; | 458 | return; |
534 | } | 459 | } |
535 | 460 | ||
536 | int new_index = list_seq_find(ws->output_priority, find_output, | 461 | int new_index = list_seq_find(ws->output_priority, find_output, |
537 | output->name); | 462 | output->wlr_output->name); |
538 | if (new_index < 0) { | 463 | if (new_index < 0) { |
539 | list_insert(ws->output_priority, old_index, strdup(output->name)); | 464 | list_insert(ws->output_priority, old_index, |
465 | strdup(output->wlr_output->name)); | ||
540 | } else if (new_index > old_index) { | 466 | } else if (new_index > old_index) { |
541 | char *name = ws->output_priority->items[new_index]; | 467 | char *name = ws->output_priority->items[new_index]; |
542 | list_del(ws->output_priority, new_index); | 468 | list_del(ws->output_priority, new_index); |
@@ -544,29 +470,24 @@ void workspace_output_raise_priority(struct sway_container *workspace, | |||
544 | } | 470 | } |
545 | } | 471 | } |
546 | 472 | ||
547 | void workspace_output_add_priority(struct sway_container *workspace, | 473 | void workspace_output_add_priority(struct sway_workspace *workspace, |
548 | struct sway_container *output) { | 474 | struct sway_output *output) { |
549 | int index = list_seq_find(workspace->sway_workspace->output_priority, | 475 | int index = list_seq_find(workspace->output_priority, |
550 | find_output, output->name); | 476 | find_output, output->wlr_output->name); |
551 | if (index < 0) { | 477 | if (index < 0) { |
552 | list_add(workspace->sway_workspace->output_priority, | 478 | list_add(workspace->output_priority, strdup(output->wlr_output->name)); |
553 | strdup(output->name)); | ||
554 | } | 479 | } |
555 | } | 480 | } |
556 | 481 | ||
557 | static bool _output_by_name(struct sway_container *output, void *data) { | 482 | struct sway_output *workspace_output_get_highest_available( |
558 | return output->type == C_OUTPUT && strcasecmp(output->name, data) == 0; | 483 | struct sway_workspace *ws, struct sway_output *exclude) { |
559 | } | 484 | for (int i = 0; i < ws->output_priority->length; i++) { |
560 | 485 | char *name = ws->output_priority->items[i]; | |
561 | struct sway_container *workspace_output_get_highest_available( | 486 | if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) { |
562 | struct sway_container *ws, struct sway_container *exclude) { | ||
563 | for (int i = 0; i < ws->sway_workspace->output_priority->length; i++) { | ||
564 | char *name = ws->sway_workspace->output_priority->items[i]; | ||
565 | if (exclude && strcasecmp(name, exclude->name) == 0) { | ||
566 | continue; | 487 | continue; |
567 | } | 488 | } |
568 | 489 | ||
569 | struct sway_container *output = root_find_output(_output_by_name, name); | 490 | struct sway_output *output = output_by_name(name); |
570 | if (output) { | 491 | if (output) { |
571 | return output; | 492 | return output; |
572 | } | 493 | } |
@@ -576,49 +497,42 @@ struct sway_container *workspace_output_get_highest_available( | |||
576 | } | 497 | } |
577 | 498 | ||
578 | static bool find_urgent_iterator(struct sway_container *con, void *data) { | 499 | static bool find_urgent_iterator(struct sway_container *con, void *data) { |
579 | return con->type == C_VIEW && view_is_urgent(con->sway_view); | 500 | return con->view && view_is_urgent(con->view); |
580 | } | 501 | } |
581 | 502 | ||
582 | void workspace_detect_urgent(struct sway_container *workspace) { | 503 | void workspace_detect_urgent(struct sway_workspace *workspace) { |
583 | bool new_urgent = (bool)workspace_find_container(workspace, | 504 | bool new_urgent = (bool)workspace_find_container(workspace, |
584 | find_urgent_iterator, NULL); | 505 | find_urgent_iterator, NULL); |
585 | 506 | ||
586 | if (workspace->sway_workspace->urgent != new_urgent) { | 507 | if (workspace->urgent != new_urgent) { |
587 | workspace->sway_workspace->urgent = new_urgent; | 508 | workspace->urgent = new_urgent; |
588 | ipc_event_workspace(NULL, workspace, "urgent"); | 509 | ipc_event_workspace(NULL, workspace, "urgent"); |
589 | container_damage_whole(workspace); | 510 | output_damage_whole(workspace->output); |
590 | } | 511 | } |
591 | } | 512 | } |
592 | 513 | ||
593 | void workspace_for_each_container(struct sway_container *ws, | 514 | void workspace_for_each_container(struct sway_workspace *ws, |
594 | void (*f)(struct sway_container *con, void *data), void *data) { | 515 | void (*f)(struct sway_container *con, void *data), void *data) { |
595 | if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { | ||
596 | return; | ||
597 | } | ||
598 | // Tiling | 516 | // Tiling |
599 | for (int i = 0; i < ws->children->length; ++i) { | 517 | for (int i = 0; i < ws->tiling->length; ++i) { |
600 | struct sway_container *container = ws->children->items[i]; | 518 | struct sway_container *container = ws->tiling->items[i]; |
601 | f(container, data); | 519 | f(container, data); |
602 | container_for_each_child(container, f, data); | 520 | container_for_each_child(container, f, data); |
603 | } | 521 | } |
604 | // Floating | 522 | // Floating |
605 | for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { | 523 | for (int i = 0; i < ws->floating->length; ++i) { |
606 | struct sway_container *container = | 524 | struct sway_container *container = ws->floating->items[i]; |
607 | ws->sway_workspace->floating->items[i]; | ||
608 | f(container, data); | 525 | f(container, data); |
609 | container_for_each_child(container, f, data); | 526 | container_for_each_child(container, f, data); |
610 | } | 527 | } |
611 | } | 528 | } |
612 | 529 | ||
613 | struct sway_container *workspace_find_container(struct sway_container *ws, | 530 | struct sway_container *workspace_find_container(struct sway_workspace *ws, |
614 | bool (*test)(struct sway_container *con, void *data), void *data) { | 531 | bool (*test)(struct sway_container *con, void *data), void *data) { |
615 | if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { | ||
616 | return NULL; | ||
617 | } | ||
618 | struct sway_container *result = NULL; | 532 | struct sway_container *result = NULL; |
619 | // Tiling | 533 | // Tiling |
620 | for (int i = 0; i < ws->children->length; ++i) { | 534 | for (int i = 0; i < ws->tiling->length; ++i) { |
621 | struct sway_container *child = ws->children->items[i]; | 535 | struct sway_container *child = ws->tiling->items[i]; |
622 | if (test(child, data)) { | 536 | if (test(child, data)) { |
623 | return child; | 537 | return child; |
624 | } | 538 | } |
@@ -627,8 +541,8 @@ struct sway_container *workspace_find_container(struct sway_container *ws, | |||
627 | } | 541 | } |
628 | } | 542 | } |
629 | // Floating | 543 | // Floating |
630 | for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { | 544 | for (int i = 0; i < ws->floating->length; ++i) { |
631 | struct sway_container *child = ws->sway_workspace->floating->items[i]; | 545 | struct sway_container *child = ws->floating->items[i]; |
632 | if (test(child, data)) { | 546 | if (test(child, data)) { |
633 | return child; | 547 | return child; |
634 | } | 548 | } |
@@ -639,37 +553,76 @@ struct sway_container *workspace_find_container(struct sway_container *ws, | |||
639 | return NULL; | 553 | return NULL; |
640 | } | 554 | } |
641 | 555 | ||
642 | struct sway_container *workspace_wrap_children(struct sway_container *ws) { | 556 | struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { |
643 | struct sway_container *middle = container_create(C_CONTAINER); | 557 | struct sway_container *middle = container_create(NULL); |
644 | middle->layout = ws->layout; | 558 | middle->layout = ws->layout; |
645 | while (ws->children->length) { | 559 | while (ws->tiling->length) { |
646 | struct sway_container *child = ws->children->items[0]; | 560 | struct sway_container *child = ws->tiling->items[0]; |
647 | container_remove_child(child); | 561 | container_detach(child); |
648 | container_add_child(middle, child); | 562 | container_add_child(middle, child); |
649 | } | 563 | } |
650 | container_add_child(ws, middle); | 564 | workspace_add_tiling(ws, middle); |
651 | return middle; | 565 | return middle; |
652 | } | 566 | } |
653 | 567 | ||
654 | void workspace_add_floating(struct sway_container *workspace, | 568 | void workspace_detach(struct sway_workspace *workspace) { |
655 | struct sway_container *con) { | 569 | struct sway_output *output = workspace->output; |
656 | if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { | 570 | int index = list_find(output->workspaces, workspace); |
657 | return; | 571 | if (index != -1) { |
572 | list_del(output->workspaces, index); | ||
658 | } | 573 | } |
659 | if (!sway_assert(con->parent == NULL, "Expected an orphan container")) { | 574 | workspace->output = NULL; |
660 | return; | 575 | |
576 | node_set_dirty(&workspace->node); | ||
577 | node_set_dirty(&output->node); | ||
578 | } | ||
579 | |||
580 | static void set_workspace(struct sway_container *container, void *data) { | ||
581 | container->workspace = container->parent->workspace; | ||
582 | } | ||
583 | |||
584 | void workspace_add_tiling(struct sway_workspace *workspace, | ||
585 | struct sway_container *con) { | ||
586 | if (con->workspace) { | ||
587 | container_detach(con); | ||
661 | } | 588 | } |
589 | list_add(workspace->tiling, con); | ||
590 | con->workspace = workspace; | ||
591 | container_for_each_child(con, set_workspace, NULL); | ||
592 | container_handle_fullscreen_reparent(con); | ||
593 | workspace_update_representation(workspace); | ||
594 | node_set_dirty(&workspace->node); | ||
595 | node_set_dirty(&con->node); | ||
596 | } | ||
662 | 597 | ||
663 | list_add(workspace->sway_workspace->floating, con); | 598 | void workspace_add_floating(struct sway_workspace *workspace, |
664 | con->parent = workspace; | 599 | struct sway_container *con) { |
665 | container_set_dirty(workspace); | 600 | if (con->workspace) { |
666 | container_set_dirty(con); | 601 | container_detach(con); |
602 | } | ||
603 | list_add(workspace->floating, con); | ||
604 | con->workspace = workspace; | ||
605 | container_for_each_child(con, set_workspace, NULL); | ||
606 | container_handle_fullscreen_reparent(con); | ||
607 | node_set_dirty(&workspace->node); | ||
608 | node_set_dirty(&con->node); | ||
667 | } | 609 | } |
668 | 610 | ||
669 | void workspace_remove_gaps(struct sway_container *ws) { | 611 | void workspace_insert_tiling(struct sway_workspace *workspace, |
670 | if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { | 612 | struct sway_container *con, int index) { |
671 | return; | 613 | if (con->workspace) { |
614 | container_detach(con); | ||
672 | } | 615 | } |
616 | list_insert(workspace->tiling, index, con); | ||
617 | con->workspace = workspace; | ||
618 | container_for_each_child(con, set_workspace, NULL); | ||
619 | container_handle_fullscreen_reparent(con); | ||
620 | workspace_update_representation(workspace); | ||
621 | node_set_dirty(&workspace->node); | ||
622 | node_set_dirty(&con->node); | ||
623 | } | ||
624 | |||
625 | void workspace_remove_gaps(struct sway_workspace *ws) { | ||
673 | if (ws->current_gaps == 0) { | 626 | if (ws->current_gaps == 0) { |
674 | return; | 627 | return; |
675 | } | 628 | } |
@@ -681,15 +634,12 @@ void workspace_remove_gaps(struct sway_container *ws) { | |||
681 | ws->current_gaps = 0; | 634 | ws->current_gaps = 0; |
682 | } | 635 | } |
683 | 636 | ||
684 | void workspace_add_gaps(struct sway_container *ws) { | 637 | void workspace_add_gaps(struct sway_workspace *ws) { |
685 | if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { | ||
686 | return; | ||
687 | } | ||
688 | if (ws->current_gaps > 0) { | 638 | if (ws->current_gaps > 0) { |
689 | return; | 639 | return; |
690 | } | 640 | } |
691 | bool should_apply = | 641 | bool should_apply = |
692 | config->edge_gaps || (config->smart_gaps && ws->children->length > 1); | 642 | config->edge_gaps || (config->smart_gaps && ws->tiling->length > 1); |
693 | if (!should_apply) { | 643 | if (!should_apply) { |
694 | return; | 644 | return; |
695 | } | 645 | } |
@@ -708,3 +658,36 @@ void workspace_add_gaps(struct sway_container *ws) { | |||
708 | ws->width -= 2 * ws->current_gaps; | 658 | ws->width -= 2 * ws->current_gaps; |
709 | ws->height -= 2 * ws->current_gaps; | 659 | ws->height -= 2 * ws->current_gaps; |
710 | } | 660 | } |
661 | |||
662 | struct sway_container *workspace_split(struct sway_workspace *workspace, | ||
663 | enum sway_container_layout layout) { | ||
664 | if (workspace->tiling->length == 0) { | ||
665 | workspace->prev_split_layout = workspace->layout; | ||
666 | workspace->layout = layout; | ||
667 | return NULL; | ||
668 | } | ||
669 | |||
670 | enum sway_container_layout old_layout = workspace->layout; | ||
671 | struct sway_container *middle = workspace_wrap_children(workspace); | ||
672 | workspace->layout = layout; | ||
673 | middle->layout = old_layout; | ||
674 | |||
675 | return middle; | ||
676 | } | ||
677 | |||
678 | void workspace_update_representation(struct sway_workspace *ws) { | ||
679 | size_t len = container_build_representation(ws->layout, ws->tiling, NULL); | ||
680 | free(ws->representation); | ||
681 | ws->representation = calloc(len + 1, sizeof(char)); | ||
682 | if (!sway_assert(ws->representation, "Unable to allocate title string")) { | ||
683 | return; | ||
684 | } | ||
685 | container_build_representation(ws->layout, ws->tiling, ws->representation); | ||
686 | } | ||
687 | |||
688 | void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box) { | ||
689 | box->x = workspace->x; | ||
690 | box->y = workspace->y; | ||
691 | box->width = workspace->width; | ||
692 | box->height = workspace->height; | ||
693 | } | ||