aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 @@
void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);
#define sway_abort(FMT, ...) \
- _sway_abort("[%s:%d] " FMT, _strip_path(__FILE__), __LINE__, ##__VA_ARGS__)
+ _sway_abort("[%s:%d] " FMT, wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__)
bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);
#define sway_assert(COND, FMT, ...) \
- _sway_assert(COND, "[%s:%d] %s:" FMT, _strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+ _sway_assert(COND, "[%s:%d] %s:" FMT, wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
void error_handler(int sig);
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,
enum expected_args type, int val);
/**
- * Parse and handles a command.
+ * Parse and executes a command.
*/
-struct cmd_results *handle_command(char *command);
+struct cmd_results *execute_command(char *command, struct sway_seat *seat);
/**
* Parse and handles a command during config file loading.
*
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 {
* The parent of this container. NULL for the root container.
*/
struct sway_container *parent;
- /**
- * Which of this container's children has focus.
- */
- struct sway_container *focused;
/**
* Number of master views in auto layouts.
@@ -162,4 +158,12 @@ void container_map(swayc_t *container,
swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy);
+/**
+ * Apply the function for each child of the container breadth first.
+ */
+void container_for_each_bfs(swayc_t *con, void (*f)(swayc_t *con, void *data),
+ void *data);
+
+swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout);
+
#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 {
struct wlr_input_device *wlr_device;
struct input_config *config;
struct wl_list link;
+ struct wl_listener device_destroy;
};
struct sway_input_manager {
- struct wl_listener input_add;
- struct wl_listener input_remove;
struct sway_server *server;
struct wl_list devices;
struct wl_list seats;
+
+ struct wl_listener new_input;
};
struct 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 {
struct wl_list link; // sway_seat::devices
};
+struct sway_seat_container {
+ struct sway_seat *seat;
+ swayc_t *container;
+
+ struct wl_list link; // sway_seat::focus_stack
+
+ struct wl_listener destroy;
+};
+
struct sway_seat {
struct wlr_seat *wlr_seat;
struct seat_config *config;
struct sway_cursor *cursor;
struct sway_input_manager *input;
- swayc_t *focus;
+
+ bool has_focus;
+ struct wl_list focus_stack; // list of containers in focus order
struct wl_listener focus_destroy;
+ struct wl_listener new_container;
struct wl_list devices; // sway_seat_device::link
@@ -44,6 +56,22 @@ void sway_seat_configure_xcursor(struct sway_seat *seat);
void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container);
+swayc_t *sway_seat_get_focus(struct sway_seat *seat);
+
+/**
+ * Return the last container to be focused for the seat (or the most recently
+ * opened if no container has received focused) that is a child of the given
+ * container. The focus-inactive container of the root window is the focused
+ * container for the seat (if the seat does have focus). This function can be
+ * used to determine what container gets focused next if the focused container
+ * is destroyed, or focus moves to a container with children and we need to
+ * descend into the next leaf in focus order.
+ */
+swayc_t *sway_seat_get_focus_inactive(struct sway_seat *seat, swayc_t *container);
+
+swayc_t *sway_seat_get_focus_by_type(struct sway_seat *seat,
+ enum swayc_types type);
+
void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config);
#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 @@
#define _SWAY_LAYOUT_H
#include <wlr/types/wlr_output_layout.h>
+#include "sway/container.h"
+
+enum movement_direction {
+ MOVE_LEFT,
+ MOVE_RIGHT,
+ MOVE_UP,
+ MOVE_DOWN,
+ MOVE_PARENT,
+ MOVE_CHILD,
+ MOVE_NEXT,
+ MOVE_PREV,
+ MOVE_FIRST
+};
struct sway_container;
@@ -11,13 +24,20 @@ struct sway_root {
struct wl_listener output_layout_change;
struct wl_list unmanaged_views; // sway_view::unmanaged_view_link
+
+ struct {
+ struct wl_signal new_container;
+ } events;
};
void init_layout(void);
void add_child(struct sway_container *parent, struct sway_container *child);
+swayc_t *add_sibling(swayc_t *parent, swayc_t *child);
struct sway_container *remove_child(struct sway_container *child);
enum swayc_layouts default_layout(struct sway_container *output);
void sort_workspaces(struct sway_container *output);
void arrange_windows(struct sway_container *container, double width, double height);
+swayc_t *get_swayc_in_direction(swayc_t *container,
+ struct sway_seat *seat, enum movement_direction dir);
#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 {
struct timespec last_frame;
struct wl_listener frame;
+ struct wl_listener output_destroy;
};
#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 {
struct sway_input_manager *input;
- struct wl_listener output_add;
- struct wl_listener output_remove;
+ struct wl_listener new_output;
struct wl_listener output_frame;
struct wlr_xdg_shell_v6 *xdg_shell_v6;
@@ -45,8 +44,7 @@ bool server_init(struct sway_server *server);
void server_fini(struct sway_server *server);
void server_run(struct sway_server *server);
-void output_add_notify(struct wl_listener *listener, void *data);
-void output_remove_notify(struct wl_listener *listener, void *data);
+void handle_new_output(struct wl_listener *listener, void *data);
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
void 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 @@
#ifndef _SWAY_WORKSPACE_H
#define _SWAY_WORKSPACE_H
-struct sway_container;
+#include "sway/container.h"
extern char *prev_workspace_name;
@@ -12,9 +12,9 @@ bool workspace_switch(swayc_t *workspace);
struct sway_container *workspace_by_number(const char* name);
swayc_t *workspace_by_name(const char*);
-struct sway_container *workspace_output_next(struct sway_container *current);
-struct sway_container *workspace_next(struct sway_container *current);
-struct sway_container *workspace_output_prev(struct sway_container *current);
-struct sway_container *workspace_prev(struct sway_container *current);
+struct sway_container *workspace_output_next(swayc_t *current);
+struct sway_container *workspace_next(swayc_t *current);
+struct sway_container *workspace_output_prev(swayc_t *current);
+struct sway_container *workspace_prev(swayc_t *current);
#endif
diff --git a/meson.build b/meson.build
index 49392126..fc995c81 100644
--- a/meson.build
+++ b/meson.build
@@ -10,6 +10,7 @@ project(
)
add_project_arguments('-Wno-unused-parameter', language: 'c')
+add_project_arguments('-Wno-unused-function', language: 'c')
cc = meson.get_compiler('c')
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)
return NULL;
}
-/* Keep alphabetized */
+/**
+ * handlers that can run in either config or command context
+ * Keep alphabetized
+ */
static struct cmd_handler handlers[] = {
{ "bindcode", cmd_bindcode },
{ "bindsym", cmd_bindsym },
{ "exec", cmd_exec },
{ "exec_always", cmd_exec_always },
- { "exit", cmd_exit },
{ "include", cmd_include },
{ "input", cmd_input },
- { "kill", cmd_kill },
{ "output", cmd_output },
- { "reload", cmd_reload },
{ "seat", cmd_seat },
- { "set", cmd_set },
{ "workspace", cmd_workspace },
};
+/**
+ * Commands that can *only* run in the config loading context
+ * Keep alphabetized
+ */
+static struct cmd_handler config_handlers[] = {
+ { "set", cmd_set },
+};
+
+/**
+ * Commands that can *not* run in the config loading context
+ * Keep alphabetized
+ */
+static struct cmd_handler command_handlers[] = {
+ { "exit", cmd_exit },
+ { "focus", cmd_focus },
+ { "kill", cmd_kill },
+ { "layout", cmd_layout },
+ { "reload", cmd_reload },
+};
+
static int handler_compare(const void *_a, const void *_b) {
const struct cmd_handler *a = _a;
const struct cmd_handler *b = _b;
@@ -179,24 +198,48 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
struct cmd_handler *res = NULL;
wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT);
+ bool config_loading = config->reading || !config->active;
+
if (block == CMD_BLOCK_INPUT) {
- res = bsearch(&d, input_handlers,
+ // input commands can run in either context
+ return bsearch(&d, input_handlers,
sizeof(input_handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare);
} else if (block == CMD_BLOCK_SEAT) {
- res = bsearch(&d, seat_handlers,
+ // seat commands can run in either context
+ return bsearch(&d, seat_handlers,
sizeof(seat_handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare);
- } else {
- res = bsearch(&d, handlers,
- sizeof(handlers) / sizeof(struct cmd_handler),
+ }
+
+ if (!config_loading) {
+ res = bsearch(&d, command_handlers,
+ sizeof(command_handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare);
+
+ if (res) {
+ return res;
+ }
+ }
+
+ if (config->reading) {
+ res = bsearch(&d, config_handlers,
+ sizeof(config_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+
+ if (res) {
+ return res;
+ }
}
+ res = bsearch(&d, handlers,
+ sizeof(handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+
return res;
}
-struct cmd_results *handle_command(char *_exec) {
+struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
// Even though this function will process multiple commands we will only
// return the last error, if any (for now). (Since we have access to an
// error string we could e.g. concatenate all errors there.)
@@ -207,6 +250,16 @@ struct cmd_results *handle_command(char *_exec) {
char *cmd;
list_t *containers = NULL;
+ if (seat == NULL) {
+ // passing a NULL seat means we just pick the default seat
+ seat = sway_input_manager_get_default_seat(input_manager);
+ if (!sway_assert(seat, "could not find a seat to run the command on")) {
+ return NULL;
+ }
+ }
+
+ config->handler_context.seat = seat;
+
head = exec;
do {
// Extract criteria (valid for this command list only).
@@ -276,23 +329,22 @@ struct cmd_results *handle_command(char *_exec) {
if (!has_criteria) {
// without criteria, the command acts upon the focused
// container
- struct sway_seat *seat = config->handler_context.seat;
- if (!seat) {
- seat = sway_input_manager_get_default_seat(input_manager);
+ config->handler_context.current_container =
+ sway_seat_get_focus_inactive(seat, &root_container);
+ if (!sway_assert(config->handler_context.current_container,
+ "could not get focus-inactive for root container")) {
+ return NULL;
}
- if (seat) {
- config->handler_context.current_container = seat->focus;
- struct cmd_results *res = handler->handle(argc-1, argv+1);
- if (res->status != CMD_SUCCESS) {
- free_argv(argc, argv);
- if (results) {
- free_cmd_results(results);
- }
- results = res;
- goto cleanup;
+ struct cmd_results *res = handler->handle(argc-1, argv+1);
+ if (res->status != CMD_SUCCESS) {
+ free_argv(argc, argv);
+ if (results) {
+ free_cmd_results(results);
}
- free_cmd_results(res);
+ results = res;
+ goto cleanup;
}
+ free_cmd_results(res);
} else {
for (int i = 0; i < containers->length; ++i) {
config->handler_context.current_container = containers->items[i];
@@ -319,13 +371,13 @@ cleanup:
return results;
}
-// this is like handle_command above, except:
+// this is like execute_command above, except:
// 1) it ignores empty commands (empty lines)
// 2) it does variable substitution
// 3) it doesn't split commands (because the multiple commands are supposed to
// be chained together)
-// 4) handle_command handles all state internally while config_command has some
-// state handled outside (notably the block mode, in read_config)
+// 4) execute_command handles all state internally while config_command has
+// some state handled outside (notably the block mode, in read_config)
struct cmd_results *config_command(char *exec, enum cmd_status block) {
struct cmd_results *results = NULL;
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);
struct cmd_results *cmd_exit(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) {
- return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
- }
if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
return error;
}
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 @@
+#include <strings.h>
+#include <wlr/util/log.h>
+#include "log.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/view.h"
+#include "sway/commands.h"
+
+static bool parse_movement_direction(const char *name, enum movement_direction *out) {
+ if (strcasecmp(name, "left") == 0) {
+ *out = MOVE_LEFT;
+ } else if (strcasecmp(name, "right") == 0) {
+ *out = MOVE_RIGHT;
+ } else if (strcasecmp(name, "up") == 0) {
+ *out = MOVE_UP;
+ } else if (strcasecmp(name, "down") == 0) {
+ *out = MOVE_DOWN;
+ } else if (strcasecmp(name, "parent") == 0) {
+ *out = MOVE_PARENT;
+ } else if (strcasecmp(name, "child") == 0) {
+ *out = MOVE_CHILD;
+ } else if (strcasecmp(name, "next") == 0) {
+ *out = MOVE_NEXT;
+ } else if (strcasecmp(name, "prev") == 0) {
+ *out = MOVE_PREV;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+struct cmd_results *cmd_focus(int argc, char **argv) {
+ swayc_t *con = config->handler_context.current_container;
+ struct sway_seat *seat = config->handler_context.seat;
+ if (con->type < C_WORKSPACE) {
+ return cmd_results_new(CMD_FAILURE, "focus",
+ "Command 'focus' cannot be used above the workspace level");
+ }
+
+ if (argc == 0) {
+ sway_seat_set_focus(seat, con);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ }
+
+ // TODO mode_toggle
+ enum movement_direction direction = 0;
+ if (!parse_movement_direction(argv[0], &direction)) {
+ return cmd_results_new(CMD_INVALID, "focus",
+ "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
+ }
+
+ swayc_t *next_focus = get_swayc_in_direction(con, seat, direction);
+ if (next_focus) {
+ sway_seat_set_focus(seat, next_focus);
+ }
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
#include "sway/commands.h"
struct cmd_results *cmd_kill(int argc, char **argv) {
- if (config->reading) {
- return cmd_results_new(CMD_FAILURE, "kill",
- "Command 'kill' cannot be used in the config file");
- }
enum swayc_types type = config->handler_context.current_container->type;
- if (type != C_VIEW || type != C_CONTAINER) {
+ if (type != C_VIEW && type != C_CONTAINER) {
return cmd_results_new(CMD_INVALID, NULL,
"Can only kill views and containers with this command");
}
+
// TODO close arbitrary containers without a view
struct sway_view *view =
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 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/container.h"
+#include "sway/layout.h"
+#include "log.h"
+
+struct cmd_results *cmd_layout(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
+ return error;
+ }
+ swayc_t *parent = config->handler_context.current_container;
+
+ // TODO: floating
+ /*
+ if (parent->is_floating) {
+ return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
+ }
+ */
+
+ while (parent->type == C_VIEW) {
+ parent = parent->parent;
+ }
+
+ // TODO: stacks and tabs
+
+ if (strcasecmp(argv[0], "default") == 0) {
+ swayc_change_layout(parent, parent->prev_layout);
+ if (parent->layout == L_NONE) {
+ swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT);
+ swayc_change_layout(parent, default_layout(output));
+ }
+ } else {
+ if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
+ parent->prev_layout = parent->layout;
+ }
+
+ if (strcasecmp(argv[0], "splith") == 0) {
+ swayc_change_layout(parent, L_HORIZ);
+ } else if (strcasecmp(argv[0], "splitv") == 0) {
+ swayc_change_layout(parent, L_VERT);
+ } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
+ if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
+ || parent->workspace_layout == L_HORIZ)) {
+ swayc_change_layout(parent, L_VERT);
+ } else {
+ swayc_change_layout(parent, L_HORIZ);
+ }
+ }
+ }
+
+ arrange_windows(parent, parent->width, parent->height);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
struct cmd_results *cmd_reload(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) {
- return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
- }
if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
return error;
}
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) {
struct cmd_results *cmd_set(int argc, char **argv) {
char *tmp;
struct cmd_results *error = NULL;
- if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
return error;
}
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) {
free(name);
}
workspace_switch(ws);
- current_container = config->handler_context.seat->focus;
+ current_container =
+ sway_seat_get_focus(config->handler_context.seat);
swayc_t *new_output = swayc_parent_by_type(current_container, C_OUTPUT);
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,
int height = surface->current->height;
int render_width = width * wlr_output->scale;
int render_height = height * wlr_output->scale;
- double ox = lx, oy = ly;
- wlr_output_layout_output_coords(layout, wlr_output, &ox, &oy);
- ox *= wlr_output->scale;
- oy *= wlr_output->scale;
+ int owidth, oheight;
+ wlr_output_effective_resolution(wlr_output, &owidth, &oheight);
- struct wlr_box render_box = {
- .x = lx, .y = ly,
+ // FIXME: view coords are inconsistently assumed to be in output or layout coords
+ struct wlr_box layout_box = {
+ .x = lx + wlr_output->lx, .y = ly + wlr_output->ly,
.width = render_width, .height = render_height,
};
- if (wlr_output_layout_intersects(layout, wlr_output, &render_box)) {
+ if (wlr_output_layout_intersects(layout, wlr_output, &layout_box)) {
+ struct wlr_box render_box = {
+ .x = lx, .y = ly,
+ .width = render_width, .height = render_height
+ };
float matrix[16];
-
- float translate_center[16];
- wlr_matrix_translate(&translate_center,
- (int)ox + render_width / 2, (int)oy + render_height / 2, 0);
-
- float rotate[16];
- wlr_matrix_rotate(&rotate, rotation);
-
- float translate_origin[16];
- wlr_matrix_translate(&translate_origin, -render_width / 2,
- -render_height / 2, 0);
-
- float scale[16];
- wlr_matrix_scale(&scale, render_width, render_height, 1);
-
- float transform[16];
- wlr_matrix_mul(&translate_center, &rotate, &transform);
- wlr_matrix_mul(&transform, &translate_origin, &transform);
- wlr_matrix_mul(&transform, &scale, &transform);
-
- if (surface->current->transform != WL_OUTPUT_TRANSFORM_NORMAL) {
- float surface_translate_center[16];
- wlr_matrix_translate(&surface_translate_center, 0.5, 0.5, 0);
-
- float surface_transform[16];
- wlr_matrix_transform(surface_transform,
- wlr_output_transform_invert(surface->current->transform));
-
- float surface_translate_origin[16];
- wlr_matrix_translate(&surface_translate_origin, -0.5, -0.5, 0);
-
- wlr_matrix_mul(&transform, &surface_translate_center,
- &transform);
- wlr_matrix_mul(&transform, &surface_transform, &transform);
- wlr_matrix_mul(&transform, &surface_translate_origin,
- &transform);
- }
-
- wlr_matrix_mul(&wlr_output->transform_matrix, &transform, &matrix);
-
+ wlr_matrix_project_box(&matrix, &render_box,
+ surface->current->transform, 0, &wlr_output->transform_matrix);
wlr_render_with_matrix(server.renderer, surface->texture,
&matrix);
@@ -125,8 +90,9 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
double width = surface->surface->current->width;
double height = surface->surface->current->height;
- struct wlr_xdg_surface_v6 *popup;
- wl_list_for_each(popup, &surface->popups, popup_link) {
+ struct wlr_xdg_popup_v6 *popup_state;
+ wl_list_for_each(popup_state, &surface->popups, link) {
+ struct wlr_xdg_surface_v6 *popup = popup_state->base;
if (!popup->configured) {
continue;
}
@@ -215,11 +181,20 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct sway_output *soutput = wl_container_of(listener, soutput, frame);
struct wlr_output *wlr_output = data;
struct sway_server *server = soutput->server;
+ float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
+ wlr_renderer_clear(renderer, &clear_color);
- wlr_output_make_current(wlr_output);
+ int buffer_age = -1;
+ wlr_output_make_current(wlr_output, &buffer_age);
wlr_renderer_begin(server->renderer, wlr_output);
- swayc_t *workspace = soutput->swayc->focused;
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ swayc_t *focus = sway_seat_get_focus_inactive(seat, soutput->swayc);
+ swayc_t *workspace = (focus->type == C_WORKSPACE ?
+ focus :
+ swayc_parent_by_type(focus, C_WORKSPACE));
+
swayc_descendants_of_type(workspace, C_VIEW, output_frame_view, soutput);
// render unmanaged views on top
@@ -236,15 +211,23 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
}
wlr_renderer_end(server->renderer);
- wlr_output_swap_buffers(wlr_output);
+ wlr_output_swap_buffers(wlr_output, &soutput->last_frame, NULL);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
soutput->last_frame = now;
}
-void output_add_notify(struct wl_listener *listener, void *data) {
- struct sway_server *server = wl_container_of(listener, server, output_add);
+static void handle_output_destroy(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, output_destroy);
+ struct wlr_output *wlr_output = data;
+ wlr_log(L_DEBUG, "Output %p %s removed", wlr_output, wlr_output->name);
+
+ destroy_output(output->swayc);
+}
+
+void handle_new_output(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
@@ -269,27 +252,11 @@ void output_add_notify(struct wl_listener *listener, void *data) {
sway_input_manager_configure_xcursor(input_manager);
- output->frame.notify = output_frame_notify;
wl_signal_add(&wlr_output->events.frame, &output->frame);
-}
-
-void output_remove_notify(struct wl_listener *listener, void *data) {
- struct sway_server *server = wl_container_of(listener, server, output_remove);
- struct wlr_output *wlr_output = data;
- wlr_log(L_DEBUG, "Output %p %s removed", wlr_output, wlr_output->name);
+ output->frame.notify = output_frame_notify;
- swayc_t *output_container = NULL;
- for (int i = 0 ; i < root_container.children->length; ++i) {
- swayc_t *child = root_container.children->items[i];
- if (child->type == C_OUTPUT &&
- child->sway_output->wlr_output == wlr_output) {
- output_container = child;
- break;
- }
- }
- if (!output_container) {
- return;
- }
+ wl_signal_add(&wlr_output->events.destroy, &output->output_destroy);
+ output->output_destroy.notify = handle_output_destroy;
- destroy_output(output_container);
+ arrange_windows(&root_container, -1, -1);
}
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) {
wl_signal_add(&xdg_surface->events.destroy, &sway_surface->destroy);
struct sway_seat *seat = input_manager_current_seat(input_manager);
- swayc_t *cont = new_view(seat->focus, sway_view);
+ swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
+ swayc_t *cont = new_view(focus, sway_view);
sway_view->swayc = cont;
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
}
}
-static void input_add_notify(struct wl_listener *listener, void *data) {
+static void handle_device_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_input_device *device = data;
+
+ struct sway_input_device *input_device =
+ input_sway_device_from_wlr(input_manager, device);
+
+ if (!sway_assert(input_device, "could not find sway device")) {
+ return;
+ }
+
+ wlr_log(L_DEBUG, "removing device: '%s'",
+ input_device->identifier);
+
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ sway_seat_remove_device(seat, input_device);
+ }
+
+ wl_list_remove(&input_device->link);
+ wl_list_remove(&input_device->device_destroy.link);
+ free_input_config(input_device->config);
+ free(input_device->identifier);
+ free(input_device);
+}
+
+static void handle_new_input(struct wl_listener *listener, void *data) {
struct sway_input_manager *input =
- wl_container_of(listener, input, input_add);
+ wl_container_of(listener, input, new_input);
struct wlr_input_device *device = data;
struct sway_input_device *input_device =
@@ -226,32 +251,9 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
"device '%s' is not configured on any seats",
input_device->identifier);
}
-}
-
-static void input_remove_notify(struct wl_listener *listener, void *data) {
- struct sway_input_manager *input =
- wl_container_of(listener, input, input_remove);
- struct wlr_input_device *device = data;
-
- struct sway_input_device *input_device =
- input_sway_device_from_wlr(input, device);
-
- if (!sway_assert(input_device, "could not find sway device")) {
- return;
- }
-
- wlr_log(L_DEBUG, "removing device: '%s'",
- input_device->identifier);
-
- struct sway_seat *seat = NULL;
- wl_list_for_each(seat, &input->seats, link) {
- sway_seat_remove_device(seat, input_device);
- }
- wl_list_remove(&input_device->link);
- free_input_config(input_device->config);
- free(input_device->identifier);
- free(input_device);
+ wl_signal_add(&device->events.destroy, &input_device->device_destroy);
+ input_device->device_destroy.notify = handle_device_destroy;
}
struct sway_input_manager *sway_input_manager_create(
@@ -269,11 +271,8 @@ struct sway_input_manager *sway_input_manager_create(
// create the default seat
input_manager_get_seat(input, default_seat);
- input->input_add.notify = input_add_notify;
- wl_signal_add(&server->backend->events.input_add, &input->input_add);
-
- input->input_remove.notify = input_remove_notify;
- wl_signal_add(&server->backend->events.input_remove, &input->input_remove);
+ input->new_input.notify = handle_new_input;
+ wl_signal_add(&server->backend->events.new_input, &input->new_input);
return input;
}
@@ -282,7 +281,7 @@ bool sway_input_manager_has_focus(struct sway_input_manager *input,
swayc_t *container) {
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input->seats, link) {
- if (seat->focus == container) {
+ if (sway_seat_get_focus(seat) == container) {
return true;
}
}
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,
binding->command);
config_clear_handler_context(config);
config->handler_context.seat = keyboard->seat_device->sway_seat;
- struct cmd_results *results = handle_command(binding->command);
+ struct cmd_results *results = execute_command(binding->command, NULL);
if (results->status != CMD_SUCCESS) {
wlr_log(L_DEBUG, "could not run command for binding: %s",
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) {
wlr_seat_destroy(seat->wlr_seat);
}
+static void handle_seat_container_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_seat_container *seat_con =
+ wl_container_of(listener, seat_con, destroy);
+ struct sway_seat *seat = seat_con->seat;
+ swayc_t *con = seat_con->container;
+
+ bool is_focus = (sway_seat_get_focus(seat) == con);
+
+ wl_list_remove(&seat_con->link);
+
+ if (is_focus) {
+ // pick next focus
+ sway_seat_set_focus(seat, NULL);
+ swayc_t *next = sway_seat_get_focus_inactive(seat, con->parent);
+ if (next == NULL) {
+ next = con->parent;
+ }
+ sway_seat_set_focus(seat, next);
+ }
+
+ wl_list_remove(&seat_con->destroy.link);
+
+ free(seat_con);
+}
+
+static struct sway_seat_container *seat_container_from_container(
+ struct sway_seat *seat, swayc_t *con) {
+ if (con->type < C_WORKSPACE) {
+ // these don't get seat containers ever
+ return NULL;
+ }
+
+ struct sway_seat_container *seat_con = NULL;
+ wl_list_for_each(seat_con, &seat->focus_stack, link) {
+ if (seat_con->container == con) {
+ return seat_con;
+ }
+ }
+
+ seat_con = calloc(1, sizeof(struct sway_seat_container));
+ if (seat_con == NULL) {
+ wlr_log(L_ERROR, "could not allocate seat container");
+ return NULL;
+ }
+
+ seat_con->container = con;
+ seat_con->seat = seat;
+ wl_list_insert(seat->focus_stack.prev, &seat_con->link);
+ wl_signal_add(&con->events.destroy, &seat_con->destroy);
+ seat_con->destroy.notify = handle_seat_container_destroy;
+
+ return seat_con;
+}
+
+static void handle_new_container(struct wl_listener *listener, void *data) {
+ struct sway_seat *seat = wl_container_of(listener, seat, new_container);
+ swayc_t *con = data;
+ seat_container_from_container(seat, con);
+}
+
+static void collect_focus_iter(swayc_t *con, void *data) {
+ struct sway_seat *seat = data;
+ if (con->type > C_WORKSPACE) {
+ return;
+ }
+ struct sway_seat_container *seat_con =
+ seat_container_from_container(seat, con);
+ if (!seat_con) {
+ return;
+ }
+ wl_list_remove(&seat_con->link);
+ wl_list_insert(&seat->focus_stack, &seat_con->link);
+}
+
struct sway_seat *sway_seat_create(struct sway_input_manager *input,
const char *seat_name) {
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,
return NULL;
}
+ // init the focus stack
+ wl_list_init(&seat->focus_stack);
+
+ container_for_each_bfs(&root_container, collect_focus_iter, seat);
+
+ wl_signal_add(&root_container.sway_root->events.new_container,
+ &seat->new_container);
+ seat->new_container.notify = handle_new_container;
+
seat->input = input;
wl_list_init(&seat->devices);
@@ -81,13 +165,14 @@ static void seat_configure_keyboard(struct sway_seat *seat,
struct wlr_keyboard *wlr_keyboard = seat_device->input_device->wlr_device->keyboard;
sway_keyboard_configure(seat_device->keyboard);
wlr_seat_set_keyboard(seat->wlr_seat,
- seat_device->input_device->wlr_device);
- if (seat->focus && seat->focus->type == C_VIEW) {
+ seat_device->input_device->wlr_device);
+ swayc_t *focus = sway_seat_get_focus(seat);
+ if (focus && focus->type == C_VIEW) {
// force notify reenter to pick up the new configuration
wlr_seat_keyboard_clear_focus(seat->wlr_seat);
wlr_seat_keyboard_notify_enter(seat->wlr_seat,
- seat->focus->sway_view->surface, wlr_keyboard->keycodes,
- wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
+ focus->sway_view->surface, wlr_keyboard->keycodes,
+ wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
}
}
@@ -204,47 +289,83 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
seat->cursor->cursor->y);
}
-static void handle_focus_destroy(struct wl_listener *listener, void *data) {
- struct sway_seat *seat = wl_container_of(listener, seat, focus_destroy);
- swayc_t *container = data;
- sway_seat_set_focus(seat, container->parent);
-}
-
void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
- swayc_t *last_focus = seat->focus;
+ swayc_t *last_focus = sway_seat_get_focus(seat);
- if (last_focus == container) {
+ if (container && last_focus == container) {
return;
}
- if (last_focus && last_focus->type == C_VIEW) {
- wl_list_remove(&seat->focus_destroy.link);
- }
+ if (container) {
+ struct sway_seat_container *seat_con =
+ seat_container_from_container(seat, container);
+ if (!seat_con) {
+ return;
+ }
- if (container && container->type == C_VIEW) {
- struct sway_view *view = container->sway_view;
- view_set_activated(view, true);
- wl_signal_add(&container->events.destroy, &seat->focus_destroy);
- seat->focus_destroy.notify = handle_focus_destroy;
-
- struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
- if (keyboard) {
- wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface,
- keyboard->keycodes, keyboard->num_keycodes,
- &keyboard->modifiers);
- } else {
- wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface,
- NULL, 0, NULL);
+ wl_list_remove(&seat_con->link);
+ wl_list_insert(&seat->focus_stack, &seat_con->link);
+
+ if (container->type == C_VIEW) {
+ struct sway_view *view = container->sway_view;
+ view_set_activated(view, true);
+ struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
+ if (keyboard) {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface,
+ keyboard->keycodes, keyboard->num_keycodes,
+ &keyboard->modifiers);
+ } else {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface,
+ NULL, 0, NULL);
+ }
}
}
- seat->focus = container;
-
if (last_focus && last_focus->type == C_VIEW &&
!sway_input_manager_has_focus(seat->input, last_focus)) {
struct sway_view *view = last_focus->sway_view;
view_set_activated(view, false);
}
+
+ seat->has_focus = (container != NULL);
+}
+
+swayc_t *sway_seat_get_focus_inactive(struct sway_seat *seat, swayc_t *container) {
+ struct sway_seat_container *current = NULL;
+ swayc_t *parent = NULL;
+ wl_list_for_each(current, &seat->focus_stack, link) {
+ parent = current->container->parent;
+
+ if (current->container == container) {
+ return current->container;
+ }
+
+ while (parent) {
+ if (parent == container) {
+ return current->container;
+ }
+ parent = parent->parent;
+ }
+ }
+
+ return NULL;
+}
+
+swayc_t *sway_seat_get_focus(struct sway_seat *seat) {
+ if (!seat->has_focus) {
+ return NULL;
+ }
+ return sway_seat_get_focus_inactive(seat, &root_container);
+}
+
+swayc_t *sway_seat_get_focus_by_type(struct sway_seat *seat,
+ enum swayc_types type) {
+ swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
+ if (focus->type == type) {
+ return focus;
+ }
+
+ return swayc_parent_by_type(focus, type);
}
void 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) {
json_object_object_add(object, "refresh", json_object_new_int(wlr_output->refresh));
json_object_object_add(object, "transform",
json_object_new_string(ipc_json_get_output_transform(wlr_output->transform)));
- json_object_object_add(object, "current_workspace",
- (container->focused) ? json_object_new_string(container->focused->name) : NULL);
+ // TODO WLR need to set "current_workspace" to the currently focused
+ // workspace in a way that makes sense with multiseat
}
static 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) {
case IPC_COMMAND:
{
config_clear_handler_context(config);
- struct cmd_results *results = handle_command(buf);
+ struct cmd_results *results = execute_command(buf, NULL);
const char *json = cmd_results_to_json(results);
char reply[256];
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(
'commands/exit.c',
'commands/exec.c',
'commands/exec_always.c',
+ 'commands/focus.c',
'commands/kill.c',
'commands/include.c',
'commands/input.c',
+ 'commands/layout.c',
'commands/seat.c',
'commands/seat/attach.c',
'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) {
config->active = true;
while (config->cmd_queue->length) {
char *line = config->cmd_queue->items[0];
- struct cmd_results *res = handle_command(line);
+ struct cmd_results *res = execute_command(line, NULL);
if (res->status != CMD_SUCCESS) {
wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error);
}
@@ -48,12 +48,8 @@ bool server_init(struct sway_server *server) {
server->data_device_manager =
wlr_data_device_manager_create(server->wl_display);
- server->output_add.notify = output_add_notify;
- wl_signal_add(&server->backend->events.output_add, &server->output_add);
-
- server->output_remove.notify = output_remove_notify;
- wl_signal_add(&server->backend->events.output_remove,
- &server->output_remove);
+ server->new_output.notify = handle_new_output;
+ wl_signal_add(&server->backend->events.new_output, &server->new_output);
server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
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 @@
#include "sway/workspace.h"
#include "log.h"
+static list_t *bfs_queue;
+
+static list_t *get_bfs_queue() {
+ if (!bfs_queue) {
+ bfs_queue = create_list();
+ if (!bfs_queue) {
+ wlr_log(L_ERROR, "could not allocate list for bfs queue");
+ return NULL;
+ }
+ }
+ bfs_queue->length = 0;
+
+ return bfs_queue;
+}
+
swayc_t *swayc_by_test(swayc_t *container,
bool (*test)(swayc_t *view, void *data), void *data) {
if (!container->children) {
@@ -151,19 +166,16 @@ swayc_t *new_output(struct sway_output *sway_output) {
char *ws_name = workspace_next_name(output->name);
wlr_log(L_DEBUG, "Creating default workspace %s", ws_name);
swayc_t *ws = new_workspace(output, ws_name);
- output->focused = ws;
// Set each seat's focus if not already set
- // TODO FOCUS: this is probably stupid, we shouldn't define focus in two
- // places. We should probably put the active workspace on the sway_output
- // struct instead of trying to do focus semantics like this
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input_manager->seats, link) {
- if (!seat->focus) {
- seat->focus = ws;
+ if (!seat->has_focus) {
+ sway_seat_set_focus(seat, ws);
}
}
free(ws_name);
+ wl_signal_emit(&root_container.sway_root->events.new_container, output);
return output;
}
@@ -185,6 +197,7 @@ swayc_t *new_workspace(swayc_t *output, const char *name) {
add_child(output, workspace);
sort_workspaces(output);
+ wl_signal_emit(&root_container.sway_root->events.new_container, workspace);
return workspace;
}
@@ -207,9 +220,9 @@ swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view) {
add_child(sibling, swayc);
} else {
// Regular case, create as sibling of current container
- // TODO WLR
- //add_sibling(sibling, swayc);
+ add_sibling(sibling, swayc);
}
+ wl_signal_emit(&root_container.sway_root->events.new_container, swayc);
return swayc;
}
@@ -235,6 +248,8 @@ swayc_t *destroy_output(swayc_t *output) {
}
}
+ wl_list_remove(&output->sway_output->output_destroy.link);
+
wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
free_swayc(output);
@@ -273,7 +288,11 @@ swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) {
- list_t *queue = create_list();
+ list_t *queue = get_bfs_queue();
+ if (!queue) {
+ return NULL;
+ }
+
list_add(queue, parent);
swayc_t *swayc = NULL;
@@ -313,7 +332,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
*sx = view_sx - popup_sx;
*sy = view_sy - popup_sy;
*surface = popup->surface;
- list_free(queue);
return swayc;
}
break;
@@ -332,7 +350,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
*sx = view_sx - sub_x;
*sy = view_sy - sub_y;
*surface = subsurface->surface;
- list_free(queue);
return swayc;
}
@@ -344,7 +361,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
*sx = view_sx;
*sy = view_sy;
*surface = swayc->sway_view->surface;
- list_free(queue);
return swayc;
}
} else {
@@ -352,8 +368,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
}
}
- list_free(queue);
-
return NULL;
}
@@ -378,3 +392,39 @@ void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), voi
f(container, data);
}
}
+
+void container_for_each_bfs(swayc_t *con, void (*f)(swayc_t *con, void *data),
+ void *data) {
+ list_t *queue = get_bfs_queue();
+ if (!queue) {
+ return;
+ }
+
+ if (queue == NULL) {
+ wlr_log(L_ERROR, "could not allocate list");
+ return;
+ }
+
+ list_add(queue, con);
+
+ swayc_t *current = NULL;
+ while (queue->length) {
+ current = queue->items[0];
+ list_del(queue, 0);
+ f(current, data);
+ // TODO floating containers
+ list_cat(queue, current->children);
+ }
+}
+
+swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
+ if (container->type == C_WORKSPACE) {
+ container->workspace_layout = layout;
+ if (layout == L_HORIZ || layout == L_VERT) {
+ container->layout = layout;
+ }
+ } else {
+ container->layout = layout;
+ }
+ return container;
+}
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 @@
#include "sway/layout.h"
#include "sway/output.h"
#include "sway/view.h"
+#include "sway/input/seat.h"
#include "list.h"
#include "log.h"
@@ -48,10 +49,12 @@ void init_layout(void) {
root_container.layout = L_NONE;
root_container.name = strdup("root");
root_container.children = create_list();
+ wl_signal_init(&root_container.events.destroy);
root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
root_container.sway_root->output_layout = wlr_output_layout_create();
wl_list_init(&root_container.sway_root->unmanaged_views);
+ wl_signal_init(&root_container.sway_root->events.new_container);
root_container.sway_root->output_layout_change.notify =
output_layout_change_notify;
@@ -59,6 +62,32 @@ void init_layout(void) {
&root_container.sway_root->output_layout_change);
}
+static int index_child(const swayc_t *child) {
+ // TODO handle floating
+ swayc_t *parent = child->parent;
+ int i, len;
+ len = parent->children->length;
+ for (i = 0; i < len; ++i) {
+ if (parent->children->items[i] == child) {
+ break;
+ }
+ }
+
+ if (!sway_assert(i < len, "Stray container")) {
+ return -1;
+ }
+ return i;
+}
+
+swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
+ // TODO handle floating
+ swayc_t *parent = fixed->parent;
+ int i = index_child(fixed);
+ list_insert(parent->children, i + 1, active);
+ active->parent = parent;
+ return active->parent;
+}
+
void add_child(swayc_t *parent, swayc_t *child) {
wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
child, child->type, child->width, child->height,
@@ -66,9 +95,6 @@ void add_child(swayc_t *parent, swayc_t *child) {
list_add(parent->children, child);
child->parent = parent;
// set focus for this container
- if (!parent->focused) {
- parent->focused = child;
- }
/* TODO WLR
if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
child = new_container(child, parent->workspace_layout);
@@ -147,8 +173,8 @@ void arrange_windows(swayc_t *container, double width, double height) {
height = floor(height);
wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
- container->name, container->width, container->height, container->x,
- container->y);
+ container->name, container->width, container->height, container->x,
+ container->y);
double x = 0, y = 0;
switch (container->type) {
@@ -249,8 +275,8 @@ static void apply_horiz_layout(swayc_t *container,
for (int i = start; i < end; ++i) {
swayc_t *child = container->children->items[i];
wlr_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)",
- child, child->type, width, scale);
+ "Calculating arrangement for %p:%d (will scale %f by %f)",
+ child, child->type, width, scale);
view_set_position(child->sway_view, child_x, y);
if (i == end - 1) {
@@ -299,8 +325,8 @@ void apply_vert_layout(swayc_t *container,
for (i = start; i < end; ++i) {
swayc_t *child = container->children->items[i];
wlr_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)",
- child, child->type, height, scale);
+ "Calculating arrangement for %p:%d (will scale %f by %f)",
+ child, child->type, height, scale);
view_set_position(child->sway_view, x, child_y);
if (i == end - 1) {
@@ -321,3 +347,244 @@ void apply_vert_layout(swayc_t *container,
*/
}
}
+
+/**
+ * Get swayc in the direction of newly entered output.
+ */
+static swayc_t *get_swayc_in_output_direction(swayc_t *output,
+ enum movement_direction dir, struct sway_seat *seat) {
+ if (!output) {
+ return NULL;
+ }
+
+ swayc_t *ws = sway_seat_get_focus_inactive(seat, output);
+ if (ws->type != C_WORKSPACE) {
+ ws = swayc_parent_by_type(ws, C_WORKSPACE);
+ }
+
+ if (ws == NULL) {
+ wlr_log(L_ERROR, "got an output without a workspace");
+ return NULL;
+ }
+
+ if (ws->children->length > 0) {
+ switch (dir) {
+ case MOVE_LEFT:
+ // get most right child of new output
+ return ws->children->items[ws->children->length-1];
+ case MOVE_RIGHT:
+ // get most left child of new output
+ return ws->children->items[0];
+ case MOVE_UP:
+ case MOVE_DOWN: {
+ swayc_t *focused = sway_seat_get_focus_inactive(seat, ws);
+ if (focused && focused->parent) {
+ swayc_t *parent = focused->parent;
+ if (parent->layout == L_VERT) {
+ if (dir == MOVE_UP) {
+ // get child furthest down on new output
+ return parent->children->items[parent->children->length-1];
+ } else if (dir == MOVE_DOWN) {
+ // get child furthest up on new output
+ return parent->children->items[0];
+ }
+ }
+ return focused;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return ws;
+}
+
+static void get_layout_center_position(swayc_t *container, int *x, int *y) {
+ // FIXME view coords are inconsistently referred to in layout/output systems
+ if (container->type == C_OUTPUT) {
+ *x = container->x + container->width/2;
+ *y = container->y + container->height/2;
+ } else {
+ swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
+ if (container->type == C_WORKSPACE) {
+ // Workspace coordinates are actually wrong/arbitrary, but should
+ // be same as output.
+ *x = output->x;
+ *y = output->y;
+ } else {
+ *x = output->x + container->x;
+ *y = output->y + container->y;
+ }
+ }
+}
+
+static bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out) {
+ switch (dir) {
+ case MOVE_UP:
+ *out = WLR_DIRECTION_UP;
+ break;
+ case MOVE_DOWN:
+ *out = WLR_DIRECTION_DOWN;
+ break;
+ case MOVE_LEFT:
+ *out = WLR_DIRECTION_LEFT;
+ break;
+ case MOVE_RIGHT:
+ *out = WLR_DIRECTION_RIGHT;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static swayc_t *sway_output_from_wlr(struct wlr_output *output) {
+ if (output == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < root_container.children->length; ++i) {
+ swayc_t *o = root_container.children->items[i];
+ if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
+ return o;
+ }
+ }
+ return NULL;
+}
+
+static swayc_t *get_swayc_in_direction_under(swayc_t *container,
+ enum movement_direction dir, struct sway_seat *seat, swayc_t *limit) {
+ if (dir == MOVE_CHILD) {
+ return sway_seat_get_focus_inactive(seat, container);
+ }
+
+ swayc_t *parent = container->parent;
+ if (dir == MOVE_PARENT) {
+ if (parent->type == C_OUTPUT) {
+ return NULL;
+ } else {
+ return parent;
+ }
+ }
+
+ if (dir == MOVE_PREV || dir == MOVE_NEXT) {
+ int focused_idx = index_child(container);
+ if (focused_idx == -1) {
+ return NULL;
+ } else {
+ int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
+ parent->children->length;
+ if (desired < 0) {
+ desired += parent->children->length;
+ }
+ return parent->children->items[desired];
+ }
+ }
+
+ // If moving to an adjacent output we need a starting position (since this
+ // output might border to multiple outputs).
+ //struct wlc_point abs_pos;
+ //get_layout_center_position(container, &abs_pos);
+
+
+ // TODO WLR fullscreen
+ /*
+ if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
+ wlr_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
+ container = swayc_parent_by_type(container, C_OUTPUT);
+ get_layout_center_position(container, &abs_pos);
+ swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
+ return get_swayc_in_output_direction(output, dir);
+ }
+ if (container->type == C_WORKSPACE && container->fullscreen) {
+ sway_log(L_DEBUG, "Moving to fullscreen view");
+ return container->fullscreen;
+ }
+ */
+
+ swayc_t *wrap_candidate = NULL;
+ while (true) {
+ // Test if we can even make a difference here
+ bool can_move = false;
+ int desired;
+ int idx = index_child(container);
+ if (parent->type == C_ROOT) {
+ enum wlr_direction wlr_dir = 0;
+ if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
+ "got invalid direction: %d", dir)) {
+ return NULL;
+ }
+ int lx, ly;
+ get_layout_center_position(container, &lx, &ly);
+ struct wlr_output_layout *layout = root_container.sway_root->output_layout;
+ struct wlr_output *wlr_adjacent =
+ wlr_output_layout_adjacent_output(layout, wlr_dir,
+ container->sway_output->wlr_output, lx, ly);
+ swayc_t *adjacent = sway_output_from_wlr(wlr_adjacent);
+
+ if (!adjacent || adjacent == container) {
+ return wrap_candidate;
+ }
+ swayc_t *next = get_swayc_in_output_direction(adjacent, dir, seat);
+ if (next == NULL) {
+ return NULL;
+ }
+ if (next->children && next->children->length) {
+ // TODO consider floating children as well
+ return sway_seat_get_focus_inactive(seat, next);
+ } else {
+ return next;
+ }
+ } else {
+ if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
+ if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_LEFT ? -1 : 1);
+ }
+ } else {
+ if (parent->layout == L_VERT || parent->layout == L_STACKED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_UP ? -1 : 1);
+ }
+ }
+ }
+
+ if (can_move) {
+ // TODO handle floating
+ if (desired < 0 || desired >= parent->children->length) {
+ can_move = false;
+ int len = parent->children->length;
+ if (!wrap_candidate && len > 1) {
+ if (desired < 0) {
+ wrap_candidate = parent->children->items[len-1];
+ } else {
+ wrap_candidate = parent->children->items[0];
+ }
+ if (config->force_focus_wrapping) {
+ return wrap_candidate;
+ }
+ }
+ } else {
+ wlr_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
+ idx, container, dir, desired, parent->children->items[desired]);
+ return parent->children->items[desired];
+ }
+ }
+
+ if (!can_move) {
+ container = parent;
+ parent = parent->parent;
+ if (!parent || container == limit) {
+ // wrapping is the last chance
+ return wrap_candidate;
+ }
+ }
+ }
+}
+
+swayc_t *get_swayc_in_direction(swayc_t *container, struct sway_seat *seat,
+ enum movement_direction dir) {
+ return get_swayc_in_direction_under(container, dir, seat, NULL);
+}
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) {
swayc_t *workspace_by_name(const char *name) {
struct sway_seat *seat = input_manager_current_seat(input_manager);
swayc_t *current_workspace = NULL, *current_output = NULL;
- if (seat->focus) {
- current_workspace = swayc_parent_by_type(seat->focus, C_WORKSPACE);
- current_output = swayc_parent_by_type(seat->focus, C_OUTPUT);
+ swayc_t *focus = sway_seat_get_focus(seat);
+ if (focus) {
+ current_workspace = swayc_parent_by_type(focus, C_WORKSPACE);
+ current_output = swayc_parent_by_type(focus, C_OUTPUT);
}
if (strcmp(name, "prev") == 0) {
return workspace_prev(current_workspace);
@@ -102,7 +103,8 @@ swayc_t *workspace_create(const char *name) {
}
// Otherwise create a new one
struct sway_seat *seat = input_manager_current_seat(input_manager);
- parent = seat->focus;
+ swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
+ parent = focus;
parent = swayc_parent_by_type(parent, C_OUTPUT);
return new_workspace(parent, name);
}
@@ -118,9 +120,15 @@ swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
return NULL;
}
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ swayc_t *focus = sway_seat_get_focus_inactive(seat, output);
+ swayc_t *workspace = (focus->type == C_WORKSPACE ?
+ focus :
+ swayc_parent_by_type(focus, C_WORKSPACE));
+
int i;
for (i = 0; i < output->children->length; i++) {
- if (output->children->items[i] == output->focused) {
+ if (output->children->items[i] == workspace) {
return output->children->items[
wrap(i + (next ? 1 : -1), output->children->length)];
}
@@ -193,12 +201,13 @@ bool workspace_switch(swayc_t *workspace) {
return false;
}
struct sway_seat *seat = input_manager_current_seat(input_manager);
- if (!seat || !seat->focus) {
+ swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container);
+ if (!seat || !focus) {
return false;
}
- swayc_t *active_ws = seat->focus;
+ swayc_t *active_ws = focus;
if (active_ws->type != C_WORKSPACE) {
- swayc_parent_by_type(seat->focus, C_WORKSPACE);
+ swayc_parent_by_type(focus, C_WORKSPACE);
}
if (config->auto_back_and_forth
@@ -222,16 +231,12 @@ bool workspace_switch(swayc_t *workspace) {
// TODO: Deal with sticky containers
wlr_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
- // TODO FOCUS: Focus the last view this seat had focused on this workspace
- if (workspace->children->length) {
- // TODO FOCUS: This is really fucking stupid
- sway_seat_set_focus(seat, workspace->children->items[0]);
- } else {
- sway_seat_set_focus(seat, workspace);
+ swayc_t *next = sway_seat_get_focus_inactive(seat, workspace);
+ if (next == NULL) {
+ next = workspace;
}
+ sway_seat_set_focus(seat, next);
swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
- // TODO FOCUS: take a look at this
- output->focused = workspace;
arrange_windows(output, -1, -1);
return true;
}