aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar emersion <contact@emersion.fr>2018-02-24 23:29:08 +0100
committerLibravatar GitHub <noreply@github.com>2018-02-24 23:29:08 +0100
commit583c30dbe3e3249ec1b753fddbfa982db56dce31 (patch)
treece07d65ab79287280f1f5468e2e62eb5cbef4089
parentImplement workspaces (diff)
parentstatic bfs queue (diff)
downloadsway-583c30dbe3e3249ec1b753fddbfa982db56dce31.tar.gz
sway-583c30dbe3e3249ec1b753fddbfa982db56dce31.tar.zst
sway-583c30dbe3e3249ec1b753fddbfa982db56dce31.zip
Merge pull request #1585 from acrisci/focus-overhaul
focus overhaul
-rw-r--r--include/log.h4
-rw-r--r--include/sway/commands.h4
-rw-r--r--include/sway/container.h12
-rw-r--r--include/sway/input/input-manager.h5
-rw-r--r--include/sway/input/seat.h30
-rw-r--r--include/sway/layout.h20
-rw-r--r--include/sway/output.h1
-rw-r--r--include/sway/server.h6
-rw-r--r--include/sway/workspace.h10
-rw-r--r--meson.build1
-rw-r--r--sway/commands.c108
-rw-r--r--sway/commands/exit.c3
-rw-r--r--sway/commands/focus.c59
-rw-r--r--sway/commands/kill.c7
-rw-r--r--sway/commands/layout.c56
-rw-r--r--sway/commands/reload.c3
-rw-r--r--sway/commands/set.c1
-rw-r--r--sway/commands/workspace.c3
-rw-r--r--sway/desktop/output.c115
-rw-r--r--sway/desktop/xdg_shell_v6.c3
-rw-r--r--sway/input/input-manager.c65
-rw-r--r--sway/input/keyboard.c2
-rw-r--r--sway/input/seat.c183
-rw-r--r--sway/ipc-json.c4
-rw-r--r--sway/ipc-server.c2
-rw-r--r--sway/meson.build2
-rw-r--r--sway/server.c10
-rw-r--r--sway/tree/container.c78
-rw-r--r--sway/tree/layout.c285
-rw-r--r--sway/tree/workspace.c37
30 files changed, 869 insertions, 250 deletions
diff --git a/include/log.h b/include/log.h
index 350a59ef..a9748127 100644
--- a/include/log.h
+++ b/include/log.h
@@ -5,11 +5,11 @@
5 5
6void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2); 6void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);
7#define sway_abort(FMT, ...) \ 7#define sway_abort(FMT, ...) \
8 _sway_abort("[%s:%d] " FMT, _strip_path(__FILE__), __LINE__, ##__VA_ARGS__) 8 _sway_abort("[%s:%d] " FMT, wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__)
9 9
10bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3); 10bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);
11#define sway_assert(COND, FMT, ...) \ 11#define sway_assert(COND, FMT, ...) \
12 _sway_assert(COND, "[%s:%d] %s:" FMT, _strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) 12 _sway_assert(COND, "[%s:%d] %s:" FMT, wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
13 13
14void error_handler(int sig); 14void error_handler(int sig);
15 15
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 4ee7af2a..9ff18823 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -46,9 +46,9 @@ struct cmd_results *checkarg(int argc, const char *name,
46 enum expected_args type, int val); 46 enum expected_args type, int val);
47 47
48/** 48/**
49 * Parse and handles a command. 49 * Parse and executes a command.
50 */ 50 */
51struct cmd_results *handle_command(char *command); 51struct cmd_results *execute_command(char *command, struct sway_seat *seat);
52/** 52/**
53 * Parse and handles a command during config file loading. 53 * Parse and handles a command during config file loading.
54 * 54 *
diff --git a/include/sway/container.h b/include/sway/container.h
index 0c66932d..f200a1a2 100644
--- a/include/sway/container.h
+++ b/include/sway/container.h
@@ -106,10 +106,6 @@ struct sway_container {
106 * The parent of this container. NULL for the root container. 106 * The parent of this container. NULL for the root container.
107 */ 107 */
108 struct sway_container *parent; 108 struct sway_container *parent;
109 /**
110 * Which of this container's children has focus.
111 */
112 struct sway_container *focused;
113 109
114 /** 110 /**
115 * Number of master views in auto layouts. 111 * Number of master views in auto layouts.
@@ -162,4 +158,12 @@ void container_map(swayc_t *container,
162swayc_t *swayc_at(swayc_t *parent, double lx, double ly, 158swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
163 struct wlr_surface **surface, double *sx, double *sy); 159 struct wlr_surface **surface, double *sx, double *sy);
164 160
161/**
162 * Apply the function for each child of the container breadth first.
163 */
164void container_for_each_bfs(swayc_t *con, void (*f)(swayc_t *con, void *data),
165 void *data);
166
167swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout);
168
165#endif 169#endif
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 66ace262..eab7dc90 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -16,14 +16,15 @@ struct sway_input_device {
16 struct wlr_input_device *wlr_device; 16 struct wlr_input_device *wlr_device;
17 struct input_config *config; 17 struct input_config *config;
18 struct wl_list link; 18 struct wl_list link;
19 struct wl_listener device_destroy;
19}; 20};
20 21
21struct sway_input_manager { 22struct sway_input_manager {
22 struct wl_listener input_add;
23 struct wl_listener input_remove;
24 struct sway_server *server; 23 struct sway_server *server;
25 struct wl_list devices; 24 struct wl_list devices;
26 struct wl_list seats; 25 struct wl_list seats;
26
27 struct wl_listener new_input;
27}; 28};
28 29
29struct sway_input_manager *sway_input_manager_create( 30struct sway_input_manager *sway_input_manager_create(
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index b21cbccb..1d55bec7 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -12,14 +12,26 @@ struct sway_seat_device {
12 struct wl_list link; // sway_seat::devices 12 struct wl_list link; // sway_seat::devices
13}; 13};
14 14
15struct sway_seat_container {
16 struct sway_seat *seat;
17 swayc_t *container;
18
19 struct wl_list link; // sway_seat::focus_stack
20
21 struct wl_listener destroy;
22};
23
15struct sway_seat { 24struct sway_seat {
16 struct wlr_seat *wlr_seat; 25 struct wlr_seat *wlr_seat;
17 struct seat_config *config; 26 struct seat_config *config;
18 struct sway_cursor *cursor; 27 struct sway_cursor *cursor;
19 struct sway_input_manager *input; 28 struct sway_input_manager *input;
20 swayc_t *focus; 29
30 bool has_focus;
31 struct wl_list focus_stack; // list of containers in focus order
21 32
22 struct wl_listener focus_destroy; 33 struct wl_listener focus_destroy;
34 struct wl_listener new_container;
23 35
24 struct wl_list devices; // sway_seat_device::link 36 struct wl_list devices; // sway_seat_device::link
25 37
@@ -44,6 +56,22 @@ void sway_seat_configure_xcursor(struct sway_seat *seat);
44 56
45void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container); 57void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container);
46 58
59swayc_t *sway_seat_get_focus(struct sway_seat *seat);
60
61/**
62 * Return the last container to be focused for the seat (or the most recently
63 * opened if no container has received focused) that is a child of the given
64 * container. The focus-inactive container of the root window is the focused
65 * container for the seat (if the seat does have focus). This function can be
66 * used to determine what container gets focused next if the focused container
67 * is destroyed, or focus moves to a container with children and we need to
68 * descend into the next leaf in focus order.
69 */
70swayc_t *sway_seat_get_focus_inactive(struct sway_seat *seat, swayc_t *container);
71
72swayc_t *sway_seat_get_focus_by_type(struct sway_seat *seat,
73 enum swayc_types type);
74
47void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config); 75void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config);
48 76
49#endif 77#endif
diff --git a/include/sway/layout.h b/include/sway/layout.h
index af561453..e82c4442 100644
--- a/include/sway/layout.h
+++ b/include/sway/layout.h
@@ -2,6 +2,19 @@
2#define _SWAY_LAYOUT_H 2#define _SWAY_LAYOUT_H
3 3
4#include <wlr/types/wlr_output_layout.h> 4#include <wlr/types/wlr_output_layout.h>
5#include "sway/container.h"
6
7enum movement_direction {
8 MOVE_LEFT,
9 MOVE_RIGHT,
10 MOVE_UP,
11 MOVE_DOWN,
12 MOVE_PARENT,
13 MOVE_CHILD,
14 MOVE_NEXT,
15 MOVE_PREV,
16 MOVE_FIRST
17};
5 18
6struct sway_container; 19struct sway_container;
7 20
@@ -11,13 +24,20 @@ struct sway_root {
11 struct wl_listener output_layout_change; 24 struct wl_listener output_layout_change;
12 25
13 struct wl_list unmanaged_views; // sway_view::unmanaged_view_link 26 struct wl_list unmanaged_views; // sway_view::unmanaged_view_link
27
28 struct {
29 struct wl_signal new_container;
30 } events;
14}; 31};
15 32
16void init_layout(void); 33void init_layout(void);
17void add_child(struct sway_container *parent, struct sway_container *child); 34void add_child(struct sway_container *parent, struct sway_container *child);
35swayc_t *add_sibling(swayc_t *parent, swayc_t *child);
18struct sway_container *remove_child(struct sway_container *child); 36struct sway_container *remove_child(struct sway_container *child);
19enum swayc_layouts default_layout(struct sway_container *output); 37enum swayc_layouts default_layout(struct sway_container *output);
20void sort_workspaces(struct sway_container *output); 38void sort_workspaces(struct sway_container *output);
21void arrange_windows(struct sway_container *container, double width, double height); 39void arrange_windows(struct sway_container *container, double width, double height);
40swayc_t *get_swayc_in_direction(swayc_t *container,
41 struct sway_seat *seat, enum movement_direction dir);
22 42
23#endif 43#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index 7ca02d7b..95d64705 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -14,6 +14,7 @@ struct sway_output {
14 struct timespec last_frame; 14 struct timespec last_frame;
15 15
16 struct wl_listener frame; 16 struct wl_listener frame;
17 struct wl_listener output_destroy;
17}; 18};
18 19
19#endif 20#endif
diff --git a/include/sway/server.h b/include/sway/server.h
index d497e132..3fcdb1ba 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -24,8 +24,7 @@ struct sway_server {
24 24
25 struct sway_input_manager *input; 25 struct sway_input_manager *input;
26 26
27 struct wl_listener output_add; 27 struct wl_listener new_output;
28 struct wl_listener output_remove;
29 struct wl_listener output_frame; 28 struct wl_listener output_frame;
30 29
31 struct wlr_xdg_shell_v6 *xdg_shell_v6; 30 struct wlr_xdg_shell_v6 *xdg_shell_v6;
@@ -45,8 +44,7 @@ bool server_init(struct sway_server *server);
45void server_fini(struct sway_server *server); 44void server_fini(struct sway_server *server);
46void server_run(struct sway_server *server); 45void server_run(struct sway_server *server);
47 46
48void output_add_notify(struct wl_listener *listener, void *data); 47void handle_new_output(struct wl_listener *listener, void *data);
49void output_remove_notify(struct wl_listener *listener, void *data);
50 48
51void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); 49void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
52void handle_xwayland_surface(struct wl_listener *listener, void *data); 50void handle_xwayland_surface(struct wl_listener *listener, void *data);
diff --git a/include/sway/workspace.h b/include/sway/workspace.h
index 30bbdaa8..fee54255 100644
--- a/include/sway/workspace.h
+++ b/include/sway/workspace.h
@@ -1,7 +1,7 @@
1#ifndef _SWAY_WORKSPACE_H 1#ifndef _SWAY_WORKSPACE_H
2#define _SWAY_WORKSPACE_H 2#define _SWAY_WORKSPACE_H
3 3
4struct sway_container; 4#include "sway/container.h"
5 5
6extern char *prev_workspace_name; 6extern char *prev_workspace_name;
7 7
@@ -12,9 +12,9 @@ bool workspace_switch(swayc_t *workspace);
12struct sway_container *workspace_by_number(const char* name); 12struct sway_container *workspace_by_number(const char* name);
13swayc_t *workspace_by_name(const char*); 13swayc_t *workspace_by_name(const char*);
14 14
15struct sway_container *workspace_output_next(struct sway_container *current); 15struct sway_container *workspace_output_next(swayc_t *current);
16struct sway_container *workspace_next(struct sway_container *current); 16struct sway_container *workspace_next(swayc_t *current);
17struct sway_container *workspace_output_prev(struct sway_container *current); 17struct sway_container *workspace_output_prev(swayc_t *current);
18struct sway_container *workspace_prev(struct sway_container *current); 18struct sway_container *workspace_prev(swayc_t *current);
19 19
20#endif 20#endif
diff --git a/meson.build b/meson.build
index 49392126..fc995c81 100644
--- a/meson.build
+++ b/meson.build
@@ -10,6 +10,7 @@ project(
10) 10)
11 11
12add_project_arguments('-Wno-unused-parameter', language: 'c') 12add_project_arguments('-Wno-unused-parameter', language: 'c')
13add_project_arguments('-Wno-unused-function', language: 'c')
13 14
14cc = meson.get_compiler('c') 15cc = meson.get_compiler('c')
15 16
diff --git a/sway/commands.c b/sway/commands.c
index 0d4aa104..b52eb200 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -125,23 +125,42 @@ struct cmd_results *add_color(const char *name, char *buffer, const char *color)
125 return NULL; 125 return NULL;
126} 126}
127 127
128/* Keep alphabetized */ 128/**
129 * handlers that can run in either config or command context
130 * Keep alphabetized
131 */
129static struct cmd_handler handlers[] = { 132static struct cmd_handler handlers[] = {
130 { "bindcode", cmd_bindcode }, 133 { "bindcode", cmd_bindcode },
131 { "bindsym", cmd_bindsym }, 134 { "bindsym", cmd_bindsym },
132 { "exec", cmd_exec }, 135 { "exec", cmd_exec },
133 { "exec_always", cmd_exec_always }, 136 { "exec_always", cmd_exec_always },
134 { "exit", cmd_exit },
135 { "include", cmd_include }, 137 { "include", cmd_include },
136 { "input", cmd_input }, 138 { "input", cmd_input },
137 { "kill", cmd_kill },
138 { "output", cmd_output }, 139 { "output", cmd_output },
139 { "reload", cmd_reload },
140 { "seat", cmd_seat }, 140 { "seat", cmd_seat },
141 { "set", cmd_set },
142 { "workspace", cmd_workspace }, 141 { "workspace", cmd_workspace },
143}; 142};
144 143
144/**
145 * Commands that can *only* run in the config loading context
146 * Keep alphabetized
147 */
148static struct cmd_handler config_handlers[] = {
149 { "set", cmd_set },
150};
151
152/**
153 * Commands that can *not* run in the config loading context
154 * Keep alphabetized
155 */
156static struct cmd_handler command_handlers[] = {
157 { "exit", cmd_exit },
158 { "focus", cmd_focus },
159 { "kill", cmd_kill },
160 { "layout", cmd_layout },
161 { "reload", cmd_reload },
162};
163
145static int handler_compare(const void *_a, const void *_b) { 164static int handler_compare(const void *_a, const void *_b) {
146 const struct cmd_handler *a = _a; 165 const struct cmd_handler *a = _a;
147 const struct cmd_handler *b = _b; 166 const struct cmd_handler *b = _b;
@@ -179,24 +198,48 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
179 struct cmd_handler *res = NULL; 198 struct cmd_handler *res = NULL;
180 wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT); 199 wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT);
181 200
201 bool config_loading = config->reading || !config->active;
202
182 if (block == CMD_BLOCK_INPUT) { 203 if (block == CMD_BLOCK_INPUT) {
183 res = bsearch(&d, input_handlers, 204 // input commands can run in either context
205 return bsearch(&d, input_handlers,
184 sizeof(input_handlers) / sizeof(struct cmd_handler), 206 sizeof(input_handlers) / sizeof(struct cmd_handler),
185 sizeof(struct cmd_handler), handler_compare); 207 sizeof(struct cmd_handler), handler_compare);
186 } else if (block == CMD_BLOCK_SEAT) { 208 } else if (block == CMD_BLOCK_SEAT) {
187 res = bsearch(&d, seat_handlers, 209 // seat commands can run in either context
210 return bsearch(&d, seat_handlers,
188 sizeof(seat_handlers) / sizeof(struct cmd_handler), 211 sizeof(seat_handlers) / sizeof(struct cmd_handler),
189 sizeof(struct cmd_handler), handler_compare); 212 sizeof(struct cmd_handler), handler_compare);
190 } else { 213 }
191 res = bsearch(&d, handlers, 214
192 sizeof(handlers) / sizeof(struct cmd_handler), 215 if (!config_loading) {
216 res = bsearch(&d, command_handlers,
217 sizeof(command_handlers) / sizeof(struct cmd_handler),
193 sizeof(struct cmd_handler), handler_compare); 218 sizeof(struct cmd_handler), handler_compare);
219
220 if (res) {
221 return res;
222 }
223 }
224
225 if (config->reading) {
226 res = bsearch(&d, config_handlers,
227 sizeof(config_handlers) / sizeof(struct cmd_handler),
228 sizeof(struct cmd_handler), handler_compare);
229
230 if (res) {
231 return res;
232 }
194 } 233 }
195 234
235 res = bsearch(&d, handlers,
236 sizeof(handlers) / sizeof(struct cmd_handler),
237 sizeof(struct cmd_handler), handler_compare);
238
196 return res; 239 return res;
197} 240}
198 241
199struct cmd_results *handle_command(char *_exec) { 242struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
200 // Even though this function will process multiple commands we will only 243 // Even though this function will process multiple commands we will only
201 // return the last error, if any (for now). (Since we have access to an 244 // return the last error, if any (for now). (Since we have access to an
202 // error string we could e.g. concatenate all errors there.) 245 // error string we could e.g. concatenate all errors there.)
@@ -207,6 +250,16 @@ struct cmd_results *handle_command(char *_exec) {
207 char *cmd; 250 char *cmd;
208 list_t *containers = NULL; 251 list_t *containers = NULL;
209 252
253 if (seat == NULL) {
254 // passing a NULL seat means we just pick the default seat
255 seat = sway_input_manager_get_default_seat(input_manager);
256 if (!sway_assert(seat, "could not find a seat to run the command on")) {
257 return NULL;
258 }
259 }
260
261 config->handler_context.seat = seat;
262
210 head = exec; 263 head = exec;
211 do { 264 do {
212 // Extract criteria (valid for this command list only). 265 // Extract criteria (valid for this command list only).
@@ -276,23 +329,22 @@ struct cmd_results *handle_command(char *_exec) {
276 if (!has_criteria) { 329 if (!has_criteria) {
277 // without criteria, the command acts upon the focused 330 // without criteria, the command acts upon the focused
278 // container 331 // container
279 struct sway_seat *seat = config->handler_context.seat; 332 config->handler_context.current_container =
280 if (!seat) { 333 sway_seat_get_focus_inactive(seat, &root_container);
281 seat = sway_input_manager_get_default_seat(input_manager); 334 if (!sway_assert(config->handler_context.current_container,
335 "could not get focus-inactive for root container")) {
336 return NULL;
282 } 337 }
283 if (seat) { 338 struct cmd_results *res = handler->handle(argc-1, argv+1);
284 config->handler_context.current_container = seat->focus; 339 if (res->status != CMD_SUCCESS) {
285 struct cmd_results *res = handler->handle(argc-1, argv+1); 340 free_argv(argc, argv);
286 if (res->status != CMD_SUCCESS) { 341 if (results) {
287 free_argv(argc, argv); 342 free_cmd_results(results);
288 if (results) {
289 free_cmd_results(results);
290 }
291 results = res;
292 goto cleanup;
293 } 343 }
294 free_cmd_results(res); 344 results = res;
345 goto cleanup;
295 } 346 }
347 free_cmd_results(res);
296 } else { 348 } else {
297 for (int i = 0; i < containers->length; ++i) { 349 for (int i = 0; i < containers->length; ++i) {
298 config->handler_context.current_container = containers->items[i]; 350 config->handler_context.current_container = containers->items[i];
@@ -319,13 +371,13 @@ cleanup:
319 return results; 371 return results;
320} 372}
321 373
322// this is like handle_command above, except: 374// this is like execute_command above, except:
323// 1) it ignores empty commands (empty lines) 375// 1) it ignores empty commands (empty lines)
324// 2) it does variable substitution 376// 2) it does variable substitution
325// 3) it doesn't split commands (because the multiple commands are supposed to 377// 3) it doesn't split commands (because the multiple commands are supposed to
326// be chained together) 378// be chained together)
327// 4) handle_command handles all state internally while config_command has some 379// 4) execute_command handles all state internally while config_command has
328// state handled outside (notably the block mode, in read_config) 380// some state handled outside (notably the block mode, in read_config)
329struct cmd_results *config_command(char *exec, enum cmd_status block) { 381struct cmd_results *config_command(char *exec, enum cmd_status block) {
330 struct cmd_results *results = NULL; 382 struct cmd_results *results = NULL;
331 int argc; 383 int argc;
diff --git a/sway/commands/exit.c b/sway/commands/exit.c
index 4bb6a97b..d5353c20 100644
--- a/sway/commands/exit.c
+++ b/sway/commands/exit.c
@@ -6,9 +6,6 @@ void sway_terminate(int exit_code);
6 6
7struct cmd_results *cmd_exit(int argc, char **argv) { 7struct cmd_results *cmd_exit(int argc, char **argv) {
8 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
9 if (config->reading) {
10 return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
11 }
12 if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) { 9 if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
13 return error; 10 return error;
14 } 11 }
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
new file mode 100644
index 00000000..f1a8078f
--- /dev/null
+++ b/sway/commands/focus.c
@@ -0,0 +1,59 @@
1#include <strings.h>
2#include <wlr/util/log.h>
3#include "log.h"
4#include "sway/input/input-manager.h"
5#include "sway/input/seat.h"
6#include "sway/view.h"
7#include "sway/commands.h"
8
9static bool parse_movement_direction(const char *name, enum movement_direction *out) {
10 if (strcasecmp(name, "left") == 0) {
11 *out = MOVE_LEFT;
12 } else if (strcasecmp(name, "right") == 0) {
13 *out = MOVE_RIGHT;
14 } else if (strcasecmp(name, "up") == 0) {
15 *out = MOVE_UP;
16 } else if (strcasecmp(name, "down") == 0) {
17 *out = MOVE_DOWN;
18 } else if (strcasecmp(name, "parent") == 0) {
19 *out = MOVE_PARENT;
20 } else if (strcasecmp(name, "child") == 0) {
21 *out = MOVE_CHILD;
22 } else if (strcasecmp(name, "next") == 0) {
23 *out = MOVE_NEXT;
24 } else if (strcasecmp(name, "prev") == 0) {
25 *out = MOVE_PREV;
26 } else {
27 return false;
28 }
29
30 return true;
31}
32
33struct cmd_results *cmd_focus(int argc, char **argv) {
34 swayc_t *con = config->handler_context.current_container;
35 struct sway_seat *seat = config->handler_context.seat;
36 if (con->type < C_WORKSPACE) {
37 return cmd_results_new(CMD_FAILURE, "focus",
38 "Command 'focus' cannot be used above the workspace level");
39 }
40
41 if (argc == 0) {
42 sway_seat_set_focus(seat, con);
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
44 }
45
46 // TODO mode_toggle
47 enum movement_direction direction = 0;
48 if (!parse_movement_direction(argv[0], &direction)) {
49 return cmd_results_new(CMD_INVALID, "focus",
50 "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
51 }
52
53 swayc_t *next_focus = get_swayc_in_direction(con, seat, direction);
54 if (next_focus) {
55 sway_seat_set_focus(seat, next_focus);
56 }
57
58 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
59}
diff --git a/sway/commands/kill.c b/sway/commands/kill.c
index cebf7f3c..f408ce2a 100644
--- a/sway/commands/kill.c
+++ b/sway/commands/kill.c
@@ -6,15 +6,12 @@
6#include "sway/commands.h" 6#include "sway/commands.h"
7 7
8struct cmd_results *cmd_kill(int argc, char **argv) { 8struct cmd_results *cmd_kill(int argc, char **argv) {
9 if (config->reading) {
10 return cmd_results_new(CMD_FAILURE, "kill",
11 "Command 'kill' cannot be used in the config file");
12 }
13 enum swayc_types type = config->handler_context.current_container->type; 9 enum swayc_types type = config->handler_context.current_container->type;
14 if (type != C_VIEW || type != C_CONTAINER) { 10 if (type != C_VIEW && type != C_CONTAINER) {
15 return cmd_results_new(CMD_INVALID, NULL, 11 return cmd_results_new(CMD_INVALID, NULL,
16 "Can only kill views and containers with this command"); 12 "Can only kill views and containers with this command");
17 } 13 }
14
18 // TODO close arbitrary containers without a view 15 // TODO close arbitrary containers without a view
19 struct sway_view *view = 16 struct sway_view *view =
20 config->handler_context.current_container->sway_view; 17 config->handler_context.current_container->sway_view;
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
new file mode 100644
index 00000000..b0fc5d66
--- /dev/null
+++ b/sway/commands/layout.c
@@ -0,0 +1,56 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/container.h"
5#include "sway/layout.h"
6#include "log.h"
7
8struct cmd_results *cmd_layout(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
11 return error;
12 }
13 swayc_t *parent = config->handler_context.current_container;
14
15 // TODO: floating
16 /*
17 if (parent->is_floating) {
18 return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
19 }
20 */
21
22 while (parent->type == C_VIEW) {
23 parent = parent->parent;
24 }
25
26 // TODO: stacks and tabs
27
28 if (strcasecmp(argv[0], "default") == 0) {
29 swayc_change_layout(parent, parent->prev_layout);
30 if (parent->layout == L_NONE) {
31 swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT);
32 swayc_change_layout(parent, default_layout(output));
33 }
34 } else {
35 if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
36 parent->prev_layout = parent->layout;
37 }
38
39 if (strcasecmp(argv[0], "splith") == 0) {
40 swayc_change_layout(parent, L_HORIZ);
41 } else if (strcasecmp(argv[0], "splitv") == 0) {
42 swayc_change_layout(parent, L_VERT);
43 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
44 if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
45 || parent->workspace_layout == L_HORIZ)) {
46 swayc_change_layout(parent, L_VERT);
47 } else {
48 swayc_change_layout(parent, L_HORIZ);
49 }
50 }
51 }
52
53 arrange_windows(parent, parent->width, parent->height);
54
55 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
56}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 419c7de3..d54d40db 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -4,9 +4,6 @@
4 4
5struct cmd_results *cmd_reload(int argc, char **argv) { 5struct cmd_results *cmd_reload(int argc, char **argv) {
6 struct cmd_results *error = NULL; 6 struct cmd_results *error = NULL;
7 if (config->reading) {
8 return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
9 }
10 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { 7 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
11 return error; 8 return error;
12 } 9 }
diff --git a/sway/commands/set.c b/sway/commands/set.c
index 856c73e7..84e9b792 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -27,7 +27,6 @@ void free_sway_variable(struct sway_variable *var) {
27struct cmd_results *cmd_set(int argc, char **argv) { 27struct cmd_results *cmd_set(int argc, char **argv) {
28 char *tmp; 28 char *tmp;
29 struct cmd_results *error = NULL; 29 struct cmd_results *error = NULL;
30 if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
31 if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) { 30 if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
32 return error; 31 return error;
33 } 32 }
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 12984ed4..fa891398 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -90,7 +90,8 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
90 free(name); 90 free(name);
91 } 91 }
92 workspace_switch(ws); 92 workspace_switch(ws);
93 current_container = config->handler_context.seat->focus; 93 current_container =
94 sway_seat_get_focus(config->handler_context.seat);
94 swayc_t *new_output = swayc_parent_by_type(current_container, C_OUTPUT); 95 swayc_t *new_output = swayc_parent_by_type(current_container, C_OUTPUT);
95 96
96 if (config->mouse_warping && old_output != new_output) { 97 if (config->mouse_warping && old_output != new_output) {
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index a650665f..63420d0c 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -46,57 +46,22 @@ static void render_surface(struct wlr_surface *surface,
46 int height = surface->current->height; 46 int height = surface->current->height;
47 int render_width = width * wlr_output->scale; 47 int render_width = width * wlr_output->scale;
48 int render_height = height * wlr_output->scale; 48 int render_height = height * wlr_output->scale;
49 double ox = lx, oy = ly; 49 int owidth, oheight;
50 wlr_output_layout_output_coords(layout, wlr_output, &ox, &oy); 50 wlr_output_effective_resolution(wlr_output, &owidth, &oheight);
51 ox *= wlr_output->scale;
52 oy *= wlr_output->scale;
53 51
54 struct wlr_box render_box = { 52 // FIXME: view coords are inconsistently assumed to be in output or layout coords
55 .x = lx, .y = ly, 53 struct wlr_box layout_box = {
54 .x = lx + wlr_output->lx, .y = ly + wlr_output->ly,
56 .width = render_width, .height = render_height, 55 .width = render_width, .height = render_height,
57 }; 56 };
58 if (wlr_output_layout_intersects(layout, wlr_output, &render_box)) { 57 if (wlr_output_layout_intersects(layout, wlr_output, &layout_box)) {
58 struct wlr_box render_box = {
59 .x = lx, .y = ly,
60 .width = render_width, .height = render_height
61 };
59 float matrix[16]; 62 float matrix[16];
60 63 wlr_matrix_project_box(&matrix, &render_box,
61 float translate_center[16]; 64 surface->current->transform, 0, &wlr_output->transform_matrix);
62 wlr_matrix_translate(&translate_center,
63 (int)ox + render_width / 2, (int)oy + render_height / 2, 0);
64
65 float rotate[16];
66 wlr_matrix_rotate(&rotate, rotation);
67
68 float translate_origin[16];
69 wlr_matrix_translate(&translate_origin, -render_width / 2,
70 -render_height / 2, 0);
71
72 float scale[16];
73 wlr_matrix_scale(&scale, render_width, render_height, 1);
74
75 float transform[16];
76 wlr_matrix_mul(&translate_center, &rotate, &transform);
77 wlr_matrix_mul(&transform, &translate_origin, &transform);
78 wlr_matrix_mul(&transform, &scale, &transform);
79
80 if (surface->current->transform != WL_OUTPUT_TRANSFORM_NORMAL) {
81 float surface_translate_center[16];
82 wlr_matrix_translate(&surface_translate_center, 0.5, 0.5, 0);
83
84 float surface_transform[16];
85 wlr_matrix_transform(surface_transform,
86 wlr_output_transform_invert(surface->current->transform));
87
88 float surface_translate_origin[16];
89 wlr_matrix_translate(&surface_translate_origin, -0.5, -0.5, 0);
90
91 wlr_matrix_mul(&transform, &surface_translate_center,
92 &transform);
93 wlr_matrix_mul(&transform, &surface_transform, &transform);
94 wlr_matrix_mul(&transform, &surface_translate_origin,
95 &transform);
96 }
97
98 wlr_matrix_mul(&wlr_output->transform_matrix, &transform, &matrix);
99
100 wlr_render_with_matrix(server.renderer, surface->texture, 65 wlr_render_with_matrix(server.renderer, surface->texture,
101 &matrix); 66 &matrix);
102 67
@@ -125,8 +90,9 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
125 double width = surface->surface->current->width; 90 double width = surface->surface->current->width;
126 double height = surface->surface->current->height; 91 double height = surface->surface->current->height;
127 92
128 struct wlr_xdg_surface_v6 *popup; 93 struct wlr_xdg_popup_v6 *popup_state;
129 wl_list_for_each(popup, &surface->popups, popup_link) { 94 wl_list_for_each(popup_state, &surface->popups, link) {
95 struct wlr_xdg_surface_v6 *popup = popup_state->base;
130 if (!popup->configured) { 96 if (!popup->configured) {
131 continue; 97 continue;
132 } 98 }
@@ -215,11 +181,20 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
215 struct sway_output *soutput = wl_container_of(listener, soutput, frame); 181 struct sway_output *soutput = wl_container_of(listener, soutput, frame);
216 struct wlr_output *wlr_output = data; 182 struct wlr_output *wlr_output = data;
217 struct sway_server *server = soutput->server; 183 struct sway_server *server = soutput->server;
184 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
185 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
186 wlr_renderer_clear(renderer, &clear_color);
218 187
219 wlr_output_make_current(wlr_output); 188 int buffer_age = -1;
189 wlr_output_make_current(wlr_output, &buffer_age);
220 wlr_renderer_begin(server->renderer, wlr_output); 190 wlr_renderer_begin(server->renderer, wlr_output);
221 191
222 swayc_t *workspace = soutput->swayc->focused; 192 struct sway_seat *seat = input_manager_current_seat(input_manager);
193 swayc_t *focus = sway_seat_get_focus_inactive(seat, soutput->swayc);
194 swayc_t *workspace = (focus->type == C_WORKSPACE ?
195 focus :
196 swayc_parent_by_type(focus, C_WORKSPACE));
197
223 swayc_descendants_of_type(workspace, C_VIEW, output_frame_view, soutput); 198 swayc_descendants_of_type(workspace, C_VIEW, output_frame_view, soutput);
224 199
225 // render unmanaged views on top 200 // render unmanaged views on top
@@ -236,15 +211,23 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
236 } 211 }
237 212
238 wlr_renderer_end(server->renderer); 213 wlr_renderer_end(server->renderer);
239 wlr_output_swap_buffers(wlr_output); 214 wlr_output_swap_buffers(wlr_output, &soutput->last_frame, NULL);
240 215
241 struct timespec now; 216 struct timespec now;
242 clock_gettime(CLOCK_MONOTONIC, &now); 217 clock_gettime(CLOCK_MONOTONIC, &now);
243 soutput->last_frame = now; 218 soutput->last_frame = now;
244} 219}
245 220
246void output_add_notify(struct wl_listener *listener, void *data) { 221static void handle_output_destroy(struct wl_listener *listener, void *data) {
247 struct sway_server *server = wl_container_of(listener, server, output_add); 222 struct sway_output *output = wl_container_of(listener, output, output_destroy);
223 struct wlr_output *wlr_output = data;
224 wlr_log(L_DEBUG, "Output %p %s removed", wlr_output, wlr_output->name);
225
226 destroy_output(output->swayc);
227}
228
229void handle_new_output(struct wl_listener *listener, void *data) {
230 struct sway_server *server = wl_container_of(listener, server, new_output);
248 struct wlr_output *wlr_output = data; 231 struct wlr_output *wlr_output = data;
249 wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 232 wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
250 233
@@ -269,27 +252,11 @@ void output_add_notify(struct wl_listener *listener, void *data) {
269 252
270 sway_input_manager_configure_xcursor(input_manager); 253 sway_input_manager_configure_xcursor(input_manager);
271 254
272 output->frame.notify = output_frame_notify;
273 wl_signal_add(&wlr_output->events.frame, &output->frame); 255 wl_signal_add(&wlr_output->events.frame, &output->frame);
274} 256 output->frame.notify = output_frame_notify;
275
276void output_remove_notify(struct wl_listener *listener, void *data) {
277 struct sway_server *server = wl_container_of(listener, server, output_remove);
278 struct wlr_output *wlr_output = data;
279 wlr_log(L_DEBUG, "Output %p %s removed", wlr_output, wlr_output->name);
280 257
281 swayc_t *output_container = NULL; 258 wl_signal_add(&wlr_output->events.destroy, &output->output_destroy);
282 for (int i = 0 ; i < root_container.children->length; ++i) { 259 output->output_destroy.notify = handle_output_destroy;
283 swayc_t *child = root_container.children->items[i];
284 if (child->type == C_OUTPUT &&
285 child->sway_output->wlr_output == wlr_output) {
286 output_container = child;
287 break;
288 }
289 }
290 if (!output_container) {
291 return;
292 }
293 260
294 destroy_output(output_container); 261 arrange_windows(&root_container, -1, -1);
295} 262}
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index ca56a9c0..b44d9e54 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -135,7 +135,8 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
135 wl_signal_add(&xdg_surface->events.destroy, &sway_surface->destroy); 135 wl_signal_add(&xdg_surface->events.destroy, &sway_surface->destroy);
136 136
137 struct sway_seat *seat = input_manager_current_seat(input_manager); 137 struct sway_seat *seat = input_manager_current_seat(input_manager);
138 swayc_t *cont = new_view(seat->focus, sway_view); 138 swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
139 swayc_t *cont = new_view(focus, sway_view);
139 sway_view->swayc = cont; 140 sway_view->swayc = cont;
140 141
141 arrange_windows(cont->parent, -1, -1); 142 arrange_windows(cont->parent, -1, -1);
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index d789c7eb..27c2c72e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -160,9 +160,34 @@ static void sway_input_manager_libinput_config_pointer(struct sway_input_device
160 } 160 }
161} 161}
162 162
163static void input_add_notify(struct wl_listener *listener, void *data) { 163static void handle_device_destroy(struct wl_listener *listener, void *data) {
164 struct wlr_input_device *device = data;
165
166 struct sway_input_device *input_device =
167 input_sway_device_from_wlr(input_manager, device);
168
169 if (!sway_assert(input_device, "could not find sway device")) {
170 return;
171 }
172
173 wlr_log(L_DEBUG, "removing device: '%s'",
174 input_device->identifier);
175
176 struct sway_seat *seat = NULL;
177 wl_list_for_each(seat, &input_manager->seats, link) {
178 sway_seat_remove_device(seat, input_device);
179 }
180
181 wl_list_remove(&input_device->link);
182 wl_list_remove(&input_device->device_destroy.link);
183 free_input_config(input_device->config);
184 free(input_device->identifier);
185 free(input_device);
186}
187
188static void handle_new_input(struct wl_listener *listener, void *data) {
164 struct sway_input_manager *input = 189 struct sway_input_manager *input =
165 wl_container_of(listener, input, input_add); 190 wl_container_of(listener, input, new_input);
166 struct wlr_input_device *device = data; 191 struct wlr_input_device *device = data;
167 192
168 struct sway_input_device *input_device = 193 struct sway_input_device *input_device =
@@ -226,32 +251,9 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
226 "device '%s' is not configured on any seats", 251 "device '%s' is not configured on any seats",
227 input_device->identifier); 252 input_device->identifier);
228 } 253 }
229}
230
231static void input_remove_notify(struct wl_listener *listener, void *data) {
232 struct sway_input_manager *input =
233 wl_container_of(listener, input, input_remove);
234 struct wlr_input_device *device = data;
235
236 struct sway_input_device *input_device =
237 input_sway_device_from_wlr(input, device);
238
239 if (!sway_assert(input_device, "could not find sway device")) {
240 return;
241 }
242
243 wlr_log(L_DEBUG, "removing device: '%s'",
244 input_device->identifier);
245
246 struct sway_seat *seat = NULL;
247 wl_list_for_each(seat, &input->seats, link) {
248 sway_seat_remove_device(seat, input_device);
249 }
250 254
251 wl_list_remove(&input_device->link); 255 wl_signal_add(&device->events.destroy, &input_device->device_destroy);
252 free_input_config(input_device->config); 256 input_device->device_destroy.notify = handle_device_destroy;
253 free(input_device->identifier);
254 free(input_device);
255} 257}
256 258
257struct sway_input_manager *sway_input_manager_create( 259struct sway_input_manager *sway_input_manager_create(
@@ -269,11 +271,8 @@ struct sway_input_manager *sway_input_manager_create(
269 // create the default seat 271 // create the default seat
270 input_manager_get_seat(input, default_seat); 272 input_manager_get_seat(input, default_seat);
271 273
272 input->input_add.notify = input_add_notify; 274 input->new_input.notify = handle_new_input;
273 wl_signal_add(&server->backend->events.input_add, &input->input_add); 275 wl_signal_add(&server->backend->events.new_input, &input->new_input);
274
275 input->input_remove.notify = input_remove_notify;
276 wl_signal_add(&server->backend->events.input_remove, &input->input_remove);
277 276
278 return input; 277 return input;
279} 278}
@@ -282,7 +281,7 @@ bool sway_input_manager_has_focus(struct sway_input_manager *input,
282 swayc_t *container) { 281 swayc_t *container) {
283 struct sway_seat *seat = NULL; 282 struct sway_seat *seat = NULL;
284 wl_list_for_each(seat, &input->seats, link) { 283 wl_list_for_each(seat, &input->seats, link) {
285 if (seat->focus == container) { 284 if (sway_seat_get_focus(seat) == container) {
286 return true; 285 return true;
287 } 286 }
288 } 287 }
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 6dc57d46..99685052 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -95,7 +95,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard,
95 binding->command); 95 binding->command);
96 config_clear_handler_context(config); 96 config_clear_handler_context(config);
97 config->handler_context.seat = keyboard->seat_device->sway_seat; 97 config->handler_context.seat = keyboard->seat_device->sway_seat;
98 struct cmd_results *results = handle_command(binding->command); 98 struct cmd_results *results = execute_command(binding->command, NULL);
99 if (results->status != CMD_SUCCESS) { 99 if (results->status != CMD_SUCCESS) {
100 wlr_log(L_DEBUG, "could not run command for binding: %s", 100 wlr_log(L_DEBUG, "could not run command for binding: %s",
101 binding->command); 101 binding->command);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 5e87986d..648e7914 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -32,6 +32,81 @@ void sway_seat_destroy(struct sway_seat *seat) {
32 wlr_seat_destroy(seat->wlr_seat); 32 wlr_seat_destroy(seat->wlr_seat);
33} 33}
34 34
35static void handle_seat_container_destroy(struct wl_listener *listener,
36 void *data) {
37 struct sway_seat_container *seat_con =
38 wl_container_of(listener, seat_con, destroy);
39 struct sway_seat *seat = seat_con->seat;
40 swayc_t *con = seat_con->container;
41
42 bool is_focus = (sway_seat_get_focus(seat) == con);
43
44 wl_list_remove(&seat_con->link);
45
46 if (is_focus) {
47 // pick next focus
48 sway_seat_set_focus(seat, NULL);
49 swayc_t *next = sway_seat_get_focus_inactive(seat, con->parent);
50 if (next == NULL) {
51 next = con->parent;
52 }
53 sway_seat_set_focus(seat, next);
54 }
55
56 wl_list_remove(&seat_con->destroy.link);
57
58 free(seat_con);
59}
60
61static struct sway_seat_container *seat_container_from_container(
62 struct sway_seat *seat, swayc_t *con) {
63 if (con->type < C_WORKSPACE) {
64 // these don't get seat containers ever
65 return NULL;
66 }
67
68 struct sway_seat_container *seat_con = NULL;
69 wl_list_for_each(seat_con, &seat->focus_stack, link) {
70 if (seat_con->container == con) {
71 return seat_con;
72 }
73 }
74
75 seat_con = calloc(1, sizeof(struct sway_seat_container));
76 if (seat_con == NULL) {
77 wlr_log(L_ERROR, "could not allocate seat container");
78 return NULL;
79 }
80
81 seat_con->container = con;
82 seat_con->seat = seat;
83 wl_list_insert(seat->focus_stack.prev, &seat_con->link);
84 wl_signal_add(&con->events.destroy, &seat_con->destroy);
85 seat_con->destroy.notify = handle_seat_container_destroy;
86
87 return seat_con;
88}
89
90static void handle_new_container(struct wl_listener *listener, void *data) {
91 struct sway_seat *seat = wl_container_of(listener, seat, new_container);
92 swayc_t *con = data;
93 seat_container_from_container(seat, con);
94}
95
96static void collect_focus_iter(swayc_t *con, void *data) {
97 struct sway_seat *seat = data;
98 if (con->type > C_WORKSPACE) {
99 return;
100 }
101 struct sway_seat_container *seat_con =
102 seat_container_from_container(seat, con);
103 if (!seat_con) {
104 return;
105 }
106 wl_list_remove(&seat_con->link);
107 wl_list_insert(&seat->focus_stack, &seat_con->link);
108}
109
35struct sway_seat *sway_seat_create(struct sway_input_manager *input, 110struct sway_seat *sway_seat_create(struct sway_input_manager *input,
36 const char *seat_name) { 111 const char *seat_name) {
37 struct sway_seat *seat = calloc(1, sizeof(struct sway_seat)); 112 struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
@@ -52,6 +127,15 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
52 return NULL; 127 return NULL;
53 } 128 }
54 129
130 // init the focus stack
131 wl_list_init(&seat->focus_stack);
132
133 container_for_each_bfs(&root_container, collect_focus_iter, seat);
134
135 wl_signal_add(&root_container.sway_root->events.new_container,
136 &seat->new_container);
137 seat->new_container.notify = handle_new_container;
138
55 seat->input = input; 139 seat->input = input;
56 wl_list_init(&seat->devices); 140 wl_list_init(&seat->devices);
57 141
@@ -81,13 +165,14 @@ static void seat_configure_keyboard(struct sway_seat *seat,
81 struct wlr_keyboard *wlr_keyboard = seat_device->input_device->wlr_device->keyboard; 165 struct wlr_keyboard *wlr_keyboard = seat_device->input_device->wlr_device->keyboard;
82 sway_keyboard_configure(seat_device->keyboard); 166 sway_keyboard_configure(seat_device->keyboard);
83 wlr_seat_set_keyboard(seat->wlr_seat, 167 wlr_seat_set_keyboard(seat->wlr_seat,
84 seat_device->input_device->wlr_device); 168 seat_device->input_device->wlr_device);
85 if (seat->focus && seat->focus->type == C_VIEW) { 169 swayc_t *focus = sway_seat_get_focus(seat);
170 if (focus && focus->type == C_VIEW) {
86 // force notify reenter to pick up the new configuration 171 // force notify reenter to pick up the new configuration
87 wlr_seat_keyboard_clear_focus(seat->wlr_seat); 172 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
88 wlr_seat_keyboard_notify_enter(seat->wlr_seat, 173 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
89 seat->focus->sway_view->surface, wlr_keyboard->keycodes, 174 focus->sway_view->surface, wlr_keyboard->keycodes,
90 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); 175 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
91 } 176 }
92} 177}
93 178
@@ -204,47 +289,83 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
204 seat->cursor->cursor->y); 289 seat->cursor->cursor->y);
205} 290}
206 291
207static void handle_focus_destroy(struct wl_listener *listener, void *data) {
208 struct sway_seat *seat = wl_container_of(listener, seat, focus_destroy);
209 swayc_t *container = data;
210 sway_seat_set_focus(seat, container->parent);
211}
212
213void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { 292void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
214 swayc_t *last_focus = seat->focus; 293 swayc_t *last_focus = sway_seat_get_focus(seat);
215 294
216 if (last_focus == container) { 295 if (container && last_focus == container) {
217 return; 296 return;
218 } 297 }
219 298
220 if (last_focus && last_focus->type == C_VIEW) { 299 if (container) {
221 wl_list_remove(&seat->focus_destroy.link); 300 struct sway_seat_container *seat_con =
222 } 301 seat_container_from_container(seat, container);
302 if (!seat_con) {
303 return;
304 }
223 305
224 if (container && container->type == C_VIEW) { 306 wl_list_remove(&seat_con->link);
225 struct sway_view *view = container->sway_view; 307 wl_list_insert(&seat->focus_stack, &seat_con->link);
226 view_set_activated(view, true); 308
227 wl_signal_add(&container->events.destroy, &seat->focus_destroy); 309 if (container->type == C_VIEW) {
228 seat->focus_destroy.notify = handle_focus_destroy; 310 struct sway_view *view = container->sway_view;
229 311 view_set_activated(view, true);
230 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); 312 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
231 if (keyboard) { 313 if (keyboard) {
232 wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, 314 wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface,
233 keyboard->keycodes, keyboard->num_keycodes, 315 keyboard->keycodes, keyboard->num_keycodes,
234 &keyboard->modifiers); 316 &keyboard->modifiers);
235 } else { 317 } else {
236 wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, 318 wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface,
237 NULL, 0, NULL); 319 NULL, 0, NULL);
320 }
238 } 321 }
239 } 322 }
240 323
241 seat->focus = container;
242
243 if (last_focus && last_focus->type == C_VIEW && 324 if (last_focus && last_focus->type == C_VIEW &&
244 !sway_input_manager_has_focus(seat->input, last_focus)) { 325 !sway_input_manager_has_focus(seat->input, last_focus)) {
245 struct sway_view *view = last_focus->sway_view; 326 struct sway_view *view = last_focus->sway_view;
246 view_set_activated(view, false); 327 view_set_activated(view, false);
247 } 328 }
329
330 seat->has_focus = (container != NULL);
331}
332
333swayc_t *sway_seat_get_focus_inactive(struct sway_seat *seat, swayc_t *container) {
334 struct sway_seat_container *current = NULL;
335 swayc_t *parent = NULL;
336 wl_list_for_each(current, &seat->focus_stack, link) {
337 parent = current->container->parent;
338
339 if (current->container == container) {
340 return current->container;
341 }
342
343 while (parent) {
344 if (parent == container) {
345 return current->container;
346 }
347 parent = parent->parent;
348 }
349 }
350
351 return NULL;
352}
353
354swayc_t *sway_seat_get_focus(struct sway_seat *seat) {
355 if (!seat->has_focus) {
356 return NULL;
357 }
358 return sway_seat_get_focus_inactive(seat, &root_container);
359}
360
361swayc_t *sway_seat_get_focus_by_type(struct sway_seat *seat,
362 enum swayc_types type) {
363 swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
364 if (focus->type == type) {
365 return focus;
366 }
367
368 return swayc_parent_by_type(focus, type);
248} 369}
249 370
250void sway_seat_set_config(struct sway_seat *seat, 371void sway_seat_set_config(struct sway_seat *seat,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index bab9a201..f0afdc9f 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -74,8 +74,8 @@ static void ipc_json_describe_output(swayc_t *container, json_object *object) {
74 json_object_object_add(object, "refresh", json_object_new_int(wlr_output->refresh)); 74 json_object_object_add(object, "refresh", json_object_new_int(wlr_output->refresh));
75 json_object_object_add(object, "transform", 75 json_object_object_add(object, "transform",
76 json_object_new_string(ipc_json_get_output_transform(wlr_output->transform))); 76 json_object_new_string(ipc_json_get_output_transform(wlr_output->transform)));
77 json_object_object_add(object, "current_workspace", 77 // TODO WLR need to set "current_workspace" to the currently focused
78 (container->focused) ? json_object_new_string(container->focused->name) : NULL); 78 // workspace in a way that makes sense with multiseat
79} 79}
80 80
81static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) { 81static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) {
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index a16a2b80..4c0953e8 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -336,7 +336,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
336 case IPC_COMMAND: 336 case IPC_COMMAND:
337 { 337 {
338 config_clear_handler_context(config); 338 config_clear_handler_context(config);
339 struct cmd_results *results = handle_command(buf); 339 struct cmd_results *results = execute_command(buf, NULL);
340 const char *json = cmd_results_to_json(results); 340 const char *json = cmd_results_to_json(results);
341 char reply[256]; 341 char reply[256];
342 int length = snprintf(reply, sizeof(reply), "%s", json); 342 int length = snprintf(reply, sizeof(reply), "%s", json);
diff --git a/sway/meson.build b/sway/meson.build
index 271d4a99..26e56ad2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -10,9 +10,11 @@ sway_sources = files(
10 'commands/exit.c', 10 'commands/exit.c',
11 'commands/exec.c', 11 'commands/exec.c',
12 'commands/exec_always.c', 12 'commands/exec_always.c',
13 'commands/focus.c',
13 'commands/kill.c', 14 'commands/kill.c',
14 'commands/include.c', 15 'commands/include.c',
15 'commands/input.c', 16 'commands/input.c',
17 'commands/layout.c',
16 'commands/seat.c', 18 'commands/seat.c',
17 'commands/seat/attach.c', 19 'commands/seat/attach.c',
18 'commands/seat/fallback.c', 20 'commands/seat/fallback.c',
diff --git a/sway/server.c b/sway/server.c
index b5eb510b..495769ee 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -22,7 +22,7 @@ static void server_ready(struct wl_listener *listener, void *data) {
22 config->active = true; 22 config->active = true;
23 while (config->cmd_queue->length) { 23 while (config->cmd_queue->length) {
24 char *line = config->cmd_queue->items[0]; 24 char *line = config->cmd_queue->items[0];
25 struct cmd_results *res = handle_command(line); 25 struct cmd_results *res = execute_command(line, NULL);
26 if (res->status != CMD_SUCCESS) { 26 if (res->status != CMD_SUCCESS) {
27 wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error); 27 wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error);
28 } 28 }
@@ -48,12 +48,8 @@ bool server_init(struct sway_server *server) {
48 server->data_device_manager = 48 server->data_device_manager =
49 wlr_data_device_manager_create(server->wl_display); 49 wlr_data_device_manager_create(server->wl_display);
50 50
51 server->output_add.notify = output_add_notify; 51 server->new_output.notify = handle_new_output;
52 wl_signal_add(&server->backend->events.output_add, &server->output_add); 52 wl_signal_add(&server->backend->events.new_output, &server->new_output);
53
54 server->output_remove.notify = output_remove_notify;
55 wl_signal_add(&server->backend->events.output_remove,
56 &server->output_remove);
57 53
58 server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display); 54 server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
59 wl_signal_add(&server->xdg_shell_v6->events.new_surface, 55 wl_signal_add(&server->xdg_shell_v6->events.new_surface,
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 48aabd86..fafbdb03 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -17,6 +17,21 @@
17#include "sway/workspace.h" 17#include "sway/workspace.h"
18#include "log.h" 18#include "log.h"
19 19
20static list_t *bfs_queue;
21
22static list_t *get_bfs_queue() {
23 if (!bfs_queue) {
24 bfs_queue = create_list();
25 if (!bfs_queue) {
26 wlr_log(L_ERROR, "could not allocate list for bfs queue");
27 return NULL;
28 }
29 }
30 bfs_queue->length = 0;
31
32 return bfs_queue;
33}
34
20swayc_t *swayc_by_test(swayc_t *container, 35swayc_t *swayc_by_test(swayc_t *container,
21 bool (*test)(swayc_t *view, void *data), void *data) { 36 bool (*test)(swayc_t *view, void *data), void *data) {
22 if (!container->children) { 37 if (!container->children) {
@@ -151,19 +166,16 @@ swayc_t *new_output(struct sway_output *sway_output) {
151 char *ws_name = workspace_next_name(output->name); 166 char *ws_name = workspace_next_name(output->name);
152 wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); 167 wlr_log(L_DEBUG, "Creating default workspace %s", ws_name);
153 swayc_t *ws = new_workspace(output, ws_name); 168 swayc_t *ws = new_workspace(output, ws_name);
154 output->focused = ws;
155 // Set each seat's focus if not already set 169 // Set each seat's focus if not already set
156 // TODO FOCUS: this is probably stupid, we shouldn't define focus in two
157 // places. We should probably put the active workspace on the sway_output
158 // struct instead of trying to do focus semantics like this
159 struct sway_seat *seat = NULL; 170 struct sway_seat *seat = NULL;
160 wl_list_for_each(seat, &input_manager->seats, link) { 171 wl_list_for_each(seat, &input_manager->seats, link) {
161 if (!seat->focus) { 172 if (!seat->has_focus) {
162 seat->focus = ws; 173 sway_seat_set_focus(seat, ws);
163 } 174 }
164 } 175 }
165 176
166 free(ws_name); 177 free(ws_name);
178 wl_signal_emit(&root_container.sway_root->events.new_container, output);
167 return output; 179 return output;
168} 180}
169 181
@@ -185,6 +197,7 @@ swayc_t *new_workspace(swayc_t *output, const char *name) {
185 197
186 add_child(output, workspace); 198 add_child(output, workspace);
187 sort_workspaces(output); 199 sort_workspaces(output);
200 wl_signal_emit(&root_container.sway_root->events.new_container, workspace);
188 return workspace; 201 return workspace;
189} 202}
190 203
@@ -207,9 +220,9 @@ swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view) {
207 add_child(sibling, swayc); 220 add_child(sibling, swayc);
208 } else { 221 } else {
209 // Regular case, create as sibling of current container 222 // Regular case, create as sibling of current container
210 // TODO WLR 223 add_sibling(sibling, swayc);
211 //add_sibling(sibling, swayc);
212 } 224 }
225 wl_signal_emit(&root_container.sway_root->events.new_container, swayc);
213 return swayc; 226 return swayc;
214} 227}
215 228
@@ -235,6 +248,8 @@ swayc_t *destroy_output(swayc_t *output) {
235 } 248 }
236 } 249 }
237 250
251 wl_list_remove(&output->sway_output->output_destroy.link);
252
238 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 253 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
239 free_swayc(output); 254 free_swayc(output);
240 255
@@ -273,7 +288,11 @@ swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
273 288
274swayc_t *swayc_at(swayc_t *parent, double lx, double ly, 289swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
275 struct wlr_surface **surface, double *sx, double *sy) { 290 struct wlr_surface **surface, double *sx, double *sy) {
276 list_t *queue = create_list(); 291 list_t *queue = get_bfs_queue();
292 if (!queue) {
293 return NULL;
294 }
295
277 list_add(queue, parent); 296 list_add(queue, parent);
278 297
279 swayc_t *swayc = NULL; 298 swayc_t *swayc = NULL;
@@ -313,7 +332,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
313 *sx = view_sx - popup_sx; 332 *sx = view_sx - popup_sx;
314 *sy = view_sy - popup_sy; 333 *sy = view_sy - popup_sy;
315 *surface = popup->surface; 334 *surface = popup->surface;
316 list_free(queue);
317 return swayc; 335 return swayc;
318 } 336 }
319 break; 337 break;
@@ -332,7 +350,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
332 *sx = view_sx - sub_x; 350 *sx = view_sx - sub_x;
333 *sy = view_sy - sub_y; 351 *sy = view_sy - sub_y;
334 *surface = subsurface->surface; 352 *surface = subsurface->surface;
335 list_free(queue);
336 return swayc; 353 return swayc;
337 } 354 }
338 355
@@ -344,7 +361,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
344 *sx = view_sx; 361 *sx = view_sx;
345 *sy = view_sy; 362 *sy = view_sy;
346 *surface = swayc->sway_view->surface; 363 *surface = swayc->sway_view->surface;
347 list_free(queue);
348 return swayc; 364 return swayc;
349 } 365 }
350 } else { 366 } else {
@@ -352,8 +368,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
352 } 368 }
353 } 369 }
354 370
355 list_free(queue);
356
357 return NULL; 371 return NULL;
358} 372}
359 373
@@ -378,3 +392,39 @@ void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), voi
378 f(container, data); 392 f(container, data);
379 } 393 }
380} 394}
395
396void container_for_each_bfs(swayc_t *con, void (*f)(swayc_t *con, void *data),
397 void *data) {
398 list_t *queue = get_bfs_queue();
399 if (!queue) {
400 return;
401 }
402
403 if (queue == NULL) {
404 wlr_log(L_ERROR, "could not allocate list");
405 return;
406 }
407
408 list_add(queue, con);
409
410 swayc_t *current = NULL;
411 while (queue->length) {
412 current = queue->items[0];
413 list_del(queue, 0);
414 f(current, data);
415 // TODO floating containers
416 list_cat(queue, current->children);
417 }
418}
419
420swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
421 if (container->type == C_WORKSPACE) {
422 container->workspace_layout = layout;
423 if (layout == L_HORIZ || layout == L_VERT) {
424 container->layout = layout;
425 }
426 } else {
427 container->layout = layout;
428 }
429 return container;
430}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 41ff81b2..3d04a1a7 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -10,6 +10,7 @@
10#include "sway/layout.h" 10#include "sway/layout.h"
11#include "sway/output.h" 11#include "sway/output.h"
12#include "sway/view.h" 12#include "sway/view.h"
13#include "sway/input/seat.h"
13#include "list.h" 14#include "list.h"
14#include "log.h" 15#include "log.h"
15 16
@@ -48,10 +49,12 @@ void init_layout(void) {
48 root_container.layout = L_NONE; 49 root_container.layout = L_NONE;
49 root_container.name = strdup("root"); 50 root_container.name = strdup("root");
50 root_container.children = create_list(); 51 root_container.children = create_list();
52 wl_signal_init(&root_container.events.destroy);
51 53
52 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); 54 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
53 root_container.sway_root->output_layout = wlr_output_layout_create(); 55 root_container.sway_root->output_layout = wlr_output_layout_create();
54 wl_list_init(&root_container.sway_root->unmanaged_views); 56 wl_list_init(&root_container.sway_root->unmanaged_views);
57 wl_signal_init(&root_container.sway_root->events.new_container);
55 58
56 root_container.sway_root->output_layout_change.notify = 59 root_container.sway_root->output_layout_change.notify =
57 output_layout_change_notify; 60 output_layout_change_notify;
@@ -59,6 +62,32 @@ void init_layout(void) {
59 &root_container.sway_root->output_layout_change); 62 &root_container.sway_root->output_layout_change);
60} 63}
61 64
65static int index_child(const swayc_t *child) {
66 // TODO handle floating
67 swayc_t *parent = child->parent;
68 int i, len;
69 len = parent->children->length;
70 for (i = 0; i < len; ++i) {
71 if (parent->children->items[i] == child) {
72 break;
73 }
74 }
75
76 if (!sway_assert(i < len, "Stray container")) {
77 return -1;
78 }
79 return i;
80}
81
82swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
83 // TODO handle floating
84 swayc_t *parent = fixed->parent;
85 int i = index_child(fixed);
86 list_insert(parent->children, i + 1, active);
87 active->parent = parent;
88 return active->parent;
89}
90
62void add_child(swayc_t *parent, swayc_t *child) { 91void add_child(swayc_t *parent, swayc_t *child) {
63 wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", 92 wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
64 child, child->type, child->width, child->height, 93 child, child->type, child->width, child->height,
@@ -66,9 +95,6 @@ void add_child(swayc_t *parent, swayc_t *child) {
66 list_add(parent->children, child); 95 list_add(parent->children, child);
67 child->parent = parent; 96 child->parent = parent;
68 // set focus for this container 97 // set focus for this container
69 if (!parent->focused) {
70 parent->focused = child;
71 }
72 /* TODO WLR 98 /* TODO WLR
73 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) { 99 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
74 child = new_container(child, parent->workspace_layout); 100 child = new_container(child, parent->workspace_layout);
@@ -147,8 +173,8 @@ void arrange_windows(swayc_t *container, double width, double height) {
147 height = floor(height); 173 height = floor(height);
148 174
149 wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, 175 wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
150 container->name, container->width, container->height, container->x, 176 container->name, container->width, container->height, container->x,
151 container->y); 177 container->y);
152 178
153 double x = 0, y = 0; 179 double x = 0, y = 0;
154 switch (container->type) { 180 switch (container->type) {
@@ -249,8 +275,8 @@ static void apply_horiz_layout(swayc_t *container,
249 for (int i = start; i < end; ++i) { 275 for (int i = start; i < end; ++i) {
250 swayc_t *child = container->children->items[i]; 276 swayc_t *child = container->children->items[i];
251 wlr_log(L_DEBUG, 277 wlr_log(L_DEBUG,
252 "Calculating arrangement for %p:%d (will scale %f by %f)", 278 "Calculating arrangement for %p:%d (will scale %f by %f)",
253 child, child->type, width, scale); 279 child, child->type, width, scale);
254 view_set_position(child->sway_view, child_x, y); 280 view_set_position(child->sway_view, child_x, y);
255 281
256 if (i == end - 1) { 282 if (i == end - 1) {
@@ -299,8 +325,8 @@ void apply_vert_layout(swayc_t *container,
299 for (i = start; i < end; ++i) { 325 for (i = start; i < end; ++i) {
300 swayc_t *child = container->children->items[i]; 326 swayc_t *child = container->children->items[i];
301 wlr_log(L_DEBUG, 327 wlr_log(L_DEBUG,
302 "Calculating arrangement for %p:%d (will scale %f by %f)", 328 "Calculating arrangement for %p:%d (will scale %f by %f)",
303 child, child->type, height, scale); 329 child, child->type, height, scale);
304 view_set_position(child->sway_view, x, child_y); 330 view_set_position(child->sway_view, x, child_y);
305 331
306 if (i == end - 1) { 332 if (i == end - 1) {
@@ -321,3 +347,244 @@ void apply_vert_layout(swayc_t *container,
321 */ 347 */
322 } 348 }
323} 349}
350
351/**
352 * Get swayc in the direction of newly entered output.
353 */
354static swayc_t *get_swayc_in_output_direction(swayc_t *output,
355 enum movement_direction dir, struct sway_seat *seat) {
356 if (!output) {
357 return NULL;
358 }
359
360 swayc_t *ws = sway_seat_get_focus_inactive(seat, output);
361 if (ws->type != C_WORKSPACE) {
362 ws = swayc_parent_by_type(ws, C_WORKSPACE);
363 }
364
365 if (ws == NULL) {
366 wlr_log(L_ERROR, "got an output without a workspace");
367 return NULL;
368 }
369
370 if (ws->children->length > 0) {
371 switch (dir) {
372 case MOVE_LEFT:
373 // get most right child of new output
374 return ws->children->items[ws->children->length-1];
375 case MOVE_RIGHT:
376 // get most left child of new output
377 return ws->children->items[0];
378 case MOVE_UP:
379 case MOVE_DOWN: {
380 swayc_t *focused = sway_seat_get_focus_inactive(seat, ws);
381 if (focused && focused->parent) {
382 swayc_t *parent = focused->parent;
383 if (parent->layout == L_VERT) {
384 if (dir == MOVE_UP) {
385 // get child furthest down on new output
386 return parent->children->items[parent->children->length-1];
387 } else if (dir == MOVE_DOWN) {
388 // get child furthest up on new output
389 return parent->children->items[0];
390 }
391 }
392 return focused;
393 }
394 break;
395 }
396 default:
397 break;
398 }
399 }
400
401 return ws;
402}
403
404static void get_layout_center_position(swayc_t *container, int *x, int *y) {
405 // FIXME view coords are inconsistently referred to in layout/output systems
406 if (container->type == C_OUTPUT) {
407 *x = container->x + container->width/2;
408 *y = container->y + container->height/2;
409 } else {
410 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
411 if (container->type == C_WORKSPACE) {
412 // Workspace coordinates are actually wrong/arbitrary, but should
413 // be same as output.
414 *x = output->x;
415 *y = output->y;
416 } else {
417 *x = output->x + container->x;
418 *y = output->y + container->y;
419 }
420 }
421}
422
423static bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out) {
424 switch (dir) {
425 case MOVE_UP:
426 *out = WLR_DIRECTION_UP;
427 break;
428 case MOVE_DOWN:
429 *out = WLR_DIRECTION_DOWN;
430 break;
431 case MOVE_LEFT:
432 *out = WLR_DIRECTION_LEFT;
433 break;
434 case MOVE_RIGHT:
435 *out = WLR_DIRECTION_RIGHT;
436 break;
437 default:
438 return false;
439 }
440
441 return true;
442}
443
444static swayc_t *sway_output_from_wlr(struct wlr_output *output) {
445 if (output == NULL) {
446 return NULL;
447 }
448 for (int i = 0; i < root_container.children->length; ++i) {
449 swayc_t *o = root_container.children->items[i];
450 if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
451 return o;
452 }
453 }
454 return NULL;
455}
456
457static swayc_t *get_swayc_in_direction_under(swayc_t *container,
458 enum movement_direction dir, struct sway_seat *seat, swayc_t *limit) {
459 if (dir == MOVE_CHILD) {
460 return sway_seat_get_focus_inactive(seat, container);
461 }
462
463 swayc_t *parent = container->parent;
464 if (dir == MOVE_PARENT) {
465 if (parent->type == C_OUTPUT) {
466 return NULL;
467 } else {
468 return parent;
469 }
470 }
471
472 if (dir == MOVE_PREV || dir == MOVE_NEXT) {
473 int focused_idx = index_child(container);
474 if (focused_idx == -1) {
475 return NULL;
476 } else {
477 int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
478 parent->children->length;
479 if (desired < 0) {
480 desired += parent->children->length;
481 }
482 return parent->children->items[desired];
483 }
484 }
485
486 // If moving to an adjacent output we need a starting position (since this
487 // output might border to multiple outputs).
488 //struct wlc_point abs_pos;
489 //get_layout_center_position(container, &abs_pos);
490
491
492 // TODO WLR fullscreen
493 /*
494 if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
495 wlr_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
496 container = swayc_parent_by_type(container, C_OUTPUT);
497 get_layout_center_position(container, &abs_pos);
498 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
499 return get_swayc_in_output_direction(output, dir);
500 }
501 if (container->type == C_WORKSPACE && container->fullscreen) {
502 sway_log(L_DEBUG, "Moving to fullscreen view");
503 return container->fullscreen;
504 }
505 */
506
507 swayc_t *wrap_candidate = NULL;
508 while (true) {
509 // Test if we can even make a difference here
510 bool can_move = false;
511 int desired;
512 int idx = index_child(container);
513 if (parent->type == C_ROOT) {
514 enum wlr_direction wlr_dir = 0;
515 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
516 "got invalid direction: %d", dir)) {
517 return NULL;
518 }
519 int lx, ly;
520 get_layout_center_position(container, &lx, &ly);
521 struct wlr_output_layout *layout = root_container.sway_root->output_layout;
522 struct wlr_output *wlr_adjacent =
523 wlr_output_layout_adjacent_output(layout, wlr_dir,
524 container->sway_output->wlr_output, lx, ly);
525 swayc_t *adjacent = sway_output_from_wlr(wlr_adjacent);
526
527 if (!adjacent || adjacent == container) {
528 return wrap_candidate;
529 }
530 swayc_t *next = get_swayc_in_output_direction(adjacent, dir, seat);
531 if (next == NULL) {
532 return NULL;
533 }
534 if (next->children && next->children->length) {
535 // TODO consider floating children as well
536 return sway_seat_get_focus_inactive(seat, next);
537 } else {
538 return next;
539 }
540 } else {
541 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
542 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
543 can_move = true;
544 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
545 }
546 } else {
547 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
548 can_move = true;
549 desired = idx + (dir == MOVE_UP ? -1 : 1);
550 }
551 }
552 }
553
554 if (can_move) {
555 // TODO handle floating
556 if (desired < 0 || desired >= parent->children->length) {
557 can_move = false;
558 int len = parent->children->length;
559 if (!wrap_candidate && len > 1) {
560 if (desired < 0) {
561 wrap_candidate = parent->children->items[len-1];
562 } else {
563 wrap_candidate = parent->children->items[0];
564 }
565 if (config->force_focus_wrapping) {
566 return wrap_candidate;
567 }
568 }
569 } else {
570 wlr_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
571 idx, container, dir, desired, parent->children->items[desired]);
572 return parent->children->items[desired];
573 }
574 }
575
576 if (!can_move) {
577 container = parent;
578 parent = parent->parent;
579 if (!parent || container == limit) {
580 // wrapping is the last chance
581 return wrap_candidate;
582 }
583 }
584 }
585}
586
587swayc_t *get_swayc_in_direction(swayc_t *container, struct sway_seat *seat,
588 enum movement_direction dir) {
589 return get_swayc_in_direction_under(container, dir, seat, NULL);
590}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 23c630b6..861fda4d 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -63,9 +63,10 @@ static bool _workspace_by_name(swayc_t *view, void *data) {
63swayc_t *workspace_by_name(const char *name) { 63swayc_t *workspace_by_name(const char *name) {
64 struct sway_seat *seat = input_manager_current_seat(input_manager); 64 struct sway_seat *seat = input_manager_current_seat(input_manager);
65 swayc_t *current_workspace = NULL, *current_output = NULL; 65 swayc_t *current_workspace = NULL, *current_output = NULL;
66 if (seat->focus) { 66 swayc_t *focus = sway_seat_get_focus(seat);
67 current_workspace = swayc_parent_by_type(seat->focus, C_WORKSPACE); 67 if (focus) {
68 current_output = swayc_parent_by_type(seat->focus, C_OUTPUT); 68 current_workspace = swayc_parent_by_type(focus, C_WORKSPACE);
69 current_output = swayc_parent_by_type(focus, C_OUTPUT);
69 } 70 }
70 if (strcmp(name, "prev") == 0) { 71 if (strcmp(name, "prev") == 0) {
71 return workspace_prev(current_workspace); 72 return workspace_prev(current_workspace);
@@ -102,7 +103,8 @@ swayc_t *workspace_create(const char *name) {
102 } 103 }
103 // Otherwise create a new one 104 // Otherwise create a new one
104 struct sway_seat *seat = input_manager_current_seat(input_manager); 105 struct sway_seat *seat = input_manager_current_seat(input_manager);
105 parent = seat->focus; 106 swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
107 parent = focus;
106 parent = swayc_parent_by_type(parent, C_OUTPUT); 108 parent = swayc_parent_by_type(parent, C_OUTPUT);
107 return new_workspace(parent, name); 109 return new_workspace(parent, name);
108} 110}
@@ -118,9 +120,15 @@ swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
118 return NULL; 120 return NULL;
119 } 121 }
120 122
123 struct sway_seat *seat = input_manager_current_seat(input_manager);
124 swayc_t *focus = sway_seat_get_focus_inactive(seat, output);
125 swayc_t *workspace = (focus->type == C_WORKSPACE ?
126 focus :
127 swayc_parent_by_type(focus, C_WORKSPACE));
128
121 int i; 129 int i;
122 for (i = 0; i < output->children->length; i++) { 130 for (i = 0; i < output->children->length; i++) {
123 if (output->children->items[i] == output->focused) { 131 if (output->children->items[i] == workspace) {
124 return output->children->items[ 132 return output->children->items[
125 wrap(i + (next ? 1 : -1), output->children->length)]; 133 wrap(i + (next ? 1 : -1), output->children->length)];
126 } 134 }
@@ -193,12 +201,13 @@ bool workspace_switch(swayc_t *workspace) {
193 return false; 201 return false;
194 } 202 }
195 struct sway_seat *seat = input_manager_current_seat(input_manager); 203 struct sway_seat *seat = input_manager_current_seat(input_manager);
196 if (!seat || !seat->focus) { 204 swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
205 if (!seat || !focus) {
197 return false; 206 return false;
198 } 207 }
199 swayc_t *active_ws = seat->focus; 208 swayc_t *active_ws = focus;
200 if (active_ws->type != C_WORKSPACE) { 209 if (active_ws->type != C_WORKSPACE) {
201 swayc_parent_by_type(seat->focus, C_WORKSPACE); 210 swayc_parent_by_type(focus, C_WORKSPACE);
202 } 211 }
203 212
204 if (config->auto_back_and_forth 213 if (config->auto_back_and_forth
@@ -222,16 +231,12 @@ bool workspace_switch(swayc_t *workspace) {
222 // TODO: Deal with sticky containers 231 // TODO: Deal with sticky containers
223 232
224 wlr_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); 233 wlr_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
225 // TODO FOCUS: Focus the last view this seat had focused on this workspace 234 swayc_t *next = sway_seat_get_focus_inactive(seat, workspace);
226 if (workspace->children->length) { 235 if (next == NULL) {
227 // TODO FOCUS: This is really fucking stupid 236 next = workspace;
228 sway_seat_set_focus(seat, workspace->children->items[0]);
229 } else {
230 sway_seat_set_focus(seat, workspace);
231 } 237 }
238 sway_seat_set_focus(seat, next);
232 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); 239 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
233 // TODO FOCUS: take a look at this
234 output->focused = workspace;
235 arrange_windows(output, -1, -1); 240 arrange_windows(output, -1, -1);
236 return true; 241 return true;
237} 242}