aboutsummaryrefslogtreecommitdiff
path: root/sway/tree
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/container.c1024
-rw-r--r--sway/tree/criteria.c451
-rw-r--r--sway/tree/focus.c278
-rw-r--r--sway/tree/layout.c1773
-rw-r--r--sway/tree/output.c278
-rw-r--r--sway/tree/workspace.c373
6 files changed, 0 insertions, 4177 deletions
diff --git a/sway/tree/container.c b/sway/tree/container.c
deleted file mode 100644
index 82c0d877..00000000
--- a/sway/tree/container.c
+++ /dev/null
@@ -1,1024 +0,0 @@
-#define _XOPEN_SOURCE 500
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <strings.h>
-#include <string.h>
-#include <wlr/types/wlr_box.h>
-#include <wlr/types/wlr_output.h>
-#include "sway/config.h"
-#include "sway/container.h"
-#include "sway/workspace.h"
-#include "sway/focus.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "sway/input_state.h"
-#include "sway/ipc-server.h"
-#include "sway/output.h"
-#include "sway/view.h"
-#include "log.h"
-#include "stringop.h"
-
-#define ASSERT_NONNULL(PTR) \
- sway_assert (PTR, #PTR "must be non-null")
-
-static swayc_t *new_swayc(enum swayc_types type) {
- // next id starts at 1 because 0 is assigned to root_container in layout.c
- static size_t next_id = 1;
- swayc_t *c = calloc(1, sizeof(swayc_t));
- if (!c) {
- return NULL;
- }
- c->id = next_id++;
- c->handle = -1;
- c->gaps = -1;
- c->layout = L_NONE;
- c->workspace_layout = L_NONE;
- c->type = type;
- c->nb_master = 1;
- c->nb_slave_groups = 1;
- if (type != C_VIEW) {
- c->children = create_list();
- }
- return c;
-}
-
-static void free_swayc(swayc_t *cont) {
- if (!ASSERT_NONNULL(cont)) {
- return;
- }
- if (cont->children) {
- // remove children until there are no more, free_swayc calls
- // remove_child, which removes child from this container
- while (cont->children->length) {
- free_swayc(cont->children->items[0]);
- }
- list_free(cont->children);
- }
- if (cont->unmanaged) {
- list_free(cont->unmanaged);
- }
- if (cont->floating) {
- while (cont->floating->length) {
- free_swayc(cont->floating->items[0]);
- }
- list_free(cont->floating);
- }
- if (cont->marks) {
- list_foreach(cont->marks, free);
- list_free(cont->marks);
- }
- if (cont->parent) {
- remove_child(cont);
- }
- if (cont->name) {
- free(cont->name);
- }
- if (cont->class) {
- free(cont->class);
- }
- if (cont->instance) {
- free(cont->instance);
- }
- if (cont->app_id) {
- free(cont->app_id);
- }
- if (cont->bg_pid != 0) {
- terminate_swaybg(cont->bg_pid);
- }
- if (cont->border) {
- if (cont->border->buffer) {
- free(cont->border->buffer);
- }
- free(cont->border);
- }
- free(cont);
-}
-
-static void update_root_geometry() {
- int width = 0;
- int height = 0;
- swayc_t *child;
- int child_width;
- int child_height;
-
- for (int i = 0; i < root_container.children->length; ++i) {
- child = root_container.children->items[i];
- child_width = child->width + child->x;
- child_height = child->height + child->y;
- if (child_width > width) {
- width = child_width;
- }
-
- if (child_height > height) {
- height = child_height;
- }
- }
-
- root_container.width = width;
- root_container.height = height;
-}
-
-// New containers
-
-swayc_t *new_output(struct sway_output *sway_output) {
- struct wlr_box size;
- wlr_output_effective_resolution(sway_output->wlr_output,
- &size.width, &size.height);
- const char *name = sway_output->wlr_output->name;
- // Find current outputs to see if this already exists
- {
- int i, len = root_container.children->length;
- for (i = 0; i < len; ++i) {
- swayc_t *op = root_container.children->items[i];
- const char *op_name = op->name;
- if (op_name && name && strcmp(op_name, name) == 0) {
- sway_log(L_DEBUG, "restoring output %p: %s",
- sway_output, op_name);
- return op;
- }
- }
- }
-
- struct output_config *oc = NULL, *all = NULL;
- int i;
- for (i = 0; i < config->output_configs->length; ++i) {
- struct output_config *cur = config->output_configs->items[i];
- if (strcasecmp(name, cur->name) == 0) {
- sway_log(L_DEBUG, "Matched output config for %s", name);
- oc = cur;
- }
- if (strcasecmp("*", cur->name) == 0) {
- sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
- all = cur;
- }
-
- if (oc && all) {
- break;
- }
- }
-
- if (!oc) {
- oc = all;
- }
-
- if (oc && !oc->enabled) {
- return NULL;
- }
-
- swayc_t *output = new_swayc(C_OUTPUT);
- output->_handle.output = sway_output;
- output->name = name ? strdup(name) : NULL;
- output->width = size.width;
- output->height = size.width;
- output->unmanaged = create_list();
- output->bg_pid = 0;
-
- apply_output_config(oc, output);
- add_child(&root_container, output);
- load_swaybars();
-
- // Create workspace
- char *ws_name = NULL;
- swayc_t *ws = NULL;
-
- if (name) {
- for (i = 0; i < config->workspace_outputs->length; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->output, name) == 0) {
- sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output);
- // Check if any other workspaces are using this name
- if ((ws = workspace_by_name(wso->workspace))) {
- // if yes, move those to this output, because they should be here
- move_workspace_to(ws, output);
- } else if (!ws_name) {
- // set a workspace name in case we need to create a default one
- ws_name = strdup(wso->workspace);
- }
- }
- }
- }
-
- if (output->children->length == 0) {
- if (!ws_name) {
- ws_name = workspace_next_name(output->name);
- }
- // create and initialize default workspace
- sway_log(L_DEBUG, "Creating default workspace %s", ws_name);
- ws = new_workspace(output, ws_name);
- ws->is_focused = true;
- } else {
- sort_workspaces(output);
- set_focused_container(output->children->items[0]);
- }
-
- free(ws_name);
- update_root_geometry();
- return output;
-}
-
-swayc_t *new_workspace(swayc_t *output, const char *name) {
- if (!ASSERT_NONNULL(output)) {
- return NULL;
- }
- sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle);
- swayc_t *workspace = new_swayc(C_WORKSPACE);
-
- workspace->prev_layout = L_NONE;
- workspace->layout = default_layout(output);
- workspace->workspace_layout = default_layout(output);
-
- workspace->x = output->x;
- workspace->y = output->y;
- workspace->width = output->width;
- workspace->height = output->height;
- workspace->name = !name ? NULL : strdup(name);
- workspace->visible = false;
- workspace->floating = create_list();
-
- add_child(output, workspace);
- sort_workspaces(output);
-
- return workspace;
-}
-
-swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(child)
- && !sway_assert(!child->is_floating, "cannot create container around floating window")) {
- return NULL;
- }
- swayc_t *cont = new_swayc(C_CONTAINER);
-
- sway_log(L_DEBUG, "creating container %p around %p", cont, child);
-
- cont->prev_layout = L_NONE;
- cont->layout = layout;
- cont->width = child->width;
- cont->height = child->height;
- cont->x = child->x;
- cont->y = child->y;
- cont->visible = child->visible;
- cont->cached_geometry = child->cached_geometry;
- cont->gaps = child->gaps;
-
- /* Container inherits all of workspaces children, layout and whatnot */
- if (child->type == C_WORKSPACE) {
- swayc_t *workspace = child;
- // reorder focus
- cont->focused = workspace->focused;
- workspace->focused = cont;
- // set all children focu to container
- int i;
- for (i = 0; i < workspace->children->length; ++i) {
- ((swayc_t *)workspace->children->items[i])->parent = cont;
- }
- // Swap children
- list_t *tmp_list = workspace->children;
- workspace->children = cont->children;
- cont->children = tmp_list;
- // add container to workspace chidren
- add_child(workspace, cont);
- // give them proper layouts
- cont->layout = workspace->workspace_layout;
- cont->prev_layout = workspace->prev_layout;
- /* TODO: might break shit in move_container!!! workspace->layout = layout; */
- set_focused_container_for(workspace, get_focused_view(workspace));
- } else { // Or is built around container
- swayc_t *parent = replace_child(child, cont);
- if (parent) {
- add_child(cont, child);
- }
- }
- return cont;
-}
-
-swayc_t *new_view(swayc_t *sibling, struct sway_view *view) {
- if (!ASSERT_NONNULL(sibling)) {
- return NULL;
- }
- const char *title = view->iface.get_prop(view, VIEW_PROP_TITLE);
- swayc_t *swayc = new_swayc(C_VIEW);
- sway_log(L_DEBUG, "Adding new view %p:%s to container %p %d",
- swayc, title, sibling, sibling ? sibling->type : 0);
- // Setup values
- swayc->_handle.view = view;
-
- swayc->name = title ? strdup(title) : NULL;
-
- const char *class = view->iface.get_prop(view, VIEW_PROP_CLASS);
- swayc->class = class ? strdup(class) : NULL;
-
- const char *instance = view->iface.get_prop(view, VIEW_PROP_INSTANCE);
- swayc->instance = instance ? strdup(instance) : NULL;
-
- const char *app_id = view->iface.get_prop(view, VIEW_PROP_APP_ID);
- swayc->app_id = app_id ? strdup(app_id) : NULL;
-
- swayc->visible = true;
- swayc->is_focused = true;
- swayc->sticky = false;
- swayc->width = 0;
- swayc->height = 0;
- swayc->desired_width = -1;
- swayc->desired_height = -1;
- // setup border
- swayc->border_type = config->border;
- swayc->border_thickness = config->border_thickness;
-
- swayc->is_floating = false;
-
- if (sibling->type == C_WORKSPACE) {
- // Case of focused workspace, just create as child of it
- add_child(sibling, swayc);
- } else {
- // Regular case, create as sibling of current container
- add_sibling(sibling, swayc);
- }
- return swayc;
-}
-
-swayc_t *new_floating_view(wlc_handle handle) {
- if (swayc_active_workspace() == NULL) {
- return NULL;
- }
- const char *title = wlc_view_get_title(handle);
- swayc_t *view = new_swayc(C_VIEW);
- sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%x:%s as a floating view",
- handle, wlc_view_get_type(handle), title);
- // Setup values
- view->handle = handle;
- view->name = title ? strdup(title) : NULL;
- const char *class = wlc_view_get_class(handle);
- view->class = class ? strdup(class) : NULL;
- const char *instance = wlc_view_get_instance(handle);
- view->instance = instance ? strdup(instance) : NULL;
- const char *app_id = wlc_view_get_app_id(handle);
- view->app_id = app_id ? strdup(app_id) : NULL;
- view->visible = true;
- view->sticky = false;
-
- // Set the geometry of the floating view
- const struct wlc_geometry *geometry = wlc_view_get_geometry(handle);
-
- // give it requested geometry, but place in center if possible
- // in top left otherwise
- if (geometry->size.w != 0) {
- view->x = (swayc_active_workspace()->width - geometry->size.w) / 2;
- } else {
- view->x = 0;
- }
- if (geometry->size.h != 0) {
- view->y = (swayc_active_workspace()->height - geometry->size.h) / 2;
- } else {
- view->y = 0;
- }
-
- view->width = geometry->size.w;
- view->height = geometry->size.h;
-
- view->desired_width = view->width;
- view->desired_height = view->height;
-
- // setup border
- view->border_type = config->floating_border;
- view->border_thickness = config->floating_border_thickness;
-
- view->is_floating = true;
-
- // Case of focused workspace, just create as child of it
- list_add(swayc_active_workspace()->floating, view);
- view->parent = swayc_active_workspace();
- if (swayc_active_workspace()->focused == NULL) {
- set_focused_container_for(swayc_active_workspace(), view);
- }
- return view;
-}
-
-void floating_view_sane_size(swayc_t *view) {
- // floating_minimum is used as sane value.
- // floating_maximum has priority in case of conflict
- // TODO: implement total_outputs_dimensions()
- if (config->floating_minimum_height != -1 &&
- view->desired_height < config->floating_minimum_height) {
- view->desired_height = config->floating_minimum_height;
- }
- if (config->floating_minimum_width != -1 &&
- view->desired_width < config->floating_minimum_width) {
- view->desired_width = config->floating_minimum_width;
- }
-
- // if 0 do not resize, only enforce max value
- if (config->floating_maximum_height == 0) {
- // Missing total_outputs_dimensions() using swayc_active_workspace()
- config->floating_maximum_height = swayc_active_workspace()->height;
-
- } else if (config->floating_maximum_height != -1 &&
- view->desired_height > config->floating_maximum_height) {
- view->desired_height = config->floating_maximum_height;
- }
-
- // if 0 do not resize, only enforce max value
- if (config->floating_maximum_width == 0) {
- // Missing total_outputs_dimensions() using swayc_active_workspace()
- config->floating_maximum_width = swayc_active_workspace()->width;
-
- } else if (config->floating_maximum_width != -1 &&
- view->desired_width > config->floating_maximum_width) {
- view->desired_width = config->floating_maximum_width;
- }
-
- sway_log(L_DEBUG, "Sane values for view to %d x %d @ %.f, %.f",
- view->desired_width, view->desired_height, view->x, view->y);
-
- return;
-}
-
-
-// Destroy container
-
-swayc_t *destroy_output(swayc_t *output) {
- if (!ASSERT_NONNULL(output)) {
- return NULL;
- }
- if (output->children->length > 0) {
- // TODO save workspaces when there are no outputs.
- // TODO also check if there will ever be no outputs except for exiting
- // program
- if (root_container.children->length > 1) {
- int p = root_container.children->items[0] == output;
- // Move workspace from this output to another output
- while (output->children->length) {
- swayc_t *child = output->children->items[0];
- remove_child(child);
- add_child(root_container.children->items[p], child);
- }
- sort_workspaces(root_container.children->items[p]);
- update_visibility(root_container.children->items[p]);
- arrange_windows(root_container.children->items[p], -1, -1);
- }
- }
- sway_log(L_DEBUG, "OUTPUT: Destroying output '%" PRIuPTR "'", output->handle);
- free_swayc(output);
- update_root_geometry();
- return &root_container;
-}
-
-swayc_t *destroy_workspace(swayc_t *workspace) {
- if (!ASSERT_NONNULL(workspace)) {
- return NULL;
- }
-
- // Do not destroy this if it's the last workspace on this output
- swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
- if (output && output->children->length == 1) {
- return NULL;
- }
-
- swayc_t *parent = workspace->parent;
- // destroy the WS if there are no children
- if (workspace->children->length == 0 && workspace->floating->length == 0) {
- sway_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
- ipc_event_workspace(workspace, NULL, "empty");
- } else {
- // Move children to a different workspace on this output
- swayc_t *new_workspace = NULL;
- int i;
- for(i = 0; i < output->children->length; i++) {
- if(output->children->items[i] != workspace) {
- break;
- }
- }
- new_workspace = output->children->items[i];
-
- sway_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
- workspace->name, new_workspace->name);
-
- for(i = 0; i < workspace->children->length; i++) {
- move_container_to(workspace->children->items[i], new_workspace);
- }
-
- for(i = 0; i < workspace->floating->length; i++) {
- move_container_to(workspace->floating->items[i], new_workspace);
- }
- }
-
- free_swayc(workspace);
- return parent;
-}
-
-swayc_t *destroy_container(swayc_t *container) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- while (container->children->length == 0 && container->type == C_CONTAINER) {
- sway_log(L_DEBUG, "Container: Destroying container '%p'", container);
- swayc_t *parent = container->parent;
- free_swayc(container);
- container = parent;
- }
- return container;
-}
-
-swayc_t *destroy_view(swayc_t *view) {
- if (!ASSERT_NONNULL(view)) {
- return NULL;
- }
- sway_log(L_DEBUG, "Destroying view '%p'", view);
- swayc_t *parent = view->parent;
- free_swayc(view);
-
- // Destroy empty containers
- if (parent && parent->type == C_CONTAINER) {
- return destroy_container(parent);
- }
- return parent;
-}
-
-// Container lookup
-
-
-swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) {
- if (!container->children) {
- return NULL;
- }
- // Special case for checking floating stuff
- int i;
- if (container->type == C_WORKSPACE) {
- for (i = 0; i < container->floating->length; ++i) {
- swayc_t *child = container->floating->items[i];
- if (test(child, data)) {
- return child;
- }
- }
- }
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- if (test(child, data)) {
- return child;
- } else {
- swayc_t *res = swayc_by_test(child, test, data);
- if (res) {
- return res;
- }
- }
- }
- return NULL;
-}
-
-static bool test_name(swayc_t *view, void *data) {
- if (!view || !view->name) {
- return false;
- }
- return strcmp(view->name, data) == 0;
-}
-
-swayc_t *swayc_by_name(const char *name) {
- return swayc_by_test(&root_container, test_name, (void *)name);
-}
-
-swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
- return NULL;
- }
- do {
- container = container->parent;
- } while (container && container->type != type);
- return container;
-}
-
-swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
- return NULL;
- }
- do {
- container = container->parent;
- } while (container && container->layout != layout);
- return container;
-}
-
-swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types type) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
- return NULL;
- }
- do {
- container = container->focused;
- } while (container && container->type != type);
- return container;
-}
-
-swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
- return NULL;
- }
- do {
- container = container->focused;
- } while (container && container->layout != layout);
- return container;
-}
-
-
-static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) {
- if (!parent || !parent->children) {
- return NULL;
- }
- int i, len;
- swayc_t **child;
- if (parent->type == C_WORKSPACE) {
- len = parent->floating->length;
- child = (swayc_t **)parent->floating->items;
- for (i = 0; i < len; ++i, ++child) {
- if ((*child)->handle == handle) {
- return *child;
- }
- }
- }
-
- len = parent->children->length;
- child = (swayc_t**)parent->children->items;
- for (i = 0; i < len; ++i, ++child) {
- if ((*child)->handle == handle) {
- return *child;
- } else {
- swayc_t *res;
- if ((res = _swayc_by_handle_helper(handle, *child))) {
- return res;
- }
- }
- }
- return NULL;
-}
-
-swayc_t *swayc_by_handle(wlc_handle handle) {
- return _swayc_by_handle_helper(handle, &root_container);
-}
-
-swayc_t *swayc_active_output(void) {
- return root_container.focused;
-}
-
-swayc_t *swayc_active_workspace(void) {
- return root_container.focused ? root_container.focused->focused : NULL;
-}
-
-swayc_t *swayc_active_workspace_for(swayc_t *cont) {
- if (!cont) {
- return NULL;
- }
- switch (cont->type) {
- case C_ROOT:
- cont = cont->focused;
- /* Fallthrough */
-
- case C_OUTPUT:
- cont = cont ? cont->focused : NULL;
- /* Fallthrough */
-
- case C_WORKSPACE:
- return cont;
-
- default:
- return swayc_parent_by_type(cont, C_WORKSPACE);
- }
-}
-
-static bool pointer_test(swayc_t *view, void *_origin) {
- const struct wlc_point *origin = _origin;
- // Determine the output that the view is under
- swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT);
- if (origin->x >= view->x && origin->y >= view->y
- && origin->x < view->x + view->width && origin->y < view->y + view->height
- && view->visible && parent == root_container.focused) {
- return true;
- }
- return false;
-}
-
-swayc_t *container_under_pointer(void) {
- // root.output->workspace
- if (!root_container.focused) {
- return NULL;
- }
- swayc_t *lookup = root_container.focused;
- // Case of empty workspace
- if (lookup->children && !lookup->unmanaged) {
- return NULL;
- }
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- struct wlc_point origin = { .x = x, .y = y };
-
- while (lookup && lookup->type != C_VIEW) {
- int i;
- int len;
- for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) {
- wlc_handle *handle = lookup->unmanaged->items[_i];
- const struct wlc_geometry *geo = wlc_view_get_geometry(*handle);
- if (origin.x >= geo->origin.x && origin.y >= geo->origin.y
- && origin.x < geo->origin.x + (int)geo->size.w
- && origin.y < geo->origin.y + (int)geo->size.h) {
- // Hack: we force focus upon unmanaged views here
- wlc_view_focus(*handle);
- return NULL;
- }
- }
- // if tabbed/stacked go directly to focused container, otherwise search
- // children
- if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) {
- lookup = lookup->focused;
- continue;
- }
- // if workspace, search floating
- if (lookup->type == C_WORKSPACE) {
- i = len = lookup->floating->length;
- bool got_floating = false;
- while (--i > -1) {
- if (pointer_test(lookup->floating->items[i], &origin)) {
- lookup = lookup->floating->items[i];
- got_floating = true;
- break;
- }
- }
- if (got_floating) {
- continue;
- }
- }
- // search children
- len = lookup->children->length;
- for (i = 0; i < len; ++i) {
- if (pointer_test(lookup->children->items[i], &origin)) {
- lookup = lookup->children->items[i];
- break;
- }
- }
- // when border and titles are done, this could happen
- if (i == len) {
- break;
- }
- }
- return lookup;
-}
-
-swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data) {
- if (container->children == NULL || container->children->length == 0) {
- return NULL;
- }
-
- swayc_t *con;
- if (container->type == C_WORKSPACE) {
- for (int i = 0; i < container->floating->length; ++i) {
- con = container->floating->items[i];
- if (f(con, data)) {
- return con;
- }
- con = container_find(con, f, data);
- if (con != NULL) {
- return con;
- }
- }
- }
-
- for (int i = 0; i < container->children->length; ++i) {
- con = container->children->items[i];
- if (f(con, data)) {
- return con;
- }
-
- con = container_find(con, f, data);
- if (con != NULL) {
- return con;
- }
- }
-
- return NULL;
-}
-
-// Container information
-
-bool swayc_is_fullscreen(swayc_t *view) {
- return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN);
-}
-
-bool swayc_is_active(swayc_t *view) {
- return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED);
-}
-
-bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) {
- while (child != &root_container) {
- child = child->parent;
- if (child == parent) {
- return true;
- }
- }
- return false;
-}
-
-bool swayc_is_child_of(swayc_t *child, swayc_t *parent) {
- return swayc_is_parent_of(parent, child);
-}
-
-bool swayc_is_empty_workspace(swayc_t *container) {
- return container->type == C_WORKSPACE && container->children->length == 0;
-}
-
-int swayc_gap(swayc_t *container) {
- if (container->type == C_VIEW || container->type == C_CONTAINER) {
- return container->gaps >= 0 ? container->gaps : config->gaps_inner;
- } else if (container->type == C_WORKSPACE) {
- int base = container->gaps >= 0 ? container->gaps : config->gaps_outer;
- if (config->edge_gaps && !(config->smart_gaps && container->children->length == 1)) {
- // the inner gap is created via a margin around each window which
- // is half the gap size, so the workspace also needs half a gap
- // size to make the outermost gap the same size (excluding the
- // actual "outer gap" size which is handled independently)
- return base + config->gaps_inner / 2;
- } else if (config->smart_gaps && container->children->length == 1) {
- return 0;
- } else {
- return base;
- }
- } else {
- return 0;
- }
-}
-
-// Mapping
-
-void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) {
- if (container) {
- int i;
- if (container->children) {
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- container_map(child, f, data);
- }
- }
- if (container->floating) {
- for (i = 0; i < container->floating->length; ++i) {
- swayc_t *child = container->floating->items[i];
- container_map(child, f, data);
- }
- }
- f(container, data);
- }
-}
-
-void update_visibility_output(swayc_t *container, wlc_handle output) {
- // Inherit visibility
- swayc_t *parent = container->parent;
- container->visible = parent->visible;
- // special cases where visibility depends on focus
- if (parent->type == C_OUTPUT || parent->layout == L_TABBED ||
- parent->layout == L_STACKED) {
- container->visible = parent->focused == container && parent->visible;
- }
- // Set visibility and output for view
- if (container->type == C_VIEW) {
- wlc_view_set_output(container->handle, output);
- wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0);
- }
- // Update visibility for children
- else {
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->children->items[i], output);
- }
- }
- if (container->floating) {
- int i, len = container->floating->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->floating->items[i], output);
- }
- }
- }
-}
-
-void update_visibility(swayc_t *container) {
- if (!container) return;
- switch (container->type) {
- case C_ROOT:
- container->visible = true;
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility(container->children->items[i]);
- }
- }
- return;
-
- case C_OUTPUT:
- container->visible = true;
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->children->items[i], container->handle);
- }
- }
- return;
-
- default:
- {
- swayc_t *op = swayc_parent_by_type(container, C_OUTPUT);
- update_visibility_output(container, op->handle);
- }
- }
-}
-
-void set_gaps(swayc_t *view, void *_data) {
- int *data = _data;
- if (!ASSERT_NONNULL(view)) {
- return;
- }
- if (view->type == C_WORKSPACE || view->type == C_VIEW) {
- view->gaps = *data;
- }
-}
-
-void add_gaps(swayc_t *view, void *_data) {
- int *data = _data;
- if (!ASSERT_NONNULL(view)) {
- return;
- }
- if (view->type == C_WORKSPACE || view->type == C_VIEW) {
- if ((view->gaps += *data) < 0) {
- view->gaps = 0;
- }
- }
-}
-
-static void close_view(swayc_t *container, void *data) {
- if (container->type == C_VIEW) {
- wlc_view_close(container->handle);
- }
-}
-
-void close_views(swayc_t *container) {
- container_map(container, close_view, NULL);
-}
-
-swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view) {
- swayc_t *parent = NULL;
- if (!ASSERT_NONNULL(view)) {
- return NULL;
- }
- while (view->type != C_WORKSPACE && view->parent && view->parent->type != C_WORKSPACE) {
- view = view->parent;
- if (view->layout == L_TABBED || view->layout == L_STACKED) {
- parent = view;
- }
- }
-
- return parent;
-}
-
-swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
- if (!ASSERT_NONNULL(con)) {
- return NULL;
- }
- if (con->parent && (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED)) {
- return con->parent;
- }
- return NULL;
-}
-
-swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
- // if layout change modifies the auto layout's major axis, swap width and height
- // to preserve current ratios.
- if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
- enum swayc_layouts prev_major =
- container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT
- ? L_HORIZ : L_VERT;
- enum swayc_layouts new_major =
- layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT
- ? L_HORIZ : L_VERT;
- if (new_major != prev_major) {
- for (int i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- double h = child->height;
- child->height = child->width;
- child->width = h;
- }
- }
- }
- if (container->type == C_WORKSPACE) {
- container->workspace_layout = layout;
- if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
- container->layout = layout;
- }
- } else {
- container->layout = layout;
- }
- return container;
-}
diff --git a/sway/tree/criteria.c b/sway/tree/criteria.c
deleted file mode 100644
index e8978ebe..00000000
--- a/sway/tree/criteria.c
+++ /dev/null
@@ -1,451 +0,0 @@
-#define _XOPEN_SOURCE 700
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <pcre.h>
-#include "sway/criteria.h"
-#include "sway/container.h"
-#include "sway/config.h"
-#include "stringop.h"
-#include "list.h"
-#include "log.h"
-
-enum criteria_type { // *must* keep in sync with criteria_strings[]
- CRIT_CLASS,
- CRIT_CON_ID,
- CRIT_CON_MARK,
- CRIT_FLOATING,
- CRIT_ID,
- CRIT_INSTANCE,
- CRIT_TILING,
- CRIT_TITLE,
- CRIT_URGENT,
- CRIT_WINDOW_ROLE,
- CRIT_WINDOW_TYPE,
- CRIT_WORKSPACE,
- CRIT_LAST
-};
-
-static const char * const criteria_strings[CRIT_LAST] = {
- [CRIT_CLASS] = "class",
- [CRIT_CON_ID] = "con_id",
- [CRIT_CON_MARK] = "con_mark",
- [CRIT_FLOATING] = "floating",
- [CRIT_ID] = "id",
- [CRIT_INSTANCE] = "instance",
- [CRIT_TILING] = "tiling",
- [CRIT_TITLE] = "title",
- [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ...
- [CRIT_WINDOW_ROLE] = "window_role",
- [CRIT_WINDOW_TYPE] = "window_type",
- [CRIT_WORKSPACE] = "workspace"
-};
-
-/**
- * A single criteria token (ie. value/regex pair),
- * e.g. 'class="some class regex"'.
- */
-struct crit_token {
- enum criteria_type type;
- pcre *regex;
- char *raw;
-};
-
-static void free_crit_token(struct crit_token *crit) {
- pcre_free(crit->regex);
- free(crit->raw);
- free(crit);
-}
-
-static void free_crit_tokens(list_t *crit_tokens) {
- for (int i = 0; i < crit_tokens->length; i++) {
- free_crit_token(crit_tokens->items[i]);
- }
- list_free(crit_tokens);
-}
-
-// Extracts criteria string from its brackets. Returns new (duplicate)
-// substring.
-static char *criteria_from(const char *arg) {
- char *criteria = NULL;
- if (*arg == '[') {
- criteria = strdup(arg + 1);
- } else {
- criteria = strdup(arg);
- }
-
- int last = strlen(criteria) - 1;
- if (criteria[last] == ']') {
- criteria[last] = '\0';
- }
- return criteria;
-}
-
-// Return instances of c found in str.
-static int countchr(char *str, char c) {
- int found = 0;
- for (int i = 0; str[i]; i++) {
- if (str[i] == c) {
- ++found;
- }
- }
- return found;
-}
-
-// criteria_str is e.g. '[class="some class regex" instance="instance name"]'.
-//
-// Will create array of pointers in buf, where first is duplicate of given
-// string (must be freed) and the rest are pointers to names and values in the
-// base string (every other, naturally). argc will be populated with the length
-// of buf.
-//
-// Returns error string or NULL if successful.
-static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) {
- sway_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
- char *base = criteria_from(criteria_str);
- char *head = base;
- char *namep = head; // start of criteria name
- char *valp = NULL; // start of value
-
- // We're going to place EOS markers where we need to and fill up an array
- // of pointers to the start of each token (either name or value).
- int pairs = countchr(base, '=');
- int max_tokens = pairs * 2 + 1; // this gives us at least enough slots
-
- char **argv = *buf = calloc(max_tokens, sizeof(char*));
- argv[0] = base; // this needs to be freed by caller
- bool quoted = true;
-
- *argc = 1; // uneven = name, even = value
- while (*head && *argc < max_tokens) {
- if (namep != head && *(head - 1) == '\\') {
- // escaped character: don't try to parse this
- } else if (*head == '=' && namep != head) {
- if (*argc % 2 != 1) {
- // we're not expecting a name
- return strdup("Unable to parse criteria: "
- "Found out of place equal sign");
- } else {
- // name ends here
- char *end = head; // don't want to rewind the head
- while (*(end - 1) == ' ') {
- --end;
- }
- *end = '\0';
- if (*(namep) == ' ') {
- namep = strrchr(namep, ' ') + 1;
- }
- argv[*argc] = namep;
- *argc += 1;
- }
- } else if (*head == '"') {
- if (*argc % 2 != 0) {
- // we're not expecting a value
- return strdup("Unable to parse criteria: "
- "Found quoted value where it was not expected");
- } else if (!valp) { // value starts here
- valp = head + 1;
- quoted = true;
- } else {
- // value ends here
- argv[*argc] = valp;
- *argc += 1;
- *head = '\0';
- valp = NULL;
- namep = head + 1;
- }
- } else if (*argc % 2 == 0 && *head != ' ') {
- // parse unquoted values
- if (!valp) {
- quoted = false;
- valp = head; // value starts here
- }
- } else if (valp && !quoted && *head == ' ') {
- // value ends here
- argv[*argc] = valp;
- *argc += 1;
- *head = '\0';
- valp = NULL;
- namep = head + 1;
- }
- head++;
- }
-
- // catch last unquoted value if needed
- if (valp && !quoted && !*head) {
- argv[*argc] = valp;
- *argc += 1;
- }
-
- return NULL;
-}
-
-// Returns error string on failure or NULL otherwise.
-static char *parse_criteria_name(enum criteria_type *type, char *name) {
- *type = CRIT_LAST;
- for (int i = 0; i < CRIT_LAST; i++) {
- if (strcmp(criteria_strings[i], name) == 0) {
- *type = (enum criteria_type) i;
- break;
- }
- }
- if (*type == CRIT_LAST) {
- const char *fmt = "Criteria type '%s' is invalid or unsupported.";
- int len = strlen(name) + strlen(fmt) - 1;
- char *error = malloc(len);
- snprintf(error, len, fmt, name);
- return error;
- } else if (*type == CRIT_URGENT || *type == CRIT_WINDOW_ROLE ||
- *type == CRIT_WINDOW_TYPE) {
- // (we're just being helpful here)
- const char *fmt = "\"%s\" criteria currently unsupported, "
- "no window will match this";
- int len = strlen(fmt) + strlen(name) - 1;
- char *error = malloc(len);
- snprintf(error, len, fmt, name);
- return error;
- }
- return NULL;
-}
-
-// Returns error string on failure or NULL otherwise.
-static char *generate_regex(pcre **regex, char *value) {
- const char *reg_err;
- int offset;
-
- *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
-
- if (!*regex) {
- const char *fmt = "Regex compilation (for '%s') failed: %s";
- int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3;
- char *error = malloc(len);
- snprintf(error, len, fmt, value, reg_err);
- return error;
- }
- return NULL;
-}
-
-// Test whether the criterion corresponds to the currently focused window
-static bool crit_is_focused(const char *value) {
- return !strcmp(value, "focused") || !strcmp(value, "__focused__");
-}
-
-// Populate list with crit_tokens extracted from criteria string, returns error
-// string or NULL if successful.
-char *extract_crit_tokens(list_t *tokens, const char * const criteria) {
- int argc;
- char **argv = NULL, *error = NULL;
- if ((error = crit_tokens(&argc, &argv, criteria))) {
- goto ect_cleanup;
- }
- for (int i = 1; i + 1 < argc; i += 2) {
- char* name = argv[i], *value = argv[i + 1];
- struct crit_token *token = calloc(1, sizeof(struct crit_token));
- token->raw = strdup(value);
-
- if ((error = parse_criteria_name(&token->type, name))) {
- free_crit_token(token);
- goto ect_cleanup;
- } else if (token->type == CRIT_URGENT || crit_is_focused(value)) {
- sway_log(L_DEBUG, "%s -> \"%s\"", name, value);
- list_add(tokens, token);
- } else if((error = generate_regex(&token->regex, value))) {
- free_crit_token(token);
- goto ect_cleanup;
- } else {
- sway_log(L_DEBUG, "%s -> /%s/", name, value);
- list_add(tokens, token);
- }
- }
-ect_cleanup:
- free(argv[0]); // base string
- free(argv);
- return error;
-}
-
-static int regex_cmp(const char *item, const pcre *regex) {
- return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
-}
-
-// test a single view if it matches list of criteria tokens (all of them).
-static bool criteria_test(swayc_t *cont, list_t *tokens) {
- if (cont->type != C_VIEW) {
- return false;
- }
- int matches = 0;
- for (int i = 0; i < tokens->length; i++) {
- struct crit_token *crit = tokens->items[i];
- switch (crit->type) {
- case CRIT_CLASS:
- if (!cont->class) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->class && strcmp(cont->class, focused->class) == 0) {
- matches++;
- }
- } else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) {
- matches++;
- }
- break;
- case CRIT_CON_ID: {
- char *endptr;
- size_t crit_id = strtoul(crit->raw, &endptr, 10);
-
- if (*endptr == 0 && cont->id == crit_id) {
- ++matches;
- }
- break;
- }
- case CRIT_CON_MARK:
- if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
- // Make sure it isn't matching the NUL string
- if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
- ++matches;
- }
- }
- break;
- case CRIT_FLOATING:
- if (cont->is_floating) {
- matches++;
- }
- break;
- case CRIT_ID:
- if (!cont->app_id) {
- // ignore
- } else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) {
- matches++;
- }
- break;
- case CRIT_INSTANCE:
- if (!cont->instance) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->instance && strcmp(cont->instance, focused->instance) == 0) {
- matches++;
- }
- } else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) {
- matches++;
- }
- break;
- case CRIT_TILING:
- if (!cont->is_floating) {
- matches++;
- }
- break;
- case CRIT_TITLE:
- if (!cont->name) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->name && strcmp(cont->name, focused->name) == 0) {
- matches++;
- }
- } else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) {
- matches++;
- }
- break;
- case CRIT_URGENT: // "latest" or "oldest"
- break;
- case CRIT_WINDOW_ROLE:
- break;
- case CRIT_WINDOW_TYPE:
- // TODO wlc indeed exposes this information
- break;
- case CRIT_WORKSPACE: ;
- swayc_t *cont_ws = swayc_parent_by_type(cont, C_WORKSPACE);
- if (!cont_ws || !cont_ws->name) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused_ws = swayc_active_workspace();
- if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) {
- matches++;
- }
- } else if (crit->regex && regex_cmp(cont_ws->name, crit->regex) == 0) {
- matches++;
- }
- break;
- default:
- sway_abort("Invalid criteria type (%i)", crit->type);
- break;
- }
- }
- return matches == tokens->length;
-}
-
-int criteria_cmp(const void *a, const void *b) {
- if (a == b) {
- return 0;
- } else if (!a) {
- return -1;
- } else if (!b) {
- return 1;
- }
- const struct criteria *crit_a = a, *crit_b = b;
- int cmp = lenient_strcmp(crit_a->cmdlist, crit_b->cmdlist);
- if (cmp != 0) {
- return cmp;
- }
- return lenient_strcmp(crit_a->crit_raw, crit_b->crit_raw);
-}
-
-void free_criteria(struct criteria *crit) {
- if (crit->tokens) {
- free_crit_tokens(crit->tokens);
- }
- if (crit->cmdlist) {
- free(crit->cmdlist);
- }
- if (crit->crit_raw) {
- free(crit->crit_raw);
- }
- free(crit);
-}
-
-bool criteria_any(swayc_t *cont, list_t *criteria) {
- for (int i = 0; i < criteria->length; i++) {
- struct criteria *bc = criteria->items[i];
- if (criteria_test(cont, bc->tokens)) {
- return true;
- }
- }
- return false;
-}
-
-list_t *criteria_for(swayc_t *cont) {
- list_t *criteria = config->criteria, *matches = create_list();
- for (int i = 0; i < criteria->length; i++) {
- struct criteria *bc = criteria->items[i];
- if (criteria_test(cont, bc->tokens)) {
- list_add(matches, bc);
- }
- }
- return matches;
-}
-
-struct list_tokens {
- list_t *list;
- list_t *tokens;
-};
-
-static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) {
- if (criteria_test(container, list_tokens->tokens)) {
- list_add(list_tokens->list, container);
- }
-}
-
-list_t *container_for(list_t *tokens) {
- struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens};
-
- container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens);
-
- for (int i = 0; i < scratchpad->length; ++i) {
- swayc_t *c = scratchpad->items[i];
- if (criteria_test(c, tokens)) {
- list_add(list_tokens.list, c);
- }
- }
-
- return list_tokens.list;
-}
diff --git a/sway/tree/focus.c b/sway/tree/focus.c
deleted file mode 100644
index 66f7ee17..00000000
--- a/sway/tree/focus.c
+++ /dev/null
@@ -1,278 +0,0 @@
-#include "stdbool.h"
-#include <wlc/wlc.h>
-#include "sway/focus.h"
-#include "sway/workspace.h"
-#include "sway/layout.h"
-#include "sway/config.h"
-#include "sway/input_state.h"
-#include "sway/ipc-server.h"
-#include "sway/border.h"
-#include "log.h"
-
-bool locked_container_focus = false;
-bool suspend_workspace_cleanup = false;
-
-// switches parent focus to c. will switch it accordingly
-static void update_focus(swayc_t *c) {
- // Handle if focus switches
- swayc_t *parent = c->parent;
- if (!parent) return;
- if (parent->focused != c) {
- // Get previous focus
- swayc_t *prev = parent->focused;
- // Set new focus
- parent->focused = c;
-
- switch (c->type) {
- // Shouldn't happen
- case C_ROOT: return;
-
- // Case where output changes
- case C_OUTPUT:
- wlc_output_focus(c->handle);
- break;
-
- // Case where workspace changes
- case C_WORKSPACE:
- if (prev) {
- ipc_event_workspace(prev, c, "focus");
-
- // if the old workspace has no children, destroy it
- if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) {
- destroy_workspace(prev);
- } else {
- // update visibility of old workspace
- update_visibility(prev);
- }
- }
- // Update visibility of newly focused workspace
- update_visibility(c);
- break;
-
- default:
- case C_VIEW:
- case C_CONTAINER:
- break;
- }
- }
-}
-
-bool move_focus(enum movement_direction direction) {
- swayc_t *old_view = get_focused_container(&root_container);
- swayc_t *new_view = get_swayc_in_direction(old_view, direction);
- if (!new_view) {
- return false;
- } else if (new_view->type == C_ROOT) {
- sway_log(L_DEBUG, "Not setting focus above the workspace level");
- return false;
- } else if (new_view->type == C_OUTPUT) {
- return set_focused_container(swayc_active_workspace_for(new_view));
- } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) {
- return set_focused_container(new_view);
- } else if (config->mouse_warping) {
- swayc_t *old_op = old_view->type == C_OUTPUT ?
- old_view : swayc_parent_by_type(old_view, C_OUTPUT);
- swayc_t *focused = get_focused_view(new_view);
- if (set_focused_container(focused)) {
- if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) {
- center_pointer_on(focused);
- }
- return true;
- }
- } else {
- return set_focused_container(get_focused_view(new_view));
- }
- return false;
-}
-
-swayc_t *get_focused_container(swayc_t *parent) {
- if (!parent) {
- return swayc_active_workspace();
- }
- while (!parent->is_focused && parent->focused) {
- parent = parent->focused;
- }
- return parent;
-}
-
-bool set_focused_container(swayc_t *c) {
- if (locked_container_focus || !c || !c->parent) {
- return false;
- }
-
- // current ("old") workspace for sending workspace change event later
- swayc_t *old_ws = swayc_active_workspace();
- // keep track of child count so we can determine if it gets destroyed
- int old_ws_child_count = 0;
- if (old_ws) {
- old_ws_child_count = old_ws->children->length + old_ws->floating->length;
- }
-
- // current ("old") focused container
- swayc_t *old_focus = get_focused_container(&root_container);
- // if old_focus is a workspace, then it's the same workspace as
- // old_ws, and we'll need to null its pointer too, since it will
- // be destroyed in the update_focus() call
- bool old_focus_was_ws = (old_focus->type == C_WORKSPACE);
-
- // workspace of new focused container
- swayc_t *workspace = swayc_active_workspace_for(c);
-
- if (swayc_is_fullscreen(get_focused_container(workspace))) {
- // if switching to a workspace with a fullscreen view,
- // focus on the fullscreen view
- c = get_focused_container(workspace);
- }
-
- swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle);
-
- if (c->type == C_VIEW) {
- // dispatch a window event
- ipc_event_window(c, "focus");
- }
-
- // update the global pointer
- current_focus = c;
-
- // update container focus from here to root, making necessary changes along
- // the way
- swayc_t *p = c;
- if (p->type != C_OUTPUT && p->type != C_ROOT) {
- p->is_focused = true;
- }
- while (p != &root_container) {
- update_focus(p);
- p = p->parent;
- p->is_focused = false;
- }
-
- if (old_focus_was_ws && old_ws_child_count == 0) {
- // this workspace was destroyed in update_focus(), so null the pointers
- old_focus = NULL;
- old_ws = NULL;
- }
-
- if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) {
- if (old_focus) {
- if (old_focus->type == C_VIEW) {
- wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false);
- }
- update_container_border(old_focus);
- }
- if (c->type == C_VIEW) {
- wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true);
- }
- /* TODO WLR
- if (!desktop_shell.is_locked) {
- // If the system is locked, we do everything _but_ actually setting
- // focus. This includes making our internals think that this view is
- // focused.
- wlc_view_focus(c->handle);
- }
- */
- if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) {
- update_container_border(c);
- }
-
- swayc_t *parent = swayc_tabbed_stacked_ancestor(c);
- if (parent != NULL) {
- arrange_backgrounds();
- arrange_windows(parent, -1, -1);
- }
- }
-
- if (old_ws != workspace) {
- // old_ws might be NULL here but that's ok
- ipc_event_workspace(old_ws, workspace, "focus");
- }
-
- return true;
-}
-
-bool set_focused_container_for(swayc_t *a, swayc_t *c) {
- if (locked_container_focus || !c) {
- return false;
- }
- swayc_t *find = c;
- while (find != a && (find = find->parent)) {
- if (find == &root_container) {
- return false;
- }
- }
-
- // Get workspace for c, get that workspaces current focused container.
- swayc_t *workspace = swayc_active_workspace_for(c);
- swayc_t *focused = get_focused_view(workspace);
- // if the workspace we are changing focus to has a fullscreen view return
- if (swayc_is_fullscreen(focused) && c != focused) {
- return false;
- }
-
- // Check if we are changing a parent container that will see change
- bool effective = true;
- while (find != &root_container) {
- if (find->parent->focused != find) {
- effective = false;
- }
- find = find->parent;
- }
- if (effective) {
- // Go to set_focused_container
- return set_focused_container(c);
- }
-
- sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR,
- a, a->handle, c, c->handle);
-
- c->is_focused = true;
- swayc_t *p = c;
- while (p != a) {
- update_focus(p);
- p = p->parent;
- p->is_focused = false;
- }
- return true;
-}
-
-swayc_t *get_focused_view(swayc_t *parent) {
- swayc_t *c = parent;
- while (c && c->type != C_VIEW) {
- if (c->type == C_WORKSPACE && c->focused == NULL) {
- return c;
- }
- c = c->focused;
- }
- if (c == NULL) {
- c = swayc_active_workspace_for(parent);
- }
- return c;
-}
-
-swayc_t *get_focused_float(swayc_t *ws) {
- if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) {
- ws = swayc_active_workspace();
- }
- if (ws->floating->length) {
- return ws->floating->items[ws->floating->length - 1];
- }
- return NULL;
-}
-
-swayc_t *get_focused_view_include_floating(swayc_t *parent) {
- swayc_t *c = parent;
- swayc_t *f = NULL;
-
- while (c && c->type != C_VIEW) {
- if (c->type == C_WORKSPACE && c->focused == NULL) {
- return ((f = get_focused_float(c))) ? f : c;
- }
-
- c = c->focused;
- }
-
- if (c == NULL) {
- c = swayc_active_workspace_for(parent);
- }
-
- return c;
-}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
deleted file mode 100644
index 22f81688..00000000
--- a/sway/tree/layout.c
+++ /dev/null
@@ -1,1773 +0,0 @@
-#define _XOPEN_SOURCE 500
-#include <stdlib.h>
-#include <stdbool.h>
-#include <math.h>
-#include <wlc/wlc.h>
-#include "sway/config.h"
-#include "sway/container.h"
-#include "sway/workspace.h"
-#include "sway/focus.h"
-#include "sway/output.h"
-#include "sway/ipc-server.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "list.h"
-#include "log.h"
-
-swayc_t root_container;
-swayc_t *current_focus;
-list_t *scratchpad;
-
-int min_sane_h = 60;
-int min_sane_w = 100;
-
-void init_layout(void) {
- root_container.id = 0; // normally assigned in new_swayc()
- root_container.type = C_ROOT;
- root_container.layout = L_NONE;
- root_container.name = strdup("root");
- root_container.children = create_list();
- root_container.handle = -1;
- root_container.visible = true;
- current_focus = &root_container;
- scratchpad = create_list();
-}
-
-int index_child(const swayc_t *child) {
- swayc_t *parent = child->parent;
- int i, len;
- if (!child->is_floating) {
- len = parent->children->length;
- for (i = 0; i < len; ++i) {
- if (parent->children->items[i] == child) {
- break;
- }
- }
- } else {
- len = parent->floating->length;
- for (i = 0; i < len; ++i) {
- if (parent->floating->items[i] == child) {
- break;
- }
- }
- }
- if (!sway_assert(i < len, "Stray container")) {
- return -1;
- }
- return i;
-}
-
-void add_child(swayc_t *parent, swayc_t *child) {
- sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
- child->width, child->height, parent, parent->type, parent->width, parent->height);
- list_add(parent->children, child);
- child->parent = parent;
- // set focus for this container
- if (!parent->focused) {
- parent->focused = child;
- }
- 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);
- }
-}
-
-static double *get_height(swayc_t *cont) {
- return &cont->height;
-}
-
-static double *get_width(swayc_t *cont) {
- return &cont->width;
-}
-
-void insert_child(swayc_t *parent, swayc_t *child, int index) {
- if (index > parent->children->length) {
- index = parent->children->length;
- }
- if (index < 0) {
- index = 0;
- }
- list_insert(parent->children, index, child);
- child->parent = parent;
- if (!parent->focused) {
- parent->focused = child;
- }
- 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);
- }
- if (is_auto_layout(parent->layout)) {
- /* go through each group, adjust the size of the first child of each group */
- double *(*get_maj_dim)(swayc_t *cont);
- double *(*get_min_dim)(swayc_t *cont);
- if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
- get_maj_dim = get_width;
- get_min_dim = get_height;
- } else {
- get_maj_dim = get_height;
- get_min_dim = get_width;
- }
- for (int i = index; i < parent->children->length;) {
- int start = auto_group_start_index(parent, i);
- int end = auto_group_end_index(parent, i);
- swayc_t *first = parent->children->items[start];
- if (start + 1 < parent->children->length) {
- /* preserve the group's dimension along major axis */
- *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
- } else {
- /* new group, let the apply_layout handle it */
- first->height = first->width = 0;
- break;
- }
- double remaining = *get_min_dim(parent);
- for (int j = end - 1; j > start; --j) {
- swayc_t *sibling = parent->children->items[j];
- if (sibling == child) {
- /* the inserted child won't yet have its minor
- dimension set */
- remaining -= *get_min_dim(parent) / (end - start);
- } else {
- remaining -= *get_min_dim(sibling);
- }
- }
- *get_min_dim(first) = remaining;
- i = end;
- }
- }
-}
-
-void add_floating(swayc_t *ws, swayc_t *child) {
- sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
- child->width, child->height, ws, ws->type, ws->width, ws->height);
- if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) {
- return;
- }
- list_add(ws->floating, child);
- child->parent = ws;
- child->is_floating = true;
- if (!ws->focused) {
- ws->focused = child;
- }
- ipc_event_window(child, "floating");
-}
-
-swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
- swayc_t *parent = fixed->parent;
- if (fixed->is_floating) {
- if (active->is_floating) {
- int i = index_child(fixed);
- list_insert(parent->floating, i + 1, active);
- } else {
- list_add(parent->children, active);
- }
- } else {
- if (active->is_floating) {
- list_add(parent->floating, active);
- } else {
- int i = index_child(fixed);
- if (is_auto_layout(parent->layout)) {
- list_add(parent->children, active);
- } else {
- list_insert(parent->children, i + 1, active);
- }
- }
- }
- active->parent = parent;
- // focus new child
- parent->focused = active;
- return active->parent;
-}
-
-swayc_t *replace_child(swayc_t *child, swayc_t *new_child) {
- swayc_t *parent = child->parent;
- if (parent == NULL) {
- return NULL;
- }
- int i = index_child(child);
- if (child->is_floating) {
- parent->floating->items[i] = new_child;
- } else {
- parent->children->items[i] = new_child;
- }
- // Set parent and focus for new_child
- new_child->parent = child->parent;
- if (child->parent->focused == child) {
- child->parent->focused = new_child;
- }
- child->parent = NULL;
-
- // Set geometry for new child
- new_child->x = child->x;
- new_child->y = child->y;
- new_child->width = child->width;
- new_child->height = child->height;
-
- // reset geometry for child
- child->width = 0;
- child->height = 0;
-
- // deactivate child
- if (child->type == C_VIEW) {
- wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
- }
- return parent;
-}
-
-swayc_t *remove_child(swayc_t *child) {
- int i;
- swayc_t *parent = child->parent;
- if (child->is_floating) {
- // Special case for floating views
- for (i = 0; i < parent->floating->length; ++i) {
- if (parent->floating->items[i] == child) {
- list_del(parent->floating, i);
- break;
- }
- }
- i = 0;
- } else {
- for (i = 0; i < parent->children->length; ++i) {
- if (parent->children->items[i] == child) {
- list_del(parent->children, i);
- break;
- }
- }
- if (is_auto_layout(parent->layout) && parent->children->length) {
- /* go through each group, adjust the size of the last child of each group */
- double *(*get_maj_dim)(swayc_t *cont);
- double *(*get_min_dim)(swayc_t *cont);
- if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
- get_maj_dim = get_width;
- get_min_dim = get_height;
- } else {
- get_maj_dim = get_height;
- get_min_dim = get_width;
- }
- for (int j = parent->children->length - 1; j >= i;) {
- int start = auto_group_start_index(parent, j);
- int end = auto_group_end_index(parent, j);
- swayc_t *first = parent->children->items[start];
- if (i == start) {
- /* removed element was first child in the current group,
- use its size along the major axis */
- *get_maj_dim(first) = *get_maj_dim(child);
- } else if (start > i) {
- /* preserve the group's dimension along major axis */
- *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
- }
- if (end != parent->children->length) {
- double remaining = *get_min_dim(parent);
- for (int k = start; k < end - 1; ++k) {
- swayc_t *sibling = parent->children->items[k];
- remaining -= *get_min_dim(sibling);
- }
- /* last element of the group gets remaining size, elements
- that don't change groups keep their ratio */
- *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
- } /* else last group, let apply_layout handle it */
- j = start - 1;
- }
- }
- }
- // Set focused to new container
- if (parent->focused == child) {
- if (parent->children->length > 0) {
- parent->focused = parent->children->items[i ? i-1:0];
- } else if (parent->floating && parent->floating->length) {
- parent->focused = parent->floating->items[parent->floating->length - 1];
- } else {
- parent->focused = NULL;
- }
- }
- child->parent = NULL;
- // deactivate view
- if (child->type == C_VIEW) {
- wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
- }
- return parent;
-}
-
-void swap_container(swayc_t *a, swayc_t *b) {
- if (!sway_assert(a&&b, "parameters must be non null") ||
- !sway_assert(a->parent && b->parent, "containers must have parents")) {
- return;
- }
- size_t a_index = index_child(a);
- size_t b_index = index_child(b);
- swayc_t *a_parent = a->parent;
- swayc_t *b_parent = b->parent;
- // Swap the pointers
- if (a->is_floating) {
- a_parent->floating->items[a_index] = b;
- } else {
- a_parent->children->items[a_index] = b;
- }
- if (b->is_floating) {
- b_parent->floating->items[b_index] = a;
- } else {
- b_parent->children->items[b_index] = a;
- }
- a->parent = b_parent;
- b->parent = a_parent;
- if (a_parent->focused == a) {
- a_parent->focused = b;
- }
- // don't want to double switch
- if (b_parent->focused == b && a_parent != b_parent) {
- b_parent->focused = a;
- }
-}
-
-void swap_geometry(swayc_t *a, swayc_t *b) {
- double x = a->x;
- double y = a->y;
- double w = a->width;
- double h = a->height;
- a->x = b->x;
- a->y = b->y;
- a->width = b->width;
- a->height = b->height;
- b->x = x;
- b->y = y;
- b->width = w;
- b->height = h;
-}
-
-static void swap_children(swayc_t *container, int a, int b) {
- if (a >= 0 && b >= 0 && a < container->children->length
- && b < container->children->length
- && a != b) {
- swayc_t *pa = (swayc_t *)container->children->items[a];
- swayc_t *pb = (swayc_t *)container->children->items[b];
- container->children->items[a] = container->children->items[b];
- container->children->items[b] = pa;
- if (is_auto_layout(container->layout)) {
- size_t ga = auto_group_index(container, a);
- size_t gb = auto_group_index(container, b);
- if (ga != gb) {
- swap_geometry(pa, pb);
- }
- }
- }
-}
-
-void move_container(swayc_t *container, enum movement_direction dir, int move_amt) {
- enum swayc_layouts layout = L_NONE;
- swayc_t *parent = container->parent;
- if (container->is_floating) {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- switch(dir) {
- case MOVE_LEFT:
- container->x = MAX(0, container->x - move_amt);
- break;
- case MOVE_RIGHT:
- container->x = MIN(output->width - container->width, container->x + move_amt);
- break;
- case MOVE_UP:
- container->y = MAX(0, container->y - move_amt);
- break;
- case MOVE_DOWN:
- container->y = MIN(output->height - container->height, container->y + move_amt);
- break;
- default:
- break;
- }
- update_geometry(container);
- return;
- }
- if (container->type != C_VIEW && container->type != C_CONTAINER) {
- return;
- }
- if (dir == MOVE_UP || dir == MOVE_DOWN) {
- layout = L_VERT;
- } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
- layout = L_HORIZ;
- } else if (dir == MOVE_FIRST) {
- // swap first child in auto layout with currently focused child
- if (is_auto_layout(parent->layout)) {
- int focused_idx = index_child(container);
- swayc_t *first = parent->children->items[0];
- if (focused_idx > 0) {
- list_swap(parent->children, 0, focused_idx);
- swap_geometry(first, container);
- }
- arrange_windows(parent->parent, -1, -1);
- ipc_event_window(container, "move");
- set_focused_container_for(parent->parent, container);
- }
- return;
- } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
- return;
- }
- swayc_t *child = container;
- bool ascended = false;
-
- // View is wrapped in intermediate container which is needed for displaying
- // the titlebar. Moving only the view outside of its parent container would just
- // wrap it again under worspace. There would effectively be no movement,
- // just a change of wrapping container.
- if (child->type == C_VIEW &&
- parent->type == C_CONTAINER &&
- parent->children->length == 1 &&
- parent->parent->type == C_WORKSPACE) {
- child = parent;
- parent = parent->parent;
- }
-
- while (true) {
- sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
- container,parent,child);
- if (parent->layout == layout
- || (layout == L_NONE && (parent->type == C_CONTAINER || parent->type == C_WORKSPACE)) /* accept any layout for next/prev direction */
- || (parent->layout == L_TABBED && layout == L_HORIZ)
- || (parent->layout == L_STACKED && layout == L_VERT)
- || is_auto_layout(parent->layout)) {
- int diff;
- // If it has ascended (parent has moved up), no container is removed
- // so insert it at index, or index+1.
- // if it has not, the moved container is removed, so it needs to be
- // inserted at index-1, or index+1
- if (ascended) {
- diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
- } else {
- diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
- }
- int idx = index_child(child);
- int desired = idx + diff;
- if (dir == MOVE_NEXT || dir == MOVE_PREV) {
- // Next/Prev always wrap.
- if (desired < 0) {
- desired += parent->children->length;
- } else if (desired >= parent->children->length) {
- desired = 0;
- }
- }
- // when it has ascended, legal insertion position is 0:len
- // when it has not, legal insertion position is 0:len-1
- if (desired >= 0 && desired - ascended < parent->children->length) {
- if (!ascended) {
- child = parent->children->items[desired];
- // Move container into sibling container
- if (child->type == C_CONTAINER) {
- parent = child;
- // Insert it in first/last if matching layout, otherwise
- // insert it next to focused container
- if (parent->layout == layout
- || (parent->layout == L_TABBED && layout == L_HORIZ)
- || (parent->layout == L_STACKED && layout == L_VERT)
- || is_auto_layout(parent->layout)) {
- desired = (diff < 0) * parent->children->length;
- } else {
- desired = index_child(child->focused) + 1;
- }
- //reset geometry
- container->width = container->height = 0;
- }
- }
- if (container->parent == parent) {
- swap_children(parent, idx, desired);
- } else {
- swayc_t *old_parent = remove_child(container);
- insert_child(parent, container, desired);
- destroy_container(old_parent);
- sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
- }
- break;
- }
- }
- // Change parent layout if we need to
- if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
- /* swayc_change_layout(parent, layout); */
- parent->layout = layout;
- continue;
- }
- if (parent->type == C_WORKSPACE) {
- // 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_absolute_center_position(container, &abs_pos);
-
- swayc_t *output = swayc_adjacent_output(parent->parent, dir, &abs_pos, true);
-
- if (output) {
- sway_log(L_DEBUG, "Moving between outputs");
- swayc_t *old_parent = remove_child(container);
- destroy_container(old_parent);
-
- swayc_t *dest = output->focused;
- switch (dir) {
- case MOVE_LEFT:
- case MOVE_UP:
- // reset container geometry
- container->width = container->height = 0;
- add_child(dest, container);
- break;
- case MOVE_RIGHT:
- case MOVE_DOWN:
- // reset container geometry
- container->width = container->height = 0;
- insert_child(dest, container, 0);
- break;
- default:
- break;
- }
- // arrange new workspace
- arrange_windows(dest, -1, -1);
- set_focused_container(container);
- break;
- }
-
- // We simply cannot move any further.
- if (parent->layout == layout) {
- break;
- }
- // Create container around workspace to insert child into
- parent = new_container(parent, layout);
- // Previous line set the resulting container's layout to
- // workspace_layout. It should have been just layout.
- parent->layout = parent->parent->layout;
- }
- ascended = true;
- child = parent;
- parent = child->parent;
- }
- arrange_windows(parent->parent, -1, -1);
- ipc_event_window(container, "move");
- set_focused_container_for(parent->parent, container);
-}
-
-void move_container_to(swayc_t* container, swayc_t* destination) {
- if (container == destination || swayc_is_parent_of(container, destination)) {
- return;
- }
- swayc_t *parent = remove_child(container);
- // Send to new destination
- if (container->is_floating) {
- swayc_t *ws = swayc_active_workspace_for(destination);
- add_floating(ws, container);
-
- // If the workspace only has one child after adding one, it
- // means that the workspace was just initialized.
- if (ws->children->length + ws->floating->length == 1) {
- ipc_event_workspace(NULL, ws, "init");
- }
- } else if (destination->type == C_WORKSPACE) {
- // reset container geometry
- container->width = container->height = 0;
- add_child(destination, container);
-
- // If the workspace only has one child after adding one, it
- // means that the workspace was just initialized.
- if (destination->children->length + destination->floating->length == 1) {
- ipc_event_workspace(NULL, destination, "init");
- }
- } else {
- // reset container geometry
- container->width = container->height = 0;
- add_sibling(destination, container);
- }
- // Destroy old container if we need to
- parent = destroy_container(parent);
- // Refocus
- swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT);
- swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT);
- set_focused_container(get_focused_view(op1));
- arrange_windows(op1, -1, -1);
- update_visibility(op1);
- if (op1 != op2) {
- set_focused_container(get_focused_view(op2));
- arrange_windows(op2, -1, -1);
- update_visibility(op2);
- }
-}
-
-void move_workspace_to(swayc_t* workspace, swayc_t* destination) {
- if (workspace == destination || swayc_is_parent_of(workspace, destination)) {
- return;
- }
- swayc_t *src_op = remove_child(workspace);
- // reset container geometry
- workspace->width = workspace->height = 0;
- add_child(destination, workspace);
- sort_workspaces(destination);
- // Refocus destination (change to new workspace)
- set_focused_container(get_focused_view(workspace));
- arrange_windows(destination, -1, -1);
- update_visibility(destination);
-
- // make sure source output has a workspace
- if (src_op->children->length == 0) {
- char *ws_name = workspace_next_name(src_op->name);
- swayc_t *ws = new_workspace(src_op, ws_name);
- ws->is_focused = true;
- free(ws_name);
- }
- set_focused_container(get_focused_view(src_op));
- update_visibility(src_op);
-}
-
-static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
- const struct wlc_size *res, int left, int right, int top, int bottom) {
-
- g->size.w += left + right;
- if (g->origin.x - left < 0) {
- g->size.w += g->origin.x - left;
- } else if (g->origin.x + g->size.w - right > res->w) {
- g->size.w = res->w - g->origin.x + right;
- }
-
- g->size.h += top + bottom;
- if (g->origin.y - top < 0) {
- g->size.h += g->origin.y - top;
- } else if (g->origin.y + g->size.h - top > res->h) {
- g->size.h = res->h - g->origin.y + top;
- }
-
- g->origin.x = MIN((uint32_t)MAX(g->origin.x - left, 0), res->w);
- g->origin.y = MIN((uint32_t)MAX(g->origin.y - top, 0), res->h);
-
-}
-
-static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) {
- struct wlc_geometry g = *geometry;
- c->actual_geometry = g;
-
- swayc_t *output = swayc_parent_by_type(c, C_OUTPUT);
- struct wlc_size res;
- output_get_scaled_size(output->handle, &res);
-
- switch (c->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- adjust_border_geometry(c, &g, &res, c->border_thickness,
- c->border_thickness, c->border_thickness, c->border_thickness);
- break;
- case B_NORMAL:
- {
- int title_bar_height = config->font_height + 4; // borders + padding
-
- adjust_border_geometry(c, &g, &res, c->border_thickness,
- c->border_thickness, title_bar_height, c->border_thickness);
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = c->actual_geometry.origin.x - c->border_thickness,
- .y = c->actual_geometry.origin.y - title_bar_height
- },
- .size = {
- .w = c->actual_geometry.size.w + (2 * c->border_thickness),
- .h = title_bar_height
- }
- };
- c->title_bar_geometry = title_bar;
- break;
- }
- }
-
- c->border_geometry = g;
- *geometry = c->actual_geometry;
-
- update_container_border(c);
-}
-
-void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
- switch (parent->layout) {
- case L_TABBED:
- case L_STACKED:
- if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
- // cache current geometry for all non-float children
- int i;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *child = parent->children->items[i];
- child->cached_geometry.origin.x = child->x;
- child->cached_geometry.origin.y = child->y;
- child->cached_geometry.size.w = child->width;
- child->cached_geometry.size.h = child->height;
- }
- }
- break;
- default:
- if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
- // recover cached geometry for all non-float children
- int i;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *child = parent->children->items[i];
- // only recoverer cached geometry if non-zero
- if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
- child->x = child->cached_geometry.origin.x;
- child->y = child->cached_geometry.origin.y;
- child->width = child->cached_geometry.size.w;
- child->height = child->cached_geometry.size.h;
- }
- }
- }
- break;
- }
-}
-
-static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
- swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
- swayc_t *op = ws->parent;
- int gap = container->is_floating ? 0 : swayc_gap(container);
- if (gap % 2 != 0) {
- // because gaps are implemented as "half sized margins" it's currently
- // not possible to align views properly with odd sized gaps.
- gap -= 1;
- }
-
- g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
- g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
- g->size.w = container->width > gap ? container->width - gap : 1;
- g->size.h = container->height > gap ? container->height - gap : 1;
-
- if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
- // Remove gap against the workspace edges. Because a pixel is not
- // divisable, depending on gap size and the number of siblings our view
- // might be at the workspace edge without being exactly so (thus test
- // with gap, and align correctly).
- if (container->x - gap <= ws->x) {
- g->origin.x = ws->x;
- g->size.w = container->width - gap/2;
- }
- if (container->y - gap <= ws->y) {
- g->origin.y = ws->y;
- g->size.h = container->height - gap/2;
- }
- if (container->x + container->width + gap >= ws->x + ws->width) {
- g->size.w = ws->x + ws->width - g->origin.x;
- }
- if (container->y + container->height + gap >= ws->y + ws->height) {
- g->size.h = ws->y + ws->height - g->origin.y;
- }
- }
-
- return gap;
-}
-
-void update_geometry(swayc_t *container) {
- if (container->type != C_VIEW && container->type != C_CONTAINER) {
- return;
- }
-
- swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
- swayc_t *op = workspace->parent;
- swayc_t *parent = container->parent;
-
- struct wlc_geometry geometry = {
- .origin = {
- .x = container->x < op->width ? container->x : op->width-1,
- .y = container->y < op->height ? container->y : op->height-1
- },
- .size = {
- .w = container->width,
- .h = container->height,
- }
- };
-
- int gap = 0;
-
- // apply inner gaps to non-tabbed/stacked containers
- swayc_t *p = swayc_tabbed_stacked_ancestor(container);
- if (p == NULL) {
- gap = update_gap_geometry(container, &geometry);
- }
-
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- struct wlc_size size;
- output_get_scaled_size(output->handle, &size);
-
- if (swayc_is_fullscreen(container)) {
- geometry.origin.x = 0;
- geometry.origin.y = 0;
- geometry.size.w = size.w;
- geometry.size.h = size.h;
- if (op->focused == workspace) {
- wlc_view_bring_to_front(container->handle);
- }
-
- container->border_geometry = wlc_geometry_zero;
- container->title_bar_geometry = wlc_geometry_zero;
- border_clear(container->border);
- } else if (container->is_floating) { // allocate border for floating window
- update_border_geometry_floating(container, &geometry);
- } else if (!container->is_floating) { // allocate border for titled window
- container->border_geometry = geometry;
-
- int border_top = container->border_thickness;
- int border_bottom = container->border_thickness;
- int border_left = container->border_thickness;
- int border_right = container->border_thickness;
-
- // handle hide_edge_borders
- if (config->hide_edge_borders != E_NONE && (gap <= 0 || (config->smart_gaps && workspace->children->length == 1))) {
- if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) {
- if (geometry.origin.x == workspace->x) {
- border_left = 0;
- }
-
- if (geometry.origin.x + geometry.size.w == workspace->x + workspace->width) {
- border_right = 0;
- }
- }
-
- if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) {
- if (geometry.origin.y == workspace->y || should_hide_top_border(container, geometry.origin.y)) {
- border_top = 0;
- }
-
- if (geometry.origin.y + geometry.size.h == workspace->y + workspace->height) {
- border_bottom = 0;
- }
- }
-
- if (config->hide_edge_borders == E_SMART && workspace->children->length == 1) {
- border_top = 0;
- border_bottom = 0;
- border_left = 0;
- border_right = 0;
- }
- }
-
- int title_bar_height = config->font_height + 4; //borders + padding
-
- if (parent->layout == L_TABBED && parent->children->length > 1) {
- int i, x = 0, w, l, r;
- l = parent->children->length;
- w = geometry.size.w / l;
- r = geometry.size.w % l;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *view = parent->children->items[i];
- if (view == container) {
- x = w * i;
- if (i == l - 1) {
- w += r;
- }
- break;
- }
- }
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x + x,
- .y = container->border_geometry.origin.y
- },
- .size = {
- .w = w,
- .h = title_bar_height
- }
- };
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar.size.h;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar.size.h);
- container->title_bar_geometry = title_bar;
- } else if (parent->layout == L_STACKED && parent->children->length > 1) {
- int i, y = 0;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *view = parent->children->items[i];
- if (view == container) {
- y = title_bar_height * i;
- }
- }
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x,
- .y = container->border_geometry.origin.y + y
- },
- .size = {
- .w = container->border_geometry.size.w,
- .h = title_bar_height
- }
- };
- title_bar_height = title_bar_height * parent->children->length;
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar_height;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar_height);
- container->title_bar_geometry = title_bar;
- } else {
- switch (container->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- geometry.origin.x += border_left;
- geometry.origin.y += border_top;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_top + border_bottom);
- break;
- case B_NORMAL:
- {
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x,
- .y = container->border_geometry.origin.y
- },
- .size = {
- .w = container->border_geometry.size.w,
- .h = title_bar_height
- }
- };
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar.size.h;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar.size.h);
- container->title_bar_geometry = title_bar;
- break;
- }
- }
- }
-
- container->actual_geometry = geometry;
-
- if (container->type == C_VIEW) {
- update_container_border(container);
- }
- }
-
- if (container->type == C_VIEW) {
- wlc_view_set_geometry(container->handle, 0, &geometry);
- }
-}
-
-/**
- * Layout application prototypes
- */
-static void apply_horiz_layout(swayc_t *container, const double x,
- const double y, const double width,
- const double height, const int start,
- const int end);
-static void apply_vert_layout(swayc_t *container, const double x,
- const double y, const double width,
- const double height, const int start,
- const int end);
-static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
- double y, double width,
- double height);
-
-static void apply_auto_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- enum swayc_layouts group_layout,
- bool master_first);
-
-static void arrange_windows_r(swayc_t *container, double width, double height) {
- int i;
- if (width == -1 || height == -1) {
- swayc_log(L_DEBUG, container, "Arranging layout for %p", container);
- width = container->width;
- height = container->height;
- }
- // pixels are indivisible. if we don't round the pixels, then the view
- // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
- // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
- width = floor(width);
- height = floor(height);
-
- sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
- container->name, container->width, container->height, container->x,
- container->y);
-
- double x = 0, y = 0;
- switch (container->type) {
- case C_ROOT:
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *output = container->children->items[i];
- sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y);
- arrange_windows_r(output, -1, -1);
- }
- return;
- case C_OUTPUT:
- {
- struct wlc_size resolution;
- output_get_scaled_size(container->handle, &resolution);
- width = resolution.w; height = resolution.h;
- // output must have correct size due to e.g. seamless mouse,
- // but a workspace might be smaller depending on panels.
- container->width = width;
- container->height = height;
- }
- // arrange all workspaces:
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- arrange_windows_r(child, -1, -1);
- }
- // Bring all unmanaged views to the front
- for (i = 0; i < container->unmanaged->length; ++i) {
- wlc_handle *handle = container->unmanaged->items[i];
- wlc_view_bring_to_front(*handle);
- }
- return;
- case C_WORKSPACE:
- {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- width = output->width, height = output->height;
- /* TODO WLR
- for (i = 0; i < desktop_shell.panels->length; ++i) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->output == output->handle) {
- struct wlc_size size = *wlc_surface_get_size(config->surface);
- sway_log(L_DEBUG, "-> Found panel for this workspace: %ux%u, position: %u", size.w, size.h, config->panel_position);
- switch (config->panel_position) {
- case DESKTOP_SHELL_PANEL_POSITION_TOP:
- y += size.h; height -= size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
- height -= size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_LEFT:
- x += size.w; width -= size.w;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
- width -= size.w;
- break;
- }
- }
- }
- */
- int gap = swayc_gap(container);
- x = container->x = x + gap;
- y = container->y = y + gap;
- width = container->width = width - gap * 2;
- height = container->height = height - gap * 2;
- sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
- }
- // children are properly handled below
- break;
- case C_VIEW:
- {
- container->width = width;
- container->height = height;
- update_geometry(container);
- sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", container->width,
- container->height, container->x, container->y);
- }
- return;
- default:
- container->width = width;
- container->height = height;
- x = container->x;
- y = container->y;
-
- // add gaps to top level tapped/stacked container
- if (container->parent->type == C_WORKSPACE &&
- (container->layout == L_TABBED || container->layout == L_STACKED)) {
- update_geometry(container);
- width = container->border_geometry.size.w;
- height = container->border_geometry.size.h;
- x = container->border_geometry.origin.x;
- y = container->border_geometry.origin.y;
- }
-
- // update container size if it's a direct child in a tabbed/stacked layout
- // if parent is a workspace, its actual_geometry won't be initialized
- if (swayc_tabbed_stacked_parent(container) != NULL &&
- container->parent->type != C_WORKSPACE) {
- // Use parent actual_geometry as a base for calculating
- // container geometry
- container->width = container->parent->actual_geometry.size.w;
- container->height = container->parent->actual_geometry.size.h;
- container->x = container->parent->actual_geometry.origin.x;
- container->y = container->parent->actual_geometry.origin.y;
-
- update_geometry(container);
- width = container->width = container->actual_geometry.size.w;
- height = container->height = container->actual_geometry.size.h;
- x = container->x = container->actual_geometry.origin.x;
- y = container->y = container->actual_geometry.origin.y;
- }
-
- break;
- }
-
- switch (container->layout) {
- case L_HORIZ:
- default:
- apply_horiz_layout(container, x, y, width, height, 0,
- container->children->length);
- break;
- case L_VERT:
- apply_vert_layout(container, x, y, width, height, 0,
- container->children->length);
- break;
- case L_TABBED:
- case L_STACKED:
- apply_tabbed_or_stacked_layout(container, x, y, width, height);
- break;
- case L_AUTO_LEFT:
- apply_auto_layout(container, x, y, width, height, L_VERT, true);
- break;
- case L_AUTO_RIGHT:
- apply_auto_layout(container, x, y, width, height, L_VERT, false);
- break;
- case L_AUTO_TOP:
- apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
- break;
- case L_AUTO_BOTTOM:
- apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
- break;
- }
-
- // Arrage floating layouts for workspaces last
- if (container->type == C_WORKSPACE) {
- for (int i = 0; i < container->floating->length; ++i) {
- swayc_t *view = container->floating->items[i];
- if (view->type == C_VIEW) {
- update_geometry(view);
- sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
- view->width, view->height, view->x, view->y);
- if (swayc_is_fullscreen(view)) {
- wlc_view_bring_to_front(view->handle);
- } else if (!container->focused ||
- !swayc_is_fullscreen(container->focused)) {
- wlc_view_bring_to_front(view->handle);
- }
- }
- }
- }
-}
-
-void apply_horiz_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- const int start, const int end) {
- double scale = 0;
- // Calculate total width
- for (int i = start; i < end; ++i) {
- double *old_width = &((swayc_t *)container->children->items[i])->width;
- if (*old_width <= 0) {
- if (end - start > 1) {
- *old_width = width / (end - start - 1);
- } else {
- *old_width = width;
- }
- }
- scale += *old_width;
- }
- scale = width / scale;
-
- // Resize windows
- double child_x = x;
- if (scale > 0.1) {
- sway_log(L_DEBUG, "Arranging %p horizontally", container);
- swayc_t *focused = NULL;
- for (int i = start; i < end; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)", child,
- child->type, width, scale);
- child->x = child_x;
- child->y = y;
-
- if (child == container->focused) {
- focused = child;
- }
-
- if (i == end - 1) {
- double remaining_width = x + width - child_x;
- arrange_windows_r(child, remaining_width, height);
- } else {
- arrange_windows_r(child, child->width * scale, height);
- }
- child_x += child->width;
- }
-
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
- }
- }
-}
-
-void apply_vert_layout(swayc_t *container, const double x, const double y,
- const double width, const double height, const int start,
- const int end) {
- int i;
- double scale = 0;
- // Calculate total height
- for (i = start; i < end; ++i) {
- double *old_height = &((swayc_t *)container->children->items[i])->height;
- if (*old_height <= 0) {
- if (end - start > 1) {
- *old_height = height / (end - start - 1);
- } else {
- *old_height = height;
- }
- }
- scale += *old_height;
- }
- scale = height / scale;
-
- // Resize
- double child_y = y;
- if (scale > 0.1) {
- sway_log(L_DEBUG, "Arranging %p vertically", container);
- swayc_t *focused = NULL;
- for (i = start; i < end; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)", child,
- child->type, height, scale);
- child->x = x;
- child->y = child_y;
-
- if (child == container->focused) {
- focused = child;
- }
-
- if (i == end - 1) {
- double remaining_height = y + height - child_y;
- arrange_windows_r(child, width, remaining_height);
- } else {
- arrange_windows_r(child, width, child->height * scale);
- }
- child_y += child->height;
- }
-
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
- }
- }
-}
-
-void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
- double width, double height) {
- int i;
- swayc_t *focused = NULL;
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- child->x = x;
- child->y = y;
- if (child == container->focused) {
- focused = child;
- } else {
- arrange_windows_r(child, width, height);
- }
- }
-
- if (focused) {
- arrange_windows_r(focused, width, height);
- }
-}
-
-void apply_auto_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- enum swayc_layouts group_layout,
- bool master_first) {
- // Auto layout "container" in width x height @ x, y
- // using "group_layout" for each of the groups in the container.
- // There is one "master" group, plus container->nb_slave_groups.
- // Each group is layed out side by side following the "major" axis.
- // The direction of the layout used for groups is the "minor" axis.
- // Example:
- //
- // ---- major axis -->
- // +---------+-----------+
- // | | | |
- // | master | slave 1 | |
- // | +-----------+ | minor axis (direction of group_layout)
- // | | | |
- // | | slave 2 | V
- // +---------+-----------+
- //
- // container with three children (one master and two slaves) and
- // a single slave group (containing slave 1 and 2). The master
- // group and slave group are layed out using L_VERT.
-
- size_t nb_groups = auto_group_count(container);
-
- // the target dimension of the container along the "major" axis, each
- // group in the container will be layed out using "group_layout" along
- // the "minor" axis.
- double dim_maj;
- double pos_maj;
-
- // x and y coords for the next group to be laid out.
- const double *group_x, *group_y;
-
- // pos of the next group to layout along the major axis
- double pos;
-
- // size of the next group along the major axis.
- double group_dim;
-
- // height and width of next group to be laid out.
- const double *group_h, *group_w;
-
- switch (group_layout) {
- default:
- sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
- group_layout, __func__);
- /* fall through */
- case L_VERT:
- dim_maj = width;
- pos_maj = x;
-
- group_x = &pos;
- group_y = &y;
- group_w = &group_dim;
- group_h = &height;
- break;
- case L_HORIZ:
- dim_maj = height;
- pos_maj = y;
-
- group_x = &x;
- group_y = &pos;
- group_w = &width;
- group_h = &group_dim;
- break;
- }
-
- /* Determine the dimension of each of the groups in the layout.
- * Dimension will be width for a VERT layout and height for a HORIZ
- * layout. */
- double old_group_dim[nb_groups];
- double old_dim = 0;
- for (size_t group = 0; group < nb_groups; ++group) {
- int idx;
- if (auto_group_bounds(container, group, &idx, NULL)) {
- swayc_t *child = container->children->items[idx];
- double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
- if (*dim <= 0) {
- // New child with uninitialized dimension
- *dim = dim_maj;
- if (nb_groups > 1) {
- // child gets a dimension proportional to existing groups,
- // it will be later scaled based on to the available size
- // in the major axis.
- *dim /= (nb_groups - 1);
- }
- }
- old_dim += *dim;
- old_group_dim[group] = *dim;
- }
- }
- double scale = dim_maj / old_dim;
-
- /* Apply layout to each group */
- pos = pos_maj;
-
- for (size_t group = 0; group < nb_groups; ++group) {
- int start, end; // index of first (inclusive) and last (exclusive) child in the group
- if (auto_group_bounds(container, group, &start, &end)) {
- // adjusted size of the group
- group_dim = old_group_dim[group] * scale;
- if (group == nb_groups - 1) {
- group_dim = pos_maj + dim_maj - pos; // remaining width
- }
- sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
- container, group, start, end, *group_w, *group_h, *group_x, *group_y);
- switch (group_layout) {
- default:
- case L_VERT:
- apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
- break;
- case L_HORIZ:
- apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
- break;
- }
-
- /* update position for next group */
- pos += group_dim;
- }
- }
-}
-
-void arrange_windows(swayc_t *container, double width, double height) {
- update_visibility(container);
- arrange_windows_r(container, width, height);
- layout_log(&root_container, 0);
-}
-
-void arrange_backgrounds(void) {
- /* TODO WLR
- struct background_config *bg;
- for (int i = 0; i < desktop_shell.backgrounds->length; ++i) {
- bg = desktop_shell.backgrounds->items[i];
- wlc_view_send_to_back(bg->handle);
- }
- */
-}
-
-/**
- * Get swayc in the direction of newly entered output.
- */
-static swayc_t *get_swayc_in_output_direction(swayc_t *output, enum movement_direction dir) {
- if (!output) {
- return NULL;
- }
-
- swayc_t *ws = swayc_focus_by_type(output, C_WORKSPACE);
- if (ws && 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_view = swayc_focus_by_type(ws, C_VIEW);
- if (focused_view && focused_view->parent) {
- swayc_t *parent = focused_view->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_view;
- }
- break;
- }
- default:
- break;
- }
- }
-
- return output;
-}
-
-swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit) {
- if (dir == MOVE_CHILD) {
- return container->focused;
- }
-
- 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_absolute_center_position(container, &abs_pos);
-
- if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
- sway_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
- container = swayc_parent_by_type(container, C_OUTPUT);
- get_absolute_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) {
- swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
- if (!output || output == container) {
- return wrap_candidate;
- }
- sway_log(L_DEBUG, "Moving between outputs");
- return get_swayc_in_output_direction(output, dir);
- } else {
- if (is_auto_layout(parent->layout)) {
- bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
- ? dir == MOVE_LEFT || dir == MOVE_RIGHT
- : dir == MOVE_DOWN || dir == MOVE_UP;
- size_t gidx = auto_group_index(parent, idx);
- if (is_major) {
- size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
- can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
- } else {
- desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
- int start, end;
- can_move = auto_group_bounds(parent, gidx, &start, &end)
- && desired >= start && desired < end;
- }
- } 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) {
- if (container->is_floating) {
- if (desired < 0) {
- wrap_candidate = parent->floating->items[parent->floating->length-1];
- } else if (desired >= parent->floating->length){
- wrap_candidate = parent->floating->items[0];
- } else {
- wrap_candidate = parent->floating->items[desired];
- }
- if (wrap_candidate) {
- wlc_view_bring_to_front(wrap_candidate->handle);
- }
- return wrap_candidate;
- } else 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 {
- sway_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, enum movement_direction dir) {
- return get_swayc_in_direction_under(container, dir, NULL);
-}
-
-void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge) {
- int i;
- bool layout_match = true;
- sway_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
- if (edge == WLC_RESIZE_EDGE_LEFT || edge == WLC_RESIZE_EDGE_RIGHT) {
- container->width += amount;
- layout_match = container->layout == L_HORIZ;
- } else if (edge == WLC_RESIZE_EDGE_TOP || edge == WLC_RESIZE_EDGE_BOTTOM) {
- container->height += amount;
- layout_match = container->layout == L_VERT;
- }
- if (container->type == C_VIEW) {
- update_geometry(container);
- return;
- }
- if (layout_match) {
- for (i = 0; i < container->children->length; i++) {
- recursive_resize(container->children->items[i], amount/container->children->length, edge);
- }
- } else {
- for (i = 0; i < container->children->length; i++) {
- recursive_resize(container->children->items[i], amount, edge);
- }
- }
-}
-
-enum swayc_layouts default_layout(swayc_t *output) {
- if (config->default_layout != L_NONE) {
- return config->default_layout;
- } else if (config->default_orientation != L_NONE) {
- return config->default_orientation;
- } else if (output->width >= output->height) {
- return L_HORIZ;
- } else {
- return L_VERT;
- }
-}
-
-bool is_auto_layout(enum swayc_layouts layout) {
- return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
-}
-
-/**
- * Return the number of master elements in a container
- */
-static inline size_t auto_master_count(const swayc_t *container) {
- sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
- container, container->children->length);
- return MIN(container->nb_master, (size_t)container->children->length);
-}
-
-/**
- * Return the number of children in the slave groups. This corresponds to the children
- * that are not members of the master group.
- */
-static inline size_t auto_slave_count(const swayc_t *container) {
- return container->children->length - auto_master_count(container);
-}
-
-/**
- * Return the number of slave groups in the container.
- */
-size_t auto_slave_group_count(const swayc_t *container) {
- return MIN(container->nb_slave_groups, auto_slave_count(container));
-}
-
-/**
- * Return the combined number of master and slave groups in the container.
- */
-size_t auto_group_count(const swayc_t *container) {
- return auto_slave_group_count(container)
- + (container->children->length && container->nb_master ? 1 : 0);
-}
-
-/**
- * given the index of a container's child, return the index of the first child of the group
- * which index is a member of.
- */
-int auto_group_start_index(const swayc_t *container, int index) {
- if (index < 0 || ! is_auto_layout(container->layout)
- || (size_t)index < container->nb_master) {
- return 0;
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- int start_idx;
- if (index < idx2) {
- start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
- } else {
- start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
- }
- return MIN(start_idx, container->children->length);
- }
-}
-
-/**
- * given the index of a container's child, return the index of the first child of the group
- * that follows the one which index is a member of.
- * This makes the function usable to walk through the groups in a container.
- */
-int auto_group_end_index(const swayc_t *container, int index) {
- if (index < 0 || ! is_auto_layout(container->layout)) {
- return container->children->length;
- } else {
- int nxt_idx;
- if ((size_t)index < container->nb_master) {
- nxt_idx = auto_master_count(container);
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- if (index < idx2) {
- nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
- } else {
- nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
- }
- }
- return MIN(nxt_idx, container->children->length);
- }
-}
-
-/**
- * return the index of the Group containing <index>th child of <container>.
- * The index is the order of the group along the container's major axis (starting at 0).
- */
-size_t auto_group_index(const swayc_t *container, int index) {
- if (index < 0) {
- return 0;
- }
- bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
- size_t nb_slaves = auto_slave_count(container);
- if ((size_t)index < container->nb_master) {
- if (master_first || nb_slaves <= 0) {
- return 0;
- } else {
- return auto_slave_group_count(container);
- }
- } else {
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- size_t grp_idx;
- if (index < idx2) {
- grp_idx = (index - container->nb_master) / grp_sz;
- } else {
- grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
- }
- return grp_idx + (master_first && container-> nb_master ? 1 : 0);
- }
-}
-
-/**
- * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
- * an auto layout.
- * If the bounds of the given group can be calculated, they are returned in the start/end
- * parameters (int pointers) and the return value will be true.
- * The indexes are passed by reference and can be NULL.
- */
-bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
- size_t nb_grp = auto_group_count(container);
- if (group_index >= nb_grp) {
- return false;
- }
- bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
- size_t nb_master = auto_master_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- int g_start, g_end;
- if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
- g_start = 0;
- g_end = nb_master;
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- size_t g0 = master_first && container->nb_master ? 1 : 0;
- size_t g1 = g0 + nb_slave_grp - remainder;
- if (group_index < g1) {
- g_start = container->nb_master + (group_index - g0) * grp_sz;
- g_end = g_start + grp_sz;
- } else {
- size_t g2 = group_index - g1;
- g_start = container->nb_master
- + (nb_slave_grp - remainder) * grp_sz
- + g2 * (grp_sz + 1);
- g_end = g_start + grp_sz + 1;
- }
- }
- if (start) {
- *start = g_start;
- }
- if (end) {
- *end = g_end;
- }
- return true;
-}
diff --git a/sway/tree/output.c b/sway/tree/output.c
deleted file mode 100644
index edfcac98..00000000
--- a/sway/tree/output.c
+++ /dev/null
@@ -1,278 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-#include <strings.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include "sway/output.h"
-#include "log.h"
-#include "list.h"
-
-void output_get_scaled_size(wlc_handle handle, struct wlc_size *size) {
- *size = *wlc_output_get_resolution(handle);
- uint32_t scale = wlc_output_get_scale(handle);
- size->w /= scale;
- size->h /= scale;
-}
-
-swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) {
- swayc_t *output = NULL;
- // If there is no output directly next to the current one, use
- // swayc_opposite_output to wrap.
- if (strcasecmp(name, "left") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_RIGHT, abs_pos);
- }
- } else if (strcasecmp(name, "right") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_LEFT, abs_pos);
- }
- } else if (strcasecmp(name, "up") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_DOWN, abs_pos);
- }
- } else if (strcasecmp(name, "down") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_UP, abs_pos);
- }
- } else {
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c->type == C_OUTPUT && strcasecmp(c->name, name) == 0) {
- return c;
- }
- }
- }
- return output;
-}
-
-swayc_t *swayc_opposite_output(enum movement_direction dir,
- const struct wlc_point *abs_pos) {
-
- // Search through all the outputs and pick the output whose edge covers the
- // given position, and is at leftmost/rightmost/upmost/downmost side of the
- // screen (decided by the direction given).
- swayc_t *opposite = NULL;
- char *dir_text = NULL;
- switch(dir) {
- case MOVE_LEFT:
- case MOVE_RIGHT: ;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
- if (!opposite) {
- opposite = c;
- } else if ((dir == MOVE_LEFT && c->x < opposite->x)
- || (dir == MOVE_RIGHT && c->x > opposite->x)) {
- opposite = c;
- }
- }
- }
- dir_text = dir == MOVE_LEFT ? "leftmost" : "rightmost";
- break;
- case MOVE_UP:
- case MOVE_DOWN: ;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
- if (!opposite) {
- opposite = c;
- } else if ((dir == MOVE_UP && c->y < opposite->y)
- || (dir == MOVE_DOWN && c->y > opposite->y)) {
- opposite = c;
- }
- }
- }
- dir_text = dir == MOVE_UP ? "upmost" : "downmost";
- break;
- default:
- sway_abort("Function called with invalid argument.");
- break;
- }
- if (opposite) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s from y-position %i",
- opposite->name, opposite->width, opposite->height, opposite->x, opposite->y,
- dir_text, abs_pos->y);
- }
- return opposite;
-}
-
-// Position is where on the edge (as absolute position) the adjacent output should be searched for.
-swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir,
- const struct wlc_point *abs_pos, bool pick_closest) {
-
- if (!output) {
- output = swayc_active_output();
- }
- // In order to find adjacent outputs we need to test that the outputs are
- // aligned on one axis (decided by the direction given) and that the given
- // position is within the edge of the adjacent output. If no such output
- // exists we pick the adjacent output within the edge that is closest to
- // the given position, if any.
- swayc_t *adjacent = NULL;
- char *dir_text = NULL;
- switch(dir) {
- case MOVE_LEFT:
- case MOVE_RIGHT: ;
- double delta_y = 0;
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c == output || c->type != C_OUTPUT) {
- continue;
- }
- bool x_aligned = dir == MOVE_LEFT ?
- c->x + c->width == output->x :
- c->x == output->x + output->width;
- if (!x_aligned) {
- continue;
- }
- if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
- delta_y = 0;
- adjacent = c;
- break;
- } else if (pick_closest) {
- // track closest adjacent output
- double top_y = c->y, bottom_y = c->y + c->height;
- if (top_y >= output->y && top_y <= output->y + output->height) {
- double delta = top_y - abs_pos->y;
- if (delta < 0) delta = -delta;
- if (delta < delta_y || !adjacent) {
- delta_y = delta;
- adjacent = c;
- }
- }
- // we check both points and pick the closest
- if (bottom_y >= output->y && bottom_y <= output->y + output->height) {
- double delta = bottom_y - abs_pos->y;
- if (delta < 0) delta = -delta;
- if (delta < delta_y || !adjacent) {
- delta_y = delta;
- adjacent = c;
- }
- }
- }
- }
- dir_text = dir == MOVE_LEFT ? "left of" : "right of";
- if (adjacent && delta_y == 0) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->y);
- } else if (adjacent) {
- // so we end up picking the closest adjacent output because
- // there is no directly adjacent to the given position
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->y, delta_y);
- }
- break;
- case MOVE_UP:
- case MOVE_DOWN: ;
- double delta_x = 0;
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c == output || c->type != C_OUTPUT) {
- continue;
- }
- bool y_aligned = dir == MOVE_UP ?
- c->y + c->height == output->y :
- c->y == output->y + output->height;
- if (!y_aligned) {
- continue;
- }
- if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
- delta_x = 0;
- adjacent = c;
- break;
- } else if (pick_closest) {
- // track closest adjacent output
- double left_x = c->x, right_x = c->x + c->width;
- if (left_x >= output->x && left_x <= output->x + output->width) {
- double delta = left_x - abs_pos->x;
- if (delta < 0) delta = -delta;
- if (delta < delta_x || !adjacent) {
- delta_x = delta;
- adjacent = c;
- }
- }
- // we check both points and pick the closest
- if (right_x >= output->x && right_x <= output->x + output->width) {
- double delta = right_x - abs_pos->x;
- if (delta < 0) delta = -delta;
- if (delta < delta_x || !adjacent) {
- delta_x = delta;
- adjacent = c;
- }
- }
- }
- }
- dir_text = dir == MOVE_UP ? "above" : "below";
- if (adjacent && delta_x == 0) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->x);
- } else if (adjacent) {
- // so we end up picking the closest adjacent output because
- // there is no directly adjacent to the given position
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->x, delta_x);
- }
- break;
- default:
- sway_abort("Function called with invalid argument.");
- break;
- }
- return adjacent;
-}
-
-void get_absolute_position(swayc_t *container, struct wlc_point *point) {
- if (!container || !point)
- sway_abort("Need container and wlc_point (was %p, %p).", container, point);
-
- if (container->type == C_OUTPUT) {
- // Coordinates are already absolute.
- point->x = container->x;
- point->y = container->y;
- } 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.
- point->x = output->x;
- point->y = output->y;
- } else {
- point->x = output->x + container->x;
- point->y = output->y + container->y;
- }
- }
-}
-
-void get_absolute_center_position(swayc_t *container, struct wlc_point *point) {
- get_absolute_position(container, point);
- point->x += container->width/2;
- point->y += container->height/2;
-}
-
-static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
- swayc_t *a = *(void **)_a;
- swayc_t *b = *(void **)_b;
- int retval = 0;
-
- if (isdigit(a->name[0]) && isdigit(b->name[0])) {
- int a_num = strtol(a->name, NULL, 10);
- int b_num = strtol(b->name, NULL, 10);
- retval = (a_num < b_num) ? -1 : (a_num > b_num);
- } else if (isdigit(a->name[0])) {
- retval = -1;
- } else if (isdigit(b->name[0])) {
- retval = 1;
- }
-
- return retval;
-}
-
-void sort_workspaces(swayc_t *output) {
- list_stable_sort(output->children, sort_workspace_cmp_qsort);
-}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
deleted file mode 100644
index 14cde146..00000000
--- a/sway/tree/workspace.c
+++ /dev/null
@@ -1,373 +0,0 @@
-#define _XOPEN_SOURCE 500
-#include <stdlib.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <ctype.h>
-#include <wlc/wlc.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/types.h>
-#include "sway/ipc-server.h"
-#include "sway/workspace.h"
-#include "sway/layout.h"
-#include "sway/container.h"
-#include "sway/handlers.h"
-#include "sway/config.h"
-#include "sway/focus.h"
-#include "stringop.h"
-#include "util.h"
-#include "list.h"
-#include "log.h"
-#include "ipc.h"
-
-char *prev_workspace_name = NULL;
-struct workspace_by_number_data {
- int len;
- const char *cset;
- const char *name;
-};
-
-static bool workspace_valid_on_output(const char *output_name, const char *ws_name) {
- int i;
- for (i = 0; i < config->workspace_outputs->length; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->workspace, ws_name) == 0) {
- if (strcasecmp(wso->output, output_name) != 0) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-char *workspace_next_name(const char *output_name) {
- sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name);
- int i;
- int l = 1;
- // Scan all workspace bindings to find the next available workspace name,
- // if none are found/available then default to a number
- struct sway_mode *mode = config->current_mode;
-
- int order = INT_MAX;
- char *target = NULL;
- for (i = 0; i < mode->bindings->length; ++i) {
- struct sway_binding *binding = mode->bindings->items[i];
- char *cmdlist = strdup(binding->command);
- char *dup = cmdlist;
- char *name = NULL;
-
- // workspace n
- char *cmd = argsep(&cmdlist, " ");
- if (cmdlist) {
- name = argsep(&cmdlist, ",;");
- }
-
- if (strcmp("workspace", cmd) == 0 && name) {
- sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
- char *_target = strdup(name);
- strip_quotes(_target);
- while (isspace(*_target))
- _target++;
-
- // Make sure that the command references an actual workspace
- // not a command about workspaces
- if (strcmp(_target, "next") == 0 ||
- strcmp(_target, "prev") == 0 ||
- strcmp(_target, "next_on_output") == 0 ||
- strcmp(_target, "prev_on_output") == 0 ||
- strcmp(_target, "number") == 0 ||
- strcmp(_target, "back_and_forth") == 0 ||
- strcmp(_target, "current") == 0)
- {
- free(_target);
- free(dup);
- continue;
- }
-
- // Make sure that the workspace doesn't already exist
- if (workspace_by_name(_target)) {
- free(_target);
- free(dup);
- continue;
- }
-
- // make sure that the workspace can appear on the given
- // output
- if (!workspace_valid_on_output(output_name, _target)) {
- free(_target);
- free(dup);
- continue;
- }
-
- if (binding->order < order) {
- order = binding->order;
- free(target);
- target = _target;
- sway_log(L_DEBUG, "Workspace: Found free name %s", _target);
- }
- }
- free(dup);
- }
- if (target != NULL) {
- return target;
- }
- // As a fall back, get the current number of active workspaces
- // and return that + 1 for the next workspace's name
- int ws_num = root_container.children->length;
- if (ws_num >= 10) {
- l = 2;
- } else if (ws_num >= 100) {
- l = 3;
- }
- char *name = malloc(l + 1);
- if (!name) {
- sway_log(L_ERROR, "Could not allocate workspace name");
- return NULL;
- }
- sprintf(name, "%d", ws_num++);
- return 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
- parent = get_focused_container(&root_container);
- parent = swayc_parent_by_type(parent, C_OUTPUT);
- return new_workspace(parent, name);
-}
-
-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) {
- if (strcmp(name, "prev") == 0) {
- return workspace_prev();
- }
- else if (strcmp(name, "prev_on_output") == 0) {
- return workspace_output_prev();
- }
- else if (strcmp(name, "next") == 0) {
- return workspace_next();
- }
- else if (strcmp(name, "next_on_output") == 0) {
- return workspace_output_next();
- }
- else if (strcmp(name, "current") == 0) {
- return swayc_active_workspace();
- }
- else {
- return swayc_by_test(&root_container, _workspace_by_name, (void *) 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);
-}
-
-/**
- * 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 = next ? (current_output->children->length) - 1 : 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() {
- return workspace_output_prev_next_impl(swayc_active_output(), true);
-}
-
-swayc_t *workspace_next() {
- return workspace_prev_next_impl(swayc_active_workspace(), true);
-}
-
-swayc_t *workspace_output_prev() {
- return workspace_output_prev_next_impl(swayc_active_output(), false);
-}
-
-swayc_t *workspace_prev() {
- return workspace_prev_next_impl(swayc_active_workspace(), false);
-}
-
-bool workspace_switch(swayc_t *workspace) {
- if (!workspace) {
- return false;
- }
- swayc_t *active_ws = swayc_active_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) {
- sway_log(L_ERROR, "Unable to allocate previous workspace name");
- return false;
- }
- strcpy(prev_workspace_name, active_ws->name);
- }
-
- // move sticky containers
- if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) {
- // don't change list while traversing it, use intermediate list instead
- list_t *stickies = create_list();
- for (int i = 0; i < active_ws->floating->length; i++) {
- swayc_t *cont = active_ws->floating->items[i];
- if (cont->sticky) {
- list_add(stickies, cont);
- }
- }
- for (int i = 0; i < stickies->length; i++) {
- swayc_t *cont = stickies->items[i];
- sway_log(L_DEBUG, "Moving sticky container %p to %p:%s",
- cont, workspace, workspace->name);
- swayc_t *parent = remove_child(cont);
- add_floating(workspace, cont);
- // Destroy old container if we need to
- destroy_container(parent);
- }
- list_free(stickies);
- }
- sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
- if (!set_focused_container(get_focused_view(workspace))) {
- return false;
- }
- swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
- arrange_backgrounds();
- arrange_windows(output, -1, -1);
- return true;
-}
-
-swayc_t *workspace_for_pid(pid_t pid) {
- int i;
- swayc_t *ws = NULL;
- struct pid_workspace *pw = NULL;
-
- sway_log(L_DEBUG, "looking for workspace for pid %d", pid);
-
- // leaving this here as it's useful for debugging
- // sway_log(L_DEBUG, "all pid_workspaces");
- // for (int k = 0; k < config->pid_workspaces->length; k++) {
- // pw = config->pid_workspaces->items[k];
- // sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added);
- // }
-
- do {
- for (i = 0; i < config->pid_workspaces->length; i++) {
- pw = config->pid_workspaces->items[i];
- pid_t *pw_pid = pw->pid;
-
- if (pid == *pw_pid) {
- sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace);
- break; // out of for loop
- }
-
- pw = NULL;
- }
-
- if (pw) {
- break; // out of do-while loop
- }
-
- pid = get_parent_pid(pid);
- // no sense in looking for matches for pid 0.
- // also, if pid == getpid(), that is the compositor's
- // pid, which definitely isn't helpful
- } while (pid > 0 && pid != getpid());
-
- if (pw) {
- ws = workspace_by_name(pw->workspace);
-
- if (!ws) {
- sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid);
- ws = workspace_create(pw->workspace);
- }
-
- list_del(config->pid_workspaces, i);
- }
-
- return ws;
-}