aboutsummaryrefslogtreecommitdiff
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/tree/workspace.c176
1 files changed, 142 insertions, 34 deletions
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 62549434..921b7d19 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -382,6 +382,148 @@ struct sway_workspace *workspace_by_name(const char *name) {
}
}
+static int workspace_get_number(struct sway_workspace *workspace) {
+ char *endptr = NULL;
+ errno = 0;
+ long long n = strtoll(workspace->name, &endptr, 10);
+ if (errno != 0 || n > INT32_MAX || n < 0 || endptr == workspace->name) {
+ n = -1;
+ }
+ return n;
+}
+
+struct sway_workspace *workspace_prev(struct sway_workspace *workspace) {
+ int n = workspace_get_number(workspace);
+ struct sway_workspace *prev = NULL, *last = NULL, *other = NULL;
+ bool found = false;
+ if (n < 0) {
+ // Find the prev named workspace
+ int othern = -1;
+ for (int i = root->outputs->length - 1; i >= 0; i--) {
+ struct sway_output *output = root->outputs->items[i];
+ for (int j = output->workspaces->length - 1; j >= 0; j--) {
+ struct sway_workspace *ws = output->workspaces->items[j];
+ int wsn = workspace_get_number(ws);
+ if (!last) {
+ // The first workspace in reverse order
+ last = ws;
+ }
+ if (!other || (wsn >= 0 && wsn > othern)) {
+ // The last (greatest) numbered workspace.
+ other = ws;
+ othern = workspace_get_number(other);
+ }
+ if (ws == workspace) {
+ found = true;
+ } else if (wsn < 0 && found) {
+ // Found a non-numbered workspace before current
+ return ws;
+ }
+ }
+ }
+ } else {
+ // Find the prev numbered workspace
+ int prevn = -1, lastn = -1;
+ for (int i = root->outputs->length - 1; i >= 0; i--) {
+ struct sway_output *output = root->outputs->items[i];
+ for (int j = output->workspaces->length - 1; j >= 0; j--) {
+ struct sway_workspace *ws = output->workspaces->items[j];
+ int wsn = workspace_get_number(ws);
+ if (!last || (wsn >= 0 && wsn > lastn)) {
+ // The greatest numbered (or last) workspace
+ last = ws;
+ lastn = workspace_get_number(last);
+ }
+ if (!other && wsn < 0) {
+ // The last named workspace
+ other = ws;
+ }
+ if (wsn < 0) {
+ // Haven't reached the numbered workspaces
+ continue;
+ }
+ if (wsn < n && (!prev || wsn > prevn)) {
+ // The closest workspace before the current
+ prev = ws;
+ prevn = workspace_get_number(prev);
+ }
+ }
+ }
+ }
+
+ if (!prev) {
+ prev = other ? other : last;
+ }
+ return prev;
+}
+
+struct sway_workspace *workspace_next(struct sway_workspace *workspace) {
+ int n = workspace_get_number(workspace);
+ struct sway_workspace *next = NULL, *first = NULL, *other = NULL;
+ bool found = false;
+ if (n < 0) {
+ // Find the next named workspace
+ int othern = -1;
+ for (int i = 0; i < root->outputs->length; i++) {
+ struct sway_output *output = root->outputs->items[i];
+ for (int j = 0; j < output->workspaces->length; j++) {
+ struct sway_workspace *ws = output->workspaces->items[j];
+ int wsn = workspace_get_number(ws);
+ if (!first) {
+ // The first named workspace
+ first = ws;
+ }
+ if (!other || (wsn >= 0 && wsn < othern)) {
+ // The first (least) numbered workspace
+ other = ws;
+ othern = workspace_get_number(other);
+ }
+ if (ws == workspace) {
+ found = true;
+ } else if (wsn < 0 && found) {
+ // The first non-numbered workspace after the current
+ return ws;
+ }
+ }
+ }
+ } else {
+ // Find the next numbered workspace
+ int nextn = -1, firstn = -1;
+ for (int i = 0; i < root->outputs->length; i++) {
+ struct sway_output *output = root->outputs->items[i];
+ for (int j = 0; j < output->workspaces->length; j++) {
+ struct sway_workspace *ws = output->workspaces->items[j];
+ int wsn = workspace_get_number(ws);
+ if (!first || (wsn >= 0 && wsn < firstn)) {
+ // The first (or least numbered) workspace
+ first = ws;
+ firstn = workspace_get_number(first);
+ }
+ if (!other && wsn < 0) {
+ // The first non-numbered workspace
+ other = ws;
+ }
+ if (wsn < 0) {
+ // Checked all the numbered workspaces
+ break;
+ }
+ if (n < wsn && (!next || wsn < nextn)) {
+ // The first workspace numerically after the current
+ next = ws;
+ nextn = workspace_get_number(next);
+ }
+ }
+ }
+ }
+
+ if (!next) {
+ // If there is no next workspace from the same category, return the
+ // first from this category.
+ next = other ? other : first;
+ }
+ return next;
+}
+
/**
* 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,
@@ -409,50 +551,16 @@ static struct sway_workspace *workspace_output_prev_next_impl(
return output->workspaces->items[new_index];
}
-/**
- * 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.
- */
-static struct sway_workspace *workspace_prev_next_impl(
- struct sway_workspace *workspace, int dir) {
- struct sway_output *output = workspace->output;
- int index = list_find(output->workspaces, workspace);
- int new_index = index + dir;
-
- if (new_index >= 0 && new_index < output->workspaces->length) {
- return output->workspaces->items[new_index];
- }
-
- // Look on a different output
- int output_index = list_find(root->outputs, output);
- new_index = wrap(output_index + dir, root->outputs->length);
- output = root->outputs->items[new_index];
-
- if (dir == 1) {
- return output->workspaces->items[0];
- } else {
- return output->workspaces->items[output->workspaces->length - 1];
- }
-}
-
struct sway_workspace *workspace_output_next(
struct sway_workspace *current, bool create) {
return workspace_output_prev_next_impl(current->output, 1, create);
}
-struct sway_workspace *workspace_next(struct sway_workspace *current) {
- return workspace_prev_next_impl(current, 1);
-}
-
struct sway_workspace *workspace_output_prev(
struct sway_workspace *current, bool create) {
return workspace_output_prev_next_impl(current->output, -1, create);
}
-struct sway_workspace *workspace_prev(struct sway_workspace *current) {
- return workspace_prev_next_impl(current, -1);
-}
-
bool workspace_switch(struct sway_workspace *workspace,
bool no_auto_back_and_forth) {
struct sway_seat *seat = input_manager_current_seat();