* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
*
- * workspace.c: Functions for modifying workspaces
+ * workspace.c: Modifying workspaces, accessing them, moving containers to
+ * workspaces.
*
*/
#include "all.h"
+/* Stores a copy of the name of the last used workspace for the workspace
+ * back-and-forth switching. */
+static char *previous_workspace_name = NULL;
+
/*
* Returns a pointer to the workspace with the given number (starting at 0),
* creating the workspace if necessary (by allocating the necessary amount of
* will handle CT_WORKSPACEs differently */
workspace = con_new(NULL, NULL);
char *name;
- asprintf(&name, "[i3 con] workspace %s", num);
+ sasprintf(&name, "[i3 con] workspace %s", num);
x_set_name(workspace, name);
free(name);
workspace->type = CT_WORKSPACE;
workspace_reassign_sticky(current);
}
-/*
- * Switches to the given workspace
- *
- */
-void workspace_show(const char *num) {
- Con *workspace, *current, *old = NULL;
- bool changed_num_workspaces;
- workspace = workspace_get(num, &changed_num_workspaces);
+static void _workspace_show(Con *workspace, bool changed_num_workspaces) {
+ Con *current, *old = NULL;
+
+ /* safe-guard against showing i3-internal workspaces like __i3_scratch */
+ if (workspace->name[0] == '_' && workspace->name[1] == '_')
+ return;
/* disable fullscreen for the other workspaces and get the workspace we are
* currently on. */
/* enable fullscreen for the target workspace. If it happens to be the
* same one we are currently on anyways, we can stop here. */
workspace->fullscreen_mode = CF_OUTPUT;
- if (workspace == con_get_workspace(focused)) {
+ current = con_get_workspace(focused);
+ if (workspace == current) {
DLOG("Not switching, already there.\n");
return;
}
+ /* Remember currently focused workspace for switching back to it later with
+ * the 'workspace back_and_forth' command.
+ * NOTE: We have to duplicate the name as the original will be freed when
+ * the corresponding workspace is cleaned up. */
+
+ FREE(previous_workspace_name);
+ if (current)
+ previous_workspace_name = sstrdup(current->name);
+
workspace_reassign_sticky(workspace);
LOG("switching to %p\n", workspace);
}
/* Update the EWMH hints */
- if (changed_num_workspaces)
- ewmh_update_workarea();
ewmh_update_current_desktop();
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"focus\"}");
}
+/*
+ * Switches to the given workspace
+ *
+ */
+void workspace_show(Con *workspace) {
+ _workspace_show(workspace, false);
+}
+
+/*
+ * Looks up the workspace by name and switches to it.
+ *
+ */
+void workspace_show_by_name(const char *num) {
+ Con *workspace;
+ bool changed_num_workspaces;
+ workspace = workspace_get(num, &changed_num_workspaces);
+ _workspace_show(workspace, changed_num_workspaces);
+}
+
/*
* Focuses the next workspace.
*
*/
-void workspace_next() {
+Con* workspace_next() {
Con *current = con_get_workspace(focused);
Con *next = NULL;
Con *output;
next = TAILQ_NEXT(current, nodes);
} else {
/* If currently a numbered workspace, find next numbered workspace. */
- TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ /* Skip outputs starting with __, they are internal. */
+ if (output->name[0] == '_' && output->name[1] == '_')
+ continue;
NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (current->num < child->num && (!next || child->num < next->num))
next = child;
}
+ }
}
/* Find next named workspace. */
if (!next) {
bool found_current = false;
- TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ /* Skip outputs starting with __, they are internal. */
+ if (output->name[0] == '_' && output->name[1] == '_')
+ continue;
NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
found_current = 1;
} else if (child->num == -1 && (current->num != -1 || found_current)) {
next = child;
- goto workspace_next_show;
+ goto workspace_next_end;
}
}
+ }
}
/* Find first workspace. */
if (!next) {
- TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ /* Skip outputs starting with __, they are internal. */
+ if (output->name[0] == '_' && output->name[1] == '_')
+ continue;
NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (!next || (child->num != -1 && child->num < next->num))
next = child;
}
+ }
}
-
-workspace_next_show:
- workspace_show(next->name);
+workspace_next_end:
+ return next;
}
/*
* Focuses the previous workspace.
*
*/
-void workspace_prev() {
+Con* workspace_prev() {
Con *current = con_get_workspace(focused);
Con *prev = NULL;
Con *output;
prev = NULL;
} else {
/* If numbered workspace, find previous numbered workspace. */
- TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes)
+ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
+ /* Skip outputs starting with __, they are internal. */
+ if (output->name[0] == '_' && output->name[1] == '_')
+ continue;
NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE || child->num == -1)
continue;
if (current->num > child->num && (!prev || child->num > prev->num))
prev = child;
}
+ }
}
/* Find previous named workspace. */
if (!prev) {
bool found_current = false;
- TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes)
+ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
+ /* Skip outputs starting with __, they are internal. */
+ if (output->name[0] == '_' && output->name[1] == '_')
+ continue;
NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (child == current) {
- found_current = 1;
+ found_current = true;
} else if (child->num == -1 && (current->num != -1 || found_current)) {
prev = child;
- goto workspace_prev_show;
+ goto workspace_prev_end;
}
}
+ }
}
/* Find last workspace. */
if (!prev) {
- TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes)
+ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
+ /* Skip outputs starting with __, they are internal. */
+ if (output->name[0] == '_' && output->name[1] == '_')
+ continue;
NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (!prev || child->num > prev->num)
prev = child;
}
+ }
+ }
+
+workspace_prev_end:
+ return prev;
+}
+
+
+/*
+ * Focuses the next workspace on the same output.
+ *
+ */
+Con* workspace_next_on_output() {
+ Con *current = con_get_workspace(focused);
+ Con *next = NULL;
+ Con *output = con_get_output(focused);
+
+ if (current->num == -1) {
+ /* If currently a named workspace, find next named workspace. */
+ next = TAILQ_NEXT(current, nodes);
+ } else {
+ /* If currently a numbered workspace, find next numbered workspace. */
+ NODES_FOREACH(output_get_content(output)) {
+ if (child->type != CT_WORKSPACE)
+ continue;
+ if (child->num == -1)
+ break;
+ /* Need to check child against current and next because we are
+ * traversing multiple lists and thus are not guaranteed the
+ * relative order between the list of workspaces. */
+ if (current->num < child->num && (!next || child->num < next->num))
+ next = child;
+ }
+ }
+
+ /* Find next named workspace. */
+ if (!next) {
+ bool found_current = false;
+ NODES_FOREACH(output_get_content(output)) {
+ if (child->type != CT_WORKSPACE)
+ continue;
+ if (child == current) {
+ found_current = 1;
+ } else if (child->num == -1 && (current->num != -1 || found_current)) {
+ next = child;
+ goto workspace_next_on_output_end;
+ }
+ }
+ }
+
+ /* Find first workspace. */
+ if (!next) {
+ NODES_FOREACH(output_get_content(output)) {
+ if (child->type != CT_WORKSPACE)
+ continue;
+ if (!next || (child->num != -1 && child->num < next->num))
+ next = child;
+ }
+ }
+workspace_next_on_output_end:
+ return next;
+}
+
+/*
+ * Focuses the previous workspace on same output.
+ *
+ */
+Con* workspace_prev_on_output() {
+ Con *current = con_get_workspace(focused);
+ Con *prev = NULL;
+ Con *output = con_get_output(focused);
+
+ if (current->num == -1) {
+ /* If named workspace, find previous named workspace. */
+ prev = TAILQ_PREV(current, nodes_head, nodes);
+ if (prev && prev->num != -1)
+ prev = NULL;
+ } else {
+ /* If numbered workspace, find previous numbered workspace. */
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
+ if (child->type != CT_WORKSPACE || child->num == -1)
+ continue;
+ /* Need to check child against current and previous because we
+ * are traversing multiple lists and thus are not guaranteed
+ * the relative order between the list of workspaces. */
+ if (current->num > child->num && (!prev || child->num > prev->num))
+ prev = child;
+ }
+ }
+
+ /* Find previous named workspace. */
+ if (!prev) {
+ bool found_current = false;
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
+ if (child->type != CT_WORKSPACE)
+ continue;
+ if (child == current) {
+ found_current = true;
+ } else if (child->num == -1 && (current->num != -1 || found_current)) {
+ prev = child;
+ goto workspace_prev_on_output_end;
+ }
+ }
+ }
+
+ /* Find last workspace. */
+ if (!prev) {
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
+ if (child->type != CT_WORKSPACE)
+ continue;
+ if (!prev || child->num > prev->num)
+ prev = child;
+ }
+ }
+
+workspace_prev_on_output_end:
+ return prev;
+}
+
+/*
+ * Focuses the previously focused workspace.
+ *
+ */
+void workspace_back_and_forth() {
+ if (!previous_workspace_name) {
+ DLOG("No previous workspace name set. Not switching.");
+ return;
}
-workspace_prev_show:
- workspace_show(prev->name);
+ workspace_show_by_name(previous_workspace_name);
}
static bool get_urgency_flag(Con *con) {