Merge pull request #1585 from acrisci/focus-overhaul

focus overhaul
This commit is contained in:
emersion 2018-02-24 23:29:08 +01:00 committed by GitHub
commit 583c30dbe3
Failed to generate hash of commit
30 changed files with 871 additions and 252 deletions

View file

@ -5,11 +5,11 @@
void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2); void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);
#define sway_abort(FMT, ...) \ #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); bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);
#define sway_assert(COND, FMT, ...) \ #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); void error_handler(int sig);

View file

@ -46,9 +46,9 @@ struct cmd_results *checkarg(int argc, const char *name,
enum expected_args type, int val); 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. * Parse and handles a command during config file loading.
* *

View file

@ -106,10 +106,6 @@ struct sway_container {
* The parent of this container. NULL for the root container. * The parent of this container. NULL for the root container.
*/ */
struct sway_container *parent; struct sway_container *parent;
/**
* Which of this container's children has focus.
*/
struct sway_container *focused;
/** /**
* Number of master views in auto layouts. * 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, swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy); 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 #endif

View file

@ -16,14 +16,15 @@ struct sway_input_device {
struct wlr_input_device *wlr_device; struct wlr_input_device *wlr_device;
struct input_config *config; struct input_config *config;
struct wl_list link; struct wl_list link;
struct wl_listener device_destroy;
}; };
struct sway_input_manager { struct sway_input_manager {
struct wl_listener input_add;
struct wl_listener input_remove;
struct sway_server *server; struct sway_server *server;
struct wl_list devices; struct wl_list devices;
struct wl_list seats; struct wl_list seats;
struct wl_listener new_input;
}; };
struct sway_input_manager *sway_input_manager_create( struct sway_input_manager *sway_input_manager_create(

View file

@ -12,14 +12,26 @@ struct sway_seat_device {
struct wl_list link; // sway_seat::devices 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 sway_seat {
struct wlr_seat *wlr_seat; struct wlr_seat *wlr_seat;
struct seat_config *config; struct seat_config *config;
struct sway_cursor *cursor; struct sway_cursor *cursor;
struct sway_input_manager *input; 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 focus_destroy;
struct wl_listener new_container;
struct wl_list devices; // sway_seat_device::link 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); 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); void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config);
#endif #endif

View file

@ -2,6 +2,19 @@
#define _SWAY_LAYOUT_H #define _SWAY_LAYOUT_H
#include <wlr/types/wlr_output_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; struct sway_container;
@ -11,13 +24,20 @@ struct sway_root {
struct wl_listener output_layout_change; struct wl_listener output_layout_change;
struct wl_list unmanaged_views; // sway_view::unmanaged_view_link struct wl_list unmanaged_views; // sway_view::unmanaged_view_link
struct {
struct wl_signal new_container;
} events;
}; };
void init_layout(void); void init_layout(void);
void add_child(struct sway_container *parent, struct sway_container *child); 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); struct sway_container *remove_child(struct sway_container *child);
enum swayc_layouts default_layout(struct sway_container *output); enum swayc_layouts default_layout(struct sway_container *output);
void sort_workspaces(struct sway_container *output); void sort_workspaces(struct sway_container *output);
void arrange_windows(struct sway_container *container, double width, double height); 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 #endif

View file

@ -14,6 +14,7 @@ struct sway_output {
struct timespec last_frame; struct timespec last_frame;
struct wl_listener frame; struct wl_listener frame;
struct wl_listener output_destroy;
}; };
#endif #endif

View file

@ -24,8 +24,7 @@ struct sway_server {
struct sway_input_manager *input; struct sway_input_manager *input;
struct wl_listener output_add; struct wl_listener new_output;
struct wl_listener output_remove;
struct wl_listener output_frame; struct wl_listener output_frame;
struct wlr_xdg_shell_v6 *xdg_shell_v6; 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_fini(struct sway_server *server);
void server_run(struct sway_server *server); void server_run(struct sway_server *server);
void output_add_notify(struct wl_listener *listener, void *data); void handle_new_output(struct wl_listener *listener, void *data);
void output_remove_notify(struct wl_listener *listener, void *data);
void handle_xdg_shell_v6_surface(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); void handle_xwayland_surface(struct wl_listener *listener, void *data);

View file

@ -1,7 +1,7 @@
#ifndef _SWAY_WORKSPACE_H #ifndef _SWAY_WORKSPACE_H
#define _SWAY_WORKSPACE_H #define _SWAY_WORKSPACE_H
struct sway_container; #include "sway/container.h"
extern char *prev_workspace_name; extern char *prev_workspace_name;
@ -12,9 +12,9 @@ bool workspace_switch(swayc_t *workspace);
struct sway_container *workspace_by_number(const char* name); struct sway_container *workspace_by_number(const char* name);
swayc_t *workspace_by_name(const char*); swayc_t *workspace_by_name(const char*);
struct sway_container *workspace_output_next(struct sway_container *current); struct sway_container *workspace_output_next(swayc_t *current);
struct sway_container *workspace_next(struct sway_container *current); struct sway_container *workspace_next(swayc_t *current);
struct sway_container *workspace_output_prev(struct sway_container *current); struct sway_container *workspace_output_prev(swayc_t *current);
struct sway_container *workspace_prev(struct sway_container *current); struct sway_container *workspace_prev(swayc_t *current);
#endif #endif

View file

@ -10,6 +10,7 @@ project(
) )
add_project_arguments('-Wno-unused-parameter', language: 'c') add_project_arguments('-Wno-unused-parameter', language: 'c')
add_project_arguments('-Wno-unused-function', language: 'c')
cc = meson.get_compiler('c') cc = meson.get_compiler('c')

View file

@ -125,23 +125,42 @@ struct cmd_results *add_color(const char *name, char *buffer, const char *color)
return NULL; return NULL;
} }
/* Keep alphabetized */ /**
* handlers that can run in either config or command context
* Keep alphabetized
*/
static struct cmd_handler handlers[] = { static struct cmd_handler handlers[] = {
{ "bindcode", cmd_bindcode }, { "bindcode", cmd_bindcode },
{ "bindsym", cmd_bindsym }, { "bindsym", cmd_bindsym },
{ "exec", cmd_exec }, { "exec", cmd_exec },
{ "exec_always", cmd_exec_always }, { "exec_always", cmd_exec_always },
{ "exit", cmd_exit },
{ "include", cmd_include }, { "include", cmd_include },
{ "input", cmd_input }, { "input", cmd_input },
{ "kill", cmd_kill },
{ "output", cmd_output }, { "output", cmd_output },
{ "reload", cmd_reload },
{ "seat", cmd_seat }, { "seat", cmd_seat },
{ "set", cmd_set },
{ "workspace", cmd_workspace }, { "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) { static int handler_compare(const void *_a, const void *_b) {
const struct cmd_handler *a = _a; const struct cmd_handler *a = _a;
const struct cmd_handler *b = _b; 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; struct cmd_handler *res = NULL;
wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT); 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) { 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(input_handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare); sizeof(struct cmd_handler), handler_compare);
} else if (block == CMD_BLOCK_SEAT) { } 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(seat_handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare); sizeof(struct cmd_handler), handler_compare);
} else {
res = bsearch(&d, handlers,
sizeof(handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare);
} }
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; 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 // 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 // return the last error, if any (for now). (Since we have access to an
// error string we could e.g. concatenate all errors there.) // error string we could e.g. concatenate all errors there.)
@ -207,6 +250,16 @@ struct cmd_results *handle_command(char *_exec) {
char *cmd; char *cmd;
list_t *containers = NULL; 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; head = exec;
do { do {
// Extract criteria (valid for this command list only). // Extract criteria (valid for this command list only).
@ -276,23 +329,22 @@ struct cmd_results *handle_command(char *_exec) {
if (!has_criteria) { if (!has_criteria) {
// without criteria, the command acts upon the focused // without criteria, the command acts upon the focused
// container // container
struct sway_seat *seat = config->handler_context.seat; config->handler_context.current_container =
if (!seat) { sway_seat_get_focus_inactive(seat, &root_container);
seat = sway_input_manager_get_default_seat(input_manager); if (!sway_assert(config->handler_context.current_container,
"could not get focus-inactive for root container")) {
return NULL;
} }
if (seat) { struct cmd_results *res = handler->handle(argc-1, argv+1);
config->handler_context.current_container = seat->focus; if (res->status != CMD_SUCCESS) {
struct cmd_results *res = handler->handle(argc-1, argv+1); free_argv(argc, argv);
if (res->status != CMD_SUCCESS) { if (results) {
free_argv(argc, argv); free_cmd_results(results);
if (results) {
free_cmd_results(results);
}
results = res;
goto cleanup;
} }
free_cmd_results(res); results = res;
goto cleanup;
} }
free_cmd_results(res);
} else { } else {
for (int i = 0; i < containers->length; ++i) { for (int i = 0; i < containers->length; ++i) {
config->handler_context.current_container = containers->items[i]; config->handler_context.current_container = containers->items[i];
@ -319,13 +371,13 @@ cleanup:
return results; return results;
} }
// this is like handle_command above, except: // this is like execute_command above, except:
// 1) it ignores empty commands (empty lines) // 1) it ignores empty commands (empty lines)
// 2) it does variable substitution // 2) it does variable substitution
// 3) it doesn't split commands (because the multiple commands are supposed to // 3) it doesn't split commands (because the multiple commands are supposed to
// be chained together) // be chained together)
// 4) handle_command handles all state internally while config_command has some // 4) execute_command handles all state internally while config_command has
// state handled outside (notably the block mode, in read_config) // 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 *config_command(char *exec, enum cmd_status block) {
struct cmd_results *results = NULL; struct cmd_results *results = NULL;
int argc; int argc;

View file

@ -6,9 +6,6 @@ void sway_terminate(int exit_code);
struct cmd_results *cmd_exit(int argc, char **argv) { struct cmd_results *cmd_exit(int argc, char **argv) {
struct cmd_results *error = NULL; 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))) { if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
return error; return error;
} }

59
sway/commands/focus.c Normal file
View 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);
}

View file

@ -6,15 +6,12 @@
#include "sway/commands.h" #include "sway/commands.h"
struct cmd_results *cmd_kill(int argc, char **argv) { 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; 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, return cmd_results_new(CMD_INVALID, NULL,
"Can only kill views and containers with this command"); "Can only kill views and containers with this command");
} }
// TODO close arbitrary containers without a view // TODO close arbitrary containers without a view
struct sway_view *view = struct sway_view *view =
config->handler_context.current_container->sway_view; config->handler_context.current_container->sway_view;

