From 7df88f18eb6bc786433433b6a149bb2824377e05 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sun, 12 Apr 2020 15:11:53 +0200 Subject: [PATCH] Sort dock clients by class and instance This is similar to #3820 but does not use qsort but an insertion sort in con_attach. Since each bar block automatically gets its own incremental bar id, bards end up being sorted according to their definition order in the config file. For i3bar, the WM_CLASS is modified to include an instance name which depends on the bar_id. This could be useful for other reason, e.g. users targeting a specific bar instance. Fixes #3491 --- i3bar/src/xcb.c | 13 ------------- include/libi3.h | 6 ++++++ libi3/safewrappers.c | 17 +++++++++++++++++ src/con.c | 24 ++++++++++++++++++++++++ src/handlers.c | 4 +--- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index ae447fbc..ecb41a60 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -685,19 +685,6 @@ static void handle_visibility_notify(xcb_visibility_notify_event_t *event) { } } -static int strcasecmp_nullable(const char *a, const char *b) { - if (a == b) { - return 0; - } - if (a == NULL) { - return -1; - } - if (b == NULL) { - return 1; - } - return strcasecmp(a, b); -} - /* * Comparison function to sort trayclients in ascending alphanumeric order * according to their class. diff --git a/include/libi3.h b/include/libi3.h index 15d1bc76..d9c89ddd 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -181,6 +181,12 @@ ssize_t writeall_nonblock(int fd, const void *buf, size_t count); */ ssize_t swrite(int fd, const void *buf, size_t count); +/** + * Like strcasecmp but considers the case where either string is NULL. + * + */ +int strcasecmp_nullable(const char *a, const char *b); + /** * Build an i3String from an UTF-8 encoded string. * Returns the newly-allocated i3String. diff --git a/libi3/safewrappers.c b/libi3/safewrappers.c index 1802b327..fdea636e 100644 --- a/libi3/safewrappers.c +++ b/libi3/safewrappers.c @@ -110,3 +110,20 @@ ssize_t swrite(int fd, const void *buf, size_t count) { else return n; } + +/* + * Like strcasecmp but considers the case where either string is NULL. + * + */ +int strcasecmp_nullable(const char *a, const char *b) { + if (a == b) { + return 0; + } + if (a == NULL) { + return -1; + } + if (b == NULL) { + return 1; + } + return strcasecmp(a, b); +} diff --git a/src/con.c b/src/con.c index ba0eaa3c..08bb7e4e 100644 --- a/src/con.c +++ b/src/con.c @@ -132,6 +132,30 @@ static void _con_attach(Con *con, Con *parent, Con *previous, bool ignore_focus) goto add_to_focus_head; } + if (parent->type == CT_DOCKAREA) { + /* Insert dock client, sorting alphanumerically by class and then + * instance name. This makes dock client order deterministic. As a side + * effect, bars without a custom bar id will be sorted according to + * their declaration order in the config file. See #3491. */ + current = NULL; + TAILQ_FOREACH (loop, nodes_head, nodes) { + int result = strcasecmp_nullable(con->window->class_class, loop->window->class_class); + if (result == 0) { + result = strcasecmp_nullable(con->window->class_instance, loop->window->class_instance); + } + if (result < 0) { + current = loop; + break; + } + } + if (current) { + TAILQ_INSERT_BEFORE(loop, con, nodes); + } else { + TAILQ_INSERT_TAIL(nodes_head, con, nodes); + } + goto add_to_focus_head; + } + if (con->type == CT_FLOATING_CON) { DLOG("Inserting into floating containers\n"); TAILQ_INSERT_TAIL(&(parent->floating_head), con, floating_windows); diff --git a/src/handlers.c b/src/handlers.c index 22a974ac..5e894ae5 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1147,9 +1147,7 @@ static bool handle_strut_partial_change(Con *con, xcb_get_property_reply_t *prop /* attach the dock to the dock area */ con_detach(con); - con->parent = dockarea; - TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused); - TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes); + con_attach(con, dockarea, true); tree_render();