aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/config.h7
-rw-r--r--include/sway/desktop/transaction.h4
-rw-r--r--include/sway/input/input-manager.h4
-rw-r--r--include/sway/input/seat.h35
-rw-r--r--include/sway/ipc-json.h4
-rw-r--r--include/sway/ipc-server.h4
-rw-r--r--include/sway/output.h59
-rw-r--r--include/sway/server.h2
-rw-r--r--include/sway/tree/arrange.h9
-rw-r--r--include/sway/tree/container.h148
-rw-r--r--include/sway/tree/node.h74
-rw-r--r--include/sway/tree/root.h28
-rw-r--r--include/sway/tree/view.h4
-rw-r--r--include/sway/tree/workspace.h105
-rw-r--r--sway/commands.c27
-rw-r--r--sway/commands/border.c13
-rw-r--r--sway/commands/floating.c18
-rw-r--r--sway/commands/focus.c264
-rw-r--r--sway/commands/fullscreen.c18
-rw-r--r--sway/commands/gaps.c35
-rw-r--r--sway/commands/hide_edge_borders.c4
-rw-r--r--sway/commands/kill.c20
-rw-r--r--sway/commands/layout.c186
-rw-r--r--sway/commands/mark.c7
-rw-r--r--sway/commands/move.c873
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/reload.c2
-rw-r--r--sway/commands/rename.c11
-rw-r--r--sway/commands/resize.c86
-rw-r--r--sway/commands/scratchpad.c42
-rw-r--r--sway/commands/seat/cursor.c4
-rw-r--r--sway/commands/show_marks.c10
-rw-r--r--sway/commands/smart_gaps.c2
-rw-r--r--sway/commands/split.c23
-rw-r--r--sway/commands/sticky.c27
-rw-r--r--sway/commands/swap.c76
-rw-r--r--sway/commands/title_format.c7
-rw-r--r--sway/commands/unmark.c13
-rw-r--r--sway/commands/urgent.c7
-rw-r--r--sway/commands/workspace.c2
-rw-r--r--sway/config.c2
-rw-r--r--sway/config/bar.c14
-rw-r--r--sway/config/output.c47
-rw-r--r--sway/criteria.c41
-rw-r--r--sway/debug-tree.c111
-rw-r--r--sway/desktop/desktop.c31
-rw-r--r--sway/desktop/layer_shell.c23
-rw-r--r--sway/desktop/output.c182
-rw-r--r--sway/desktop/render.c259
-rw-r--r--sway/desktop/transaction.c374
-rw-r--r--sway/desktop/xdg_shell.c47
-rw-r--r--sway/desktop/xdg_shell_v6.c44
-rw-r--r--sway/desktop/xwayland.c50
-rw-r--r--sway/input/cursor.c158
-rw-r--r--sway/input/input-manager.c14
-rw-r--r--sway/input/seat.c514
-rw-r--r--sway/ipc-json.c189
-rw-r--r--sway/ipc-server.c55
-rw-r--r--sway/main.c6
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c7
-rw-r--r--sway/tree/arrange.c110
-rw-r--r--sway/tree/container.c871
-rw-r--r--sway/tree/node.c151
-rw-r--r--sway/tree/output.c348
-rw-r--r--sway/tree/root.c186
-rw-r--r--sway/tree/view.c277
-rw-r--r--sway/tree/workspace.c493
68 files changed, 3439 insertions, 3433 deletions
diff --git a/include/sway/config.h b/include/sway/config.h
index 6024f0f6..2fef0081 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -407,7 +407,9 @@ struct sway_config {
407 struct output_config *output_config; 407 struct output_config *output_config;
408 struct seat_config *seat_config; 408 struct seat_config *seat_config;
409 struct sway_seat *seat; 409 struct sway_seat *seat;
410 struct sway_container *current_container; 410 struct sway_node *node;
411 struct sway_container *container;
412 struct sway_workspace *workspace;
411 bool using_criteria; 413 bool using_criteria;
412 struct { 414 struct {
413 int argc; 415 int argc;
@@ -486,8 +488,7 @@ struct output_config *new_output_config(const char *name);
486 488
487void merge_output_config(struct output_config *dst, struct output_config *src); 489void merge_output_config(struct output_config *dst, struct output_config *src);
488 490
489void apply_output_config(struct output_config *oc, 491void apply_output_config(struct output_config *oc, struct sway_output *output);
490 struct sway_container *output);
491 492
492struct output_config *store_output_config(struct output_config *oc); 493struct output_config *store_output_config(struct output_config *oc);
493 494
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 7ac924e7..66e8c9a2 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -1,7 +1,6 @@
1#ifndef _SWAY_TRANSACTION_H 1#ifndef _SWAY_TRANSACTION_H
2#define _SWAY_TRANSACTION_H 2#define _SWAY_TRANSACTION_H
3#include <wlr/render/wlr_texture.h> 3#include <stdint.h>
4#include "sway/tree/container.h"
5 4
6/** 5/**
7 * Transactions enable us to perform atomic layout updates. 6 * Transactions enable us to perform atomic layout updates.
@@ -21,6 +20,7 @@
21 */ 20 */
22 21
23struct sway_transaction_instruction; 22struct sway_transaction_instruction;
23struct sway_view;
24 24
25/** 25/**
26 * Find all dirty containers, create and commit a transaction containing them, 26 * Find all dirty containers, create and commit a transaction containing them,
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index aa2f6f19..bde3cf46 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -37,10 +37,10 @@ struct sway_input_manager {
37struct sway_input_manager *input_manager_create(struct sway_server *server); 37struct sway_input_manager *input_manager_create(struct sway_server *server);
38 38
39bool input_manager_has_focus(struct sway_input_manager *input, 39bool input_manager_has_focus(struct sway_input_manager *input,
40 struct sway_container *container); 40 struct sway_node *node);
41 41
42void input_manager_set_focus(struct sway_input_manager *input, 42void input_manager_set_focus(struct sway_input_manager *input,
43 struct sway_container *container); 43 struct sway_node *node);
44 44
45void input_manager_configure_xcursor(struct sway_input_manager *input); 45void input_manager_configure_xcursor(struct sway_input_manager *input);
46 46
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 5c404ecd..8a7e5450 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -13,9 +13,9 @@ struct sway_seat_device {
13 struct wl_list link; // sway_seat::devices 13 struct wl_list link; // sway_seat::devices
14}; 14};
15 15
16struct sway_seat_container { 16struct sway_seat_node {
17 struct sway_seat *seat; 17 struct sway_seat *seat;
18 struct sway_container *container; 18 struct sway_node *node;
19 19
20 struct wl_list link; // sway_seat::focus_stack 20 struct wl_list link; // sway_seat::focus_stack
21 21
@@ -76,7 +76,7 @@ struct sway_seat {
76 uint32_t last_button_serial; 76 uint32_t last_button_serial;
77 77
78 struct wl_listener focus_destroy; 78 struct wl_listener focus_destroy;
79 struct wl_listener new_container; 79 struct wl_listener new_node;
80 struct wl_listener new_drag_icon; 80 struct wl_listener new_drag_icon;
81 81
82 struct wl_list devices; // sway_seat_device::link 82 struct wl_list devices; // sway_seat_device::link
@@ -100,10 +100,10 @@ void seat_remove_device(struct sway_seat *seat,
100 100
101void seat_configure_xcursor(struct sway_seat *seat); 101void seat_configure_xcursor(struct sway_seat *seat);
102 102
103void seat_set_focus(struct sway_seat *seat, struct sway_container *container); 103void seat_set_focus(struct sway_seat *seat, struct sway_node *node);
104 104
105void seat_set_focus_warp(struct sway_seat *seat, 105void seat_set_focus_warp(struct sway_seat *seat,
106 struct sway_container *container, bool warp, bool notify); 106 struct sway_node *node, bool warp, bool notify);
107 107
108void seat_set_focus_surface(struct sway_seat *seat, 108void seat_set_focus_surface(struct sway_seat *seat,
109 struct wlr_surface *surface, bool unfocus); 109 struct wlr_surface *surface, bool unfocus);
@@ -114,7 +114,11 @@ void seat_set_focus_layer(struct sway_seat *seat,
114void seat_set_exclusive_client(struct sway_seat *seat, 114void seat_set_exclusive_client(struct sway_seat *seat,
115 struct wl_client *client); 115 struct wl_client *client);
116 116
117struct sway_container *seat_get_focus(struct sway_seat *seat); 117struct sway_node *seat_get_focus(struct sway_seat *seat);
118
119struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat);
120
121struct sway_container *seat_get_focused_container(struct sway_seat *seat);
118 122
119/** 123/**
120 * Return the last container to be focused for the seat (or the most recently 124 * Return the last container to be focused for the seat (or the most recently
@@ -125,32 +129,31 @@ struct sway_container *seat_get_focus(struct sway_seat *seat);
125 * is destroyed, or focus moves to a container with children and we need to 129 * is destroyed, or focus moves to a container with children and we need to
126 * descend into the next leaf in focus order. 130 * descend into the next leaf in focus order.
127 */ 131 */
128struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, 132struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
129 struct sway_container *container); 133 struct sway_node *node);
130 134
131struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, 135struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
132 struct sway_container *container); 136 struct sway_workspace *workspace);
133 137
134/** 138/**
135 * Descend into the focus stack to find the focus-inactive view. Useful for 139 * Descend into the focus stack to find the focus-inactive view. Useful for
136 * container placement when they change position in the tree. 140 * container placement when they change position in the tree.
137 */ 141 */
138struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 142struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
139 struct sway_container *container); 143 struct sway_node *ancestor);
140 144
141/** 145/**
142 * Return the immediate child of container which was most recently focused. 146 * Return the immediate child of container which was most recently focused.
143 */ 147 */
144struct sway_container *seat_get_active_child(struct sway_seat *seat, 148struct sway_node *seat_get_active_child(struct sway_seat *seat,
145 struct sway_container *container); 149 struct sway_node *parent);
146 150
147/** 151/**
148 * Iterate over the focus-inactive children of the container calling the 152 * Iterate over the focus-inactive children of the container calling the
149 * function on each. 153 * function on each.
150 */ 154 */
151void seat_focus_inactive_children_for_each(struct sway_seat *seat, 155void seat_for_each_node(struct sway_seat *seat,
152 struct sway_container *container, 156 void (*f)(struct sway_node *node, void *data), void *data);
153 void (*f)(struct sway_container *container, void *data), void *data);
154 157
155void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); 158void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);
156 159
@@ -173,7 +176,7 @@ void seat_begin_resize_tiling(struct sway_seat *seat,
173 struct sway_container *con, uint32_t button, enum wlr_edges edge); 176 struct sway_container *con, uint32_t button, enum wlr_edges edge);
174 177
175struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, 178struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
176 struct sway_container *container); 179 struct sway_workspace *workspace);
177 180
178void seat_end_mouse_operation(struct sway_seat *seat); 181void seat_end_mouse_operation(struct sway_seat *seat);
179 182
diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h
index eaaa2164..fef243e3 100644
--- a/include/sway/ipc-json.h
+++ b/include/sway/ipc-json.h
@@ -7,8 +7,8 @@
7json_object *ipc_json_get_version(); 7json_object *ipc_json_get_version();
8 8
9json_object *ipc_json_describe_disabled_output(struct sway_output *o); 9json_object *ipc_json_describe_disabled_output(struct sway_output *o);
10json_object *ipc_json_describe_container(struct sway_container *c); 10json_object *ipc_json_describe_node(struct sway_node *node);
11json_object *ipc_json_describe_container_recursive(struct sway_container *c); 11json_object *ipc_json_describe_node_recursive(struct sway_node *node);
12json_object *ipc_json_describe_input(struct sway_input_device *device); 12json_object *ipc_json_describe_input(struct sway_input_device *device);
13json_object *ipc_json_describe_seat(struct sway_seat *seat); 13json_object *ipc_json_describe_seat(struct sway_seat *seat);
14json_object *ipc_json_describe_bar_config(struct bar_config *bar); 14json_object *ipc_json_describe_bar_config(struct bar_config *bar);
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h
index 4b6d0e25..80180ec4 100644
--- a/include/sway/ipc-server.h
+++ b/include/sway/ipc-server.h
@@ -11,8 +11,8 @@ void ipc_init(struct sway_server *server);
11 11
12struct sockaddr_un *ipc_user_sockaddr(void); 12struct sockaddr_un *ipc_user_sockaddr(void);
13 13
14void ipc_event_workspace(struct sway_container *old, 14void ipc_event_workspace(struct sway_workspace *old,
15 struct sway_container *new, const char *change); 15 struct sway_workspace *new, const char *change);
16void ipc_event_window(struct sway_container *window, const char *change); 16void ipc_event_window(struct sway_container *window, const char *change);
17void ipc_event_barconfig_update(struct bar_config *bar); 17void ipc_event_barconfig_update(struct bar_config *bar);
18void ipc_event_mode(const char *mode, bool pango); 18void ipc_event_mode(const char *mode, bool pango);
diff --git a/include/sway/output.h b/include/sway/output.h
index 651fdfe7..540ed8a0 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -6,14 +6,20 @@
6#include <wlr/types/wlr_box.h> 6#include <wlr/types/wlr_box.h>
7#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include "config.h" 8#include "config.h"
9#include "sway/tree/node.h"
9#include "sway/tree/view.h" 10#include "sway/tree/view.h"
10 11
11struct sway_server; 12struct sway_server;
12struct sway_container; 13struct sway_container;
13 14
15struct sway_output_state {
16 list_t *workspaces;
17 struct sway_workspace *active_workspace;
18};
19
14struct sway_output { 20struct sway_output {
21 struct sway_node node;
15 struct wlr_output *wlr_output; 22 struct wlr_output *wlr_output;
16 struct sway_container *swayc;
17 struct sway_server *server; 23 struct sway_server *server;
18 24
19 struct wl_list layers[4]; // sway_layer_surface::link 25 struct wl_list layers[4]; // sway_layer_surface::link
@@ -22,11 +28,15 @@ struct sway_output {
22 struct timespec last_frame; 28 struct timespec last_frame;
23 struct wlr_output_damage *damage; 29 struct wlr_output_damage *damage;
24 30
31 bool enabled;
32 list_t *workspaces;
33
34 struct sway_output_state current;
35
25 struct wl_listener destroy; 36 struct wl_listener destroy;
26 struct wl_listener mode; 37 struct wl_listener mode;
27 struct wl_listener transform; 38 struct wl_listener transform;
28 struct wl_listener scale; 39 struct wl_listener scale;
29
30 struct wl_listener damage_destroy; 40 struct wl_listener damage_destroy;
31 struct wl_listener damage_frame; 41 struct wl_listener damage_frame;
32 42
@@ -39,13 +49,19 @@ struct sway_output {
39 } events; 49 } events;
40}; 50};
41 51
42struct sway_container *output_create(struct sway_output *sway_output); 52struct sway_output *output_create(struct wlr_output *wlr_output);
53
54void output_destroy(struct sway_output *output);
55
56void output_begin_destroy(struct sway_output *output);
43 57
44void output_destroy(struct sway_container *output); 58struct sway_output *output_from_wlr_output(struct wlr_output *output);
45 59
46void output_begin_destroy(struct sway_container *output); 60struct sway_output *output_get_in_direction(struct sway_output *reference,
61 enum movement_direction direction);
47 62
48struct sway_container *output_from_wlr_output(struct wlr_output *output); 63void output_add_workspace(struct sway_output *output,
64 struct sway_workspace *workspace);
49 65
50typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, 66typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
51 struct wlr_surface *surface, struct wlr_box *box, float rotation, 67 struct wlr_surface *surface, struct wlr_box *box, float rotation,
@@ -64,15 +80,19 @@ void output_damage_box(struct sway_output *output, struct wlr_box *box);
64void output_damage_whole_container(struct sway_output *output, 80void output_damage_whole_container(struct sway_output *output,
65 struct sway_container *con); 81 struct sway_container *con);
66 82
67struct sway_container *output_by_name(const char *name); 83struct sway_output *output_by_name(const char *name);
68 84
69void output_sort_workspaces(struct sway_container *output); 85void output_sort_workspaces(struct sway_output *output);
70 86
71void output_enable(struct sway_output *output); 87struct output_config *output_find_config(struct sway_output *output);
88
89void output_enable(struct sway_output *output, struct output_config *oc);
90
91void output_disable(struct sway_output *output);
72 92
73bool output_has_opaque_overlay_layer_surface(struct sway_output *output); 93bool output_has_opaque_overlay_layer_surface(struct sway_output *output);
74 94
75struct sway_container *output_get_active_workspace(struct sway_output *output); 95struct sway_workspace *output_get_active_workspace(struct sway_output *output);
76 96
77void output_render(struct sway_output *output, struct timespec *when, 97void output_render(struct sway_output *output, struct timespec *when,
78 pixman_region32_t *damage); 98 pixman_region32_t *damage);
@@ -103,16 +123,23 @@ void output_drag_icons_for_each_surface(struct sway_output *output,
103 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, 123 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
104 void *user_data); 124 void *user_data);
105 125
106void output_for_each_workspace(struct sway_container *output, 126void output_for_each_workspace(struct sway_output *output,
107 void (*f)(struct sway_container *con, void *data), void *data); 127 void (*f)(struct sway_workspace *ws, void *data), void *data);
108 128
109void output_for_each_container(struct sway_container *output, 129void output_for_each_container(struct sway_output *output,
110 void (*f)(struct sway_container *con, void *data), void *data); 130 void (*f)(struct sway_container *con, void *data), void *data);
111 131
112struct sway_container *output_find_workspace(struct sway_container *output, 132struct sway_workspace *output_find_workspace(struct sway_output *output,
113 bool (*test)(struct sway_container *con, void *data), void *data); 133 bool (*test)(struct sway_workspace *ws, void *data), void *data);
114 134
115struct sway_container *output_find_container(struct sway_container *output, 135struct sway_container *output_find_container(struct sway_output *output,
116 bool (*test)(struct sway_container *con, void *data), void *data); 136 bool (*test)(struct sway_container *con, void *data), void *data);
117 137
138void output_get_box(struct sway_output *output, struct wlr_box *box);
139
140enum sway_container_layout output_get_default_layout(
141 struct sway_output *output);
142
143void output_add_listeners(struct sway_output *output);
144
118#endif 145#endif
diff --git a/include/sway/server.h b/include/sway/server.h
index 1e20f2c8..07e0949a 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -56,7 +56,7 @@ struct sway_server {
56 56
57 size_t txn_timeout_ms; 57 size_t txn_timeout_ms;
58 list_t *transactions; 58 list_t *transactions;
59 list_t *dirty_containers; 59 list_t *dirty_nodes;
60}; 60};
61 61
62struct sway_server server; 62struct sway_server server;
diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h
index f47e8db5..06a2279c 100644
--- a/include/sway/tree/arrange.h
+++ b/include/sway/tree/arrange.h
@@ -1,16 +1,19 @@
1#ifndef _SWAY_ARRANGE_H 1#ifndef _SWAY_ARRANGE_H
2#define _SWAY_ARRANGE_H 2#define _SWAY_ARRANGE_H
3 3
4struct sway_output;
5struct sway_workspace;
4struct sway_container; 6struct sway_container;
7struct sway_node;
5 8
6void arrange_container(struct sway_container *container); 9void arrange_container(struct sway_container *container);
7 10
8void arrange_workspace(struct sway_container *workspace); 11void arrange_workspace(struct sway_workspace *workspace);
9 12
10void arrange_output(struct sway_container *output); 13void arrange_output(struct sway_output *output);
11 14
12void arrange_root(void); 15void arrange_root(void);
13 16
14void arrange_windows(struct sway_container *container); 17void arrange_node(struct sway_node *node);
15 18
16#endif 19#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index e4071cfe..c51425c9 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -5,8 +5,7 @@
5#include <wlr/types/wlr_box.h> 5#include <wlr/types/wlr_box.h>
6#include <wlr/types/wlr_surface.h> 6#include <wlr/types/wlr_surface.h>
7#include "list.h" 7#include "list.h"
8 8#include "sway/tree/node.h"
9extern struct sway_container root_container;
10 9
11struct sway_view; 10struct sway_view;
12struct sway_seat; 11struct sway_seat;
@@ -17,23 +16,6 @@ struct sway_seat;
17#define TITLEBAR_H_PADDING 3 16#define TITLEBAR_H_PADDING 3
18#define TITLEBAR_V_PADDING 4 17#define TITLEBAR_V_PADDING 4
19 18
20/**
21 * Different kinds of containers.
22 *
23 * This enum is in order. A container will never be inside of a container below
24 * it on this list.
25 */
26enum sway_container_type {
27 C_ROOT,
28 C_OUTPUT,
29 C_WORKSPACE,
30 C_CONTAINER,
31 C_VIEW,
32
33 // Keep last
34 C_TYPES,
35};
36
37enum sway_container_layout { 19enum sway_container_layout {
38 L_NONE, 20 L_NONE,
39 L_HORIZ, 21 L_HORIZ,
@@ -57,18 +39,14 @@ enum movement_direction;
57enum wlr_direction; 39enum wlr_direction;
58 40
59struct sway_container_state { 41struct sway_container_state {
60 // Container/swayc properties 42 // Container properties
61 enum sway_container_layout layout; 43 enum sway_container_layout layout;
62 double swayc_x, swayc_y; 44 double con_x, con_y;
63 double swayc_width, swayc_height; 45 double con_width, con_height;
64 46
65 bool is_fullscreen; 47 bool is_fullscreen;
66 48
67 bool has_gaps; 49 struct sway_workspace *workspace;
68 double current_gaps;
69 double gaps_inner;
70 double gaps_outer;
71
72 struct sway_container *parent; 50 struct sway_container *parent;
73 list_t *children; 51 list_t *children;
74 52
@@ -86,35 +64,19 @@ struct sway_container_state {
86 bool border_left; 64 bool border_left;
87 bool border_right; 65 bool border_right;
88 bool using_csd; 66 bool using_csd;
89
90 // Workspace properties
91 struct sway_container *ws_fullscreen;
92 list_t *ws_floating;
93}; 67};
94 68
95struct sway_container { 69struct sway_container {
96 union { 70 struct sway_node node;
97 // TODO: Encapsulate state for other node types as well like C_CONTAINER 71 struct sway_view *view;
98 struct sway_root *sway_root;
99 struct sway_output *sway_output;
100 struct sway_workspace *sway_workspace;
101 struct sway_view *sway_view;
102 };
103
104 /**
105 * A unique ID to identify this container. Primarily used in the
106 * get_tree JSON output.
107 */
108 size_t id;
109 72
110 // The pending state is the main container properties, and the current state is in the below struct. 73 // The pending state is the main container properties, and the current state is in the below struct.
111 // This means most places of the code can refer to the main variables (pending state) and it'll just work. 74 // This means most places of the code can refer to the main variables (pending state) and it'll just work.
112 struct sway_container_state current; 75 struct sway_container_state current;
113 76
114 char *name; // The view's title (unformatted) 77 char *title; // The view's title (unformatted)
115 char *formatted_title; // The title displayed in the title bar 78 char *formatted_title; // The title displayed in the title bar
116 79
117 enum sway_container_type type;
118 enum sway_container_layout layout; 80 enum sway_container_layout layout;
119 enum sway_container_layout prev_split_layout; 81 enum sway_container_layout prev_split_layout;
120 82
@@ -132,14 +94,13 @@ struct sway_container {
132 94
133 // The gaps currently applied to the container. 95 // The gaps currently applied to the container.
134 double current_gaps; 96 double current_gaps;
135
136 bool has_gaps; 97 bool has_gaps;
137 double gaps_inner; 98 double gaps_inner;
138 double gaps_outer; 99 double gaps_outer;
139 100
140 list_t *children; 101 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
141 102 struct sway_container *parent; // NULL if container in root of workspace
142 struct sway_container *parent; 103 list_t *children; // struct sway_container
143 104
144 // Outputs currently being intersected 105 // Outputs currently being intersected
145 list_t *outputs; // struct sway_output 106 list_t *outputs; // struct sway_output
@@ -157,42 +118,17 @@ struct sway_container {
157 struct wlr_texture *title_urgent; 118 struct wlr_texture *title_urgent;
158 size_t title_height; 119 size_t title_height;
159 120
160 // The number of transactions which reference this container.
161 size_t ntxnrefs;
162
163 // If this container is a view and is waiting for the client to respond to a
164 // configure then this will be populated, otherwise NULL.
165 struct sway_transaction_instruction *instruction;
166
167 bool destroying;
168
169 // If true, indicates that the container has pending state that differs from
170 // the current.
171 bool dirty;
172
173 struct { 121 struct {
174 struct wl_signal destroy; 122 struct wl_signal destroy;
175 } events; 123 } events;
176}; 124};
177 125
178struct sway_container *container_create(enum sway_container_type type); 126struct sway_container *container_create(struct sway_view *view);
179
180const char *container_type_to_str(enum sway_container_type type);
181
182/*
183 * Create a new view container. A view can be a child of a workspace container
184 * or a container container and are rendered in the order and structure of
185 * how they are attached to the tree.
186 */
187struct sway_container *container_view_create(
188 struct sway_container *sibling, struct sway_view *sway_view);
189 127
190void container_destroy(struct sway_container *con); 128void container_destroy(struct sway_container *con);
191 129
192void container_begin_destroy(struct sway_container *con); 130void container_begin_destroy(struct sway_container *con);
193 131
194struct sway_container *container_close(struct sway_container *container);
195
196/** 132/**
197 * Search a container's descendants a container based on test criteria. Returns 133 * Search a container's descendants a container based on test criteria. Returns
198 * the first container that passes the test. 134 * the first container that passes the test.
@@ -201,22 +137,16 @@ struct sway_container *container_find_child(struct sway_container *container,
201 bool (*test)(struct sway_container *view, void *data), void *data); 137 bool (*test)(struct sway_container *view, void *data), void *data);
202 138
203/** 139/**
204 * Finds a parent container with the given struct sway_containerype.
205 */
206struct sway_container *container_parent(struct sway_container *container,
207 enum sway_container_type type);
208
209/**
210 * Find a container at the given coordinates. Returns the the surface and 140 * Find a container at the given coordinates. Returns the the surface and
211 * surface-local coordinates of the given layout coordinates if the container 141 * surface-local coordinates of the given layout coordinates if the container
212 * is a view and the view contains a surface at those coordinates. 142 * is a view and the view contains a surface at those coordinates.
213 */ 143 */
214struct sway_container *container_at(struct sway_container *workspace, 144struct sway_container *container_at(struct sway_workspace *workspace,
215 double lx, double ly, struct wlr_surface **surface, 145 double lx, double ly, struct wlr_surface **surface,
216 double *sx, double *sy); 146 double *sx, double *sy);
217 147
218struct sway_container *tiling_container_at( 148struct sway_container *tiling_container_at(
219 struct sway_container *con, double lx, double ly, 149 struct sway_node *parent, double lx, double ly,
220 struct wlr_surface **surface, double *sx, double *sy); 150 struct wlr_surface **surface, double *sx, double *sy);
221 151
222void container_for_each_child(struct sway_container *container, 152void container_for_each_child(struct sway_container *container,
@@ -228,16 +158,11 @@ void container_for_each_child(struct sway_container *container,
228bool container_has_ancestor(struct sway_container *container, 158bool container_has_ancestor(struct sway_container *container,
229 struct sway_container *ancestor); 159 struct sway_container *ancestor);
230 160
231int container_count_descendants_of_type(struct sway_container *con,
232 enum sway_container_type type);
233
234void container_create_notify(struct sway_container *container);
235
236void container_update_textures_recursive(struct sway_container *con); 161void container_update_textures_recursive(struct sway_container *con);
237 162
238void container_damage_whole(struct sway_container *container); 163void container_damage_whole(struct sway_container *container);
239 164
240struct sway_container *container_reap_empty(struct sway_container *con); 165void container_reap_empty(struct sway_container *con);
241 166
242struct sway_container *container_flatten(struct sway_container *container); 167struct sway_container *container_flatten(struct sway_container *container);
243 168
@@ -248,11 +173,10 @@ void container_update_title_textures(struct sway_container *container);
248 */ 173 */
249void container_calculate_title_height(struct sway_container *container); 174void container_calculate_title_height(struct sway_container *container);
250 175
251/** 176size_t container_build_representation(enum sway_container_layout layout,
252 * Notify a container that a tree modification has changed in its children, 177 list_t *children, char *buffer);
253 * so the container can update its tree representation. 178
254 */ 179void container_update_representation(struct sway_container *container);
255void container_notify_subtree_changed(struct sway_container *container);
256 180
257/** 181/**
258 * Return the height of a regular title bar. 182 * Return the height of a regular title bar.
@@ -288,8 +212,7 @@ void container_floating_translate(struct sway_container *con,
288/** 212/**
289 * Choose an output for the floating container's new position. 213 * Choose an output for the floating container's new position.
290 */ 214 */
291struct sway_container *container_floating_find_output( 215struct sway_output *container_floating_find_output(struct sway_container *con);
292 struct sway_container *con);
293 216
294/** 217/**
295 * Move a floating container to a new layout-local position. 218 * Move a floating container to a new layout-local position.
@@ -302,12 +225,6 @@ void container_floating_move_to(struct sway_container *con,
302 */ 225 */
303void container_floating_move_to_center(struct sway_container *con); 226void container_floating_move_to_center(struct sway_container *con);
304 227
305/**
306 * Mark a container as dirty if it isn't already. Dirty containers will be
307 * included in the next transaction then unmarked as dirty.
308 */
309void container_set_dirty(struct sway_container *container);
310
311bool container_has_urgent_child(struct sway_container *container); 228bool container_has_urgent_child(struct sway_container *container);
312 229
313/** 230/**
@@ -342,10 +259,18 @@ void container_remove_gaps(struct sway_container *container);
342 259
343void container_add_gaps(struct sway_container *container); 260void container_add_gaps(struct sway_container *container);
344 261
262enum sway_container_layout container_parent_layout(struct sway_container *con);
263
264enum sway_container_layout container_current_parent_layout(
265 struct sway_container *con);
266
267list_t *container_get_siblings(const struct sway_container *container);
268
345int container_sibling_index(const struct sway_container *child); 269int container_sibling_index(const struct sway_container *child);
346 270
347void container_handle_fullscreen_reparent(struct sway_container *con, 271list_t *container_get_current_siblings(struct sway_container *container);
348 struct sway_container *old_parent); 272
273void container_handle_fullscreen_reparent(struct sway_container *con);
349 274
350void container_add_child(struct sway_container *parent, 275void container_add_child(struct sway_container *parent,
351 struct sway_container *child); 276 struct sway_container *child);
@@ -353,19 +278,16 @@ void container_add_child(struct sway_container *parent,
353void container_insert_child(struct sway_container *parent, 278void container_insert_child(struct sway_container *parent,
354 struct sway_container *child, int i); 279 struct sway_container *child, int i);
355 280
356struct sway_container *container_add_sibling(struct sway_container *parent, 281void container_add_sibling(struct sway_container *parent,
357 struct sway_container *child); 282 struct sway_container *child, int offset);
358 283
359struct sway_container *container_remove_child(struct sway_container *child); 284void container_detach(struct sway_container *child);
360 285
361struct sway_container *container_replace_child(struct sway_container *child, 286void container_replace(struct sway_container *container,
362 struct sway_container *new_child); 287 struct sway_container *replacement);
363 288
364bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out); 289bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out);
365 290
366enum sway_container_layout container_get_default_layout(
367 struct sway_container *con);
368
369struct sway_container *container_split(struct sway_container *child, 291struct sway_container *container_split(struct sway_container *child,
370 enum sway_container_layout layout); 292 enum sway_container_layout layout);
371 293
diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h
new file mode 100644
index 00000000..5b8c1909
--- /dev/null
+++ b/include/sway/tree/node.h
@@ -0,0 +1,74 @@
1#ifndef _SWAY_NODE_H
2#define _SWAY_NODE_H
3#include <stdbool.h>
4#include "list.h"
5
6struct sway_root;
7struct sway_output;
8struct sway_workspace;
9struct sway_container;
10struct sway_transaction_instruction;
11struct wlr_box;
12
13enum sway_node_type {
14 N_ROOT,
15 N_OUTPUT,
16 N_WORKSPACE,
17 N_CONTAINER,
18};
19
20struct sway_node {
21 enum sway_node_type type;
22 union {
23 struct sway_root *sway_root;
24 struct sway_output *sway_output;
25 struct sway_workspace *sway_workspace;
26 struct sway_container *sway_container;
27 };
28
29 /**
30 * A unique ID to identify this node.
31 * Primarily used in the get_tree JSON output.
32 */
33 size_t id;
34
35 struct sway_transaction_instruction *instruction;
36 size_t ntxnrefs;
37 bool destroying;
38
39 // If true, indicates that the container has pending state that differs from
40 // the current.
41 bool dirty;
42
43 struct {
44 struct wl_signal destroy;
45 } events;
46};
47
48void node_init(struct sway_node *node, enum sway_node_type type, void *thing);
49
50const char *node_type_to_str(enum sway_node_type type);
51
52/**
53 * Mark a node as dirty if it isn't already. Dirty nodes will be included in the
54 * next transaction then unmarked as dirty.
55 */
56void node_set_dirty(struct sway_node *node);
57
58bool node_is_view(struct sway_node *node);
59
60char *node_get_name(struct sway_node *node);
61
62void node_get_box(struct sway_node *node, struct wlr_box *box);
63
64struct sway_output *node_get_output(struct sway_node *node);
65
66enum sway_container_layout node_get_layout(struct sway_node *node);
67
68struct sway_node *node_get_parent(struct sway_node *node);
69
70list_t *node_get_children(struct sway_node *node);
71
72bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor);
73
74#endif
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h
index ec6516c9..a2d464f9 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -5,12 +5,14 @@
5#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/render/wlr_texture.h> 6#include <wlr/render/wlr_texture.h>
7#include "sway/tree/container.h" 7#include "sway/tree/container.h"
8#include "sway/tree/node.h"
8#include "config.h" 9#include "config.h"
9#include "list.h" 10#include "list.h"
10 11
11extern struct sway_container root_container; 12extern struct sway_root *root;
12 13
13struct sway_root { 14struct sway_root {
15 struct sway_node node;
14 struct wlr_output_layout *output_layout; 16 struct wlr_output_layout *output_layout;
15 17
16 struct wl_listener output_layout_change; 18 struct wl_listener output_layout_change;
@@ -24,17 +26,21 @@ struct sway_root {
24 // Includes disabled outputs 26 // Includes disabled outputs
25 struct wl_list all_outputs; // sway_output::link 27 struct wl_list all_outputs; // sway_output::link
26 28
29 double x, y;
30 double width, height;
31
32 list_t *outputs; // struct sway_output
27 list_t *scratchpad; // struct sway_container 33 list_t *scratchpad; // struct sway_container
28 list_t *saved_workspaces; // For when there's no connected outputs 34 list_t *saved_workspaces; // For when there's no connected outputs
29 35
30 struct { 36 struct {
31 struct wl_signal new_container; 37 struct wl_signal new_node;
32 } events; 38 } events;
33}; 39};
34 40
35void root_create(void); 41struct sway_root *root_create(void);
36 42
37void root_destroy(void); 43void root_destroy(struct sway_root *root);
38 44
39/** 45/**
40 * Move a container to the scratchpad. 46 * Move a container to the scratchpad.
@@ -56,23 +62,25 @@ void root_scratchpad_show(struct sway_container *con);
56 */ 62 */
57void root_scratchpad_hide(struct sway_container *con); 63void root_scratchpad_hide(struct sway_container *con);
58 64
59struct sway_container *root_workspace_for_pid(pid_t pid); 65struct sway_workspace *root_workspace_for_pid(pid_t pid);
60 66
61void root_record_workspace_pid(pid_t pid); 67void root_record_workspace_pid(pid_t pid);
62 68
63void root_for_each_workspace(void (*f)(struct sway_container *con, void *data), 69void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
64 void *data); 70 void *data);
65 71
66void root_for_each_container(void (*f)(struct sway_container *con, void *data), 72void root_for_each_container(void (*f)(struct sway_container *con, void *data),
67 void *data); 73 void *data);
68 74
69struct sway_container *root_find_output( 75struct sway_output *root_find_output(
70 bool (*test)(struct sway_container *con, void *data), void *data); 76 bool (*test)(struct sway_output *output, void *data), void *data);
71 77
72struct sway_container *root_find_workspace( 78struct sway_workspace *root_find_workspace(
73 bool (*test)(struct sway_container *con, void *data), void *data); 79 bool (*test)(struct sway_workspace *ws, void *data), void *data);
74 80
75struct sway_container *root_find_container( 81struct sway_container *root_find_container(
76 bool (*test)(struct sway_container *con, void *data), void *data); 82 bool (*test)(struct sway_container *con, void *data), void *data);
77 83
84void root_get_box(struct sway_root *root, struct wlr_box *box);
85
78#endif 86#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 30d3e742..439dc1bf 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -58,7 +58,7 @@ struct sway_view {
58 enum sway_view_type type; 58 enum sway_view_type type;
59 const struct sway_view_impl *impl; 59 const struct sway_view_impl *impl;
60 60
61 struct sway_container *swayc; // NULL for unmapped views 61 struct sway_container *container; // NULL if unmapped and transactions finished
62 struct wlr_surface *surface; // NULL for unmapped views 62 struct wlr_surface *surface; // NULL for unmapped views
63 63
64 // Geometry of the view itself (excludes borders) in layout coordinates 64 // Geometry of the view itself (excludes borders) in layout coordinates
@@ -254,7 +254,7 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
254 int height); 254 int height);
255 255
256/** 256/**
257 * Configure the view's position and size based on the swayc's position and 257 * Configure the view's position and size based on the container's position and
258 * size, taking borders into consideration. 258 * size, taking borders into consideration.
259 */ 259 */
260void view_autoconfigure(struct sway_view *view); 260void view_autoconfigure(struct sway_view *view);
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index 04325919..af9a071a 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -3,66 +3,98 @@
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/node.h"
6 7
7struct sway_view; 8struct sway_view;
8 9
10struct sway_workspace_state {
11 struct sway_container *fullscreen;
12 double x, y;
13 int width, height;
14 enum sway_container_layout layout;
15 struct sway_output *output;
16 list_t *floating;
17 list_t *tiling;
18
19 struct sway_container *focused_inactive_child;
20 bool focused;
21};
22
9struct sway_workspace { 23struct sway_workspace {
10 struct sway_container *swayc; 24 struct sway_node node;
11 struct sway_container *fullscreen; 25 struct sway_container *fullscreen;
12 list_t *floating; // struct sway_container 26
27 char *name;
28 char *representation;
29
30 double x, y;
31 int width, height;
32 enum sway_container_layout layout;
33 enum sway_container_layout prev_split_layout;
34
35 double current_gaps;
36 bool has_gaps;
37 double gaps_inner;
38 double gaps_outer;
39
40 struct sway_output *output; // NULL if no outputs are connected
41 list_t *floating; // struct sway_container
42 list_t *tiling; // struct sway_container
13 list_t *output_priority; 43 list_t *output_priority;
14 bool urgent; 44 bool urgent;
45
46 struct sway_workspace_state current;
15}; 47};
16 48
17extern char *prev_workspace_name; 49extern char *prev_workspace_name;
18 50
19struct sway_container *workspace_get_initial_output(const char *name); 51struct sway_output *workspace_get_initial_output(const char *name);
20 52
21struct sway_container *workspace_create(struct sway_container *output, 53struct sway_workspace *workspace_create(struct sway_output *output,
22 const char *name); 54 const char *name);
23 55
24void workspace_destroy(struct sway_container *workspace); 56void workspace_destroy(struct sway_workspace *workspace);
25 57
26void workspace_begin_destroy(struct sway_container *workspace); 58void workspace_begin_destroy(struct sway_workspace *workspace);
27 59
28void workspace_consider_destroy(struct sway_container *ws); 60void workspace_consider_destroy(struct sway_workspace *ws);
29 61
30char *workspace_next_name(const char *output_name); 62char *workspace_next_name(const char *output_name);
31 63
32bool workspace_switch(struct sway_container *workspace, 64bool workspace_switch(struct sway_workspace *workspace,
33 bool no_auto_back_and_forth); 65 bool no_auto_back_and_forth);
34 66
35struct sway_container *workspace_by_number(const char* name); 67struct sway_workspace *workspace_by_number(const char* name);
36 68
37struct sway_container *workspace_by_name(const char*); 69struct sway_workspace *workspace_by_name(const char*);
38 70
39struct sway_container *workspace_output_next(struct sway_container *current); 71struct sway_workspace *workspace_output_next(struct sway_workspace *current);
40 72
41struct sway_container *workspace_next(struct sway_container *current); 73struct sway_workspace *workspace_next(struct sway_workspace *current);
42 74
43struct sway_container *workspace_output_prev(struct sway_container *current); 75struct sway_workspace *workspace_output_prev(struct sway_workspace *current);
44 76
45struct sway_container *workspace_prev(struct sway_container *current); 77struct sway_workspace *workspace_prev(struct sway_workspace *current);
46 78
47bool workspace_is_visible(struct sway_container *ws); 79bool workspace_is_visible(struct sway_workspace *ws);
48 80
49bool workspace_is_empty(struct sway_container *ws); 81bool workspace_is_empty(struct sway_workspace *ws);
50 82
51void workspace_output_raise_priority(struct sway_container *workspace, 83void workspace_output_raise_priority(struct sway_workspace *workspace,
52 struct sway_container *old_output, struct sway_container *new_output); 84 struct sway_output *old_output, struct sway_output *new_output);
53 85
54void workspace_output_add_priority(struct sway_container *workspace, 86void workspace_output_add_priority(struct sway_workspace *workspace,
55 struct sway_container *output); 87 struct sway_output *output);
56 88
57struct sway_container *workspace_output_get_highest_available( 89struct sway_output *workspace_output_get_highest_available(
58 struct sway_container *ws, struct sway_container *exclude); 90 struct sway_workspace *ws, struct sway_output *exclude);
59 91
60void workspace_detect_urgent(struct sway_container *workspace); 92void workspace_detect_urgent(struct sway_workspace *workspace);
61 93
62void workspace_for_each_container(struct sway_container *ws, 94void workspace_for_each_container(struct sway_workspace *ws,
63 void (*f)(struct sway_container *con, void *data), void *data); 95 void (*f)(struct sway_container *con, void *data), void *data);
64 96
65struct sway_container *workspace_find_container(struct sway_container *ws, 97struct sway_container *workspace_find_container(struct sway_workspace *ws,
66 bool (*test)(struct sway_container *con, void *data), void *data); 98 bool (*test)(struct sway_container *con, void *data), void *data);
67 99
68/** 100/**
@@ -70,13 +102,28 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
70 * The new container will be the only direct tiling child of the workspace. 102 * The new container will be the only direct tiling child of the workspace.
71 * The new container is returned. 103 * The new container is returned.
72 */ 104 */
73struct sway_container *workspace_wrap_children(struct sway_container *ws); 105struct sway_container *workspace_wrap_children(struct sway_workspace *ws);
74 106
75void workspace_add_floating(struct sway_container *workspace, 107void workspace_detach(struct sway_workspace *workspace);
108
109void workspace_add_tiling(struct sway_workspace *workspace,
110 struct sway_container *con);
111
112void workspace_add_floating(struct sway_workspace *workspace,
76 struct sway_container *con); 113 struct sway_container *con);
77 114
78void workspace_remove_gaps(struct sway_container *ws); 115void workspace_insert_tiling(struct sway_workspace *workspace,
116 struct sway_container *con, int index);
117
118void workspace_remove_gaps(struct sway_workspace *ws);
119
120void workspace_add_gaps(struct sway_workspace *ws);
121
122struct sway_container *workspace_split(struct sway_workspace *workspace,
123 enum sway_container_layout layout);
124
125void workspace_update_representation(struct sway_workspace *ws);
79 126
80void workspace_add_gaps(struct sway_container *ws); 127void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box);
81 128
82#endif 129#endif
diff --git a/sway/commands.c b/sway/commands.c
index e72b8916..b32628cd 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -212,6 +212,24 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
212 return res; 212 return res;
213} 213}
214 214
215static void set_config_node(struct sway_node *node) {
216 config->handler_context.node = node;
217 switch (node->type) {
218 case N_CONTAINER:
219 config->handler_context.container = node->sway_container;
220 config->handler_context.workspace = node->sway_container->workspace;
221 break;
222 case N_WORKSPACE:
223 config->handler_context.container = NULL;
224 config->handler_context.workspace = node->sway_workspace;
225 break;
226 default:
227 config->handler_context.container = NULL;
228 config->handler_context.workspace = NULL;
229 break;
230 }
231}
232
215struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { 233struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
216 // Even though this function will process multiple commands we will only 234 // Even though this function will process multiple commands we will only
217 // return the last error, if any (for now). (Since we have access to an 235 // return the last error, if any (for now). (Since we have access to an
@@ -295,12 +313,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
295 if (!config->handler_context.using_criteria) { 313 if (!config->handler_context.using_criteria) {
296 // without criteria, the command acts upon the focused 314 // without criteria, the command acts upon the focused
297 // container 315 // container
298 config->handler_context.current_container = 316 set_config_node(seat_get_focus_inactive(seat, &root->node));
299 seat_get_focus_inactive(seat, &root_container);
300 if (!sway_assert(config->handler_context.current_container,
301 "could not get focus-inactive for root container")) {
302 return NULL;
303 }
304 struct cmd_results *res = handler->handle(argc-1, argv+1); 317 struct cmd_results *res = handler->handle(argc-1, argv+1);
305 if (res->status != CMD_SUCCESS) { 318 if (res->status != CMD_SUCCESS) {
306 free_argv(argc, argv); 319 free_argv(argc, argv);
@@ -314,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
314 } else { 327 } else {
315 for (int i = 0; i < views->length; ++i) { 328 for (int i = 0; i < views->length; ++i) {
316 struct sway_view *view = views->items[i]; 329 struct sway_view *view = views->items[i];
317 config->handler_context.current_container = view->swayc; 330 set_config_node(&view->container->node);
318 struct cmd_results *res = handler->handle(argc-1, argv+1); 331 struct cmd_results *res = handler->handle(argc-1, argv+1);
319 if (res->status != CMD_SUCCESS) { 332 if (res->status != CMD_SUCCESS) {
320 free_argv(argc, argv); 333 free_argv(argc, argv);
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 9502c877..95498b2f 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -13,13 +13,12 @@ struct cmd_results *cmd_border(int argc, char **argv) {
13 return error; 13 return error;
14 } 14 }
15 15
16 struct sway_container *container = 16 struct sway_container *container = config->handler_context.container;
17 config->handler_context.current_container; 17 if (!container->view) {
18 if (container->type != C_VIEW) {
19 return cmd_results_new(CMD_INVALID, "border", 18 return cmd_results_new(CMD_INVALID, "border",
20 "Only views can have borders"); 19 "Only views can have borders");
21 } 20 }
22 struct sway_view *view = container->sway_view; 21 struct sway_view *view = container->view;
23 22
24 if (strcmp(argv[0], "none") == 0) { 23 if (strcmp(argv[0], "none") == 0) {
25 view->border = B_NONE; 24 view->border = B_NONE;
@@ -38,11 +37,11 @@ struct cmd_results *cmd_border(int argc, char **argv) {
38 view->border_thickness = atoi(argv[1]); 37 view->border_thickness = atoi(argv[1]);
39 } 38 }
40 39
41 if (container_is_floating(view->swayc)) { 40 if (container_is_floating(view->container)) {
42 container_set_geometry_from_floating_view(view->swayc); 41 container_set_geometry_from_floating_view(view->container);
43 } 42 }
44 43
45 arrange_windows(view->swayc); 44 arrange_container(view->container);
46 45
47 struct sway_seat *seat = input_manager_current_seat(input_manager); 46 struct sway_seat *seat = input_manager_current_seat(input_manager);
48 if (seat->cursor) { 47 if (seat->cursor) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index 436376e3..d8729094 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -15,24 +15,23 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
15 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) { 15 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
16 return error; 16 return error;
17 } 17 }
18 struct sway_container *container = 18 struct sway_container *container = config->handler_context.container;
19 config->handler_context.current_container; 19 struct sway_workspace *workspace = config->handler_context.workspace;
20 if (container->type == C_WORKSPACE && container->children->length == 0) { 20 if (!container && workspace->tiling->length == 0) {
21 return cmd_results_new(CMD_INVALID, "floating", 21 return cmd_results_new(CMD_INVALID, "floating",
22 "Can't float an empty workspace"); 22 "Can't float an empty workspace");
23 } 23 }
24 if (container->type == C_WORKSPACE) { 24 if (!container) {
25 // Wrap the workspace's children in a container so we can float it 25 // Wrap the workspace's children in a container so we can float it
26 struct sway_container *workspace = container; 26 container = workspace_wrap_children(workspace);
27 container = workspace_wrap_children(container);
28 workspace->layout = L_HORIZ; 27 workspace->layout = L_HORIZ;
29 seat_set_focus(config->handler_context.seat, container); 28 seat_set_focus(config->handler_context.seat, &container->node);
30 } 29 }
31 30
32 // If the container is in a floating split container, 31 // If the container is in a floating split container,
33 // operate on the split container instead of the child. 32 // operate on the split container instead of the child.
34 if (container_is_floating_or_child(container)) { 33 if (container_is_floating_or_child(container)) {
35 while (container->parent->type != C_WORKSPACE) { 34 while (container->parent) {
36 container = container->parent; 35 container = container->parent;
37 } 36 }
38 } 37 }
@@ -51,8 +50,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
51 50
52 container_set_floating(container, wants_floating); 51 container_set_floating(container, wants_floating);
53 52
54 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 53 arrange_workspace(container->workspace);
55 arrange_windows(workspace);
56 54
57 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 55 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
58} 56}
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index f342e524..e31898af 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -34,211 +34,139 @@ static bool parse_movement_direction(const char *name,
34} 34}
35 35
36/** 36/**
37 * Get swayc in the direction of newly entered output. 37 * Get node in the direction of newly entered output.
38 */ 38 */
39static struct sway_container *get_swayc_in_output_direction( 39static struct sway_node *get_node_in_output_direction(
40 struct sway_container *output, enum movement_direction dir, 40 struct sway_output *output, enum movement_direction dir) {
41 struct sway_seat *seat) { 41 struct sway_seat *seat = config->handler_context.seat;
42 if (!output) { 42 struct sway_workspace *ws = output_get_active_workspace(output);
43 return NULL; 43 if (ws->fullscreen) {
44 } 44 return seat_get_focus_inactive(seat, &ws->fullscreen->node);
45
46 struct sway_container *ws = seat_get_focus_inactive(seat, output);
47 if (ws->type != C_WORKSPACE) {
48 ws = container_parent(ws, C_WORKSPACE);
49 }
50
51 if (ws == NULL) {
52 wlr_log(WLR_ERROR, "got an output without a workspace");
53 return NULL;
54 } 45 }
46 struct sway_container *container = NULL;
55 47
56 if (ws->children->length > 0) { 48 if (ws->tiling->length > 0) {
57 switch (dir) { 49 switch (dir) {
58 case MOVE_LEFT: 50 case MOVE_LEFT:
59 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 51 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
60 // get most right child of new output 52 // get most right child of new output
61 return ws->children->items[ws->children->length-1]; 53 container = ws->tiling->items[ws->tiling->length-1];
62 } else { 54 } else {
63 return seat_get_focus_inactive(seat, ws); 55 container = seat_get_focus_inactive_tiling(seat, ws);
64 } 56 }
57 return &container->node;
65 case MOVE_RIGHT: 58 case MOVE_RIGHT:
66 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 59 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
67 // get most left child of new output 60 // get most left child of new output
68 return ws->children->items[0]; 61 container = ws->tiling->items[0];
69 } else { 62 } else {
70 return seat_get_focus_inactive(seat, ws); 63 container = seat_get_focus_inactive_tiling(seat, ws);
71 } 64 }
65 return &container->node;
72 case MOVE_UP: 66 case MOVE_UP:
67 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
68 // get most bottom child of new output
69 container = ws->tiling->items[ws->tiling->length-1];
70 } else {
71 container = seat_get_focus_inactive_tiling(seat, ws);
72 }
73 return &container->node;
73 case MOVE_DOWN: { 74 case MOVE_DOWN: {
74 struct sway_container *focused = 75 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
75 seat_get_focus_inactive(seat, ws); 76 // get most top child of new output
76 if (focused && focused->parent) { 77 container = ws->tiling->items[0];
77 struct sway_container *parent = focused->parent; 78 } else {
78 if (parent->layout == L_VERT) { 79 container = seat_get_focus_inactive_tiling(seat, ws);
79 if (dir == MOVE_UP) {
80 // get child furthest down on new output
81 int idx = parent->children->length - 1;
82 return parent->children->items[idx];
83 } else if (dir == MOVE_DOWN) {
84 // get child furthest up on new output
85 return parent->children->items[0];
86 }
87 }
88 return focused;
89 } 80 }
90 break; 81 return &container->node;
91 } 82 }
92 default: 83 default:
93 break; 84 break;
94 } 85 }
95 } 86 }
96 87
97 return ws; 88 return &ws->node;
98} 89}
99 90
100static struct sway_container *container_get_in_direction( 91static struct sway_node *node_get_in_direction(struct sway_container *container,
101 struct sway_container *container, struct sway_seat *seat, 92 struct sway_seat *seat, enum movement_direction dir) {
102 enum movement_direction dir) {
103 struct sway_container *parent = container->parent;
104
105 if (dir == MOVE_CHILD) { 93 if (dir == MOVE_CHILD) {
106 return seat_get_focus_inactive(seat, container); 94 return seat_get_active_child(seat, &container->node);
107 } 95 }
108 if (container->is_fullscreen) { 96 if (container->is_fullscreen) {
109 if (dir == MOVE_PARENT) { 97 if (dir == MOVE_PARENT) {
110 return NULL; 98 return NULL;
111 } 99 }
112 container = container_parent(container, C_OUTPUT); 100 // Fullscreen container with a direction - go straight to outputs
113 parent = container->parent; 101 struct sway_output *output = container->workspace->output;
114 } else { 102 struct sway_output *new_output = output_get_in_direction(output, dir);
115 if (dir == MOVE_PARENT) { 103 return get_node_in_output_direction(new_output, dir);
116 if (parent->type == C_OUTPUT || container_is_floating(container)) { 104 }
117 return NULL; 105 if (dir == MOVE_PARENT) {
118 } else { 106 return node_get_parent(&container->node);
119 return parent;
120 }
121 }
122 } 107 }
123 108
124 struct sway_container *wrap_candidate = NULL; 109 struct sway_container *wrap_candidate = NULL;
125 while (true) { 110 struct sway_container *current = container;
111 while (current) {
126 bool can_move = false; 112 bool can_move = false;
127 int desired; 113 int desired;
128 int idx = list_find(container->parent->children, container); 114 int idx = container_sibling_index(current);
129 if (idx == -1) { 115 enum sway_container_layout parent_layout =
130 return NULL; 116 container_parent_layout(current);
131 } 117 list_t *siblings = container_get_siblings(current);
132 if (parent->type == C_ROOT) {
133 enum wlr_direction wlr_dir = 0;
134 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
135 "got invalid direction: %d", dir)) {
136 return NULL;
137 }
138 int lx = container->x + container->width / 2;
139 int ly = container->y + container->height / 2;
140 struct wlr_output_layout *layout =
141 root_container.sway_root->output_layout;
142 struct wlr_output *wlr_adjacent =
143 wlr_output_layout_adjacent_output(layout, wlr_dir,
144 container->sway_output->wlr_output, lx, ly);
145 struct sway_container *adjacent =
146 output_from_wlr_output(wlr_adjacent);
147 118
148 if (!adjacent || adjacent == container) { 119 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
149 if (!wrap_candidate) { 120 if (parent_layout == L_HORIZ || parent_layout == L_TABBED) {
150 return NULL; 121 can_move = true;
151 } 122 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
152 return seat_get_focus_inactive_view(seat, wrap_candidate);
153 }
154 struct sway_container *next =
155 get_swayc_in_output_direction(adjacent, dir, seat);
156 if (next == NULL) {
157 return NULL;
158 }
159 struct sway_container *next_workspace = next;
160 if (next_workspace->type != C_WORKSPACE) {
161 next_workspace = container_parent(next_workspace, C_WORKSPACE);
162 }
163 sway_assert(next_workspace, "Next container has no workspace");
164 if (next_workspace->sway_workspace->fullscreen) {
165 return seat_get_focus_inactive(seat,
166 next_workspace->sway_workspace->fullscreen);
167 }
168 if (next->children && next->children->length) {
169 // TODO consider floating children as well
170 return seat_get_focus_inactive_view(seat, next);
171 } else {
172 return next;
173 } 123 }
174 } else { 124 } else {
175 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 125 if (parent_layout == L_VERT || parent_layout == L_STACKED) {
176 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { 126 can_move = true;
177 can_move = true; 127 desired = idx + (dir == MOVE_UP ? -1 : 1);
178 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
179 }
180 } else {
181 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
182 can_move = true;
183 desired = idx + (dir == MOVE_UP ? -1 : 1);
184 }
185 } 128 }
186 } 129 }
187 130
188 if (can_move) { 131 if (can_move) {
189 // TODO handle floating 132 if (desired < 0 || desired >= siblings->length) {
190 if (desired < 0 || desired >= parent->children->length) {
191 can_move = false; 133 can_move = false;
192 int len = parent->children->length; 134 int len = siblings->length;
193 if (config->focus_wrapping != WRAP_NO && !wrap_candidate 135 if (config->focus_wrapping != WRAP_NO && !wrap_candidate
194 && len > 1) { 136 && len > 1) {
195 if (desired < 0) { 137 if (desired < 0) {
196 wrap_candidate = parent->children->items[len-1]; 138 wrap_candidate = siblings->items[len-1];
197 } else { 139 } else {
198 wrap_candidate = parent->children->items[0]; 140 wrap_candidate = siblings->items[0];
199 } 141 }
200 if (config->focus_wrapping == WRAP_FORCE) { 142 if (config->focus_wrapping == WRAP_FORCE) {
201 return seat_get_focus_inactive_view(seat, 143 struct sway_container *c = seat_get_focus_inactive_view(
202 wrap_candidate); 144 seat, &wrap_candidate->node);
145 return &c->node;
203 } 146 }
204 } 147 }
205 } else { 148 } else {
206 struct sway_container *desired_con = 149 struct sway_container *desired_con = siblings->items[desired];
207 parent->children->items[desired]; 150 struct sway_container *c = seat_get_focus_inactive_view(
208 wlr_log(WLR_DEBUG, 151 seat, &desired_con->node);
209 "cont %d-%p dir %i sibling %d: %p", idx, 152 return &c->node;
210 container, dir, desired, desired_con);
211 return seat_get_focus_inactive_view(seat, desired_con);
212 } 153 }
213 } 154 }
214 155
215 if (!can_move) { 156 current = current->parent;
216 container = parent;
217 parent = parent->parent;
218 if (!parent) {
219 // wrapping is the last chance
220 if (!wrap_candidate) {
221 return NULL;
222 }
223 return seat_get_focus_inactive_view(seat, wrap_candidate);
224 }
225 }
226 } 157 }
227}
228 158
229static struct cmd_results *focus_mode(struct sway_container *con, 159 // Check a different output
230 struct sway_seat *seat, bool floating) { 160 struct sway_output *output = container->workspace->output;
231 struct sway_container *ws = con->type == C_WORKSPACE ? 161 struct sway_output *new_output = output_get_in_direction(output, dir);
232 con : container_parent(con, C_WORKSPACE); 162 if (new_output) {
233 163 return get_node_in_output_direction(new_output, dir);
234 // If the container is in a floating split container,
235 // operate on the split container instead of the child.
236 if (container_is_floating_or_child(con)) {
237 while (con->parent->type != C_WORKSPACE) {
238 con = con->parent;
239 }
240 } 164 }
165 return NULL;
166}
241 167
168static struct cmd_results *focus_mode(struct sway_workspace *ws,
169 struct sway_seat *seat, bool floating) {
242 struct sway_container *new_focus = NULL; 170 struct sway_container *new_focus = NULL;
243 if (floating) { 171 if (floating) {
244 new_focus = seat_get_focus_inactive_floating(seat, ws); 172 new_focus = seat_get_focus_inactive_floating(seat, ws);
@@ -246,7 +174,7 @@ static struct cmd_results *focus_mode(struct sway_container *con,
246 new_focus = seat_get_focus_inactive_tiling(seat, ws); 174 new_focus = seat_get_focus_inactive_tiling(seat, ws);
247 } 175 }
248 if (new_focus) { 176 if (new_focus) {
249 seat_set_focus(seat, new_focus); 177 seat_set_focus(seat, &new_focus->node);
250 } else { 178 } else {
251 return cmd_results_new(CMD_FAILURE, "focus", 179 return cmd_results_new(CMD_FAILURE, "focus",
252 "Failed to find a %s container in workspace", 180 "Failed to find a %s container in workspace",
@@ -255,14 +183,14 @@ static struct cmd_results *focus_mode(struct sway_container *con,
255 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 183 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
256} 184}
257 185
258static struct cmd_results *focus_output(struct sway_container *con, 186static struct cmd_results *focus_output(struct sway_seat *seat,
259 struct sway_seat *seat, int argc, char **argv) { 187 int argc, char **argv) {
260 if (!argc) { 188 if (!argc) {
261 return cmd_results_new(CMD_INVALID, "focus", 189 return cmd_results_new(CMD_INVALID, "focus",
262 "Expected 'focus output <direction|name>'"); 190 "Expected 'focus output <direction|name>'");
263 } 191 }
264 char *identifier = join_args(argv, argc); 192 char *identifier = join_args(argv, argc);
265 struct sway_container *output = output_by_name(identifier); 193 struct sway_output *output = output_by_name(identifier);
266 194
267 if (!output) { 195 if (!output) {
268 enum movement_direction direction; 196 enum movement_direction direction;
@@ -272,14 +200,13 @@ static struct cmd_results *focus_output(struct sway_container *con,
272 return cmd_results_new(CMD_INVALID, "focus", 200 return cmd_results_new(CMD_INVALID, "focus",
273 "There is no output with that name"); 201 "There is no output with that name");
274 } 202 }
275 struct sway_container *focus = seat_get_focus(seat); 203 struct sway_workspace *ws = seat_get_focused_workspace(seat);
276 focus = container_parent(focus, C_OUTPUT); 204 output = output_get_in_direction(ws->output, direction);
277 output = container_get_in_direction(focus, seat, direction);
278 } 205 }
279 206
280 free(identifier); 207 free(identifier);
281 if (output) { 208 if (output) {
282 seat_set_focus(seat, seat_get_focus_inactive(seat, output)); 209 seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node));
283 } 210 }
284 211
285 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 212 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -289,29 +216,32 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
289 if (config->reading || !config->active) { 216 if (config->reading || !config->active) {
290 return cmd_results_new(CMD_DEFER, NULL, NULL); 217 return cmd_results_new(CMD_DEFER, NULL, NULL);
291 } 218 }
292 struct sway_container *con = config->handler_context.current_container; 219 struct sway_node *node = config->handler_context.node;
220 struct sway_container *container = config->handler_context.container;
221 struct sway_workspace *workspace = config->handler_context.workspace;
293 struct sway_seat *seat = config->handler_context.seat; 222 struct sway_seat *seat = config->handler_context.seat;
294 if (con->type < C_WORKSPACE) { 223 if (node->type < N_WORKSPACE) {
295 return cmd_results_new(CMD_FAILURE, "focus", 224 return cmd_results_new(CMD_FAILURE, "focus",
296 "Command 'focus' cannot be used above the workspace level"); 225 "Command 'focus' cannot be used above the workspace level");
297 } 226 }
298 227
299 if (argc == 0) { 228 if (argc == 0) {
300 seat_set_focus(seat, con); 229 seat_set_focus(seat, node);
301 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 230 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
302 } 231 }
303 232
304 if (strcmp(argv[0], "floating") == 0) { 233 if (strcmp(argv[0], "floating") == 0) {
305 return focus_mode(con, seat, true); 234 return focus_mode(workspace, seat, true);
306 } else if (strcmp(argv[0], "tiling") == 0) { 235 } else if (strcmp(argv[0], "tiling") == 0) {
307 return focus_mode(con, seat, false); 236 return focus_mode(workspace, seat, false);
308 } else if (strcmp(argv[0], "mode_toggle") == 0) { 237 } else if (strcmp(argv[0], "mode_toggle") == 0) {
309 return focus_mode(con, seat, !container_is_floating_or_child(con)); 238 bool floating = container && container_is_floating_or_child(container);
239 return focus_mode(workspace, seat, !floating);
310 } 240 }
311 241
312 if (strcmp(argv[0], "output") == 0) { 242 if (strcmp(argv[0], "output") == 0) {
313 argc--; argv++; 243 argc--; argv++;
314 return focus_output(con, seat, argc, argv); 244 return focus_output(seat, argc, argv);
315 } 245 }
316 246
317 enum movement_direction direction = 0; 247 enum movement_direction direction = 0;
@@ -321,8 +251,18 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
321 "or 'focus output <direction|name>'"); 251 "or 'focus output <direction|name>'");
322 } 252 }
323 253
324 struct sway_container *next_focus = container_get_in_direction( 254 if (node->type == N_WORKSPACE) {
325 con, seat, direction); 255 // A workspace is focused, so just jump to the next output
256 struct sway_output *new_output =
257 output_get_in_direction(workspace->output, direction);
258 struct sway_node *node =
259 get_node_in_output_direction(new_output, direction);
260 seat_set_focus(seat, node);
261 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
262 }
263
264 struct sway_node *next_focus =
265 node_get_in_direction(container, seat, direction);
326 if (next_focus) { 266 if (next_focus) {
327 seat_set_focus(seat, next_focus); 267 seat_set_focus(seat, next_focus);
328 } 268 }
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index ac65dffb..3bbe00c5 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -12,18 +12,18 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
12 if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) { 12 if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) {
13 return error; 13 return error;
14 } 14 }
15 struct sway_container *container = 15 struct sway_node *node = config->handler_context.node;
16 config->handler_context.current_container; 16 struct sway_container *container = config->handler_context.container;
17 if (container->type == C_WORKSPACE && container->children->length == 0) { 17 struct sway_workspace *workspace = config->handler_context.workspace;
18 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
18 return cmd_results_new(CMD_INVALID, "fullscreen", 19 return cmd_results_new(CMD_INVALID, "fullscreen",
19 "Can't fullscreen an empty workspace"); 20 "Can't fullscreen an empty workspace");
20 } 21 }
21 if (container->type == C_WORKSPACE) { 22 if (node->type == N_WORKSPACE) {
22 // Wrap the workspace's children in a container so we can fullscreen it 23 // Wrap the workspace's children in a container so we can fullscreen it
23 struct sway_container *workspace = container; 24 container = workspace_wrap_children(workspace);
24 container = workspace_wrap_children(container);
25 workspace->layout = L_HORIZ; 25 workspace->layout = L_HORIZ;
26 seat_set_focus(config->handler_context.seat, container); 26 seat_set_focus(config->handler_context.seat, &container->node);
27 } 27 }
28 bool enable = !container->is_fullscreen; 28 bool enable = !container->is_fullscreen;
29 29
@@ -32,9 +32,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
32 } 32 }
33 33
34 container_set_fullscreen(container, enable); 34 container_set_fullscreen(container, enable);
35 35 arrange_workspace(workspace);
36 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
37 arrange_windows(workspace->parent);
38 36
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40} 38}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 3906eb70..d676e475 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -2,6 +2,7 @@
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
5#include "sway/tree/workspace.h"
5#include "log.h" 6#include "log.h"
6#include "stringop.h" 7#include "stringop.h"
7#include <math.h> 8#include <math.h>
@@ -43,7 +44,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
43 return cmd_results_new(CMD_INVALID, "gaps", 44 return cmd_results_new(CMD_INVALID, "gaps",
44 "gaps edge_gaps on|off|toggle"); 45 "gaps edge_gaps on|off|toggle");
45 } 46 }
46 arrange_windows(&root_container); 47 arrange_root();
47 } else { 48 } else {
48 int amount_idx = 0; // the current index in argv 49 int amount_idx = 0; // the current index in argv
49 enum gaps_op op = GAPS_OP_SET; 50 enum gaps_op op = GAPS_OP_SET;
@@ -124,7 +125,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
124 if (amount_idx == 0) { // gaps <amount> 125 if (amount_idx == 0) { // gaps <amount>
125 config->gaps_inner = val; 126 config->gaps_inner = val;
126 config->gaps_outer = val; 127 config->gaps_outer = val;
127 arrange_windows(&root_container); 128 arrange_root();
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 129 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 130 }
130 // Other variants. The middle-length variant (gaps inner|outer <amount>) 131 // Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -155,21 +156,27 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
155 } else { 156 } else {
156 config->gaps_outer = total; 157 config->gaps_outer = total;
157 } 158 }
158 arrange_windows(&root_container); 159 arrange_root();
159 } else { 160 } else {
160 struct sway_container *c = 161 if (scope == GAPS_SCOPE_WORKSPACE) {
161 config->handler_context.current_container; 162 struct sway_workspace *ws = config->handler_context.workspace;
162 if (scope == GAPS_SCOPE_WORKSPACE && c->type != C_WORKSPACE) { 163 ws->has_gaps = true;
163 c = container_parent(c, C_WORKSPACE); 164 if (inner) {
164 } 165 ws->gaps_inner = total;
165 c->has_gaps = true; 166 } else {
166 if (inner) { 167 ws->gaps_outer = total;
167 c->gaps_inner = total; 168 }
169 arrange_workspace(ws);
168 } else { 170 } else {
169 c->gaps_outer = total; 171 struct sway_container *c = config->handler_context.container;
172 c->has_gaps = true;
173 if (inner) {
174 c->gaps_inner = total;
175 } else {
176 c->gaps_outer = total;
177 }
178 arrange_workspace(c->workspace);
170 } 179 }
171
172 arrange_windows(c->parent ? c->parent : &root_container);
173 } 180 }
174 } 181 }
175 182
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index e494f6aa..0a5c7f28 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -5,8 +5,8 @@
5#include "sway/tree/view.h" 5#include "sway/tree/view.h"
6 6
7static void _configure_view(struct sway_container *con, void *data) { 7static void _configure_view(struct sway_container *con, void *data) {
8 if (con->type == C_VIEW) { 8 if (con->view) {
9 view_autoconfigure(con->sway_view); 9 view_autoconfigure(con->view);
10 } 10 }
11} 11}
12 12
diff --git a/sway/commands/kill.c b/sway/commands/kill.c
index f3fa52f1..85ca0f33 100644
--- a/sway/commands/kill.c
+++ b/sway/commands/kill.c
@@ -2,15 +2,27 @@
2#include "log.h" 2#include "log.h"
3#include "sway/input/input-manager.h" 3#include "sway/input/input-manager.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/tree/view.h"
6#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h"
7#include "sway/tree/workspace.h"
7#include "sway/commands.h" 8#include "sway/commands.h"
8 9
10static void close_container_iterator(struct sway_container *con, void *data) {
11 if (con->view) {
12 view_close(con->view);
13 }
14}
15
9struct cmd_results *cmd_kill(int argc, char **argv) { 16struct cmd_results *cmd_kill(int argc, char **argv) {
10 struct sway_container *con = 17 struct sway_container *con = config->handler_context.container;
11 config->handler_context.current_container; 18 struct sway_workspace *ws = config->handler_context.workspace;
12 19
13 container_close(con); 20 if (con) {
21 close_container_iterator(con, NULL);
22 container_for_each_child(con, close_container_iterator, NULL);
23 } else {
24 workspace_for_each_container(ws, close_container_iterator, NULL);
25 }
14 26
15 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
16} 28}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index a06832de..8fa1ce98 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -4,21 +4,20 @@
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/tree/arrange.h" 5#include "sway/tree/arrange.h"
6#include "sway/tree/container.h" 6#include "sway/tree/container.h"
7#include "sway/tree/workspace.h"
7#include "log.h" 8#include "log.h"
8 9
9static bool parse_layout_string(char *s, enum sway_container_layout *ptr) { 10static enum sway_container_layout parse_layout_string(char *s) {
10 if (strcasecmp(s, "splith") == 0) { 11 if (strcasecmp(s, "splith") == 0) {
11 *ptr = L_HORIZ; 12 return L_HORIZ;
12 } else if (strcasecmp(s, "splitv") == 0) { 13 } else if (strcasecmp(s, "splitv") == 0) {
13 *ptr = L_VERT; 14 return L_VERT;
14 } else if (strcasecmp(s, "tabbed") == 0) { 15 } else if (strcasecmp(s, "tabbed") == 0) {
15 *ptr = L_TABBED; 16 return L_TABBED;
16 } else if (strcasecmp(s, "stacking") == 0) { 17 } else if (strcasecmp(s, "stacking") == 0) {
17 *ptr = L_STACKED; 18 return L_STACKED;
18 } else {
19 return false;
20 } 19 }
21 return true; 20 return L_NONE;
22} 21}
23 22
24static const char* expected_syntax = 23static const char* expected_syntax =
@@ -26,84 +25,129 @@ static const char* expected_syntax =
26 "'layout toggle [split|all]' or " 25 "'layout toggle [split|all]' or "
27 "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; 26 "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'";
28 27
28static enum sway_container_layout get_layout_toggle(int argc, char **argv,
29 enum sway_container_layout layout,
30 enum sway_container_layout prev_split_layout) {
31 // "layout toggle"
32 if (argc == 0) {
33 return layout == L_HORIZ ? L_VERT : L_HORIZ;
34 }
35
36 if (argc == 2) {
37 // "layout toggle split" (same as "layout toggle")
38 if (strcasecmp(argv[1], "split") == 0) {
39 return layout == L_HORIZ ? L_VERT : L_HORIZ;
40 }
41 // "layout toggle all"
42 if (strcasecmp(argv[1], "all") == 0) {
43 return layout == L_HORIZ ? L_VERT :
44 layout == L_VERT ? L_STACKED :
45 layout == L_STACKED ? L_TABBED : L_HORIZ;
46 }
47 return L_NONE;
48 }
49
50 enum sway_container_layout parsed;
51 int curr = 1;
52 for (; curr < argc; curr++) {
53 parsed = parse_layout_string(argv[curr]);
54 if (parsed == layout || (strcmp(argv[curr], "split") == 0 &&
55 (layout == L_VERT || layout == L_HORIZ))) {
56 break;
57 }
58 }
59 for (int i = curr + 1; i != curr; ++i) {
60 // cycle round to find next valid layout
61 if (i >= argc) {
62 i = 1;
63 }
64 parsed = parse_layout_string(argv[i]);
65 if (parsed != L_NONE) {
66 return parsed;
67 }
68 if (strcmp(argv[i], "split") == 0) {
69 return layout == L_HORIZ ? L_VERT :
70 layout == L_VERT ? L_HORIZ : prev_split_layout;
71 }
72 // invalid layout strings are silently ignored
73 }
74 return L_NONE;
75}
76
77static enum sway_container_layout get_layout(int argc, char **argv,
78 enum sway_container_layout layout,
79 enum sway_container_layout prev_split_layout) {
80 // Check if assigned directly
81 enum sway_container_layout parsed = parse_layout_string(argv[0]);
82 if (parsed != L_NONE) {
83 return parsed;
84 }
85
86 if (strcasecmp(argv[0], "default") == 0) {
87 return prev_split_layout;
88 }
89
90 if (strcasecmp(argv[0], "toggle") == 0) {
91 argc--; argv++;
92 return get_layout_toggle(argc, argv, layout, prev_split_layout);
93 }
94
95 return L_NONE;
96}
97
29struct cmd_results *cmd_layout(int argc, char **argv) { 98struct cmd_results *cmd_layout(int argc, char **argv) {
30 struct cmd_results *error = NULL; 99 struct cmd_results *error = NULL;
31 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { 100 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
32 return error; 101 return error;
33 } 102 }
34 struct sway_container *parent = config->handler_context.current_container; 103 struct sway_container *container = config->handler_context.container;
104 struct sway_workspace *workspace = config->handler_context.workspace;
35 105
36 if (container_is_floating(parent)) { 106 if (container && container_is_floating(container)) {
37 return cmd_results_new(CMD_FAILURE, "layout", 107 return cmd_results_new(CMD_FAILURE, "layout",
38 "Unable to change layout of floating windows"); 108 "Unable to change layout of floating windows");
39 } 109 }
40 110
41 while (parent->type == C_VIEW) { 111 // Typically we change the layout of the current container, but if the
42 parent = parent->parent; 112 // current container is a view (it usually is) then we'll change the layout
113 // of the parent instead, as it doesn't make sense for views to have layout.
114 if (container && container->view) {
115 container = container->parent;
43 } 116 }
44 117
45 enum sway_container_layout prev = parent->layout; 118 // We could be working with a container OR a workspace. These are different
46 bool assigned_directly = parse_layout_string(argv[0], &parent->layout); 119 // structures, so we set up pointers to they layouts so we can refer them in
47 if (!assigned_directly) { 120 // an abstract way.
48 if (strcasecmp(argv[0], "default") == 0) { 121 enum sway_container_layout new_layout = L_NONE;
49 parent->layout = parent->prev_split_layout; 122 enum sway_container_layout old_layout = L_NONE;
50 } else if (strcasecmp(argv[0], "toggle") == 0) { 123 if (container) {
51 if (argc == 1) { 124 old_layout = container->layout;
52 parent->layout = 125 new_layout = get_layout(argc, argv,
53 parent->layout == L_STACKED ? L_TABBED : 126 container->layout, container->prev_split_layout);
54 parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; 127 } else {
55 } else if (argc == 2) { 128 old_layout = workspace->layout;
56 if (strcasecmp(argv[1], "all") == 0) { 129 new_layout = get_layout(argc, argv,
57 parent->layout = 130 workspace->layout, workspace->prev_split_layout);
58 parent->layout == L_HORIZ ? L_VERT :
59 parent->layout == L_VERT ? L_STACKED :
60 parent->layout == L_STACKED ? L_TABBED : L_HORIZ;
61 } else if (strcasecmp(argv[1], "split") == 0) {
62 parent->layout =
63 parent->layout == L_HORIZ ? L_VERT :
64 parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
65 } else {
66 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
67 }
68 } else {
69 enum sway_container_layout parsed_layout;
70 int curr = 1;
71 for (; curr < argc; curr++) {
72 bool valid = parse_layout_string(argv[curr], &parsed_layout);
73 if ((valid && parsed_layout == parent->layout) ||
74 (strcmp(argv[curr], "split") == 0 &&
75 (parent->layout == L_VERT || parent->layout == L_HORIZ))) {
76 break;
77 }
78 }
79 for (int i = curr + 1; i != curr; ++i) {
80 // cycle round to find next valid layout
81 if (i >= argc) {
82 i = 1;
83 }
84 if (parse_layout_string(argv[i], &parent->layout)) {
85 break;
86 } else if (strcmp(argv[i], "split") == 0) {
87 parent->layout =
88 parent->layout == L_HORIZ ? L_VERT :
89 parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
90 break;
91 } // invalid layout strings are silently ignored
92 }
93 }
94 } else {
95 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
96 }
97 } 131 }
98 if (parent->layout == L_NONE) { 132 if (new_layout == L_NONE) {
99 parent->layout = container_get_default_layout(parent); 133 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
100 } 134 }
101 if (prev != parent->layout) { 135 if (new_layout != old_layout) {
102 if (prev != L_TABBED && prev != L_STACKED) { 136 if (container) {
103 parent->prev_split_layout = prev; 137 if (old_layout != L_TABBED && old_layout != L_STACKED) {
138 container->prev_split_layout = old_layout;
139 }
140 container->layout = new_layout;
141 container_update_representation(container);
142 arrange_container(container);
143 } else {
144 if (old_layout != L_TABBED && old_layout != L_STACKED) {
145 workspace->prev_split_layout = old_layout;
146 }
147 workspace->layout = new_layout;
148 workspace_update_representation(workspace);
149 arrange_workspace(workspace);
104 } 150 }
105 container_notify_subtree_changed(parent);
106 arrange_windows(parent->parent);
107 } 151 }
108 152
109 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 153 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index 9ea8c301..fb95a7d0 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -18,13 +18,12 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
18 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) { 18 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
19 return error; 19 return error;
20 } 20 }
21 struct sway_container *container = 21 struct sway_container *container = config->handler_context.container;
22 config->handler_context.current_container; 22 if (!container->view) {
23 if (container->type != C_VIEW) {
24 return cmd_results_new(CMD_INVALID, "mark", 23 return cmd_results_new(CMD_INVALID, "mark",
25 "Only views can have marks"); 24 "Only views can have marks");
26 } 25 }
27 struct sway_view *view = container->sway_view; 26 struct sway_view *view = container->view;
28 27
29 bool add = false, toggle = false; 28 bool add = false, toggle = false;
30 while (argc > 0 && strncmp(*argv, "--", 2) == 0) { 29 while (argc > 0 && strncmp(*argv, "--", 2) == 0) {
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 4426f24e..1b2e830c 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -40,7 +40,7 @@ enum wlr_direction opposite_direction(enum wlr_direction d) {
40 } 40 }
41} 41}
42 42
43static struct sway_container *output_in_direction(const char *direction_string, 43static struct sway_output *output_in_direction(const char *direction_string,
44 struct wlr_output *reference, int ref_lx, int ref_ly) { 44 struct wlr_output *reference, int ref_lx, int ref_ly) {
45 struct { 45 struct {
46 char *name; 46 char *name;
@@ -63,447 +63,367 @@ static struct sway_container *output_in_direction(const char *direction_string,
63 63
64 if (direction) { 64 if (direction) {
65 struct wlr_output *target = wlr_output_layout_adjacent_output( 65 struct wlr_output *target = wlr_output_layout_adjacent_output(
66 root_container.sway_root->output_layout, 66 root->output_layout, direction, reference, ref_lx, ref_ly);
67 direction, reference, ref_lx, ref_ly);
68 67
69 if (!target) { 68 if (!target) {
70 target = wlr_output_layout_farthest_output( 69 target = wlr_output_layout_farthest_output(
71 root_container.sway_root->output_layout, 70 root->output_layout, opposite_direction(direction),
72 opposite_direction(direction), reference, ref_lx, ref_ly); 71 reference, ref_lx, ref_ly);
73 } 72 }
74 73
75 if (target) { 74 if (target) {
76 struct sway_output *sway_output = target->data; 75 return target->data;
77 return sway_output->swayc;
78 } 76 }
79 } 77 }
80 78
81 return output_by_name(direction_string); 79 return output_by_name(direction_string);
82} 80}
83 81
84static void container_move_to(struct sway_container *container, 82static bool is_parallel(enum sway_container_layout layout,
85 struct sway_container *destination) { 83 enum movement_direction dir) {
86 if (!sway_assert(container->type == C_CONTAINER || 84 switch (layout) {
87 container->type == C_VIEW, "Expected a container or view")) { 85 case L_TABBED:
88 return; 86 case L_HORIZ:
87 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
88 case L_STACKED:
89 case L_VERT:
90 return dir == MOVE_UP || dir == MOVE_DOWN;
91 default:
92 return false;
89 } 93 }
90 if (container == destination 94}
91 || container_has_ancestor(container, destination)) { 95
96/**
97 * Ensures all seats focus the fullscreen container if needed.
98 */
99static void workspace_focus_fullscreen(struct sway_workspace *workspace) {
100 if (!workspace->fullscreen) {
92 return; 101 return;
93 } 102 }
94 struct sway_container *old_parent = NULL; 103 struct sway_seat *seat;
95 struct sway_container *new_parent = NULL; 104 struct sway_workspace *focus_ws;
96 if (container_is_floating(container)) { 105 wl_list_for_each(seat, &input_manager->seats, link) {
97 // Resolve destination into a workspace 106 focus_ws = seat_get_focused_workspace(seat);
98 struct sway_container *new_ws = NULL; 107 if (focus_ws == workspace) {
99 if (destination->type == C_OUTPUT) { 108 struct sway_node *new_focus =
100 new_ws = output_get_active_workspace(destination->sway_output); 109 seat_get_focus_inactive(seat, &workspace->fullscreen->node);
101 } else if (destination->type == C_WORKSPACE) { 110 seat_set_focus(seat, new_focus);
102 new_ws = destination;
103 } else {
104 new_ws = container_parent(destination, C_WORKSPACE);
105 } 111 }
106 if (!new_ws) { 112 }
107 // This can happen if the user has run "move container to mark foo", 113}
108 // where mark foo is on a hidden scratchpad container. 114
109 return; 115static void container_move_to_container_from_direction(
116 struct sway_container *container, struct sway_container *destination,
117 enum movement_direction move_dir) {
118 if (destination->view) {
119 if (destination->parent == container->parent) {
120 wlr_log(WLR_DEBUG, "Swapping siblings");
121 list_t *siblings = container_get_siblings(container);
122 int container_index = list_find(siblings, container);
123 int destination_index = list_find(siblings, destination);
124 list_swap(siblings, container_index, destination_index);
125 } else {
126 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
127 int offset = move_dir == MOVE_LEFT || move_dir == MOVE_UP;
128 int index = container_sibling_index(destination) + offset;
129 if (destination->parent) {
130 container_insert_child(destination->parent, container, index);
131 } else {
132 workspace_insert_tiling(destination->workspace,
133 container, index);
134 }
135 container->width = container->height = 0;
110 } 136 }
111 struct sway_container *old_output = 137 return;
112 container_parent(container, C_OUTPUT); 138 }
113 old_parent = container_remove_child(container); 139
114 workspace_add_floating(new_ws, container); 140 if (is_parallel(destination->layout, move_dir)) {
115 container_handle_fullscreen_reparent(container, old_parent); 141 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
142 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
143 0 : destination->children->length;
144 container_insert_child(destination, container, index);
145 container->width = container->height = 0;
146 return;
147 }
148
149 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
150 struct sway_node *focus_inactive = seat_get_active_child(
151 config->handler_context.seat, &destination->node);
152 if (!focus_inactive || focus_inactive == &destination->node) {
153 // The container has no children
154 container_add_child(destination, container);
155 return;
156 }
157
158 // Try again but with the child
159 container_move_to_container_from_direction(container,
160 focus_inactive->sway_container, move_dir);
161}
162
163static void container_move_to_workspace_from_direction(
164 struct sway_container *container, struct sway_workspace *workspace,
165 enum movement_direction move_dir) {
166 if (is_parallel(workspace->layout, move_dir)) {
167 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
168 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
169 0 : workspace->tiling->length;
170 workspace_insert_tiling(workspace, container, index);
171 return;
172 }
173
174 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
175 struct sway_container *focus_inactive = seat_get_focus_inactive_tiling(
176 config->handler_context.seat, workspace);
177 if (!focus_inactive) {
178 // The workspace has no tiling children
179 workspace_add_tiling(workspace, container);
180 return;
181 }
182 while (focus_inactive->parent) {
183 focus_inactive = focus_inactive->parent;
184 }
185 container_move_to_container_from_direction(container, focus_inactive,
186 move_dir);
187}
188
189static void container_move_to_workspace(struct sway_container *container,
190 struct sway_workspace *workspace) {
191 if (container->workspace == workspace) {
192 return;
193 }
194 struct sway_workspace *old_workspace = container->workspace;
195 if (container_is_floating(container)) {
196 struct sway_output *old_output = container->workspace->output;
197 container_detach(container);
198 workspace_add_floating(workspace, container);
199 container_handle_fullscreen_reparent(container);
116 // If changing output, center it within the workspace 200 // If changing output, center it within the workspace
117 if (old_output != new_ws->parent && !container->is_fullscreen) { 201 if (old_output != workspace->output && !container->is_fullscreen) {
118 container_floating_move_to_center(container); 202 container_floating_move_to_center(container);
119 } 203 }
120 } else { 204 } else {
121 old_parent = container_remove_child(container); 205 container_detach(container);
122 container->width = container->height = 0; 206 container->width = container->height = 0;
123 container->saved_width = container->saved_height = 0; 207 container->saved_width = container->saved_height = 0;
124 208 workspace_add_tiling(workspace, container);
125 if (destination->type == C_VIEW) { 209 container_update_representation(container);
126 new_parent = container_add_sibling(destination, container);
127 } else {
128 new_parent = destination;
129 container_add_child(destination, container);
130 }
131 } 210 }
132 211 if (container->view) {
133 if (container->type == C_VIEW) {
134 ipc_event_window(container, "move"); 212 ipc_event_window(container, "move");
135 } 213 }
136 container_notify_subtree_changed(old_parent); 214 workspace_detect_urgent(old_workspace);
137 container_notify_subtree_changed(new_parent); 215 workspace_detect_urgent(workspace);
138 216 workspace_focus_fullscreen(workspace);
139 // If view was moved to a fullscreen workspace, refocus the fullscreen view 217}
140 struct sway_container *new_workspace = container; 218
141 if (new_workspace->type != C_WORKSPACE) { 219static void container_move_to_container(struct sway_container *container,
142 new_workspace = container_parent(new_workspace, C_WORKSPACE); 220 struct sway_container *destination) {
143 } 221 if (container == destination
144 if (new_workspace->sway_workspace->fullscreen) { 222 || container_has_ancestor(container, destination)
145 struct sway_seat *seat; 223 || container_has_ancestor(destination, container)) {
146 struct sway_container *focus, *focus_ws; 224 return;
147 wl_list_for_each(seat, &input_manager->seats, link) {
148 focus = seat_get_focus(seat);
149 focus_ws = focus;
150 if (focus_ws->type != C_WORKSPACE) {
151 focus_ws = container_parent(focus_ws, C_WORKSPACE);
152 }
153 if (focus_ws == new_workspace) {
154 struct sway_container *new_focus = seat_get_focus_inactive(seat,
155 new_workspace->sway_workspace->fullscreen);
156 seat_set_focus(seat, new_focus);
157 }
158 }
159 } 225 }
160 // Update workspace urgent state 226 if (container_is_floating(container)) {
161 struct sway_container *old_workspace = old_parent; 227 return;
162 if (old_workspace->type != C_WORKSPACE) {
163 old_workspace = container_parent(old_workspace, C_WORKSPACE);
164 }
165 if (new_workspace != old_workspace) {
166 workspace_detect_urgent(new_workspace);
167 if (old_workspace) {
168 workspace_detect_urgent(old_workspace);
169 }
170 } 228 }
171} 229 struct sway_workspace *old_workspace = container->workspace;
172 230
173static bool is_parallel(enum sway_container_layout layout, 231 container_detach(container);
174 enum movement_direction dir) { 232 container->width = container->height = 0;
175 switch (layout) { 233 container->saved_width = container->saved_height = 0;
176 case L_TABBED: 234
177 case L_HORIZ: 235 if (destination->view) {
178 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 236 container_add_sibling(destination, container, 1);
179 case L_STACKED: 237 } else {
180 case L_VERT: 238 container_add_child(destination, container);
181 return dir == MOVE_UP || dir == MOVE_DOWN;
182 default:
183 return false;
184 } 239 }
185}
186 240
187static enum movement_direction invert_movement(enum movement_direction dir) { 241 if (container->view) {
188 switch (dir) { 242 ipc_event_window(container, "move");
189 case MOVE_LEFT:
190 return MOVE_RIGHT;
191 case MOVE_RIGHT:
192 return MOVE_LEFT;
193 case MOVE_UP:
194 return MOVE_DOWN;
195 case MOVE_DOWN:
196 return MOVE_UP;
197 default:
198 sway_assert(0, "This function expects left|right|up|down");
199 return MOVE_LEFT;
200 } 243 }
201}
202 244
203static int move_offs(enum movement_direction move_dir) { 245 workspace_focus_fullscreen(destination->workspace);
204 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
205}
206 246
207/* Gets the index of the most extreme member based on the movement offset */ 247 // Update workspace urgent state
208static int container_limit(struct sway_container *container, 248 workspace_detect_urgent(destination->workspace);
209 enum movement_direction move_dir) { 249 if (old_workspace != destination->workspace) {
210 return move_offs(move_dir) < 0 ? 0 : container->children->length; 250 workspace_detect_urgent(old_workspace);
251 }
211} 252}
212 253
213/* Takes one child, sets it aside, wraps the rest of the children in a new 254/* Takes one child, sets it aside, wraps the rest of the children in a new
214 * container, switches the layout of the workspace, and drops the child back in. 255 * container, switches the layout of the workspace, and drops the child back in.
215 * In other words, rejigger it. */ 256 * In other words, rejigger it. */
216static void workspace_rejigger(struct sway_container *ws, 257static void workspace_rejigger(struct sway_workspace *ws,
217 struct sway_container *child, enum movement_direction move_dir) { 258 struct sway_container *child, enum movement_direction move_dir) {
218 struct sway_container *original_parent = child->parent; 259 if (!sway_assert(child->parent == NULL, "Expected a root child")) {
219 struct sway_container *new_parent = 260 return;
220 container_split(ws, ws->layout);
221
222 container_remove_child(child);
223 for (int i = 0; i < ws->children->length; ++i) {
224 struct sway_container *_child = ws->children->items[i];
225 container_move_to(new_parent, _child);
226 } 261 }
262 container_detach(child);
263 workspace_wrap_children(ws);
227 264
228 int index = move_offs(move_dir); 265 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1;
229 container_insert_child(ws, child, index < 0 ? 0 : 1); 266 workspace_insert_tiling(ws, child, index);
230 ws->layout = 267 ws->layout =
231 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 268 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
232 269 workspace_update_representation(ws);
233 container_flatten(ws);
234 container_reap_empty(original_parent);
235 container_create_notify(new_parent);
236} 270}
237 271
238static void move_out_of_tabs_stacks(struct sway_container *container, 272static void move_out_of_tabs_stacks(struct sway_container *container,
239 struct sway_container *current, enum movement_direction move_dir, 273 struct sway_container *current, enum movement_direction move_dir,
240 int offs) { 274 int offs) {
241 if (container->parent == current->parent 275 enum sway_container_layout layout = move_dir ==
242 && current->parent->children->length == 1) { 276 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
243 wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); 277 list_t *siblings = container_get_siblings(container);
244 current->parent->layout = move_dir == 278 if (container == current && siblings->length == 1) {
245 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 279 wlr_log(WLR_DEBUG, "Changing layout of parent");
280 if (container->parent) {
281 container->parent->layout = layout;
282 container_update_representation(container);
283 } else {
284 container->workspace->layout = layout;
285 workspace_update_representation(container->workspace);
286 }
246 return; 287 return;
247 } 288 }
248 289
249 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); 290 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
250 bool is_workspace = current->parent->type == C_WORKSPACE; 291 if (container->parent) {
251 struct sway_container *new_parent = container_split(current->parent, 292 struct sway_container *new_parent =
252 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); 293 container_split(current->parent, layout);
253 if (is_workspace) {
254 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1);
255 } else {
256 container_insert_child(new_parent, container, offs < 0 ? 0 : 1); 294 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
257 container_reap_empty(new_parent->parent); 295 container_reap_empty(new_parent);
258 container_flatten(new_parent->parent); 296 container_flatten(new_parent);
297 } else {
298 // Changing a workspace
299 struct sway_workspace *workspace = container->workspace;
300 workspace_split(workspace, layout);
301 workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1);
259 } 302 }
260 container_create_notify(new_parent);
261 container_notify_subtree_changed(new_parent);
262} 303}
263 304
264static void container_move(struct sway_container *container, 305// Returns true if moved
265 enum movement_direction move_dir, int move_amt) { 306static bool container_move_in_direction(struct sway_container *container,
266 if (!sway_assert( 307 enum movement_direction move_dir) {
267 container->type != C_CONTAINER || container->type != C_VIEW,
268 "Can only move containers and views")) {
269 return;
270 }
271 int offs = move_offs(move_dir);
272
273 struct sway_container *sibling = NULL;
274 struct sway_container *current = container;
275 struct sway_container *parent = current->parent;
276 struct sway_container *top = &root_container;
277
278 // If moving a fullscreen view, only consider outputs 308 // If moving a fullscreen view, only consider outputs
279 if (container->is_fullscreen) { 309 if (container->is_fullscreen) {
280 current = container_parent(container, C_OUTPUT); 310 struct sway_output *new_output =
281 } else if (container_is_fullscreen_or_child(container) || 311 output_get_in_direction(container->workspace->output, move_dir);
282 container_is_floating_or_child(container)) { 312 if (!new_output) {
283 // If we've fullscreened a split container, only allow the child to move 313 return false;
284 // around within the fullscreen parent. 314 }
285 // Same with floating a split container. 315 struct sway_workspace *ws = output_get_active_workspace(new_output);
286 struct sway_container *ws = container_parent(container, C_WORKSPACE); 316 container_move_to_workspace(container, ws);
287 top = ws->sway_workspace->fullscreen; 317 return true;
288 }
289
290 struct sway_container *new_parent = container_flatten(parent);
291 if (new_parent != parent) {
292 // Special case: we were the last one in this container, so leave
293 return;
294 } 318 }
295 319
296 while (!sibling) { 320 // If container is in a split container by itself, move out of the split
297 if (current == top) { 321 if (container->parent) {
298 return; 322 struct sway_container *new_parent =
323 container_flatten(container->parent);
324 if (new_parent != container->parent) {
325 return true;
299 } 326 }
327 }
300 328
301 parent = current->parent; 329 // Look for a suitable *container* sibling or parent.
302 wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, 330 // The below loop stops once we hit the workspace because current->parent
303 container_type_to_str(current->type), current->name); 331 // is NULL for the topmost containers in a workspace.
304 332 struct sway_container *current = container;
305 int index = container_sibling_index(current); 333 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
306 334
307 switch (current->type) { 335 while (current) {
308 case C_OUTPUT: { 336 struct sway_container *parent = current->parent;
309 enum wlr_direction wlr_dir = 0; 337 list_t *siblings = container_get_siblings(current);
310 if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), 338 enum sway_container_layout layout = container_parent_layout(current);
311 "got invalid direction: %d", move_dir)) { 339 int index = list_find(siblings, current);
312 return; 340 int desired = index + offs;
313 } 341
314 double ref_lx = current->x + current->width / 2; 342 if (is_parallel(layout, move_dir)) {
315 double ref_ly = current->y + current->height / 2; 343 if (desired == -1 || desired == siblings->length) {
316 struct wlr_output *next = wlr_output_layout_adjacent_output( 344 if (current->parent == container->parent) {
317 root_container.sway_root->output_layout, wlr_dir, 345 if (!(parent && parent->is_fullscreen) &&
318 current->sway_output->wlr_output, ref_lx, ref_ly); 346 (layout == L_TABBED || layout == L_STACKED)) {
319 if (!next) { 347 move_out_of_tabs_stacks(container, current,
320 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); 348 move_dir, offs);
321 return; 349 return true;
322 }
323 struct sway_output *next_output = next->data;
324 current = next_output->swayc;
325 wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);
326 // Select workspace and get outta here
327 current = seat_get_focus_inactive(
328 config->handler_context.seat, current);
329 if (current->type != C_WORKSPACE) {
330 current = container_parent(current, C_WORKSPACE);
331 }
332 sibling = current;
333 break;
334 }
335 case C_WORKSPACE:
336 if (!is_parallel(current->layout, move_dir)) {
337 if (current->children->length >= 2) {
338 wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",
339 current->children->length);
340 workspace_rejigger(current, container, move_dir);
341 return;
342 } else {
343 wlr_log(WLR_DEBUG, "Selecting output");
344 current = current->parent;
345 }
346 } else if (current->layout == L_TABBED
347 || current->layout == L_STACKED) {
348 wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");
349 workspace_rejigger(current, container, move_dir);
350 } else {
351 wlr_log(WLR_DEBUG, "Selecting output");
352 current = current->parent;
353 }
354 break;
355 case C_CONTAINER:
356 case C_VIEW:
357 if (is_parallel(parent->layout, move_dir)) {
358 if ((index == parent->children->length - 1 && offs > 0)
359 || (index == 0 && offs < 0)) {
360 if (current->parent == container->parent) {
361 if (!parent->is_fullscreen &&
362 (parent->layout == L_TABBED ||
363 parent->layout == L_STACKED)) {
364 move_out_of_tabs_stacks(container, current,
365 move_dir, offs);
366 return;
367 } else {
368 wlr_log(WLR_DEBUG, "Hit limit, selecting parent");
369 current = current->parent;
370 }
371 } else { 350 } else {
372 wlr_log(WLR_DEBUG, "Hit limit, " 351 current = current->parent;
373 "promoting descendant to sibling"); 352 continue;
374 // Special case 353 }
354 } else {
355 // Special case
356 if (current->parent) {
375 container_insert_child(current->parent, container, 357 container_insert_child(current->parent, container,
376 index + (offs < 0 ? 0 : 1)); 358 index + (offs < 0 ? 0 : 1));
377 container->width = container->height = 0; 359 } else {
378 return; 360 workspace_insert_tiling(current->workspace, container,
361 index + (offs < 0 ? 0 : 1));
379 } 362 }
380 } else { 363 return true;
381 sibling = parent->children->items[index + offs];
382 wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
383 } 364 }
384 } else if (!parent->is_fullscreen && (parent->layout == L_TABBED ||
385 parent->layout == L_STACKED)) {
386 move_out_of_tabs_stacks(container, current, move_dir, offs);
387 return;
388 } else { 365 } else {
389 wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); 366 // Container can move within its siblings
390 current = current->parent; 367 container_move_to_container_from_direction(container,
368 siblings->items[desired], move_dir);
369 return true;
391 } 370 }
392 break; 371 } else if (!(parent && parent->is_fullscreen) &&
393 default: 372 (layout == L_TABBED || layout == L_STACKED)) {
394 sway_assert(0, "Not expecting to see container of type %s here", 373 move_out_of_tabs_stacks(container, current, move_dir, offs);
395 container_type_to_str(current->type)); 374 return true;
396 return;
397 } 375 }
398 }
399 376
400 // Part two: move stuff around 377 current = current->parent;
401 int index = container_sibling_index(container);
402 struct sway_container *old_parent = container->parent;
403 378
404 while (sibling) { 379 // Don't allow containers to move out of their
405 switch (sibling->type) { 380 // fullscreen or floating parent
406 case C_VIEW: 381 if (current &&
407 if (sibling->parent == container->parent) { 382 (current->is_fullscreen || container_is_floating(current))) {
408 wlr_log(WLR_DEBUG, "Swapping siblings"); 383 return false;
409 sibling->parent->children->items[index + offs] = container;
410 sibling->parent->children->items[index] = sibling;
411 } else {
412 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
413 container_insert_child(sibling->parent, container,
414 container_sibling_index(sibling) + (offs > 0 ? 0 : 1));
415 container->width = container->height = 0;
416 }
417 sibling = NULL;
418 break;
419 case C_WORKSPACE: // Note: only in the case of moving between outputs
420 case C_CONTAINER:
421 if (is_parallel(sibling->layout, move_dir)) {
422 int limit = container_limit(sibling, invert_movement(move_dir));
423 wlr_log(WLR_DEBUG, "limit: %d", limit);
424 wlr_log(WLR_DEBUG,
425 "Reparenting container (parallel) to index %d "
426 "(move dir: %d)", limit, move_dir);
427 container_insert_child(sibling, container, limit);
428 container->width = container->height = 0;
429 sibling = NULL;
430 } else {
431 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
432 struct sway_container *focus_inactive = seat_get_focus_inactive(
433 config->handler_context.seat, sibling);
434 if (focus_inactive && focus_inactive != sibling) {
435 while (focus_inactive->parent != sibling) {
436 focus_inactive = focus_inactive->parent;
437 }
438 wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",
439 focus_inactive->id);
440 sibling = focus_inactive;
441 continue;
442 } else if (sibling->children->length) {
443 wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");
444 container_remove_child(container);
445 container_add_sibling(sibling->children->items[0], container);
446 } else {
447 wlr_log(WLR_DEBUG, "No kiddos, adding container alone");
448 container_remove_child(container);
449 container_add_child(sibling, container);
450 }
451 container->width = container->height = 0;
452 sibling = NULL;
453 }
454 break;
455 default:
456 sway_assert(0, "Not expecting to see container of type %s here",
457 container_type_to_str(sibling->type));
458 return;
459 } 384 }
460 } 385 }
461 386
462 container_notify_subtree_changed(old_parent); 387 // Maybe rejigger the workspace
463 container_notify_subtree_changed(container->parent); 388 struct sway_workspace *ws = container->workspace;
464 389 if (!is_parallel(ws->layout, move_dir)) {
465 if (container->type == C_VIEW) { 390 if (ws->tiling->length >= 2) {
466 ipc_event_window(container, "move"); 391 workspace_rejigger(ws, container, move_dir);
467 } 392 return true;
468 393 }
469 if (old_parent) { 394 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
470 seat_set_focus(config->handler_context.seat, old_parent); 395 workspace_rejigger(ws, container, move_dir);
471 seat_set_focus(config->handler_context.seat, container); 396 return true;
472 } 397 }
473 398
474 struct sway_container *last_ws = old_parent; 399 // Try adjacent output
475 struct sway_container *next_ws = container->parent; 400 struct sway_output *output =
476 if (last_ws && last_ws->type != C_WORKSPACE) { 401 output_get_in_direction(container->workspace->output, move_dir);
477 last_ws = container_parent(last_ws, C_WORKSPACE); 402 if (output) {
478 } 403 struct sway_workspace *ws = output_get_active_workspace(output);
479 if (next_ws && next_ws->type != C_WORKSPACE) { 404 container_move_to_workspace_from_direction(container, ws, move_dir);
480 next_ws = container_parent(next_ws, C_WORKSPACE); 405 return true;
481 } 406 }
482 if (last_ws && next_ws && last_ws != next_ws) { 407 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");
483 ipc_event_workspace(last_ws, next_ws, "focus"); 408 return false;
484 workspace_detect_urgent(last_ws);
485 workspace_detect_urgent(next_ws);
486 }
487 container_end_mouse_operation(container);
488} 409}
489 410
490static struct cmd_results *cmd_move_container(struct sway_container *current, 411static struct cmd_results *cmd_move_container(int argc, char **argv) {
491 int argc, char **argv) {
492 struct cmd_results *error = NULL; 412 struct cmd_results *error = NULL;
493 if ((error = checkarg(argc, "move container/window", 413 if ((error = checkarg(argc, "move container/window",
494 EXPECTED_AT_LEAST, 3))) { 414 EXPECTED_AT_LEAST, 3))) {
495 return error; 415 return error;
496 } 416 }
497 417
498 if (current->type == C_WORKSPACE) { 418 struct sway_node *node = config->handler_context.node;
499 if (current->children->length == 0) { 419 struct sway_workspace *workspace = config->handler_context.workspace;
420 struct sway_container *container = config->handler_context.container;
421 if (node->type == N_WORKSPACE) {
422 if (workspace->tiling->length == 0) {
500 return cmd_results_new(CMD_FAILURE, "move", 423 return cmd_results_new(CMD_FAILURE, "move",
501 "Can't move an empty workspace"); 424 "Can't move an empty workspace");
502 } 425 }
503 current = workspace_wrap_children(current); 426 container = workspace_wrap_children(workspace);
504 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
505 return cmd_results_new(CMD_FAILURE, "move",
506 "Can only move containers and views.");
507 } 427 }
508 428
509 bool no_auto_back_and_forth = false; 429 bool no_auto_back_and_forth = false;
@@ -530,15 +450,15 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
530 } 450 }
531 451
532 struct sway_seat *seat = config->handler_context.seat; 452 struct sway_seat *seat = config->handler_context.seat;
533 struct sway_container *old_parent = current->parent; 453 struct sway_container *old_parent = container->parent;
534 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 454 struct sway_workspace *old_ws = container->workspace;
535 struct sway_container *old_output = container_parent(current, C_OUTPUT); 455 struct sway_output *old_output = old_ws->output;
536 struct sway_container *destination = NULL; 456 struct sway_node *destination = NULL;
537 457
538 // determine destination 458 // determine destination
539 if (strcasecmp(argv[1], "workspace") == 0) { 459 if (strcasecmp(argv[1], "workspace") == 0) {
540 // move container to workspace x 460 // move container to workspace x
541 struct sway_container *ws = NULL; 461 struct sway_workspace *ws = NULL;
542 char *ws_name = NULL; 462 char *ws_name = NULL;
543 if (strcasecmp(argv[2], "next") == 0 || 463 if (strcasecmp(argv[2], "next") == 0 ||
544 strcasecmp(argv[2], "prev") == 0 || 464 strcasecmp(argv[2], "prev") == 0 ||
@@ -588,8 +508,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
588 // We have to create the workspace, but if the container is 508 // We have to create the workspace, but if the container is
589 // sticky and the workspace is going to be created on the same 509 // sticky and the workspace is going to be created on the same
590 // output, we'll bail out first. 510 // output, we'll bail out first.
591 if (current->is_sticky) { 511 if (container->is_sticky) {
592 struct sway_container *new_output = 512 struct sway_output *new_output =
593 workspace_get_initial_output(ws_name); 513 workspace_get_initial_output(ws_name);
594 if (old_output == new_output) { 514 if (old_output == new_output) {
595 free(ws_name); 515 free(ws_name);
@@ -601,105 +521,113 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
601 ws = workspace_create(NULL, ws_name); 521 ws = workspace_create(NULL, ws_name);
602 } 522 }
603 free(ws_name); 523 free(ws_name);
604 destination = seat_get_focus_inactive(seat, ws); 524 destination = seat_get_focus_inactive(seat, &ws->node);
605 } else if (strcasecmp(argv[1], "output") == 0) { 525 } else if (strcasecmp(argv[1], "output") == 0) {
606 struct sway_container *dest_output = output_in_direction(argv[2], 526 struct sway_output *new_output = output_in_direction(argv[2],
607 old_output->sway_output->wlr_output, current->x, current->y); 527 old_output->wlr_output, container->x, container->y);
608 if (!dest_output) { 528 if (!new_output) {
609 return cmd_results_new(CMD_FAILURE, "move workspace", 529 return cmd_results_new(CMD_FAILURE, "move workspace",
610 "Can't find output with name/direction '%s'", argv[2]); 530 "Can't find output with name/direction '%s'", argv[2]);
611 } 531 }
612 destination = seat_get_focus_inactive(seat, dest_output); 532 destination = seat_get_focus_inactive(seat, &new_output->node);
613 if (!destination) {
614 // We've never been to this output before
615 destination = dest_output->children->items[0];
616 }
617 } else if (strcasecmp(argv[1], "mark") == 0) { 533 } else if (strcasecmp(argv[1], "mark") == 0) {
618 struct sway_view *dest_view = view_find_mark(argv[2]); 534 struct sway_view *dest_view = view_find_mark(argv[2]);
619 if (dest_view == NULL) { 535 if (dest_view == NULL) {
620 return cmd_results_new(CMD_FAILURE, "move", 536 return cmd_results_new(CMD_FAILURE, "move",
621 "Mark '%s' not found", argv[2]); 537 "Mark '%s' not found", argv[2]);
622 } 538 }
623 destination = dest_view->swayc; 539 destination = &dest_view->container->node;
624 } else { 540 } else {
625 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 541 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
626 } 542 }
627 543
628 struct sway_container *new_output = destination->type == C_OUTPUT ? 544 if (container->is_sticky &&
629 destination : container_parent(destination, C_OUTPUT); 545 node_has_ancestor(destination, &old_output->node)) {
630 if (current->is_sticky && old_output == new_output) {
631 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " 546 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky "
632 "container to another workspace on the same output"); 547 "container to another workspace on the same output");
633 } 548 }
634 549
635 struct sway_container *new_output_last_ws = old_output == new_output ? 550 struct sway_output *new_output = node_get_output(destination);
636 NULL : seat_get_active_child(seat, new_output); 551 struct sway_workspace *new_output_last_ws = old_output == new_output ?
637 struct sway_container *new_workspace = destination->type == C_WORKSPACE ? 552 NULL : output_get_active_workspace(new_output);
638 destination : container_parent(destination, C_WORKSPACE);
639 553
640 // move container, arrange windows and return focus 554 // move container, arrange windows and return focus
641 container_move_to(current, destination); 555 switch (destination->type) {
556 case N_WORKSPACE:
557 container_move_to_workspace(container, destination->sway_workspace);
558 break;
559 case N_OUTPUT: {
560 struct sway_output *output = destination->sway_output;
561 struct sway_workspace *ws = output_get_active_workspace(output);
562 container_move_to_workspace(container, ws);
563 }
564 break;
565 case N_CONTAINER:
566 container_move_to_container(container, destination->sway_container);
567 break;
568 case N_ROOT:
569 break;
570 }
571 struct sway_workspace *new_workspace =
572 output_get_active_workspace(new_output);
642 if (new_output_last_ws && new_output_last_ws != new_workspace) { 573 if (new_output_last_ws && new_output_last_ws != new_workspace) {
643 // change focus on destination output back to its last active workspace 574 // change focus on destination output back to its last active workspace
644 struct sway_container *new_output_last_focus = 575 struct sway_node *new_output_last_focus =
645 seat_get_focus_inactive(seat, new_output_last_ws); 576 seat_get_focus_inactive(seat, &new_output_last_ws->node);
646 seat_set_focus_warp(seat, new_output_last_focus, false, false); 577 seat_set_focus_warp(seat, new_output_last_focus, false, false);
647 } 578 }
648 struct sway_container *focus = seat_get_focus_inactive(seat, old_parent); 579
580 struct sway_node *focus = NULL;
581 if (old_parent) {
582 focus = seat_get_focus_inactive(seat, &old_parent->node);
583 } else {
584 focus = seat_get_focus_inactive(seat, &old_ws->node);
585 }
649 seat_set_focus_warp(seat, focus, true, false); 586 seat_set_focus_warp(seat, focus, true, false);
650 container_reap_empty(old_parent);
651 container_reap_empty(destination->parent);
652 587
653 // TODO: Ideally we would arrange the surviving parent after reaping, 588 if (old_parent) {
654 // but container_reap_empty does not return it, so we arrange the 589 container_reap_empty(old_parent);
655 // workspace instead. 590 } else {
656 arrange_windows(old_ws); 591 workspace_consider_destroy(old_ws);
657 arrange_windows(destination->parent); 592 }
593
594 arrange_workspace(old_ws);
595 arrange_node(node_get_parent(destination));
658 596
659 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 597 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
660} 598}
661 599
662static void workspace_move_to_output(struct sway_container *workspace, 600static void workspace_move_to_output(struct sway_workspace *workspace,
663 struct sway_container *output) { 601 struct sway_output *output) {
664 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 602 if (workspace->output == output) {
665 return; 603 return;
666 } 604 }
667 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 605 struct sway_output *old_output = workspace->output;
668 return; 606 workspace_detach(workspace);
669 } 607 struct sway_workspace *new_output_old_ws =
670 if (workspace->parent == output) { 608 output_get_active_workspace(output);
671 return;
672 }
673 struct sway_container *old_output = container_remove_child(workspace);
674 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
675 struct sway_container *new_output_focus =
676 seat_get_focus_inactive(seat, output);
677 609
678 container_add_child(output, workspace); 610 output_add_workspace(output, workspace);
679 611
680 // If moving the last workspace from the old output, create a new workspace 612 // If moving the last workspace from the old output, create a new workspace
681 // on the old output 613 // on the old output
682 if (old_output->children->length == 0) { 614 struct sway_seat *seat = config->handler_context.seat;
683 char *ws_name = workspace_next_name(old_output->name); 615 if (old_output->workspaces->length == 0) {
684 struct sway_container *ws = workspace_create(old_output, ws_name); 616 char *ws_name = workspace_next_name(old_output->wlr_output->name);
617 struct sway_workspace *ws = workspace_create(old_output, ws_name);
685 free(ws_name); 618 free(ws_name);
686 seat_set_focus(seat, ws); 619 seat_set_focus(seat, &ws->node);
687 } 620 }
688 621
689 // Try to remove an empty workspace from the destination output. 622 workspace_consider_destroy(new_output_old_ws);
690 container_reap_empty(new_output_focus);
691 623
692 output_sort_workspaces(output); 624 output_sort_workspaces(output);
693 seat_set_focus(seat, output); 625 seat_set_focus(seat, &output->node);
694 workspace_output_raise_priority(workspace, old_output, output); 626 workspace_output_raise_priority(workspace, old_output, output);
695 ipc_event_workspace(NULL, workspace, "move"); 627 ipc_event_workspace(NULL, workspace, "move");
696
697 container_notify_subtree_changed(old_output);
698 container_notify_subtree_changed(output);
699} 628}
700 629
701static struct cmd_results *cmd_move_workspace(struct sway_container *current, 630static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
702 int argc, char **argv) {
703 struct cmd_results *error = NULL; 631 struct cmd_results *error = NULL;
704 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { 632 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
705 return error; 633 return error;
@@ -716,27 +644,25 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
716 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 644 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
717 } 645 }
718 646
719 struct sway_container *source = container_parent(current, C_OUTPUT); 647 struct sway_workspace *workspace = config->handler_context.workspace;
720 int center_x = current->width / 2 + current->x, 648 struct sway_output *old_output = workspace->output;
721 center_y = current->height / 2 + current->y; 649 int center_x = workspace->width / 2 + workspace->x,
722 struct sway_container *destination = output_in_direction(argv[2], 650 center_y = workspace->height / 2 + workspace->y;
723 source->sway_output->wlr_output, center_x, center_y); 651 struct sway_output *new_output = output_in_direction(argv[2],
724 if (!destination) { 652 old_output->wlr_output, center_x, center_y);
653 if (!new_output) {
725 return cmd_results_new(CMD_FAILURE, "move workspace", 654 return cmd_results_new(CMD_FAILURE, "move workspace",
726 "Can't find output with name/direction '%s'", argv[2]); 655 "Can't find output with name/direction '%s'", argv[2]);
727 } 656 }
728 if (current->type != C_WORKSPACE) { 657 workspace_move_to_output(workspace, new_output);
729 current = container_parent(current, C_WORKSPACE);
730 }
731 workspace_move_to_output(current, destination);
732 658
733 arrange_windows(source); 659 arrange_output(old_output);
734 arrange_windows(destination); 660 arrange_output(new_output);
735 661
736 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 662 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
737} 663}
738 664
739static struct cmd_results *move_in_direction(struct sway_container *container, 665static struct cmd_results *cmd_move_in_direction(
740 enum movement_direction direction, int argc, char **argv) { 666 enum movement_direction direction, int argc, char **argv) {
741 int move_amt = 10; 667 int move_amt = 10;
742 if (argc > 1) { 668 if (argc > 1) {
@@ -748,7 +674,8 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
748 } 674 }
749 } 675 }
750 676
751 if (container->type == C_WORKSPACE) { 677 struct sway_container *container = config->handler_context.container;
678 if (!container) {
752 return cmd_results_new(CMD_FAILURE, "move", 679 return cmd_results_new(CMD_FAILURE, "move",
753 "Cannot move workspaces in a direction"); 680 "Cannot move workspaces in a direction");
754 } 681 }
@@ -780,20 +707,34 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
780 container_floating_move_to(container, lx, ly); 707 container_floating_move_to(container, lx, ly);
781 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 708 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
782 } 709 }
783 // For simplicity, we'll arrange the entire workspace. The reason for this 710 struct sway_workspace *old_ws = container->workspace;
784 // is moving the container might reap the old parent, and container_move
785 // does not return a surviving parent.
786 // TODO: Make container_move return the surviving parent so we can arrange
787 // just that.
788 struct sway_container *old_ws = container_parent(container, C_WORKSPACE);
789 container_move(container, direction, move_amt);
790 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
791 711
792 arrange_windows(old_ws); 712 if (!container_move_in_direction(container, direction)) {
713 // Container didn't move
714 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
715 }
716
717 struct sway_workspace *new_ws = container->workspace;
718
719 arrange_workspace(old_ws);
793 if (new_ws != old_ws) { 720 if (new_ws != old_ws) {
794 arrange_windows(new_ws); 721 arrange_workspace(new_ws);
722 }
723
724 if (container->view) {
725 ipc_event_window(container, "move");
795 } 726 }
796 727
728 seat_set_focus(config->handler_context.seat, &new_ws->node);
729 seat_set_focus(config->handler_context.seat, &container->node);
730
731 if (old_ws != new_ws) {
732 ipc_event_workspace(old_ws, new_ws, "focus");
733 workspace_detect_urgent(old_ws);
734 workspace_detect_urgent(new_ws);
735 }
736 container_end_mouse_operation(container);
737
797 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 738 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
798} 739}
799 740
@@ -802,9 +743,9 @@ static const char *expected_position_syntax =
802 "'move [absolute] position center' or " 743 "'move [absolute] position center' or "
803 "'move position cursor|mouse|pointer'"; 744 "'move position cursor|mouse|pointer'";
804 745
805static struct cmd_results *move_to_position(struct sway_container *container, 746static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
806 int argc, char **argv) { 747 struct sway_container *container = config->handler_context.container;
807 if (!container_is_floating(container)) { 748 if (!container || !container_is_floating(container)) {
808 return cmd_results_new(CMD_FAILURE, "move", 749 return cmd_results_new(CMD_FAILURE, "move",
809 "Only floating containers " 750 "Only floating containers "
810 "can be moved to an absolute position"); 751 "can be moved to an absolute position");
@@ -842,10 +783,10 @@ static struct cmd_results *move_to_position(struct sway_container *container,
842 } else if (strcmp(argv[0], "center") == 0) { 783 } else if (strcmp(argv[0], "center") == 0) {
843 double lx, ly; 784 double lx, ly;
844 if (absolute) { 785 if (absolute) {
845 lx = root_container.x + (root_container.width - container->width) / 2; 786 lx = root->x + (root->width - container->width) / 2;
846 ly = root_container.y + (root_container.height - container->height) / 2; 787 ly = root->y + (root->height - container->height) / 2;
847 } else { 788 } else {
848 struct sway_container *ws = container_parent(container, C_WORKSPACE); 789 struct sway_workspace *ws = container->workspace;
849 lx = ws->x + (ws->width - container->width) / 2; 790 lx = ws->x + (ws->width - container->width) / 2;
850 ly = ws->y + (ws->height - container->height) / 2; 791 ly = ws->y + (ws->height - container->height) / 2;
851 } 792 }
@@ -881,30 +822,31 @@ static struct cmd_results *move_to_position(struct sway_container *container,
881 } 822 }
882 823
883 if (!absolute) { 824 if (!absolute) {
884 struct sway_container *ws = container_parent(container, C_WORKSPACE); 825 lx += container->workspace->x;
885 lx += ws->x; 826 ly += container->workspace->y;
886 ly += ws->y;
887 } 827 }
888 container_floating_move_to(container, lx, ly); 828 container_floating_move_to(container, lx, ly);
889 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 829 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
890} 830}
891 831
892static struct cmd_results *move_to_scratchpad(struct sway_container *con) { 832static struct cmd_results *cmd_move_to_scratchpad(void) {
893 if (con->type == C_WORKSPACE && con->children->length == 0) { 833 struct sway_node *node = config->handler_context.node;
834 struct sway_container *con = config->handler_context.container;
835 struct sway_workspace *ws = config->handler_context.workspace;
836 if (node->type == N_WORKSPACE && ws->tiling->length == 0) {
894 return cmd_results_new(CMD_INVALID, "move", 837 return cmd_results_new(CMD_INVALID, "move",
895 "Can't move an empty workspace to the scratchpad"); 838 "Can't move an empty workspace to the scratchpad");
896 } 839 }
897 if (con->type == C_WORKSPACE) { 840 if (node->type == N_WORKSPACE) {
898 // Wrap the workspace's children in a container 841 // Wrap the workspace's children in a container
899 struct sway_container *workspace = con; 842 con = workspace_wrap_children(ws);
900 con = workspace_wrap_children(con); 843 ws->layout = L_HORIZ;
901 workspace->layout = L_HORIZ;
902 } 844 }
903 845
904 // If the container is in a floating split container, 846 // If the container is in a floating split container,
905 // operate on the split container instead of the child. 847 // operate on the split container instead of the child.
906 if (container_is_floating_or_child(con)) { 848 if (container_is_floating_or_child(con)) {
907 while (con->parent->type != C_WORKSPACE) { 849 while (con->parent) {
908 con = con->parent; 850 con = con->parent;
909 } 851 }
910 } 852 }
@@ -922,32 +864,31 @@ struct cmd_results *cmd_move(int argc, char **argv) {
922 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 864 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
923 return error; 865 return error;
924 } 866 }
925 struct sway_container *current = config->handler_context.current_container;
926 867
927 if (strcasecmp(argv[0], "left") == 0) { 868 if (strcasecmp(argv[0], "left") == 0) {
928 return move_in_direction(current, MOVE_LEFT, argc, argv); 869 return cmd_move_in_direction(MOVE_LEFT, argc, argv);
929 } else if (strcasecmp(argv[0], "right") == 0) { 870 } else if (strcasecmp(argv[0], "right") == 0) {
930 return move_in_direction(current, MOVE_RIGHT, argc, argv); 871 return cmd_move_in_direction(MOVE_RIGHT, argc, argv);
931 } else if (strcasecmp(argv[0], "up") == 0) { 872 } else if (strcasecmp(argv[0], "up") == 0) {
932 return move_in_direction(current, MOVE_UP, argc, argv); 873 return cmd_move_in_direction(MOVE_UP, argc, argv);
933 } else if (strcasecmp(argv[0], "down") == 0) { 874 } else if (strcasecmp(argv[0], "down") == 0) {
934 return move_in_direction(current, MOVE_DOWN, argc, argv); 875 return cmd_move_in_direction(MOVE_DOWN, argc, argv);
935 } else if ((strcasecmp(argv[0], "container") == 0 876 } else if ((strcasecmp(argv[0], "container") == 0
936 || strcasecmp(argv[0], "window") == 0) || 877 || strcasecmp(argv[0], "window") == 0) ||
937 (strcasecmp(argv[0], "--no-auto-back-and-forth") && 878 (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2
938 (strcasecmp(argv[0], "container") == 0 879 && (strcasecmp(argv[1], "container") == 0
939 || strcasecmp(argv[0], "window") == 0))) { 880 || strcasecmp(argv[1], "window") == 0))) {
940 return cmd_move_container(current, argc, argv); 881 return cmd_move_container(argc, argv);
941 } else if (strcasecmp(argv[0], "workspace") == 0) { 882 } else if (strcasecmp(argv[0], "workspace") == 0) {
942 return cmd_move_workspace(current, argc, argv); 883 return cmd_move_workspace(argc, argv);
943 } else if (strcasecmp(argv[0], "scratchpad") == 0 884 } else if (strcasecmp(argv[0], "scratchpad") == 0
944 || (strcasecmp(argv[0], "to") == 0 && argc == 2 885 || (strcasecmp(argv[0], "to") == 0 && argc == 2
945 && strcasecmp(argv[1], "scratchpad") == 0)) { 886 && strcasecmp(argv[1], "scratchpad") == 0)) {
946 return move_to_scratchpad(current); 887 return cmd_move_to_scratchpad();
947 } else if (strcasecmp(argv[0], "position") == 0) { 888 } else if (strcasecmp(argv[0], "position") == 0) {
948 return move_to_position(current, argc, argv); 889 return cmd_move_to_position(argc, argv);
949 } else if (strcasecmp(argv[0], "absolute") == 0) { 890 } else if (strcasecmp(argv[0], "absolute") == 0) {
950 return move_to_position(current, argc, argv); 891 return cmd_move_to_position(argc, argv);
951 } else { 892 } else {
952 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 893 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
953 } 894 }
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
index 68fd9f42..9cdaad7f 100644
--- a/sway/commands/opacity.c
+++ b/sway/commands/opacity.c
@@ -19,8 +19,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
19 return error; 19 return error;
20 } 20 }
21 21
22 struct sway_container *con = 22 struct sway_container *con = config->handler_context.container;
23 config->handler_context.current_container;
24 23
25 float opacity = 0.0f; 24 float opacity = 0.0f;
26 25
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index f8ca374d..36fb9092 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -42,7 +42,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
42 } 42 }
43 list_free(bar_ids); 43 list_free(bar_ids);
44 44
45 arrange_windows(&root_container); 45 arrange_root();
46 46
47 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 47 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
48} 48}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 21d2aa64..d982f941 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -25,14 +25,11 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
25 } 25 }
26 26
27 int argn = 1; 27 int argn = 1;
28 struct sway_container *workspace; 28 struct sway_workspace *workspace = NULL;
29 29
30 if (strcasecmp(argv[1], "to") == 0) { 30 if (strcasecmp(argv[1], "to") == 0) {
31 // 'rename workspace to new_name' 31 // 'rename workspace to new_name'
32 workspace = config->handler_context.current_container; 32 workspace = config->handler_context.workspace;
33 if (workspace->type != C_WORKSPACE) {
34 workspace = container_parent(workspace, C_WORKSPACE);
35 }
36 } else if (strcasecmp(argv[1], "number") == 0) { 33 } else if (strcasecmp(argv[1], "number") == 0) {
37 // 'rename workspace number x to new_name' 34 // 'rename workspace number x to new_name'
38 if (!isdigit(argv[2][0])) { 35 if (!isdigit(argv[2][0])) {
@@ -78,7 +75,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
78 return cmd_results_new(CMD_INVALID, "rename", 75 return cmd_results_new(CMD_INVALID, "rename",
79 "Cannot use special workspace name '%s'", argv[argn]); 76 "Cannot use special workspace name '%s'", argv[argn]);
80 } 77 }
81 struct sway_container *tmp_workspace = workspace_by_name(new_name); 78 struct sway_workspace *tmp_workspace = workspace_by_name(new_name);
82 if (tmp_workspace) { 79 if (tmp_workspace) {
83 free(new_name); 80 free(new_name);
84 return cmd_results_new(CMD_INVALID, "rename", 81 return cmd_results_new(CMD_INVALID, "rename",
@@ -89,7 +86,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
89 free(workspace->name); 86 free(workspace->name);
90 workspace->name = new_name; 87 workspace->name = new_name;
91 88
92 output_sort_workspaces(workspace->parent); 89 output_sort_workspaces(workspace->output);
93 ipc_event_workspace(NULL, workspace, "rename"); 90 ipc_event_workspace(NULL, workspace, "rename");
94 91
95 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 92 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ad659ef5..99e9dbda 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -10,6 +10,7 @@
10#include "sway/commands.h" 10#include "sway/commands.h"
11#include "sway/tree/arrange.h" 11#include "sway/tree/arrange.h"
12#include "sway/tree/view.h" 12#include "sway/tree/view.h"
13#include "sway/tree/workspace.h"
13#include "log.h" 14#include "log.h"
14 15
15static const int MIN_SANE_W = 100, MIN_SANE_H = 60; 16static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
@@ -75,7 +76,7 @@ static int parse_resize_amount(int argc, char **argv,
75 76
76static void calculate_constraints(int *min_width, int *max_width, 77static void calculate_constraints(int *min_width, int *max_width,
77 int *min_height, int *max_height) { 78 int *min_height, int *max_height) {
78 struct sway_container *con = config->handler_context.current_container; 79 struct sway_container *con = config->handler_context.container;
79 80
80 if (config->floating_minimum_width == -1) { // no minimum 81 if (config->floating_minimum_width == -1) { // no minimum
81 *min_width = 0; 82 *min_width = 0;
@@ -96,8 +97,7 @@ static void calculate_constraints(int *min_width, int *max_width,
96 if (config->floating_maximum_width == -1) { // no maximum 97 if (config->floating_maximum_width == -1) { // no maximum
97 *max_width = INT_MAX; 98 *max_width = INT_MAX;
98 } else if (config->floating_maximum_width == 0) { // automatic 99 } else if (config->floating_maximum_width == 0) { // automatic
99 struct sway_container *ws = container_parent(con, C_WORKSPACE); 100 *max_width = con->workspace->width;
100 *max_width = ws->width;
101 } else { 101 } else {
102 *max_width = config->floating_maximum_width; 102 *max_width = config->floating_maximum_width;
103 } 103 }
@@ -105,8 +105,7 @@ static void calculate_constraints(int *min_width, int *max_width,
105 if (config->floating_maximum_height == -1) { // no maximum 105 if (config->floating_maximum_height == -1) { // no maximum
106 *max_height = INT_MAX; 106 *max_height = INT_MAX;
107 } else if (config->floating_maximum_height == 0) { // automatic 107 } else if (config->floating_maximum_height == 0) { // automatic
108 struct sway_container *ws = container_parent(con, C_WORKSPACE); 108 *max_height = con->workspace->height;
109 *max_height = ws->height;
110 } else { 109 } else {
111 *max_height = config->floating_maximum_height; 110 *max_height = config->floating_maximum_height;
112 } 111 }
@@ -191,11 +190,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
191 normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT; 190 normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
192 int minor_weight = 0; 191 int minor_weight = 0;
193 int major_weight = 0; 192 int major_weight = 0;
194 while (parent->parent) { 193 while (parent) {
195 struct sway_container *next = parent->parent; 194 list_t *siblings = container_get_siblings(parent);
196 if (next->layout == parallel_layout) { 195 if (container_parent_layout(parent) == parallel_layout) {
197 for (int i = 0; i < next->children->length; i++) { 196 for (int i = 0; i < siblings->length; i++) {
198 struct sway_container *sibling = next->children->items[i]; 197 struct sway_container *sibling = siblings->items[i];
199 198
200 int sibling_pos = parallel_coord(sibling, axis); 199 int sibling_pos = parallel_coord(sibling, axis);
201 int focused_pos = parallel_coord(focused, axis); 200 int focused_pos = parallel_coord(focused, axis);
@@ -213,17 +212,13 @@ static void resize_tiled(struct sway_container *parent, int amount,
213 break; 212 break;
214 } 213 }
215 } 214 }
216 parent = next; 215 parent = parent->parent;
217 } 216 }
218 217 if (!parent) {
219 if (parent->type == C_ROOT) { 218 // Can't resize in this direction
220 return; 219 return;
221 } 220 }
222 221
223 wlr_log(WLR_DEBUG,
224 "Found the proper parent: %p. It has %d l conts, and %d r conts",
225 parent->parent, minor_weight, major_weight);
226
227 // Implement up/down/left/right direction by zeroing one of the weights, 222 // Implement up/down/left/right direction by zeroing one of the weights,
228 // then setting the axis to be horizontal or vertical 223 // then setting the axis to be horizontal or vertical
229 if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) { 224 if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) {
@@ -237,9 +232,10 @@ static void resize_tiled(struct sway_container *parent, int amount,
237 232
238 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks 233 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
239 // ^ ????? 234 // ^ ?????
235 list_t *siblings = container_get_siblings(parent);
240 236
241 for (int i = 0; i < parent->parent->children->length; i++) { 237 for (int i = 0; i < siblings->length; i++) {
242 struct sway_container *sibling = parent->parent->children->items[i]; 238 struct sway_container *sibling = siblings->items[i];
243 239
244 int sibling_pos = parallel_coord(sibling, axis); 240 int sibling_pos = parallel_coord(sibling, axis);
245 int focused_pos = parallel_coord(focused, axis); 241 int focused_pos = parallel_coord(focused, axis);
@@ -277,8 +273,8 @@ static void resize_tiled(struct sway_container *parent, int amount,
277 enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ? 273 enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
278 WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM; 274 WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM;
279 275
280 for (int i = 0; i < parent->parent->children->length; i++) { 276 for (int i = 0; i < siblings->length; i++) {
281 struct sway_container *sibling = parent->parent->children->items[i]; 277 struct sway_container *sibling = siblings->items[i];
282 278
283 int sibling_pos = parallel_coord(sibling, axis); 279 int sibling_pos = parallel_coord(sibling, axis);
284 int focused_pos = parallel_coord(focused, axis); 280 int focused_pos = parallel_coord(focused, axis);
@@ -316,7 +312,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
316 } 312 }
317 } 313 }
318 314
319 arrange_windows(parent->parent); 315 if (parent->parent) {
316 arrange_container(parent->parent);
317 } else {
318 arrange_workspace(parent->workspace);
319 }
320} 320}
321 321
322void container_resize_tiled(struct sway_container *parent, 322void container_resize_tiled(struct sway_container *parent,
@@ -346,7 +346,7 @@ void container_resize_tiled(struct sway_container *parent,
346 */ 346 */
347static struct cmd_results *resize_adjust_floating(enum resize_axis axis, 347static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
348 struct resize_amount *amount) { 348 struct resize_amount *amount) {
349 struct sway_container *con = config->handler_context.current_container; 349 struct sway_container *con = config->handler_context.container;
350 int grow_width = 0, grow_height = 0; 350 int grow_width = 0, grow_height = 0;
351 switch (axis) { 351 switch (axis) {
352 case RESIZE_AXIS_HORIZONTAL: 352 case RESIZE_AXIS_HORIZONTAL:
@@ -400,15 +400,15 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
400 con->width += grow_width; 400 con->width += grow_width;
401 con->height += grow_height; 401 con->height += grow_height;
402 402
403 if (con->type == C_VIEW) { 403 if (con->view) {
404 struct sway_view *view = con->sway_view; 404 struct sway_view *view = con->view;
405 view->x += grow_x; 405 view->x += grow_x;
406 view->y += grow_y; 406 view->y += grow_y;
407 view->width += grow_width; 407 view->width += grow_width;
408 view->height += grow_height; 408 view->height += grow_height;
409 } 409 }
410 410
411 arrange_windows(con); 411 arrange_container(con);
412 412
413 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 413 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
414} 414}
@@ -418,7 +418,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
418 */ 418 */
419static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, 419static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
420 struct resize_amount *amount) { 420 struct resize_amount *amount) {
421 struct sway_container *current = config->handler_context.current_container; 421 struct sway_container *current = config->handler_context.container;
422 422
423 if (amount->unit == RESIZE_UNIT_DEFAULT) { 423 if (amount->unit == RESIZE_UNIT_DEFAULT) {
424 amount->unit = RESIZE_UNIT_PPT; 424 amount->unit = RESIZE_UNIT_PPT;
@@ -456,13 +456,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
456 width->unit == RESIZE_UNIT_DEFAULT) { 456 width->unit == RESIZE_UNIT_DEFAULT) {
457 // Convert to px 457 // Convert to px
458 struct sway_container *parent = con->parent; 458 struct sway_container *parent = con->parent;
459 while (parent->type >= C_WORKSPACE && parent->layout != L_HORIZ) { 459 while (parent && parent->layout != L_HORIZ) {
460 parent = parent->parent; 460 parent = parent->parent;
461 } 461 }
462 if (parent->type >= C_WORKSPACE) { 462 if (parent) {
463 width->amount = parent->width * width->amount / 100; 463 width->amount = parent->width * width->amount / 100;
464 width->unit = RESIZE_UNIT_PX; 464 } else {
465 width->amount = con->workspace->width * width->amount / 100;
465 } 466 }
467 width->unit = RESIZE_UNIT_PX;
466 } 468 }
467 if (width->unit == RESIZE_UNIT_PX) { 469 if (width->unit == RESIZE_UNIT_PX) {
468 resize_tiled(con, width->amount - con->width, 470 resize_tiled(con, width->amount - con->width,
@@ -475,13 +477,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
475 height->unit == RESIZE_UNIT_DEFAULT) { 477 height->unit == RESIZE_UNIT_DEFAULT) {
476 // Convert to px 478 // Convert to px
477 struct sway_container *parent = con->parent; 479 struct sway_container *parent = con->parent;
478 while (parent->type >= C_WORKSPACE && parent->layout != L_VERT) { 480 while (parent && parent->layout != L_VERT) {
479 parent = parent->parent; 481 parent = parent->parent;
480 } 482 }
481 if (parent->type >= C_WORKSPACE) { 483 if (parent) {
482 height->amount = parent->height * height->amount / 100; 484 height->amount = parent->height * height->amount / 100;
483 height->unit = RESIZE_UNIT_PX; 485 } else {
486 height->amount = con->workspace->height * height->amount / 100;
484 } 487 }
488 height->unit = RESIZE_UNIT_PX;
485 } 489 }
486 if (height->unit == RESIZE_UNIT_PX) { 490 if (height->unit == RESIZE_UNIT_PX) {
487 resize_tiled(con, height->amount - con->height, 491 resize_tiled(con, height->amount - con->height,
@@ -508,15 +512,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
508 con->width = width->amount; 512 con->width = width->amount;
509 con->height = height->amount; 513 con->height = height->amount;
510 514
511 if (con->type == C_VIEW) { 515 if (con->view) {
512 struct sway_view *view = con->sway_view; 516 struct sway_view *view = con->view;
513 view->x -= grow_width / 2; 517 view->x -= grow_width / 2;
514 view->y -= grow_height / 2; 518 view->y -= grow_height / 2;
515 view->width += grow_width; 519 view->width += grow_width;
516 view->height += grow_height; 520 view->height += grow_height;
517 } 521 }
518 522
519 arrange_windows(con); 523 arrange_container(con);
520 524
521 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 525 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
522} 526}
@@ -555,7 +559,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
555 } 559 }
556 560
557 // If 0, don't resize that dimension 561 // If 0, don't resize that dimension
558 struct sway_container *con = config->handler_context.current_container; 562 struct sway_container *con = config->handler_context.container;
559 if (width.amount <= 0) { 563 if (width.amount <= 0) {
560 width.amount = con->width; 564 width.amount = con->width;
561 } 565 }
@@ -624,7 +628,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
624 first_amount.amount *= multiplier; 628 first_amount.amount *= multiplier;
625 second_amount.amount *= multiplier; 629 second_amount.amount *= multiplier;
626 630
627 struct sway_container *con = config->handler_context.current_container; 631 struct sway_container *con = config->handler_context.container;
628 if (container_is_floating(con)) { 632 if (container_is_floating(con)) {
629 // Floating containers can only resize in px. Choose an amount which 633 // Floating containers can only resize in px. Choose an amount which
630 // uses px, with fallback to an amount that specified no unit. 634 // uses px, with fallback to an amount that specified no unit.
@@ -657,14 +661,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
657} 661}
658 662
659struct cmd_results *cmd_resize(int argc, char **argv) { 663struct cmd_results *cmd_resize(int argc, char **argv) {
660 struct sway_container *current = config->handler_context.current_container; 664 struct sway_container *current = config->handler_context.container;
661 if (!current) { 665 if (!current) {
662 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); 666 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
663 } 667 }
664 if (current->type != C_VIEW && current->type != C_CONTAINER) {
665 return cmd_results_new(CMD_INVALID, "resize",
666 "Can only resize views/containers");
667 }
668 668
669 struct cmd_results *error; 669 struct cmd_results *error;
670 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 670 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
index 7da20015..d8bae615 100644
--- a/sway/commands/scratchpad.c
+++ b/sway/commands/scratchpad.c
@@ -9,36 +9,34 @@
9 9
10static void scratchpad_toggle_auto(void) { 10static void scratchpad_toggle_auto(void) {
11 struct sway_seat *seat = input_manager_current_seat(input_manager); 11 struct sway_seat *seat = input_manager_current_seat(input_manager);
12 struct sway_container *focus = seat_get_focus(seat); 12 struct sway_container *focus = seat_get_focused_container(seat);
13 struct sway_container *ws = focus->type == C_WORKSPACE ? 13 struct sway_workspace *ws = seat_get_focused_workspace(seat);
14 focus : container_parent(focus, C_WORKSPACE);
15 14
16 // If the focus is in a floating split container, 15 // If the focus is in a floating split container,
17 // operate on the split container instead of the child. 16 // operate on the split container instead of the child.
18 if (container_is_floating_or_child(focus)) { 17 if (focus && container_is_floating_or_child(focus)) {
19 while (focus->parent->type != C_WORKSPACE) { 18 while (focus->parent) {
20 focus = focus->parent; 19 focus = focus->parent;
21 } 20 }
22 } 21 }
23 22
24
25 // Check if the currently focused window is a scratchpad window and should 23 // Check if the currently focused window is a scratchpad window and should
26 // be hidden again. 24 // be hidden again.
27 if (focus->scratchpad) { 25 if (focus && focus->scratchpad) {
28 wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", 26 wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s",
29 focus->name); 27 focus->title);
30 root_scratchpad_hide(focus); 28 root_scratchpad_hide(focus);
31 return; 29 return;
32 } 30 }
33 31
34 // Check if there is an unfocused scratchpad window on the current workspace 32 // Check if there is an unfocused scratchpad window on the current workspace
35 // and focus it. 33 // and focus it.
36 for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { 34 for (int i = 0; i < ws->floating->length; ++i) {
37 struct sway_container *floater = ws->sway_workspace->floating->items[i]; 35 struct sway_container *floater = ws->floating->items[i];
38 if (floater->scratchpad && focus != floater) { 36 if (floater->scratchpad && focus != floater) {
39 wlr_log(WLR_DEBUG, 37 wlr_log(WLR_DEBUG,
40 "Focusing other scratchpad window (%s) in this workspace", 38 "Focusing other scratchpad window (%s) in this workspace",
41 floater->name); 39 floater->title);
42 root_scratchpad_show(floater); 40 root_scratchpad_show(floater);
43 return; 41 return;
44 } 42 }
@@ -46,25 +44,23 @@ static void scratchpad_toggle_auto(void) {
46 44
47 // Check if there is a visible scratchpad window on another workspace. 45 // Check if there is a visible scratchpad window on another workspace.
48 // In this case we move it to the current workspace. 46 // In this case we move it to the current workspace.
49 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 47 for (int i = 0; i < root->scratchpad->length; ++i) {
50 struct sway_container *con = 48 struct sway_container *con = root->scratchpad->items[i];
51 root_container.sway_root->scratchpad->items[i];
52 if (con->parent) { 49 if (con->parent) {
53 wlr_log(WLR_DEBUG, 50 wlr_log(WLR_DEBUG,
54 "Moving a visible scratchpad window (%s) to this workspace", 51 "Moving a visible scratchpad window (%s) to this workspace",
55 con->name); 52 con->title);
56 root_scratchpad_show(con); 53 root_scratchpad_show(con);
57 return; 54 return;
58 } 55 }
59 } 56 }
60 57
61 // Take the container at the bottom of the scratchpad list 58 // Take the container at the bottom of the scratchpad list
62 if (!sway_assert(root_container.sway_root->scratchpad->length, 59 if (!sway_assert(root->scratchpad->length, "Scratchpad is empty")) {
63 "Scratchpad is empty")) {
64 return; 60 return;
65 } 61 }
66 struct sway_container *con = root_container.sway_root->scratchpad->items[0]; 62 struct sway_container *con = root->scratchpad->items[0];
67 wlr_log(WLR_DEBUG, "Showing %s from list", con->name); 63 wlr_log(WLR_DEBUG, "Showing %s from list", con->title);
68 root_scratchpad_show(con); 64 root_scratchpad_show(con);
69} 65}
70 66
@@ -74,7 +70,7 @@ static void scratchpad_toggle_container(struct sway_container *con) {
74 } 70 }
75 71
76 // Check if it matches a currently visible scratchpad window and hide it. 72 // Check if it matches a currently visible scratchpad window and hide it.
77 if (con->parent) { 73 if (con->workspace) {
78 root_scratchpad_hide(con); 74 root_scratchpad_hide(con);
79 return; 75 return;
80 } 76 }
@@ -91,18 +87,18 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) {
91 return cmd_results_new(CMD_INVALID, "scratchpad", 87 return cmd_results_new(CMD_INVALID, "scratchpad",
92 "Expected 'scratchpad show'"); 88 "Expected 'scratchpad show'");
93 } 89 }
94 if (!root_container.sway_root->scratchpad->length) { 90 if (!root->scratchpad->length) {
95 return cmd_results_new(CMD_INVALID, "scratchpad", 91 return cmd_results_new(CMD_INVALID, "scratchpad",
96 "Scratchpad is empty"); 92 "Scratchpad is empty");
97 } 93 }
98 94
99 if (config->handler_context.using_criteria) { 95 if (config->handler_context.using_criteria) {
100 struct sway_container *con = config->handler_context.current_container; 96 struct sway_container *con = config->handler_context.container;
101 97
102 // If the container is in a floating split container, 98 // If the container is in a floating split container,
103 // operate on the split container instead of the child. 99 // operate on the split container instead of the child.
104 if (container_is_floating_or_child(con)) { 100 if (container_is_floating_or_child(con)) {
105 while (con->parent->type != C_WORKSPACE) { 101 while (con->parent) {
106 con = con->parent; 102 con = con->parent;
107 } 103 }
108 } 104 }
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 4d0a22c7..cd6630e0 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -42,8 +42,8 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
43 } 43 }
44 // map absolute coords (0..1,0..1) to root container coords 44 // map absolute coords (0..1,0..1) to root container coords
45 float x = strtof(argv[1], NULL) / root_container.width; 45 float x = strtof(argv[1], NULL) / root->width;
46 float y = strtof(argv[2], NULL) / root_container.height; 46 float y = strtof(argv[2], NULL) / root->height;
47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); 47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
48 cursor_send_pointer_motion(cursor, 0, true); 48 cursor_send_pointer_motion(cursor, 0, true);
49 } else { 49 } else {
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index 1844e917..d501584a 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -11,8 +11,8 @@
11#include "util.h" 11#include "util.h"
12 12
13static void rebuild_marks_iterator(struct sway_container *con, void *data) { 13static void rebuild_marks_iterator(struct sway_container *con, void *data) {
14 if (con->type == C_VIEW) { 14 if (con->view) {
15 view_update_marks_textures(con->sway_view); 15 view_update_marks_textures(con->view);
16 } 16 }
17} 17}
18 18
@@ -28,9 +28,9 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
28 root_for_each_container(rebuild_marks_iterator, NULL); 28 root_for_each_container(rebuild_marks_iterator, NULL);
29 } 29 }
30 30
31 for (int i = 0; i < root_container.children->length; ++i) { 31 for (int i = 0; i < root->outputs->length; ++i) {
32 struct sway_container *con = root_container.children->items[i]; 32 struct sway_output *output = root->outputs->items[i];
33 output_damage_whole(con->sway_output); 33 output_damage_whole(output);
34 } 34 }
35 35
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index 7d27e571..273905df 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
23 "Expected 'smart_gaps <on|off>' "); 23 "Expected 'smart_gaps <on|off>' ");
24 } 24 }
25 25
26 arrange_windows(&root_container); 26 arrange_root();
27 27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 29}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index a8eddf54..9a53f3d3 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -4,15 +4,21 @@
4#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h" 6#include "sway/tree/view.h"
7#include "sway/tree/workspace.h"
7#include "sway/input/input-manager.h" 8#include "sway/input/input-manager.h"
8#include "sway/input/seat.h" 9#include "sway/input/seat.h"
9#include "log.h" 10#include "log.h"
10 11
11static struct cmd_results *do_split(int layout) { 12static struct cmd_results *do_split(int layout) {
12 struct sway_container *con = config->handler_context.current_container; 13 struct sway_container *con = config->handler_context.container;
13 struct sway_container *parent = container_split(con, layout); 14 struct sway_workspace *ws = config->handler_context.workspace;
14 container_create_notify(parent); 15 if (con) {
15 arrange_windows(parent->parent); 16 container_split(con, layout);
17 } else {
18 workspace_split(ws, layout);
19 }
20
21 arrange_workspace(ws);
16 22
17 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
18} 24}
@@ -29,10 +35,9 @@ struct cmd_results *cmd_split(int argc, char **argv) {
29 return do_split(L_HORIZ); 35 return do_split(L_HORIZ);
30 } else if (strcasecmp(argv[0], "t") == 0 || 36 } else if (strcasecmp(argv[0], "t") == 0 ||
31 strcasecmp(argv[0], "toggle") == 0) { 37 strcasecmp(argv[0], "toggle") == 0) {
32 struct sway_container *focused = 38 struct sway_container *focused = config->handler_context.container;
33 config->handler_context.current_container;
34 39
35 if (focused->parent->layout == L_VERT) { 40 if (focused && container_parent_layout(focused) == L_VERT) {
36 return do_split(L_HORIZ); 41 return do_split(L_HORIZ);
37 } else { 42 } else {
38 return do_split(L_VERT); 43 return do_split(L_VERT);
@@ -66,9 +71,9 @@ struct cmd_results *cmd_splitt(int argc, char **argv) {
66 return error; 71 return error;
67 } 72 }
68 73
69 struct sway_container *con = config->handler_context.current_container; 74 struct sway_container *con = config->handler_context.container;
70 75
71 if (con->parent->layout == L_VERT) { 76 if (con && container_parent_layout(con) == L_VERT) {
72 return do_split(L_HORIZ); 77 return do_split(L_HORIZ);
73 } else { 78 } else {
74 return do_split(L_VERT); 79 return do_split(L_VERT);
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index 8692e08d..7995cdd6 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -15,8 +15,7 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
15 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) { 15 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
16 return error; 16 return error;
17 } 17 }
18 struct sway_container *container = 18 struct sway_container *container = config->handler_context.container;
19 config->handler_context.current_container;
20 if (!container_is_floating(container)) { 19 if (!container_is_floating(container)) {
21 return cmd_results_new(CMD_FAILURE, "sticky", 20 return cmd_results_new(CMD_FAILURE, "sticky",
22 "Can't set sticky on a tiled container"); 21 "Can't set sticky on a tiled container");
@@ -37,20 +36,16 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
37 container->is_sticky = wants_sticky; 36 container->is_sticky = wants_sticky;
38 37
39 if (wants_sticky) { 38 if (wants_sticky) {
40 // move container to focused workspace 39 // move container to active workspace
41 struct sway_container *output = container_parent(container, C_OUTPUT); 40 struct sway_workspace *active_workspace =
42 struct sway_seat *seat = input_manager_current_seat(input_manager); 41 output_get_active_workspace(container->workspace->output);
43 struct sway_container *focus = seat_get_focus_inactive(seat, output); 42 if (container->workspace != active_workspace) {
44 struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); 43 struct sway_workspace *old_workspace = container->workspace;
45 struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); 44 container_detach(container);
46 if (current_workspace != focused_workspace) { 45 workspace_add_floating(active_workspace, container);
47 container_remove_child(container); 46 container_handle_fullscreen_reparent(container);
48 workspace_add_floating(focused_workspace, container); 47 arrange_workspace(active_workspace);
49 container_handle_fullscreen_reparent(container, current_workspace); 48 workspace_consider_destroy(old_workspace);
50 arrange_windows(focused_workspace);
51 if (!container_reap_empty(current_workspace)) {
52 arrange_windows(current_workspace);
53 }
54 } 49 }
55 } 50 }
56 51
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index f25c43a1..a0ffbda8 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -4,6 +4,7 @@
4#include "config.h" 4#include "config.h"
5#include "log.h" 5#include "log.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/output.h"
7#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
8#include "sway/tree/root.h" 9#include "sway/tree/root.h"
9#include "sway/tree/view.h" 10#include "sway/tree/view.h"
@@ -43,27 +44,28 @@ static void swap_focus(struct sway_container *con1,
43 struct sway_container *con2, struct sway_seat *seat, 44 struct sway_container *con2, struct sway_seat *seat,
44 struct sway_container *focus) { 45 struct sway_container *focus) {
45 if (focus == con1 || focus == con2) { 46 if (focus == con1 || focus == con2) {
46 struct sway_container *ws1 = container_parent(con1, C_WORKSPACE); 47 struct sway_workspace *ws1 = con1->workspace;
47 struct sway_container *ws2 = container_parent(con2, C_WORKSPACE); 48 struct sway_workspace *ws2 = con2->workspace;
48 if (focus == con1 && (con2->parent->layout == L_TABBED 49 enum sway_container_layout layout1 = container_parent_layout(con1);
49 || con2->parent->layout == L_STACKED)) { 50 enum sway_container_layout layout2 = container_parent_layout(con2);
51 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
50 if (workspace_is_visible(ws2)) { 52 if (workspace_is_visible(ws2)) {
51 seat_set_focus_warp(seat, con2, false, true); 53 seat_set_focus_warp(seat, &con2->node, false, true);
52 } 54 }
53 seat_set_focus(seat, ws1 != ws2 ? con2 : con1); 55 seat_set_focus(seat, ws1 != ws2 ? &con2->node : &con1->node);
54 } else if (focus == con2 && (con1->parent->layout == L_TABBED 56 } else if (focus == con2 && (layout1 == L_TABBED
55 || con1->parent->layout == L_STACKED)) { 57 || layout1 == L_STACKED)) {
56 if (workspace_is_visible(ws1)) { 58 if (workspace_is_visible(ws1)) {
57 seat_set_focus_warp(seat, con1, false, true); 59 seat_set_focus_warp(seat, &con1->node, false, true);
58 } 60 }
59 seat_set_focus(seat, ws1 != ws2 ? con1 : con2); 61 seat_set_focus(seat, ws1 != ws2 ? &con1->node : &con2->node);
60 } else if (ws1 != ws2) { 62 } else if (ws1 != ws2) {
61 seat_set_focus(seat, focus == con1 ? con2 : con1); 63 seat_set_focus(seat, focus == con1 ? &con2->node : &con1->node);
62 } else { 64 } else {
63 seat_set_focus(seat, focus); 65 seat_set_focus(seat, &focus->node);
64 } 66 }
65 } else { 67 } else {
66 seat_set_focus(seat, focus); 68 seat_set_focus(seat, &focus->node);
67 } 69 }
68} 70}
69 71
@@ -72,10 +74,6 @@ static void container_swap(struct sway_container *con1,
72 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { 74 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
73 return; 75 return;
74 } 76 }
75 if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER,
76 "Can only swap containers and views")) {
77 return;
78 }
79 if (!sway_assert(!container_has_ancestor(con1, con2) 77 if (!sway_assert(!container_has_ancestor(con1, con2)
80 && !container_has_ancestor(con2, con1), 78 && !container_has_ancestor(con2, con1),
81 "Cannot swap ancestor and descendant")) { 79 "Cannot swap ancestor and descendant")) {
@@ -87,10 +85,11 @@ static void container_swap(struct sway_container *con1,
87 return; 85 return;
88 } 86 }
89 87
90 wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); 88 wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu",
89 con1->node.id, con2->node.id);
91 90
92 int fs1 = con1->is_fullscreen; 91 bool fs1 = con1->is_fullscreen;
93 int fs2 = con2->is_fullscreen; 92 bool fs2 = con2->is_fullscreen;
94 if (fs1) { 93 if (fs1) {
95 container_set_fullscreen(con1, false); 94 container_set_fullscreen(con1, false);
96 } 95 }
@@ -99,13 +98,11 @@ static void container_swap(struct sway_container *con1,
99 } 98 }
100 99
101 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 100 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
102 struct sway_container *focus = seat_get_focus(seat); 101 struct sway_container *focus = seat_get_focused_container(seat);
103 struct sway_container *vis1 = container_parent( 102 struct sway_workspace *vis1 =
104 seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)), 103 output_get_active_workspace(con1->workspace->output);
105 C_WORKSPACE); 104 struct sway_workspace *vis2 =
106 struct sway_container *vis2 = container_parent( 105 output_get_active_workspace(con2->workspace->output);
107 seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)),
108 C_WORKSPACE);
109 106
110 char *stored_prev_name = NULL; 107 char *stored_prev_name = NULL;
111 if (prev_workspace_name) { 108 if (prev_workspace_name) {
@@ -115,10 +112,10 @@ static void container_swap(struct sway_container *con1,
115 swap_places(con1, con2); 112 swap_places(con1, con2);
116 113
117 if (!workspace_is_visible(vis1)) { 114 if (!workspace_is_visible(vis1)) {
118 seat_set_focus(seat, seat_get_focus_inactive(seat, vis1)); 115 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
119 } 116 }
120 if (!workspace_is_visible(vis2)) { 117 if (!workspace_is_visible(vis2)) {
121 seat_set_focus(seat, seat_get_focus_inactive(seat, vis2)); 118 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
122 } 119 }
123 120
124 swap_focus(con1, con2, seat, focus); 121 swap_focus(con1, con2, seat, focus);
@@ -137,23 +134,22 @@ static void container_swap(struct sway_container *con1,
137} 134}
138 135
139static bool test_con_id(struct sway_container *container, void *con_id) { 136static bool test_con_id(struct sway_container *container, void *con_id) {
140 return container->id == (size_t)con_id; 137 return container->node.id == (size_t)con_id;
141} 138}
142 139
143static bool test_id(struct sway_container *container, void *id) { 140static bool test_id(struct sway_container *container, void *id) {
144#ifdef HAVE_XWAYLAND 141#ifdef HAVE_XWAYLAND
145 xcb_window_t *wid = id; 142 xcb_window_t *wid = id;
146 return (container->type == C_VIEW 143 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
147 && container->sway_view->type == SWAY_VIEW_XWAYLAND 144 && container->view->wlr_xwayland_surface->window_id == *wid);
148 && container->sway_view->wlr_xwayland_surface->window_id == *wid);
149#else 145#else
150 return false; 146 return false;
151#endif 147#endif
152} 148}
153 149
154static bool test_mark(struct sway_container *container, void *mark) { 150static bool test_mark(struct sway_container *container, void *mark) {
155 if (container->type == C_VIEW && container->sway_view->marks->length) { 151 if (container->view && container->view->marks->length) {
156 return !list_seq_find(container->sway_view->marks, 152 return !list_seq_find(container->view->marks,
157 (int (*)(const void *, const void *))strcmp, mark); 153 (int (*)(const void *, const void *))strcmp, mark);
158 } 154 }
159 return false; 155 return false;
@@ -169,7 +165,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
169 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); 165 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
170 } 166 }
171 167
172 struct sway_container *current = config->handler_context.current_container; 168 struct sway_container *current = config->handler_context.container;
173 struct sway_container *other; 169 struct sway_container *other;
174 170
175 char *value = join_args(argv + 3, argc - 3); 171 char *value = join_args(argv + 3, argc - 3);
@@ -191,7 +187,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
191 if (!other) { 187 if (!other) {
192 error = cmd_results_new(CMD_FAILURE, "swap", 188 error = cmd_results_new(CMD_FAILURE, "swap",
193 "Failed to find %s '%s'", argv[2], value); 189 "Failed to find %s '%s'", argv[2], value);
194 } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) { 190 } else if (!current) {
195 error = cmd_results_new(CMD_FAILURE, "swap", 191 error = cmd_results_new(CMD_FAILURE, "swap",
196 "Can only swap with containers and views"); 192 "Can only swap with containers and views");
197 } else if (container_has_ancestor(current, other) 193 } else if (container_has_ancestor(current, other)
@@ -211,9 +207,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
211 207
212 container_swap(current, other); 208 container_swap(current, other);
213 209
214 arrange_windows(current->parent); 210 arrange_node(node_get_parent(&current->node));
215 if (other->parent != current->parent) { 211 if (node_get_parent(&other->node) != node_get_parent(&current->node)) {
216 arrange_windows(other->parent); 212 arrange_node(node_get_parent(&other->node));
217 } 213 }
218 214
219 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 215 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index 3d1c578c..c9ffe8fa 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -11,13 +11,12 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
11 if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) { 11 if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct sway_container *container = 14 struct sway_container *container = config->handler_context.container;
15 config->handler_context.current_container; 15 if (!container->view) {
16 if (container->type != C_VIEW) {
17 return cmd_results_new(CMD_INVALID, "title_format", 16 return cmd_results_new(CMD_INVALID, "title_format",
18 "Only views can have a title_format"); 17 "Only views can have a title_format");
19 } 18 }
20 struct sway_view *view = container->sway_view; 19 struct sway_view *view = container->view;
21 char *format = join_args(argv, argc); 20 char *format = join_args(argv, argc);
22 if (view->title_format) { 21 if (view->title_format) {
23 free(view->title_format); 22 free(view->title_format);
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index 62127c97..c6251dc8 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -9,9 +9,9 @@
9#include "stringop.h" 9#include "stringop.h"
10 10
11static void remove_all_marks_iterator(struct sway_container *con, void *data) { 11static void remove_all_marks_iterator(struct sway_container *con, void *data) {
12 if (con->type == C_VIEW) { 12 if (con->view) {
13 view_clear_marks(con->sway_view); 13 view_clear_marks(con->view);
14 view_update_marks_textures(con->sway_view); 14 view_update_marks_textures(con->view);
15 } 15 }
16} 16}
17 17
@@ -24,13 +24,12 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
24 // Determine the view 24 // Determine the view
25 struct sway_view *view = NULL; 25 struct sway_view *view = NULL;
26 if (config->handler_context.using_criteria) { 26 if (config->handler_context.using_criteria) {
27 struct sway_container *container = 27 struct sway_container *container = config->handler_context.container;
28 config->handler_context.current_container; 28 if (!container->view) {
29 if (container->type != C_VIEW) {
30 return cmd_results_new(CMD_INVALID, "unmark", 29 return cmd_results_new(CMD_INVALID, "unmark",
31 "Only views can have marks"); 30 "Only views can have marks");
32 } 31 }
33 view = container->sway_view; 32 view = container->view;
34 } 33 }
35 34
36 // Determine the mark 35 // Determine the mark
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c
index bccb33fe..53c37d4d 100644
--- a/sway/commands/urgent.c
+++ b/sway/commands/urgent.c
@@ -11,13 +11,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) {
11 if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { 11 if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct sway_container *container = 14 struct sway_container *container = config->handler_context.container;
15 config->handler_context.current_container; 15 if (!container->view) {
16 if (container->type != C_VIEW) {
17 return cmd_results_new(CMD_INVALID, "urgent", 16 return cmd_results_new(CMD_INVALID, "urgent",
18 "Only views can be urgent"); 17 "Only views can be urgent");
19 } 18 }
20 struct sway_view *view = container->sway_view; 19 struct sway_view *view = container->view;
21 20
22 if (strcmp(argv[0], "allow") == 0) { 21 if (strcmp(argv[0], "allow") == 0) {
23 view->allow_request_urgent = true; 22 view->allow_request_urgent = true;
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index ceb4cd6e..f026a39d 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -58,7 +58,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
58 } 58 }
59 59
60 60
61 struct sway_container *ws = NULL; 61 struct sway_workspace *ws = NULL;
62 if (strcasecmp(argv[0], "number") == 0) { 62 if (strcasecmp(argv[0], "number") == 0) {
63 if (argc < 2) { 63 if (argc < 2) {
64 return cmd_results_new(CMD_INVALID, "workspace", 64 return cmd_results_new(CMD_INVALID, "workspace",
diff --git a/sway/config.c b/sway/config.c
index 8105722a..89701640 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -825,6 +825,6 @@ void config_update_font_height(bool recalculate) {
825 root_for_each_container(find_font_height_iterator, &recalculate); 825 root_for_each_container(find_font_height_iterator, &recalculate);
826 826
827 if (config->font_height != prev_max_height) { 827 if (config->font_height != prev_max_height) {
828 arrange_windows(&root_container); 828 arrange_root();
829 } 829 }
830} 830}
diff --git a/sway/config/bar.c b/sway/config/bar.c
index ae9383d6..f83b37d1 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -12,6 +12,7 @@
12#include <strings.h> 12#include <strings.h>
13#include <signal.h> 13#include <signal.h>
14#include "sway/config.h" 14#include "sway/config.h"
15#include "sway/output.h"
15#include "stringop.h" 16#include "stringop.h"
16#include "list.h" 17#include "list.h"
17#include "log.h" 18#include "log.h"
@@ -218,17 +219,6 @@ void invoke_swaybar(struct bar_config *bar) {
218 close(filedes[1]); 219 close(filedes[1]);
219} 220}
220 221
221static bool active_output(const char *name) {
222 struct sway_container *cont = NULL;
223 for (int i = 0; i < root_container.children->length; ++i) {
224 cont = root_container.children->items[i];
225 if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
226 return true;
227 }
228 }
229 return false;
230}
231
232void load_swaybars() { 222void load_swaybars() {
233 for (int i = 0; i < config->bars->length; ++i) { 223 for (int i = 0; i < config->bars->length; ++i) {
234 struct bar_config *bar = config->bars->items[i]; 224 struct bar_config *bar = config->bars->items[i];
@@ -236,7 +226,7 @@ void load_swaybars() {
236 if (bar->outputs) { 226 if (bar->outputs) {
237 for (int j = 0; j < bar->outputs->length; ++j) { 227 for (int j = 0; j < bar->outputs->length; ++j) {
238 char *o = bar->outputs->items[j]; 228 char *o = bar->outputs->items[j];
239 if (!strcmp(o, "*") || active_output(o)) { 229 if (!strcmp(o, "*") || output_by_name(o)) {
240 apply = true; 230 apply = true;
241 break; 231 break;
242 } 232 }
diff --git a/sway/config/output.c b/sway/config/output.c
index 65f09258..aa53fc46 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -174,21 +174,16 @@ void terminate_swaybg(pid_t pid) {
174 } 174 }
175} 175}
176 176
177void apply_output_config(struct output_config *oc, struct sway_container *output) { 177void apply_output_config(struct output_config *oc, struct sway_output *output) {
178 assert(output->type == C_OUTPUT); 178 struct wlr_output *wlr_output = output->wlr_output;
179
180 struct wlr_output_layout *output_layout =
181 root_container.sway_root->output_layout;
182 struct wlr_output *wlr_output = output->sway_output->wlr_output;
183 179
184 if (oc && oc->enabled == 0) { 180 if (oc && oc->enabled == 0) {
185 if (output->sway_output->bg_pid != 0) { 181 if (output->bg_pid != 0) {
186 terminate_swaybg(output->sway_output->bg_pid); 182 terminate_swaybg(output->bg_pid);
187 output->sway_output->bg_pid = 0; 183 output->bg_pid = 0;
188 } 184 }
189 output_begin_destroy(output); 185 output_disable(output);
190 wlr_output_layout_remove(root_container.sway_root->output_layout, 186 wlr_output_layout_remove(root->output_layout, wlr_output);
191 wlr_output);
192 return; 187 return;
193 } 188 }
194 189
@@ -213,21 +208,21 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
213 // Find position for it 208 // Find position for it
214 if (oc && (oc->x != -1 || oc->y != -1)) { 209 if (oc && (oc->x != -1 || oc->y != -1)) {
215 wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); 210 wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
216 wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); 211 wlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y);
217 } else { 212 } else {
218 wlr_output_layout_add_auto(output_layout, wlr_output); 213 wlr_output_layout_add_auto(root->output_layout, wlr_output);
219 } 214 }
220 215
221 int output_i; 216 int output_i;
222 for (output_i = 0; output_i < root_container.children->length; ++output_i) { 217 for (output_i = 0; output_i < root->outputs->length; ++output_i) {
223 if (root_container.children->items[output_i] == output) { 218 if (root->outputs->items[output_i] == output) {
224 break; 219 break;
225 } 220 }
226 } 221 }
227 222
228 if (oc && oc->background) { 223 if (oc && oc->background) {
229 if (output->sway_output->bg_pid != 0) { 224 if (output->bg_pid != 0) {
230 terminate_swaybg(output->sway_output->bg_pid); 225 terminate_swaybg(output->bg_pid);
231 } 226 }
232 227
233 wlr_log(WLR_DEBUG, "Setting background for output %d to %s", 228 wlr_log(WLR_DEBUG, "Setting background for output %d to %s",
@@ -249,8 +244,8 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
249 wlr_log(WLR_DEBUG, "-> %s", command); 244 wlr_log(WLR_DEBUG, "-> %s", command);
250 245
251 char *const cmd[] = { "sh", "-c", command, NULL }; 246 char *const cmd[] = { "sh", "-c", command, NULL };
252 output->sway_output->bg_pid = fork(); 247 output->bg_pid = fork();
253 if (output->sway_output->bg_pid == 0) { 248 if (output->bg_pid == 0) {
254 execvp(cmd[0], cmd); 249 execvp(cmd[0], cmd);
255 } else { 250 } else {
256 free(command); 251 free(command);
@@ -293,12 +288,11 @@ void apply_output_config_to_outputs(struct output_config *oc) {
293 bool wildcard = strcmp(oc->name, "*") == 0; 288 bool wildcard = strcmp(oc->name, "*") == 0;
294 char id[128]; 289 char id[128];
295 struct sway_output *sway_output; 290 struct sway_output *sway_output;
296 wl_list_for_each(sway_output, 291 wl_list_for_each(sway_output, &root->all_outputs, link) {
297 &root_container.sway_root->all_outputs, link) {
298 char *name = sway_output->wlr_output->name; 292 char *name = sway_output->wlr_output->name;
299 output_get_identifier(id, sizeof(id), sway_output); 293 output_get_identifier(id, sizeof(id), sway_output);
300 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { 294 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
301 if (!sway_output->swayc) { 295 if (!sway_output->enabled) {
302 if (!oc->enabled) { 296 if (!oc->enabled) {
303 if (!wildcard) { 297 if (!wildcard) {
304 break; 298 break;
@@ -306,7 +300,7 @@ void apply_output_config_to_outputs(struct output_config *oc) {
306 continue; 300 continue;
307 } 301 }
308 302
309 output_enable(sway_output); 303 output_enable(sway_output, oc);
310 } 304 }
311 305
312 struct output_config *current = oc; 306 struct output_config *current = oc;
@@ -316,7 +310,7 @@ void apply_output_config_to_outputs(struct output_config *oc) {
316 current = tmp; 310 current = tmp;
317 } 311 }
318 } 312 }
319 apply_output_config(current, sway_output->swayc); 313 apply_output_config(current, sway_output);
320 314
321 if (!wildcard) { 315 if (!wildcard) {
322 // Stop looking if the output config isn't applicable to all 316 // Stop looking if the output config isn't applicable to all
@@ -354,8 +348,7 @@ static void default_output_config(struct output_config *oc,
354 348
355void create_default_output_configs(void) { 349void create_default_output_configs(void) {
356 struct sway_output *sway_output; 350 struct sway_output *sway_output;
357 wl_list_for_each(sway_output, 351 wl_list_for_each(sway_output, &root->all_outputs, link) {
358 &root_container.sway_root->all_outputs, link) {
359 char *name = sway_output->wlr_output->name; 352 char *name = sway_output->wlr_output->name;
360 struct output_config *oc = new_output_config(name); 353 struct output_config *oc = new_output_config(name);
361 default_output_config(oc, sway_output->wlr_output); 354 default_output_config(oc, sway_output->wlr_output);
diff --git a/sway/criteria.c b/sway/criteria.c
index feca904a..e5b308e0 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -9,6 +9,7 @@
9#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/tree/root.h" 10#include "sway/tree/root.h"
11#include "sway/tree/view.h" 11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
12#include "stringop.h" 13#include "stringop.h"
13#include "list.h" 14#include "list.h"
14#include "log.h" 15#include "log.h"
@@ -87,12 +88,12 @@ static int cmp_urgent(const void *_a, const void *_b) {
87 return 0; 88 return 0;
88} 89}
89 90
90static void find_urgent_iterator(struct sway_container *swayc, void *data) { 91static void find_urgent_iterator(struct sway_container *con, void *data) {
91 if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { 92 if (!con->view || !view_is_urgent(con->view)) {
92 return; 93 return;
93 } 94 }
94 list_t *urgent_views = data; 95 list_t *urgent_views = data;
95 list_add(urgent_views, swayc->sway_view); 96 list_add(urgent_views, con->view);
96} 97}
97 98
98static bool criteria_matches_view(struct criteria *criteria, 99static bool criteria_matches_view(struct criteria *criteria,
@@ -132,7 +133,7 @@ static bool criteria_matches_view(struct criteria *criteria,
132 } 133 }
133 134
134 if (criteria->con_id) { // Internal ID 135 if (criteria->con_id) { // Internal ID
135 if (!view->swayc || view->swayc->id != criteria->con_id) { 136 if (!view->container || view->container->node.id != criteria->con_id) {
136 return false; 137 return false;
137 } 138 }
138 } 139 }
@@ -174,13 +175,13 @@ static bool criteria_matches_view(struct criteria *criteria,
174#endif 175#endif
175 176
176 if (criteria->floating) { 177 if (criteria->floating) {
177 if (!container_is_floating(view->swayc)) { 178 if (!container_is_floating(view->container)) {
178 return false; 179 return false;
179 } 180 }
180 } 181 }
181 182
182 if (criteria->tiling) { 183 if (criteria->tiling) {
183 if (container_is_floating(view->swayc)) { 184 if (container_is_floating(view->container)) {
184 return false; 185 return false;
185 } 186 }
186 } 187 }
@@ -205,10 +206,7 @@ static bool criteria_matches_view(struct criteria *criteria,
205 } 206 }
206 207
207 if (criteria->workspace) { 208 if (criteria->workspace) {
208 if (!view->swayc) { 209 struct sway_workspace *ws = view->container->workspace;
209 return false;
210 }
211 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
212 if (!ws || strcmp(ws->name, criteria->workspace) != 0) { 210 if (!ws || strcmp(ws->name, criteria->workspace) != 0) {
213 return false; 211 return false;
214 } 212 }
@@ -237,9 +235,9 @@ struct match_data {
237static void criteria_get_views_iterator(struct sway_container *container, 235static void criteria_get_views_iterator(struct sway_container *container,
238 void *data) { 236 void *data) {
239 struct match_data *match_data = data; 237 struct match_data *match_data = data;
240 if (container->type == C_VIEW) { 238 if (!container->view) {
241 if (criteria_matches_view(match_data->criteria, container->sway_view)) { 239 if (criteria_matches_view(match_data->criteria, container->view)) {
242 list_add(match_data->matches, container->sway_view); 240 list_add(match_data->matches, container->view);
243 } 241 }
244 } 242 }
245} 243}
@@ -355,12 +353,12 @@ static enum criteria_token token_from_name(char *name) {
355 */ 353 */
356static char *get_focused_prop(enum criteria_token token) { 354static char *get_focused_prop(enum criteria_token token) {
357 struct sway_seat *seat = input_manager_current_seat(input_manager); 355 struct sway_seat *seat = input_manager_current_seat(input_manager);
358 struct sway_container *focus = seat_get_focus(seat); 356 struct sway_container *focus = seat_get_focused_container(seat);
359 357
360 if (!focus || focus->type != C_VIEW) { 358 if (!focus || !focus->view) {
361 return NULL; 359 return NULL;
362 } 360 }
363 struct sway_view *view = focus->sway_view; 361 struct sway_view *view = focus->view;
364 const char *value = NULL; 362 const char *value = NULL;
365 363
366 switch (token) { 364 switch (token) {
@@ -374,18 +372,15 @@ static char *get_focused_prop(enum criteria_token token) {
374 value = view_get_title(view); 372 value = view_get_title(view);
375 break; 373 break;
376 case T_WORKSPACE: 374 case T_WORKSPACE:
377 { 375 if (focus->workspace) {
378 struct sway_container *ws = container_parent(focus, C_WORKSPACE); 376 value = focus->workspace->name;
379 if (ws) {
380 value = ws->name;
381 }
382 } 377 }
383 break; 378 break;
384 case T_CON_ID: 379 case T_CON_ID:
385 if (view->swayc == NULL) { 380 if (view->container == NULL) {
386 return NULL; 381 return NULL;
387 } 382 }
388 size_t id = view->swayc->id; 383 size_t id = view->container->node.id;
389 size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; 384 size_t id_size = snprintf(NULL, 0, "%zu", id) + 1;
390 char *id_str = malloc(id_size); 385 char *id_str = malloc(id_size);
391 snprintf(id_str, id_size, "%zu", id); 386 snprintf(id_str, id_size, "%zu", id);
diff --git a/sway/debug-tree.c b/sway/debug-tree.c
index 2768cf58..2ed3c087 100644
--- a/sway/debug-tree.c
+++ b/sway/debug-tree.c
@@ -10,6 +10,7 @@
10#include "sway/server.h" 10#include "sway/server.h"
11#include "sway/tree/container.h" 11#include "sway/tree/container.h"
12#include "sway/tree/root.h" 12#include "sway/tree/root.h"
13#include "sway/tree/workspace.h"
13#include "cairo.h" 14#include "cairo.h"
14#include "config.h" 15#include "config.h"
15#include "pango.h" 16#include "pango.h"
@@ -32,28 +33,78 @@ static const char *layout_to_str(enum sway_container_layout layout) {
32 return "L_NONE"; 33 return "L_NONE";
33} 34}
34 35
35static int draw_container(cairo_t *cairo, struct sway_container *container, 36static char *get_string(struct sway_node *node) {
36 struct sway_container *focus, int x, int y) { 37 char *buffer = malloc(512);
38 switch (node->type) {
39 case N_ROOT:
40 snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id,
41 root->width, root->height, root->x, root->y);
42 break;
43 case N_OUTPUT:
44 snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id,
45 node->sway_output->wlr_output->name,
46 node->sway_output->wlr_output->width,
47 node->sway_output->wlr_output->height,
48 node->sway_output->wlr_output->lx,
49 node->sway_output->wlr_output->ly);
50 break;
51 case N_WORKSPACE:
52 snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f",
53 node->id, node->sway_workspace->name,
54 layout_to_str(node->sway_workspace->layout),
55 node->sway_workspace->width, node->sway_workspace->height,
56 node->sway_workspace->x, node->sway_workspace->y);
57 break;
58 case N_CONTAINER:
59 snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f",
60 node->id, node->sway_container->title,
61 layout_to_str(node->sway_container->layout),
62 node->sway_container->width, node->sway_container->height,
63 node->sway_container->x, node->sway_container->y);
64 break;
65 }
66 return buffer;
67}
68
69static list_t *get_children(struct sway_node *node) {
70 switch (node->type) {
71 case N_ROOT:
72 return root->outputs;
73 case N_OUTPUT:
74 return node->sway_output->workspaces;
75 case N_WORKSPACE:
76 return node->sway_workspace->tiling;
77 case N_CONTAINER:
78 return node->sway_container->children;
79 }
80 return NULL;
81}
82
83static int draw_node(cairo_t *cairo, struct sway_node *node,
84 struct sway_node *focus, int x, int y) {
37 int text_width, text_height; 85 int text_width, text_height;
86 char *buffer = get_string(node);
38 get_text_size(cairo, "monospace", &text_width, &text_height, 87 get_text_size(cairo, "monospace", &text_width, &text_height,
39 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", 88 1, false, buffer);
40 container_type_to_str(container->type), container->id, container->name,
41 layout_to_str(container->layout),
42 container->width, container->height, container->x, container->y);
43 cairo_save(cairo); 89 cairo_save(cairo);
44 cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); 90 cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
45 cairo_set_source_u32(cairo, 0xFFFFFFE0); 91 cairo_set_source_u32(cairo, 0xFFFFFFE0);
46 cairo_fill(cairo); 92 cairo_fill(cairo);
47 int height = text_height; 93 int height = text_height;
48 if (container->children) { 94 list_t *children = get_children(node);
49 for (int i = 0; i < container->children->length; ++i) { 95 if (children) {
50 struct sway_container *child = container->children->items[i]; 96 for (int i = 0; i < children->length; ++i) {
51 if (child->parent == container) { 97 // This is really dirty - the list contains specific structs but
98 // we're casting them as nodes. This works because node is the first
99 // item in each specific struct. This is acceptable because this is
100 // debug code.
101 struct sway_node *child = children->items[i];
102 if (node_get_parent(child) == node) {
52 cairo_set_source_u32(cairo, 0x000000FF); 103 cairo_set_source_u32(cairo, 0x000000FF);
53 } else { 104 } else {
54 cairo_set_source_u32(cairo, 0xFF0000FF); 105 cairo_set_source_u32(cairo, 0xFF0000FF);
55 } 106 }
56 height += draw_container(cairo, child, focus, x + 10, y + height); 107 height += draw_node(cairo, child, focus, x + 10, y + height);
57 } 108 }
58 } 109 }
59 cairo_set_source_u32(cairo, 0xFFFFFFE0); 110 cairo_set_source_u32(cairo, 0xFFFFFFE0);
@@ -61,13 +112,11 @@ static int draw_container(cairo_t *cairo, struct sway_container *container,
61 cairo_fill(cairo); 112 cairo_fill(cairo);
62 cairo_restore(cairo); 113 cairo_restore(cairo);
63 cairo_move_to(cairo, x, y); 114 cairo_move_to(cairo, x, y);
64 if (focus == container) { 115 if (focus == node) {
65 cairo_set_source_u32(cairo, 0x0000FFFF); 116 cairo_set_source_u32(cairo, 0x0000FFFF);
66 } 117 }
67 pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", 118 pango_printf(cairo, "monospace", 1, false, buffer);
68 container_type_to_str(container->type), container->id, container->name, 119 free(buffer);
69 layout_to_str(container->layout),
70 container->width, container->height, container->x, container->y);
71 return height; 120 return height;
72} 121}
73 122
@@ -77,13 +126,13 @@ void update_debug_tree() {
77 } 126 }
78 127
79 int width = 640, height = 480; 128 int width = 640, height = 480;
80 for (int i = 0; i < root_container.children->length; ++i) { 129 for (int i = 0; i < root->outputs->length; ++i) {
81 struct sway_container *container = root_container.children->items[i]; 130 struct sway_output *output = root->outputs->items[i];
82 if (container->width > width) { 131 if (output->wlr_output->width > width) {
83 width = container->width; 132 width = output->wlr_output->width;
84 } 133 }
85 if (container->height > height) { 134 if (output->wlr_output->height > height) {
86 height = container->height; 135 height = output->wlr_output->height;
87 } 136 }
88 } 137 }
89 cairo_surface_t *surface = 138 cairo_surface_t *surface =
@@ -91,28 +140,22 @@ void update_debug_tree() {
91 cairo_t *cairo = cairo_create(surface); 140 cairo_t *cairo = cairo_create(surface);
92 PangoContext *pango = pango_cairo_create_context(cairo); 141 PangoContext *pango = pango_cairo_create_context(cairo);
93 142
94 struct sway_seat *seat = NULL; 143 struct sway_seat *seat = input_manager_current_seat(input_manager);
95 wl_list_for_each(seat, &input_manager->seats, link) { 144 struct sway_node *focus = seat_get_focus(seat);
96 break;
97 }
98 145
99 struct sway_container *focus = NULL;
100 if (seat != NULL) {
101 focus = seat_get_focus(seat);
102 }
103 cairo_set_source_u32(cairo, 0x000000FF); 146 cairo_set_source_u32(cairo, 0x000000FF);
104 draw_container(cairo, &root_container, focus, 0, 0); 147 draw_node(cairo, &root->node, focus, 0, 0);
105 148
106 cairo_surface_flush(surface); 149 cairo_surface_flush(surface);
107 struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); 150 struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
108 if (root_container.sway_root->debug_tree) { 151 if (root->debug_tree) {
109 wlr_texture_destroy(root_container.sway_root->debug_tree); 152 wlr_texture_destroy(root->debug_tree);
110 } 153 }
111 unsigned char *data = cairo_image_surface_get_data(surface); 154 unsigned char *data = cairo_image_surface_get_data(surface);
112 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 155 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
113 struct wlr_texture *texture = wlr_texture_from_pixels(renderer, 156 struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
114 WL_SHM_FORMAT_ARGB8888, stride, width, height, data); 157 WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
115 root_container.sway_root->debug_tree = texture; 158 root->debug_tree = texture;
116 cairo_surface_destroy(surface); 159 cairo_surface_destroy(surface);
117 g_object_unref(pango); 160 g_object_unref(pango);
118 cairo_destroy(cairo); 161 cairo_destroy(cairo);
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index 72650397..771b58fe 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -4,37 +4,32 @@
4 4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, 5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) { 6 bool whole) {
7 for (int i = 0; i < root_container.children->length; ++i) { 7 for (int i = 0; i < root->outputs->length; ++i) {
8 struct sway_container *cont = root_container.children->items[i]; 8 struct sway_output *output = root->outputs->items[i];
9 if (cont->type == C_OUTPUT) { 9 output_damage_surface(output, lx - output->wlr_output->lx,
10 output_damage_surface(cont->sway_output, 10 ly - output->wlr_output->ly, surface, whole);
11 lx - cont->current.swayc_x, ly - cont->current.swayc_y,
12 surface, whole);
13 }
14 } 11 }
15} 12}
16 13
17void desktop_damage_whole_container(struct sway_container *con) { 14void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root_container.children->length; ++i) { 15 for (int i = 0; i < root->outputs->length; ++i) {
19 struct sway_container *cont = root_container.children->items[i]; 16 struct sway_output *output = root->outputs->items[i];
20 if (cont->type == C_OUTPUT) { 17 output_damage_whole_container(output, con);
21 output_damage_whole_container(cont->sway_output, con);
22 }
23 } 18 }
24} 19}
25 20
26void desktop_damage_box(struct wlr_box *box) { 21void desktop_damage_box(struct wlr_box *box) {
27 for (int i = 0; i < root_container.children->length; ++i) { 22 for (int i = 0; i < root->outputs->length; ++i) {
28 struct sway_container *cont = root_container.children->items[i]; 23 struct sway_output *output = root->outputs->items[i];
29 output_damage_box(cont->sway_output, box); 24 output_damage_box(output, box);
30 } 25 }
31} 26}
32 27
33void desktop_damage_view(struct sway_view *view) { 28void desktop_damage_view(struct sway_view *view) {
34 desktop_damage_whole_container(view->swayc); 29 desktop_damage_whole_container(view->container);
35 struct wlr_box box = { 30 struct wlr_box box = {
36 .x = view->swayc->current.view_x - view->geometry.x, 31 .x = view->container->current.view_x - view->geometry.x,
37 .y = view->swayc->current.view_y - view->geometry.y, 32 .y = view->container->current.view_y - view->geometry.y,
38 .width = view->surface->current.width, 33 .width = view->surface->current.width,
39 .height = view->surface->current.height, 34 .height = view->surface->current.height,
40 }; 35 };
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index a4f7f928..7d254173 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -14,6 +14,7 @@
14#include "sway/output.h" 14#include "sway/output.h"
15#include "sway/server.h" 15#include "sway/server.h"
16#include "sway/tree/arrange.h" 16#include "sway/tree/arrange.h"
17#include "sway/tree/workspace.h"
17#include "log.h" 18#include "log.h"
18 19
19static void apply_exclusive(struct wlr_box *usable_area, 20static void apply_exclusive(struct wlr_box *usable_area,
@@ -176,7 +177,7 @@ void arrange_layers(struct sway_output *output) {
176 sizeof(struct wlr_box)) != 0) { 177 sizeof(struct wlr_box)) != 0) {
177 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); 178 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 179 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_output(output->swayc); 180 arrange_output(output);
180 } 181 }
181 182
182 // Arrange non-exlusive surfaces from top->bottom 183 // Arrange non-exlusive surfaces from top->bottom
@@ -256,7 +257,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {
256 return; 257 return;
257 } 258 }
258 struct sway_output *output = wlr_output->data; 259 struct sway_output *output = wlr_output->data;
259 if (output == NULL || output->swayc == NULL) { 260 if (output == NULL) {
260 return; 261 return;
261 } 262 }
262 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 263 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
@@ -283,9 +284,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
283 wl_list_remove(&sway_layer->surface_commit.link); 284 wl_list_remove(&sway_layer->surface_commit.link);
284 if (sway_layer->layer_surface->output != NULL) { 285 if (sway_layer->layer_surface->output != NULL) {
285 struct sway_output *output = sway_layer->layer_surface->output->data; 286 struct sway_output *output = sway_layer->layer_surface->output->data;
286 if (output != NULL && output->swayc != NULL) { 287 if (output != NULL) {
287 arrange_layers(output); 288 arrange_layers(output);
288 arrange_windows(output->swayc); 289 arrange_output(output);
289 transaction_commit_dirty(); 290 transaction_commit_dirty();
290 } 291 }
291 wl_list_remove(&sway_layer->output_destroy.link); 292 wl_list_remove(&sway_layer->output_destroy.link);
@@ -332,23 +333,21 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
332 333
333 if (!layer_surface->output) { 334 if (!layer_surface->output) {
334 // Assign last active output 335 // Assign last active output
335 struct sway_container *output = NULL; 336 struct sway_output *output = NULL;
336 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 337 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
337 if (seat) { 338 if (seat) {
338 output = seat_get_focus_inactive(seat, &root_container); 339 struct sway_workspace *ws = seat_get_focused_workspace(seat);
340 output = ws->output;
339 } 341 }
340 if (!output) { 342 if (!output) {
341 if (!sway_assert(root_container.children->length, 343 if (!sway_assert(root->outputs->length,
342 "cannot auto-assign output for layer")) { 344 "cannot auto-assign output for layer")) {
343 wlr_layer_surface_close(layer_surface); 345 wlr_layer_surface_close(layer_surface);
344 return; 346 return;
345 } 347 }
346 output = root_container.children->items[0]; 348 output = root->outputs->items[0];
347 } 349 }
348 if (output->type != C_OUTPUT) { 350 layer_surface->output = output->wlr_output;
349 output = container_parent(output, C_OUTPUT);
350 }
351 layer_surface->output = output->sway_output->wlr_output;
352 } 351 }
353 352
354 struct sway_layer_surface *sway_layer = 353 struct sway_layer_surface *sway_layer =
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index c30e52a1..c182bad6 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -28,10 +28,10 @@
28#include "sway/tree/view.h" 28#include "sway/tree/view.h"
29#include "sway/tree/workspace.h" 29#include "sway/tree/workspace.h"
30 30
31struct sway_container *output_by_name(const char *name) { 31struct sway_output *output_by_name(const char *name) {
32 for (int i = 0; i < root_container.children->length; ++i) { 32 for (int i = 0; i < root->outputs->length; ++i) {
33 struct sway_container *output = root_container.children->items[i]; 33 struct sway_output *output = root->outputs->items[i];
34 if (strcasecmp(output->name, name) == 0) { 34 if (strcasecmp(output->wlr_output->name, name) == 0) {
35 return output; 35 return output;
36 } 36 }
37 } 37 }
@@ -98,8 +98,8 @@ static bool get_surface_box(struct surface_iterator_data *data,
98 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); 98 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box);
99 99
100 struct wlr_box output_box = { 100 struct wlr_box output_box = {
101 .width = output->swayc->current.swayc_width, 101 .width = output->wlr_output->width,
102 .height = output->swayc->current.swayc_height, 102 .height = output->wlr_output->height,
103 }; 103 };
104 104
105 struct wlr_box intersection; 105 struct wlr_box intersection;
@@ -145,12 +145,12 @@ void output_view_for_each_surface(struct sway_output *output,
145 .user_iterator = iterator, 145 .user_iterator = iterator,
146 .user_data = user_data, 146 .user_data = user_data,
147 .output = output, 147 .output = output,
148 .ox = view->swayc->current.view_x - output->swayc->current.swayc_x 148 .ox = view->container->current.view_x - output->wlr_output->lx
149 - view->geometry.x, 149 - view->geometry.x,
150 .oy = view->swayc->current.view_y - output->swayc->current.swayc_y 150 .oy = view->container->current.view_y - output->wlr_output->ly
151 - view->geometry.y, 151 - view->geometry.y,
152 .width = view->swayc->current.view_width, 152 .width = view->container->current.view_width,
153 .height = view->swayc->current.view_height, 153 .height = view->container->current.view_height,
154 .rotation = 0, // TODO 154 .rotation = 0, // TODO
155 }; 155 };
156 156
@@ -164,12 +164,12 @@ void output_view_for_each_popup(struct sway_output *output,
164 .user_iterator = iterator, 164 .user_iterator = iterator,
165 .user_data = user_data, 165 .user_data = user_data,
166 .output = output, 166 .output = output,
167 .ox = view->swayc->current.view_x - output->swayc->current.swayc_x 167 .ox = view->container->current.view_x - output->wlr_output->lx
168 - view->geometry.x, 168 - view->geometry.x,
169 .oy = view->swayc->current.view_y - output->swayc->current.swayc_y 169 .oy = view->container->current.view_y - output->wlr_output->ly
170 - view->geometry.y, 170 - view->geometry.y,
171 .width = view->swayc->current.view_width, 171 .width = view->container->current.view_width,
172 .height = view->swayc->current.view_height, 172 .height = view->container->current.view_height,
173 .rotation = 0, // TODO 173 .rotation = 0, // TODO
174 }; 174 };
175 175
@@ -197,8 +197,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output,
197 wl_list_for_each(unmanaged_surface, unmanaged, link) { 197 wl_list_for_each(unmanaged_surface, unmanaged, link) {
198 struct wlr_xwayland_surface *xsurface = 198 struct wlr_xwayland_surface *xsurface =
199 unmanaged_surface->wlr_xwayland_surface; 199 unmanaged_surface->wlr_xwayland_surface;
200 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; 200 double ox = unmanaged_surface->lx - output->wlr_output->lx;
201 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; 201 double oy = unmanaged_surface->ly - output->wlr_output->ly;
202 202
203 output_surface_for_each_surface(output, xsurface->surface, ox, oy, 203 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
204 iterator, user_data); 204 iterator, user_data);
@@ -211,8 +211,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output,
211 void *user_data) { 211 void *user_data) {
212 struct sway_drag_icon *drag_icon; 212 struct sway_drag_icon *drag_icon;
213 wl_list_for_each(drag_icon, drag_icons, link) { 213 wl_list_for_each(drag_icon, drag_icons, link) {
214 double ox = drag_icon->x - output->swayc->x; 214 double ox = drag_icon->x - output->wlr_output->lx;
215 double oy = drag_icon->y - output->swayc->y; 215 double oy = drag_icon->y - output->wlr_output->ly;
216 216
217 if (drag_icon->wlr_drag_icon->mapped) { 217 if (drag_icon->wlr_drag_icon->mapped) {
218 output_surface_for_each_surface(output, 218 output_surface_for_each_surface(output,
@@ -229,19 +229,13 @@ static void scale_box(struct wlr_box *box, float scale) {
229 box->height *= scale; 229 box->height *= scale;
230} 230}
231 231
232struct sway_container *output_get_active_workspace(struct sway_output *output) { 232struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
233 struct sway_seat *seat = input_manager_current_seat(input_manager); 233 struct sway_seat *seat = input_manager_current_seat(input_manager);
234 struct sway_container *focus = 234 struct sway_node *focus = seat_get_active_child(seat, &output->node);
235 seat_get_focus_inactive(seat, output->swayc);
236 if (!focus) { 235 if (!focus) {
237 // We've never been to this output before 236 return output->workspaces->items[0];
238 focus = output->swayc->current.children->items[0];
239 } 237 }
240 struct sway_container *workspace = focus; 238 return focus->sway_workspace;
241 if (workspace->type != C_WORKSPACE) {
242 workspace = container_parent(workspace, C_WORKSPACE);
243 }
244 return workspace;
245} 239}
246 240
247bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 241bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
@@ -255,8 +249,8 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
255 struct sway_layer_surface *sway_layer_surface = 249 struct sway_layer_surface *sway_layer_surface =
256 layer_from_wlr_layer_surface(wlr_layer_surface); 250 layer_from_wlr_layer_surface(wlr_layer_surface);
257 pixman_box32_t output_box = { 251 pixman_box32_t output_box = {
258 .x2 = output->swayc->current.swayc_width, 252 .x2 = output->wlr_output->width,
259 .y2 = output->swayc->current.swayc_height, 253 .y2 = output->wlr_output->height,
260 }; 254 };
261 pixman_region32_t surface_opaque_box; 255 pixman_region32_t surface_opaque_box;
262 pixman_region32_init(&surface_opaque_box); 256 pixman_region32_init(&surface_opaque_box);
@@ -307,15 +301,15 @@ struct send_frame_done_data {
307 301
308static void send_frame_done_container_iterator(struct sway_container *con, 302static void send_frame_done_container_iterator(struct sway_container *con,
309 void *_data) { 303 void *_data) {
310 if (con->type != C_VIEW) { 304 if (!con->view) {
311 return; 305 return;
312 } 306 }
313 if (!view_is_visible(con->sway_view)) { 307 if (!view_is_visible(con->view)) {
314 return; 308 return;
315 } 309 }
316 310
317 struct send_frame_done_data *data = _data; 311 struct send_frame_done_data *data = _data;
318 output_view_for_each_surface(data->output, con->sway_view, 312 output_view_for_each_surface(data->output, con->view,
319 send_frame_done_iterator, data->when); 313 send_frame_done_iterator, data->when);
320} 314}
321 315
@@ -328,15 +322,14 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
328 .output = output, 322 .output = output,
329 .when = when, 323 .when = when,
330 }; 324 };
331 struct sway_container *workspace = output_get_active_workspace(output); 325 struct sway_workspace *workspace = output_get_active_workspace(output);
332 if (workspace->current.ws_fullscreen) { 326 if (workspace->current.fullscreen) {
333 send_frame_done_container_iterator( 327 send_frame_done_container_iterator(
334 workspace->current.ws_fullscreen, &data); 328 workspace->current.fullscreen, &data);
335 container_for_each_child(workspace->current.ws_fullscreen, 329 container_for_each_child(workspace->current.fullscreen,
336 send_frame_done_container_iterator, &data); 330 send_frame_done_container_iterator, &data);
337#ifdef HAVE_XWAYLAND 331#ifdef HAVE_XWAYLAND
338 send_frame_done_unmanaged(output, 332 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
339 &root_container.sway_root->xwayland_unmanaged, when);
340#endif 333#endif
341 } else { 334 } else {
342 send_frame_done_layer(output, 335 send_frame_done_layer(output,
@@ -348,8 +341,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
348 send_frame_done_container_iterator, &data); 341 send_frame_done_container_iterator, &data);
349 342
350#ifdef HAVE_XWAYLAND 343#ifdef HAVE_XWAYLAND
351 send_frame_done_unmanaged(output, 344 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
352 &root_container.sway_root->xwayland_unmanaged, when);
353#endif 345#endif
354 send_frame_done_layer(output, 346 send_frame_done_layer(output,
355 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); 347 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when);
@@ -358,8 +350,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
358send_frame_overlay: 350send_frame_overlay:
359 send_frame_done_layer(output, 351 send_frame_done_layer(output,
360 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); 352 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when);
361 send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons, 353 send_frame_done_drag_icons(output, &root->drag_icons, when);
362 when);
363} 354}
364 355
365static void damage_handle_frame(struct wl_listener *listener, void *data) { 356static void damage_handle_frame(struct wl_listener *listener, void *data) {
@@ -391,7 +382,11 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
391} 382}
392 383
393void output_damage_whole(struct sway_output *output) { 384void output_damage_whole(struct sway_output *output) {
394 wlr_output_damage_add_whole(output->damage); 385 // The output can exist with no wlr_output if it's just been disconnected
386 // and the transaction to evacuate it has't completed yet.
387 if (output && output->wlr_output) {
388 wlr_output_damage_add_whole(output->damage);
389 }
395} 390}
396 391
397static void damage_surface_iterator(struct sway_output *output, 392static void damage_surface_iterator(struct sway_output *output,
@@ -446,14 +441,9 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
446 441
447static void output_damage_view(struct sway_output *output, 442static void output_damage_view(struct sway_output *output,
448 struct sway_view *view, bool whole) { 443 struct sway_view *view, bool whole) {
449 if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
450 return;
451 }
452
453 if (!view_is_visible(view)) { 444 if (!view_is_visible(view)) {
454 return; 445 return;
455 } 446 }
456
457 output_view_for_each_surface(output, view, damage_surface_iterator, &whole); 447 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
458} 448}
459 449
@@ -466,31 +456,29 @@ void output_damage_from_view(struct sway_output *output,
466void output_damage_box(struct sway_output *output, struct wlr_box *_box) { 456void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
467 struct wlr_box box; 457 struct wlr_box box;
468 memcpy(&box, _box, sizeof(struct wlr_box)); 458 memcpy(&box, _box, sizeof(struct wlr_box));
469 box.x -= output->swayc->current.swayc_x; 459 box.x -= output->wlr_output->lx;
470 box.y -= output->swayc->current.swayc_y; 460 box.y -= output->wlr_output->ly;
471 scale_box(&box, output->wlr_output->scale); 461 scale_box(&box, output->wlr_output->scale);
472 wlr_output_damage_add_box(output->damage, &box); 462 wlr_output_damage_add_box(output->damage, &box);
473} 463}
474 464
475static void output_damage_whole_container_iterator(struct sway_container *con, 465static void output_damage_whole_container_iterator(struct sway_container *con,
476 void *data) { 466 void *data) {
477 struct sway_output *output = data; 467 if (!sway_assert(con->view, "expected a view")) {
478
479 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
480 return; 468 return;
481 } 469 }
482 470 struct sway_output *output = data;
483 output_damage_view(output, con->sway_view, true); 471 output_damage_view(output, con->view, true);
484} 472}
485 473
486void output_damage_whole_container(struct sway_output *output, 474void output_damage_whole_container(struct sway_output *output,
487 struct sway_container *con) { 475 struct sway_container *con) {
488 // Pad the box by 1px, because the width is a double and might be a fraction 476 // Pad the box by 1px, because the width is a double and might be a fraction
489 struct wlr_box box = { 477 struct wlr_box box = {
490 .x = con->current.swayc_x - output->wlr_output->lx - 1, 478 .x = con->current.con_x - output->wlr_output->lx - 1,
491 .y = con->current.swayc_y - output->wlr_output->ly - 1, 479 .y = con->current.con_y - output->wlr_output->ly - 1,
492 .width = con->current.swayc_width + 2, 480 .width = con->current.con_width + 2,
493 .height = con->current.swayc_height + 2, 481 .height = con->current.con_height + 2,
494 }; 482 };
495 scale_box(&box, output->wlr_output->scale); 483 scale_box(&box, output->wlr_output->scale);
496 wlr_output_damage_add_box(output->damage, &box); 484 wlr_output_damage_add_box(output->damage, &box);
@@ -499,44 +487,48 @@ void output_damage_whole_container(struct sway_output *output,
499static void damage_handle_destroy(struct wl_listener *listener, void *data) { 487static void damage_handle_destroy(struct wl_listener *listener, void *data) {
500 struct sway_output *output = 488 struct sway_output *output =
501 wl_container_of(listener, output, damage_destroy); 489 wl_container_of(listener, output, damage_destroy);
502 output_begin_destroy(output->swayc); 490 output_disable(output);
491 transaction_commit_dirty();
503} 492}
504 493
505static void handle_destroy(struct wl_listener *listener, void *data) { 494static void handle_destroy(struct wl_listener *listener, void *data) {
506 struct sway_output *output = wl_container_of(listener, output, destroy); 495 struct sway_output *output = wl_container_of(listener, output, destroy);
507 wl_signal_emit(&output->events.destroy, output); 496 wl_signal_emit(&output->events.destroy, output);
508 497
509 if (output->swayc) { 498 if (output->enabled) {
510 output_begin_destroy(output->swayc); 499 output_disable(output);
511 } 500 }
501 output_begin_destroy(output);
512 502
513 wl_list_remove(&output->link); 503 transaction_commit_dirty();
514 wl_list_remove(&output->destroy.link);
515 output->wlr_output->data = NULL;
516 free(output);
517
518 arrange_windows(&root_container);
519} 504}
520 505
521static void handle_mode(struct wl_listener *listener, void *data) { 506static void handle_mode(struct wl_listener *listener, void *data) {
522 struct sway_output *output = wl_container_of(listener, output, mode); 507 struct sway_output *output = wl_container_of(listener, output, mode);
523 arrange_layers(output); 508 arrange_layers(output);
524 arrange_windows(output->swayc); 509 arrange_output(output);
525 transaction_commit_dirty(); 510 transaction_commit_dirty();
526} 511}
527 512
528static void handle_transform(struct wl_listener *listener, void *data) { 513static void handle_transform(struct wl_listener *listener, void *data) {
529 struct sway_output *output = wl_container_of(listener, output, transform); 514 struct sway_output *output = wl_container_of(listener, output, transform);
530 arrange_layers(output); 515 arrange_layers(output);
531 arrange_windows(output->swayc); 516 arrange_output(output);
532 transaction_commit_dirty(); 517 transaction_commit_dirty();
533} 518}
534 519
520static void update_textures(struct sway_container *con, void *data) {
521 container_update_title_textures(con);
522 if (con->view) {
523 view_update_marks_textures(con->view);
524 }
525}
526
535static void handle_scale(struct wl_listener *listener, void *data) { 527static void handle_scale(struct wl_listener *listener, void *data) {
536 struct sway_output *output = wl_container_of(listener, output, scale); 528 struct sway_output *output = wl_container_of(listener, output, scale);
537 arrange_layers(output); 529 arrange_layers(output);
538 container_update_textures_recursive(output->swayc); 530 output_for_each_container(output, update_textures, NULL);
539 arrange_windows(output->swayc); 531 arrange_output(output);
540 transaction_commit_dirty(); 532 transaction_commit_dirty();
541} 533}
542 534
@@ -545,57 +537,27 @@ void handle_new_output(struct wl_listener *listener, void *data) {
545 struct wlr_output *wlr_output = data; 537 struct wlr_output *wlr_output = data;
546 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 538 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
547 539
548 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 540 struct sway_output *output = output_create(wlr_output);
549 if (!output) { 541 if (!output) {
550 return; 542 return;
551 } 543 }
552 output->wlr_output = wlr_output;
553 wlr_output->data = output;
554 output->server = server; 544 output->server = server;
555 output->damage = wlr_output_damage_create(wlr_output); 545 output->damage = wlr_output_damage_create(wlr_output);
556
557 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
558 output->destroy.notify = handle_destroy; 546 output->destroy.notify = handle_destroy;
559 547
560 wl_list_insert(&root_container.sway_root->all_outputs, &output->link); 548 struct output_config *oc = output_find_config(output);
561
562 output_enable(output);
563}
564
565void output_enable(struct sway_output *output) {
566 struct wlr_output *wlr_output = output->wlr_output;
567
568 if (!sway_assert(output->swayc == NULL, "output is already enabled")) {
569 return;
570 }
571
572 output->swayc = output_create(output);
573 if (!output->swayc) {
574 // Output is disabled
575 return;
576 }
577 549
578 size_t len = sizeof(output->layers) / sizeof(output->layers[0]); 550 if (oc && oc->enabled) {
579 for (size_t i = 0; i < len; ++i) { 551 output_enable(output, oc);
580 wl_list_init(&output->layers[i]);
581 } 552 }
582 wl_signal_init(&output->events.destroy);
583 553
584 input_manager_configure_xcursor(input_manager); 554 transaction_commit_dirty();
555}
585 556
586 wl_signal_add(&wlr_output->events.mode, &output->mode); 557void output_add_listeners(struct sway_output *output) {
587 output->mode.notify = handle_mode; 558 output->mode.notify = handle_mode;
588 wl_signal_add(&wlr_output->events.transform, &output->transform);
589 output->transform.notify = handle_transform; 559 output->transform.notify = handle_transform;
590 wl_signal_add(&wlr_output->events.scale, &output->scale);
591 output->scale.notify = handle_scale; 560 output->scale.notify = handle_scale;
592
593 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
594 output->damage_frame.notify = damage_handle_frame; 561 output->damage_frame.notify = damage_handle_frame;
595 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
596 output->damage_destroy.notify = damage_handle_destroy; 562 output->damage_destroy.notify = damage_handle_destroy;
597
598 arrange_layers(output);
599 arrange_windows(&root_container);
600 transaction_commit_dirty();
601} 563}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 695213eb..99b2cf3d 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -193,10 +193,10 @@ static void render_view_toplevels(struct sway_view *view,
193 .alpha = alpha, 193 .alpha = alpha,
194 }; 194 };
195 // Render all toplevels without descending into popups 195 // Render all toplevels without descending into popups
196 double ox = 196 double ox = view->container->current.view_x -
197 view->swayc->current.view_x - output->wlr_output->lx - view->geometry.x; 197 output->wlr_output->lx - view->geometry.x;
198 double oy = 198 double oy = view->container->current.view_y -
199 view->swayc->current.view_y - output->wlr_output->ly - view->geometry.y; 199 output->wlr_output->ly - view->geometry.y;
200 output_surface_for_each_surface(output, view->surface, ox, oy, 200 output_surface_for_each_surface(output, view->surface, ox, oy,
201 render_surface_iterator, &data); 201 render_surface_iterator, &data);
202} 202}
@@ -229,17 +229,17 @@ static void render_saved_view(struct sway_view *view,
229 return; 229 return;
230 } 230 }
231 struct wlr_box box = { 231 struct wlr_box box = {
232 .x = view->swayc->current.view_x - output->swayc->current.swayc_x - 232 .x = view->container->current.view_x - output->wlr_output->lx -
233 view->saved_geometry.x, 233 view->saved_geometry.x,
234 .y = view->swayc->current.view_y - output->swayc->current.swayc_y - 234 .y = view->container->current.view_y - output->wlr_output->ly -
235 view->saved_geometry.y, 235 view->saved_geometry.y,
236 .width = view->saved_buffer_width, 236 .width = view->saved_buffer_width,
237 .height = view->saved_buffer_height, 237 .height = view->saved_buffer_height,
238 }; 238 };
239 239
240 struct wlr_box output_box = { 240 struct wlr_box output_box = {
241 .width = output->swayc->current.swayc_width, 241 .width = output->wlr_output->width,
242 .height = output->swayc->current.swayc_height, 242 .height = output->wlr_output->height,
243 }; 243 };
244 244
245 struct wlr_box intersection; 245 struct wlr_box intersection;
@@ -263,14 +263,14 @@ static void render_saved_view(struct sway_view *view,
263 */ 263 */
264static void render_view(struct sway_output *output, pixman_region32_t *damage, 264static void render_view(struct sway_output *output, pixman_region32_t *damage,
265 struct sway_container *con, struct border_colors *colors) { 265 struct sway_container *con, struct border_colors *colors) {
266 struct sway_view *view = con->sway_view; 266 struct sway_view *view = con->view;
267 if (view->saved_buffer) { 267 if (view->saved_buffer) {
268 render_saved_view(view, output, damage, view->swayc->alpha); 268 render_saved_view(view, output, damage, view->container->alpha);
269 } else { 269 } else {
270 render_view_toplevels(view, output, damage, view->swayc->alpha); 270 render_view_toplevels(view, output, damage, view->container->alpha);
271 } 271 }
272 272
273 if (view->swayc->current.using_csd) { 273 if (view->container->current.using_csd) {
274 return; 274 return;
275 } 275 }
276 276
@@ -283,7 +283,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
283 if (state->border_left) { 283 if (state->border_left) {
284 memcpy(&color, colors->child_border, sizeof(float) * 4); 284 memcpy(&color, colors->child_border, sizeof(float) * 4);
285 premultiply_alpha(color, con->alpha); 285 premultiply_alpha(color, con->alpha);
286 box.x = state->swayc_x; 286 box.x = state->con_x;
287 box.y = state->view_y; 287 box.y = state->view_y;
288 box.width = state->border_thickness; 288 box.width = state->border_thickness;
289 box.height = state->view_height; 289 box.height = state->view_height;
@@ -291,9 +291,12 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
291 render_rect(output->wlr_output, damage, &box, color); 291 render_rect(output->wlr_output, damage, &box, color);
292 } 292 }
293 293
294 list_t *siblings = container_get_current_siblings(con);
295 enum sway_container_layout layout =
296 container_current_parent_layout(con);
297
294 if (state->border_right) { 298 if (state->border_right) {
295 if (state->parent->current.children->length == 1 299 if (siblings->length == 1 && layout == L_HORIZ) {
296 && state->parent->current.layout == L_HORIZ) {
297 memcpy(&color, colors->indicator, sizeof(float) * 4); 300 memcpy(&color, colors->indicator, sizeof(float) * 4);
298 } else { 301 } else {
299 memcpy(&color, colors->child_border, sizeof(float) * 4); 302 memcpy(&color, colors->child_border, sizeof(float) * 4);
@@ -308,16 +311,15 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
308 } 311 }
309 312
310 if (state->border_bottom) { 313 if (state->border_bottom) {
311 if (state->parent->current.children->length == 1 314 if (siblings->length == 1 && layout == L_VERT) {
312 && con->current.parent->current.layout == L_VERT) {
313 memcpy(&color, colors->indicator, sizeof(float) * 4); 315 memcpy(&color, colors->indicator, sizeof(float) * 4);
314 } else { 316 } else {
315 memcpy(&color, colors->child_border, sizeof(float) * 4); 317 memcpy(&color, colors->child_border, sizeof(float) * 4);
316 } 318 }
317 premultiply_alpha(color, con->alpha); 319 premultiply_alpha(color, con->alpha);
318 box.x = state->swayc_x; 320 box.x = state->con_x;
319 box.y = state->view_y + state->view_height; 321 box.y = state->view_y + state->view_height;
320 box.width = state->swayc_width; 322 box.width = state->con_width;
321 box.height = state->border_thickness; 323 box.height = state->border_thickness;
322 scale_box(&box, output_scale); 324 scale_box(&box, output_scale);
323 render_rect(output->wlr_output, damage, &box, color); 325 render_rect(output->wlr_output, damage, &box, color);
@@ -344,12 +346,12 @@ static void render_titlebar(struct sway_output *output,
344 float color[4]; 346 float color[4];
345 struct sway_container_state *state = &con->current; 347 struct sway_container_state *state = &con->current;
346 float output_scale = output->wlr_output->scale; 348 float output_scale = output->wlr_output->scale;
347 enum sway_container_layout layout = state->parent->current.layout; 349 enum sway_container_layout layout = container_current_parent_layout(con);
348 list_t *children = state->parent->current.children; 350 list_t *children = container_get_current_siblings(con);
349 bool is_last_child = children->length == 0 || 351 bool is_last_child = children->length == 0 ||
350 children->items[children->length - 1] == con; 352 children->items[children->length - 1] == con;
351 double output_x = output->swayc->current.swayc_x; 353 double output_x = output->wlr_output->lx;
352 double output_y = output->swayc->current.swayc_y; 354 double output_y = output->wlr_output->ly;
353 355
354 // Single pixel bar above title 356 // Single pixel bar above title
355 memcpy(&color, colors->border, sizeof(float) * 4); 357 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -366,7 +368,7 @@ static void render_titlebar(struct sway_output *output,
366 bool connects_sides = false; 368 bool connects_sides = false;
367 if (layout == L_HORIZ || layout == L_VERT || 369 if (layout == L_HORIZ || layout == L_VERT ||
368 (layout == L_STACKED && is_last_child)) { 370 (layout == L_STACKED && is_last_child)) {
369 if (con->type == C_VIEW) { 371 if (con->view) {
370 left_offset = state->border_left * state->border_thickness; 372 left_offset = state->border_left * state->border_thickness;
371 right_offset = state->border_right * state->border_thickness; 373 right_offset = state->border_right * state->border_thickness;
372 connects_sides = true; 374 connects_sides = true;
@@ -542,14 +544,22 @@ static void render_top_border(struct sway_output *output,
542 // Child border - top edge 544 // Child border - top edge
543 memcpy(&color, colors->child_border, sizeof(float) * 4); 545 memcpy(&color, colors->child_border, sizeof(float) * 4);
544 premultiply_alpha(color, con->alpha); 546 premultiply_alpha(color, con->alpha);
545 box.x = state->swayc_x; 547 box.x = state->con_x;
546 box.y = state->swayc_y; 548 box.y = state->con_y;
547 box.width = state->swayc_width; 549 box.width = state->con_width;
548 box.height = state->border_thickness; 550 box.height = state->border_thickness;
549 scale_box(&box, output_scale); 551 scale_box(&box, output_scale);
550 render_rect(output->wlr_output, output_damage, &box, color); 552 render_rect(output->wlr_output, output_damage, &box, color);
551} 553}
552 554
555struct parent_data {
556 enum sway_container_layout layout;
557 struct wlr_box box;
558 list_t *children;
559 bool focused;
560 struct sway_container *active_child;
561};
562
553static void render_container(struct sway_output *output, 563static void render_container(struct sway_output *output,
554 pixman_region32_t *damage, struct sway_container *con, bool parent_focused); 564 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
555 565
@@ -559,14 +569,13 @@ static void render_container(struct sway_output *output,
559 * Wrap child views in borders and leave child containers borderless because 569 * Wrap child views in borders and leave child containers borderless because
560 * they'll apply their own borders to their children. 570 * they'll apply their own borders to their children.
561 */ 571 */
562static void render_container_simple(struct sway_output *output, 572static void render_containers_linear(struct sway_output *output,
563 pixman_region32_t *damage, struct sway_container *con, 573 pixman_region32_t *damage, struct parent_data *parent) {
564 bool parent_focused) { 574 for (int i = 0; i < parent->children->length; ++i) {
565 for (int i = 0; i < con->current.children->length; ++i) { 575 struct sway_container *child = parent->children->items[i];
566 struct sway_container *child = con->current.children->items[i]; 576
567 577 if (child->view) {
568 if (child->type == C_VIEW) { 578 struct sway_view *view = child->view;
569 struct sway_view *view = child->sway_view;
570 struct border_colors *colors; 579 struct border_colors *colors;
571 struct wlr_texture *title_texture; 580 struct wlr_texture *title_texture;
572 struct wlr_texture *marks_texture; 581 struct wlr_texture *marks_texture;
@@ -576,11 +585,11 @@ static void render_container_simple(struct sway_output *output,
576 colors = &config->border_colors.urgent; 585 colors = &config->border_colors.urgent;
577 title_texture = child->title_urgent; 586 title_texture = child->title_urgent;
578 marks_texture = view->marks_urgent; 587 marks_texture = view->marks_urgent;
579 } else if (state->focused || parent_focused) { 588 } else if (state->focused || parent->focused) {
580 colors = &config->border_colors.focused; 589 colors = &config->border_colors.focused;
581 title_texture = child->title_focused; 590 title_texture = child->title_focused;
582 marks_texture = view->marks_focused; 591 marks_texture = view->marks_focused;
583 } else if (con->current.focused_inactive_child == child) { 592 } else if (child == parent->active_child) {
584 colors = &config->border_colors.focused_inactive; 593 colors = &config->border_colors.focused_inactive;
585 title_texture = child->title_focused_inactive; 594 title_texture = child->title_focused_inactive;
586 marks_texture = view->marks_focused_inactive; 595 marks_texture = view->marks_focused_inactive;
@@ -590,10 +599,10 @@ static void render_container_simple(struct sway_output *output,
590 marks_texture = view->marks_unfocused; 599 marks_texture = view->marks_unfocused;
591 } 600 }
592 601
593 if (!view->swayc->current.using_csd) { 602 if (!view->container->current.using_csd) {
594 if (state->border == B_NORMAL) { 603 if (state->border == B_NORMAL) {
595 render_titlebar(output, damage, child, state->swayc_x, 604 render_titlebar(output, damage, child, state->con_x,
596 state->swayc_y, state->swayc_width, colors, 605 state->con_y, state->con_width, colors,
597 title_texture, marks_texture); 606 title_texture, marks_texture);
598 } else { 607 } else {
599 render_top_border(output, damage, child, colors); 608 render_top_border(output, damage, child, colors);
@@ -602,7 +611,7 @@ static void render_container_simple(struct sway_output *output,
602 render_view(output, damage, child, colors); 611 render_view(output, damage, child, colors);
603 } else { 612 } else {
604 render_container(output, damage, child, 613 render_container(output, damage, child,
605 parent_focused || child->current.focused); 614 parent->focused || child->current.focused);
606 } 615 }
607 } 616 }
608} 617}
@@ -610,22 +619,19 @@ static void render_container_simple(struct sway_output *output,
610/** 619/**
611 * Render a container's children using the L_TABBED layout. 620 * Render a container's children using the L_TABBED layout.
612 */ 621 */
613static void render_container_tabbed(struct sway_output *output, 622static void render_containers_tabbed(struct sway_output *output,
614 pixman_region32_t *damage, struct sway_container *con, 623 pixman_region32_t *damage, struct parent_data *parent) {
615 bool parent_focused) { 624 if (!parent->children->length) {
616 if (!con->current.children->length) {
617 return; 625 return;
618 } 626 }
619 struct sway_container_state *pstate = &con->current; 627 struct sway_container *current = parent->active_child;
620 struct sway_container *current = pstate->focused_inactive_child;
621 struct border_colors *current_colors = &config->border_colors.unfocused; 628 struct border_colors *current_colors = &config->border_colors.unfocused;
622 629 int tab_width = parent->box.width / parent->children->length;
623 int tab_width = (pstate->swayc_width) / pstate->children->length;
624 630
625 // Render tabs 631 // Render tabs
626 for (int i = 0; i < pstate->children->length; ++i) { 632 for (int i = 0; i < parent->children->length; ++i) {
627 struct sway_container *child = pstate->children->items[i]; 633 struct sway_container *child = parent->children->items[i];
628 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 634 struct sway_view *view = child->view;
629 struct sway_container_state *cstate = &child->current; 635 struct sway_container_state *cstate = &child->current;
630 struct border_colors *colors; 636 struct border_colors *colors;
631 struct wlr_texture *title_texture; 637 struct wlr_texture *title_texture;
@@ -637,11 +643,11 @@ static void render_container_tabbed(struct sway_output *output,
637 colors = &config->border_colors.urgent; 643 colors = &config->border_colors.urgent;
638 title_texture = child->title_urgent; 644 title_texture = child->title_urgent;
639 marks_texture = view ? view->marks_urgent : NULL; 645 marks_texture = view ? view->marks_urgent : NULL;
640 } else if (cstate->focused || parent_focused) { 646 } else if (cstate->focused || parent->focused) {
641 colors = &config->border_colors.focused; 647 colors = &config->border_colors.focused;
642 title_texture = child->title_focused; 648 title_texture = child->title_focused;
643 marks_texture = view ? view->marks_focused : NULL; 649 marks_texture = view ? view->marks_focused : NULL;
644 } else if (child == pstate->focused_inactive_child) { 650 } else if (child == parent->active_child) {
645 colors = &config->border_colors.focused_inactive; 651 colors = &config->border_colors.focused_inactive;
646 title_texture = child->title_focused_inactive; 652 title_texture = child->title_focused_inactive;
647 marks_texture = view ? view->marks_focused_inactive : NULL; 653 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -651,14 +657,14 @@ static void render_container_tabbed(struct sway_output *output,
651 marks_texture = view ? view->marks_unfocused : NULL; 657 marks_texture = view ? view->marks_unfocused : NULL;
652 } 658 }
653 659
654 int x = cstate->swayc_x + tab_width * i; 660 int x = cstate->con_x + tab_width * i;
655 661
656 // Make last tab use the remaining width of the parent 662 // Make last tab use the remaining width of the parent
657 if (i == pstate->children->length - 1) { 663 if (i == parent->children->length - 1) {
658 tab_width = pstate->swayc_width - tab_width * i; 664 tab_width = parent->box.width - tab_width * i;
659 } 665 }
660 666
661 render_titlebar(output, damage, child, x, pstate->swayc_y, tab_width, 667 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
662 colors, title_texture, marks_texture); 668 colors, title_texture, marks_texture);
663 669
664 if (child == current) { 670 if (child == current) {
@@ -667,33 +673,30 @@ static void render_container_tabbed(struct sway_output *output,
667 } 673 }
668 674
669 // Render surface and left/right/bottom borders 675 // Render surface and left/right/bottom borders
670 if (current->type == C_VIEW) { 676 if (current->view) {
671 render_view(output, damage, current, current_colors); 677 render_view(output, damage, current, current_colors);
672 } else { 678 } else {
673 render_container(output, damage, current, 679 render_container(output, damage, current,
674 parent_focused || current->current.focused); 680 parent->focused || current->current.focused);
675 } 681 }
676} 682}
677 683
678/** 684/**
679 * Render a container's children using the L_STACKED layout. 685 * Render a container's children using the L_STACKED layout.
680 */ 686 */
681static void render_container_stacked(struct sway_output *output, 687static void render_containers_stacked(struct sway_output *output,
682 pixman_region32_t *damage, struct sway_container *con, 688 pixman_region32_t *damage, struct parent_data *parent) {
683 bool parent_focused) { 689 if (!parent->children->length) {
684 if (!con->current.children->length) {
685 return; 690 return;
686 } 691 }
687 struct sway_container_state *pstate = &con->current; 692 struct sway_container *current = parent->active_child;
688 struct sway_container *current = pstate->focused_inactive_child;
689 struct border_colors *current_colors = &config->border_colors.unfocused; 693 struct border_colors *current_colors = &config->border_colors.unfocused;
690
691 size_t titlebar_height = container_titlebar_height(); 694 size_t titlebar_height = container_titlebar_height();
692 695
693 // Render titles 696 // Render titles
694 for (int i = 0; i < pstate->children->length; ++i) { 697 for (int i = 0; i < parent->children->length; ++i) {
695 struct sway_container *child = pstate->children->items[i]; 698 struct sway_container *child = parent->children->items[i];
696 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 699 struct sway_view *view = child->view;
697 struct sway_container_state *cstate = &child->current; 700 struct sway_container_state *cstate = &child->current;
698 struct border_colors *colors; 701 struct border_colors *colors;
699 struct wlr_texture *title_texture; 702 struct wlr_texture *title_texture;
@@ -705,11 +708,11 @@ static void render_container_stacked(struct sway_output *output,
705 colors = &config->border_colors.urgent; 708 colors = &config->border_colors.urgent;
706 title_texture = child->title_urgent; 709 title_texture = child->title_urgent;
707 marks_texture = view ? view->marks_urgent : NULL; 710 marks_texture = view ? view->marks_urgent : NULL;
708 } else if (cstate->focused || parent_focused) { 711 } else if (cstate->focused || parent->focused) {
709 colors = &config->border_colors.focused; 712 colors = &config->border_colors.focused;
710 title_texture = child->title_focused; 713 title_texture = child->title_focused;
711 marks_texture = view ? view->marks_focused : NULL; 714 marks_texture = view ? view->marks_focused : NULL;
712 } else if (child == pstate->focused_inactive_child) { 715 } else if (child == parent->active_child) {
713 colors = &config->border_colors.focused_inactive; 716 colors = &config->border_colors.focused_inactive;
714 title_texture = child->title_focused_inactive; 717 title_texture = child->title_focused_inactive;
715 marks_texture = view ? view->marks_focused_inactive : NULL; 718 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -719,9 +722,9 @@ static void render_container_stacked(struct sway_output *output,
719 marks_texture = view ? view->marks_unfocused : NULL; 722 marks_texture = view ? view->marks_unfocused : NULL;
720 } 723 }
721 724
722 int y = pstate->swayc_y + titlebar_height * i; 725 int y = parent->box.y + titlebar_height * i;
723 render_titlebar(output, damage, child, pstate->swayc_x, y, 726 render_titlebar(output, damage, child, parent->box.x, y,
724 pstate->swayc_width, colors, title_texture, marks_texture); 727 parent->box.width, colors, title_texture, marks_texture);
725 728
726 if (child == current) { 729 if (child == current) {
727 current_colors = colors; 730 current_colors = colors;
@@ -729,36 +732,69 @@ static void render_container_stacked(struct sway_output *output,
729 } 732 }
730 733
731 // Render surface and left/right/bottom borders 734 // Render surface and left/right/bottom borders
732 if (current->type == C_VIEW) { 735 if (current->view) {
733 render_view(output, damage, current, current_colors); 736 render_view(output, damage, current, current_colors);
734 } else { 737 } else {
735 render_container(output, damage, current, 738 render_container(output, damage, current,
736 parent_focused || current->current.focused); 739 parent->focused || current->current.focused);
737 } 740 }
738} 741}
739 742
740static void render_container(struct sway_output *output, 743static void render_containers(struct sway_output *output,
741 pixman_region32_t *damage, struct sway_container *con, 744 pixman_region32_t *damage, struct parent_data *parent) {
742 bool parent_focused) { 745 switch (parent->layout) {
743 switch (con->current.layout) {
744 case L_NONE: 746 case L_NONE:
745 case L_HORIZ: 747 case L_HORIZ:
746 case L_VERT: 748 case L_VERT:
747 render_container_simple(output, damage, con, parent_focused); 749 render_containers_linear(output, damage, parent);
748 break; 750 break;
749 case L_STACKED: 751 case L_STACKED:
750 render_container_stacked(output, damage, con, parent_focused); 752 render_containers_stacked(output, damage, parent);
751 break; 753 break;
752 case L_TABBED: 754 case L_TABBED:
753 render_container_tabbed(output, damage, con, parent_focused); 755 render_containers_tabbed(output, damage, parent);
754 break; 756 break;
755 } 757 }
756} 758}
757 759
760static void render_container(struct sway_output *output,
761 pixman_region32_t *damage, struct sway_container *con, bool focused) {
762 struct parent_data data = {
763 .layout = con->current.layout,
764 .box = {
765 .x = con->current.con_x,
766 .y = con->current.con_y,
767 .width = con->current.con_width,
768 .height = con->current.con_height,
769 },
770 .children = con->current.children,
771 .focused = focused,
772 .active_child = con->current.focused_inactive_child,
773 };
774 render_containers(output, damage, &data);
775}
776
777static void render_workspace(struct sway_output *output,
778 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
779 struct parent_data data = {
780 .layout = ws->current.layout,
781 .box = {
782 .x = ws->current.x,
783 .y = ws->current.y,
784 .width = ws->current.width,
785 .height = ws->current.height,
786 },
787 .children = ws->current.tiling,
788 .focused = focused,
789 .active_child = ws->current.focused_inactive_child,
790 };
791 render_containers(output, damage, &data);
792}
793
758static void render_floating_container(struct sway_output *soutput, 794static void render_floating_container(struct sway_output *soutput,
759 pixman_region32_t *damage, struct sway_container *con) { 795 pixman_region32_t *damage, struct sway_container *con) {
760 if (con->type == C_VIEW) { 796 if (con->view) {
761 struct sway_view *view = con->sway_view; 797 struct sway_view *view = con->view;
762 struct border_colors *colors; 798 struct border_colors *colors;
763 struct wlr_texture *title_texture; 799 struct wlr_texture *title_texture;
764 struct wlr_texture *marks_texture; 800 struct wlr_texture *marks_texture;
@@ -777,10 +813,10 @@ static void render_floating_container(struct sway_output *soutput,
777 marks_texture = view->marks_unfocused; 813 marks_texture = view->marks_unfocused;
778 } 814 }
779 815
780 if (!view->swayc->current.using_csd) { 816 if (!view->container->current.using_csd) {
781 if (con->current.border == B_NORMAL) { 817 if (con->current.border == B_NORMAL) {
782 render_titlebar(soutput, damage, con, con->current.swayc_x, 818 render_titlebar(soutput, damage, con, con->current.con_x,
783 con->current.swayc_y, con->current.swayc_width, colors, 819 con->current.con_y, con->current.con_width, colors,
784 title_texture, marks_texture); 820 title_texture, marks_texture);
785 } else if (con->current.border != B_NONE) { 821 } else if (con->current.border != B_NONE) {
786 render_top_border(soutput, damage, con, colors); 822 render_top_border(soutput, damage, con, colors);
@@ -794,17 +830,15 @@ static void render_floating_container(struct sway_output *soutput,
794 830
795static void render_floating(struct sway_output *soutput, 831static void render_floating(struct sway_output *soutput,
796 pixman_region32_t *damage) { 832 pixman_region32_t *damage) {
797 for (int i = 0; i < root_container.current.children->length; ++i) { 833 for (int i = 0; i < root->outputs->length; ++i) {
798 struct sway_container *output = 834 struct sway_output *output = root->outputs->items[i];
799 root_container.current.children->items[i]; 835 for (int j = 0; j < output->current.workspaces->length; ++j) {
800 for (int j = 0; j < output->current.children->length; ++j) { 836 struct sway_workspace *ws = output->current.workspaces->items[j];
801 struct sway_container *ws = output->current.children->items[j];
802 if (!workspace_is_visible(ws)) { 837 if (!workspace_is_visible(ws)) {
803 continue; 838 continue;
804 } 839 }
805 list_t *floating = ws->current.ws_floating; 840 for (int k = 0; k < ws->current.floating->length; ++k) {
806 for (int k = 0; k < floating->length; ++k) { 841 struct sway_container *floater = ws->current.floating->items[k];
807 struct sway_container *floater = floating->items[k];
808 render_floating_container(soutput, damage, floater); 842 render_floating_container(soutput, damage, floater);
809 } 843 }
810 } 844 }
@@ -837,8 +871,8 @@ void output_render(struct sway_output *output, struct timespec *when,
837 pixman_region32_union_rect(damage, damage, 0, 0, width, height); 871 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
838 } 872 }
839 873
840 struct sway_container *workspace = output_get_active_workspace(output); 874 struct sway_workspace *workspace = output_get_active_workspace(output);
841 struct sway_container *fullscreen_con = workspace->current.ws_fullscreen; 875 struct sway_container *fullscreen_con = workspace->current.fullscreen;
842 876
843 if (output_has_opaque_overlay_layer_surface(output)) { 877 if (output_has_opaque_overlay_layer_surface(output)) {
844 goto render_overlay; 878 goto render_overlay;
@@ -855,12 +889,11 @@ void output_render(struct sway_output *output, struct timespec *when,
855 } 889 }
856 890
857 // TODO: handle views smaller than the output 891 // TODO: handle views smaller than the output
858 if (fullscreen_con->type == C_VIEW) { 892 if (fullscreen_con->view) {
859 if (fullscreen_con->sway_view->saved_buffer) { 893 if (fullscreen_con->view->saved_buffer) {
860 render_saved_view(fullscreen_con->sway_view, 894 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
861 output, damage, 1.0f);
862 } else { 895 } else {
863 render_view_toplevels(fullscreen_con->sway_view, 896 render_view_toplevels(fullscreen_con->view,
864 output, damage, 1.0f); 897 output, damage, 1.0f);
865 } 898 }
866 } else { 899 } else {
@@ -868,8 +901,7 @@ void output_render(struct sway_output *output, struct timespec *when,
868 fullscreen_con->current.focused); 901 fullscreen_con->current.focused);
869 } 902 }
870#ifdef HAVE_XWAYLAND 903#ifdef HAVE_XWAYLAND
871 render_unmanaged(output, damage, 904 render_unmanaged(output, damage, &root->xwayland_unmanaged);
872 &root_container.sway_root->xwayland_unmanaged);
873#endif 905#endif
874 } else { 906 } else {
875 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; 907 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
@@ -886,31 +918,30 @@ void output_render(struct sway_output *output, struct timespec *when,
886 render_layer(output, damage, 918 render_layer(output, damage,
887 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); 919 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
888 920
889 render_container(output, damage, workspace, workspace->current.focused); 921 render_workspace(output, damage, workspace, workspace->current.focused);
890 render_floating(output, damage); 922 render_floating(output, damage);
891#ifdef HAVE_XWAYLAND 923#ifdef HAVE_XWAYLAND
892 render_unmanaged(output, damage, 924 render_unmanaged(output, damage, &root->xwayland_unmanaged);
893 &root_container.sway_root->xwayland_unmanaged);
894#endif 925#endif
895 render_layer(output, damage, 926 render_layer(output, damage,
896 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 927 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
897 } 928 }
898 929
899 struct sway_seat *seat = input_manager_current_seat(input_manager); 930 struct sway_seat *seat = input_manager_current_seat(input_manager);
900 struct sway_container *focus = seat_get_focus(seat); 931 struct sway_container *focus = seat_get_focused_container(seat);
901 if (focus && focus->type == C_VIEW) { 932 if (focus && focus->view) {
902 render_view_popups(focus->sway_view, output, damage, focus->alpha); 933 render_view_popups(focus->view, output, damage, focus->alpha);
903 } 934 }
904 935
905render_overlay: 936render_overlay:
906 render_layer(output, damage, 937 render_layer(output, damage,
907 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); 938 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
908 render_drag_icons(output, damage, &root_container.sway_root->drag_icons); 939 render_drag_icons(output, damage, &root->drag_icons);
909 940
910renderer_end: 941renderer_end:
911 if (debug.render_tree) { 942 if (debug.render_tree) {
912 wlr_renderer_scissor(renderer, NULL); 943 wlr_renderer_scissor(renderer, NULL);
913 wlr_render_texture(renderer, root_container.sway_root->debug_tree, 944 wlr_render_texture(renderer, root->debug_tree,
914 wlr_output->transform_matrix, 0, 40, 1); 945 wlr_output->transform_matrix, 0, 40, 1);
915 } 946 }
916 if (debug.damage == DAMAGE_HIGHLIGHT) { 947 if (debug.damage == DAMAGE_HIGHLIGHT) {
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 145c5f92..b4eec933 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -12,6 +12,7 @@
12#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
13#include "sway/output.h" 13#include "sway/output.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"
@@ -27,8 +28,12 @@ struct sway_transaction {
27 28
28struct sway_transaction_instruction { 29struct sway_transaction_instruction {
29 struct sway_transaction *transaction; 30 struct sway_transaction *transaction;
30 struct sway_container *container; 31 struct sway_node *node;
31 struct sway_container_state state; 32 union {
33 struct sway_output_state *output_state;
34 struct sway_workspace_state *workspace_state;
35 struct sway_container_state *container_state;
36 };
32 uint32_t serial; 37 uint32_t serial;
33}; 38};
34 39
@@ -47,26 +52,24 @@ static void transaction_destroy(struct sway_transaction *transaction) {
47 for (int i = 0; i < transaction->instructions->length; ++i) { 52 for (int i = 0; i < transaction->instructions->length; ++i) {
48 struct sway_transaction_instruction *instruction = 53 struct sway_transaction_instruction *instruction =
49 transaction->instructions->items[i]; 54 transaction->instructions->items[i];
50 struct sway_container *con = instruction->container; 55 struct sway_node *node = instruction->node;
51 con->ntxnrefs--; 56 node->ntxnrefs--;
52 if (con->instruction == instruction) { 57 if (node->instruction == instruction) {
53 con->instruction = NULL; 58 node->instruction = NULL;
54 } 59 }
55 if (con->destroying && con->ntxnrefs == 0) { 60 if (node->destroying && node->ntxnrefs == 0) {
56 switch (con->type) { 61 switch (node->type) {
57 case C_ROOT: 62 case N_ROOT:
63 sway_assert(false, "Never reached");
58 break; 64 break;
59 case C_OUTPUT: 65 case N_OUTPUT:
60 output_destroy(con); 66 output_destroy(node->sway_output);
61 break; 67 break;
62 case C_WORKSPACE: 68 case N_WORKSPACE:
63 workspace_destroy(con); 69 workspace_destroy(node->sway_workspace);
64 break; 70 break;
65 case C_CONTAINER: 71 case N_CONTAINER:
66 case C_VIEW: 72 container_destroy(node->sway_container);
67 container_destroy(con);
68 break;
69 case C_TYPES:
70 break; 73 break;
71 } 74 }
72 } 75 }
@@ -80,22 +83,79 @@ static void transaction_destroy(struct sway_transaction *transaction) {
80 free(transaction); 83 free(transaction);
81} 84}
82 85
83static void copy_pending_state(struct sway_container *container, 86static void copy_output_state(struct sway_output *output,
84 struct sway_container_state *state) { 87 struct sway_transaction_instruction *instruction) {
88 struct sway_output_state *state =
89 calloc(1, sizeof(struct sway_output_state));
90 if (!state) {
91 wlr_log(WLR_ERROR, "Could not allocate output state");
92 return;
93 }
94 instruction->output_state = state;
95
96 state->workspaces = create_list();
97 list_cat(state->workspaces, output->workspaces);
98
99 state->active_workspace = output_get_active_workspace(output);
100}
101
102static void copy_workspace_state(struct sway_workspace *ws,
103 struct sway_transaction_instruction *instruction) {
104 struct sway_workspace_state *state =
105 calloc(1, sizeof(struct sway_workspace_state));
106 if (!state) {
107 wlr_log(WLR_ERROR, "Could not allocate workspace state");
108 return;
109 }
110 instruction->workspace_state = state;
111
112 state->fullscreen = ws->fullscreen;
113 state->x = ws->x;
114 state->y = ws->y;
115 state->width = ws->width;
116 state->height = ws->height;
117 state->layout = ws->layout;
118
119 state->output = ws->output;
120 state->floating = create_list();
121 state->tiling = create_list();
122 list_cat(state->floating, ws->floating);
123 list_cat(state->tiling, ws->tiling);
124
125 struct sway_seat *seat = input_manager_current_seat(input_manager);
126 state->focused = seat_get_focus(seat) == &ws->node;
127
128 // Set focused_inactive_child to the direct tiling child
129 struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws);
130 if (focus) {
131 while (focus->parent) {
132 focus = focus->parent;
133 }
134 }
135 state->focused_inactive_child = focus;
136}
137
138static void copy_container_state(struct sway_container *container,
139 struct sway_transaction_instruction *instruction) {
140 struct sway_container_state *state =
141 calloc(1, sizeof(struct sway_container_state));
142 if (!state) {
143 wlr_log(WLR_ERROR, "Could not allocate container state");
144 return;
145 }
146 instruction->container_state = state;
147
85 state->layout = container->layout; 148 state->layout = container->layout;
86 state->swayc_x = container->x; 149 state->con_x = container->x;
87 state->swayc_y = container->y; 150 state->con_y = container->y;
88 state->swayc_width = container->width; 151 state->con_width = container->width;
89 state->swayc_height = container->height; 152 state->con_height = container->height;
90 state->is_fullscreen = container->is_fullscreen; 153 state->is_fullscreen = container->is_fullscreen;
91 state->has_gaps = container->has_gaps;
92 state->current_gaps = container->current_gaps;
93 state->gaps_inner = container->gaps_inner;
94 state->gaps_outer = container->gaps_outer;
95 state->parent = container->parent; 154 state->parent = container->parent;
155 state->workspace = container->workspace;
96 156
97 if (container->type == C_VIEW) { 157 if (container->view) {
98 struct sway_view *view = container->sway_view; 158 struct sway_view *view = container->view;
99 state->view_x = view->x; 159 state->view_x = view->x;
100 state->view_y = view->y; 160 state->view_y = view->y;
101 state->view_width = view->width; 161 state->view_width = view->width;
@@ -107,50 +167,111 @@ static void copy_pending_state(struct sway_container *container,
107 state->border_right = view->border_right; 167 state->border_right = view->border_right;
108 state->border_bottom = view->border_bottom; 168 state->border_bottom = view->border_bottom;
109 state->using_csd = view->using_csd; 169 state->using_csd = view->using_csd;
110 } else if (container->type == C_WORKSPACE) {
111 state->ws_fullscreen = container->sway_workspace->fullscreen;
112 state->ws_floating = create_list();
113 state->children = create_list();
114 list_cat(state->ws_floating, container->sway_workspace->floating);
115 list_cat(state->children, container->children);
116 } else { 170 } else {
117 state->children = create_list(); 171 state->children = create_list();
118 list_cat(state->children, container->children); 172 list_cat(state->children, container->children);
119 } 173 }
120 174
121 struct sway_seat *seat = input_manager_current_seat(input_manager); 175 struct sway_seat *seat = input_manager_current_seat(input_manager);
122 state->focused = seat_get_focus(seat) == container; 176 state->focused = seat_get_focus(seat) == &container->node;
123 177
124 if (container->type == C_WORKSPACE) { 178 if (!container->view) {
125 // Set focused_inactive_child to the direct tiling child 179 struct sway_node *focus = seat_get_active_child(seat, &container->node);
126 struct sway_container *focus = 180 state->focused_inactive_child = focus ? focus->sway_container : NULL;
127 seat_get_focus_inactive_tiling(seat, container);
128 if (focus && focus->type > C_WORKSPACE) {
129 while (focus->parent->type != C_WORKSPACE) {
130 focus = focus->parent;
131 }
132 }
133 state->focused_inactive_child = focus;
134 } else if (container->type != C_VIEW) {
135 state->focused_inactive_child =
136 seat_get_active_child(seat, container);
137 } 181 }
138} 182}
139 183
140static void transaction_add_container(struct sway_transaction *transaction, 184static void transaction_add_node(struct sway_transaction *transaction,
141 struct sway_container *container) { 185 struct sway_node *node) {
142 struct sway_transaction_instruction *instruction = 186 struct sway_transaction_instruction *instruction =
143 calloc(1, sizeof(struct sway_transaction_instruction)); 187 calloc(1, sizeof(struct sway_transaction_instruction));
144 if (!sway_assert(instruction, "Unable to allocate instruction")) { 188 if (!sway_assert(instruction, "Unable to allocate instruction")) {
145 return; 189 return;
146 } 190 }
147 instruction->transaction = transaction; 191 instruction->transaction = transaction;
148 instruction->container = container; 192 instruction->node = node;
149 193
150 copy_pending_state(container, &instruction->state); 194 switch (node->type) {
195 case N_ROOT:
196 break;
197 case N_OUTPUT:
198 copy_output_state(node->sway_output, instruction);
199 break;
200 case N_WORKSPACE:
201 copy_workspace_state(node->sway_workspace, instruction);
202 break;
203 case N_CONTAINER:
204 copy_container_state(node->sway_container, instruction);
205 break;
206 }
151 207
152 list_add(transaction->instructions, instruction); 208 list_add(transaction->instructions, instruction);
153 container->ntxnrefs++; 209 node->ntxnrefs++;
210}
211
212static void apply_output_state(struct sway_output *output,
213 struct sway_output_state *state) {
214 output_damage_whole(output);
215 list_free(output->current.workspaces);
216 memcpy(&output->current, state, sizeof(struct sway_output_state));
217 output_damage_whole(output);
218}
219
220static void apply_workspace_state(struct sway_workspace *ws,
221 struct sway_workspace_state *state) {
222 output_damage_whole(ws->current.output);
223 list_free(ws->current.floating);
224 list_free(ws->current.tiling);
225 memcpy(&ws->current, state, sizeof(struct sway_workspace_state));
226 output_damage_whole(ws->current.output);
227}
228
229static void apply_container_state(struct sway_container *container,
230 struct sway_container_state *state) {
231 struct sway_view *view = container->view;
232 // Damage the old location
233 desktop_damage_whole_container(container);
234 if (view && view->saved_buffer) {
235 struct wlr_box box = {
236 .x = container->current.view_x - view->saved_geometry.x,
237 .y = container->current.view_y - view->saved_geometry.y,
238 .width = view->saved_buffer_width,
239 .height = view->saved_buffer_height,
240 };
241 desktop_damage_box(&box);
242 }
243
244 // There are separate children lists for each instruction state, the
245 // container's current state and the container's pending state
246 // (ie. con->children). The list itself needs to be freed here.
247 // Any child containers which are being deleted will be cleaned up in
248 // transaction_destroy().
249 list_free(container->current.children);
250
251 memcpy(&container->current, state, sizeof(struct sway_container_state));
252
253 if (view && view->saved_buffer) {
254 if (!container->node.destroying || container->node.ntxnrefs == 1) {
255 view_remove_saved_buffer(view);
256 }
257 }
258
259 // Damage the new location
260 desktop_damage_whole_container(container);
261 if (view && view->surface) {
262 struct wlr_surface *surface = view->surface;
263 struct wlr_box box = {
264 .x = container->current.view_x - view->geometry.x,
265 .y = container->current.view_y - view->geometry.y,
266 .width = surface->current.width,
267 .height = surface->current.height,
268 };
269 desktop_damage_box(&box);
270 }
271
272 if (!container->node.destroying) {
273 container_discover_outputs(container);
274 }
154} 275}
155 276
156/** 277/**
@@ -168,67 +289,36 @@ static void transaction_apply(struct sway_transaction *transaction) {
168 "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60)); 289 "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60));
169 } 290 }
170 291
171 // Apply the instruction state to the container's current state 292 // Apply the instruction state to the node's current state
172 for (int i = 0; i < transaction->instructions->length; ++i) { 293 for (int i = 0; i < transaction->instructions->length; ++i) {
173 struct sway_transaction_instruction *instruction = 294 struct sway_transaction_instruction *instruction =
174 transaction->instructions->items[i]; 295 transaction->instructions->items[i];
175 struct sway_container *container = instruction->container; 296 struct sway_node *node = instruction->node;
176
177 // Damage the old location
178 desktop_damage_whole_container(container);
179 if (container->type == C_VIEW && container->sway_view->saved_buffer) {
180 struct sway_view *view = container->sway_view;
181 struct wlr_box box = {
182 .x = container->current.view_x - view->saved_geometry.x,
183 .y = container->current.view_y - view->saved_geometry.y,
184 .width = view->saved_buffer_width,
185 .height = view->saved_buffer_height,
186 };
187 desktop_damage_box(&box);
188 }
189
190 // There are separate children lists for each instruction state, the
191 // container's current state and the container's pending state
192 // (ie. con->children). The list itself needs to be freed here.
193 // Any child containers which are being deleted will be cleaned up in
194 // transaction_destroy().
195 list_free(container->current.children);
196 list_free(container->current.ws_floating);
197
198 memcpy(&container->current, &instruction->state,
199 sizeof(struct sway_container_state));
200
201 if (container->type == C_VIEW && container->sway_view->saved_buffer) {
202 if (!container->destroying || container->ntxnrefs == 1) {
203 view_remove_saved_buffer(container->sway_view);
204 }
205 }
206 297
207 // Damage the new location 298 switch (node->type) {
208 desktop_damage_whole_container(container); 299 case N_ROOT:
209 if (container->type == C_VIEW && container->sway_view->surface) { 300 break;
210 struct sway_view *view = container->sway_view; 301 case N_OUTPUT:
211 struct wlr_surface *surface = view->surface; 302 apply_output_state(node->sway_output, instruction->output_state);
212 struct wlr_box box = { 303 break;
213 .x = container->current.view_x - view->geometry.x, 304 case N_WORKSPACE:
214 .y = container->current.view_y - view->geometry.y, 305 apply_workspace_state(node->sway_workspace,
215 .width = surface->current.width, 306 instruction->workspace_state);
216 .height = surface->current.height, 307 break;
217 }; 308 case N_CONTAINER:
218 desktop_damage_box(&box); 309 apply_container_state(node->sway_container,
310 instruction->container_state);
311 break;
219 } 312 }
220 313
221 container->instruction = NULL; 314 node->instruction = NULL;
222 if (container->type == C_CONTAINER || container->type == C_VIEW) {
223 container_discover_outputs(container);
224 }
225 } 315 }
226} 316}
227 317
228static void transaction_commit(struct sway_transaction *transaction); 318static void transaction_commit(struct sway_transaction *transaction);
229 319
230// Return true if both transactions operate on the same containers 320// Return true if both transactions operate on the same nodes
231static bool transaction_same_containers(struct sway_transaction *a, 321static bool transaction_same_nodes(struct sway_transaction *a,
232 struct sway_transaction *b) { 322 struct sway_transaction *b) {
233 if (a->instructions->length != b->instructions->length) { 323 if (a->instructions->length != b->instructions->length) {
234 return false; 324 return false;
@@ -236,7 +326,7 @@ static bool transaction_same_containers(struct sway_transaction *a,
236 for (int i = 0; i < a->instructions->length; ++i) { 326 for (int i = 0; i < a->instructions->length; ++i) {
237 struct sway_transaction_instruction *a_inst = a->instructions->items[i]; 327 struct sway_transaction_instruction *a_inst = a->instructions->items[i];
238 struct sway_transaction_instruction *b_inst = b->instructions->items[i]; 328 struct sway_transaction_instruction *b_inst = b->instructions->items[i];
239 if (a_inst->container != b_inst->container) { 329 if (a_inst->node != b_inst->node) {
240 return false; 330 return false;
241 } 331 }
242 } 332 }
@@ -267,7 +357,7 @@ static void transaction_progress_queue() {
267 while (server.transactions->length >= 2) { 357 while (server.transactions->length >= 2) {
268 struct sway_transaction *a = server.transactions->items[0]; 358 struct sway_transaction *a = server.transactions->items[0];
269 struct sway_transaction *b = server.transactions->items[1]; 359 struct sway_transaction *b = server.transactions->items[1];
270 if (transaction_same_containers(a, b)) { 360 if (transaction_same_nodes(a, b)) {
271 list_del(server.transactions, 0); 361 list_del(server.transactions, 0);
272 transaction_destroy(a); 362 transaction_destroy(a);
273 } else { 363 } else {
@@ -289,16 +379,18 @@ static int handle_timeout(void *data) {
289 return 0; 379 return 0;
290} 380}
291 381
292static bool should_configure(struct sway_container *con, 382static bool should_configure(struct sway_node *node,
293 struct sway_transaction_instruction *instruction) { 383 struct sway_transaction_instruction *instruction) {
294 if (con->type != C_VIEW) { 384 if (!node_is_view(node)) {
295 return false; 385 return false;
296 } 386 }
297 if (con->destroying) { 387 if (node->destroying) {
298 return false; 388 return false;
299 } 389 }
300 if (con->current.view_width == instruction->state.view_width && 390 struct sway_container_state *cstate = &node->sway_container->current;
301 con->current.view_height == instruction->state.view_height) { 391 struct sway_container_state *istate = instruction->container_state;
392 if (cstate->view_width == istate->view_width &&
393 cstate->view_height == istate->view_height) {
302 return false; 394 return false;
303 } 395 }
304 return true; 396 return true;
@@ -311,13 +403,13 @@ static void transaction_commit(struct sway_transaction *transaction) {
311 for (int i = 0; i < transaction->instructions->length; ++i) { 403 for (int i = 0; i < transaction->instructions->length; ++i) {
312 struct sway_transaction_instruction *instruction = 404 struct sway_transaction_instruction *instruction =
313 transaction->instructions->items[i]; 405 transaction->instructions->items[i];
314 struct sway_container *con = instruction->container; 406 struct sway_node *node = instruction->node;
315 if (should_configure(con, instruction)) { 407 if (should_configure(node, instruction)) {
316 instruction->serial = view_configure(con->sway_view, 408 instruction->serial = view_configure(node->sway_container->view,
317 instruction->state.view_x, 409 instruction->container_state->view_x,
318 instruction->state.view_y, 410 instruction->container_state->view_y,
319 instruction->state.view_width, 411 instruction->container_state->view_width,
320 instruction->state.view_height); 412 instruction->container_state->view_height);
321 ++transaction->num_waiting; 413 ++transaction->num_waiting;
322 414
323 // From here on we are rendering a saved buffer of the view, which 415 // From here on we are rendering a saved buffer of the view, which
@@ -325,14 +417,16 @@ static void transaction_commit(struct sway_transaction *transaction) {
325 // as soon as possible. Additionally, this is required if a view is 417 // as soon as possible. Additionally, this is required if a view is
326 // mapping and its default geometry doesn't intersect an output. 418 // mapping and its default geometry doesn't intersect an output.
327 struct timespec when; 419 struct timespec when;
328 wlr_surface_send_frame_done(con->sway_view->surface, &when); 420 wlr_surface_send_frame_done(
421 node->sway_container->view->surface, &when);
329 } 422 }
330 if (con->type == C_VIEW && !con->sway_view->saved_buffer) { 423 if (node_is_view(node) && !node->sway_container->view->saved_buffer) {
331 view_save_buffer(con->sway_view); 424 view_save_buffer(node->sway_container->view);
332 memcpy(&con->sway_view->saved_geometry, &con->sway_view->geometry, 425 memcpy(&node->sway_container->view->saved_geometry,
426 &node->sway_container->view->geometry,
333 sizeof(struct wlr_box)); 427 sizeof(struct wlr_box));
334 } 428 }
335 con->instruction = instruction; 429 node->instruction = instruction;
336 } 430 }
337 transaction->num_configures = transaction->num_waiting; 431 transaction->num_configures = transaction->num_waiting;
338 if (debug.txn_timings) { 432 if (debug.txn_timings) {
@@ -381,7 +475,7 @@ static void set_instruction_ready(
381 transaction, 475 transaction,
382 transaction->num_configures - transaction->num_waiting + 1, 476 transaction->num_configures - transaction->num_waiting + 1,
383 transaction->num_configures, ms, 477 transaction->num_configures, ms,
384 instruction->container->name); 478 instruction->node->sway_container->title);
385 } 479 }
386 480
387 // If the transaction has timed out then its num_waiting will be 0 already. 481 // If the transaction has timed out then its num_waiting will be 0 already.
@@ -390,41 +484,43 @@ static void set_instruction_ready(
390 wl_event_source_timer_update(transaction->timer, 0); 484 wl_event_source_timer_update(transaction->timer, 0);
391 } 485 }
392 486
393 instruction->container->instruction = NULL; 487 instruction->node->instruction = NULL;
394 transaction_progress_queue(); 488 transaction_progress_queue();
395} 489}
396 490
397void transaction_notify_view_ready_by_serial(struct sway_view *view, 491void transaction_notify_view_ready_by_serial(struct sway_view *view,
398 uint32_t serial) { 492 uint32_t serial) {
399 struct sway_transaction_instruction *instruction = view->swayc->instruction; 493 struct sway_transaction_instruction *instruction =
400 if (view->swayc->instruction->serial == serial) { 494 view->container->node.instruction;
495 if (instruction->serial == serial) {
401 set_instruction_ready(instruction); 496 set_instruction_ready(instruction);
402 } 497 }
403} 498}
404 499
405void transaction_notify_view_ready_by_size(struct sway_view *view, 500void transaction_notify_view_ready_by_size(struct sway_view *view,
406 int width, int height) { 501 int width, int height) {
407 struct sway_transaction_instruction *instruction = view->swayc->instruction; 502 struct sway_transaction_instruction *instruction =
408 if (instruction->state.view_width == width && 503 view->container->node.instruction;
409 instruction->state.view_height == height) { 504 if (instruction->container_state->view_width == width &&
505 instruction->container_state->view_height == height) {
410 set_instruction_ready(instruction); 506 set_instruction_ready(instruction);
411 } 507 }
412} 508}
413 509
414void transaction_commit_dirty(void) { 510void transaction_commit_dirty(void) {
415 if (!server.dirty_containers->length) { 511 if (!server.dirty_nodes->length) {
416 return; 512 return;
417 } 513 }
418 struct sway_transaction *transaction = transaction_create(); 514 struct sway_transaction *transaction = transaction_create();
419 if (!transaction) { 515 if (!transaction) {
420 return; 516 return;
421 } 517 }
422 for (int i = 0; i < server.dirty_containers->length; ++i) { 518 for (int i = 0; i < server.dirty_nodes->length; ++i) {
423 struct sway_container *container = server.dirty_containers->items[i]; 519 struct sway_node *node = server.dirty_nodes->items[i];
424 transaction_add_container(transaction, container); 520 transaction_add_node(transaction, node);
425 container->dirty = false; 521 node->dirty = false;
426 } 522 }
427 server.dirty_containers->length = 0; 523 server.dirty_nodes->length = 0;
428 524
429 list_add(server.transactions, transaction); 525 list_add(server.transactions, transaction);
430 526
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 587deb0f..e19d8e18 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -11,10 +11,12 @@
11#include "sway/desktop/transaction.h" 11#include "sway/desktop/transaction.h"
12#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
13#include "sway/input/seat.h" 13#include "sway/input/seat.h"
14#include "sway/output.h"
14#include "sway/server.h" 15#include "sway/server.h"
15#include "sway/tree/arrange.h" 16#include "sway/tree/arrange.h"
16#include "sway/tree/container.h" 17#include "sway/tree/container.h"
17#include "sway/tree/view.h" 18#include "sway/tree/view.h"
19#include "sway/tree/workspace.h"
18 20
19static const struct sway_view_child_impl popup_impl; 21static const struct sway_view_child_impl popup_impl;
20 22
@@ -52,15 +54,15 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
52 struct sway_view *view = popup->child.view; 54 struct sway_view *view = popup->child.view;
53 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 55 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
54 56
55 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 57 struct sway_output *output = view->container->workspace->output;
56 58
57 // the output box expressed in the coordinate system of the toplevel parent 59 // the output box expressed in the coordinate system of the toplevel parent
58 // of the popup 60 // of the popup
59 struct wlr_box output_toplevel_sx_box = { 61 struct wlr_box output_toplevel_sx_box = {
60 .x = output->x - view->x, 62 .x = output->wlr_output->lx - view->x,
61 .y = output->y - view->y, 63 .y = output->wlr_output->ly - view->y,
62 .width = output->width, 64 .width = output->wlr_output->width,
63 .height = output->height, 65 .height = output->wlr_output->height,
64 }; 66 };
65 67
66 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 68 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@@ -252,11 +254,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
252 struct sway_view *view = &xdg_shell_view->view; 254 struct sway_view *view = &xdg_shell_view->view;
253 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 255 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
254 256
255 if (!view->swayc) { 257 if (view->container->node.instruction) {
256 return;
257 }
258
259 if (view->swayc->instruction) {
260 wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry); 258 wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry);
261 transaction_notify_view_ready_by_serial(view, 259 transaction_notify_view_ready_by_serial(view,
262 xdg_surface->configure_serial); 260 xdg_surface->configure_serial);
@@ -265,7 +263,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
265 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 263 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
266 264
267 if ((new_geo.width != view->width || new_geo.height != view->height) && 265 if ((new_geo.width != view->width || new_geo.height != view->height) &&
268 container_is_floating(view->swayc)) { 266 container_is_floating(view->container)) {
269 // A floating view has unexpectedly sent a new size 267 // A floating view has unexpectedly sent a new size
270 desktop_damage_view(view); 268 desktop_damage_view(view);
271 view_update_size(view, new_geo.width, new_geo.height); 269 view_update_size(view, new_geo.width, new_geo.height);
@@ -319,10 +317,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
319 return; 317 return;
320 } 318 }
321 319
322 container_set_fullscreen(view->swayc, e->fullscreen); 320 container_set_fullscreen(view->container, e->fullscreen);
323 321
324 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 322 arrange_workspace(view->container->workspace);
325 arrange_windows(output);
326 transaction_commit_dirty(); 323 transaction_commit_dirty();
327} 324}
328 325
@@ -330,13 +327,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
330 struct sway_xdg_shell_view *xdg_shell_view = 327 struct sway_xdg_shell_view *xdg_shell_view =
331 wl_container_of(listener, xdg_shell_view, request_move); 328 wl_container_of(listener, xdg_shell_view, request_move);
332 struct sway_view *view = &xdg_shell_view->view; 329 struct sway_view *view = &xdg_shell_view->view;
333 if (!container_is_floating(view->swayc)) { 330 if (!container_is_floating(view->container)) {
334 return; 331 return;
335 } 332 }
336 struct wlr_xdg_toplevel_move_event *e = data; 333 struct wlr_xdg_toplevel_move_event *e = data;
337 struct sway_seat *seat = e->seat->seat->data; 334 struct sway_seat *seat = e->seat->seat->data;
338 if (e->serial == seat->last_button_serial) { 335 if (e->serial == seat->last_button_serial) {
339 seat_begin_move(seat, view->swayc, seat->last_button); 336 seat_begin_move(seat, view->container, seat->last_button);
340 } 337 }
341} 338}
342 339
@@ -344,13 +341,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
344 struct sway_xdg_shell_view *xdg_shell_view = 341 struct sway_xdg_shell_view *xdg_shell_view =
345 wl_container_of(listener, xdg_shell_view, request_resize); 342 wl_container_of(listener, xdg_shell_view, request_resize);
346 struct sway_view *view = &xdg_shell_view->view; 343 struct sway_view *view = &xdg_shell_view->view;
347 if (!container_is_floating(view->swayc)) { 344 if (!container_is_floating(view->container)) {
348 return; 345 return;
349 } 346 }
350 struct wlr_xdg_toplevel_resize_event *e = data; 347 struct wlr_xdg_toplevel_resize_event *e = data;
351 struct sway_seat *seat = e->seat->seat->data; 348 struct sway_seat *seat = e->seat->seat->data;
352 if (e->serial == seat->last_button_serial) { 349 if (e->serial == seat->last_button_serial) {
353 seat_begin_resize_floating(seat, view->swayc, 350 seat_begin_resize_floating(seat, view->container,
354 seat->last_button, e->edges); 351 seat->last_button, e->edges);
355 } 352 }
356} 353}
@@ -399,11 +396,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
399 view_map(view, view->wlr_xdg_surface->surface); 396 view_map(view, view->wlr_xdg_surface->surface);
400 397
401 if (xdg_surface->toplevel->client_pending.fullscreen) { 398 if (xdg_surface->toplevel->client_pending.fullscreen) {
402 container_set_fullscreen(view->swayc, true); 399 container_set_fullscreen(view->container, true);
403 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 400 arrange_workspace(view->container->workspace);
404 arrange_windows(ws);
405 } else { 401 } else {
406 arrange_windows(view->swayc->parent); 402 if (view->container->parent) {
403 arrange_container(view->container->parent);
404 } else {
405 arrange_workspace(view->container->workspace);
406 }
407 } 407 }
408 transaction_commit_dirty(); 408 transaction_commit_dirty();
409 409
@@ -440,8 +440,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
440 struct sway_xdg_shell_view *xdg_shell_view = 440 struct sway_xdg_shell_view *xdg_shell_view =
441 wl_container_of(listener, xdg_shell_view, destroy); 441 wl_container_of(listener, xdg_shell_view, destroy);
442 struct sway_view *view = &xdg_shell_view->view; 442 struct sway_view *view = &xdg_shell_view->view;
443 if (!sway_assert(view->swayc == NULL || view->swayc->destroying, 443 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
444 "Tried to destroy a mapped view")) {
445 return; 444 return;
446 } 445 }
447 wl_list_remove(&xdg_shell_view->destroy.link); 446 wl_list_remove(&xdg_shell_view->destroy.link);
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 175416f3..b23d4577 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -10,10 +10,12 @@
10#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
11#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
12#include "sway/input/seat.h" 12#include "sway/input/seat.h"
13#include "sway/output.h"
13#include "sway/server.h" 14#include "sway/server.h"
14#include "sway/tree/arrange.h" 15#include "sway/tree/arrange.h"
15#include "sway/tree/container.h" 16#include "sway/tree/container.h"
16#include "sway/tree/view.h" 17#include "sway/tree/view.h"
18#include "sway/tree/workspace.h"
17 19
18static const struct sway_view_child_impl popup_impl; 20static const struct sway_view_child_impl popup_impl;
19 21
@@ -51,15 +53,15 @@ static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
51 struct sway_view *view = popup->child.view; 53 struct sway_view *view = popup->child.view;
52 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; 54 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup;
53 55
54 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 56 struct sway_output *output = view->container->workspace->output;
55 57
56 // the output box expressed in the coordinate system of the toplevel parent 58 // the output box expressed in the coordinate system of the toplevel parent
57 // of the popup 59 // of the popup
58 struct wlr_box output_toplevel_sx_box = { 60 struct wlr_box output_toplevel_sx_box = {
59 .x = output->x - view->x, 61 .x = output->wlr_output->lx - view->x,
60 .y = output->y - view->y, 62 .y = output->wlr_output->ly - view->y,
61 .width = output->width, 63 .width = output->wlr_output->width,
62 .height = output->height, 64 .height = output->wlr_output->height,
63 }; 65 };
64 66
65 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 67 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@@ -249,11 +251,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
249 struct sway_view *view = &xdg_shell_v6_view->view; 251 struct sway_view *view = &xdg_shell_v6_view->view;
250 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; 252 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
251 253
252 if (!view->swayc) { 254 if (view->container->node.instruction) {
253 return;
254 }
255
256 if (view->swayc->instruction) {
257 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry); 255 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry);
258 transaction_notify_view_ready_by_serial(view, 256 transaction_notify_view_ready_by_serial(view,
259 xdg_surface_v6->configure_serial); 257 xdg_surface_v6->configure_serial);
@@ -262,7 +260,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
262 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); 260 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo);
263 261
264 if ((new_geo.width != view->width || new_geo.height != view->height) && 262 if ((new_geo.width != view->width || new_geo.height != view->height) &&
265 container_is_floating(view->swayc)) { 263 container_is_floating(view->container)) {
266 // A floating view has unexpectedly sent a new size 264 // A floating view has unexpectedly sent a new size
267 desktop_damage_view(view); 265 desktop_damage_view(view);
268 view_update_size(view, new_geo.width, new_geo.height); 266 view_update_size(view, new_geo.width, new_geo.height);
@@ -316,10 +314,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
316 return; 314 return;
317 } 315 }
318 316
319 container_set_fullscreen(view->swayc, e->fullscreen); 317 container_set_fullscreen(view->container, e->fullscreen);
320 318
321 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 319 arrange_workspace(view->container->workspace);
322 arrange_windows(output);
323 transaction_commit_dirty(); 320 transaction_commit_dirty();
324} 321}
325 322
@@ -327,13 +324,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
327 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 324 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
328 wl_container_of(listener, xdg_shell_v6_view, request_move); 325 wl_container_of(listener, xdg_shell_v6_view, request_move);
329 struct sway_view *view = &xdg_shell_v6_view->view; 326 struct sway_view *view = &xdg_shell_v6_view->view;
330 if (!container_is_floating(view->swayc)) { 327 if (!container_is_floating(view->container)) {
331 return; 328 return;
332 } 329 }
333 struct wlr_xdg_toplevel_v6_move_event *e = data; 330 struct wlr_xdg_toplevel_v6_move_event *e = data;
334 struct sway_seat *seat = e->seat->seat->data; 331 struct sway_seat *seat = e->seat->seat->data;
335 if (e->serial == seat->last_button_serial) { 332 if (e->serial == seat->last_button_serial) {
336 seat_begin_move(seat, view->swayc, seat->last_button); 333 seat_begin_move(seat, view->container, seat->last_button);
337 } 334 }
338} 335}
339 336
@@ -341,13 +338,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
341 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 338 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
342 wl_container_of(listener, xdg_shell_v6_view, request_resize); 339 wl_container_of(listener, xdg_shell_v6_view, request_resize);
343 struct sway_view *view = &xdg_shell_v6_view->view; 340 struct sway_view *view = &xdg_shell_v6_view->view;
344 if (!container_is_floating(view->swayc)) { 341 if (!container_is_floating(view->container)) {
345 return; 342 return;
346 } 343 }
347 struct wlr_xdg_toplevel_v6_resize_event *e = data; 344 struct wlr_xdg_toplevel_v6_resize_event *e = data;
348 struct sway_seat *seat = e->seat->seat->data; 345 struct sway_seat *seat = e->seat->seat->data;
349 if (e->serial == seat->last_button_serial) { 346 if (e->serial == seat->last_button_serial) {
350 seat_begin_resize_floating(seat, view->swayc, 347 seat_begin_resize_floating(seat, view->container,
351 seat->last_button, e->edges); 348 seat->last_button, e->edges);
352 } 349 }
353} 350}
@@ -396,11 +393,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
396 view_map(view, view->wlr_xdg_surface_v6->surface); 393 view_map(view, view->wlr_xdg_surface_v6->surface);
397 394
398 if (xdg_surface->toplevel->client_pending.fullscreen) { 395 if (xdg_surface->toplevel->client_pending.fullscreen) {
399 container_set_fullscreen(view->swayc, true); 396 container_set_fullscreen(view->container, true);
400 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 397 arrange_workspace(view->container->workspace);
401 arrange_windows(ws);
402 } else { 398 } else {
403 arrange_windows(view->swayc->parent); 399 if (view->container->parent) {
400 arrange_container(view->container->parent);
401 } else {
402 arrange_workspace(view->container->workspace);
403 }
404 } 404 }
405 transaction_commit_dirty(); 405 transaction_commit_dirty();
406 406
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 94a30239..0d192b76 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -59,8 +59,7 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
59 wl_container_of(listener, surface, map); 59 wl_container_of(listener, surface, map);
60 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 60 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
61 61
62 wl_list_insert(root_container.sway_root->xwayland_unmanaged.prev, 62 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link);
63 &surface->link);
64 63
65 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 64 wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
66 surface->commit.notify = unmanaged_handle_commit; 65 surface->commit.notify = unmanaged_handle_commit;
@@ -90,11 +89,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
90 if (seat->wlr_seat->keyboard_state.focused_surface == 89 if (seat->wlr_seat->keyboard_state.focused_surface ==
91 xsurface->surface) { 90 xsurface->surface) {
92 // Restore focus 91 // Restore focus
93 struct sway_container *previous = 92 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
94 seat_get_focus_inactive(seat, &root_container);
95 if (previous) { 93 if (previous) {
96 // Hack to get seat to re-focus the return value of get_focus 94 // Hack to get seat to re-focus the return value of get_focus
97 seat_set_focus(seat, previous->parent); 95 seat_set_focus(seat, NULL);
98 seat_set_focus(seat, previous); 96 seat_set_focus(seat, previous);
99 } 97 }
100 } 98 }
@@ -299,7 +297,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
299 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 297 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
300 struct wlr_surface_state *state = &xsurface->surface->current; 298 struct wlr_surface_state *state = &xsurface->surface->current;
301 299
302 if (view->swayc->instruction) { 300 if (view->container->node.instruction) {
303 get_geometry(view, &view->geometry); 301 get_geometry(view, &view->geometry);
304 transaction_notify_view_ready_by_size(view, 302 transaction_notify_view_ready_by_size(view,
305 state->width, state->height); 303 state->width, state->height);
@@ -308,7 +306,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
308 get_geometry(view, &new_geo); 306 get_geometry(view, &new_geo);
309 307
310 if ((new_geo.width != view->width || new_geo.height != view->height) && 308 if ((new_geo.width != view->width || new_geo.height != view->height) &&
311 container_is_floating(view->swayc)) { 309 container_is_floating(view->container)) {
312 // A floating view has unexpectedly sent a new size 310 // A floating view has unexpectedly sent a new size
313 // eg. The Firefox "Save As" dialog when downloading a file 311 // eg. The Firefox "Save As" dialog when downloading a file
314 desktop_damage_view(view); 312 desktop_damage_view(view);
@@ -391,11 +389,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
391 view_map(view, xsurface->surface); 389 view_map(view, xsurface->surface);
392 390
393 if (xsurface->fullscreen) { 391 if (xsurface->fullscreen) {
394 container_set_fullscreen(view->swayc, true); 392 container_set_fullscreen(view->container, true);
395 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 393 arrange_workspace(view->container->workspace);
396 arrange_windows(ws);
397 } else { 394 } else {
398 arrange_windows(view->swayc->parent); 395 if (view->container->parent) {
396 arrange_container(view->container->parent);
397 } else {
398 arrange_workspace(view->container->workspace);
399 }
399 } 400 }
400 transaction_commit_dirty(); 401 transaction_commit_dirty();
401} 402}
@@ -411,13 +412,14 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
411 ev->width, ev->height); 412 ev->width, ev->height);
412 return; 413 return;
413 } 414 }
414 if (container_is_floating(view->swayc)) { 415 if (container_is_floating(view->container)) {
415 configure(view, view->swayc->current.view_x, 416 configure(view, view->container->current.view_x,
416 view->swayc->current.view_y, ev->width, ev->height); 417 view->container->current.view_y, ev->width, ev->height);
417 } else { 418 } else {
418 configure(view, view->swayc->current.view_x, 419 configure(view, view->container->current.view_x,
419 view->swayc->current.view_y, view->swayc->current.view_width, 420 view->container->current.view_y,
420 view->swayc->current.view_height); 421 view->container->current.view_width,
422 view->container->current.view_height);
421 } 423 }
422} 424}
423 425
@@ -429,10 +431,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
429 if (!xsurface->mapped) { 431 if (!xsurface->mapped) {
430 return; 432 return;
431 } 433 }
432 container_set_fullscreen(view->swayc, xsurface->fullscreen); 434 container_set_fullscreen(view->container, xsurface->fullscreen);
433 435
434 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 436 arrange_workspace(view->container->workspace);
435 arrange_windows(output);
436 transaction_commit_dirty(); 437 transaction_commit_dirty();
437} 438}
438 439
@@ -444,11 +445,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
444 if (!xsurface->mapped) { 445 if (!xsurface->mapped) {
445 return; 446 return;
446 } 447 }
447 if (!container_is_floating(view->swayc)) { 448 if (!container_is_floating(view->container)) {
448 return; 449 return;
449 } 450 }
450 struct sway_seat *seat = input_manager_current_seat(input_manager); 451 struct sway_seat *seat = input_manager_current_seat(input_manager);
451 seat_begin_move(seat, view->swayc, seat->last_button); 452 seat_begin_move(seat, view->container, seat->last_button);
452} 453}
453 454
454static void handle_request_resize(struct wl_listener *listener, void *data) { 455static void handle_request_resize(struct wl_listener *listener, void *data) {
@@ -459,12 +460,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
459 if (!xsurface->mapped) { 460 if (!xsurface->mapped) {
460 return; 461 return;
461 } 462 }
462 if (!container_is_floating(view->swayc)) { 463 if (!container_is_floating(view->container)) {
463 return; 464 return;
464 } 465 }
465 struct wlr_xwayland_resize_event *e = data; 466 struct wlr_xwayland_resize_event *e = data;
466 struct sway_seat *seat = input_manager_current_seat(input_manager); 467 struct sway_seat *seat = input_manager_current_seat(input_manager);
467 seat_begin_resize_floating(seat, view->swayc, seat->last_button, e->edges); 468 seat_begin_resize_floating(seat, view->container,
469 seat->last_button, e->edges);
468} 470}
469 471
470static void handle_request_activate(struct wl_listener *listener, void *data) { 472static void handle_request_activate(struct wl_listener *listener, void *data) {
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 00240e84..15993265 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -20,6 +20,7 @@
20#include "sway/layers.h" 20#include "sway/layers.h"
21#include "sway/output.h" 21#include "sway/output.h"
22#include "sway/tree/arrange.h" 22#include "sway/tree/arrange.h"
23#include "sway/tree/container.h"
23#include "sway/tree/root.h" 24#include "sway/tree/root.h"
24#include "sway/tree/view.h" 25#include "sway/tree/view.h"
25#include "sway/tree/workspace.h" 26#include "sway/tree/workspace.h"
@@ -50,15 +51,15 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
50} 51}
51 52
52/** 53/**
53 * Returns the container at the cursor's position. If there is a surface at that 54 * Returns the node at the cursor's position. If there is a surface at that
54 * location, it is stored in **surface (it may not be a view). 55 * location, it is stored in **surface (it may not be a view).
55 */ 56 */
56static struct sway_container *container_at_coords( 57static struct sway_node *node_at_coords(
57 struct sway_seat *seat, double lx, double ly, 58 struct sway_seat *seat, double lx, double ly,
58 struct wlr_surface **surface, double *sx, double *sy) { 59 struct wlr_surface **surface, double *sx, double *sy) {
59 // check for unmanaged views first 60 // check for unmanaged views first
60#ifdef HAVE_XWAYLAND 61#ifdef HAVE_XWAYLAND
61 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; 62 struct wl_list *unmanaged = &root->xwayland_unmanaged;
62 struct sway_xwayland_unmanaged *unmanaged_surface; 63 struct sway_xwayland_unmanaged *unmanaged_surface;
63 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { 64 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
64 struct wlr_xwayland_surface *xsurface = 65 struct wlr_xwayland_surface *xsurface =
@@ -75,67 +76,64 @@ static struct sway_container *container_at_coords(
75 } 76 }
76#endif 77#endif
77 // find the output the cursor is on 78 // find the output the cursor is on
78 struct wlr_output_layout *output_layout =
79 root_container.sway_root->output_layout;
80 struct wlr_output *wlr_output = wlr_output_layout_output_at( 79 struct wlr_output *wlr_output = wlr_output_layout_output_at(
81 output_layout, lx, ly); 80 root->output_layout, lx, ly);
82 if (wlr_output == NULL) { 81 if (wlr_output == NULL) {
83 return NULL; 82 return NULL;
84 } 83 }
85 struct sway_output *output = wlr_output->data; 84 struct sway_output *output = wlr_output->data;
86 double ox = lx, oy = ly; 85 double ox = lx, oy = ly;
87 wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); 86 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
88 87
89 // find the focused workspace on the output for this seat 88 // find the focused workspace on the output for this seat
90 struct sway_container *ws = seat_get_focus_inactive(seat, output->swayc); 89 struct sway_workspace *ws = output_get_active_workspace(output);
91 if (ws && ws->type != C_WORKSPACE) {
92 ws = container_parent(ws, C_WORKSPACE);
93 }
94 if (!ws) {
95 return output->swayc;
96 }
97 90
98 if ((*surface = layer_surface_at(output, 91 if ((*surface = layer_surface_at(output,
99 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 92 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
100 ox, oy, sx, sy))) { 93 ox, oy, sx, sy))) {
101 return ws; 94 return &ws->node;
102 } 95 }
103 if (ws->sway_workspace->fullscreen) { 96 if (ws->fullscreen) {
104 return tiling_container_at(ws->sway_workspace->fullscreen, lx, ly, 97 struct sway_container *con =
105 surface, sx, sy); 98 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
99 if (con) {
100 return &con->node;
101 }
102 return NULL;
106 } 103 }
107 if ((*surface = layer_surface_at(output, 104 if ((*surface = layer_surface_at(output,
108 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 105 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
109 ox, oy, sx, sy))) { 106 ox, oy, sx, sy))) {
110 return ws; 107 return &ws->node;
111 } 108 }
112 109
113 struct sway_container *c; 110 struct sway_container *c;
114 if ((c = container_at(ws, lx, ly, surface, sx, sy))) { 111 if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
115 return c; 112 return &c->node;
116 } 113 }
117 114
118 if ((*surface = layer_surface_at(output, 115 if ((*surface = layer_surface_at(output,
119 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], 116 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
120 ox, oy, sx, sy))) { 117 ox, oy, sx, sy))) {
121 return ws; 118 return &ws->node;
122 } 119 }
123 if ((*surface = layer_surface_at(output, 120 if ((*surface = layer_surface_at(output,
124 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 121 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
125 ox, oy, sx, sy))) { 122 ox, oy, sx, sy))) {
126 return ws; 123 return &ws->node;
127 } 124 }
128 125
129 c = seat_get_active_child(seat, output->swayc); 126 return &ws->node;
130 if (c) { 127}
131 return c;
132 }
133 if (!c && output->swayc->children->length) {
134 c = output->swayc->children->items[0];
135 return c;
136 }
137 128
138 return output->swayc; 129static struct sway_container *container_at_coords(struct sway_seat *seat,
130 double lx, double ly,
131 struct wlr_surface **surface, double *sx, double *sy) {
132 struct sway_node *node = node_at_coords(seat, lx, ly, surface, sx, sy);
133 if (node && node->type == N_CONTAINER) {
134 return node->sway_container;
135 }
136 return NULL;
139} 137}
140 138
141/** 139/**
@@ -160,13 +158,14 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
160 158
161 // Iterate the parents until we find one with the layout we want, 159 // Iterate the parents until we find one with the layout we want,
162 // then check if the child has siblings between it and the edge. 160 // then check if the child has siblings between it and the edge.
163 while (cont->type != C_OUTPUT) { 161 while (cont) {
164 if (cont->parent->layout == layout) { 162 if (container_parent_layout(cont) == layout) {
165 int index = list_find(cont->parent->children, cont); 163 list_t *siblings = container_get_siblings(cont);
164 int index = list_find(siblings, cont);
166 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { 165 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
167 return false; 166 return false;
168 } 167 }
169 if (index < cont->parent->children->length - 1 && 168 if (index < siblings->length - 1 &&
170 (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { 169 (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) {
171 return false; 170 return false;
172 } 171 }
@@ -178,10 +177,10 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
178 177
179static enum wlr_edges find_edge(struct sway_container *cont, 178static enum wlr_edges find_edge(struct sway_container *cont,
180 struct sway_cursor *cursor) { 179 struct sway_cursor *cursor) {
181 if (cont->type != C_VIEW) { 180 if (!cont->view) {
182 return WLR_EDGE_NONE; 181 return WLR_EDGE_NONE;
183 } 182 }
184 struct sway_view *view = cont->sway_view; 183 struct sway_view *view = cont->view;
185 if (view->border == B_NONE || !view->border_thickness || view->using_csd) { 184 if (view->border == B_NONE || !view->border_thickness || view->using_csd) {
186 return WLR_EDGE_NONE; 185 return WLR_EDGE_NONE;
187 } 186 }
@@ -219,7 +218,7 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont,
219static void handle_down_motion(struct sway_seat *seat, 218static void handle_down_motion(struct sway_seat *seat,
220 struct sway_cursor *cursor, uint32_t time_msec) { 219 struct sway_cursor *cursor, uint32_t time_msec) {
221 struct sway_container *con = seat->op_container; 220 struct sway_container *con = seat->op_container;
222 if (seat_is_input_allowed(seat, con->sway_view->surface)) { 221 if (seat_is_input_allowed(seat, con->view->surface)) {
223 double moved_x = cursor->cursor->x - seat->op_ref_lx; 222 double moved_x = cursor->cursor->x - seat->op_ref_lx;
224 double moved_y = cursor->cursor->y - seat->op_ref_ly; 223 double moved_y = cursor->cursor->y - seat->op_ref_ly;
225 double sx = seat->op_ref_con_lx + moved_x; 224 double sx = seat->op_ref_con_lx + moved_x;
@@ -260,8 +259,7 @@ static void calculate_floating_constraints(struct sway_container *con,
260 if (config->floating_maximum_width == -1) { // no maximum 259 if (config->floating_maximum_width == -1) { // no maximum
261 *max_width = INT_MAX; 260 *max_width = INT_MAX;
262 } else if (config->floating_maximum_width == 0) { // automatic 261 } else if (config->floating_maximum_width == 0) { // automatic
263 struct sway_container *ws = container_parent(con, C_WORKSPACE); 262 *max_width = con->workspace->width;
264 *max_width = ws->width;
265 } else { 263 } else {
266 *max_width = config->floating_maximum_width; 264 *max_width = config->floating_maximum_width;
267 } 265 }
@@ -269,8 +267,7 @@ static void calculate_floating_constraints(struct sway_container *con,
269 if (config->floating_maximum_height == -1) { // no maximum 267 if (config->floating_maximum_height == -1) { // no maximum
270 *max_height = INT_MAX; 268 *max_height = INT_MAX;
271 } else if (config->floating_maximum_height == 0) { // automatic 269 } else if (config->floating_maximum_height == 0) { // automatic
272 struct sway_container *ws = container_parent(con, C_WORKSPACE); 270 *max_height = con->workspace->height;
273 *max_height = ws->height;
274 } else { 271 } else {
275 *max_height = config->floating_maximum_height; 272 *max_height = config->floating_maximum_height;
276 } 273 }
@@ -314,9 +311,9 @@ static void handle_resize_floating_motion(struct sway_seat *seat,
314 height = fmax(min_height, fmin(height, max_height)); 311 height = fmax(min_height, fmin(height, max_height));
315 312
316 // Apply the view's min/max size 313 // Apply the view's min/max size
317 if (con->type == C_VIEW) { 314 if (con->view) {
318 double view_min_width, view_max_width, view_min_height, view_max_height; 315 double view_min_width, view_max_width, view_min_height, view_max_height;
319 view_get_constraints(con->sway_view, &view_min_width, &view_max_width, 316 view_get_constraints(con->view, &view_min_width, &view_max_width,
320 &view_min_height, &view_max_height); 317 &view_min_height, &view_max_height);
321 width = fmax(view_min_width, fmin(width, view_max_width)); 318 width = fmax(view_min_width, fmin(width, view_max_width));
322 height = fmax(view_min_height, fmin(height, view_max_height)); 319 height = fmax(view_min_height, fmin(height, view_max_height));
@@ -357,15 +354,15 @@ static void handle_resize_floating_motion(struct sway_seat *seat,
357 con->width += relative_grow_width; 354 con->width += relative_grow_width;
358 con->height += relative_grow_height; 355 con->height += relative_grow_height;
359 356
360 if (con->type == C_VIEW) { 357 if (con->view) {
361 struct sway_view *view = con->sway_view; 358 struct sway_view *view = con->view;
362 view->x += relative_grow_x; 359 view->x += relative_grow_x;
363 view->y += relative_grow_y; 360 view->y += relative_grow_y;
364 view->width += relative_grow_width; 361 view->width += relative_grow_width;
365 view->height += relative_grow_height; 362 view->height += relative_grow_height;
366 } 363 }
367 364
368 arrange_windows(con); 365 arrange_container(con);
369} 366}
370 367
371static void handle_resize_tiling_motion(struct sway_seat *seat, 368static void handle_resize_tiling_motion(struct sway_seat *seat,
@@ -435,44 +432,40 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
435 struct wlr_surface *surface = NULL; 432 struct wlr_surface *surface = NULL;
436 double sx, sy; 433 double sx, sy;
437 434
438 // Find the container beneath the pointer's previous position 435 // Find the node beneath the pointer's previous position
439 struct sway_container *prev_c = container_at_coords(seat, 436 struct sway_node *prev_node = node_at_coords(seat,
440 cursor->previous.x, cursor->previous.y, &surface, &sx, &sy); 437 cursor->previous.x, cursor->previous.y, &surface, &sx, &sy);
441 // Update the stored previous position 438 // Update the stored previous position
442 cursor->previous.x = cursor->cursor->x; 439 cursor->previous.x = cursor->cursor->x;
443 cursor->previous.y = cursor->cursor->y; 440 cursor->previous.y = cursor->cursor->y;
444 441
445 struct sway_container *c = container_at_coords(seat, 442 struct sway_node *node = node_at_coords(seat,
446 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 443 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
447 if (c && config->focus_follows_mouse && allow_refocusing) { 444 if (node && config->focus_follows_mouse && allow_refocusing) {
448 struct sway_container *focus = seat_get_focus(seat); 445 struct sway_node *focus = seat_get_focus(seat);
449 if (focus && c->type == C_WORKSPACE) { 446 if (focus && node->type == N_WORKSPACE) {
450 // Only follow the mouse if it would move to a new output 447 // Only follow the mouse if it would move to a new output
451 // Otherwise we'll focus the workspace, which is probably wrong 448 // Otherwise we'll focus the workspace, which is probably wrong
452 if (focus->type != C_OUTPUT) { 449 struct sway_output *focused_output = node_get_output(focus);
453 focus = container_parent(focus, C_OUTPUT); 450 struct sway_output *output = node_get_output(node);
454 } 451 if (output != focused_output) {
455 struct sway_container *output = c; 452 seat_set_focus_warp(seat, node, false, true);
456 if (output->type != C_OUTPUT) {
457 output = container_parent(c, C_OUTPUT);
458 }
459 if (output != focus) {
460 seat_set_focus_warp(seat, c, false, true);
461 } 453 }
462 } else if (c->type == C_VIEW) { 454 } else if (node->type == N_CONTAINER && node->sway_container->view) {
463 // Focus c if the following are true: 455 // Focus node if the following are true:
464 // - cursor is over a new view, i.e. entered a new window; and 456 // - cursor is over a new view, i.e. entered a new window; and
465 // - the new view is visible, i.e. not hidden in a stack or tab; and 457 // - the new view is visible, i.e. not hidden in a stack or tab; and
466 // - the seat does not have a keyboard grab 458 // - the seat does not have a keyboard grab
467 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && 459 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
468 c != prev_c && 460 node != prev_node &&
469 view_is_visible(c->sway_view)) { 461 view_is_visible(node->sway_container->view)) {
470 seat_set_focus_warp(seat, c, false, true); 462 seat_set_focus_warp(seat, node, false, true);
471 } else { 463 } else {
472 struct sway_container *next_focus = 464 struct sway_node *next_focus =
473 seat_get_focus_inactive(seat, &root_container); 465 seat_get_focus_inactive(seat, &root->node);
474 if (next_focus && next_focus->type == C_VIEW && 466 if (next_focus && next_focus->type == N_CONTAINER &&
475 view_is_visible(next_focus->sway_view)) { 467 node->sway_container->view &&
468 view_is_visible(next_focus->sway_container->view)) {
476 seat_set_focus_warp(seat, next_focus, false, true); 469 seat_set_focus_warp(seat, next_focus, false, true);
477 } 470 }
478 } 471 }
@@ -486,12 +479,12 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
486 if (client != cursor->image_client) { 479 if (client != cursor->image_client) {
487 cursor_set_image(cursor, "left_ptr", client); 480 cursor_set_image(cursor, "left_ptr", client);
488 } 481 }
489 } else if (c) { 482 } else if (node && node->type == N_CONTAINER) {
490 // Try a container's resize edge 483 // Try a node's resize edge
491 enum wlr_edges edge = find_resize_edge(c, cursor); 484 enum wlr_edges edge = find_resize_edge(node->sway_container, cursor);
492 if (edge == WLR_EDGE_NONE) { 485 if (edge == WLR_EDGE_NONE) {
493 cursor_set_image(cursor, "left_ptr", NULL); 486 cursor_set_image(cursor, "left_ptr", NULL);
494 } else if (container_is_floating(c)) { 487 } else if (container_is_floating(node->sway_container)) {
495 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); 488 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
496 } else { 489 } else {
497 if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { 490 if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) {
@@ -684,7 +677,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
684 // Handle tiling resize via border 677 // Handle tiling resize via border
685 if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && 678 if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED &&
686 !is_floating) { 679 !is_floating) {
687 seat_set_focus(seat, cont); 680 seat_set_focus(seat, &cont->node);
688 seat_begin_resize_tiling(seat, cont, button, edge); 681 seat_begin_resize_tiling(seat, cont, button, edge);
689 return; 682 return;
690 } 683 }
@@ -713,7 +706,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
713 image = "sw-resize"; 706 image = "sw-resize";
714 } 707 }
715 cursor_set_image(seat->cursor, image, NULL); 708 cursor_set_image(seat->cursor, image, NULL);
716 seat_set_focus(seat, cont); 709 seat_set_focus(seat, &cont->node);
717 seat_begin_resize_tiling(seat, cont, button, edge); 710 seat_begin_resize_tiling(seat, cont, button, edge);
718 return; 711 return;
719 } 712 }
@@ -725,7 +718,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
725 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 718 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
726 if (button == btn_move && state == WLR_BUTTON_PRESSED && 719 if (button == btn_move && state == WLR_BUTTON_PRESSED &&
727 (mod_pressed || on_titlebar)) { 720 (mod_pressed || on_titlebar)) {
728 while (cont->parent->type != C_WORKSPACE) { 721 while (cont->parent) {
729 cont = cont->parent; 722 cont = cont->parent;
730 } 723 }
731 seat_begin_move(seat, cont, button); 724 seat_begin_move(seat, cont, button);
@@ -747,7 +740,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
747 BTN_LEFT : BTN_RIGHT; 740 BTN_LEFT : BTN_RIGHT;
748 if (mod_pressed && button == btn_resize) { 741 if (mod_pressed && button == btn_resize) {
749 struct sway_container *floater = cont; 742 struct sway_container *floater = cont;
750 while (floater->parent->type != C_WORKSPACE) { 743 while (floater->parent) {
751 floater = floater->parent; 744 floater = floater->parent;
752 } 745 }
753 edge = 0; 746 edge = 0;
@@ -762,7 +755,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
762 755
763 // Handle mousedown on a container surface 756 // Handle mousedown on a container surface
764 if (surface && cont && state == WLR_BUTTON_PRESSED) { 757 if (surface && cont && state == WLR_BUTTON_PRESSED) {
765 seat_set_focus(seat, cont); 758 seat_set_focus(seat, &cont->node);
766 seat_pointer_notify_button(seat, time_msec, button, state); 759 seat_pointer_notify_button(seat, time_msec, button, state);
767 seat_begin_down(seat, cont, button, sx, sy); 760 seat_begin_down(seat, cont, button, sx, sy);
768 return; 761 return;
@@ -770,7 +763,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
770 763
771 // Handle clicking a container surface 764 // Handle clicking a container surface
772 if (cont) { 765 if (cont) {
773 seat_set_focus(seat, cont); 766 seat_set_focus(seat, &cont->node);
774 seat_pointer_notify_button(seat, time_msec, button, state); 767 seat_pointer_notify_button(seat, time_msec, button, state);
775 return; 768 return;
776 } 769 }
@@ -1025,8 +1018,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1025 cursor->previous.y = wlr_cursor->y; 1018 cursor->previous.y = wlr_cursor->y;
1026 1019
1027 cursor->seat = seat; 1020 cursor->seat = seat;
1028 wlr_cursor_attach_output_layout(wlr_cursor, 1021 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout);
1029 root_container.sway_root->output_layout);
1030 1022
1031 // input events 1023 // input events
1032 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1024 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index c820e032..b4352c6a 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -293,12 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
293 struct sway_seat *seat; 293 struct sway_seat *seat;
294 wl_list_for_each(seat, &input_manager->seats, link) { 294 wl_list_for_each(seat, &input_manager->seats, link) {
295 seat_set_exclusive_client(seat, NULL); 295 seat_set_exclusive_client(seat, NULL);
296 struct sway_container *previous = seat_get_focus(seat); 296 struct sway_node *previous = seat_get_focus(seat);
297 if (previous) { 297 if (previous) {
298 wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,
299 container_type_to_str(previous->type), previous->name);
300 // Hack to get seat to re-focus the return value of get_focus 298 // Hack to get seat to re-focus the return value of get_focus
301 seat_set_focus(seat, previous->parent); 299 seat_set_focus(seat, NULL);
302 seat_set_focus(seat, previous); 300 seat_set_focus(seat, previous);
303 } 301 }
304 } 302 }
@@ -369,10 +367,10 @@ struct sway_input_manager *input_manager_create(
369} 367}
370 368
371bool input_manager_has_focus(struct sway_input_manager *input, 369bool input_manager_has_focus(struct sway_input_manager *input,
372 struct sway_container *container) { 370 struct sway_node *node) {
373 struct sway_seat *seat = NULL; 371 struct sway_seat *seat = NULL;
374 wl_list_for_each(seat, &input->seats, link) { 372 wl_list_for_each(seat, &input->seats, link) {
375 if (seat_get_focus(seat) == container) { 373 if (seat_get_focus(seat) == node) {
376 return true; 374 return true;
377 } 375 }
378 } 376 }
@@ -381,10 +379,10 @@ bool input_manager_has_focus(struct sway_input_manager *input,
381} 379}
382 380
383void input_manager_set_focus(struct sway_input_manager *input, 381void input_manager_set_focus(struct sway_input_manager *input,
384 struct sway_container *container) { 382 struct sway_node *node) {
385 struct sway_seat *seat; 383 struct sway_seat *seat;
386 wl_list_for_each(seat, &input->seats, link) { 384 wl_list_for_each(seat, &input->seats, link) {
387 seat_set_focus(seat, container); 385 seat_set_focus(seat, node);
388 } 386 }
389} 387}
390 388
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 4b7c7893..2f7a3318 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -47,48 +47,36 @@ void seat_destroy(struct sway_seat *seat) {
47 seat_device_destroy(seat_device); 47 seat_device_destroy(seat_device);
48 } 48 }
49 sway_cursor_destroy(seat->cursor); 49 sway_cursor_destroy(seat->cursor);
50 wl_list_remove(&seat->new_container.link); 50 wl_list_remove(&seat->new_node.link);
51 wl_list_remove(&seat->new_drag_icon.link); 51 wl_list_remove(&seat->new_drag_icon.link);
52 wl_list_remove(&seat->link); 52 wl_list_remove(&seat->link);
53 wlr_seat_destroy(seat->wlr_seat); 53 wlr_seat_destroy(seat->wlr_seat);
54} 54}
55 55
56static struct sway_seat_container *seat_container_from_container( 56static struct sway_seat_node *seat_node_from_node(
57 struct sway_seat *seat, struct sway_container *con); 57 struct sway_seat *seat, struct sway_node *node);
58 58
59static void seat_container_destroy(struct sway_seat_container *seat_con) { 59static void seat_node_destroy(struct sway_seat_node *seat_node) {
60 struct sway_container *con = seat_con->container; 60 wl_list_remove(&seat_node->destroy.link);
61 struct sway_container *child = NULL; 61 wl_list_remove(&seat_node->link);
62 62 free(seat_node);
63 if (con->children != NULL) {
64 for (int i = 0; i < con->children->length; ++i) {
65 child = con->children->items[i];
66 struct sway_seat_container *seat_child =
67 seat_container_from_container(seat_con->seat, child);
68 seat_container_destroy(seat_child);
69 }
70 }
71
72 wl_list_remove(&seat_con->destroy.link);
73 wl_list_remove(&seat_con->link);
74 free(seat_con);
75} 63}
76 64
77/** 65/**
78 * Activate all views within this container recursively. 66 * Activate all views within this container recursively.
79 */ 67 */
80static void seat_send_activate(struct sway_container *con, 68static void seat_send_activate(struct sway_node *node, struct sway_seat *seat) {
81 struct sway_seat *seat) { 69 if (node_is_view(node)) {
82 if (con->type == C_VIEW) { 70 if (!seat_is_input_allowed(seat, node->sway_container->view->surface)) {
83 if (!seat_is_input_allowed(seat, con->sway_view->surface)) {
84 wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited"); 71 wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited");
85 return; 72 return;
86 } 73 }
87 view_set_activated(con->sway_view, true); 74 view_set_activated(node->sway_container->view, true);
88 } else { 75 } else {
89 for (int i = 0; i < con->children->length; ++i) { 76 list_t *children = node_get_children(node);
90 struct sway_container *child = con->children->items[i]; 77 for (int i = 0; i < children->length; ++i) {
91 seat_send_activate(child, seat); 78 struct sway_container *child = children->items[i];
79 seat_send_activate(&child->node, seat);
92 } 80 }
93 } 81 }
94} 82}
@@ -98,14 +86,15 @@ static void seat_send_activate(struct sway_container *con,
98 * If con is a container, set all child views as active and don't enable 86 * If con is a container, set all child views as active and don't enable
99 * keyboard input on any. 87 * keyboard input on any.
100 */ 88 */
101static void seat_send_focus(struct sway_container *con, 89static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
102 struct sway_seat *seat) { 90 seat_send_activate(node, seat);
103 seat_send_activate(con, seat);
104 91
105 if (con->type == C_VIEW 92 struct sway_view *view = node->type == N_CONTAINER ?
106 && seat_is_input_allowed(seat, con->sway_view->surface)) { 93 node->sway_container->view : NULL;
94
95 if (view && seat_is_input_allowed(seat, view->surface)) {
107#ifdef HAVE_XWAYLAND 96#ifdef HAVE_XWAYLAND
108 if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { 97 if (view->type == SWAY_VIEW_XWAYLAND) {
109 struct wlr_xwayland *xwayland = 98 struct wlr_xwayland *xwayland =
110 seat->input->server->xwayland.wlr_xwayland; 99 seat->input->server->xwayland.wlr_xwayland;
111 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 100 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@@ -114,71 +103,67 @@ static void seat_send_focus(struct sway_container *con,
114 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); 103 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
115 if (keyboard) { 104 if (keyboard) {
116 wlr_seat_keyboard_notify_enter(seat->wlr_seat, 105 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
117 con->sway_view->surface, keyboard->keycodes, 106 view->surface, keyboard->keycodes,
118 keyboard->num_keycodes, &keyboard->modifiers); 107 keyboard->num_keycodes, &keyboard->modifiers);
119 } else { 108 } else {
120 wlr_seat_keyboard_notify_enter( 109 wlr_seat_keyboard_notify_enter(
121 seat->wlr_seat, con->sway_view->surface, NULL, 0, NULL); 110 seat->wlr_seat, view->surface, NULL, 0, NULL);
122 } 111 }
123 } 112 }
124} 113}
125 114
126void seat_focus_inactive_children_for_each(struct sway_seat *seat, 115void seat_for_each_node(struct sway_seat *seat,
127 struct sway_container *container, 116 void (*f)(struct sway_node *node, void *data), void *data) {
128 void (*f)(struct sway_container *container, void *data), void *data) { 117 struct sway_seat_node *current = NULL;
129 struct sway_seat_container *current = NULL;
130 wl_list_for_each(current, &seat->focus_stack, link) { 118 wl_list_for_each(current, &seat->focus_stack, link) {
131 if (current->container->parent == NULL) { 119 f(current->node, data);
132 continue;
133 }
134 if (current->container->parent == container) {
135 f(current->container, data);
136 }
137 } 120 }
138} 121}
139 122
140struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 123struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
141 struct sway_container *ancestor) { 124 struct sway_node *ancestor) {
142 if (ancestor->type == C_VIEW) { 125 if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) {
143 return ancestor; 126 return ancestor->sway_container;
144 } 127 }
145 struct sway_seat_container *current; 128 struct sway_seat_node *current;
146 wl_list_for_each(current, &seat->focus_stack, link) { 129 wl_list_for_each(current, &seat->focus_stack, link) {
147 struct sway_container *con = current->container; 130 struct sway_node *node = current->node;
148 if (con->type == C_VIEW && container_has_ancestor(con, ancestor)) { 131 if (node->type == N_CONTAINER && node->sway_container->view &&
149 return con; 132 node_has_ancestor(node, ancestor)) {
133 return node->sway_container;
150 } 134 }
151 } 135 }
152 return NULL; 136 return NULL;
153} 137}
154 138
155static void handle_seat_container_destroy(struct wl_listener *listener, 139static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
156 void *data) { 140 struct sway_seat_node *seat_node =
157 struct sway_seat_container *seat_con = 141 wl_container_of(listener, seat_node, destroy);
158 wl_container_of(listener, seat_con, destroy); 142 struct sway_seat *seat = seat_node->seat;
159 struct sway_seat *seat = seat_con->seat; 143 struct sway_node *node = seat_node->node;
160 struct sway_container *con = seat_con->container; 144 struct sway_node *parent = node_get_parent(node);
161 struct sway_container *parent = con->parent; 145 struct sway_node *focus = seat_get_focus(seat);
162 struct sway_container *focus = seat_get_focus(seat);
163 146
164 bool set_focus = 147 bool set_focus =
165 focus != NULL && 148 focus != NULL &&
166 (focus == con || container_has_ancestor(focus, con)) && 149 (focus == node || node_has_ancestor(focus, node)) &&
167 con->type != C_WORKSPACE; 150 node->type == N_CONTAINER;
168 151
169 seat_container_destroy(seat_con); 152 seat_node_destroy(seat_node);
170 153
171 if (set_focus) { 154 if (set_focus) {
172 struct sway_container *next_focus = NULL; 155 struct sway_node *next_focus = NULL;
173 while (next_focus == NULL) { 156 while (next_focus == NULL) {
174 next_focus = seat_get_focus_inactive_view(seat, parent); 157 struct sway_container *con =
158 seat_get_focus_inactive_view(seat, parent);
159 next_focus = con ? &con->node : NULL;
175 160
176 if (next_focus == NULL && parent->type == C_WORKSPACE) { 161 if (next_focus == NULL && parent->type == N_WORKSPACE) {
177 next_focus = parent; 162 next_focus = parent;
178 break; 163 break;
179 } 164 }
180 165
181 parent = parent->parent; 166 parent = node_get_parent(parent);
182 } 167 }
183 168
184 // the structure change might have caused it to move up to the top of 169 // the structure change might have caused it to move up to the top of
@@ -191,39 +176,39 @@ static void handle_seat_container_destroy(struct wl_listener *listener,
191 } 176 }
192} 177}
193 178
194static struct sway_seat_container *seat_container_from_container( 179static struct sway_seat_node *seat_node_from_node(
195 struct sway_seat *seat, struct sway_container *con) { 180 struct sway_seat *seat, struct sway_node *node) {
196 if (con->type == C_ROOT || con->type == C_OUTPUT) { 181 if (node->type == N_ROOT || node->type == N_OUTPUT) {
197 // these don't get seat containers ever 182 // these don't get seat nodes ever
198 return NULL; 183 return NULL;
199 } 184 }
200 185
201 struct sway_seat_container *seat_con = NULL; 186 struct sway_seat_node *seat_node = NULL;
202 wl_list_for_each(seat_con, &seat->focus_stack, link) { 187 wl_list_for_each(seat_node, &seat->focus_stack, link) {
203 if (seat_con->container == con) { 188 if (seat_node->node == node) {
204 return seat_con; 189 return seat_node;
205 } 190 }
206 } 191 }
207 192
208 seat_con = calloc(1, sizeof(struct sway_seat_container)); 193 seat_node = calloc(1, sizeof(struct sway_seat_node));
209 if (seat_con == NULL) { 194 if (seat_node == NULL) {
210 wlr_log(WLR_ERROR, "could not allocate seat container"); 195 wlr_log(WLR_ERROR, "could not allocate seat node");
211 return NULL; 196 return NULL;
212 } 197 }
213 198
214 seat_con->container = con; 199 seat_node->node = node;
215 seat_con->seat = seat; 200 seat_node->seat = seat;
216 wl_list_insert(seat->focus_stack.prev, &seat_con->link); 201 wl_list_insert(seat->focus_stack.prev, &seat_node->link);
217 wl_signal_add(&con->events.destroy, &seat_con->destroy); 202 wl_signal_add(&node->events.destroy, &seat_node->destroy);
218 seat_con->destroy.notify = handle_seat_container_destroy; 203 seat_node->destroy.notify = handle_seat_node_destroy;
219 204
220 return seat_con; 205 return seat_node;
221} 206}
222 207
223static void handle_new_container(struct wl_listener *listener, void *data) { 208static void handle_new_node(struct wl_listener *listener, void *data) {
224 struct sway_seat *seat = wl_container_of(listener, seat, new_container); 209 struct sway_seat *seat = wl_container_of(listener, seat, new_node);
225 struct sway_container *con = data; 210 struct sway_node *node = data;
226 seat_container_from_container(seat, con); 211 seat_node_from_node(seat, node);
227} 212}
228 213
229static void drag_icon_damage_whole(struct sway_drag_icon *icon) { 214static void drag_icon_damage_whole(struct sway_drag_icon *icon) {
@@ -272,8 +257,7 @@ static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) {
272 drag_icon_damage_whole(icon); 257 drag_icon_damage_whole(icon);
273} 258}
274 259
275static void drag_icon_handle_destroy(struct wl_listener *listener, 260static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) {
276 void *data) {
277 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); 261 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy);
278 icon->wlr_drag_icon->data = NULL; 262 icon->wlr_drag_icon->data = NULL;
279 wl_list_remove(&icon->link); 263 wl_list_remove(&icon->link);
@@ -305,20 +289,29 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) {
305 icon->destroy.notify = drag_icon_handle_destroy; 289 icon->destroy.notify = drag_icon_handle_destroy;
306 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); 290 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy);
307 291
308 wl_list_insert(&root_container.sway_root->drag_icons, &icon->link); 292 wl_list_insert(&root->drag_icons, &icon->link);
309 293
310 drag_icon_update_position(icon); 294 drag_icon_update_position(icon);
311} 295}
312 296
313static void collect_focus_iter(struct sway_container *con, void *data) { 297static void collect_focus_iter(struct sway_node *node, void *data) {
314 struct sway_seat *seat = data; 298 struct sway_seat *seat = data;
315 struct sway_seat_container *seat_con = 299 struct sway_seat_node *seat_node = seat_node_from_node(seat, node);
316 seat_container_from_container(seat, con); 300 if (!seat_node) {
317 if (!seat_con) {
318 return; 301 return;
319 } 302 }
320 wl_list_remove(&seat_con->link); 303 wl_list_remove(&seat_node->link);
321 wl_list_insert(&seat->focus_stack, &seat_con->link); 304 wl_list_insert(&seat->focus_stack, &seat_node->link);
305}
306
307static void collect_focus_workspace_iter(struct sway_workspace *workspace,
308 void *data) {
309 collect_focus_iter(&workspace->node, data);
310}
311
312static void collect_focus_container_iter(struct sway_container *container,
313 void *data) {
314 collect_focus_iter(&container->node, data);
322} 315}
323 316
324struct sway_seat *seat_create(struct sway_input_manager *input, 317struct sway_seat *seat_create(struct sway_input_manager *input,
@@ -345,12 +338,11 @@ struct sway_seat *seat_create(struct sway_input_manager *input,
345 // init the focus stack 338 // init the focus stack
346 wl_list_init(&seat->focus_stack); 339 wl_list_init(&seat->focus_stack);
347 340
348 root_for_each_workspace(collect_focus_iter, seat); 341 root_for_each_workspace(collect_focus_workspace_iter, seat);
349 root_for_each_container(collect_focus_iter, seat); 342 root_for_each_container(collect_focus_container_iter, seat);
350 343
351 wl_signal_add(&root_container.sway_root->events.new_container, 344 wl_signal_add(&root->events.new_node, &seat->new_node);
352 &seat->new_container); 345 seat->new_node.notify = handle_new_node;
353 seat->new_container.notify = handle_new_container;
354 346
355 wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon); 347 wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon);
356 seat->new_drag_icon.notify = handle_new_drag_icon; 348 seat->new_drag_icon.notify = handle_new_drag_icon;
@@ -388,19 +380,11 @@ static void seat_apply_input_config(struct sway_seat *seat,
388 if (mapped_to_output != NULL) { 380 if (mapped_to_output != NULL) {
389 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", 381 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s",
390 sway_device->input_device->identifier, mapped_to_output); 382 sway_device->input_device->identifier, mapped_to_output);
391 struct sway_container *output = NULL; 383 struct sway_output *output = output_by_name(mapped_to_output);
392 for (int i = 0; i < root_container.children->length; ++i) {
393 struct sway_container *_output = root_container.children->items[i];
394 if (strcasecmp(_output->name, mapped_to_output) == 0) {
395 output = _output;
396 break;
397 }
398 }
399 if (output) { 384 if (output) {
400 wlr_cursor_map_input_to_output(seat->cursor->cursor, 385 wlr_cursor_map_input_to_output(seat->cursor->cursor,
401 sway_device->input_device->wlr_device, 386 sway_device->input_device->wlr_device, output->wlr_output);
402 output->sway_output->wlr_output); 387 wlr_log(WLR_DEBUG, "Mapped to output %s", output->wlr_output->name);
403 wlr_log(WLR_DEBUG, "Mapped to output %s", output->name);
404 } 388 }
405 } 389 }
406} 390}
@@ -423,12 +407,12 @@ static void seat_configure_keyboard(struct sway_seat *seat,
423 sway_keyboard_configure(seat_device->keyboard); 407 sway_keyboard_configure(seat_device->keyboard);
424 wlr_seat_set_keyboard(seat->wlr_seat, 408 wlr_seat_set_keyboard(seat->wlr_seat,
425 seat_device->input_device->wlr_device); 409 seat_device->input_device->wlr_device);
426 struct sway_container *focus = seat_get_focus(seat); 410 struct sway_node *focus = seat_get_focus(seat);
427 if (focus && focus->type == C_VIEW) { 411 if (focus && node_is_view(focus)) {
428 // force notify reenter to pick up the new configuration 412 // force notify reenter to pick up the new configuration
429 wlr_seat_keyboard_clear_focus(seat->wlr_seat); 413 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
430 wlr_seat_keyboard_notify_enter(seat->wlr_seat, 414 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
431 focus->sway_view->surface, wlr_keyboard->keycodes, 415 focus->sway_container->view->surface, wlr_keyboard->keycodes,
432 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); 416 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
433 } 417 }
434} 418}
@@ -461,8 +445,7 @@ static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
461 445
462void seat_configure_device(struct sway_seat *seat, 446void seat_configure_device(struct sway_seat *seat,
463 struct sway_input_device *input_device) { 447 struct sway_input_device *input_device) {
464 struct sway_seat_device *seat_device = 448 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
465 seat_get_device(seat, input_device);
466 if (!seat_device) { 449 if (!seat_device) {
467 return; 450 return;
468 } 451 }
@@ -512,8 +495,7 @@ void seat_add_device(struct sway_seat *seat,
512 495
513void seat_remove_device(struct sway_seat *seat, 496void seat_remove_device(struct sway_seat *seat,
514 struct sway_input_device *input_device) { 497 struct sway_input_device *input_device) {
515 struct sway_seat_device *seat_device = 498 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
516 seat_get_device(seat, input_device);
517 499
518 if (!seat_device) { 500 if (!seat_device) {
519 return; 501 return;
@@ -539,11 +521,9 @@ void seat_configure_xcursor(struct sway_seat *seat) {
539 } 521 }
540 } 522 }
541 523
542 for (int i = 0; i < root_container.children->length; ++i) { 524 for (int i = 0; i < root->outputs->length; ++i) {
543 struct sway_container *output_container = 525 struct sway_output *sway_output = root->outputs->items[i];
544 root_container.children->items[i]; 526 struct wlr_output *output = sway_output->wlr_output;
545 struct wlr_output *output =
546 output_container->sway_output->wlr_output;
547 bool result = 527 bool result =
548 wlr_xcursor_manager_load(seat->cursor->xcursor_manager, 528 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
549 output->scale); 529 output->scale);
@@ -566,17 +546,20 @@ bool seat_is_input_allowed(struct sway_seat *seat,
566 return !seat->exclusive_client || seat->exclusive_client == client; 546 return !seat->exclusive_client || seat->exclusive_client == client;
567} 547}
568 548
549static void send_unfocus(struct sway_container *con, void *data) {
550 if (con->view) {
551 view_set_activated(con->view, false);
552 }
553}
554
569// Unfocus the container and any children (eg. when leaving `focus parent`) 555// Unfocus the container and any children (eg. when leaving `focus parent`)
570static void seat_send_unfocus(struct sway_container *container, 556static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) {
571 struct sway_seat *seat) { 557 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
572 if (container->type == C_VIEW) { 558 if (node->type == N_WORKSPACE) {
573 wlr_seat_keyboard_clear_focus(seat->wlr_seat); 559 workspace_for_each_container(node->sway_workspace, send_unfocus, seat);
574 view_set_activated(container->sway_view, false);
575 } else { 560 } else {
576 for (int i = 0; i < container->children->length; ++i) { 561 send_unfocus(node->sway_container, seat);
577 struct sway_container *child = container->children->items[i]; 562 container_for_each_child(node->sway_container, send_unfocus, seat);
578 seat_send_unfocus(child, seat);
579 }
580 } 563 }
581} 564}
582 565
@@ -586,26 +569,23 @@ static int handle_urgent_timeout(void *data) {
586 return 0; 569 return 0;
587} 570}
588 571
589void seat_set_focus_warp(struct sway_seat *seat, 572void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
590 struct sway_container *container, bool warp, bool notify) { 573 bool warp, bool notify) {
591 if (seat->focused_layer) { 574 if (seat->focused_layer) {
592 return; 575 return;
593 } 576 }
594 577
595 struct sway_container *last_focus = seat_get_focus(seat); 578 struct sway_node *last_focus = seat_get_focus(seat);
596 if (last_focus == container) { 579 if (last_focus == node) {
597 return; 580 return;
598 } 581 }
599 582
600 struct sway_container *last_workspace = last_focus; 583 struct sway_workspace *last_workspace = seat_get_focused_workspace(seat);
601 if (last_workspace && last_workspace->type != C_WORKSPACE) {
602 last_workspace = container_parent(last_workspace, C_WORKSPACE);
603 }
604 584
605 if (container == NULL) { 585 if (node == NULL) {
606 // Close any popups on the old focus 586 // Close any popups on the old focus
607 if (last_focus->type == C_VIEW) { 587 if (node_is_view(last_focus)) {
608 view_close_popups(last_focus->sway_view); 588 view_close_popups(last_focus->sway_container->view);
609 } 589 }
610 seat_send_unfocus(last_focus, seat); 590 seat_send_unfocus(last_focus, seat);
611 seat->has_focus = false; 591 seat->has_focus = false;
@@ -613,69 +593,70 @@ void seat_set_focus_warp(struct sway_seat *seat,
613 return; 593 return;
614 } 594 }
615 595
616 struct sway_container *new_workspace = container; 596 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ?
617 if (new_workspace->type != C_WORKSPACE) { 597 node->sway_workspace : node->sway_container->workspace;
618 new_workspace = container_parent(new_workspace, C_WORKSPACE); 598 struct sway_container *container = node->type == N_CONTAINER ?
619 } 599 node->sway_container : NULL;
620 600
621 if (last_workspace == new_workspace 601 // Deny setting focus to a view which is hidden by a fullscreen container
622 && last_workspace->sway_workspace->fullscreen 602 if (new_workspace && new_workspace->fullscreen && container &&
623 && !container_is_fullscreen_or_child(container)) { 603 !container_is_fullscreen_or_child(container)) {
624 return; 604 return;
625 } 605 }
626 606
627 struct sway_container *last_output = last_focus; 607 struct sway_output *last_output = last_workspace ?
628 if (last_output && last_output->type != C_OUTPUT) { 608 last_workspace->output : NULL;
629 last_output = container_parent(last_output, C_OUTPUT); 609 struct sway_output *new_output = new_workspace->output;
630 }
631 struct sway_container *new_output = container;
632 if (new_output->type != C_OUTPUT) {
633 new_output = container_parent(new_output, C_OUTPUT);
634 }
635 610
636 // find new output's old workspace, which might have to be removed if empty 611 // find new output's old workspace, which might have to be removed if empty
637 struct sway_container *new_output_last_ws = NULL; 612 struct sway_workspace *new_output_last_ws = NULL;
638 if (new_output && last_output != new_output) { 613 if (new_output && last_output != new_output) {
639 new_output_last_ws = seat_get_active_child(seat, new_output); 614 new_output_last_ws = output_get_active_workspace(new_output);
640 } 615 }
641 616
642 if (container->parent) { 617 // Put the container parents on the focus stack, then the workspace, then
643 struct sway_seat_container *seat_con = 618 // the focused container.
644 seat_container_from_container(seat, container); 619 if (container) {
645 if (seat_con == NULL) { 620 struct sway_container *parent = container->parent;
646 return;
647 }
648
649 // put all the ancestors of this container on top of the focus stack
650 struct sway_seat_container *parent =
651 seat_container_from_container(seat, container->parent);
652 while (parent) { 621 while (parent) {
653 wl_list_remove(&parent->link); 622 struct sway_seat_node *seat_node =
654 wl_list_insert(&seat->focus_stack, &parent->link); 623 seat_node_from_node(seat, &parent->node);
655 container_set_dirty(parent->container); 624 wl_list_remove(&seat_node->link);
656 625 wl_list_insert(&seat->focus_stack, &seat_node->link);
657 parent = seat_container_from_container(seat, 626 node_set_dirty(&parent->node);
658 parent->container->parent); 627 parent = parent->parent;
659 } 628 }
660 629 }
661 wl_list_remove(&seat_con->link); 630 if (new_workspace) {
662 wl_list_insert(&seat->focus_stack, &seat_con->link); 631 struct sway_seat_node *seat_node =
632 seat_node_from_node(seat, &new_workspace->node);
633 wl_list_remove(&seat_node->link);
634 wl_list_insert(&seat->focus_stack, &seat_node->link);
635 node_set_dirty(&new_workspace->node);
636 }
637 if (container) {
638 struct sway_seat_node *seat_node =
639 seat_node_from_node(seat, &container->node);
640 wl_list_remove(&seat_node->link);
641 wl_list_insert(&seat->focus_stack, &seat_node->link);
642 node_set_dirty(&container->node);
663 643
664 if (last_focus) { 644 if (last_focus) {
665 seat_send_unfocus(last_focus, seat); 645 seat_send_unfocus(last_focus, seat);
666 container_set_dirty(last_focus); 646 node_set_dirty(last_focus);
647 struct sway_node *last_parent = node_get_parent(last_focus);
648 if (last_parent) {
649 node_set_dirty(last_parent);
650 }
667 } 651 }
668 seat_send_focus(container, seat); 652 seat_send_focus(&container->node, seat);
669
670 container_set_dirty(container);
671 container_set_dirty(container->parent); // for focused_inactive_child
672 } 653 }
673 654
674 // emit ipc events 655 // emit ipc events
675 if (notify && new_workspace && last_workspace != new_workspace) { 656 if (notify && new_workspace && last_workspace != new_workspace) {
676 ipc_event_workspace(last_workspace, new_workspace, "focus"); 657 ipc_event_workspace(last_workspace, new_workspace, "focus");
677 } 658 }
678 if (container->type == C_VIEW) { 659 if (container && container->view) {
679 ipc_event_window(container, "focus"); 660 ipc_event_window(container, "focus");
680 } 661 }
681 662
@@ -684,14 +665,14 @@ void seat_set_focus_warp(struct sway_seat *seat,
684 } 665 }
685 666
686 // Close any popups on the old focus 667 // Close any popups on the old focus
687 if (last_focus && last_focus->type == C_VIEW) { 668 if (last_focus && node_is_view(last_focus)) {
688 view_close_popups(last_focus->sway_view); 669 view_close_popups(last_focus->sway_container->view);
689 } 670 }
690 671
691 // If urgent, either unset the urgency or start a timer to unset it 672 // If urgent, either unset the urgency or start a timer to unset it
692 if (container->type == C_VIEW && view_is_urgent(container->sway_view) && 673 if (container && container->view && view_is_urgent(container->view) &&
693 !container->sway_view->urgent_timer) { 674 !container->view->urgent_timer) {
694 struct sway_view *view = container->sway_view; 675 struct sway_view *view = container->view;
695 if (last_workspace && last_workspace != new_workspace && 676 if (last_workspace && last_workspace != new_workspace &&
696 config->urgent_timeout > 0) { 677 config->urgent_timeout > 0) {
697 view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, 678 view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop,
@@ -711,12 +692,15 @@ void seat_set_focus_warp(struct sway_seat *seat,
711 692
712 // If we've focused a floating container, bring it to the front. 693 // If we've focused a floating container, bring it to the front.
713 // We do this by putting it at the end of the floating list. 694 // We do this by putting it at the end of the floating list.
714 struct sway_container *floater = container; 695 if (container) {
715 while (floater->parent && floater->parent->type != C_WORKSPACE) { 696 struct sway_container *floater = container;
716 floater = floater->parent; 697 while (floater->parent) {
717 } 698 floater = floater->parent;
718 if (container_is_floating(floater)) { 699 }
719 list_move_to_end(floater->parent->sway_workspace->floating, floater); 700 if (container_is_floating(floater)) {
701 list_move_to_end(floater->workspace->floating, floater);
702 node_set_dirty(&floater->workspace->node);
703 }
720 } 704 }
721 705
722 if (last_focus) { 706 if (last_focus) {
@@ -727,11 +711,8 @@ void seat_set_focus_warp(struct sway_seat *seat,
727 if (config->mouse_warping && warp && new_output != last_output) { 711 if (config->mouse_warping && warp && new_output != last_output) {
728 double x = container->x + container->width / 2.0; 712 double x = container->x + container->width / 2.0;
729 double y = container->y + container->height / 2.0; 713 double y = container->y + container->height / 2.0;
730 struct wlr_output *wlr_output = 714 if (!wlr_output_layout_contains_point(root->output_layout,
731 new_output->sway_output->wlr_output; 715 new_output->wlr_output, seat->cursor->cursor->x,
732 if (!wlr_output_layout_contains_point(
733 root_container.sway_root->output_layout,
734 wlr_output, seat->cursor->cursor->x,
735 seat->cursor->cursor->y)) { 716 seat->cursor->cursor->y)) {
736 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); 717 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
737 cursor_send_pointer_motion(seat->cursor, 0, true); 718 cursor_send_pointer_motion(seat->cursor, 0, true);
@@ -744,9 +725,8 @@ void seat_set_focus_warp(struct sway_seat *seat,
744 update_debug_tree(); 725 update_debug_tree();
745} 726}
746 727
747void seat_set_focus(struct sway_seat *seat, 728void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
748 struct sway_container *container) { 729 seat_set_focus_warp(seat, node, true, true);
749 seat_set_focus_warp(seat, container, true, true);
750} 730}
751 731
752void seat_set_focus_surface(struct sway_seat *seat, 732void seat_set_focus_surface(struct sway_seat *seat,
@@ -755,12 +735,11 @@ void seat_set_focus_surface(struct sway_seat *seat,
755 return; 735 return;
756 } 736 }
757 if (seat->has_focus && unfocus) { 737 if (seat->has_focus && unfocus) {
758 struct sway_container *focus = seat_get_focus(seat); 738 struct sway_node *focus = seat_get_focus(seat);
759 seat_send_unfocus(focus, seat); 739 seat_send_unfocus(focus, seat);
760 seat->has_focus = false; 740 seat->has_focus = false;
761 } 741 }
762 struct wlr_keyboard *keyboard = 742 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
763 wlr_seat_get_keyboard(seat->wlr_seat);
764 if (keyboard) { 743 if (keyboard) {
765 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, 744 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,
766 keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); 745 keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
@@ -773,11 +752,8 @@ void seat_set_focus_layer(struct sway_seat *seat,
773 struct wlr_layer_surface *layer) { 752 struct wlr_layer_surface *layer) {
774 if (!layer && seat->focused_layer) { 753 if (!layer && seat->focused_layer) {
775 seat->focused_layer = NULL; 754 seat->focused_layer = NULL;
776 struct sway_container *previous = 755 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
777 seat_get_focus_inactive(seat, &root_container);
778 if (previous) { 756 if (previous) {
779 wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,
780 container_type_to_str(previous->type), previous->name);
781 // Hack to get seat to re-focus the return value of get_focus 757 // Hack to get seat to re-focus the return value of get_focus
782 seat_set_focus(seat, NULL); 758 seat_set_focus(seat, NULL);
783 seat_set_focus(seat, previous); 759 seat_set_focus(seat, previous);
@@ -798,13 +774,9 @@ void seat_set_exclusive_client(struct sway_seat *seat,
798 seat->exclusive_client = client; 774 seat->exclusive_client = client;
799 // Triggers a refocus of the topmost surface layer if necessary 775 // Triggers a refocus of the topmost surface layer if necessary
800 // TODO: Make layer surface focus per-output based on cursor position 776 // TODO: Make layer surface focus per-output based on cursor position
801 for (int i = 0; i < root_container.children->length; ++i) { 777 for (int i = 0; i < root->outputs->length; ++i) {
802 struct sway_container *output = root_container.children->items[i]; 778 struct sway_output *output = root->outputs->items[i];
803 if (!sway_assert(output->type == C_OUTPUT, 779 arrange_layers(output);
804 "root container has non-output child")) {
805 continue;
806 }
807 arrange_layers(output->sway_output);
808 } 780 }
809 return; 781 return;
810 } 782 }
@@ -814,9 +786,9 @@ void seat_set_exclusive_client(struct sway_seat *seat,
814 } 786 }
815 } 787 }
816 if (seat->has_focus) { 788 if (seat->has_focus) {
817 struct sway_container *focus = seat_get_focus(seat); 789 struct sway_node *focus = seat_get_focus(seat);
818 if (focus->type == C_VIEW && wl_resource_get_client( 790 if (node_is_view(focus) && wl_resource_get_client(
819 focus->sway_view->surface->resource) != client) { 791 focus->sway_container->view->surface->resource) != client) {
820 seat_set_focus(seat, NULL); 792 seat_set_focus(seat, NULL);
821 } 793 }
822 } 794 }
@@ -837,79 +809,101 @@ void seat_set_exclusive_client(struct sway_seat *seat,
837 seat->exclusive_client = client; 809 seat->exclusive_client = client;
838} 810}
839 811
840struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, 812struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
841 struct sway_container *con) { 813 struct sway_node *node) {
842 if (con->type == C_WORKSPACE && !con->children->length && 814 if (node_is_view(node)) {
843 !con->sway_workspace->floating->length) { 815 return node;
844 return con;
845 }
846 if (con->type == C_VIEW) {
847 return con;
848 } 816 }
849 struct sway_seat_container *current; 817 struct sway_seat_node *current;
850 wl_list_for_each(current, &seat->focus_stack, link) { 818 wl_list_for_each(current, &seat->focus_stack, link) {
851 if (container_has_ancestor(current->container, con)) { 819 if (node_has_ancestor(current->node, node)) {
852 return current->container; 820 return current->node;
853 } 821 }
854 } 822 }
823 if (node->type == N_WORKSPACE) {
824 return node;
825 }
855 return NULL; 826 return NULL;
856} 827}
857 828
858struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, 829struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
859 struct sway_container *ancestor) { 830 struct sway_workspace *workspace) {
860 if (ancestor->type == C_WORKSPACE && !ancestor->children->length) { 831 if (!workspace->tiling->length) {
861 return ancestor; 832 return NULL;
862 } 833 }
863 struct sway_seat_container *current; 834 struct sway_seat_node *current;
864 wl_list_for_each(current, &seat->focus_stack, link) { 835 wl_list_for_each(current, &seat->focus_stack, link) {
865 struct sway_container *con = current->container; 836 struct sway_node *node = current->node;
866 if (!container_is_floating_or_child(con) && 837 if (node->type == N_CONTAINER &&
867 container_has_ancestor(current->container, ancestor)) { 838 !container_is_floating_or_child(node->sway_container) &&
868 return con; 839 node->sway_container->workspace == workspace) {
840 return node->sway_container;
869 } 841 }
870 } 842 }
871 return NULL; 843 return NULL;
872} 844}
873 845
874struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, 846struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
875 struct sway_container *ancestor) { 847 struct sway_workspace *workspace) {
876 if (ancestor->type == C_WORKSPACE && 848 if (!workspace->floating->length) {
877 !ancestor->sway_workspace->floating->length) {
878 return NULL; 849 return NULL;
879 } 850 }
880 struct sway_seat_container *current; 851 struct sway_seat_node *current;
881 wl_list_for_each(current, &seat->focus_stack, link) { 852 wl_list_for_each(current, &seat->focus_stack, link) {
882 struct sway_container *con = current->container; 853 struct sway_node *node = current->node;
883 if (container_is_floating_or_child(con) && 854 if (node->type == N_CONTAINER &&
884 container_has_ancestor(current->container, ancestor)) { 855 container_is_floating_or_child(node->sway_container) &&
885 return con; 856 node->sway_container->workspace == workspace) {
857 return node->sway_container;
886 } 858 }
887 } 859 }
888 return NULL; 860 return NULL;
889} 861}
890 862
891struct sway_container *seat_get_active_child(struct sway_seat *seat, 863struct sway_node *seat_get_active_child(struct sway_seat *seat,
892 struct sway_container *parent) { 864 struct sway_node *parent) {
893 if (parent->type == C_VIEW) { 865 if (node_is_view(parent)) {
894 return parent; 866 return parent;
895 } 867 }
896 struct sway_seat_container *current; 868 struct sway_seat_node *current;
897 wl_list_for_each(current, &seat->focus_stack, link) { 869 wl_list_for_each(current, &seat->focus_stack, link) {
898 struct sway_container *con = current->container; 870 struct sway_node *node = current->node;
899 if (con->parent == parent) { 871 if (node_get_parent(node) == parent) {
900 return con; 872 return node;
901 } 873 }
902 } 874 }
903 return NULL; 875 return NULL;
904} 876}
905 877
906struct sway_container *seat_get_focus(struct sway_seat *seat) { 878struct sway_node *seat_get_focus(struct sway_seat *seat) {
907 if (!seat->has_focus) { 879 if (!seat->has_focus) {
908 return NULL; 880 return NULL;
909 } 881 }
910 struct sway_seat_container *current = 882 struct sway_seat_node *current =
911 wl_container_of(seat->focus_stack.next, current, link); 883 wl_container_of(seat->focus_stack.next, current, link);
912 return current->container; 884 return current->node;
885}
886
887struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) {
888 struct sway_node *focus = seat_get_focus(seat);
889 if (!focus) {
890 return NULL;
891 }
892 if (focus->type == N_CONTAINER) {
893 return focus->sway_container->workspace;
894 }
895 if (focus->type == N_WORKSPACE) {
896 return focus->sway_workspace;
897 }
898 return NULL; // unreachable
899}
900
901struct sway_container *seat_get_focused_container(struct sway_seat *seat) {
902 struct sway_node *focus = seat_get_focus(seat);
903 if (focus && focus->type == N_CONTAINER) {
904 return focus->sway_container;
905 }
906 return NULL;
913} 907}
914 908
915void seat_apply_config(struct sway_seat *seat, 909void seat_apply_config(struct sway_seat *seat,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 06cb7e11..9a955991 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -46,18 +46,18 @@ json_object *ipc_json_get_version() {
46 return version; 46 return version;
47} 47}
48 48
49static json_object *ipc_json_create_rect(struct sway_container *c) { 49static json_object *ipc_json_create_rect(struct wlr_box *box) {
50 json_object *rect = json_object_new_object(); 50 json_object *rect = json_object_new_object();
51 51
52 json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x)); 52 json_object_object_add(rect, "x", json_object_new_int(box->x));
53 json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y)); 53 json_object_object_add(rect, "y", json_object_new_int(box->y));
54 json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width)); 54 json_object_object_add(rect, "width", json_object_new_int(box->width));
55 json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height)); 55 json_object_object_add(rect, "height", json_object_new_int(box->height));
56 56
57 return rect; 57 return rect;
58} 58}
59 59
60static void ipc_json_describe_root(struct sway_container *root, json_object *object) { 60static void ipc_json_describe_root(struct sway_root *root, json_object *object) {
61 json_object_object_add(object, "type", json_object_new_string("root")); 61 json_object_object_add(object, "type", json_object_new_string("root"));
62 json_object_object_add(object, "layout", json_object_new_string("splith")); 62 json_object_object_add(object, "layout", json_object_new_string("splith"));
63} 63}
@@ -84,17 +84,13 @@ static const char *ipc_json_get_output_transform(enum wl_output_transform transf
84 return NULL; 84 return NULL;
85} 85}
86 86
87static void ipc_json_describe_output(struct sway_container *container, 87static void ipc_json_describe_output(struct sway_output *output,
88 json_object *object) { 88 json_object *object) {
89 struct wlr_output *wlr_output = container->sway_output->wlr_output; 89 struct wlr_output *wlr_output = output->wlr_output;
90 json_object_object_add(object, "type", 90 json_object_object_add(object, "type", json_object_new_string("output"));
91 json_object_new_string("output")); 91 json_object_object_add(object, "active", json_object_new_boolean(true));
92 json_object_object_add(object, "active", 92 json_object_object_add(object, "primary", json_object_new_boolean(false));
93 json_object_new_boolean(true)); 93 json_object_object_add(object, "layout", json_object_new_string("output"));
94 json_object_object_add(object, "primary",
95 json_object_new_boolean(false));
96 json_object_object_add(object, "layout",
97 json_object_new_string("output"));
98 json_object_object_add(object, "make", 94 json_object_object_add(object, "make",
99 json_object_new_string(wlr_output->make)); 95 json_object_new_string(wlr_output->make));
100 json_object_object_add(object, "model", 96 json_object_object_add(object, "model",
@@ -109,20 +105,9 @@ static void ipc_json_describe_output(struct sway_container *container,
109 json_object_new_string( 105 json_object_new_string(
110 ipc_json_get_output_transform(wlr_output->transform))); 106 ipc_json_get_output_transform(wlr_output->transform)));
111 107
112 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 108 struct sway_workspace *ws = output_get_active_workspace(output);
113 const char *ws = NULL;
114 if (seat) {
115 struct sway_container *focus =
116 seat_get_focus_inactive(seat, container);
117 if (focus && focus->type != C_WORKSPACE) {
118 focus = container_parent(focus, C_WORKSPACE);
119 }
120 if (focus) {
121 ws = focus->name;
122 }
123 }
124 json_object_object_add(object, "current_workspace", 109 json_object_object_add(object, "current_workspace",
125 json_object_new_string(ws)); 110 json_object_new_string(ws->name));
126 111
127 json_object *modes_array = json_object_new_array(); 112 json_object *modes_array = json_object_new_array();
128 struct wlr_output_mode *mode; 113 struct wlr_output_mode *mode;
@@ -161,60 +146,57 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
161 return object; 146 return object;
162} 147}
163 148
164static void ipc_json_describe_workspace(struct sway_container *workspace, 149static void ipc_json_describe_workspace(struct sway_workspace *workspace,
165 json_object *object) { 150 json_object *object) {
166 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; 151 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1;
167 152
168 json_object_object_add(object, "num", json_object_new_int(num)); 153 json_object_object_add(object, "num", json_object_new_int(num));
169 json_object_object_add(object, "output", workspace->parent ? 154 json_object_object_add(object, "output", workspace->output ?
170 json_object_new_string(workspace->parent->name) : NULL); 155 json_object_new_string(workspace->output->wlr_output->name) : NULL);
171 json_object_object_add(object, "type", json_object_new_string("workspace")); 156 json_object_object_add(object, "type", json_object_new_string("workspace"));
172 json_object_object_add(object, "urgent", 157 json_object_object_add(object, "urgent",
173 json_object_new_boolean(workspace->sway_workspace->urgent)); 158 json_object_new_boolean(workspace->urgent));
174 json_object_object_add(object, "representation", workspace->formatted_title ? 159 json_object_object_add(object, "representation", workspace->representation ?
175 json_object_new_string(workspace->formatted_title) : NULL); 160 json_object_new_string(workspace->representation) : NULL);
176 161
177 const char *layout = ipc_json_layout_description(workspace->layout); 162 const char *layout = ipc_json_layout_description(workspace->layout);
178 json_object_object_add(object, "layout", json_object_new_string(layout)); 163 json_object_object_add(object, "layout", json_object_new_string(layout));
179 164
180 // Floating 165 // Floating
181 json_object *floating_array = json_object_new_array(); 166 json_object *floating_array = json_object_new_array();
182 list_t *floating = workspace->sway_workspace->floating; 167 for (int i = 0; i < workspace->floating->length; ++i) {
183 for (int i = 0; i < floating->length; ++i) { 168 struct sway_container *floater = workspace->floating->items[i];
184 struct sway_container *floater = floating->items[i];
185 json_object_array_add(floating_array, 169 json_object_array_add(floating_array,
186 ipc_json_describe_container_recursive(floater)); 170 ipc_json_describe_node_recursive(&floater->node));
187 } 171 }
188 json_object_object_add(object, "floating_nodes", floating_array); 172 json_object_object_add(object, "floating_nodes", floating_array);
189} 173}
190 174
191static void ipc_json_describe_view(struct sway_container *c, json_object *object) { 175static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
192 json_object_object_add(object, "name", 176 json_object_object_add(object, "name",
193 c->name ? json_object_new_string(c->name) : NULL); 177 c->title ? json_object_new_string(c->title) : NULL);
194 json_object_object_add(object, "type", json_object_new_string("con")); 178 json_object_object_add(object, "type", json_object_new_string("con"));
195 179
196 if (c->type == C_VIEW) { 180 if (c->view) {
197 const char *app_id = view_get_app_id(c->sway_view); 181 const char *app_id = view_get_app_id(c->view);
198 json_object_object_add(object, "app_id", 182 json_object_object_add(object, "app_id",
199 app_id ? json_object_new_string(app_id) : NULL); 183 app_id ? json_object_new_string(app_id) : NULL);
200 184
201 const char *class = view_get_class(c->sway_view); 185 const char *class = view_get_class(c->view);
202 json_object_object_add(object, "class", 186 json_object_object_add(object, "class",
203 class ? json_object_new_string(class) : NULL); 187 class ? json_object_new_string(class) : NULL);
204 } 188 }
205 189
206 if (c->parent) { 190 json_object_object_add(object, "layout",
207 json_object_object_add(object, "layout", 191 json_object_new_string(ipc_json_layout_description(c->layout)));
208 json_object_new_string(ipc_json_layout_description(c->layout)));
209 }
210 192
211 bool urgent = c->type == C_VIEW ? 193 bool urgent = c->view ?
212 view_is_urgent(c->sway_view) : container_has_urgent_child(c); 194 view_is_urgent(c->view) : container_has_urgent_child(c);
213 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 195 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
214 196
215 if (c->type == C_VIEW) { 197 if (c->view) {
216 json_object *marks = json_object_new_array(); 198 json_object *marks = json_object_new_array();
217 list_t *view_marks = c->sway_view->marks; 199 list_t *view_marks = c->view->marks;
218 for (int i = 0; i < view_marks->length; ++i) { 200 for (int i = 0; i < view_marks->length; ++i) {
219 json_object_array_add(marks, json_object_new_string(view_marks->items[i])); 201 json_object_array_add(marks, json_object_new_string(view_marks->items[i]));
220 } 202 }
@@ -222,64 +204,97 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
222 } 204 }
223} 205}
224 206
225static void focus_inactive_children_iterator(struct sway_container *c, void *data) { 207struct focus_inactive_data {
226 json_object *focus = data; 208 struct sway_node *node;
227 json_object_array_add(focus, json_object_new_int(c->id)); 209 json_object *object;
228} 210};
229 211
230json_object *ipc_json_describe_container(struct sway_container *c) { 212static void focus_inactive_children_iterator(struct sway_node *node,
231 if (!(sway_assert(c, "Container must not be null."))) { 213 void *_data) {
232 return NULL; 214 struct focus_inactive_data *data = _data;
215 if (node_get_parent(node) == data->node) {
216 json_object_array_add(data->object, json_object_new_int(node->id));
233 } 217 }
218}
234 219
220json_object *ipc_json_describe_node(struct sway_node *node) {
235 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 221 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
236 bool focused = seat_get_focus(seat) == c; 222 bool focused = seat_get_focus(seat) == node;
237 223
238 json_object *object = json_object_new_object(); 224 json_object *object = json_object_new_object();
225 char *name = node_get_name(node);
239 226
240 json_object_object_add(object, "id", json_object_new_int((int)c->id)); 227 struct wlr_box box;
228 node_get_box(node, &box);
229 json_object_object_add(object, "id", json_object_new_int((int)node->id));
241 json_object_object_add(object, "name", 230 json_object_object_add(object, "name",
242 c->name ? json_object_new_string(c->name) : NULL); 231 name ? json_object_new_string(name) : NULL);
243 json_object_object_add(object, "rect", ipc_json_create_rect(c)); 232 json_object_object_add(object, "rect", ipc_json_create_rect(&box));
244 json_object_object_add(object, "focused", 233 json_object_object_add(object, "focused", json_object_new_boolean(focused));
245 json_object_new_boolean(focused));
246 234
247 json_object *focus = json_object_new_array(); 235 json_object *focus = json_object_new_array();
248 seat_focus_inactive_children_for_each(seat, c, 236 struct focus_inactive_data data = {
249 focus_inactive_children_iterator, focus); 237 .node = node,
238 .object = focus,
239 };
240 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
250 json_object_object_add(object, "focus", focus); 241 json_object_object_add(object, "focus", focus);
251 242
252 switch (c->type) { 243 switch (node->type) {
253 case C_ROOT: 244 case N_ROOT:
254 ipc_json_describe_root(c, object); 245 ipc_json_describe_root(root, object);
255 break; 246 break;
256 case C_OUTPUT: 247 case N_OUTPUT:
257 ipc_json_describe_output(c, object); 248 ipc_json_describe_output(node->sway_output, object);
258 break; 249 break;
259 case C_CONTAINER: 250 case N_CONTAINER:
260 case C_VIEW: 251 ipc_json_describe_view(node->sway_container, object);
261 ipc_json_describe_view(c, object);
262 break; 252 break;
263 case C_WORKSPACE: 253 case N_WORKSPACE:
264 ipc_json_describe_workspace(c, object); 254 ipc_json_describe_workspace(node->sway_workspace, object);
265 break;
266 case C_TYPES:
267 default:
268 break; 255 break;
269 } 256 }
270 257
271 return object; 258 return object;
272} 259}
273 260
274json_object *ipc_json_describe_container_recursive(struct sway_container *c) { 261json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
275 json_object *object = ipc_json_describe_container(c); 262 json_object *object = ipc_json_describe_node(node);
276 int i; 263 int i;
277 264
278 json_object *children = json_object_new_array(); 265 json_object *children = json_object_new_array();
279 if (c->type != C_VIEW && c->children) { 266 switch (node->type) {
280 for (i = 0; i < c->children->length; ++i) { 267 case N_ROOT:
281 json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); 268 for (i = 0; i < root->outputs->length; ++i) {
269 struct sway_output *output = root->outputs->items[i];
270 json_object_array_add(children,
271 ipc_json_describe_node_recursive(&output->node));
272 }
273 break;
274 case N_OUTPUT:
275 for (i = 0; i < node->sway_output->workspaces->length; ++i) {
276 struct sway_workspace *ws = node->sway_output->workspaces->items[i];
277 json_object_array_add(children,
278 ipc_json_describe_node_recursive(&ws->node));
282 } 279 }
280 break;
281 case N_WORKSPACE:
282 for (i = 0; i < node->sway_workspace->tiling->length; ++i) {
283 struct sway_container *con = node->sway_workspace->tiling->items[i];
284 json_object_array_add(children,
285 ipc_json_describe_node_recursive(&con->node));
286 }
287 break;
288 case N_CONTAINER:
289 if (node->sway_container->children) {
290 for (i = 0; i < node->sway_container->children->length; ++i) {
291 struct sway_container *child =
292 node->sway_container->children->items[i];
293 json_object_array_add(children,
294 ipc_json_describe_node_recursive(&child->node));
295 }
296 }
297 break;
283 } 298 }
284 json_object_object_add(object, "nodes", children); 299 json_object_object_add(object, "nodes", children);
285 300
@@ -329,7 +344,7 @@ json_object *ipc_json_describe_seat(struct sway_seat *seat) {
329 } 344 }
330 345
331 json_object *object = json_object_new_object(); 346 json_object *object = json_object_new_object();
332 struct sway_container *focus = seat_get_focus(seat); 347 struct sway_node *focus = seat_get_focus(seat);
333 348
334 json_object_object_add(object, "name", 349 json_object_object_add(object, "name",
335 json_object_new_string(seat->wlr_seat->name)); 350 json_object_new_string(seat->wlr_seat->name));
@@ -470,13 +485,13 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
470 json_object_object_add(json, "colors", colors); 485 json_object_object_add(json, "colors", colors);
471 486
472 // Add outputs if defined 487 // Add outputs if defined
488 json_object *outputs = json_object_new_array();
473 if (bar->outputs && bar->outputs->length > 0) { 489 if (bar->outputs && bar->outputs->length > 0) {
474 json_object *outputs = json_object_new_array();
475 for (int i = 0; i < bar->outputs->length; ++i) { 490 for (int i = 0; i < bar->outputs->length; ++i) {
476 const char *name = bar->outputs->items[i]; 491 const char *name = bar->outputs->items[i];
477 json_object_array_add(outputs, json_object_new_string(name)); 492 json_object_array_add(outputs, json_object_new_string(name));
478 } 493 }
479 json_object_object_add(json, "outputs", outputs);
480 } 494 }
495 json_object_object_add(json, "outputs", outputs);
481 return json; 496 return json;
482} 497}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index fb5be27b..8ae265f6 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -33,6 +33,7 @@
33#include "sway/input/seat.h" 33#include "sway/input/seat.h"
34#include "sway/tree/root.h" 34#include "sway/tree/root.h"
35#include "sway/tree/view.h" 35#include "sway/tree/view.h"
36#include "sway/tree/workspace.h"
36#include "list.h" 37#include "list.h"
37#include "log.h" 38#include "log.h"
38#include "util.h" 39#include "util.h"
@@ -291,8 +292,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event)
291 } 292 }
292} 293}
293 294
294void ipc_event_workspace(struct sway_container *old, 295void ipc_event_workspace(struct sway_workspace *old,
295 struct sway_container *new, const char *change) { 296 struct sway_workspace *new, const char *change) {
296 if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { 297 if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
297 return; 298 return;
298 } 299 }
@@ -301,14 +302,14 @@ void ipc_event_workspace(struct sway_container *old,
301 json_object_object_add(obj, "change", json_object_new_string(change)); 302 json_object_object_add(obj, "change", json_object_new_string(change));
302 if (old) { 303 if (old) {
303 json_object_object_add(obj, "old", 304 json_object_object_add(obj, "old",
304 ipc_json_describe_container_recursive(old)); 305 ipc_json_describe_node_recursive(&old->node));
305 } else { 306 } else {
306 json_object_object_add(obj, "old", NULL); 307 json_object_object_add(obj, "old", NULL);
307 } 308 }
308 309
309 if (new) { 310 if (new) {
310 json_object_object_add(obj, "current", 311 json_object_object_add(obj, "current",
311 ipc_json_describe_container_recursive(new)); 312 ipc_json_describe_node_recursive(&new->node));
312 } else { 313 } else {
313 json_object_object_add(obj, "current", NULL); 314 json_object_object_add(obj, "current", NULL);
314 } 315 }
@@ -325,7 +326,8 @@ void ipc_event_window(struct sway_container *window, const char *change) {
325 wlr_log(WLR_DEBUG, "Sending window::%s event", change); 326 wlr_log(WLR_DEBUG, "Sending window::%s event", change);
326 json_object *obj = json_object_new_object(); 327 json_object *obj = json_object_new_object();
327 json_object_object_add(obj, "change", json_object_new_string(change)); 328 json_object_object_add(obj, "change", json_object_new_string(change));
328 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); 329 json_object_object_add(obj, "container",
330 ipc_json_describe_node_recursive(&window->node));
329 331
330 const char *json_string = json_object_to_json_string(obj); 332 const char *json_string = json_object_to_json_string(obj);
331 ipc_send_event(json_string, IPC_EVENT_WINDOW); 333 ipc_send_event(json_string, IPC_EVENT_WINDOW);
@@ -521,30 +523,20 @@ void ipc_client_disconnect(struct ipc_client *client) {
521 free(client); 523 free(client);
522} 524}
523 525
524static void ipc_get_workspaces_callback(struct sway_container *workspace, 526static void ipc_get_workspaces_callback(struct sway_workspace *workspace,
525 void *data) { 527 void *data) {
526 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 528 json_object *workspace_json = ipc_json_describe_node(&workspace->node);
527 return;
528 }
529 json_object *workspace_json = ipc_json_describe_container(workspace);
530 // override the default focused indicator because 529 // override the default focused indicator because
531 // it's set differently for the get_workspaces reply 530 // it's set differently for the get_workspaces reply
532 struct sway_seat *seat = 531 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
533 input_manager_get_default_seat(input_manager); 532 struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
534 struct sway_container *focused_ws = seat_get_focus(seat);
535 if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) {
536 focused_ws = container_parent(focused_ws, C_WORKSPACE);
537 }
538 bool focused = workspace == focused_ws; 533 bool focused = workspace == focused_ws;
539 json_object_object_del(workspace_json, "focused"); 534 json_object_object_del(workspace_json, "focused");
540 json_object_object_add(workspace_json, "focused", 535 json_object_object_add(workspace_json, "focused",
541 json_object_new_boolean(focused)); 536 json_object_new_boolean(focused));
542 json_object_array_add((json_object *)data, workspace_json); 537 json_object_array_add((json_object *)data, workspace_json);
543 538
544 focused_ws = seat_get_focus_inactive(seat, workspace->parent); 539 focused_ws = output_get_active_workspace(workspace->output);
545 if (focused_ws->type != C_WORKSPACE) {
546 focused_ws = container_parent(focused_ws, C_WORKSPACE);
547 }
548 bool visible = workspace == focused_ws; 540 bool visible = workspace == focused_ws;
549 json_object_object_add(workspace_json, "visible", 541 json_object_object_add(workspace_json, "visible",
550 json_object_new_boolean(visible)); 542 json_object_new_boolean(visible));
@@ -552,9 +544,9 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace,
552 544
553static void ipc_get_marks_callback(struct sway_container *con, void *data) { 545static void ipc_get_marks_callback(struct sway_container *con, void *data) {
554 json_object *marks = (json_object *)data; 546 json_object *marks = (json_object *)data;
555 if (con->type == C_VIEW && con->sway_view->marks) { 547 if (con->view && con->view->marks) {
556 for (int i = 0; i < con->sway_view->marks->length; ++i) { 548 for (int i = 0; i < con->view->marks->length; ++i) {
557 char *mark = (char *)con->sway_view->marks->items[i]; 549 char *mark = (char *)con->view->marks->items[i];
558 json_object_array_add(marks, json_object_new_string(mark)); 550 json_object_array_add(marks, json_object_new_string(mark));
559 } 551 }
560 } 552 }
@@ -608,16 +600,14 @@ void ipc_client_handle_command(struct ipc_client *client) {
608 case IPC_GET_OUTPUTS: 600 case IPC_GET_OUTPUTS:
609 { 601 {
610 json_object *outputs = json_object_new_array(); 602 json_object *outputs = json_object_new_array();
611 for (int i = 0; i < root_container.children->length; ++i) { 603 for (int i = 0; i < root->outputs->length; ++i) {
612 struct sway_container *container = root_container.children->items[i]; 604 struct sway_output *output = root->outputs->items[i];
613 if (container->type == C_OUTPUT) { 605 json_object_array_add(outputs,
614 json_object_array_add(outputs, 606 ipc_json_describe_node(&output->node));
615 ipc_json_describe_container(container));
616 }
617 } 607 }
618 struct sway_output *output; 608 struct sway_output *output;
619 wl_list_for_each(output, &root_container.sway_root->all_outputs, link) { 609 wl_list_for_each(output, &root->all_outputs, link) {
620 if (!output->swayc) { 610 if (!output->enabled) {
621 json_object_array_add(outputs, 611 json_object_array_add(outputs,
622 ipc_json_describe_disabled_output(output)); 612 ipc_json_describe_disabled_output(output));
623 } 613 }
@@ -717,8 +707,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
717 707
718 case IPC_GET_TREE: 708 case IPC_GET_TREE:
719 { 709 {
720 json_object *tree = 710 json_object *tree = ipc_json_describe_node_recursive(&root->node);
721 ipc_json_describe_container_recursive(&root_container);
722 const char *json_string = json_object_to_json_string(tree); 711 const char *json_string = json_object_to_json_string(tree);
723 client_valid = 712 client_valid =
724 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); 713 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
diff --git a/sway/main.c b/sway/main.c
index 2f05dc38..fb4f0d8c 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -42,7 +42,6 @@ void sway_terminate(int exit_code) {
42} 42}
43 43
44void sig_handler(int signal) { 44void sig_handler(int signal) {
45 //close_views(&root_container);
46 sway_terminate(EXIT_SUCCESS); 45 sway_terminate(EXIT_SUCCESS);
47} 46}
48 47
@@ -395,7 +394,7 @@ int main(int argc, char **argv) {
395 394
396 wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION); 395 wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION);
397 396
398 root_create(); 397 root = root_create();
399 398
400 if (!server_init(&server)) { 399 if (!server_init(&server)) {
401 return 1; 400 return 1;
@@ -450,7 +449,8 @@ int main(int argc, char **argv) {
450 wlr_log(WLR_INFO, "Shutting down sway"); 449 wlr_log(WLR_INFO, "Shutting down sway");
451 450
452 server_fini(&server); 451 server_fini(&server);
453 root_destroy(); 452 root_destroy(root);
453 root = NULL;
454 454
455 if (config) { 455 if (config) {
456 free_config(config); 456 free_config(config);
diff --git a/sway/meson.build b/sway/meson.build
index c14e58dd..8891ebc0 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -151,6 +151,7 @@ sway_sources = files(
151 151
152 'tree/arrange.c', 152 'tree/arrange.c',
153 'tree/container.c', 153 'tree/container.c',
154 'tree/node.c',
154 'tree/root.c', 155 'tree/root.c',
155 'tree/view.c', 156 'tree/view.c',
156 'tree/workspace.c', 157 'tree/workspace.c',
diff --git a/sway/server.c b/sway/server.c
index 749365cb..09ebe83f 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -61,8 +61,7 @@ bool server_init(struct sway_server *server) {
61 server->new_output.notify = handle_new_output; 61 server->new_output.notify = handle_new_output;
62 wl_signal_add(&server->backend->events.new_output, &server->new_output); 62 wl_signal_add(&server->backend->events.new_output, &server->new_output);
63 63
64 wlr_xdg_output_manager_create(server->wl_display, 64 wlr_xdg_output_manager_create(server->wl_display, root->output_layout);
65 root_container.sway_root->output_layout);
66 65
67 server->idle = wlr_idle_create(server->wl_display); 66 server->idle = wlr_idle_create(server->wl_display);
68 server->idle_inhibit_manager_v1 = 67 server->idle_inhibit_manager_v1 =
@@ -131,7 +130,7 @@ bool server_init(struct sway_server *server) {
131 server->txn_timeout_ms = 200; 130 server->txn_timeout_ms = 200;
132 } 131 }
133 132
134 server->dirty_containers = create_list(); 133 server->dirty_nodes = create_list();
135 server->transactions = create_list(); 134 server->transactions = create_list();
136 135
137 input_manager = input_manager_create(server); 136 input_manager = input_manager_create(server);
@@ -145,7 +144,7 @@ void server_fini(struct sway_server *server) {
145#endif 144#endif
146 wl_display_destroy_clients(server->wl_display); 145 wl_display_destroy_clients(server->wl_display);
147 wl_display_destroy(server->wl_display); 146 wl_display_destroy(server->wl_display);
148 list_free(server->dirty_containers); 147 list_free(server->dirty_nodes);
149 list_free(server->transactions); 148 list_free(server->transactions);
150} 149}
151 150
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 92f20fcc..f86d4a74 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -166,29 +166,23 @@ void arrange_container(struct sway_container *container) {
166 if (config->reloading) { 166 if (config->reloading) {
167 return; 167 return;
168 } 168 }
169 if (container->type == C_VIEW) { 169 if (container->view) {
170 view_autoconfigure(container->sway_view); 170 view_autoconfigure(container->view);
171 container_set_dirty(container); 171 node_set_dirty(&container->node);
172 return;
173 }
174 if (!sway_assert(container->type == C_CONTAINER, "Expected a container")) {
175 return; 172 return;
176 } 173 }
177 struct wlr_box box; 174 struct wlr_box box;
178 container_get_box(container, &box); 175 container_get_box(container, &box);
179 arrange_children(container->children, container->layout, &box); 176 arrange_children(container->children, container->layout, &box);
180 container_set_dirty(container); 177 node_set_dirty(&container->node);
181} 178}
182 179
183void arrange_workspace(struct sway_container *workspace) { 180void arrange_workspace(struct sway_workspace *workspace) {
184 if (config->reloading) { 181 if (config->reloading) {
185 return; 182 return;
186 } 183 }
187 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 184 struct sway_output *output = workspace->output;
188 return; 185 struct wlr_box *area = &output->usable_area;
189 }
190 struct sway_container *output = workspace->parent;
191 struct wlr_box *area = &output->sway_output->usable_area;
192 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", 186 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",
193 area->width, area->height, area->x, area->y); 187 area->width, area->height, area->x, area->y);
194 workspace_remove_gaps(workspace); 188 workspace_remove_gaps(workspace);
@@ -197,21 +191,20 @@ void arrange_workspace(struct sway_container *workspace) {
197 double prev_y = workspace->y; 191 double prev_y = workspace->y;
198 workspace->width = area->width; 192 workspace->width = area->width;
199 workspace->height = area->height; 193 workspace->height = area->height;
200 workspace->x = output->x + area->x; 194 workspace->x = output->wlr_output->lx + area->x;
201 workspace->y = output->y + area->y; 195 workspace->y = output->wlr_output->ly + area->y;
202 196
203 // Adjust any floating containers 197 // Adjust any floating containers
204 double diff_x = workspace->x - prev_x; 198 double diff_x = workspace->x - prev_x;
205 double diff_y = workspace->y - prev_y; 199 double diff_y = workspace->y - prev_y;
206 if (diff_x != 0 || diff_y != 0) { 200 if (diff_x != 0 || diff_y != 0) {
207 for (int i = 0; i < workspace->sway_workspace->floating->length; ++i) { 201 for (int i = 0; i < workspace->floating->length; ++i) {
208 struct sway_container *floater = 202 struct sway_container *floater = workspace->floating->items[i];
209 workspace->sway_workspace->floating->items[i];
210 container_floating_translate(floater, diff_x, diff_y); 203 container_floating_translate(floater, diff_x, diff_y);
211 double center_x = floater->x + floater->width / 2; 204 double center_x = floater->x + floater->width / 2;
212 double center_y = floater->y + floater->height / 2; 205 double center_y = floater->y + floater->height / 2;
213 struct wlr_box workspace_box; 206 struct wlr_box workspace_box;
214 container_get_box(workspace, &workspace_box); 207 workspace_get_box(workspace, &workspace_box);
215 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 208 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) {
216 container_floating_move_to_center(floater); 209 container_floating_move_to_center(floater);
217 } 210 }
@@ -219,43 +212,32 @@ void arrange_workspace(struct sway_container *workspace) {
219 } 212 }
220 213
221 workspace_add_gaps(workspace); 214 workspace_add_gaps(workspace);
222 container_set_dirty(workspace); 215 node_set_dirty(&workspace->node);
223 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, 216 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
224 workspace->x, workspace->y); 217 workspace->x, workspace->y);
225 if (workspace->sway_workspace->fullscreen) { 218 if (workspace->fullscreen) {
226 struct sway_container *fs = workspace->sway_workspace->fullscreen; 219 struct sway_container *fs = workspace->fullscreen;
227 fs->x = workspace->parent->x; 220 fs->x = output->wlr_output->lx;
228 fs->y = workspace->parent->y; 221 fs->y = output->wlr_output->ly;
229 fs->width = workspace->parent->width; 222 fs->width = output->wlr_output->width;
230 fs->height = workspace->parent->height; 223 fs->height = output->wlr_output->height;
231 arrange_container(fs); 224 arrange_container(fs);
232 } else { 225 } else {
233 struct wlr_box box; 226 struct wlr_box box;
234 container_get_box(workspace, &box); 227 workspace_get_box(workspace, &box);
235 arrange_children(workspace->children, workspace->layout, &box); 228 arrange_children(workspace->tiling, workspace->layout, &box);
236 arrange_floating(workspace->sway_workspace->floating); 229 arrange_floating(workspace->floating);
237 } 230 }
238} 231}
239 232
240void arrange_output(struct sway_container *output) { 233void arrange_output(struct sway_output *output) {
241 if (config->reloading) { 234 if (config->reloading) {
242 return; 235 return;
243 } 236 }
244 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 237 // Outputs have no pending x/y/width/height,
245 return; 238 // so all we do here is arrange the workspaces.
246 } 239 for (int i = 0; i < output->workspaces->length; ++i) {
247 const struct wlr_box *output_box = wlr_output_layout_get_box( 240 struct sway_workspace *workspace = output->workspaces->items[i];
248 root_container.sway_root->output_layout,
249 output->sway_output->wlr_output);
250 output->x = output_box->x;
251 output->y = output_box->y;
252 output->width = output_box->width;
253 output->height = output_box->height;
254 container_set_dirty(output);
255 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
256 output->name, output->x, output->y);
257 for (int i = 0; i < output->children->length; ++i) {
258 struct sway_container *workspace = output->children->items[i];
259 arrange_workspace(workspace); 241 arrange_workspace(workspace);
260 } 242 }
261} 243}
@@ -264,37 +246,31 @@ void arrange_root(void) {
264 if (config->reloading) { 246 if (config->reloading) {
265 return; 247 return;
266 } 248 }
267 struct wlr_output_layout *output_layout =
268 root_container.sway_root->output_layout;
269 const struct wlr_box *layout_box = 249 const struct wlr_box *layout_box =
270 wlr_output_layout_get_box(output_layout, NULL); 250 wlr_output_layout_get_box(root->output_layout, NULL);
271 root_container.x = layout_box->x; 251 root->x = layout_box->x;
272 root_container.y = layout_box->y; 252 root->y = layout_box->y;
273 root_container.width = layout_box->width; 253 root->width = layout_box->width;
274 root_container.height = layout_box->height; 254 root->height = layout_box->height;
275 container_set_dirty(&root_container); 255 for (int i = 0; i < root->outputs->length; ++i) {
276 for (int i = 0; i < root_container.children->length; ++i) { 256 struct sway_output *output = root->outputs->items[i];
277 struct sway_container *output = root_container.children->items[i];
278 arrange_output(output); 257 arrange_output(output);
279 } 258 }
280} 259}
281 260
282void arrange_windows(struct sway_container *container) { 261void arrange_node(struct sway_node *node) {
283 switch (container->type) { 262 switch (node->type) {
284 case C_ROOT: 263 case N_ROOT:
285 arrange_root(); 264 arrange_root();
286 break; 265 break;
287 case C_OUTPUT: 266 case N_OUTPUT:
288 arrange_output(container); 267 arrange_output(node->sway_output);
289 break;
290 case C_WORKSPACE:
291 arrange_workspace(container);
292 break; 268 break;
293 case C_CONTAINER: 269 case N_WORKSPACE:
294 case C_VIEW: 270 arrange_workspace(node->sway_workspace);
295 arrange_container(container);
296 break; 271 break;
297 case C_TYPES: 272 case N_CONTAINER:
273 arrange_container(node->sway_container);
298 break; 274 break;
299 } 275 }
300} 276}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 520b4566..0cb8d0a5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -24,97 +24,39 @@
24#include "log.h" 24#include "log.h"
25#include "stringop.h" 25#include "stringop.h"
26 26
27const char *container_type_to_str(enum sway_container_type type) { 27struct sway_container *container_create(struct sway_view *view) {
28 switch (type) {
29 case C_ROOT:
30 return "C_ROOT";
31 case C_OUTPUT:
32 return "C_OUTPUT";
33 case C_WORKSPACE:
34 return "C_WORKSPACE";
35 case C_CONTAINER:
36 return "C_CONTAINER";
37 case C_VIEW:
38 return "C_VIEW";
39 default:
40 return "C_UNKNOWN";
41 }
42}
43
44void container_create_notify(struct sway_container *container) {
45 if (container->type == C_VIEW) {
46 ipc_event_window(container, "new");
47 } else if (container->type == C_WORKSPACE) {
48 ipc_event_workspace(NULL, container, "init");
49 }
50 wl_signal_emit(&root_container.sway_root->events.new_container, container);
51}
52
53void container_update_textures_recursive(struct sway_container *con) {
54 if (con->type == C_CONTAINER || con->type == C_VIEW) {
55 container_update_title_textures(con);
56 }
57
58 if (con->type == C_VIEW) {
59 view_update_marks_textures(con->sway_view);
60 } else {
61 for (int i = 0; i < con->children->length; ++i) {
62 struct sway_container *child = con->children->items[i];
63 container_update_textures_recursive(child);
64 }
65
66 if (con->type == C_WORKSPACE) {
67 for (int i = 0; i < con->sway_workspace->floating->length; ++i) {
68 struct sway_container *floater =
69 con->sway_workspace->floating->items[i];
70 container_update_textures_recursive(floater);
71 }
72 }
73 }
74}
75
76struct sway_container *container_create(enum sway_container_type type) {
77 // next id starts at 1 because 0 is assigned to root_container in layout.c
78 static size_t next_id = 1;
79 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 28 struct sway_container *c = calloc(1, sizeof(struct sway_container));
80 if (!c) { 29 if (!c) {
30 wlr_log(WLR_ERROR, "Unable to allocate sway_container");
81 return NULL; 31 return NULL;
82 } 32 }
83 c->id = next_id++; 33 node_init(&c->node, N_CONTAINER, c);
84 c->layout = L_NONE; 34 c->layout = L_NONE;
85 c->type = type; 35 c->view = view;
86 c->alpha = 1.0f; 36 c->alpha = 1.0f;
87 37
88 if (type != C_VIEW) { 38 if (!view) {
89 c->children = create_list(); 39 c->children = create_list();
90 c->current.children = create_list(); 40 c->current.children = create_list();
91 } 41 }
92 c->outputs = create_list(); 42 c->outputs = create_list();
93 43
94 wl_signal_init(&c->events.destroy); 44 wl_signal_init(&c->events.destroy);
95 45 wl_signal_emit(&root->events.new_node, &c->node);
96 c->has_gaps = false;
97 c->gaps_inner = 0;
98 c->gaps_outer = 0;
99 c->current_gaps = 0;
100 46
101 return c; 47 return c;
102} 48}
103 49
104void container_destroy(struct sway_container *con) { 50void container_destroy(struct sway_container *con) {
105 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, 51 if (!sway_assert(con->node.destroying,
106 "Expected a container or view")) {
107 return;
108 }
109 if (!sway_assert(con->destroying,
110 "Tried to free container which wasn't marked as destroying")) { 52 "Tried to free container which wasn't marked as destroying")) {
111 return; 53 return;
112 } 54 }
113 if (!sway_assert(con->ntxnrefs == 0, "Tried to free container " 55 if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container "
114 "which is still referenced by transactions")) { 56 "which is still referenced by transactions")) {
115 return; 57 return;
116 } 58 }
117 free(con->name); 59 free(con->title);
118 free(con->formatted_title); 60 free(con->formatted_title);
119 wlr_texture_destroy(con->title_focused); 61 wlr_texture_destroy(con->title_focused);
120 wlr_texture_destroy(con->title_focused_inactive); 62 wlr_texture_destroy(con->title_focused_inactive);
@@ -124,14 +66,14 @@ void container_destroy(struct sway_container *con) {
124 list_free(con->current.children); 66 list_free(con->current.children);
125 list_free(con->outputs); 67 list_free(con->outputs);
126 68
127 if (con->type == C_VIEW) { 69 if (con->view) {
128 struct sway_view *view = con->sway_view; 70 struct sway_view *view = con->view;
129 view->swayc = NULL; 71 view->container = NULL;
130 free(view->title_format); 72 free(view->title_format);
131 view->title_format = NULL; 73 view->title_format = NULL;
132 74
133 if (view->destroying) { 75 if (view->destroying) {
134 view_destroy(view); 76 view_destroy(con->view);
135 } 77 }
136 } 78 }
137 79
@@ -139,115 +81,57 @@ void container_destroy(struct sway_container *con) {
139} 81}
140 82
141void container_begin_destroy(struct sway_container *con) { 83void container_begin_destroy(struct sway_container *con) {
142 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, 84 if (con->view) {
143 "Expected a container or view")) {
144 return;
145 }
146
147 if (con->type == C_VIEW) {
148 ipc_event_window(con, "close"); 85 ipc_event_window(con, "close");
149 } 86 }
150 wl_signal_emit(&con->events.destroy, con); 87 wl_signal_emit(&con->node.events.destroy, &con->node);
151 88
152 container_end_mouse_operation(con); 89 container_end_mouse_operation(con);
153 90
154 con->destroying = true; 91 con->node.destroying = true;
155 container_set_dirty(con); 92 node_set_dirty(&con->node);
156 93
157 if (con->scratchpad) { 94 if (con->scratchpad) {
158 root_scratchpad_remove_container(con); 95 root_scratchpad_remove_container(con);
159 } 96 }
160 97
161 if (con->parent) { 98 if (con->parent || con->workspace) {
162 container_remove_child(con); 99 container_detach(con);
163 } 100 }
164} 101}
165 102
166struct sway_container *container_reap_empty(struct sway_container *con) { 103void container_reap_empty(struct sway_container *con) {
167 while (con && con->type == C_CONTAINER) { 104 if (con->view) {
168 struct sway_container *next = con->parent; 105 return;
169 if (con->children->length == 0) {
170 container_begin_destroy(con);
171 }
172 con = next;
173 } 106 }
174 if (con && con->type == C_WORKSPACE) { 107 struct sway_workspace *ws = con->workspace;
175 workspace_consider_destroy(con); 108 while (con) {
176 if (con->destroying) { 109 if (con->children->length) {
177 con = con->parent; 110 return;
178 } 111 }
112 struct sway_container *parent = con->parent;
113 container_begin_destroy(con);
114 con = parent;
179 } 115 }
180 return con; 116 workspace_consider_destroy(ws);
181} 117}
182 118
183struct sway_container *container_flatten(struct sway_container *container) { 119struct sway_container *container_flatten(struct sway_container *container) {
184 while (container->type == C_CONTAINER && container->children->length == 1) { 120 if (container->view) {
121 return NULL;
122 }
123 while (container && container->children->length == 1) {
185 struct sway_container *child = container->children->items[0]; 124 struct sway_container *child = container->children->items[0];
186 struct sway_container *parent = container->parent; 125 struct sway_container *parent = container->parent;
187 container_replace_child(container, child); 126 container_replace(container, child);
188 container_begin_destroy(container); 127 container_begin_destroy(container);
189 container = parent; 128 container = parent;
190 } 129 }
191 return container; 130 return container;
192} 131}
193 132
194static void container_close_func(struct sway_container *container, void *data) {
195 if (container->type == C_VIEW) {
196 view_close(container->sway_view);
197 }
198}
199
200struct sway_container *container_close(struct sway_container *con) {
201 if (!sway_assert(con != NULL,
202 "container_close called with a NULL container")) {
203 return NULL;
204 }
205
206 struct sway_container *parent = con->parent;
207
208 if (con->type == C_VIEW) {
209 view_close(con->sway_view);
210 } else if (con->type == C_CONTAINER) {
211 container_for_each_child(con, container_close_func, NULL);
212 } else if (con->type == C_WORKSPACE) {
213 workspace_for_each_container(con, container_close_func, NULL);
214 }
215
216 return parent;
217}
218
219struct sway_container *container_view_create(struct sway_container *sibling,
220 struct sway_view *sway_view) {
221 if (!sway_assert(sibling,
222 "container_view_create called with NULL sibling/parent")) {
223 return NULL;
224 }
225 const char *title = view_get_title(sway_view);
226 struct sway_container *swayc = container_create(C_VIEW);
227 wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s",
228 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
229 // Setup values
230 swayc->sway_view = sway_view;
231 swayc->width = 0;
232 swayc->height = 0;
233
234 if (sibling->type == C_WORKSPACE) {
235 // Case of focused workspace, just create as child of it
236 container_add_child(sibling, swayc);
237 } else {
238 // Regular case, create as sibling of current container
239 container_add_sibling(sibling, swayc);
240 }
241 container_create_notify(swayc);
242 return swayc;
243}
244
245struct sway_container *container_find_child(struct sway_container *container, 133struct sway_container *container_find_child(struct sway_container *container,
246 bool (*test)(struct sway_container *view, void *data), void *data) { 134 bool (*test)(struct sway_container *con, void *data), void *data) {
247 if (!sway_assert(container->type == C_CONTAINER ||
248 container->type == C_VIEW, "Expected a container or view")) {
249 return NULL;
250 }
251 if (!container->children) { 135 if (!container->children) {
252 return NULL; 136 return NULL;
253 } 137 }
@@ -264,46 +148,32 @@ struct sway_container *container_find_child(struct sway_container *container,
264 return NULL; 148 return NULL;
265} 149}
266 150
267struct sway_container *container_parent(struct sway_container *container, 151static void surface_at_view(struct sway_container *con, double lx, double ly,
268 enum sway_container_type type) {
269 if (!sway_assert(container, "container is NULL")) {
270 return NULL;
271 }
272 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
273 return NULL;
274 }
275 do {
276 container = container->parent;
277 } while (container && container->type != type);
278 return container;
279}
280
281static void surface_at_view(struct sway_container *swayc, double lx, double ly,
282 struct wlr_surface **surface, double *sx, double *sy) { 152 struct wlr_surface **surface, double *sx, double *sy) {
283 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { 153 if (!sway_assert(con->view, "Expected a view")) {
284 return; 154 return;
285 } 155 }
286 struct sway_view *sview = swayc->sway_view; 156 struct sway_view *view = con->view;
287 double view_sx = lx - sview->x + sview->geometry.x; 157 double view_sx = lx - view->x + view->geometry.x;
288 double view_sy = ly - sview->y + sview->geometry.y; 158 double view_sy = ly - view->y + view->geometry.y;
289 159
290 double _sx, _sy; 160 double _sx, _sy;
291 struct wlr_surface *_surface = NULL; 161 struct wlr_surface *_surface = NULL;
292 switch (sview->type) { 162 switch (view->type) {
293#ifdef HAVE_XWAYLAND 163#ifdef HAVE_XWAYLAND
294 case SWAY_VIEW_XWAYLAND: 164 case SWAY_VIEW_XWAYLAND:
295 _surface = wlr_surface_surface_at(sview->surface, 165 _surface = wlr_surface_surface_at(view->surface,
296 view_sx, view_sy, &_sx, &_sy); 166 view_sx, view_sy, &_sx, &_sy);
297 break; 167 break;
298#endif 168#endif
299 case SWAY_VIEW_XDG_SHELL_V6: 169 case SWAY_VIEW_XDG_SHELL_V6:
300 _surface = wlr_xdg_surface_v6_surface_at( 170 _surface = wlr_xdg_surface_v6_surface_at(
301 sview->wlr_xdg_surface_v6, 171 view->wlr_xdg_surface_v6,
302 view_sx, view_sy, &_sx, &_sy); 172 view_sx, view_sy, &_sx, &_sy);
303 break; 173 break;
304 case SWAY_VIEW_XDG_SHELL: 174 case SWAY_VIEW_XDG_SHELL:
305 _surface = wlr_xdg_surface_surface_at( 175 _surface = wlr_xdg_surface_surface_at(
306 sview->wlr_xdg_surface, 176 view->wlr_xdg_surface,
307 view_sx, view_sy, &_sx, &_sy); 177 view_sx, view_sy, &_sx, &_sy);
308 break; 178 break;
309 } 179 }
@@ -317,65 +187,72 @@ static void surface_at_view(struct sway_container *swayc, double lx, double ly,
317/** 187/**
318 * container_at for a container with layout L_TABBED. 188 * container_at for a container with layout L_TABBED.
319 */ 189 */
320static struct sway_container *container_at_tabbed(struct sway_container *parent, 190static struct sway_container *container_at_tabbed(struct sway_node *parent,
321 double lx, double ly, 191 double lx, double ly,
322 struct wlr_surface **surface, double *sx, double *sy) { 192 struct wlr_surface **surface, double *sx, double *sy) {
323 if (ly < parent->y || ly > parent->y + parent->height) { 193 struct wlr_box box;
194 node_get_box(parent, &box);
195 if (ly < box.y || ly > box.y + box.height) {
324 return NULL; 196 return NULL;
325 } 197 }
326 struct sway_seat *seat = input_manager_current_seat(input_manager); 198 struct sway_seat *seat = input_manager_current_seat(input_manager);
199 list_t *children = node_get_children(parent);
327 200
328 // Tab titles 201 // Tab titles
329 int title_height = container_titlebar_height(); 202 int title_height = container_titlebar_height();
330 if (ly < parent->y + title_height) { 203 if (ly < box.y + title_height) {
331 int tab_width = parent->width / parent->children->length; 204 int tab_width = box.width / children->length;
332 int child_index = (lx - parent->x) / tab_width; 205 int child_index = (lx - box.x) / tab_width;
333 if (child_index >= parent->children->length) { 206 if (child_index >= children->length) {
334 child_index = parent->children->length - 1; 207 child_index = children->length - 1;
335 } 208 }
336 struct sway_container *child = parent->children->items[child_index]; 209 struct sway_container *child = children->items[child_index];
337 return seat_get_focus_inactive(seat, child); 210 struct sway_node *node = seat_get_focus_inactive(seat, &child->node);
211 return node->sway_container;
338 } 212 }
339 213
340 // Surfaces 214 // Surfaces
341 struct sway_container *current = seat_get_active_child(seat, parent); 215 struct sway_node *current = seat_get_active_child(seat, parent);
342
343 return tiling_container_at(current, lx, ly, surface, sx, sy); 216 return tiling_container_at(current, lx, ly, surface, sx, sy);
344} 217}
345 218
346/** 219/**
347 * container_at for a container with layout L_STACKED. 220 * container_at for a container with layout L_STACKED.
348 */ 221 */
349static struct sway_container *container_at_stacked( 222static struct sway_container *container_at_stacked(struct sway_node *parent,
350 struct sway_container *parent, double lx, double ly, 223 double lx, double ly,
351 struct wlr_surface **surface, double *sx, double *sy) { 224 struct wlr_surface **surface, double *sx, double *sy) {
352 if (ly < parent->y || ly > parent->y + parent->height) { 225 struct wlr_box box;
226 node_get_box(parent, &box);
227 if (ly < box.y || ly > box.y + box.height) {
353 return NULL; 228 return NULL;
354 } 229 }
355 struct sway_seat *seat = input_manager_current_seat(input_manager); 230 struct sway_seat *seat = input_manager_current_seat(input_manager);
231 list_t *children = node_get_children(parent);
356 232
357 // Title bars 233 // Title bars
358 int title_height = container_titlebar_height(); 234 int title_height = container_titlebar_height();
359 int child_index = (ly - parent->y) / title_height; 235 int child_index = (ly - box.y) / title_height;
360 if (child_index < parent->children->length) { 236 if (child_index < children->length) {
361 struct sway_container *child = parent->children->items[child_index]; 237 struct sway_container *child = children->items[child_index];
362 return seat_get_focus_inactive(seat, child); 238 struct sway_node *node = seat_get_focus_inactive(seat, &child->node);
239 return node->sway_container;
363 } 240 }
364 241
365 // Surfaces 242 // Surfaces
366 struct sway_container *current = seat_get_active_child(seat, parent); 243 struct sway_node *current = seat_get_active_child(seat, parent);
367
368 return tiling_container_at(current, lx, ly, surface, sx, sy); 244 return tiling_container_at(current, lx, ly, surface, sx, sy);
369} 245}
370 246
371/** 247/**
372 * container_at for a container with layout L_HORIZ or L_VERT. 248 * container_at for a container with layout L_HORIZ or L_VERT.
373 */ 249 */
374static struct sway_container *container_at_linear(struct sway_container *parent, 250static struct sway_container *container_at_linear(struct sway_node *parent,
375 double lx, double ly, 251 double lx, double ly,
376 struct wlr_surface **surface, double *sx, double *sy) { 252 struct wlr_surface **surface, double *sx, double *sy) {
377 for (int i = 0; i < parent->children->length; ++i) { 253 list_t *children = node_get_children(parent);
378 struct sway_container *child = parent->children->items[i]; 254 for (int i = 0; i < children->length; ++i) {
255 struct sway_container *child = children->items[i];
379 struct wlr_box box = { 256 struct wlr_box box = {
380 .x = child->x, 257 .x = child->x,
381 .y = child->y, 258 .y = child->y,
@@ -383,7 +260,7 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
383 .height = child->height, 260 .height = child->height,
384 }; 261 };
385 if (wlr_box_contains_point(&box, lx, ly)) { 262 if (wlr_box_contains_point(&box, lx, ly)) {
386 return tiling_container_at(child, lx, ly, surface, sx, sy); 263 return tiling_container_at(&child->node, lx, ly, surface, sx, sy);
387 } 264 }
388 } 265 }
389 return NULL; 266 return NULL;
@@ -391,12 +268,11 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
391 268
392static struct sway_container *floating_container_at(double lx, double ly, 269static struct sway_container *floating_container_at(double lx, double ly,
393 struct wlr_surface **surface, double *sx, double *sy) { 270 struct wlr_surface **surface, double *sx, double *sy) {
394 for (int i = 0; i < root_container.children->length; ++i) { 271 for (int i = 0; i < root->outputs->length; ++i) {
395 struct sway_container *output = root_container.children->items[i]; 272 struct sway_output *output = root->outputs->items[i];
396 for (int j = 0; j < output->children->length; ++j) { 273 for (int j = 0; j < output->workspaces->length; ++j) {
397 struct sway_container *workspace = output->children->items[j]; 274 struct sway_workspace *ws = output->workspaces->items[j];
398 struct sway_workspace *ws = workspace->sway_workspace; 275 if (!workspace_is_visible(ws)) {
399 if (!workspace_is_visible(workspace)) {
400 continue; 276 continue;
401 } 277 }
402 // Items at the end of the list are on top, so iterate the list in 278 // Items at the end of the list are on top, so iterate the list in
@@ -410,7 +286,7 @@ static struct sway_container *floating_container_at(double lx, double ly,
410 .height = floater->height, 286 .height = floater->height,
411 }; 287 };
412 if (wlr_box_contains_point(&box, lx, ly)) { 288 if (wlr_box_contains_point(&box, lx, ly)) {
413 return tiling_container_at(floater, lx, ly, 289 return tiling_container_at(&floater->node, lx, ly,
414 surface, sx, sy); 290 surface, sx, sy);
415 } 291 }
416 } 292 }
@@ -419,25 +295,24 @@ static struct sway_container *floating_container_at(double lx, double ly,
419 return NULL; 295 return NULL;
420} 296}
421 297
422struct sway_container *tiling_container_at( 298struct sway_container *tiling_container_at(struct sway_node *parent,
423 struct sway_container *con, double lx, double ly, 299 double lx, double ly,
424 struct wlr_surface **surface, double *sx, double *sy) { 300 struct wlr_surface **surface, double *sx, double *sy) {
425 if (con->type == C_VIEW) { 301 if (node_is_view(parent)) {
426 surface_at_view(con, lx, ly, surface, sx, sy); 302 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
427 return con; 303 return parent->sway_container;
428 } 304 }
429 if (!con->children->length) { 305 if (!node_get_children(parent)) {
430 return NULL; 306 return NULL;
431 } 307 }
432 308 switch (node_get_layout(parent)) {
433 switch (con->layout) {
434 case L_HORIZ: 309 case L_HORIZ:
435 case L_VERT: 310 case L_VERT:
436 return container_at_linear(con, lx, ly, surface, sx, sy); 311 return container_at_linear(parent, lx, ly, surface, sx, sy);
437 case L_TABBED: 312 case L_TABBED:
438 return container_at_tabbed(con, lx, ly, surface, sx, sy); 313 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
439 case L_STACKED: 314 case L_STACKED:
440 return container_at_stacked(con, lx, ly, surface, sx, sy); 315 return container_at_stacked(parent, lx, ly, surface, sx, sy);
441 case L_NONE: 316 case L_NONE:
442 return NULL; 317 return NULL;
443 } 318 }
@@ -472,19 +347,16 @@ static bool surface_is_popup(struct wlr_surface *surface) {
472 return false; 347 return false;
473} 348}
474 349
475struct sway_container *container_at(struct sway_container *workspace, 350struct sway_container *container_at(struct sway_workspace *workspace,
476 double lx, double ly, 351 double lx, double ly,
477 struct wlr_surface **surface, double *sx, double *sy) { 352 struct wlr_surface **surface, double *sx, double *sy) {
478 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
479 return NULL;
480 }
481 struct sway_container *c; 353 struct sway_container *c;
354 // Focused view's popups
482 struct sway_seat *seat = input_manager_current_seat(input_manager); 355 struct sway_seat *seat = input_manager_current_seat(input_manager);
483 struct sway_container *focus = 356 struct sway_container *focus = seat_get_focused_container(seat);
484 seat_get_focus_inactive(seat, &root_container);
485 bool is_floating = focus && container_is_floating_or_child(focus); 357 bool is_floating = focus && container_is_floating_or_child(focus);
486 // Focused view's popups 358 // Focused view's popups
487 if (focus && focus->type == C_VIEW) { 359 if (focus && focus->view) {
488 surface_at_view(focus, lx, ly, surface, sx, sy); 360 surface_at_view(focus, lx, ly, surface, sx, sy);
489 if (*surface && surface_is_popup(*surface)) { 361 if (*surface && surface_is_popup(*surface)) {
490 return focus; 362 return focus;
@@ -492,7 +364,7 @@ struct sway_container *container_at(struct sway_container *workspace,
492 *surface = NULL; 364 *surface = NULL;
493 } 365 }
494 // If focused is floating, focused view's non-popups 366 // If focused is floating, focused view's non-popups
495 if (focus && focus->type == C_VIEW && is_floating) { 367 if (focus && focus->view && is_floating) {
496 surface_at_view(focus, lx, ly, surface, sx, sy); 368 surface_at_view(focus, lx, ly, surface, sx, sy);
497 if (*surface) { 369 if (*surface) {
498 return focus; 370 return focus;
@@ -504,7 +376,7 @@ struct sway_container *container_at(struct sway_container *workspace,
504 return c; 376 return c;
505 } 377 }
506 // If focused is tiling, focused view's non-popups 378 // If focused is tiling, focused view's non-popups
507 if (focus && focus->type == C_VIEW && !is_floating) { 379 if (focus && focus->view && !is_floating) {
508 surface_at_view(focus, lx, ly, surface, sx, sy); 380 surface_at_view(focus, lx, ly, surface, sx, sy);
509 if (*surface) { 381 if (*surface) {
510 return focus; 382 return focus;
@@ -512,7 +384,7 @@ struct sway_container *container_at(struct sway_container *workspace,
512 *surface = NULL; 384 *surface = NULL;
513 } 385 }
514 // Tiling (non-focused) 386 // Tiling (non-focused)
515 if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { 387 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
516 return c; 388 return c;
517 } 389 }
518 return NULL; 390 return NULL;
@@ -521,10 +393,6 @@ struct sway_container *container_at(struct sway_container *workspace,
521void container_for_each_child(struct sway_container *container, 393void container_for_each_child(struct sway_container *container,
522 void (*f)(struct sway_container *container, void *data), 394 void (*f)(struct sway_container *container, void *data),
523 void *data) { 395 void *data) {
524 if (!sway_assert(container->type == C_CONTAINER ||
525 container->type == C_VIEW, "Expected a container or view")) {
526 return;
527 }
528 if (container->children) { 396 if (container->children) {
529 for (int i = 0; i < container->children->length; ++i) { 397 for (int i = 0; i < container->children->length; ++i) {
530 struct sway_container *child = container->children->items[i]; 398 struct sway_container *child = container->children->items[i];
@@ -536,7 +404,7 @@ void container_for_each_child(struct sway_container *container,
536 404
537bool container_has_ancestor(struct sway_container *descendant, 405bool container_has_ancestor(struct sway_container *descendant,
538 struct sway_container *ancestor) { 406 struct sway_container *ancestor) {
539 while (descendant && descendant->type != C_ROOT) { 407 while (descendant) {
540 descendant = descendant->parent; 408 descendant = descendant->parent;
541 if (descendant == ancestor) { 409 if (descendant == ancestor) {
542 return true; 410 return true;
@@ -545,27 +413,10 @@ bool container_has_ancestor(struct sway_container *descendant,
545 return false; 413 return false;
546} 414}
547 415
548int container_count_descendants_of_type(struct sway_container *con,
549 enum sway_container_type type) {
550 int children = 0;
551 if (con->type == type) {
552 children++;
553 }
554 if (con->children) {
555 for (int i = 0; i < con->children->length; i++) {
556 struct sway_container *child = con->children->items[i];
557 children += container_count_descendants_of_type(child, type);
558 }
559 }
560 return children;
561}
562
563void container_damage_whole(struct sway_container *container) { 416void container_damage_whole(struct sway_container *container) {
564 for (int i = 0; i < root_container.children->length; ++i) { 417 for (int i = 0; i < root->outputs->length; ++i) {
565 struct sway_container *cont = root_container.children->items[i]; 418 struct sway_output *output = root->outputs->items[i];
566 if (cont->type == C_OUTPUT) { 419 output_damage_whole_container(output, container);
567 output_damage_whole_container(cont->sway_output, container);
568 }
569 } 420 }
570} 421}
571 422
@@ -582,10 +433,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con) {
582 433
583static void update_title_texture(struct sway_container *con, 434static void update_title_texture(struct sway_container *con,
584 struct wlr_texture **texture, struct border_colors *class) { 435 struct wlr_texture **texture, struct border_colors *class) {
585 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
586 "Unexpected type %s", container_type_to_str(con->type))) {
587 return;
588 }
589 struct sway_output *output = container_get_effective_output(con); 436 struct sway_output *output = container_get_effective_output(con);
590 if (!output) { 437 if (!output) {
591 return; 438 return;
@@ -664,9 +511,10 @@ void container_calculate_title_height(struct sway_container *container) {
664 * An example tree representation is: V[Terminal, Firefox] 511 * An example tree representation is: V[Terminal, Firefox]
665 * If buffer is not NULL, also populate the buffer with the representation. 512 * If buffer is not NULL, also populate the buffer with the representation.
666 */ 513 */
667static size_t get_tree_representation(struct sway_container *parent, char *buffer) { 514size_t container_build_representation(enum sway_container_layout layout,
515 list_t *children, char *buffer) {
668 size_t len = 2; 516 size_t len = 2;
669 switch (parent->layout) { 517 switch (layout) {
670 case L_VERT: 518 case L_VERT:
671 lenient_strcat(buffer, "V["); 519 lenient_strcat(buffer, "V[");
672 break; 520 break;
@@ -683,17 +531,17 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe
683 lenient_strcat(buffer, "D["); 531 lenient_strcat(buffer, "D[");
684 break; 532 break;
685 } 533 }
686 for (int i = 0; i < parent->children->length; ++i) { 534 for (int i = 0; i < children->length; ++i) {
687 if (i != 0) { 535 if (i != 0) {
688 ++len; 536 ++len;
689 lenient_strcat(buffer, " "); 537 lenient_strcat(buffer, " ");
690 } 538 }
691 struct sway_container *child = parent->children->items[i]; 539 struct sway_container *child = children->items[i];
692 const char *identifier = NULL; 540 const char *identifier = NULL;
693 if (child->type == C_VIEW) { 541 if (child->view) {
694 identifier = view_get_class(child->sway_view); 542 identifier = view_get_class(child->view);
695 if (!identifier) { 543 if (!identifier) {
696 identifier = view_get_app_id(child->sway_view); 544 identifier = view_get_app_id(child->view);
697 } 545 }
698 } else { 546 } else {
699 identifier = child->formatted_title; 547 identifier = child->formatted_title;
@@ -711,25 +559,25 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe
711 return len; 559 return len;
712} 560}
713 561
714void container_notify_subtree_changed(struct sway_container *container) { 562void container_update_representation(struct sway_container *con) {
715 if (!container || container->type < C_WORKSPACE) { 563 if (!con->view) {
716 return; 564 size_t len = container_build_representation(con->layout,
717 } 565 con->children, NULL);
718 free(container->formatted_title); 566 free(con->formatted_title);
719 container->formatted_title = NULL; 567 con->formatted_title = calloc(len + 1, sizeof(char));
720 568 if (!sway_assert(con->formatted_title,
721 size_t len = get_tree_representation(container, NULL); 569 "Unable to allocate title string")) {
722 char *buffer = calloc(len + 1, sizeof(char)); 570 return;
723 if (!sway_assert(buffer, "Unable to allocate title string")) { 571 }
724 return; 572 container_build_representation(con->layout, con->children,
573 con->formatted_title);
574 container_calculate_title_height(con);
575 container_update_title_textures(con);
725 } 576 }
726 get_tree_representation(container, buffer); 577 if (con->parent) {
727 578 container_update_representation(con->parent);
728 container->formatted_title = buffer; 579 } else if (con->workspace) {
729 if (container->type != C_WORKSPACE) { 580 workspace_update_representation(con->workspace);
730 container_calculate_title_height(container);
731 container_update_title_textures(container);
732 container_notify_subtree_changed(container->parent);
733 } 581 }
734} 582}
735 583
@@ -738,11 +586,7 @@ size_t container_titlebar_height() {
738} 586}
739 587
740void container_init_floating(struct sway_container *con) { 588void container_init_floating(struct sway_container *con) {
741 if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, 589 struct sway_workspace *ws = con->workspace;
742 "Expected a view or container")) {
743 return;
744 }
745 struct sway_container *ws = container_parent(con, C_WORKSPACE);
746 int min_width, min_height; 590 int min_width, min_height;
747 int max_width, max_height; 591 int max_width, max_height;
748 592
@@ -778,13 +622,13 @@ void container_init_floating(struct sway_container *con) {
778 max_height = config->floating_maximum_height; 622 max_height = config->floating_maximum_height;
779 } 623 }
780 624
781 if (con->type == C_CONTAINER) { 625 if (!con->view) {
782 con->width = max_width; 626 con->width = max_width;
783 con->height = max_height; 627 con->height = max_height;
784 con->x = ws->x + (ws->width - con->width) / 2; 628 con->x = ws->x + (ws->width - con->width) / 2;
785 con->y = ws->y + (ws->height - con->height) / 2; 629 con->y = ws->y + (ws->height - con->height) / 2;
786 } else { 630 } else {
787 struct sway_view *view = con->sway_view; 631 struct sway_view *view = con->view;
788 view->width = fmax(min_width, fmin(view->natural_width, max_width)); 632 view->width = fmax(min_width, fmin(view->natural_width, max_width));
789 view->height = fmax(min_height, fmin(view->natural_height, max_height)); 633 view->height = fmax(min_height, fmin(view->natural_height, max_height));
790 view->x = ws->x + (ws->width - view->width) / 2; 634 view->x = ws->x + (ws->width - view->width) / 2;
@@ -794,7 +638,7 @@ void container_init_floating(struct sway_container *con) {
794 view->border_top = view->border_bottom = true; 638 view->border_top = view->border_bottom = true;
795 view->border_left = view->border_right = true; 639 view->border_left = view->border_right = true;
796 640
797 container_set_geometry_from_floating_view(view->swayc); 641 container_set_geometry_from_floating_view(con);
798 } 642 }
799} 643}
800 644
@@ -804,32 +648,41 @@ void container_set_floating(struct sway_container *container, bool enable) {
804 } 648 }
805 649
806 struct sway_seat *seat = input_manager_current_seat(input_manager); 650 struct sway_seat *seat = input_manager_current_seat(input_manager);
807 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 651 struct sway_workspace *workspace = container->workspace;
808 652
809 if (enable) { 653 if (enable) {
810 struct sway_container *old_parent = container_remove_child(container); 654 struct sway_container *old_parent = container->parent;
655 container_detach(container);
811 workspace_add_floating(workspace, container); 656 workspace_add_floating(workspace, container);
812 container_init_floating(container); 657 container_init_floating(container);
813 if (container->type == C_VIEW) { 658 if (container->view) {
814 view_set_tiled(container->sway_view, false); 659 view_set_tiled(container->view, false);
660 }
661 if (old_parent) {
662 container_reap_empty(old_parent);
815 } 663 }
816 container_reap_empty(old_parent);
817 } else { 664 } else {
818 // Returning to tiled 665 // Returning to tiled
819 if (container->scratchpad) { 666 if (container->scratchpad) {
820 root_scratchpad_remove_container(container); 667 root_scratchpad_remove_container(container);
821 } 668 }
822 container_remove_child(container); 669 container_detach(container);
823 struct sway_container *reference = 670 struct sway_container *reference =
824 seat_get_focus_inactive_tiling(seat, workspace); 671 seat_get_focus_inactive_tiling(seat, workspace);
825 if (reference->type == C_VIEW) { 672 if (reference && reference->view) {
826 reference = reference->parent; 673 reference = reference->parent;
827 } 674 }
828 container_add_child(reference, container); 675 if (reference) {
829 container->width = container->parent->width; 676 container_add_child(reference, container);
830 container->height = container->parent->height; 677 container->width = reference->width;
831 if (container->type == C_VIEW) { 678 container->height = reference->height;
832 view_set_tiled(container->sway_view, true); 679 } else {
680 workspace_add_tiling(workspace, container);
681 container->width = workspace->width;
682 container->height = workspace->height;
683 }
684 if (container->view) {
685 view_set_tiled(container->view, true);
833 } 686 }
834 container->is_sticky = false; 687 container->is_sticky = false;
835 } 688 }
@@ -840,14 +693,13 @@ void container_set_floating(struct sway_container *container, bool enable) {
840} 693}
841 694
842void container_set_geometry_from_floating_view(struct sway_container *con) { 695void container_set_geometry_from_floating_view(struct sway_container *con) {
843 if (!sway_assert(con->type == C_VIEW, "Expected a view")) { 696 if (!sway_assert(con->view, "Expected a view")) {
844 return; 697 return;
845 } 698 }
846 if (!sway_assert(container_is_floating(con), 699 if (!sway_assert(container_is_floating(con), "Expected a floating view")) {
847 "Expected a floating view")) {
848 return; 700 return;
849 } 701 }
850 struct sway_view *view = con->sway_view; 702 struct sway_view *view = con->view;
851 size_t border_width = 0; 703 size_t border_width = 0;
852 size_t top = 0; 704 size_t top = 0;
853 705
@@ -861,12 +713,12 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
861 con->y = view->y - top; 713 con->y = view->y - top;
862 con->width = view->width + border_width * 2; 714 con->width = view->width + border_width * 2;
863 con->height = top + view->height + border_width; 715 con->height = top + view->height + border_width;
864 container_set_dirty(con); 716 node_set_dirty(&con->node);
865} 717}
866 718
867bool container_is_floating(struct sway_container *container) { 719bool container_is_floating(struct sway_container *container) {
868 return container->parent && container->parent->type == C_WORKSPACE && 720 return !container->parent && container->workspace &&
869 list_find(container->parent->sway_workspace->floating, container) != -1; 721 list_find(container->workspace->floating, container) != -1;
870} 722}
871 723
872void container_get_box(struct sway_container *container, struct wlr_box *box) { 724void container_get_box(struct sway_container *container, struct wlr_box *box) {
@@ -883,16 +735,16 @@ void container_floating_translate(struct sway_container *con,
883 double x_amount, double y_amount) { 735 double x_amount, double y_amount) {
884 con->x += x_amount; 736 con->x += x_amount;
885 con->y += y_amount; 737 con->y += y_amount;
886 if (con->type == C_VIEW) { 738 if (con->view) {
887 con->sway_view->x += x_amount; 739 con->view->x += x_amount;
888 con->sway_view->y += y_amount; 740 con->view->y += y_amount;
889 } else { 741 } else {
890 for (int i = 0; i < con->children->length; ++i) { 742 for (int i = 0; i < con->children->length; ++i) {
891 struct sway_container *child = con->children->items[i]; 743 struct sway_container *child = con->children->items[i];
892 container_floating_translate(child, x_amount, y_amount); 744 container_floating_translate(child, x_amount, y_amount);
893 } 745 }
894 } 746 }
895 container_set_dirty(con); 747 node_set_dirty(&con->node);
896} 748}
897 749
898/** 750/**
@@ -902,17 +754,16 @@ void container_floating_translate(struct sway_container *con,
902 * one, otherwise we'll choose whichever output is closest to the container's 754 * one, otherwise we'll choose whichever output is closest to the container's
903 * center. 755 * center.
904 */ 756 */
905struct sway_container *container_floating_find_output( 757struct sway_output *container_floating_find_output(struct sway_container *con) {
906 struct sway_container *con) {
907 double center_x = con->x + con->width / 2; 758 double center_x = con->x + con->width / 2;
908 double center_y = con->y + con->height / 2; 759 double center_y = con->y + con->height / 2;
909 struct sway_container *closest_output = NULL; 760 struct sway_output *closest_output = NULL;
910 double closest_distance = DBL_MAX; 761 double closest_distance = DBL_MAX;
911 for (int i = 0; i < root_container.children->length; ++i) { 762 for (int i = 0; i < root->outputs->length; ++i) {
912 struct sway_container *output = root_container.children->items[i]; 763 struct sway_output *output = root->outputs->items[i];
913 struct wlr_box output_box; 764 struct wlr_box output_box;
914 double closest_x, closest_y; 765 double closest_x, closest_y;
915 container_get_box(output, &output_box); 766 output_get_box(output, &output_box);
916 wlr_box_closest_point(&output_box, center_x, center_y, 767 wlr_box_closest_point(&output_box, center_x, center_y,
917 &closest_x, &closest_y); 768 &closest_x, &closest_y);
918 if (center_x == closest_x && center_y == closest_y) { 769 if (center_x == closest_x && center_y == closest_y) {
@@ -937,18 +788,18 @@ void container_floating_move_to(struct sway_container *con,
937 return; 788 return;
938 } 789 }
939 container_floating_translate(con, lx - con->x, ly - con->y); 790 container_floating_translate(con, lx - con->x, ly - con->y);
940 struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); 791 struct sway_workspace *old_workspace = con->workspace;
941 struct sway_container *new_output = container_floating_find_output(con); 792 struct sway_output *new_output = container_floating_find_output(con);
942 if (!sway_assert(new_output, "Unable to find any output")) { 793 if (!sway_assert(new_output, "Unable to find any output")) {
943 return; 794 return;
944 } 795 }
945 struct sway_container *new_workspace = 796 struct sway_workspace *new_workspace =
946 output_get_active_workspace(new_output->sway_output); 797 output_get_active_workspace(new_output);
947 if (old_workspace != new_workspace) { 798 if (old_workspace != new_workspace) {
948 container_remove_child(con); 799 container_detach(con);
949 workspace_add_floating(new_workspace, con); 800 workspace_add_floating(new_workspace, con);
950 arrange_windows(old_workspace); 801 arrange_workspace(old_workspace);
951 arrange_windows(new_workspace); 802 arrange_workspace(new_workspace);
952 workspace_detect_urgent(old_workspace); 803 workspace_detect_urgent(old_workspace);
953 workspace_detect_urgent(new_workspace); 804 workspace_detect_urgent(new_workspace);
954 } 805 }
@@ -959,22 +810,14 @@ void container_floating_move_to_center(struct sway_container *con) {
959 "Expected a floating container")) { 810 "Expected a floating container")) {
960 return; 811 return;
961 } 812 }
962 struct sway_container *ws = container_parent(con, C_WORKSPACE); 813 struct sway_workspace *ws = con->workspace;
963 double new_lx = ws->x + (ws->width - con->width) / 2; 814 double new_lx = ws->x + (ws->width - con->width) / 2;
964 double new_ly = ws->y + (ws->height - con->height) / 2; 815 double new_ly = ws->y + (ws->height - con->height) / 2;
965 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 816 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
966} 817}
967 818
968void container_set_dirty(struct sway_container *container) {
969 if (container->dirty) {
970 return;
971 }
972 container->dirty = true;
973 list_add(server.dirty_containers, container);
974}
975
976static bool find_urgent_iterator(struct sway_container *con, void *data) { 819static bool find_urgent_iterator(struct sway_container *con, void *data) {
977 return con->type == C_VIEW && view_is_urgent(con->sway_view); 820 return con->view && view_is_urgent(con->view);
978} 821}
979 822
980bool container_has_urgent_child(struct sway_container *container) { 823bool container_has_urgent_child(struct sway_container *container) {
@@ -991,12 +834,12 @@ void container_end_mouse_operation(struct sway_container *container) {
991} 834}
992 835
993static void set_fullscreen_iterator(struct sway_container *con, void *data) { 836static void set_fullscreen_iterator(struct sway_container *con, void *data) {
994 if (con->type != C_VIEW) { 837 if (!con->view) {
995 return; 838 return;
996 } 839 }
997 if (con->sway_view->impl->set_fullscreen) { 840 if (con->view->impl->set_fullscreen) {
998 bool *enable = data; 841 bool *enable = data;
999 con->sway_view->impl->set_fullscreen(con->sway_view, *enable); 842 con->view->impl->set_fullscreen(con->view, *enable);
1000 } 843 }
1001} 844}
1002 845
@@ -1005,9 +848,9 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1005 return; 848 return;
1006 } 849 }
1007 850
1008 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 851 struct sway_workspace *workspace = container->workspace;
1009 if (enable && workspace->sway_workspace->fullscreen) { 852 if (enable && workspace->fullscreen) {
1010 container_set_fullscreen(workspace->sway_workspace->fullscreen, false); 853 container_set_fullscreen(workspace->fullscreen, false);
1011 } 854 }
1012 855
1013 set_fullscreen_iterator(container, &enable); 856 set_fullscreen_iterator(container, &enable);
@@ -1016,36 +859,32 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1016 container->is_fullscreen = enable; 859 container->is_fullscreen = enable;
1017 860
1018 if (enable) { 861 if (enable) {
1019 workspace->sway_workspace->fullscreen = container; 862 workspace->fullscreen = container;
1020 container->saved_x = container->x; 863 container->saved_x = container->x;
1021 container->saved_y = container->y; 864 container->saved_y = container->y;
1022 container->saved_width = container->width; 865 container->saved_width = container->width;
1023 container->saved_height = container->height; 866 container->saved_height = container->height;
1024 867
1025 struct sway_seat *seat; 868 struct sway_seat *seat;
1026 struct sway_container *focus, *focus_ws; 869 struct sway_workspace *focus_ws;
1027 wl_list_for_each(seat, &input_manager->seats, link) { 870 wl_list_for_each(seat, &input_manager->seats, link) {
1028 focus = seat_get_focus(seat); 871 focus_ws = seat_get_focused_workspace(seat);
1029 if (focus) { 872 if (focus_ws) {
1030 focus_ws = focus;
1031 if (focus_ws->type != C_WORKSPACE) {
1032 focus_ws = container_parent(focus_ws, C_WORKSPACE);
1033 }
1034 if (focus_ws == workspace) { 873 if (focus_ws == workspace) {
1035 seat_set_focus(seat, container); 874 seat_set_focus(seat, &container->node);
1036 } 875 }
1037 } 876 }
1038 } 877 }
1039 } else { 878 } else {
1040 workspace->sway_workspace->fullscreen = NULL; 879 workspace->fullscreen = NULL;
1041 if (container_is_floating(container)) { 880 if (container_is_floating(container)) {
1042 container->x = container->saved_x; 881 container->x = container->saved_x;
1043 container->y = container->saved_y; 882 container->y = container->saved_y;
1044 container->width = container->saved_width; 883 container->width = container->saved_width;
1045 container->height = container->saved_height; 884 container->height = container->saved_height;
1046 struct sway_container *output = 885 struct sway_output *output =
1047 container_floating_find_output(container); 886 container_floating_find_output(container);
1048 if (!container_has_ancestor(container, output)) { 887 if (workspace->output != output) {
1049 container_floating_move_to_center(container); 888 container_floating_move_to_center(container);
1050 } 889 }
1051 } else { 890 } else {
@@ -1060,7 +899,7 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1060} 899}
1061 900
1062bool container_is_floating_or_child(struct sway_container *container) { 901bool container_is_floating_or_child(struct sway_container *container) {
1063 while (container->parent && container->parent->type != C_WORKSPACE) { 902 while (container->parent) {
1064 container = container->parent; 903 container = container->parent;
1065 } 904 }
1066 return container_is_floating(container); 905 return container_is_floating(container);
@@ -1072,7 +911,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1072 return true; 911 return true;
1073 } 912 }
1074 container = container->parent; 913 container = container->parent;
1075 } while (container && container->type != C_WORKSPACE); 914 } while (container);
1076 915
1077 return false; 916 return false;
1078} 917}
@@ -1090,42 +929,37 @@ static void surface_send_leave_iterator(struct wlr_surface *surface,
1090} 929}
1091 930
1092void container_discover_outputs(struct sway_container *con) { 931void container_discover_outputs(struct sway_container *con) {
1093 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
1094 "Expected a container or view")) {
1095 return;
1096 }
1097 struct wlr_box con_box = { 932 struct wlr_box con_box = {
1098 .x = con->current.swayc_x, 933 .x = con->current.con_x,
1099 .y = con->current.swayc_y, 934 .y = con->current.con_y,
1100 .width = con->current.swayc_width, 935 .width = con->current.con_width,
1101 .height = con->current.swayc_height, 936 .height = con->current.con_height,
1102 }; 937 };
1103 struct sway_output *old_output = container_get_effective_output(con); 938 struct sway_output *old_output = container_get_effective_output(con);
1104 939
1105 for (int i = 0; i < root_container.children->length; ++i) { 940 for (int i = 0; i < root->outputs->length; ++i) {
1106 struct sway_container *output = root_container.children->items[i]; 941 struct sway_output *output = root->outputs->items[i];
1107 struct sway_output *sway_output = output->sway_output;
1108 struct wlr_box output_box; 942 struct wlr_box output_box;
1109 container_get_box(output, &output_box); 943 output_get_box(output, &output_box);
1110 struct wlr_box intersection; 944 struct wlr_box intersection;
1111 bool intersects = 945 bool intersects =
1112 wlr_box_intersection(&con_box, &output_box, &intersection); 946 wlr_box_intersection(&con_box, &output_box, &intersection);
1113 int index = list_find(con->outputs, sway_output); 947 int index = list_find(con->outputs, output);
1114 948
1115 if (intersects && index == -1) { 949 if (intersects && index == -1) {
1116 // Send enter 950 // Send enter
1117 wlr_log(WLR_DEBUG, "Con %p entered output %p", con, sway_output); 951 wlr_log(WLR_DEBUG, "Container %p entered output %p", con, output);
1118 if (con->type == C_VIEW) { 952 if (con->view) {
1119 view_for_each_surface(con->sway_view, 953 view_for_each_surface(con->view,
1120 surface_send_enter_iterator, sway_output->wlr_output); 954 surface_send_enter_iterator, output->wlr_output);
1121 } 955 }
1122 list_add(con->outputs, sway_output); 956 list_add(con->outputs, output);
1123 } else if (!intersects && index != -1) { 957 } else if (!intersects && index != -1) {
1124 // Send leave 958 // Send leave
1125 wlr_log(WLR_DEBUG, "Con %p left output %p", con, sway_output); 959 wlr_log(WLR_DEBUG, "Container %p left output %p", con, output);
1126 if (con->type == C_VIEW) { 960 if (con->view) {
1127 view_for_each_surface(con->sway_view, 961 view_for_each_surface(con->view,
1128 surface_send_leave_iterator, sway_output->wlr_output); 962 surface_send_leave_iterator, output->wlr_output);
1129 } 963 }
1130 list_del(con->outputs, index); 964 list_del(con->outputs, index);
1131 } 965 }
@@ -1135,17 +969,13 @@ void container_discover_outputs(struct sway_container *con) {
1135 double new_scale = new_output ? new_output->wlr_output->scale : -1; 969 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1136 if (old_scale != new_scale) { 970 if (old_scale != new_scale) {
1137 container_update_title_textures(con); 971 container_update_title_textures(con);
1138 if (con->type == C_VIEW) { 972 if (con->view) {
1139 view_update_marks_textures(con->sway_view); 973 view_update_marks_textures(con->view);
1140 } 974 }
1141 } 975 }
1142} 976}
1143 977
1144void container_remove_gaps(struct sway_container *c) { 978void container_remove_gaps(struct sway_container *c) {
1145 if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW,
1146 "Expected a container or view")) {
1147 return;
1148 }
1149 if (c->current_gaps == 0) { 979 if (c->current_gaps == 0) {
1150 return; 980 return;
1151 } 981 }
@@ -1158,25 +988,20 @@ void container_remove_gaps(struct sway_container *c) {
1158} 988}
1159 989
1160void container_add_gaps(struct sway_container *c) { 990void container_add_gaps(struct sway_container *c) {
1161 if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW,
1162 "Expected a container or view")) {
1163 return;
1164 }
1165 if (c->current_gaps > 0) { 991 if (c->current_gaps > 0) {
1166 return; 992 return;
1167 } 993 }
1168 // Linear containers don't have gaps because it'd create double gaps 994 // Linear containers don't have gaps because it'd create double gaps
1169 if (c->type == C_CONTAINER && 995 if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) {
1170 c->layout != L_TABBED && c->layout != L_STACKED) {
1171 return; 996 return;
1172 } 997 }
1173 // Children of tabbed/stacked containers re-use the gaps of the container 998 // Children of tabbed/stacked containers re-use the gaps of the container
1174 enum sway_container_layout layout = c->parent->layout; 999 enum sway_container_layout layout = container_parent_layout(c);
1175 if (layout == L_TABBED || layout == L_STACKED) { 1000 if (layout == L_TABBED || layout == L_STACKED) {
1176 return; 1001 return;
1177 } 1002 }
1178 1003
1179 struct sway_container *ws = container_parent(c, C_WORKSPACE); 1004 struct sway_workspace *ws = c->workspace;
1180 1005
1181 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 1006 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner;
1182 c->x += c->current_gaps; 1007 c->x += c->current_gaps;
@@ -1185,222 +1010,154 @@ void container_add_gaps(struct sway_container *c) {
1185 c->height -= 2 * c->current_gaps; 1010 c->height -= 2 * c->current_gaps;
1186} 1011}
1187 1012
1188int container_sibling_index(const struct sway_container *child) { 1013enum sway_container_layout container_parent_layout(struct sway_container *con) {
1189 return list_find(child->parent->children, child); 1014 if (con->parent) {
1015 return con->parent->layout;
1016 }
1017 return con->workspace->layout;
1190} 1018}
1191 1019
1192void container_handle_fullscreen_reparent(struct sway_container *con, 1020enum sway_container_layout container_current_parent_layout(
1193 struct sway_container *old_parent) { 1021 struct sway_container *con) {
1194 if (!con->is_fullscreen) { 1022 if (con->current.parent) {
1195 return; 1023 return con->current.parent->current.layout;
1196 } 1024 }
1197 struct sway_container *old_workspace = old_parent; 1025 return con->current.workspace->current.layout;
1198 if (old_workspace && old_workspace->type != C_WORKSPACE) { 1026}
1199 old_workspace = container_parent(old_workspace, C_WORKSPACE); 1027
1028list_t *container_get_siblings(const struct sway_container *container) {
1029 if (container->parent) {
1030 return container->parent->children;
1200 } 1031 }
1201 struct sway_container *new_workspace = container_parent(con, C_WORKSPACE); 1032 if (!container->workspace) {
1202 if (old_workspace == new_workspace) { 1033 return NULL;
1203 return;
1204 } 1034 }
1205 // Unmark the old workspace as fullscreen 1035 if (list_find(container->workspace->tiling, container) != -1) {
1206 if (old_workspace) { 1036 return container->workspace->tiling;
1207 old_workspace->sway_workspace->fullscreen = NULL;
1208 } 1037 }
1038 return container->workspace->floating;
1039}
1209 1040
1210 // Mark the new workspace as fullscreen 1041int container_sibling_index(const struct sway_container *child) {
1211 if (new_workspace->sway_workspace->fullscreen) { 1042 return list_find(container_get_siblings(child), child);
1212 container_set_fullscreen( 1043}
1213 new_workspace->sway_workspace->fullscreen, false);
1214 }
1215 new_workspace->sway_workspace->fullscreen = con;
1216 1044
1217 // Resize container to new output dimensions 1045list_t *container_get_current_siblings(struct sway_container *container) {
1218 struct sway_container *output = new_workspace->parent; 1046 if (container->current.parent) {
1219 con->x = output->x; 1047 return container->current.parent->current.children;
1220 con->y = output->y; 1048 }
1221 con->width = output->width; 1049 return container->current.workspace->current.tiling;
1222 con->height = output->height; 1050}
1223 1051
1224 if (con->type == C_VIEW) { 1052void container_handle_fullscreen_reparent(struct sway_container *con) {
1225 struct sway_view *view = con->sway_view; 1053 if (!con->is_fullscreen || con->workspace->fullscreen == con) {
1226 view->x = output->x; 1054 return;
1227 view->y = output->y;
1228 view->width = output->width;
1229 view->height = output->height;
1230 } else {
1231 arrange_windows(new_workspace);
1232 } 1055 }
1056 if (con->workspace->fullscreen) {
1057 container_set_fullscreen(con->workspace->fullscreen, false);
1058 }
1059 con->workspace->fullscreen = con;
1060
1061 arrange_workspace(con->workspace);
1062}
1063
1064static void set_workspace(struct sway_container *container, void *data) {
1065 container->workspace = container->parent->workspace;
1233} 1066}
1234 1067
1235void container_insert_child(struct sway_container *parent, 1068void container_insert_child(struct sway_container *parent,
1236 struct sway_container *child, int i) { 1069 struct sway_container *child, int i) {
1237 struct sway_container *old_parent = child->parent; 1070 if (child->workspace) {
1238 if (old_parent) { 1071 container_detach(child);
1239 container_remove_child(child);
1240 } 1072 }
1241 wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i);
1242 list_insert(parent->children, i, child); 1073 list_insert(parent->children, i, child);
1243 child->parent = parent; 1074 child->parent = parent;
1244 container_handle_fullscreen_reparent(child, old_parent); 1075 child->workspace = parent->workspace;
1076 container_for_each_child(child, set_workspace, NULL);
1077 container_handle_fullscreen_reparent(child);
1078 container_update_representation(parent);
1245} 1079}
1246 1080
1247struct sway_container *container_add_sibling(struct sway_container *fixed, 1081void container_add_sibling(struct sway_container *fixed,
1248 struct sway_container *active) { 1082 struct sway_container *active, int offset) {
1249 // TODO handle floating 1083 if (active->workspace) {
1250 struct sway_container *old_parent = NULL; 1084 container_detach(active);
1251 if (active->parent) { 1085 }
1252 old_parent = active->parent; 1086 list_t *siblings = container_get_siblings(fixed);
1253 container_remove_child(active); 1087 int index = list_find(siblings, fixed);
1254 } 1088 list_insert(siblings, index + offset, active);
1255 struct sway_container *parent = fixed->parent; 1089 active->parent = fixed->parent;
1256 int i = container_sibling_index(fixed); 1090 active->workspace = fixed->workspace;
1257 list_insert(parent->children, i + 1, active); 1091 container_for_each_child(active, set_workspace, NULL);
1258 active->parent = parent; 1092 container_handle_fullscreen_reparent(active);
1259 container_handle_fullscreen_reparent(active, old_parent); 1093 container_update_representation(active);
1260 return active->parent;
1261} 1094}
1262 1095
1263void container_add_child(struct sway_container *parent, 1096void container_add_child(struct sway_container *parent,
1264 struct sway_container *child) { 1097 struct sway_container *child) {
1265 wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", 1098 if (child->workspace) {
1266 child, child->type, child->width, child->height, 1099 container_detach(child);
1267 parent, parent->type, parent->width, parent->height); 1100 }
1268 struct sway_container *old_parent = child->parent;
1269 list_add(parent->children, child); 1101 list_add(parent->children, child);
1270 child->parent = parent; 1102 child->parent = parent;
1271 container_handle_fullscreen_reparent(child, old_parent); 1103 child->workspace = parent->workspace;
1272 if (old_parent) { 1104 container_for_each_child(child, set_workspace, NULL);
1273 container_set_dirty(old_parent); 1105 container_handle_fullscreen_reparent(child);
1274 } 1106 container_update_representation(parent);
1275 container_set_dirty(child); 1107 node_set_dirty(&child->node);
1108 node_set_dirty(&parent->node);
1276} 1109}
1277 1110
1278struct sway_container *container_remove_child(struct sway_container *child) { 1111void container_detach(struct sway_container *child) {
1279 if (child->is_fullscreen) { 1112 if (child->is_fullscreen) {
1280 struct sway_container *workspace = container_parent(child, C_WORKSPACE); 1113 child->workspace->fullscreen = NULL;
1281 workspace->sway_workspace->fullscreen = NULL;
1282 } 1114 }
1283 1115
1284 struct sway_container *parent = child->parent; 1116 struct sway_container *old_parent = child->parent;
1285 list_t *list = container_is_floating(child) ? 1117 struct sway_workspace *old_workspace = child->workspace;
1286 parent->sway_workspace->floating : parent->children; 1118 list_t *siblings = container_get_siblings(child);
1287 int index = list_find(list, child); 1119 int index = list_find(siblings, child);
1288 if (index != -1) { 1120 if (index != -1) {
1289 list_del(list, index); 1121 list_del(siblings, index);
1290 } 1122 }
1291 child->parent = NULL; 1123 child->parent = NULL;
1292 container_notify_subtree_changed(parent); 1124 child->workspace = NULL;
1293 1125 container_for_each_child(child, set_workspace, NULL);
1294 container_set_dirty(parent);
1295 container_set_dirty(child);
1296
1297 return parent;
1298}
1299
1300enum sway_container_layout container_get_default_layout(
1301 struct sway_container *con) {
1302 if (con->type != C_OUTPUT) {
1303 con = container_parent(con, C_OUTPUT);
1304 }
1305 1126
1306 if (!sway_assert(con != NULL, 1127 if (old_parent) {
1307 "container_get_default_layout must be called on an attached" 1128 container_update_representation(old_parent);
1308 " container below the root container")) { 1129 node_set_dirty(&old_parent->node);
1309 return 0;
1310 }
1311
1312 if (config->default_layout != L_NONE) {
1313 return config->default_layout;
1314 } else if (config->default_orientation != L_NONE) {
1315 return config->default_orientation;
1316 } else if (con->width >= con->height) {
1317 return L_HORIZ;
1318 } else { 1130 } else {
1319 return L_VERT; 1131 workspace_update_representation(old_workspace);
1132 node_set_dirty(&old_workspace->node);
1320 } 1133 }
1134 node_set_dirty(&child->node);
1321} 1135}
1322 1136
1323struct sway_container *container_replace_child(struct sway_container *child, 1137void container_replace(struct sway_container *container,
1324 struct sway_container *new_child) { 1138 struct sway_container *replacement) {
1325 struct sway_container *parent = child->parent; 1139 container_add_sibling(container, replacement, 1);
1326 if (parent == NULL) { 1140 container_detach(container);
1327 return NULL;
1328 }
1329
1330 list_t *list = container_is_floating(child) ?
1331 parent->sway_workspace->floating : parent->children;
1332 int i = list_find(list, child);
1333
1334 if (new_child->parent) {
1335 container_remove_child(new_child);
1336 }
1337 list->items[i] = new_child;
1338 new_child->parent = parent;
1339 child->parent = NULL;
1340
1341 // Set geometry for new child
1342 new_child->x = child->x;
1343 new_child->y = child->y;
1344 new_child->width = child->width;
1345 new_child->height = child->height;
1346
1347 // reset geometry for child
1348 child->width = 0;
1349 child->height = 0;
1350
1351 return parent;
1352} 1141}
1353 1142
1354struct sway_container *container_split(struct sway_container *child, 1143struct sway_container *container_split(struct sway_container *child,
1355 enum sway_container_layout layout) { 1144 enum sway_container_layout layout) {
1356 // TODO floating: cannot split a floating container 1145 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
1357 if (!sway_assert(child, "child cannot be null")) { 1146 bool set_focus = (seat_get_focus(seat) == &child->node);
1358 return NULL;
1359 }
1360 if (child->type == C_WORKSPACE && child->children->length == 0) {
1361 // Special case: this just behaves like splitt
1362 child->prev_split_layout = child->layout;
1363 child->layout = layout;
1364 return child;
1365 }
1366
1367 struct sway_container *cont = container_create(C_CONTAINER);
1368
1369 wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child);
1370 1147
1371 cont->prev_split_layout = L_NONE; 1148 struct sway_container *cont = container_create(NULL);
1372 cont->width = child->width; 1149 cont->width = child->width;
1373 cont->height = child->height; 1150 cont->height = child->height;
1374 cont->x = child->x;
1375 cont->y = child->y;
1376 cont->current_gaps = child->current_gaps; 1151 cont->current_gaps = child->current_gaps;
1152 cont->layout = layout;
1377 1153
1378 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 1154 container_replace(child, cont);
1379 bool set_focus = (seat_get_focus(seat) == child); 1155 container_add_child(cont, child);
1380
1381 if (child->type == C_WORKSPACE) {
1382 struct sway_container *workspace = child;
1383 while (workspace->children->length) {
1384 struct sway_container *ws_child = workspace->children->items[0];
1385 container_remove_child(ws_child);
1386 container_add_child(cont, ws_child);
1387 }
1388
1389 container_add_child(workspace, cont);
1390 enum sway_container_layout old_layout = workspace->layout;
1391 workspace->layout = layout;
1392 cont->layout = old_layout;
1393 } else {
1394 cont->layout = layout;
1395 container_replace_child(child, cont);
1396 container_add_child(cont, child);
1397 }
1398 1156
1399 if (set_focus) { 1157 if (set_focus) {
1400 seat_set_focus(seat, cont); 1158 seat_set_focus(seat, &cont->node);
1401 seat_set_focus(seat, child); 1159 seat_set_focus(seat, &child->node);
1402 } 1160 }
1403 1161
1404 container_notify_subtree_changed(cont);
1405 return cont; 1162 return cont;
1406} 1163}
diff --git a/sway/tree/node.c b/sway/tree/node.c
new file mode 100644
index 00000000..74661c1a
--- /dev/null
+++ b/sway/tree/node.c
@@ -0,0 +1,151 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/output.h"
3#include "sway/server.h"
4#include "sway/tree/container.h"
5#include "sway/tree/node.h"
6#include "sway/tree/root.h"
7#include "sway/tree/workspace.h"
8#include "log.h"
9
10void node_init(struct sway_node *node, enum sway_node_type type, void *thing) {
11 static size_t next_id = 1;
12 node->id = next_id++;
13 node->type = type;
14 node->sway_root = thing;
15 wl_signal_init(&node->events.destroy);
16}
17
18const char *node_type_to_str(enum sway_node_type type) {
19 switch (type) {
20 case N_ROOT:
21 return "N_ROOT";
22 case N_OUTPUT:
23 return "N_OUTPUT";
24 case N_WORKSPACE:
25 return "N_WORKSPACE";
26 case N_CONTAINER:
27 return "N_CONTAINER";
28 }
29 return "";
30}
31
32void node_set_dirty(struct sway_node *node) {
33 if (node->dirty) {
34 return;
35 }
36 node->dirty = true;
37 list_add(server.dirty_nodes, node);
38}
39
40bool node_is_view(struct sway_node *node) {
41 return node->type == N_CONTAINER && node->sway_container->view;
42}
43
44char *node_get_name(struct sway_node *node) {
45 switch (node->type) {
46 case N_ROOT:
47 return "root";
48 case N_OUTPUT:
49 return node->sway_output->wlr_output->name;
50 case N_WORKSPACE:
51 return node->sway_workspace->name;
52 case N_CONTAINER:
53 return node->sway_container->title;
54 }
55 return NULL;
56}
57
58void node_get_box(struct sway_node *node, struct wlr_box *box) {
59 switch (node->type) {
60 case N_ROOT:
61 root_get_box(root, box);
62 break;
63 case N_OUTPUT:
64 output_get_box(node->sway_output, box);
65 break;
66 case N_WORKSPACE:
67 workspace_get_box(node->sway_workspace, box);
68 break;
69 case N_CONTAINER:
70 container_get_box(node->sway_container, box);
71 break;
72 }
73}
74
75struct sway_output *node_get_output(struct sway_node *node) {
76 switch (node->type) {
77 case N_CONTAINER:
78 return node->sway_container->workspace->output;
79 case N_WORKSPACE:
80 return node->sway_workspace->output;
81 case N_OUTPUT:
82 return node->sway_output;
83 case N_ROOT:
84 return NULL;
85 }
86 return NULL;
87}
88
89enum sway_container_layout node_get_layout(struct sway_node *node) {
90 switch (node->type) {
91 case N_CONTAINER:
92 return node->sway_container->layout;
93 case N_WORKSPACE:
94 return node->sway_workspace->layout;
95 case N_OUTPUT:
96 case N_ROOT:
97 return L_NONE;
98 }
99 return L_NONE;
100}
101
102struct sway_node *node_get_parent(struct sway_node *node) {
103 switch (node->type) {
104 case N_CONTAINER: {
105 struct sway_container *con = node->sway_container;
106 if (con->parent) {
107 return &con->parent->node;
108 }
109 if (con->workspace) {
110 return &con->workspace->node;
111 }
112 }
113 return NULL;
114 case N_WORKSPACE: {
115 struct sway_workspace *ws = node->sway_workspace;
116 if (ws->output) {
117 return &ws->output->node;
118 }
119 }
120 return NULL;
121 case N_OUTPUT:
122 return &root->node;
123 case N_ROOT:
124 return NULL;
125 }
126 return NULL;
127}
128
129list_t *node_get_children(struct sway_node *node) {
130 switch (node->type) {
131 case N_CONTAINER:
132 return node->sway_container->children;
133 case N_WORKSPACE:
134 return node->sway_workspace->tiling;
135 case N_OUTPUT:
136 case N_ROOT:
137 return NULL;
138 }
139 return NULL;
140}
141
142bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
143 struct sway_node *parent = node_get_parent(node);
144 while (parent) {
145 if (parent == ancestor) {
146 return true;
147 }
148 parent = node_get_parent(parent);
149 }
150 return false;
151}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 6601220b..d72eb1a1 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -2,28 +2,31 @@
2#include <ctype.h> 2#include <ctype.h>
3#include <string.h> 3#include <string.h>
4#include <strings.h> 4#include <strings.h>
5#include <wlr/types/wlr_output_damage.h>
5#include "sway/ipc-server.h" 6#include "sway/ipc-server.h"
7#include "sway/layers.h"
6#include "sway/output.h" 8#include "sway/output.h"
7#include "sway/tree/arrange.h" 9#include "sway/tree/arrange.h"
8#include "sway/tree/output.h" 10#include "sway/tree/output.h"
9#include "sway/tree/workspace.h" 11#include "sway/tree/workspace.h"
10#include "log.h" 12#include "log.h"
13#include "util.h"
11 14
12static void restore_workspaces(struct sway_container *output) { 15static void restore_workspaces(struct sway_output *output) {
13 // Workspace output priority 16 // Workspace output priority
14 for (int i = 0; i < root_container.children->length; i++) { 17 for (int i = 0; i < root->outputs->length; i++) {
15 struct sway_container *other = root_container.children->items[i]; 18 struct sway_output *other = root->outputs->items[i];
16 if (other == output) { 19 if (other == output) {
17 continue; 20 continue;
18 } 21 }
19 22
20 for (int j = 0; j < other->children->length; j++) { 23 for (int j = 0; j < other->workspaces->length; j++) {
21 struct sway_container *ws = other->children->items[j]; 24 struct sway_workspace *ws = other->workspaces->items[j];
22 struct sway_container *highest = 25 struct sway_output *highest =
23 workspace_output_get_highest_available(ws, NULL); 26 workspace_output_get_highest_available(ws, NULL);
24 if (highest == output) { 27 if (highest == output) {
25 container_remove_child(ws); 28 workspace_detach(ws);
26 container_add_child(output, ws); 29 output_add_workspace(output, ws);
27 ipc_event_workspace(NULL, ws, "move"); 30 ipc_event_workspace(NULL, ws, "move");
28 j--; 31 j--;
29 } 32 }
@@ -31,111 +34,111 @@ static void restore_workspaces(struct sway_container *output) {
31 } 34 }
32 35
33 // Saved workspaces 36 // Saved workspaces
34 list_t *saved = root_container.sway_root->saved_workspaces; 37 for (int i = 0; i < root->saved_workspaces->length; ++i) {
35 for (int i = 0; i < saved->length; ++i) { 38 struct sway_workspace *ws = root->saved_workspaces->items[i];
36 struct sway_container *ws = saved->items[i]; 39 output_add_workspace(output, ws);
37 container_add_child(output, ws);
38 ipc_event_workspace(NULL, ws, "move"); 40 ipc_event_workspace(NULL, ws, "move");
39 } 41 }
40 saved->length = 0; 42 root->saved_workspaces->length = 0;
41 43
42 output_sort_workspaces(output); 44 output_sort_workspaces(output);
43} 45}
44 46
45struct sway_container *output_create( 47struct sway_output *output_create(struct wlr_output *wlr_output) {
46 struct sway_output *sway_output) { 48 struct sway_output *output = calloc(1, sizeof(struct sway_output));
47 const char *name = sway_output->wlr_output->name; 49 node_init(&output->node, N_OUTPUT, output);
48 char identifier[128]; 50 output->wlr_output = wlr_output;
49 output_get_identifier(identifier, sizeof(identifier), sway_output); 51 wlr_output->data = output;
50 52
51 struct output_config *oc = NULL, *all = NULL; 53 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
52 for (int i = 0; i < config->output_configs->length; ++i) {
53 struct output_config *cur = config->output_configs->items[i];
54 54
55 if (strcasecmp(name, cur->name) == 0 || 55 wl_list_insert(&root->all_outputs, &output->link);
56 strcasecmp(identifier, cur->name) == 0) {
57 wlr_log(WLR_DEBUG, "Matched output config for %s", name);
58 oc = cur;
59 }
60 if (strcasecmp("*", cur->name) == 0) {
61 wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
62 all = cur;
63 }
64 56
65 if (oc && all) { 57 if (!wl_list_empty(&wlr_output->modes)) {
66 break; 58 struct wlr_output_mode *mode =
67 } 59 wl_container_of(wlr_output->modes.prev, mode, link);
68 } 60 wlr_output_set_mode(wlr_output, mode);
69 if (!oc) {
70 oc = all;
71 } 61 }
72 62
73 if (oc && !oc->enabled) { 63 output->workspaces = create_list();
74 return NULL; 64 output->current.workspaces = create_list();
75 }
76 65
77 struct sway_container *output = container_create(C_OUTPUT); 66 return output;
78 output->sway_output = sway_output; 67}
79 output->name = strdup(name);
80 if (output->name == NULL) {
81 output_begin_destroy(output);
82 return NULL;
83 }
84 68
69void output_enable(struct sway_output *output, struct output_config *oc) {
70 if (!sway_assert(!output->enabled, "output is already enabled")) {
71 return;
72 }
73 struct wlr_output *wlr_output = output->wlr_output;
74 output->enabled = true;
85 apply_output_config(oc, output); 75 apply_output_config(oc, output);
86 container_add_child(&root_container, output); 76 list_add(root->outputs, output);
87 load_swaybars();
88
89 struct wlr_box size;
90 wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
91 &size.height);
92 output->width = size.width;
93 output->height = size.height;
94 77
95 restore_workspaces(output); 78 restore_workspaces(output);
96 79
97 if (!output->children->length) { 80 if (!output->workspaces->length) {
98 // Create workspace 81 // Create workspace
99 char *ws_name = workspace_next_name(output->name); 82 char *ws_name = workspace_next_name(wlr_output->name);
100 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); 83 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
101 struct sway_container *ws = workspace_create(output, ws_name); 84 struct sway_workspace *ws = workspace_create(output, ws_name);
102 // Set each seat's focus if not already set 85 // Set each seat's focus if not already set
103 struct sway_seat *seat = NULL; 86 struct sway_seat *seat = NULL;
104 wl_list_for_each(seat, &input_manager->seats, link) { 87 wl_list_for_each(seat, &input_manager->seats, link) {
105 if (!seat->has_focus) { 88 if (!seat->has_focus) {
106 seat_set_focus(seat, ws); 89 seat_set_focus(seat, &ws->node);
107 } 90 }
108 } 91 }
109 free(ws_name); 92 free(ws_name);
110 } 93 }
111 94
112 container_create_notify(output); 95 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
113 return output; 96 for (size_t i = 0; i < len; ++i) {
97 wl_list_init(&output->layers[i]);
98 }
99 wl_signal_init(&output->events.destroy);
100
101 input_manager_configure_xcursor(input_manager);
102
103 wl_signal_add(&wlr_output->events.mode, &output->mode);
104 wl_signal_add(&wlr_output->events.transform, &output->transform);
105 wl_signal_add(&wlr_output->events.scale, &output->scale);
106 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
107 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
108
109 output_add_listeners(output);
110
111 wl_signal_emit(&root->events.new_node, &output->node);
112
113 load_swaybars();
114
115 arrange_layers(output);
116 arrange_root();
114} 117}
115 118
116static void output_evacuate(struct sway_container *output) { 119static void output_evacuate(struct sway_output *output) {
117 if (!output->children->length) { 120 if (!output->workspaces->length) {
118 return; 121 return;
119 } 122 }
120 struct sway_container *fallback_output = NULL; 123 struct sway_output *fallback_output = NULL;
121 if (root_container.children->length > 1) { 124 if (root->outputs->length > 1) {
122 fallback_output = root_container.children->items[0]; 125 fallback_output = root->outputs->items[0];
123 if (fallback_output == output) { 126 if (fallback_output == output) {
124 fallback_output = root_container.children->items[1]; 127 fallback_output = root->outputs->items[1];
125 } 128 }
126 } 129 }
127 130
128 while (output->children->length) { 131 while (output->workspaces->length) {
129 struct sway_container *workspace = output->children->items[0]; 132 struct sway_workspace *workspace = output->workspaces->items[0];
130 133
131 container_remove_child(workspace); 134 workspace_detach(workspace);
132 135
133 if (workspace_is_empty(workspace)) { 136 if (workspace_is_empty(workspace)) {
134 workspace_begin_destroy(workspace); 137 workspace_begin_destroy(workspace);
135 continue; 138 continue;
136 } 139 }
137 140
138 struct sway_container *new_output = 141 struct sway_output *new_output =
139 workspace_output_get_highest_available(workspace, output); 142 workspace_output_get_highest_available(workspace, output);
140 if (!new_output) { 143 if (!new_output) {
141 new_output = fallback_output; 144 new_output = fallback_output;
@@ -143,39 +146,31 @@ static void output_evacuate(struct sway_container *output) {
143 146
144 if (new_output) { 147 if (new_output) {
145 workspace_output_add_priority(workspace, new_output); 148 workspace_output_add_priority(workspace, new_output);
146 container_add_child(new_output, workspace); 149 output_add_workspace(new_output, workspace);
147 output_sort_workspaces(new_output); 150 output_sort_workspaces(new_output);
148 ipc_event_workspace(NULL, workspace, "move"); 151 ipc_event_workspace(NULL, workspace, "move");
149 } else { 152 } else {
150 list_add(root_container.sway_root->saved_workspaces, workspace); 153 list_add(root->saved_workspaces, workspace);
151 } 154 }
152 } 155 }
153} 156}
154 157
155void output_destroy(struct sway_container *output) { 158void output_destroy(struct sway_output *output) {
156 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 159 if (!sway_assert(output->node.destroying,
160 "Tried to free output which wasn't marked as destroying")) {
157 return; 161 return;
158 } 162 }
159 if (!sway_assert(output->destroying, 163 if (!sway_assert(output->wlr_output == NULL,
160 "Tried to free output which wasn't marked as destroying")) { 164 "Tried to free output which still had a wlr_output")) {
161 return; 165 return;
162 } 166 }
163 if (!sway_assert(output->ntxnrefs == 0, "Tried to free output " 167 if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output "
164 "which is still referenced by transactions")) { 168 "which is still referenced by transactions")) {
165 return; 169 return;
166 } 170 }
167 free(output->name); 171 list_free(output->workspaces);
168 free(output->formatted_title); 172 list_free(output->current.workspaces);
169 wlr_texture_destroy(output->title_focused);
170 wlr_texture_destroy(output->title_focused_inactive);
171 wlr_texture_destroy(output->title_unfocused);
172 wlr_texture_destroy(output->title_urgent);
173 list_free(output->children);
174 list_free(output->current.children);
175 list_free(output->outputs);
176 free(output); 173 free(output);
177
178 // NOTE: We don't actually destroy the sway_output here
179} 174}
180 175
181static void untrack_output(struct sway_container *con, void *data) { 176static void untrack_output(struct sway_container *con, void *data) {
@@ -186,76 +181,131 @@ static void untrack_output(struct sway_container *con, void *data) {
186 } 181 }
187} 182}
188 183
189void output_begin_destroy(struct sway_container *output) { 184void output_disable(struct sway_output *output) {
190 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 185 if (!sway_assert(output->enabled, "Expected an enabled output")) {
191 return; 186 return;
192 } 187 }
193 wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 188 wlr_log(WLR_DEBUG, "Disabling output '%s'", output->wlr_output->name);
194 wl_signal_emit(&output->events.destroy, output); 189 wl_signal_emit(&output->events.destroy, output);
195 190
196 output_evacuate(output); 191 output_evacuate(output);
197 192
198 output->destroying = true; 193 root_for_each_container(untrack_output, output);
199 container_set_dirty(output); 194
195 int index = list_find(root->outputs, output);
196 list_del(root->outputs, index);
200 197
201 root_for_each_container(untrack_output, output->sway_output); 198 wl_list_remove(&output->mode.link);
199 wl_list_remove(&output->transform.link);
200 wl_list_remove(&output->scale.link);
201 wl_list_remove(&output->damage_destroy.link);
202 wl_list_remove(&output->damage_frame.link);
202 203
203 wl_list_remove(&output->sway_output->mode.link); 204 output->enabled = false;
204 wl_list_remove(&output->sway_output->transform.link);
205 wl_list_remove(&output->sway_output->scale.link);
206 wl_list_remove(&output->sway_output->damage_destroy.link);
207 wl_list_remove(&output->sway_output->damage_frame.link);
208 205
209 output->sway_output->swayc = NULL; 206 arrange_root();
210 output->sway_output = NULL; 207}
211 208
212 if (output->parent) { 209void output_begin_destroy(struct sway_output *output) {
213 container_remove_child(output); 210 if (!sway_assert(!output->enabled, "Expected a disabled output")) {
211 return;
214 } 212 }
213 wlr_log(WLR_DEBUG, "Destroying output '%s'", output->wlr_output->name);
214
215 output->node.destroying = true;
216 node_set_dirty(&output->node);
217
218 wl_list_remove(&output->link);
219 wl_list_remove(&output->destroy.link);
220 output->wlr_output->data = NULL;
221 output->wlr_output = NULL;
215} 222}
216 223
217struct sway_container *output_from_wlr_output(struct wlr_output *output) { 224struct output_config *output_find_config(struct sway_output *output) {
218 if (output == NULL) { 225 const char *name = output->wlr_output->name;
226 char identifier[128];
227 output_get_identifier(identifier, sizeof(identifier), output);
228
229 struct output_config *oc = NULL, *all = NULL;
230 for (int i = 0; i < config->output_configs->length; ++i) {
231 struct output_config *cur = config->output_configs->items[i];
232
233 if (strcasecmp(name, cur->name) == 0 ||
234 strcasecmp(identifier, cur->name) == 0) {
235 wlr_log(WLR_DEBUG, "Matched output config for %s", name);
236 oc = cur;
237 }
238 if (strcasecmp("*", cur->name) == 0) {
239 wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
240 all = cur;
241 }
242
243 if (oc && all) {
244 break;
245 }
246 }
247 if (!oc) {
248 oc = all;
249 }
250
251 if (oc && !oc->enabled) {
219 return NULL; 252 return NULL;
220 } 253 }
221 for (int i = 0; i < root_container.children->length; ++i) { 254 return oc;
222 struct sway_container *o = root_container.children->items[i]; 255}
223 if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { 256
224 return o; 257struct sway_output *output_from_wlr_output(struct wlr_output *output) {
225 } 258 return output->data;
259}
260
261struct sway_output *output_get_in_direction(struct sway_output *reference,
262 enum movement_direction direction) {
263 enum wlr_direction wlr_dir = 0;
264 if (!sway_assert(sway_dir_to_wlr(direction, &wlr_dir),
265 "got invalid direction: %d", direction)) {
266 return NULL;
226 } 267 }
227 return NULL; 268 int lx = reference->wlr_output->lx + reference->wlr_output->width / 2;
269 int ly = reference->wlr_output->ly + reference->wlr_output->height / 2;
270 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
271 root->output_layout, wlr_dir, reference->wlr_output, lx, ly);
272 if (!wlr_adjacent) {
273 return NULL;
274 }
275 return output_from_wlr_output(wlr_adjacent);
228} 276}
229 277
230void output_for_each_workspace(struct sway_container *output, 278void output_add_workspace(struct sway_output *output,
231 void (*f)(struct sway_container *con, void *data), void *data) { 279 struct sway_workspace *workspace) {
232 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 280 if (workspace->output) {
233 return; 281 workspace_detach(workspace);
234 } 282 }
235 for (int i = 0; i < output->children->length; ++i) { 283 list_add(output->workspaces, workspace);
236 struct sway_container *workspace = output->children->items[i]; 284 workspace->output = output;
285 node_set_dirty(&output->node);
286 node_set_dirty(&workspace->node);
287}
288
289void output_for_each_workspace(struct sway_output *output,
290 void (*f)(struct sway_workspace *ws, void *data), void *data) {
291 for (int i = 0; i < output->workspaces->length; ++i) {
292 struct sway_workspace *workspace = output->workspaces->items[i];
237 f(workspace, data); 293 f(workspace, data);
238 } 294 }
239} 295}
240 296
241void output_for_each_container(struct sway_container *output, 297void output_for_each_container(struct sway_output *output,
242 void (*f)(struct sway_container *con, void *data), void *data) { 298 void (*f)(struct sway_container *con, void *data), void *data) {
243 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 299 for (int i = 0; i < output->workspaces->length; ++i) {
244 return; 300 struct sway_workspace *workspace = output->workspaces->items[i];
245 }
246 for (int i = 0; i < output->children->length; ++i) {
247 struct sway_container *workspace = output->children->items[i];
248 workspace_for_each_container(workspace, f, data); 301 workspace_for_each_container(workspace, f, data);
249 } 302 }
250} 303}
251 304
252struct sway_container *output_find_workspace(struct sway_container *output, 305struct sway_workspace *output_find_workspace(struct sway_output *output,
253 bool (*test)(struct sway_container *con, void *data), void *data) { 306 bool (*test)(struct sway_workspace *ws, void *data), void *data) {
254 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 307 for (int i = 0; i < output->workspaces->length; ++i) {
255 return NULL; 308 struct sway_workspace *workspace = output->workspaces->items[i];
256 }
257 for (int i = 0; i < output->children->length; ++i) {
258 struct sway_container *workspace = output->children->items[i];
259 if (test(workspace, data)) { 309 if (test(workspace, data)) {
260 return workspace; 310 return workspace;
261 } 311 }
@@ -263,14 +313,11 @@ struct sway_container *output_find_workspace(struct sway_container *output,
263 return NULL; 313 return NULL;
264} 314}
265 315
266struct sway_container *output_find_container(struct sway_container *output, 316struct sway_container *output_find_container(struct sway_output *output,
267 bool (*test)(struct sway_container *con, void *data), void *data) { 317 bool (*test)(struct sway_container *con, void *data), void *data) {
268 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
269 return NULL;
270 }
271 struct sway_container *result = NULL; 318 struct sway_container *result = NULL;
272 for (int i = 0; i < output->children->length; ++i) { 319 for (int i = 0; i < output->workspaces->length; ++i) {
273 struct sway_container *workspace = output->children->items[i]; 320 struct sway_workspace *workspace = output->workspaces->items[i];
274 if ((result = workspace_find_container(workspace, test, data))) { 321 if ((result = workspace_find_container(workspace, test, data))) {
275 return result; 322 return result;
276 } 323 }
@@ -279,8 +326,8 @@ struct sway_container *output_find_container(struct sway_container *output,
279} 326}
280 327
281static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { 328static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
282 struct sway_container *a = *(void **)_a; 329 struct sway_workspace *a = *(void **)_a;
283 struct sway_container *b = *(void **)_b; 330 struct sway_workspace *b = *(void **)_b;
284 331
285 if (isdigit(a->name[0]) && isdigit(b->name[0])) { 332 if (isdigit(a->name[0]) && isdigit(b->name[0])) {
286 int a_num = strtol(a->name, NULL, 10); 333 int a_num = strtol(a->name, NULL, 10);
@@ -294,6 +341,27 @@ static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
294 return 0; 341 return 0;
295} 342}
296 343
297void output_sort_workspaces(struct sway_container *output) { 344void output_sort_workspaces(struct sway_output *output) {
298 list_stable_sort(output->children, sort_workspace_cmp_qsort); 345 list_stable_sort(output->workspaces, sort_workspace_cmp_qsort);
346}
347
348void output_get_box(struct sway_output *output, struct wlr_box *box) {
349 box->x = output->wlr_output->lx;
350 box->y = output->wlr_output->ly;
351 box->width = output->wlr_output->width;
352 box->height = output->wlr_output->height;
353}
354
355enum sway_container_layout output_get_default_layout(
356 struct sway_output *output) {
357 if (config->default_layout != L_NONE) {
358 return config->default_layout;
359 }
360 if (config->default_orientation != L_NONE) {
361 return config->default_orientation;
362 }
363 if (output->wlr_output->height > output->wlr_output->width) {
364 return L_VERT;
365 }
366 return L_HORIZ;
299} 367}
diff --git a/sway/tree/root.c b/sway/tree/root.c
index b42371de..ecc04ddb 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -14,54 +14,45 @@
14#include "log.h" 14#include "log.h"
15#include "util.h" 15#include "util.h"
16 16
17struct sway_container root_container; 17struct sway_root *root;
18 18
19static void output_layout_handle_change(struct wl_listener *listener, 19static void output_layout_handle_change(struct wl_listener *listener,
20 void *data) { 20 void *data) {
21 arrange_windows(&root_container); 21 arrange_root();
22 transaction_commit_dirty(); 22 transaction_commit_dirty();
23} 23}
24 24
25void root_create(void) { 25struct sway_root *root_create(void) {
26 root_container.id = 0; // normally assigned in new_swayc() 26 struct sway_root *root = calloc(1, sizeof(struct sway_root));
27 root_container.type = C_ROOT; 27 if (!root) {
28 root_container.layout = L_NONE; 28 wlr_log(WLR_ERROR, "Unable to allocate sway_root");
29 root_container.name = strdup("root"); 29 return NULL;
30 root_container.children = create_list(); 30 }
31 root_container.current.children = create_list(); 31 node_init(&root->node, N_ROOT, root);
32 wl_signal_init(&root_container.events.destroy); 32 root->output_layout = wlr_output_layout_create();
33 33 wl_list_init(&root->all_outputs);
34 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
35 root_container.sway_root->output_layout = wlr_output_layout_create();
36 wl_list_init(&root_container.sway_root->all_outputs);
37#ifdef HAVE_XWAYLAND 34#ifdef HAVE_XWAYLAND
38 wl_list_init(&root_container.sway_root->xwayland_unmanaged); 35 wl_list_init(&root->xwayland_unmanaged);
39#endif 36#endif
40 wl_list_init(&root_container.sway_root->drag_icons); 37 wl_list_init(&root->drag_icons);
41 wl_signal_init(&root_container.sway_root->events.new_container); 38 wl_signal_init(&root->events.new_node);
42 root_container.sway_root->scratchpad = create_list(); 39 root->outputs = create_list();
43 root_container.sway_root->saved_workspaces = create_list(); 40 root->scratchpad = create_list();
44 41 root->saved_workspaces = create_list();
45 root_container.sway_root->output_layout_change.notify = 42
46 output_layout_handle_change; 43 root->output_layout_change.notify = output_layout_handle_change;
47 wl_signal_add(&root_container.sway_root->output_layout->events.change, 44 wl_signal_add(&root->output_layout->events.change,
48 &root_container.sway_root->output_layout_change); 45 &root->output_layout_change);
46 return root;
49} 47}
50 48
51void root_destroy(void) { 49void root_destroy(struct sway_root *root) {
52 // sway_root 50 wl_list_remove(&root->output_layout_change.link);
53 wl_list_remove(&root_container.sway_root->output_layout_change.link); 51 list_free(root->scratchpad);
54 list_free(root_container.sway_root->scratchpad); 52 list_free(root->saved_workspaces);
55 list_free(root_container.sway_root->saved_workspaces); 53 list_free(root->outputs);
56 wlr_output_layout_destroy(root_container.sway_root->output_layout); 54 wlr_output_layout_destroy(root->output_layout);
57 free(root_container.sway_root); 55 free(root);
58
59 // root_container
60 list_free(root_container.children);
61 list_free(root_container.current.children);
62 free(root_container.name);
63
64 memset(&root_container, 0, sizeof(root_container));
65} 56}
66 57
67void root_scratchpad_add_container(struct sway_container *con) { 58void root_scratchpad_add_container(struct sway_container *con) {
@@ -69,15 +60,21 @@ void root_scratchpad_add_container(struct sway_container *con) {
69 return; 60 return;
70 } 61 }
71 con->scratchpad = true; 62 con->scratchpad = true;
72 list_add(root_container.sway_root->scratchpad, con); 63 list_add(root->scratchpad, con);
73 64
74 struct sway_container *parent = con->parent; 65 struct sway_container *parent = con->parent;
66 struct sway_workspace *workspace = con->workspace;
75 container_set_floating(con, true); 67 container_set_floating(con, true);
76 container_remove_child(con); 68 container_detach(con);
77 arrange_windows(parent);
78 69
79 struct sway_seat *seat = input_manager_current_seat(input_manager); 70 struct sway_seat *seat = input_manager_current_seat(input_manager);
80 seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); 71 if (parent) {
72 arrange_container(parent);
73 seat_set_focus(seat, seat_get_focus_inactive(seat, &parent->node));
74 } else {
75 arrange_workspace(workspace);
76 seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node));
77 }
81} 78}
82 79
83void root_scratchpad_remove_container(struct sway_container *con) { 80void root_scratchpad_remove_container(struct sway_container *con) {
@@ -85,28 +82,25 @@ void root_scratchpad_remove_container(struct sway_container *con) {
85 return; 82 return;
86 } 83 }
87 con->scratchpad = false; 84 con->scratchpad = false;
88 int index = list_find(root_container.sway_root->scratchpad, con); 85 int index = list_find(root->scratchpad, con);
89 if (index != -1) { 86 if (index != -1) {
90 list_del(root_container.sway_root->scratchpad, index); 87 list_del(root->scratchpad, index);
91 } 88 }
92} 89}
93 90
94void root_scratchpad_show(struct sway_container *con) { 91void root_scratchpad_show(struct sway_container *con) {
95 struct sway_seat *seat = input_manager_current_seat(input_manager); 92 struct sway_seat *seat = input_manager_current_seat(input_manager);
96 struct sway_container *ws = seat_get_focus(seat); 93 struct sway_workspace *ws = seat_get_focused_workspace(seat);
97 if (ws->type != C_WORKSPACE) {
98 ws = container_parent(ws, C_WORKSPACE);
99 }
100 94
101 // If the current con or any of its parents are in fullscreen mode, we 95 // If the current con or any of its parents are in fullscreen mode, we
102 // first need to disable it before showing the scratchpad con. 96 // first need to disable it before showing the scratchpad con.
103 if (ws->sway_workspace->fullscreen) { 97 if (ws->fullscreen) {
104 container_set_fullscreen(ws->sway_workspace->fullscreen, false); 98 container_set_fullscreen(ws->fullscreen, false);
105 } 99 }
106 100
107 // Show the container 101 // Show the container
108 if (con->parent) { 102 if (con->workspace) {
109 container_remove_child(con); 103 container_detach(con);
110 } 104 }
111 workspace_add_floating(ws, con); 105 workspace_add_floating(ws, con);
112 106
@@ -115,7 +109,7 @@ void root_scratchpad_show(struct sway_container *con) {
115 double center_ly = con->y + con->height / 2; 109 double center_ly = con->y + con->height / 2;
116 110
117 struct wlr_box workspace_box; 111 struct wlr_box workspace_box;
118 container_get_box(ws, &workspace_box); 112 workspace_get_box(ws, &workspace_box);
119 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { 113 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
120 // Maybe resize it 114 // Maybe resize it
121 if (con->width > ws->width || con->height > ws->height) { 115 if (con->width > ws->width || con->height > ws->height) {
@@ -128,23 +122,21 @@ void root_scratchpad_show(struct sway_container *con) {
128 container_floating_move_to(con, new_lx, new_ly); 122 container_floating_move_to(con, new_lx, new_ly);
129 } 123 }
130 124
131 arrange_windows(ws); 125 arrange_workspace(ws);
132 seat_set_focus(seat, seat_get_focus_inactive(seat, con)); 126 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
133
134 container_set_dirty(con->parent);
135} 127}
136 128
137void root_scratchpad_hide(struct sway_container *con) { 129void root_scratchpad_hide(struct sway_container *con) {
138 struct sway_seat *seat = input_manager_current_seat(input_manager); 130 struct sway_seat *seat = input_manager_current_seat(input_manager);
139 struct sway_container *focus = seat_get_focus(seat); 131 struct sway_node *focus = seat_get_focus(seat);
140 struct sway_container *ws = container_parent(con, C_WORKSPACE); 132 struct sway_workspace *ws = con->workspace;
141 133
142 container_remove_child(con); 134 container_detach(con);
143 arrange_windows(ws); 135 arrange_workspace(ws);
144 if (con == focus) { 136 if (&con->node == focus) {
145 seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); 137 seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));
146 } 138 }
147 list_move_to_end(root_container.sway_root->scratchpad, con); 139 list_move_to_end(root->scratchpad, con);
148} 140}
149 141
150struct pid_workspace { 142struct pid_workspace {
@@ -152,7 +144,7 @@ struct pid_workspace {
152 char *workspace; 144 char *workspace;
153 struct timespec time_added; 145 struct timespec time_added;
154 146
155 struct sway_container *output; 147 struct sway_output *output;
156 struct wl_listener output_destroy; 148 struct wl_listener output_destroy;
157 149
158 struct wl_list link; 150 struct wl_list link;
@@ -160,13 +152,13 @@ struct pid_workspace {
160 152
161static struct wl_list pid_workspaces; 153static struct wl_list pid_workspaces;
162 154
163struct sway_container *root_workspace_for_pid(pid_t pid) { 155struct sway_workspace *root_workspace_for_pid(pid_t pid) {
164 if (!pid_workspaces.prev && !pid_workspaces.next) { 156 if (!pid_workspaces.prev && !pid_workspaces.next) {
165 wl_list_init(&pid_workspaces); 157 wl_list_init(&pid_workspaces);
166 return NULL; 158 return NULL;
167 } 159 }
168 160
169 struct sway_container *ws = NULL; 161 struct sway_workspace *ws = NULL;
170 struct pid_workspace *pw = NULL; 162 struct pid_workspace *pw = NULL;
171 163
172 wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); 164 wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid);
@@ -219,16 +211,12 @@ void root_record_workspace_pid(pid_t pid) {
219 } 211 }
220 212
221 struct sway_seat *seat = input_manager_current_seat(input_manager); 213 struct sway_seat *seat = input_manager_current_seat(input_manager);
222 struct sway_container *ws = 214 struct sway_workspace *ws = seat_get_focused_workspace(seat);
223 seat_get_focus_inactive(seat, &root_container);
224 if (ws && ws->type != C_WORKSPACE) {
225 ws = container_parent(ws, C_WORKSPACE);
226 }
227 if (!ws) { 215 if (!ws) {
228 wlr_log(WLR_DEBUG, "Bailing out, no workspace"); 216 wlr_log(WLR_DEBUG, "Bailing out, no workspace");
229 return; 217 return;
230 } 218 }
231 struct sway_container *output = ws->parent; 219 struct sway_output *output = ws->output;
232 if (!output) { 220 if (!output) {
233 wlr_log(WLR_DEBUG, "Bailing out, no output"); 221 wlr_log(WLR_DEBUG, "Bailing out, no output");
234 return; 222 return;
@@ -255,30 +243,28 @@ void root_record_workspace_pid(pid_t pid) {
255 pw->pid = pid; 243 pw->pid = pid;
256 memcpy(&pw->time_added, &now, sizeof(struct timespec)); 244 memcpy(&pw->time_added, &now, sizeof(struct timespec));
257 pw->output_destroy.notify = pw_handle_output_destroy; 245 pw->output_destroy.notify = pw_handle_output_destroy;
258 wl_signal_add(&output->sway_output->wlr_output->events.destroy, 246 wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy);
259 &pw->output_destroy);
260 wl_list_insert(&pid_workspaces, &pw->link); 247 wl_list_insert(&pid_workspaces, &pw->link);
261} 248}
262 249
263void root_for_each_workspace(void (*f)(struct sway_container *con, void *data), 250void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
264 void *data) { 251 void *data) {
265 for (int i = 0; i < root_container.children->length; ++i) { 252 for (int i = 0; i < root->outputs->length; ++i) {
266 struct sway_container *output = root_container.children->items[i]; 253 struct sway_output *output = root->outputs->items[i];
267 output_for_each_workspace(output, f, data); 254 output_for_each_workspace(output, f, data);
268 } 255 }
269} 256}
270 257
271void root_for_each_container(void (*f)(struct sway_container *con, void *data), 258void root_for_each_container(void (*f)(struct sway_container *con, void *data),
272 void *data) { 259 void *data) {
273 for (int i = 0; i < root_container.children->length; ++i) { 260 for (int i = 0; i < root->outputs->length; ++i) {
274 struct sway_container *output = root_container.children->items[i]; 261 struct sway_output *output = root->outputs->items[i];
275 output_for_each_container(output, f, data); 262 output_for_each_container(output, f, data);
276 } 263 }
277 264
278 // Scratchpad 265 // Scratchpad
279 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 266 for (int i = 0; i < root->scratchpad->length; ++i) {
280 struct sway_container *container = 267 struct sway_container *container = root->scratchpad->items[i];
281 root_container.sway_root->scratchpad->items[i];
282 // If the container has a parent then it's visible on a workspace 268 // If the container has a parent then it's visible on a workspace
283 // and will have been iterated in the previous for loop. So we only 269 // and will have been iterated in the previous for loop. So we only
284 // iterate the hidden scratchpad containers here. 270 // iterate the hidden scratchpad containers here.
@@ -289,10 +275,10 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
289 } 275 }
290} 276}
291 277
292struct sway_container *root_find_output( 278struct sway_output *root_find_output(
293 bool (*test)(struct sway_container *con, void *data), void *data) { 279 bool (*test)(struct sway_output *output, void *data), void *data) {
294 for (int i = 0; i < root_container.children->length; ++i) { 280 for (int i = 0; i < root->outputs->length; ++i) {
295 struct sway_container *output = root_container.children->items[i]; 281 struct sway_output *output = root->outputs->items[i];
296 if (test(output, data)) { 282 if (test(output, data)) {
297 return output; 283 return output;
298 } 284 }
@@ -300,11 +286,11 @@ struct sway_container *root_find_output(
300 return NULL; 286 return NULL;
301} 287}
302 288
303struct sway_container *root_find_workspace( 289struct sway_workspace *root_find_workspace(
304 bool (*test)(struct sway_container *con, void *data), void *data) { 290 bool (*test)(struct sway_workspace *ws, void *data), void *data) {
305 struct sway_container *result = NULL; 291 struct sway_workspace *result = NULL;
306 for (int i = 0; i < root_container.children->length; ++i) { 292 for (int i = 0; i < root->outputs->length; ++i) {
307 struct sway_container *output = root_container.children->items[i]; 293 struct sway_output *output = root->outputs->items[i];
308 if ((result = output_find_workspace(output, test, data))) { 294 if ((result = output_find_workspace(output, test, data))) {
309 return result; 295 return result;
310 } 296 }
@@ -315,17 +301,16 @@ struct sway_container *root_find_workspace(
315struct sway_container *root_find_container( 301struct sway_container *root_find_container(
316 bool (*test)(struct sway_container *con, void *data), void *data) { 302 bool (*test)(struct sway_container *con, void *data), void *data) {
317 struct sway_container *result = NULL; 303 struct sway_container *result = NULL;
318 for (int i = 0; i < root_container.children->length; ++i) { 304 for (int i = 0; i < root->outputs->length; ++i) {
319 struct sway_container *output = root_container.children->items[i]; 305 struct sway_output *output = root->outputs->items[i];
320 if ((result = output_find_container(output, test, data))) { 306 if ((result = output_find_container(output, test, data))) {
321 return result; 307 return result;
322 } 308 }
323 } 309 }
324 310
325 // Scratchpad 311 // Scratchpad
326 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 312 for (int i = 0; i < root->scratchpad->length; ++i) {
327 struct sway_container *container = 313 struct sway_container *container = root->scratchpad->items[i];
328 root_container.sway_root->scratchpad->items[i];
329 if (!container->parent) { 314 if (!container->parent) {
330 if (test(container, data)) { 315 if (test(container, data)) {
331 return container; 316 return container;
@@ -337,3 +322,10 @@ struct sway_container *root_find_container(
337 } 322 }
338 return NULL; 323 return NULL;
339} 324}
325
326void root_get_box(struct sway_root *root, struct wlr_box *box) {
327 box->x = root->x;
328 box->y = root->y;
329 box->width = root->width;
330 box->height = root->height;
331}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 6bd0ef67..452c2bd1 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -33,6 +33,8 @@ void view_init(struct sway_view *view, enum sway_view_type type,
33 view->marks = create_list(); 33 view->marks = create_list();
34 view->allow_request_urgent = true; 34 view->allow_request_urgent = true;
35 wl_signal_init(&view->events.unmap); 35 wl_signal_init(&view->events.unmap);
36
37 view->container = container_create(view);
36} 38}
37 39
38void view_destroy(struct sway_view *view) { 40void view_destroy(struct sway_view *view) {
@@ -43,8 +45,8 @@ void view_destroy(struct sway_view *view) {
43 "Tried to free view which wasn't marked as destroying")) { 45 "Tried to free view which wasn't marked as destroying")) {
44 return; 46 return;
45 } 47 }
46 if (!sway_assert(view->swayc == NULL, 48 if (!sway_assert(view->container == NULL,
47 "Tried to free view which still has a swayc " 49 "Tried to free view which still has a container "
48 "(might have a pending transaction?)")) { 50 "(might have a pending transaction?)")) {
49 return; 51 return;
50 } 52 }
@@ -57,6 +59,7 @@ void view_destroy(struct sway_view *view) {
57 wlr_texture_destroy(view->marks_focused_inactive); 59 wlr_texture_destroy(view->marks_focused_inactive);
58 wlr_texture_destroy(view->marks_unfocused); 60 wlr_texture_destroy(view->marks_unfocused);
59 wlr_texture_destroy(view->marks_urgent); 61 wlr_texture_destroy(view->marks_urgent);
62 free(view->title_format);
60 63
61 if (view->impl->destroy) { 64 if (view->impl->destroy) {
62 view->impl->destroy(view); 65 view->impl->destroy(view);
@@ -65,23 +68,13 @@ void view_destroy(struct sway_view *view) {
65 } 68 }
66} 69}
67 70
68/**
69 * The view may or may not be involved in a transaction. For example, a view may
70 * unmap then attempt to destroy itself before we've applied the new layout. If
71 * an unmapping view is still involved in a transaction then it'll still have a
72 * swayc.
73 *
74 * If there's no transaction we can simply free the view. Otherwise the
75 * destroying flag will make the view get freed when the transaction is
76 * finished.
77 */
78void view_begin_destroy(struct sway_view *view) { 71void view_begin_destroy(struct sway_view *view) {
79 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { 72 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
80 return; 73 return;
81 } 74 }
82 view->destroying = true; 75 view->destroying = true;
83 76
84 if (!view->swayc) { 77 if (!view->container) {
85 view_destroy(view); 78 view_destroy(view);
86 } 79 }
87} 80}
@@ -171,30 +164,27 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
171} 164}
172 165
173void view_autoconfigure(struct sway_view *view) { 166void view_autoconfigure(struct sway_view *view) {
174 if (!sway_assert(view->swayc, 167 struct sway_output *output = view->container->workspace->output;
175 "Called view_autoconfigure() on a view without a swayc")) {
176 return;
177 }
178
179 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
180 168
181 if (view->swayc->is_fullscreen) { 169 if (view->container->is_fullscreen) {
182 view->x = output->x; 170 view->x = output->wlr_output->lx;
183 view->y = output->y; 171 view->y = output->wlr_output->ly;
184 view->width = output->width; 172 view->width = output->wlr_output->width;
185 view->height = output->height; 173 view->height = output->wlr_output->height;
186 return; 174 return;
187 } 175 }
188 176
189 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 177 struct sway_workspace *ws = view->container->workspace;
190 178
191 int other_views = 0; 179 bool other_views = false;
192 if (config->hide_edge_borders == E_SMART) { 180 if (config->hide_edge_borders == E_SMART) {
193 struct sway_container *con = view->swayc; 181 struct sway_container *con = view->container;
194 while (con != output) { 182 while (con) {
195 if (con->layout != L_TABBED && con->layout != L_STACKED) { 183 enum sway_container_layout layout = container_parent_layout(con);
196 other_views += con->children ? con->children->length - 1 : 0; 184 if (layout != L_TABBED && layout != L_STACKED) {
197 if (other_views > 0) { 185 list_t *siblings = container_get_siblings(con);
186 if (siblings && siblings->length > 1) {
187 other_views = true;
198 break; 188 break;
199 } 189 }
200 } 190 }
@@ -202,7 +192,7 @@ void view_autoconfigure(struct sway_view *view) {
202 } 192 }
203 } 193 }
204 194
205 struct sway_container *con = view->swayc; 195 struct sway_container *con = view->container;
206 196
207 view->border_top = view->border_bottom = true; 197 view->border_top = view->border_bottom = true;
208 view->border_left = view->border_right = true; 198 view->border_left = view->border_right = true;
@@ -228,7 +218,8 @@ void view_autoconfigure(struct sway_view *view) {
228 // In a tabbed or stacked container, the swayc's y is the bottom of the 218 // In a tabbed or stacked container, the swayc's y is the bottom of the
229 // title area. We have to disable any top border because the title bar is 219 // title area. We have to disable any top border because the title bar is
230 // rendered by the parent. 220 // rendered by the parent.
231 if (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED) { 221 enum sway_container_layout layout = container_parent_layout(con);
222 if (layout == L_TABBED || layout == L_STACKED) {
232 view->border_top = false; 223 view->border_top = false;
233 } else { 224 } else {
234 y_offset = container_titlebar_height(); 225 y_offset = container_titlebar_height();
@@ -281,13 +272,16 @@ void view_set_activated(struct sway_view *view, bool activated) {
281} 272}
282 273
283void view_request_activate(struct sway_view *view) { 274void view_request_activate(struct sway_view *view) {
284 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 275 struct sway_workspace *ws = view->container->workspace;
276 if (!ws) { // hidden scratchpad container
277 return;
278 }
285 struct sway_seat *seat = input_manager_current_seat(input_manager); 279 struct sway_seat *seat = input_manager_current_seat(input_manager);
286 280
287 switch (config->focus_on_window_activation) { 281 switch (config->focus_on_window_activation) {
288 case FOWA_SMART: 282 case FOWA_SMART:
289 if (workspace_is_visible(ws)) { 283 if (workspace_is_visible(ws)) {
290 seat_set_focus(seat, view->swayc); 284 seat_set_focus(seat, &view->container->node);
291 } else { 285 } else {
292 view_set_urgent(view, true); 286 view_set_urgent(view, true);
293 } 287 }
@@ -296,7 +290,7 @@ void view_request_activate(struct sway_view *view) {
296 view_set_urgent(view, true); 290 view_set_urgent(view, true);
297 break; 291 break;
298 case FOWA_FOCUS: 292 case FOWA_FOCUS:
299 seat_set_focus(seat, view->swayc); 293 seat_set_focus(seat, &view->container->node);
300 break; 294 break;
301 case FOWA_NONE: 295 case FOWA_NONE:
302 break; 296 break;
@@ -331,23 +325,12 @@ void view_close_popups(struct sway_view *view) {
331} 325}
332 326
333void view_damage_from(struct sway_view *view) { 327void view_damage_from(struct sway_view *view) {
334 for (int i = 0; i < root_container.children->length; ++i) { 328 for (int i = 0; i < root->outputs->length; ++i) {
335 struct sway_container *cont = root_container.children->items[i]; 329 struct sway_output *output = root->outputs->items[i];
336 if (cont->type == C_OUTPUT) { 330 output_damage_from_view(output, view);
337 output_damage_from_view(cont->sway_output, view);
338 }
339 } 331 }
340} 332}
341 333
342static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
343 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
344
345 box->x = output->x + view->swayc->x;
346 box->y = output->y + view->swayc->y;
347 box->width = view->width;
348 box->height = view->height;
349}
350
351void view_for_each_surface(struct sway_view *view, 334void view_for_each_surface(struct sway_view *view,
352 wlr_surface_iterator_func_t iterator, void *user_data) { 335 wlr_surface_iterator_func_t iterator, void *user_data) {
353 if (!view->surface) { 336 if (!view->surface) {
@@ -396,11 +379,8 @@ static bool view_has_executed_criteria(struct sway_view *view,
396} 379}
397 380
398void view_execute_criteria(struct sway_view *view) { 381void view_execute_criteria(struct sway_view *view) {
399 if (!view->swayc) {
400 return;
401 }
402 struct sway_seat *seat = input_manager_current_seat(input_manager); 382 struct sway_seat *seat = input_manager_current_seat(input_manager);
403 struct sway_container *prior_focus = seat_get_focus(seat); 383 struct sway_node *prior_focus = seat_get_focus(seat);
404 list_t *criterias = criteria_for_view(view, CT_COMMAND); 384 list_t *criterias = criteria_for_view(view, CT_COMMAND);
405 for (int i = 0; i < criterias->length; i++) { 385 for (int i = 0; i < criterias->length; i++) {
406 struct criteria *criteria = criterias->items[i]; 386 struct criteria *criteria = criterias->items[i];
@@ -411,7 +391,7 @@ void view_execute_criteria(struct sway_view *view) {
411 } 391 }
412 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 392 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
413 criteria->raw, view, criteria->cmdlist); 393 criteria->raw, view, criteria->cmdlist);
414 seat_set_focus(seat, view->swayc); 394 seat_set_focus(seat, &view->container->node);
415 list_add(view->executed_criteria, criteria); 395 list_add(view->executed_criteria, criteria);
416 struct cmd_results *res = execute_command(criteria->cmdlist, NULL); 396 struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
417 if (res->status != CMD_SUCCESS) { 397 if (res->status != CMD_SUCCESS) {
@@ -423,19 +403,19 @@ void view_execute_criteria(struct sway_view *view) {
423 seat_set_focus(seat, prior_focus); 403 seat_set_focus(seat, prior_focus);
424} 404}
425 405
426static struct sway_container *select_workspace(struct sway_view *view) { 406static struct sway_workspace *select_workspace(struct sway_view *view) {
427 struct sway_seat *seat = input_manager_current_seat(input_manager); 407 struct sway_seat *seat = input_manager_current_seat(input_manager);
428 408
429 // Check if there's any `assign` criteria for the view 409 // Check if there's any `assign` criteria for the view
430 list_t *criterias = criteria_for_view(view, 410 list_t *criterias = criteria_for_view(view,
431 CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); 411 CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT);
432 struct sway_container *ws = NULL; 412 struct sway_workspace *ws = NULL;
433 for (int i = 0; i < criterias->length; ++i) { 413 for (int i = 0; i < criterias->length; ++i) {
434 struct criteria *criteria = criterias->items[i]; 414 struct criteria *criteria = criterias->items[i];
435 if (criteria->type == CT_ASSIGN_OUTPUT) { 415 if (criteria->type == CT_ASSIGN_OUTPUT) {
436 struct sway_container *output = output_by_name(criteria->target); 416 struct sway_output *output = output_by_name(criteria->target);
437 if (output) { 417 if (output) {
438 ws = seat_get_active_child(seat, output); 418 ws = output_get_active_workspace(output);
439 break; 419 break;
440 } 420 }
441 } else { 421 } else {
@@ -484,20 +464,14 @@ static struct sway_container *select_workspace(struct sway_view *view) {
484 } 464 }
485 465
486 // Use the focused workspace 466 // Use the focused workspace
487 ws = seat_get_focus_inactive(seat, &root_container); 467 return seat_get_focused_workspace(seat);
488 if (ws->type != C_WORKSPACE) {
489 ws = container_parent(ws, C_WORKSPACE);
490 }
491 return ws;
492} 468}
493 469
494static bool should_focus(struct sway_view *view) { 470static bool should_focus(struct sway_view *view) {
495 struct sway_seat *seat = input_manager_current_seat(input_manager); 471 struct sway_seat *seat = input_manager_current_seat(input_manager);
496 struct sway_container *prev_focus = 472 struct sway_container *prev_con = seat_get_focused_container(seat);
497 seat_get_focus_inactive(seat, &root_container); 473 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
498 struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? 474 struct sway_workspace *map_ws = view->container->workspace;
499 prev_focus : container_parent(prev_focus, C_WORKSPACE);
500 struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE);
501 475
502 // Views can only take focus if they are mapped into the active workspace 476 // Views can only take focus if they are mapped into the active workspace
503 if (prev_ws != map_ws) { 477 if (prev_ws != map_ws) {
@@ -506,10 +480,9 @@ static bool should_focus(struct sway_view *view) {
506 480
507 // If the view is the only one in the focused workspace, it'll get focus 481 // If the view is the only one in the focused workspace, it'll get focus
508 // regardless of any no_focus criteria. 482 // regardless of any no_focus criteria.
509 struct sway_container *parent = view->swayc->parent; 483 if (!view->container->parent && !prev_con) {
510 if (parent->type == C_WORKSPACE && prev_focus == parent) { 484 size_t num_children = view->container->workspace->tiling->length +
511 size_t num_children = parent->children->length + 485 view->container->workspace->floating->length;
512 parent->sway_workspace->floating->length;
513 if (num_children == 1) { 486 if (num_children == 1) {
514 return true; 487 return true;
515 } 488 }
@@ -529,16 +502,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
529 view->surface = wlr_surface; 502 view->surface = wlr_surface;
530 503
531 struct sway_seat *seat = input_manager_current_seat(input_manager); 504 struct sway_seat *seat = input_manager_current_seat(input_manager);
532 struct sway_container *ws = select_workspace(view); 505 struct sway_workspace *ws = select_workspace(view);
533 struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); 506 struct sway_node *node = seat_get_focus_inactive(seat, &ws->node);
507 struct sway_container *target_sibling = node->type == N_CONTAINER ?
508 node->sway_container : NULL;
534 509
535 // If we're about to launch the view into the floating container, then 510 // If we're about to launch the view into the floating container, then
536 // launch it as a tiled view in the root of the workspace instead. 511 // launch it as a tiled view in the root of the workspace instead.
537 if (container_is_floating(target_sibling)) { 512 if (target_sibling && container_is_floating(target_sibling)) {
538 target_sibling = target_sibling->parent; 513 target_sibling = NULL;
539 } 514 }
540 515
541 view->swayc = container_view_create(target_sibling, view); 516 view->container = container_create(view);
517 if (target_sibling) {
518 container_add_sibling(target_sibling, view->container, 1);
519 } else {
520 workspace_add_tiling(ws, view->container);
521 }
522 ipc_event_window(view->container, "new");
542 523
543 view_init_subsurfaces(view, wlr_surface); 524 view_init_subsurfaces(view, wlr_surface);
544 wl_signal_add(&wlr_surface->events.new_subsurface, 525 wl_signal_add(&wlr_surface->events.new_subsurface,
@@ -548,7 +529,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
548 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 529 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
549 view->border = config->floating_border; 530 view->border = config->floating_border;
550 view->border_thickness = config->floating_border_thickness; 531 view->border_thickness = config->floating_border_thickness;
551 container_set_floating(view->swayc, true); 532 container_set_floating(view->container, true);
552 } else { 533 } else {
553 view->border = config->border; 534 view->border = config->border;
554 view->border_thickness = config->border_thickness; 535 view->border_thickness = config->border_thickness;
@@ -556,11 +537,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
556 } 537 }
557 538
558 if (should_focus(view)) { 539 if (should_focus(view)) {
559 input_manager_set_focus(input_manager, view->swayc); 540 input_manager_set_focus(input_manager, &view->container->node);
560 } 541 }
561 542
562 view_update_title(view, false); 543 view_update_title(view, false);
563 container_notify_subtree_changed(view->swayc->parent); 544 container_update_representation(view->container);
564 view_execute_criteria(view); 545 view_execute_criteria(view);
565} 546}
566 547
@@ -574,17 +555,17 @@ void view_unmap(struct sway_view *view) {
574 view->urgent_timer = NULL; 555 view->urgent_timer = NULL;
575 } 556 }
576 557
577 bool was_fullscreen = view->swayc->is_fullscreen; 558 struct sway_container *parent = view->container->parent;
578 struct sway_container *parent = view->swayc->parent; 559 struct sway_workspace *ws = view->container->workspace;
579 container_begin_destroy(view->swayc); 560 container_begin_destroy(view->container);
580 struct sway_container *surviving_ancestor = container_reap_empty(parent); 561 if (parent) {
562 container_reap_empty(parent);
563 } else {
564 workspace_consider_destroy(ws);
565 }
581 566
582 // If the workspace wasn't reaped 567 if (!ws->node.destroying) {
583 if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { 568 arrange_workspace(ws);
584 struct sway_container *ws = surviving_ancestor->type == C_WORKSPACE ?
585 surviving_ancestor :
586 container_parent(surviving_ancestor, C_WORKSPACE);
587 arrange_windows(was_fullscreen ? ws : surviving_ancestor);
588 workspace_detect_urgent(ws); 569 workspace_detect_urgent(ws);
589 } 570 }
590 571
@@ -593,15 +574,15 @@ void view_unmap(struct sway_view *view) {
593} 574}
594 575
595void view_update_size(struct sway_view *view, int width, int height) { 576void view_update_size(struct sway_view *view, int width, int height) {
596 if (!sway_assert(container_is_floating(view->swayc), 577 if (!sway_assert(container_is_floating(view->container),
597 "Expected a floating container")) { 578 "Expected a floating container")) {
598 return; 579 return;
599 } 580 }
600 view->width = width; 581 view->width = width;
601 view->height = height; 582 view->height = height;
602 view->swayc->current.view_width = width; 583 view->container->current.view_width = width;
603 view->swayc->current.view_height = height; 584 view->container->current.view_height = height;
604 container_set_geometry_from_floating_view(view->swayc); 585 container_set_geometry_from_floating_view(view->container);
605} 586}
606 587
607static void view_subsurface_create(struct sway_view *view, 588static void view_subsurface_create(struct sway_view *view,
@@ -670,27 +651,18 @@ void view_child_init(struct sway_view_child *child,
670 wl_signal_add(&view->events.unmap, &child->view_unmap); 651 wl_signal_add(&view->events.unmap, &child->view_unmap);
671 child->view_unmap.notify = view_child_handle_view_unmap; 652 child->view_unmap.notify = view_child_handle_view_unmap;
672 653
673 struct sway_container *output = child->view->swayc->parent; 654 struct sway_output *output = child->view->container->workspace->output;
674 if (output != NULL) { 655 wlr_surface_send_enter(child->surface, output->wlr_output);
675 if (output->type != C_OUTPUT) {
676 output = container_parent(output, C_OUTPUT);
677 }
678 wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
679 }
680 656
681 view_init_subsurfaces(child->view, surface); 657 view_init_subsurfaces(child->view, surface);
682 658
683 // TODO: only damage the whole child 659 // TODO: only damage the whole child
684 if (child->view->swayc) { 660 container_damage_whole(child->view->container);
685 container_damage_whole(child->view->swayc);
686 }
687} 661}
688 662
689void view_child_destroy(struct sway_view_child *child) { 663void view_child_destroy(struct sway_view_child *child) {
690 // TODO: only damage the whole child 664 // TODO: only damage the whole child
691 if (child->view->swayc) { 665 container_damage_whole(child->view->container);
692 container_damage_whole(child->view->swayc);
693 }
694 666
695 wl_list_remove(&child->surface_commit.link); 667 wl_list_remove(&child->surface_commit.link);
696 wl_list_remove(&child->surface_destroy.link); 668 wl_list_remove(&child->surface_destroy.link);
@@ -808,22 +780,20 @@ static char *escape_title(char *buffer) {
808} 780}
809 781
810void view_update_title(struct sway_view *view, bool force) { 782void view_update_title(struct sway_view *view, bool force) {
811 if (!view->swayc) {
812 return;
813 }
814 const char *title = view_get_title(view); 783 const char *title = view_get_title(view);
815 784
816 if (!force) { 785 if (!force) {
817 if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) { 786 if (title && view->container->title &&
787 strcmp(title, view->container->title) == 0) {
818 return; 788 return;
819 } 789 }
820 if (!title && !view->swayc->name) { 790 if (!title && !view->container->title) {
821 return; 791 return;
822 } 792 }
823 } 793 }
824 794
825 free(view->swayc->name); 795 free(view->container->title);
826 free(view->swayc->formatted_title); 796 free(view->container->formatted_title);
827 if (title) { 797 if (title) {
828 size_t len = parse_title_format(view, NULL); 798 size_t len = parse_title_format(view, NULL);
829 char *buffer = calloc(len + 1, sizeof(char)); 799 char *buffer = calloc(len + 1, sizeof(char));
@@ -836,25 +806,25 @@ void view_update_title(struct sway_view *view, bool force) {
836 buffer = escape_title(buffer); 806 buffer = escape_title(buffer);
837 } 807 }
838 808
839 view->swayc->name = strdup(title); 809 view->container->title = strdup(title);
840 view->swayc->formatted_title = buffer; 810 view->container->formatted_title = buffer;
841 } else { 811 } else {
842 view->swayc->name = NULL; 812 view->container->title = NULL;
843 view->swayc->formatted_title = NULL; 813 view->container->formatted_title = NULL;
844 } 814 }
845 container_calculate_title_height(view->swayc); 815 container_calculate_title_height(view->container);
846 config_update_font_height(false); 816 config_update_font_height(false);
847 817
848 // Update title after the global font height is updated 818 // Update title after the global font height is updated
849 container_update_title_textures(view->swayc); 819 container_update_title_textures(view->container);
850 820
851 ipc_event_window(view->swayc, "title"); 821 ipc_event_window(view->container, "title");
852} 822}
853 823
854static bool find_by_mark_iterator(struct sway_container *con, 824static bool find_by_mark_iterator(struct sway_container *con,
855 void *data) { 825 void *data) {
856 char *mark = data; 826 char *mark = data;
857 return con->type == C_VIEW && view_has_mark(con->sway_view, mark); 827 return con->view && view_has_mark(con->view, mark);
858} 828}
859 829
860struct sway_view *view_find_mark(char *mark) { 830struct sway_view *view_find_mark(char *mark) {
@@ -863,7 +833,7 @@ struct sway_view *view_find_mark(char *mark) {
863 if (!container) { 833 if (!container) {
864 return NULL; 834 return NULL;
865 } 835 }
866 return container->sway_view; 836 return container->view;
867} 837}
868 838
869bool view_find_and_unmark(char *mark) { 839bool view_find_and_unmark(char *mark) {
@@ -872,7 +842,7 @@ bool view_find_and_unmark(char *mark) {
872 if (!container) { 842 if (!container) {
873 return false; 843 return false;
874 } 844 }
875 struct sway_view *view = container->sway_view; 845 struct sway_view *view = container->view;
876 846
877 for (int i = 0; i < view->marks->length; ++i) { 847 for (int i = 0; i < view->marks->length; ++i) {
878 char *view_mark = view->marks->items[i]; 848 char *view_mark = view->marks->items[i];
@@ -888,10 +858,9 @@ bool view_find_and_unmark(char *mark) {
888} 858}
889 859
890void view_clear_marks(struct sway_view *view) { 860void view_clear_marks(struct sway_view *view) {
891 while (view->marks->length) { 861 list_foreach(view->marks, free);
892 list_del(view->marks, 0); 862 view->marks->length = 0;
893 ipc_event_window(view->swayc, "mark"); 863 ipc_event_window(view->container, "mark");
894 }
895} 864}
896 865
897bool view_has_mark(struct sway_view *view, char *mark) { 866bool view_has_mark(struct sway_view *view, char *mark) {
@@ -906,12 +875,13 @@ bool view_has_mark(struct sway_view *view, char *mark) {
906 875
907void view_add_mark(struct sway_view *view, char *mark) { 876void view_add_mark(struct sway_view *view, char *mark) {
908 list_add(view->marks, strdup(mark)); 877 list_add(view->marks, strdup(mark));
909 ipc_event_window(view->swayc, "mark"); 878 ipc_event_window(view->container, "mark");
910} 879}
911 880
912static void update_marks_texture(struct sway_view *view, 881static void update_marks_texture(struct sway_view *view,
913 struct wlr_texture **texture, struct border_colors *class) { 882 struct wlr_texture **texture, struct border_colors *class) {
914 struct sway_output *output = container_get_effective_output(view->swayc); 883 struct sway_output *output =
884 container_get_effective_output(view->container);
915 if (!output) { 885 if (!output) {
916 return; 886 return;
917 } 887 }
@@ -949,7 +919,7 @@ static void update_marks_texture(struct sway_view *view,
949 919
950 double scale = output->wlr_output->scale; 920 double scale = output->wlr_output->scale;
951 int width = 0; 921 int width = 0;
952 int height = view->swayc->title_height * scale; 922 int height = view->container->title_height * scale;
953 923
954 cairo_t *c = cairo_create(NULL); 924 cairo_t *c = cairo_create(NULL);
955 get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); 925 get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer);
@@ -994,44 +964,40 @@ void view_update_marks_textures(struct sway_view *view) {
994 &config->border_colors.unfocused); 964 &config->border_colors.unfocused);
995 update_marks_texture(view, &view->marks_urgent, 965 update_marks_texture(view, &view->marks_urgent,
996 &config->border_colors.urgent); 966 &config->border_colors.urgent);
997 container_damage_whole(view->swayc); 967 container_damage_whole(view->container);
998} 968}
999 969
1000bool view_is_visible(struct sway_view *view) { 970bool view_is_visible(struct sway_view *view) {
1001 if (!view->swayc || view->swayc->destroying) { 971 if (view->container->node.destroying) {
1002 return false; 972 return false;
1003 } 973 }
1004 struct sway_container *workspace = 974 struct sway_workspace *workspace = view->container->workspace;
1005 container_parent(view->swayc, C_WORKSPACE);
1006 if (!workspace) { 975 if (!workspace) {
1007 return false; 976 return false;
1008 } 977 }
1009 // Determine if view is nested inside a floating container which is sticky. 978 // Determine if view is nested inside a floating container which is sticky
1010 // A simple floating view will have this ancestry: 979 struct sway_container *floater = view->container;
1011 // C_VIEW -> floating -> workspace 980 while (floater->parent) {
1012 // A more complex ancestry could be:
1013 // C_VIEW -> C_CONTAINER (tabbed) -> floating -> workspace
1014 struct sway_container *floater = view->swayc;
1015 while (floater->parent->type != C_WORKSPACE
1016 && floater->parent->parent->type != C_WORKSPACE) {
1017 floater = floater->parent; 981 floater = floater->parent;
1018 } 982 }
1019 bool is_sticky = container_is_floating(floater) && floater->is_sticky; 983 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
1020 // Check view isn't in a tabbed or stacked container on an inactive tab 984 // Check view isn't in a tabbed or stacked container on an inactive tab
1021 struct sway_seat *seat = input_manager_current_seat(input_manager); 985 struct sway_seat *seat = input_manager_current_seat(input_manager);
1022 struct sway_container *container = view->swayc; 986 struct sway_container *container = view->container;
1023 while (container->type != C_WORKSPACE) { 987 while (container) {
1024 if (container->parent->layout == L_TABBED || 988 enum sway_container_layout layout = container_parent_layout(container);
1025 container->parent->layout == L_STACKED) { 989 if (layout == L_TABBED || layout == L_STACKED) {
1026 if (seat_get_active_child(seat, container->parent) != container) { 990 struct sway_node *parent = container->parent ?
991 &container->parent->node : &container->workspace->node;
992 if (seat_get_active_child(seat, parent) != &container->node) {
1027 return false; 993 return false;
1028 } 994 }
1029 } 995 }
1030 container = container->parent; 996 container = container->parent;
1031 } 997 }
1032 // Check view isn't hidden by another fullscreen view 998 // Check view isn't hidden by another fullscreen view
1033 if (workspace->sway_workspace->fullscreen && 999 if (workspace->fullscreen &&
1034 !container_is_fullscreen_or_child(view->swayc)) { 1000 !container_is_fullscreen_or_child(view->container)) {
1035 return false; 1001 return false;
1036 } 1002 }
1037 // Check the workspace is visible 1003 // Check the workspace is visible
@@ -1047,7 +1013,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1047 } 1013 }
1048 if (enable) { 1014 if (enable) {
1049 struct sway_seat *seat = input_manager_current_seat(input_manager); 1015 struct sway_seat *seat = input_manager_current_seat(input_manager);
1050 if (seat_get_focus(seat) == view->swayc) { 1016 if (seat_get_focused_container(seat) == view->container) {
1051 return; 1017 return;
1052 } 1018 }
1053 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1019 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
@@ -1058,12 +1024,11 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1058 view->urgent_timer = NULL; 1024 view->urgent_timer = NULL;
1059 } 1025 }
1060 } 1026 }
1061 container_damage_whole(view->swayc); 1027 container_damage_whole(view->container);
1062 1028
1063 ipc_event_window(view->swayc, "urgent"); 1029 ipc_event_window(view->container, "urgent");
1064 1030
1065 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 1031 workspace_detect_urgent(view->container->workspace);
1066 workspace_detect_urgent(ws);
1067} 1032}
1068 1033
1069bool view_is_urgent(struct sway_view *view) { 1034bool view_is_urgent(struct sway_view *view) {
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
21struct sway_container *workspace_get_initial_output(const char *name) { 22struct 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
48struct sway_container *workspace_create(struct sway_container *output, 41struct 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
82void workspace_destroy(struct sway_container *workspace) { 77void 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
114void workspace_begin_destroy(struct sway_container *workspace) { 98void 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
130void workspace_consider_destroy(struct sway_container *ws) { 111void 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
285static bool _workspace_by_number(struct sway_container *view, void *data) { 262static 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
299struct sway_container *workspace_by_number(const char* name) { 273struct 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
303static bool _workspace_by_name(struct sway_container *view, void *data) { 277static 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
308struct sway_container *workspace_by_name(const char *name) { 281struct 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 */
342static struct sway_container *workspace_output_prev_next_impl( 309static 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 */
367static struct sway_container *workspace_prev_next_impl( 323static 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
397struct sway_container *workspace_output_next(struct sway_container *current) { 345struct 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
401struct sway_container *workspace_next(struct sway_container *current) { 349struct 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
405struct sway_container *workspace_output_prev(struct sway_container *current) { 353struct 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
409struct sway_container *workspace_prev(struct sway_container *current) { 357struct 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
413bool workspace_switch(struct sway_container *workspace, 361bool 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
491bool workspace_is_visible(struct sway_container *ws) { 428bool 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
504bool workspace_is_empty(struct sway_container *ws) { 435bool 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
526void workspace_output_raise_priority(struct sway_container *workspace, 453void 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
547void workspace_output_add_priority(struct sway_container *workspace, 473void 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
557static bool _output_by_name(struct sway_container *output, void *data) { 482struct 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];
561struct 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
578static bool find_urgent_iterator(struct sway_container *con, void *data) { 499static 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
582void workspace_detect_urgent(struct sway_container *workspace) { 503void 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
593void workspace_for_each_container(struct sway_container *ws, 514void 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
613struct sway_container *workspace_find_container(struct sway_container *ws, 530struct 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
642struct sway_container *workspace_wrap_children(struct sway_container *ws) { 556struct 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
654void workspace_add_floating(struct sway_container *workspace, 568void 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
580static void set_workspace(struct sway_container *container, void *data) {
581 container->workspace = container->parent->workspace;
582}
583
584void 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); 598void 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
669void workspace_remove_gaps(struct sway_container *ws) { 611void 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
625void 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
684void workspace_add_gaps(struct sway_container *ws) { 637void 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
662struct 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
678void 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
688void 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}