56
sway/commands/layout.c Normal file
View 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);
}

View file

@ -4,9 +4,6 @@
struct cmd_results *cmd_reload(int argc, char **argv) { struct cmd_results *cmd_reload(int argc, char **argv) {
struct cmd_results *error = NULL; 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))) { if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
return error; return error;
} }

View file

@ -27,7 +27,6 @@ void free_sway_variable(struct sway_variable *var) {
struct cmd_results *cmd_set(int argc, char **argv) { struct cmd_results *cmd_set(int argc, char **argv) {
char *tmp; char *tmp;
struct cmd_results *error = NULL; 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))) { if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
return error; return error;
} }

View file

@ -90,7 +90,8 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
free(name); free(name);
} }
workspace_switch(ws); 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); swayc_t *new_output = swayc_parent_by_type(current_container, C_OUTPUT);
if (config->mouse_warping && old_output != new_output) { if (config->mouse_warping && old_output != new_output) {

View file

@ -46,57 +46,22 @@ static void render_surface(struct wlr_surface *surface,
int height = surface->current->height; int height = surface->current->height;
int render_width = width * wlr_output->scale; int render_width = width * wlr_output->scale;
int render_height = height * wlr_output->scale; int render_height = height * wlr_output->scale;
double ox = lx, oy = ly; int owidth, oheight;
wlr_output_layout_output_coords(layout, wlr_output, &ox, &oy); wlr_output_effective_resolution(wlr_output, &owidth, &oheight);
ox *= wlr_output->scale;
oy *= wlr_output->scale;
struct wlr_box render_box = { // FIXME: view coords are inconsistently assumed to be in output or layout coords
.x = lx, .y = ly, struct wlr_box layout_box = {
.x = lx + wlr_output->lx, .y = ly + wlr_output->ly,
.width = render_width, .height = render_height, .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 matrix[16];
wlr_matrix_project_box(&matrix, &render_box,
float translate_center[16]; surface->current->transform, 0, &wlr_output->transform_matrix);
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_render_with_matrix(server.renderer, surface->texture, wlr_render_with_matrix(server.renderer, surface->texture,
&matrix); &matrix);
@ -125,8 +90,9 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
double width = surface->surface->current->width; double width = surface->surface->current->width;
double height = surface->surface->current->height; double height = surface->surface->current->height;
struct wlr_xdg_surface_v6 *popup; struct wlr_xdg_popup_v6 *popup_state;
wl_list_for_each(popup, &surface->popups, popup_link) { wl_list_for_each(popup_state, &surface->popups, link) {
struct wlr_xdg_surface_v6 *popup = popup_state->base;
if (!popup->configured) { if (!popup->configured) {
continue; 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 sway_output *soutput = wl_container_of(listener, soutput, frame);
struct wlr_output *wlr_output = data; struct wlr_output *wlr_output = data;
struct sway_server *server = soutput->server; 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); 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); swayc_descendants_of_type(workspace, C_VIEW, output_frame_view, soutput);
// render unmanaged views on top // 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_renderer_end(server->renderer);
wlr_output_swap_buffers(wlr_output); wlr_output_swap_buffers(wlr_output, &soutput->last_frame, NULL);
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
soutput->last_frame = now; soutput->last_frame = now;
} }
void output_add_notify(struct wl_listener *listener, void *data) { static void handle_output_destroy(struct wl_listener *listener, void *data) {
struct sway_server *server = wl_container_of(listener, server, output_add); 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; struct wlr_output *wlr_output = data;
wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 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); sway_input_manager_configure_xcursor(input_manager);
output->frame.notify = output_frame_notify;
wl_signal_add(&wlr_output->events.frame, &output->frame); wl_signal_add(&wlr_output->events.frame, &output->frame);
} output->frame.notify = output_frame_notify;
void output_remove_notify(struct wl_listener *listener, void *data) { wl_signal_add(&wlr_output->events.destroy, &output->output_destroy);
struct sway_server *server = wl_container_of(listener, server, output_remove); output->output_destroy.notify = handle_output_destroy;
struct wlr_output *wlr_output = data;
wlr_log(L_DEBUG, "Output %p %s removed", wlr_output, wlr_output->name); arrange_windows(&root_container, -1, -1);
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);
} }

View file

@ -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); wl_signal_add(&xdg_surface->events.destroy, &sway_surface->destroy);
struct sway_seat *seat = input_manager_current_seat(input_manager); 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; sway_view->swayc = cont;
arrange_windows(cont->parent, -1, -1); arrange_windows(cont->parent, -1, -1);

View file

@ -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 = 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 wlr_input_device *device = data;
struct sway_input_device *input_device = 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", "device '%s' is not configured on any seats",
input_device->identifier); input_device->identifier);
} }
}
static void input_remove_notify(struct wl_listener *listener, void *data) { wl_signal_add(&device->events.destroy, &input_device->device_destroy);
struct sway_input_manager *input = input_device->device_destroy.notify = handle_device_destroy;
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);
} }
struct sway_input_manager *sway_input_manager_create( struct sway_input_manager *sway_input_manager_create(
@ -269,11 +271,8 @@ struct sway_input_manager *sway_input_manager_create(
// create the default seat // create the default seat
input_manager_get_seat(input, default_seat); input_manager_get_seat(input, default_seat);
input->input_add.notify = input_add_notify; input->new_input.notify = handle_new_input;
wl_signal_add(&server->backend->events.input_add, &input->input_add); wl_signal_add(&server->backend->events.new_input, &input->new_input);
input->input_remove.notify = input_remove_notify;
wl_signal_add(&server->backend->events.input_remove, &input->input_remove);
return input; return input;
} }
@ -282,7 +281,7 @@ bool sway_input_manager_has_focus(struct sway_input_manager *input,
swayc_t *container) { swayc_t *container) {
struct sway_seat *seat = NULL; struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input->seats, link) { wl_list_for_each(seat, &input->seats, link) {
if (seat->focus == container) { if (sway_seat_get_focus(seat) == container) {
return true; return true;
} }
} }

View file

@ -95,7 +95,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard,
binding->command); binding->command);
config_clear_handler_context(config); config_clear_handler_context(config);
config->handler_context.seat = keyboard->seat_device->sway_seat; 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) { if (results->status != CMD_SUCCESS) {
wlr_log(L_DEBUG, "could not run command for binding: %s", wlr_log(L_DEBUG, "could not run command for binding: %s",
binding->command); binding->command);

View file

@ -32,6 +32,81 @@ void sway_seat_destroy(struct sway_seat *seat) {
wlr_seat_destroy(seat->wlr_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, struct sway_seat *sway_seat_create(struct sway_input_manager *input,
const char *seat_name) { const char *seat_name) {
struct sway_seat *seat = calloc(1, sizeof(struct sway_seat)); 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; 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; seat->input = input;
wl_list_init(&seat->devices); 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; struct wlr_keyboard *wlr_keyboard = seat_device->input_device->wlr_device->keyboard;
sway_keyboard_configure(seat_device->keyboard); sway_keyboard_configure(seat_device->keyboard);
wlr_seat_set_keyboard(seat->wlr_seat, wlr_seat_set_keyboard(seat->wlr_seat,
seat_device->input_device->wlr_device); 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 // force notify reenter to pick up the new configuration
wlr_seat_keyboard_clear_focus(seat->wlr_seat); wlr_seat_keyboard_clear_focus(seat->wlr_seat);
wlr_seat_keyboard_notify_enter(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); wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
} }
} }
@ -204,47 +289,83 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
seat->cursor->cursor->y); 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) { 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; return;
} }
if (last_focus && last_focus->type == C_VIEW) { if (container) {
wl_list_remove(&seat->focus_destroy.link); 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);
struct sway_view *view = container->sway_view; wl_list_insert(&seat->focus_stack, &seat_con->link);
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 (container->type == C_VIEW) {
if (keyboard) { struct sway_view *view = container->sway_view;
wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, view_set_activated(view, true);
keyboard->keycodes, keyboard->num_keycodes, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
&keyboard->modifiers); if (keyboard) {
} else { wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface,
wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, keyboard->keycodes, keyboard->num_keycodes,
NULL, 0, NULL); &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 && if (last_focus && last_focus->type == C_VIEW &&
!sway_input_manager_has_focus(seat->input, last_focus)) { !sway_input_manager_has_focus(seat->input, last_focus)) {
struct sway_view *view = last_focus->sway_view; struct sway_view *view = last_focus->sway_view;
view_set_activated(view, false); 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, void sway_seat_set_config(struct sway_seat *seat,

View file

@ -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, "refresh", json_object_new_int(wlr_output->refresh));
json_object_object_add(object, "transform", json_object_object_add(object, "transform",
json_object_new_string(ipc_json_get_output_transform(wlr_output->transform))); json_object_new_string(ipc_json_get_output_transform(wlr_output->transform)));
json_object_object_add(object, "current_workspace", // TODO WLR need to set "current_workspace" to the currently focused
(container->focused) ? json_object_new_string(container->focused->name) : NULL); // workspace in a way that makes sense with multiseat
} }
static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) { static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) {

View file

@ -336,7 +336,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
case IPC_COMMAND: case IPC_COMMAND:
{ {
config_clear_handler_context(config); 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); const char *json = cmd_results_to_json(results);
char reply[256]; char reply[256];
int length = snprintf(reply, sizeof(reply), "%s", json); int length = snprintf(reply, sizeof(reply), "%s", json);

View file

@ -10,9 +10,11 @@ sway_sources = files(
'commands/exit.c', 'commands/exit.c',
'commands/exec.c', 'commands/exec.c',
'commands/exec_always.c', 'commands/exec_always.c',
'commands/focus.c',
'commands/kill.c', 'commands/kill.c',
'commands/include.c', 'commands/include.c',
'commands/input.c', 'commands/input.c',
'commands/layout.c',
'commands/seat.c', 'commands/seat.c',
'commands/seat/attach.c', 'commands/seat/attach.c',
'commands/seat/fallback.c', 'commands/seat/fallback.c',

View file

@ -22,7 +22,7 @@ static void server_ready(struct wl_listener *listener, void *data) {
config->active = true; config->active = true;
while (config->cmd_queue->length) { while (config->cmd_queue->length) {
char *line = config->cmd_queue->items[0]; 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) { if (res->status != CMD_SUCCESS) {
wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error); 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 = server->data_device_manager =
wlr_data_device_manager_create(server->wl_display); wlr_data_device_manager_create(server->wl_display);
server->output_add.notify = output_add_notify; server->new_output.notify = handle_new_output;
wl_signal_add(&server->backend->events.output_add, &server->output_add); wl_signal_add(&server->backend->events.new_output, &server->new_output);
server->output_remove.notify = output_remove_notify;
wl_signal_add(&server->backend->events.output_remove,
&server->output_remove);
server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display); server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
wl_signal_add(&server->xdg_shell_v6->events.new_surface, wl_signal_add(&server->xdg_shell_v6->events.new_surface,

View file

@ -17,6 +17,21 @@
#include "sway/workspace.h" #include "sway/workspace.h"
#include "log.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, swayc_t *swayc_by_test(swayc_t *container,
bool (*test)(swayc_t *view, void *data), void *data) { bool (*test)(swayc_t *view, void *data), void *data) {
if (!container->children) { if (!container->children) {
@ -151,19 +166,16 @@ swayc_t *new_output(struct sway_output *sway_output) {
char *ws_name = workspace_next_name(output->name); char *ws_name = workspace_next_name(output->name);
wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); wlr_log(L_DEBUG, "Creating default workspace %s", ws_name);
swayc_t *ws = new_workspace(output, ws_name); swayc_t *ws = new_workspace(output, ws_name);
output->focused = ws;
// Set each seat's focus if not already set // 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; struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input_manager->seats, link) { wl_list_for_each(seat, &input_manager->seats, link) {
if (!seat->focus) { if (!seat->has_focus) {
seat->focus = ws; sway_seat_set_focus(seat, ws);
} }
} }
free(ws_name); free(ws_name);
wl_signal_emit(&root_container.sway_root->events.new_container, output);
return output; return output;
} }
@ -185,6 +197,7 @@ swayc_t *new_workspace(swayc_t *output, const char *name) {
add_child(output, workspace); add_child(output, workspace);
sort_workspaces(output); sort_workspaces(output);
wl_signal_emit(&root_container.sway_root->events.new_container, workspace);
return workspace; return workspace;
} }
@ -207,9 +220,9 @@ swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view) {
add_child(sibling, swayc); add_child(sibling, swayc);
} else { } else {
// Regular case, create as sibling of current container // 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; 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); wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
free_swayc(output); 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, swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) { 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); list_add(queue, parent);
swayc_t *swayc = NULL; swayc_t *swayc = NULL;
@ -313,7 +332,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
*sx = view_sx - popup_sx; *sx = view_sx - popup_sx;
*sy = view_sy - popup_sy; *sy = view_sy - popup_sy;
*surface = popup->surface; *surface = popup->surface;
list_free(queue);
return swayc; return swayc;
} }
break; break;
@ -332,7 +350,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
*sx = view_sx - sub_x; *sx = view_sx - sub_x;
*sy = view_sy - sub_y; *sy = view_sy - sub_y;
*surface = subsurface->surface; *surface = subsurface->surface;
list_free(queue);
return swayc; return swayc;
} }
@ -344,7 +361,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
*sx = view_sx; *sx = view_sx;
*sy = view_sy; *sy = view_sy;
*surface = swayc->sway_view->surface; *surface = swayc->sway_view->surface;
list_free(queue);
return swayc; return swayc;
} }
} else { } else {
@ -352,8 +368,6 @@ swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
} }
} }
list_free(queue);
return NULL; return NULL;
} }
@ -378,3 +392,39 @@ void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), voi
f(container, data); 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;
}

