From 515150229847c9ebdfd0cabb6f0026fca9d57a23 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 4 Feb 2018 13:39:10 -0500 Subject: [PATCH] basic focus overhaul --- include/sway/container.h | 6 ++ include/sway/input/seat.h | 16 ++++- include/sway/layout.h | 6 ++ sway/commands.c | 5 +- sway/commands/kill.c | 7 +- sway/commands/workspace.c | 3 +- sway/desktop/xdg_shell_v6.c | 3 +- sway/input/input-manager.c | 2 +- sway/input/seat.c | 137 ++++++++++++++++++++++++++++++------ sway/tree/container.c | 33 +++++++-- sway/tree/layout.c | 30 ++++++++ sway/tree/workspace.c | 17 +++-- 12 files changed, 225 insertions(+), 40 deletions(-) diff --git a/include/sway/container.h b/include/sway/container.h index 0c66932d..997240bd 100644 --- a/include/sway/container.h +++ b/include/sway/container.h @@ -162,4 +162,10 @@ 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); +/** + * Get a list of containers that are descendents of the container in rendering + * order + */ +list_t *container_list_children(swayc_t *con); + #endif diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index b21cbccb..8d5d6b75 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -12,14 +12,26 @@ struct sway_seat_device { struct wl_list link; // sway_seat::devices }; +struct sway_seat_container { + struct sway_seat *seat; + swayc_t *container; + + struct wl_list link; // sway_seat::focus_stack + + struct wl_listener destroy; +}; + struct sway_seat { struct wlr_seat *wlr_seat; struct seat_config *config; struct sway_cursor *cursor; struct sway_input_manager *input; - swayc_t *focus; + + bool has_focus; + struct wl_list focus_stack; // list of containers in focus order struct wl_listener focus_destroy; + struct wl_listener new_container; struct wl_list devices; // sway_seat_device::link @@ -44,6 +56,8 @@ 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, swayc_t *container); + void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config); #endif diff --git a/include/sway/layout.h b/include/sway/layout.h index af561453..69a66674 100644 --- a/include/sway/layout.h +++ b/include/sway/layout.h @@ -2,6 +2,7 @@ #define _SWAY_LAYOUT_H #include +#include "sway/container.h" struct sway_container; @@ -11,10 +12,15 @@ 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); diff --git a/sway/commands.c b/sway/commands.c index 0d4aa104..6bb4db0b 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -281,7 +281,10 @@ struct cmd_results *handle_command(char *_exec) { seat = sway_input_manager_get_default_seat(input_manager); } if (seat) { - config->handler_context.current_container = seat->focus; + config->handler_context.current_container = + (seat->has_focus ? + sway_seat_get_focus(seat, &root_container) : + NULL); struct cmd_results *res = handler->handle(argc-1, argv+1); if (res->status != CMD_SUCCESS) { free_argv(argc, argv); diff --git a/sway/commands/kill.c b/sway/commands/kill.c index cebf7f3c..4b3666be 100644 --- a/sway/commands/kill.c +++ b/sway/commands/kill.c @@ -10,11 +10,16 @@ struct cmd_results *cmd_kill(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "kill", "Command 'kill' cannot be used in the config file"); } + if (config->handler_context.current_container == NULL) { + wlr_log(L_DEBUG, "no container to kill"); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } enum swayc_types type = config->handler_context.current_container->type; - if (type != C_VIEW || type != C_CONTAINER) { + if (type != C_VIEW && type != C_CONTAINER) { return cmd_results_new(CMD_INVALID, NULL, "Can only kill views and containers with this command"); } + // TODO close arbitrary containers without a view struct sway_view *view = config->handler_context.current_container->sway_view; diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 12984ed4..e7d6cc9f 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -90,7 +90,8 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { free(name); } workspace_switch(ws); - current_container = config->handler_context.seat->focus; + current_container = + sway_seat_get_focus(config->handler_context.seat, &root_container); swayc_t *new_output = swayc_parent_by_type(current_container, C_OUTPUT); if (config->mouse_warping && old_output != new_output) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index ca56a9c0..04d89015 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -135,7 +135,8 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_surface->events.destroy, &sway_surface->destroy); struct sway_seat *seat = input_manager_current_seat(input_manager); - swayc_t *cont = new_view(seat->focus, sway_view); + swayc_t *focus = sway_seat_get_focus(seat, &root_container); + swayc_t *cont = new_view(focus, sway_view); sway_view->swayc = cont; arrange_windows(cont->parent, -1, -1); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index d789c7eb..a406636e 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -282,7 +282,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 (seat->has_focus && sway_seat_get_focus(seat, &root_container) == container) { return true; } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 5e87986d..cbf05abd 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -32,6 +32,44 @@ 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); + wl_list_remove(&seat_con->link); + 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) { + 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; + 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); +} + 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 +90,24 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input, return NULL; } + // init the focus stack + wl_list_init(&seat->focus_stack); + list_t *containers = container_list_children(&root_container); + if (containers == NULL) { + wlr_seat_destroy(seat->wlr_seat); + free(seat); + return NULL; + } + for (int i = containers->length - 1; i >= 0; --i) { + swayc_t *con = containers->items[i]; + seat_container_from_container(seat, con); + } + free(containers); + + 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,12 +138,15 @@ 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) { - // force notify reenter to pick up the new configuration - wlr_seat_keyboard_clear_focus(seat->wlr_seat); - wlr_seat_keyboard_notify_enter(seat->wlr_seat, - seat->focus->sway_view->surface, wlr_keyboard->keycodes, - wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); + if (seat->has_focus) { + swayc_t *focus = sway_seat_get_focus(seat, &root_container); + 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, + focus->sway_view->surface, wlr_keyboard->keycodes, + wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); + } } } @@ -211,35 +270,43 @@ static void handle_focus_destroy(struct wl_listener *listener, void *data) { } void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { - swayc_t *last_focus = seat->focus; + swayc_t *last_focus = + (seat->has_focus ? sway_seat_get_focus(seat, &root_container) : NULL); - if (last_focus == container) { + if (container && last_focus == container) { return; } - if (last_focus && last_focus->type == C_VIEW) { + if (last_focus) { wl_list_remove(&seat->focus_destroy.link); + seat->has_focus = false; } - if (container && container->type == C_VIEW) { - struct sway_view *view = container->sway_view; - view_set_activated(view, true); + if (container) { + struct sway_seat_container *seat_con = + seat_container_from_container(seat, container); wl_signal_add(&container->events.destroy, &seat->focus_destroy); seat->focus_destroy.notify = handle_focus_destroy; - - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - if (keyboard) { - wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, - keyboard->keycodes, keyboard->num_keycodes, - &keyboard->modifiers); - } else { - wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, - NULL, 0, NULL); + seat->has_focus = true; + + wl_list_remove(&seat_con->link); + wl_list_insert(&seat->focus_stack, &seat_con->link); + + if (container->type == C_VIEW) { + struct sway_view *view = container->sway_view; + view_set_activated(view, true); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + if (keyboard) { + wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, + keyboard->keycodes, keyboard->num_keycodes, + &keyboard->modifiers); + } else { + wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, + NULL, 0, NULL); + } } } - seat->focus = container; - if (last_focus && last_focus->type == C_VIEW && !sway_input_manager_has_focus(seat->input, last_focus)) { struct sway_view *view = last_focus->sway_view; @@ -247,6 +314,30 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { } } +swayc_t *sway_seat_get_focus(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) { + if (current->container->type < C_WORKSPACE) { + continue; + } + parent = current->container->parent; + + if (current->container == container) { + return current->container; + } + + while (parent) { + if (parent == container) { + return current->container; + } + parent = parent->parent; + } + } + + return NULL; +} + void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config) { // clear configs diff --git a/sway/tree/container.c b/sway/tree/container.c index 48aabd86..67fac5ee 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -158,12 +158,13 @@ swayc_t *new_output(struct sway_output *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 +186,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 +209,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; } @@ -378,3 +380,26 @@ void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), voi f(container, data); } } + +/** + * Get a list of containers that are descendents of the container in rendering + * order + */ +list_t *container_list_children(swayc_t *con) { + list_t *list = create_list(); + list_t *queue = create_list(); + + list_add(queue, con); + + swayc_t *current = NULL; + while (queue->length) { + current = queue->items[0]; + list_del(queue, 0); + list_add(list, current); + // TODO floating containers + list_cat(queue, current->children); + } + + list_free(queue); + return list; +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 41ff81b2..45f8c3ae 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -48,10 +48,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 +61,34 @@ void init_layout(void) { &root_container.sway_root->output_layout_change); } +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; + // focus new child + parent->focused = active; + 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, diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 23c630b6..ce5b425c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -63,9 +63,10 @@ static bool _workspace_by_name(swayc_t *view, void *data) { swayc_t *workspace_by_name(const char *name) { struct sway_seat *seat = input_manager_current_seat(input_manager); swayc_t *current_workspace = NULL, *current_output = NULL; - if (seat->focus) { - current_workspace = swayc_parent_by_type(seat->focus, C_WORKSPACE); - current_output = swayc_parent_by_type(seat->focus, C_OUTPUT); + if (seat->has_focus) { + swayc_t *focus = sway_seat_get_focus(seat, &root_container); + 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(seat, &root_container); + parent = focus; parent = swayc_parent_by_type(parent, C_OUTPUT); return new_workspace(parent, name); } @@ -193,12 +195,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(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