]> git.sur5r.net Git - i3/i3/blobdiff - src/workspace.c
Send IPC window events for focus and title changes
[i3/i3] / src / workspace.c
index 1e7071875cd97f186986723f193d2c6e9bba6fa6..670322a9428dbbe0c5cea78ccc3af5468b503856 100644 (file)
@@ -125,6 +125,8 @@ Con *create_workspace_on_output(Output *output, Con *content) {
             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",
@@ -192,17 +194,16 @@ Con *create_workspace_on_output(Output *output, Con *content) {
         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);
 
@@ -312,12 +313,30 @@ static void workspace_reassign_sticky(Con *con) {
         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
@@ -353,9 +372,49 @@ static void _workspace_show(Con *workspace) {
 
     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)) {
@@ -365,10 +424,6 @@ static void _workspace_show(Con *workspace) {
         }
     }
 
-    /* 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);
 
@@ -380,8 +435,6 @@ static void _workspace_show(Con *workspace) {
 
     /* Update the EWMH hints */
     ewmh_update_current_desktop();
-
-    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"focus\"}");
 }
 
 /*
@@ -398,8 +451,7 @@ void workspace_show(Con *workspace) {
  */
 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);
 }
 
@@ -419,7 +471,7 @@ Con* workspace_next(void) {
         /* 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)
@@ -440,7 +492,7 @@ Con* workspace_next(void) {
         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)
@@ -459,7 +511,7 @@ Con* workspace_next(void) {
     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)
@@ -491,7 +543,7 @@ Con* workspace_prev(void) {
         /* 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)
@@ -510,7 +562,7 @@ Con* workspace_prev(void) {
         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)
@@ -529,7 +581,7 @@ Con* workspace_prev(void) {
     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)
@@ -670,6 +722,22 @@ void workspace_back_and_forth(void) {
     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)
@@ -706,7 +774,6 @@ void ws_force_orientation(Con *ws, orientation_t orientation) {
     /* 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;
@@ -758,7 +825,6 @@ Con *workspace_attach_to(Con *ws) {
     /* 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;
@@ -769,3 +835,34 @@ Con *workspace_attach_to(Con *ws) {
 
     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;
+}