diff options
Diffstat (limited to 'sway/tree/workspace.c')
-rw-r--r-- | sway/tree/workspace.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index c37a873c..23c630b6 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -2,8 +2,20 @@ #include <stdbool.h> #include <stdlib.h> #include <stdio.h> +#include <strings.h> #include "sway/container.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" +#include "sway/workspace.h" #include "log.h" +#include "util.h" + +char *prev_workspace_name = NULL; +struct workspace_by_number_data { + int len; + const char *cset; + const char *name; +}; void next_name_map(swayc_t *ws, void *data) { int *count = data; @@ -24,3 +36,202 @@ char *workspace_next_name(const char *output_name) { snprintf(name, len + 1, "%d", count); return name; } + +static bool _workspace_by_number(swayc_t *view, void *data) { + if (view->type != C_WORKSPACE) { + return false; + } + struct workspace_by_number_data *wbnd = data; + int a = strspn(view->name, wbnd->cset); + return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0; +} + +swayc_t *workspace_by_number(const char* name) { + struct workspace_by_number_data wbnd = {0, "1234567890", name}; + wbnd.len = strspn(name, wbnd.cset); + if (wbnd.len <= 0) { + return NULL; + } + return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd); +} + +static bool _workspace_by_name(swayc_t *view, void *data) { + return (view->type == C_WORKSPACE) && + (strcasecmp(view->name, (char *) data) == 0); +} + +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 (strcmp(name, "prev") == 0) { + return workspace_prev(current_workspace); + } else if (strcmp(name, "prev_on_output") == 0) { + return workspace_output_prev(current_output); + } else if (strcmp(name, "next") == 0) { + return workspace_next(current_workspace); + } else if (strcmp(name, "next_on_output") == 0) { + return workspace_output_next(current_output); + } else if (strcmp(name, "current") == 0) { + return current_workspace; + } else { + return swayc_by_test(&root_container, _workspace_by_name, (void *) name); + } +} + +swayc_t *workspace_create(const char *name) { + swayc_t *parent; + // Search for workspace<->output pair + int i, e = config->workspace_outputs->length; + for (i = 0; i < e; ++i) { + struct workspace_output *wso = config->workspace_outputs->items[i]; + if (strcasecmp(wso->workspace, name) == 0) { + // Find output to use if it exists + e = root_container.children->length; + for (i = 0; i < e; ++i) { + parent = root_container.children->items[i]; + if (strcmp(parent->name, wso->output) == 0) { + return new_workspace(parent, name); + } + } + break; + } + } + // Otherwise create a new one + struct sway_seat *seat = input_manager_current_seat(input_manager); + parent = seat->focus; + parent = swayc_parent_by_type(parent, C_OUTPUT); + return new_workspace(parent, name); +} + +/** + * Get the previous or next workspace on the specified output. Wraps around at + * the end and beginning. If next is false, the previous workspace is returned, + * otherwise the next one is returned. + */ +swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) { + if (!sway_assert(output->type == C_OUTPUT, + "Argument must be an output, is %d", output->type)) { + return NULL; + } + + int i; + for (i = 0; i < output->children->length; i++) { + if (output->children->items[i] == output->focused) { + return output->children->items[ + wrap(i + (next ? 1 : -1), output->children->length)]; + } + } + + // Doesn't happen, at worst the for loop returns the previously active workspace + return NULL; +} + +/** + * Get the previous or next workspace. If the first/last workspace on an output + * is active, proceed to the previous/next output's previous/next workspace. If + * next is false, the previous workspace is returned, otherwise the next one is + * returned. + */ +swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) { + if (!sway_assert(workspace->type == C_WORKSPACE, + "Argument must be a workspace, is %d", workspace->type)) { + return NULL; + } + + swayc_t *current_output = workspace->parent; + int offset = next ? 1 : -1; + int start = next ? 0 : 1; + int end; + if (next) { + end = current_output->children->length - 1; + } else { + end = current_output->children->length; + } + int i; + for (i = start; i < end; i++) { + if (current_output->children->items[i] == workspace) { + return current_output->children->items[i + offset]; + } + } + + // Given workspace is the first/last on the output, jump to the previous/next output + int num_outputs = root_container.children->length; + for (i = 0; i < num_outputs; i++) { + if (root_container.children->items[i] == current_output) { + swayc_t *next_output = root_container.children->items[ + wrap(i + offset, num_outputs)]; + return workspace_output_prev_next_impl(next_output, next); + } + } + + // Doesn't happen, at worst the for loop returns the previously active workspace on the active output + return NULL; +} + +swayc_t *workspace_output_next(swayc_t *current) { + return workspace_output_prev_next_impl(current, true); +} + +swayc_t *workspace_next(swayc_t *current) { + return workspace_prev_next_impl(current, true); +} + +swayc_t *workspace_output_prev(swayc_t *current) { + return workspace_output_prev_next_impl(current, false); +} + +swayc_t *workspace_prev(swayc_t *current) { + return workspace_prev_next_impl(current, false); +} + +bool workspace_switch(swayc_t *workspace) { + if (!workspace) { + return false; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (!seat || !seat->focus) { + return false; + } + swayc_t *active_ws = seat->focus; + if (active_ws->type != C_WORKSPACE) { + swayc_parent_by_type(seat->focus, C_WORKSPACE); + } + + if (config->auto_back_and_forth + && active_ws == workspace + && prev_workspace_name) { + swayc_t *new_ws = workspace_by_name(prev_workspace_name); + workspace = new_ws ? new_ws : workspace_create(prev_workspace_name); + } + + if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name) + && active_ws != workspace)) { + free(prev_workspace_name); + prev_workspace_name = malloc(strlen(active_ws->name) + 1); + if (!prev_workspace_name) { + wlr_log(L_ERROR, "Unable to allocate previous workspace name"); + return false; + } + strcpy(prev_workspace_name, active_ws->name); + } + + // 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 *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; +} |