* 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->name = sstrdup(num);
/* We set ->num to the number if this workspace’s name consists only of
* a positive number. Otherwise it’s a named ws and num will be -1. */
-
- long parsed_num = strtol(num, NULL, 10);
+ char *endptr = NULL;
+ long parsed_num = strtol(num, &endptr, 10);
if (parsed_num == LONG_MIN ||
parsed_num == LONG_MAX ||
- parsed_num <= 0)
+ parsed_num < 0 ||
+ endptr == num)
workspace->num = -1;
else workspace->num = parsed_num;
LOG("num = %d\n", workspace->num);
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);
/* check if this workspace is currently visible */
if (!workspace_is_visible(old)) {
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
- tree_close(old, DONT_KILL_WINDOW, false);
+ tree_close(old, DONT_KILL_WINDOW, false, false);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
changed_num_workspaces = true;
}
}
/* 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 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) {