View file

@ -10,6 +10,7 @@
#include "sway/layout.h" #include "sway/layout.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/view.h" #include "sway/view.h"
#include "sway/input/seat.h"
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
@ -48,10 +49,12 @@ void init_layout(void) {
root_container.layout = L_NONE; root_container.layout = L_NONE;
root_container.name = strdup("root"); root_container.name = strdup("root");
root_container.children = create_list(); 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 = calloc(1, sizeof(*root_container.sway_root));
root_container.sway_root->output_layout = wlr_output_layout_create(); root_container.sway_root->output_layout = wlr_output_layout_create();
wl_list_init(&root_container.sway_root->unmanaged_views); 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 = root_container.sway_root->output_layout_change.notify =
output_layout_change_notify; output_layout_change_notify;
@ -59,6 +62,32 @@ void init_layout(void) {
&root_container.sway_root->output_layout_change); &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) { void add_child(swayc_t *parent, swayc_t *child) {
wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
child, child->type, child->width, child->height, 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); list_add(parent->children, child);
child->parent = parent; child->parent = parent;
// set focus for this container // set focus for this container
if (!parent->focused) {
parent->focused = child;
}
/* TODO WLR /* TODO WLR
if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) { 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); child = new_container(child, parent->workspace_layout);
@ -147,8 +173,8 @@ void arrange_windows(swayc_t *container, double width, double height) {
height = floor(height); height = floor(height);
wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
container->name, container->width, container->height, container->x, container->name, container->width, container->height, container->x,
container->y); container->y);
double x = 0, y = 0; double x = 0, y = 0;
switch (container->type) { switch (container->type) {
@ -249,8 +275,8 @@ static void apply_horiz_layout(swayc_t *container,
for (int i = start; i < end; ++i) { for (int i = start; i < end; ++i) {
swayc_t *child = container->children->items[i]; swayc_t *child = container->children->items[i];
wlr_log(L_DEBUG, wlr_log(L_DEBUG,
"Calculating arrangement for %p:%d (will scale %f by %f)", "Calculating arrangement for %p:%d (will scale %f by %f)",
child, child->type, width, scale); child, child->type, width, scale);
view_set_position(child->sway_view, child_x, y); view_set_position(child->sway_view, child_x, y);
if (i == end - 1) { if (i == end - 1) {
@ -299,8 +325,8 @@ void apply_vert_layout(swayc_t *container,
for (i = start; i < end; ++i) { for (i = start; i < end; ++i) {
swayc_t *child = container->children->items[i]; swayc_t *child = container->children->items[i];
wlr_log(L_DEBUG, wlr_log(L_DEBUG,
"Calculating arrangement for %p:%d (will scale %f by %f)", "Calculating arrangement for %p:%d (will scale %f by %f)",
child, child->type, height, scale); child, child->type, height, scale);
view_set_position(child->sway_view, x, child_y); view_set_position(child->sway_view, x, child_y);
if (i == end - 1) { 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);
}

View file

@ -63,9 +63,10 @@ static bool _workspace_by_name(swayc_t *view, void *data) {
swayc_t *workspace_by_name(const char *name) { swayc_t *workspace_by_name(const char *name) {
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
swayc_t *current_workspace = NULL, *current_output = NULL; swayc_t *current_workspace = NULL, *current_output = NULL;
if (seat->focus) { swayc_t *focus = sway_seat_get_focus(seat);
current_workspace = swayc_parent_by_type(seat->focus, C_WORKSPACE); if (focus) {
current_output = swayc_parent_by_type(seat->focus, C_OUTPUT); current_workspace = swayc_parent_by_type(focus, C_WORKSPACE);
current_output = swayc_parent_by_type(focus, C_OUTPUT);
} }
if (strcmp(name, "prev") == 0) { if (strcmp(name, "prev") == 0) {
return workspace_prev(current_workspace); return workspace_prev(current_workspace);
@ -102,7 +103,8 @@ swayc_t *workspace_create(const char *name) {
} }
// Otherwise create a new one // Otherwise create a new one
struct sway_seat *seat = input_manager_current_seat(input_manager); 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); parent = swayc_parent_by_type(parent, C_OUTPUT);
return new_workspace(parent, name); return new_workspace(parent, name);
} }
@ -118,9 +120,15 @@ swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
return NULL; 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; int i;
for (i = 0; i < output->children->length; 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[ return output->children->items[
wrap(i + (next ? 1 : -1), output->children->length)]; wrap(i + (next ? 1 : -1), output->children->length)];
} }
@ -193,12 +201,13 @@ bool workspace_switch(swayc_t *workspace) {
return false; return false;
} }
struct sway_seat *seat = input_manager_current_seat(input_manager); 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; return false;
} }
swayc_t *active_ws = seat->focus; swayc_t *active_ws = focus;
if (active_ws->type != C_WORKSPACE) { 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 if (config->auto_back_and_forth
@ -222,16 +231,12 @@ bool workspace_switch(swayc_t *workspace) {
// TODO: Deal with sticky containers // TODO: Deal with sticky containers
wlr_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); 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 swayc_t *next = sway_seat_get_focus_inactive(seat, workspace);
if (workspace->children->length) { if (next == NULL) {
// TODO FOCUS: This is really fucking stupid next = workspace;
sway_seat_set_focus(seat, workspace->children->items[0]);
} else {
sway_seat_set_focus(seat, workspace);
} }
sway_seat_set_focus(seat, next);
swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); 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); arrange_windows(output, -1, -1);
return true; return true;
} }