mirror of
https://github.com/swaywm/sway.git
synced 2024-12-29 16:36:26 +01:00
commit
583c30dbe3
30 changed files with 871 additions and 252 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,6 +14,7 @@ struct sway_output {
|
|||
struct timespec last_frame;
|
||||
|
||||
struct wl_listener frame;
|
||||
struct wl_listener output_destroy;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
||||
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,12 +329,12 @@ 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);
|
||||
|
@ -292,7 +345,6 @@ struct cmd_results *handle_command(char *_exec) {
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
59
sway/commands/focus.c
Normal file
59
sway/commands/focus.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
56
sway/commands/layout.c
Normal file
56
sway/commands/layout.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
destroy_output(output_container);
|
||||
output->frame.notify = output_frame_notify;
|
||||
|
||||
wl_signal_add(&wlr_output->events.destroy, &output->output_destroy);
|
||||
output->output_destroy.notify = handle_output_destroy;
|
||||
|
||||
arrange_windows(&root_container, -1, -1);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -82,11 +166,12 @@ static void seat_configure_keyboard(struct sway_seat *seat,
|
|||
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) {
|
||||
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,
|
||||
focus->sway_view->surface, wlr_keyboard->keycodes,
|
||||
wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
|
||||
}
|
||||
}
|
||||
|
@ -204,29 +289,26 @@ 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) {
|
||||
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);
|
||||
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,
|
||||
|
@ -237,14 +319,53 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
|
|||
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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue