From 1de97a1f1fae52b513b1d1b4b01ccf9fa70425b9 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 21 Nov 2010 23:35:49 +0100 Subject: [PATCH] correctly sort numbered workspaces (+testcase) 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 | 4 +++ src/con.c | 34 +++++++++++++++++++-- src/ipc.c | 4 ++- src/tree.c | 4 ++- src/workspace.c | 16 +++++++++- testcases/t/39-ws-numbers.t | 59 +++++++++++++++++++++++++++++++++++++ testcases/t/lib/i3test.pm | 2 +- 7 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 testcases/t/39-ws-numbers.t diff --git a/include/data.h b/include/data.h index 97d47948..06ceef0b 100644 --- a/include/data.h +++ b/include/data.h @@ -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 */ diff --git a/src/con.c b/src/con.c index a01bdcc1..3d1441f6 100644 --- 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. */ diff --git a/src/ipc.c b/src/ipc.c index 40b6a712..1a76950a 100644 --- 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); diff --git a/src/tree.c b/src/tree.c index f9f10d59..290f1fc2 100644 --- a/src/tree.c +++ b/src/tree.c @@ -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); diff --git a/src/workspace.c b/src/workspace.c index caa35c88..837ac1fe 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -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 index 00000000..e3f35722 --- /dev/null +++ b/testcases/t/39-ws-numbers.t @@ -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'); diff --git a/testcases/t/lib/i3test.pm b/testcases/t/lib/i3test.pm index c0a229dc..83b48e11 100644 --- a/testcases/t/lib/i3test.pm +++ b/testcases/t/lib/i3test.pm @@ -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;