]> git.sur5r.net Git - i3/i3/commitdiff
correctly sort numbered workspaces (+testcase)
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 21 Nov 2010 22:35:49 +0000 (23:35 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 21 Nov 2010 22:35:49 +0000 (23:35 +0100)
Numbered workspaces (workspaces with a name containing only digits) will be
inserted in the correct order now. Named workspaces are always sorted after
numbered workspaces and in the order of creation.

include/data.h
src/con.c
src/ipc.c
src/tree.c
src/workspace.c
testcases/t/39-ws-numbers.t [new file with mode: 0644]
testcases/t/lib/i3test.pm

index 97d479488532fbcb13ef16a380a9ddd35b5626ff..06ceef0b51e010495d9c055ec9125a1c3bea6a53 100644 (file)
@@ -281,6 +281,10 @@ struct Con {
 
     char *name;
 
+    /** the workspace number, if this Con is of type CT_WORKSPACE and the
+     * workspace is not a named workspace (for named workspaces, num == -1) */
+    int num;
+
     /* a sticky-group is an identifier which bundles several containers to a
      * group. The contents are shared between all of them, that is they are
      * displayed on whichever of the containers is currently visible */
index a01bdcc1c45dbd9ae7645297769414b75470611a..3d1441f65e07ffe1f1561478e97b7ddfd476e704 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -72,6 +72,35 @@ void con_attach(Con *con, Con *parent) {
     con->parent = parent;
     Con *loop;
     Con *current = NULL;
+    struct nodes_head *nodes_head = &(parent->nodes_head);
+
+    /* Workspaces are handled differently: they need to be inserted at the
+     * right position. */
+    if (con->type == CT_WORKSPACE) {
+        DLOG("it's a workspace. num = %d\n", con->num);
+        if (con->num == -1 || TAILQ_EMPTY(nodes_head)) {
+            TAILQ_INSERT_TAIL(nodes_head, con, nodes);
+        } else {
+            current = TAILQ_FIRST(nodes_head);
+            if (con->num < current->num) {
+                /* we need to insert the container at the beginning */
+                TAILQ_INSERT_HEAD(nodes_head, con, nodes);
+                return;
+            }
+            while (current->num != -1 && con->num > current->num) {
+                current = TAILQ_NEXT(current, nodes);
+                if (current == TAILQ_END(nodes_head)) {
+                    current = NULL;
+                    break;
+                }
+            }
+            /* we need to insert con after current, if current is not NULL */
+            if (current)
+                TAILQ_INSERT_BEFORE(current, con, nodes);
+            else TAILQ_INSERT_TAIL(nodes_head, con, nodes);
+        }
+        goto add_to_focus_head;
+    }
 
     /* Get the first tiling container in focus stack */
     TAILQ_FOREACH(loop, &(parent->focus_head), focused) {
@@ -85,9 +114,10 @@ void con_attach(Con *con, Con *parent) {
     if (current) {
         DLOG("Inserting con = %p after last focused tiling con %p\n",
              con, current);
-        TAILQ_INSERT_AFTER(&(parent->nodes_head), current, con, nodes);
-    } else TAILQ_INSERT_TAIL(&(parent->nodes_head), con, nodes);
+        TAILQ_INSERT_AFTER(nodes_head, current, con, nodes);
+    } else TAILQ_INSERT_TAIL(nodes_head, con, nodes);
 
+add_to_focus_head:
     /* We insert to the TAIL because con_focus() will correct this.
      * This way, we have the option to insert Cons without having
      * to focus them. */
index 40b6a71269fb6f91b07c5e02bce33383959b2298..1a76950ab90c3ae342539cf43e521e5a5dac834d 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -269,7 +269,9 @@ IPC_HANDLER(get_workspaces) {
             y(map_open);
 
             ystr("num");
-            y(integer, con_num_children(ws));
+            if (ws->num == -1)
+                y(null);
+            else y(integer, ws->num);
 
             ystr("name");
             ystr(ws->name);
index f9f10d59b44cd55070ef0c246e9d821aff752fc1..290f1fc24381adbc8e3a47a0d203fe18b8fa1ff0 100644 (file)
@@ -77,10 +77,12 @@ void tree_init() {
         free(name);
 
         /* add a workspace to this output */
-        ws = con_new(oc);
+        ws = con_new(NULL);
         ws->type = CT_WORKSPACE;
+        ws->num = c;
         asprintf(&(ws->name), "%d", c);
         c++;
+        con_attach(ws, oc);
 
         asprintf(&name, "[i3 con] workspace %s", ws->name);
         x_set_name(ws, name);
index caa35c88f74262e03ee9a2384e923ee6f193f903..837ac1febb9332a38421cee6b1820d8321b70875 100644 (file)
@@ -38,14 +38,28 @@ Con *workspace_get(const char *num) {
         LOG("need to create this one\n");
         output = con_get_output(focused);
         LOG("got output %p\n", output);
-        workspace = con_new(output);
+        /* We need to attach this container after setting its type. con_attach
+         * will handle CT_WORKSPACEs differently */
+        workspace = con_new(NULL);
         char *name;
         asprintf(&name, "[i3 con] workspace %s", num);
         x_set_name(workspace, name);
         free(name);
         workspace->type = CT_WORKSPACE;
         workspace->name = strdup(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. */
+        char *end;
+        long parsed_num = strtol(num, &end, 10);
+        if (parsed_num == LONG_MIN ||
+            parsed_num == LONG_MAX ||
+            parsed_num < 0 ||
+            (end && *end != '\0'))
+            workspace->num = -1;
+        else workspace->num = parsed_num;
+        LOG("num = %d\n", workspace->num);
         workspace->orientation = HORIZ;
+        con_attach(workspace, output);
 
         ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
     }
diff --git a/testcases/t/39-ws-numbers.t b/testcases/t/39-ws-numbers.t
new file mode 100644 (file)
index 0000000..e3f3572
--- /dev/null
@@ -0,0 +1,59 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+# Check if numbered workspaces and named workspaces are sorted in the right way
+# in get_workspaces IPC output (necessary for i3bar etc.).
+use i3test tests => 9;
+use X11::XCB qw(:all);
+use Time::HiRes qw(sleep);
+
+BEGIN {
+    use_ok('X11::XCB::Window');
+}
+
+my $i3 = i3("/tmp/nestedcons");
+my $x = X11::XCB::Connection->new;
+
+sub check_order {
+    my ($msg) = @_;
+
+    my @ws = @{$i3->get_workspaces->recv};
+    my @nums = map { $_->{num} } grep { defined($_->{num}) } @ws;
+    my @sorted = sort @nums;
+
+    cmp_deeply(\@nums, \@sorted, $msg);
+}
+
+check_order('workspace order alright before testing');
+
+#############################################################################
+# open a window to keep this ws open
+#############################################################################
+
+$i3->command("workspace 93")->recv;
+
+open_standard_window($x);
+
+my @ws = @{$i3->get_workspaces->recv};
+my @f = grep { defined($_->{num}) && $_->{num} == 93 } @ws;
+is(@f, 1, 'ws 93 found by num');
+check_order('workspace order alright after opening 93');
+
+$i3->command("workspace 92")->recv;
+open_standard_window($x);
+check_order('workspace order alright after opening 92');
+
+$i3->command("workspace 94")->recv;
+open_standard_window($x);
+check_order('workspace order alright after opening 94');
+
+$i3->command("workspace 96")->recv;
+open_standard_window($x);
+check_order('workspace order alright after opening 96');
+
+$i3->command("workspace foo")->recv;
+open_standard_window($x);
+check_order('workspace order alright after opening foo');
+
+$i3->command("workspace 91")->recv;
+open_standard_window($x);
+check_order('workspace order alright after opening 91');
index c0a229dc5f25f0222e1fd9202331c24ca713de31..83b48e11da55811ea3723433f7d3a1c3308fa53b 100644 (file)
@@ -10,7 +10,7 @@ use List::Util qw(first);
 use v5.10;
 
 use Exporter ();
-our @EXPORT = qw(get_workspace_names get_unused_workspace get_ws_content get_ws get_focused open_empty_con);
+our @EXPORT = qw(get_workspace_names get_unused_workspace get_ws_content get_ws get_focused open_empty_con open_standard_window);
 
 BEGIN {
     my $window_count = 0;