continue;
DLOG("relevant command = %s\n", bind->command);
char *target = bind->command + strlen("workspace ");
+ while((*target == ' ' || *target == '\t') && target != '\0')
+ target++;
/* We check if this is the workspace
* next/prev/next_on_output/prev_on_output/back_and_forth/number command.
* Beware: The workspace names "next", "prev", "next_on_output",
while (exists) {
c++;
- FREE(ws->name);
- sasprintf(&(ws->name), "%d", c);
+ ws->num = c;
current = NULL;
TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
- GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name));
+ GREP_FIRST(current, output_get_content(out), child->num == ws->num);
exists = (current != NULL);
- DLOG("result for ws %s / %d: exists = %d\n", ws->name, c, exists);
+ DLOG("result for ws %d: exists = %d\n", c, exists);
}
- ws->num = c;
+ sasprintf(&(ws->name), "%d", c);
}
con_attach(ws, content, false);
workspace_reassign_sticky(current);
}
+/*
+ * Callback to reset the urgent flag of the given con to false. May be started by
+ * _workspace_show to avoid urgency hints being lost by switching to a workspace
+ * focusing the con.
+ *
+ */
+static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
+ Con *con = w->data;
+
+ DLOG("Resetting urgency flag of con %p by timer\n", con);
+ con->urgent = false;
+ con_update_parents_urgency(con);
+ workspace_update_urgent_flag(con_get_workspace(con));
+ tree_render();
+
+ ev_timer_stop(main_loop, con->urgency_timer);
+ FREE(con->urgency_timer);
+}
static void _workspace_show(Con *workspace) {
Con *current, *old = NULL;
/* safe-guard against showing i3-internal workspaces like __i3_scratch */
- if (workspace->name[0] == '_' && workspace->name[1] == '_')
+ if (con_is_internal(workspace))
return;
/* disable fullscreen for the other workspaces and get the workspace we are
* the corresponding workspace is cleaned up.
* NOTE: Internal cons such as __i3_scratch (when a scratchpad window is
* focused) are skipped, see bug #868. */
- if (current && !(current->name[0] == '_' && current->name[1] == '_')) {
+ if (current && !con_is_internal(current)) {
FREE(previous_workspace_name);
if (current) {
previous_workspace_name = sstrdup(current->name);
workspace_reassign_sticky(workspace);
- LOG("switching to %p\n", workspace);
+ DLOG("switching to %p / %s\n", workspace, workspace->name);
Con *next = con_descend_focused(workspace);
+ /* Memorize current output */
+ Con *old_output = con_get_output(focused);
+
+ /* Display urgency hint for a while if the newly visible workspace would
+ * focus and thereby immediately destroy it */
+ if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
+ /* focus for now… */
+ con_focus(next);
+
+ /* … but immediately reset urgency flags; they will be set to false by
+ * the timer callback in case the container is focused at the time of
+ * its expiration */
+ focused->urgent = true;
+ workspace->urgent = true;
+
+ if (focused->urgency_timer == NULL) {
+ DLOG("Deferring reset of urgency flag of con %p on newly shown workspace %p\n",
+ focused, workspace);
+ focused->urgency_timer = scalloc(sizeof(struct ev_timer));
+ /* use a repeating timer to allow for easy resets */
+ ev_timer_init(focused->urgency_timer, workspace_defer_update_urgent_hint_cb,
+ config.workspace_urgency_timer, config.workspace_urgency_timer);
+ focused->urgency_timer->data = focused;
+ ev_timer_start(main_loop, focused->urgency_timer);
+ } else {
+ DLOG("Resetting urgency timer of con %p on workspace %p\n",
+ focused, workspace);
+ ev_timer_again(main_loop, focused->urgency_timer);
+ }
+ } else
+ con_focus(next);
+
+ ipc_send_workspace_focus_event(workspace, current);
+
+ DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
+ /* Close old workspace if necessary. This must be done *after* doing
+ * urgency handling, because tree_close() will do a con_focus() on the next
+ * client, which will clear the urgency flag too early. Also, there is no
+ * way for con_focus() to know about when to clear urgency immediately and
+ * when to defer it. */
if (old && TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
/* check if this workspace is currently visible */
if (!workspace_is_visible(old)) {
}
}
- /* Memorize current output */
- Con *old_output = con_get_output(focused);
-
- con_focus(next);
workspace->fullscreen_mode = CF_OUTPUT;
LOG("focused now = %p / %s\n", focused, focused->name);
/* Update the EWMH hints */
ewmh_update_current_desktop();
-
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"focus\"}");
}
/*
*/
void workspace_show_by_name(const char *num) {
Con *workspace;
- bool changed_num_workspaces;
- workspace = workspace_get(num, &changed_num_workspaces);
+ workspace = workspace_get(num, NULL);
_workspace_show(workspace);
}
/* If currently a numbered workspace, find next numbered workspace. */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
/* Skip outputs starting with __, they are internal. */
- if (output->name[0] == '_' && output->name[1] == '_')
+ if (con_is_internal(output))
continue;
NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
bool found_current = false;
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
/* Skip outputs starting with __, they are internal. */
- if (output->name[0] == '_' && output->name[1] == '_')
+ if (con_is_internal(output))
continue;
NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
if (!next) {
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
/* Skip outputs starting with __, they are internal. */
- if (output->name[0] == '_' && output->name[1] == '_')
+ if (con_is_internal(output))
continue;
NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
/* If numbered workspace, find previous numbered workspace. */
TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
/* Skip outputs starting with __, they are internal. */
- if (output->name[0] == '_' && output->name[1] == '_')
+ if (con_is_internal(output))
continue;
NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE || child->num == -1)
bool found_current = false;
TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
/* Skip outputs starting with __, they are internal. */
- if (output->name[0] == '_' && output->name[1] == '_')
+ if (con_is_internal(output))
continue;
NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
if (!prev) {
TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
/* Skip outputs starting with __, they are internal. */
- if (output->name[0] == '_' && output->name[1] == '_')
+ if (con_is_internal(output))
continue;
NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
workspace_show_by_name(previous_workspace_name);
}
+/*
+ * Returns the previously focused workspace con, or NULL if unavailable.
+ *
+ */
+Con *workspace_back_and_forth_get(void) {
+ if (!previous_workspace_name) {
+ DLOG("no previous workspace name set.");
+ return NULL;
+ }
+
+ Con *workspace;
+ workspace = workspace_get(previous_workspace_name, NULL);
+
+ return workspace;
+}
+
static bool get_urgency_flag(Con *con) {
Con *child;
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
/* 1: create a new split container */
Con *split = con_new(NULL, NULL);
split->parent = ws;
- split->split = true;
/* 2: copy layout from workspace */
split->layout = ws->layout;
/* 1: create a new split container */
Con *new = con_new(NULL, NULL);
new->parent = ws;
- new->split = true;
/* 2: set the requested layout on the split con */
new->layout = ws->workspace_layout;
return new;
}
+
+/**
+ * Creates a new container and re-parents all of children from the given
+ * workspace into it.
+ *
+ * The container inherits the layout from the workspace.
+ */
+Con *workspace_encapsulate(Con *ws) {
+ if (TAILQ_EMPTY(&(ws->nodes_head))) {
+ ELOG("Workspace %p / %s has no children to encapsulate\n", ws, ws->name);
+ return NULL;
+ }
+
+ Con *new = con_new(NULL, NULL);
+ new->parent = ws;
+ new->layout = ws->layout;
+
+ DLOG("Moving children of workspace %p / %s into container %p\n",
+ ws, ws->name, new);
+
+ Con *child;
+ while (!TAILQ_EMPTY(&(ws->nodes_head))) {
+ child = TAILQ_FIRST(&(ws->nodes_head));
+ con_detach(child);
+ con_attach(child, new, true);
+ }
+
+ con_attach(new, ws, true);
+
+ return new;
+}