]> git.sur5r.net Git - i3/i3/commitdiff
Implement jumping to windows by matching their class / title
authorMichael Stapelberg <michael+x200@stapelberg.de>
Tue, 31 Mar 2009 08:46:12 +0000 (10:46 +0200)
committerMichael Stapelberg <michael+x200@stapelberg.de>
Tue, 5 May 2009 14:53:22 +0000 (16:53 +0200)
CMDMODE
include/util.h
src/commands.c
src/util.c

diff --git a/CMDMODE b/CMDMODE
index 37dcb3a3b347183ab9d98a03aaccf9c9e24de378..0231c7051b0e34f4e3f73431539b5aa872180c5e 100644 (file)
--- a/CMDMODE
+++ b/CMDMODE
@@ -17,7 +17,7 @@ with := <w> { [ <times> ] <where> }+ <space> <cmd>
 
 oder
 
-jump := <workspace> [ <column> <row> ]
+jump := [ "<window class>[/<window title>]" | <workspace> [ <column> <row> ] ]
 
 oder
 
index 3ea2b3a75c519b8af8dcbac3c1825a614c64818e..fba1206f916a29eb6f0d949e3074a60633fe03e5 100644 (file)
@@ -22,8 +22,8 @@
 #define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \
                                                 CIRCLEQ_PREV(elm, field) : NULL)
 #define FOR_TABLE(workspace) \
-                        for (int cols = 0; cols < workspace->cols; cols++) \
-                                for (int rows = 0; rows < workspace->rows; rows++)
+                        for (int cols = 0; cols < (workspace)->cols; cols++) \
+                                for (int rows = 0; rows < (workspace)->rows; rows++)
 #define FREE(pointer) do { \
         if (pointer == NULL) { \
                 free(pointer); \
index 0462438a9f3a9d429ebe4a6ad7108cf45345a64d..95c60c8e3b3c97fb5445fca87b38c194e514168d 100644 (file)
@@ -585,18 +585,93 @@ void show_workspace(xcb_connection_t *conn, int workspace) {
         render_layout(conn);
 }
 
+/*
+ * Checks if the given window class and title match the given client
+ * Window title is passed as "normal" string and as UCS-2 converted string for
+ * matching _NET_WM_NAME capable clients as well as those using legacy hints.
+ *
+ */
+static bool client_matches_class_name(Client *client, char *to_class, char *to_title,
+                                      char *to_title_ucs, int to_title_ucs_len) {
+        /* Check if the given class is part of the window class */
+        if (strcasestr(client->window_class, to_class) == NULL)
+                return false;
+
+        /* If no title was given, we’re done */
+        if (to_title == NULL)
+                return true;
+
+        if (client->name_len > -1) {
+                /* UCS-2 converted window titles */
+                if (memmem(client->name, (client->name_len * 2), to_title_ucs, (to_title_ucs_len * 2)) == NULL)
+                        return false;
+        } else {
+                /* Legacy hints */
+                if (strcasestr(client->name, to_title) == NULL)
+                        return false;
+        }
+
+        return true;
+}
+
+/*
+ * Jumps to the given window class / title.
+ * Title is matched using strstr, that is, matches if it appears anywhere
+ * in the string. Regular expressions seem to be a bit overkill here. However,
+ * if we need them for something else somewhen, we may introduce them here, too.
+ *
+ */
+static void jump_to_window(xcb_connection_t *conn, const char *arguments) {
+        char *to_class, *to_title, *to_title_ucs = NULL;
+        int to_title_ucs_len;
+
+        /* The first character is a quote, this was checked before */
+        to_class = sstrdup(arguments+1);
+        /* The last character is a quote, we just set it to NULL */
+        to_class[strlen(to_class)-1] = '\0';
+
+        /* If a title was specified, split both strings at the slash */
+        if ((to_title = strstr(to_class, "/")) != NULL) {
+                *(to_title++) = '\0';
+                /* Convert to UCS-2 */
+                to_title_ucs = convert_utf8_to_ucs2(to_title, &to_title_ucs_len);
+        }
+
+        LOG("Should jump to class \"%s\" / title \"%s\"\n", to_class, to_title);
+        for (int workspace = 0; workspace < 10; workspace++) {
+                if (workspaces[workspace].screen == NULL)
+                        continue;
+
+                FOR_TABLE(&(workspaces[workspace])) {
+                        Container *con = workspaces[workspace].table[cols][rows];
+                        Client *client;
+
+                        CIRCLEQ_FOREACH(client, &(con->clients), clients) {
+                                LOG("Checking client with class=%s, name=%s\n", client->window_class, client->name);
+                                if (client_matches_class_name(client, to_class, to_title, to_title_ucs, to_title_ucs_len)) {
+                                        set_focus(conn, client);
+                                        goto done;
+                                }
+                        }
+                }
+        }
+
+done:
+        free(to_class);
+        FREE(to_title_ucs);
+}
+
 /*
  * Jump directly to the specified workspace, row and col.
- * Great for reaching windows that you always keep in the
- * same spot (hello irssi, I'm looking at you)
+ * Great for reaching windows that you always keep in the same spot (hello irssi, I'm looking at you)
  *
  */
-static void jump_to_container(xcb_connection_t *conn, const char* arg_str) {
+static void jump_to_container(xcb_connection_t *conn, const char *arguments) {
         int ws, row, col;
         int result;
 
-        result = sscanf(arg_str, "%d %d %d", &ws, &col, &row);
-        LOG("Jump called with %d parameters (\"%s\")\n", result, arg_str);
+        result = sscanf(arguments, "%d %d %d", &ws, &col, &row);
+        LOG("Jump called with %d parameters (\"%s\")\n", result, arguments);
 
         /* No match? Either no arguments were specified, or no numbers */
         if (result < 1) {
@@ -664,7 +739,10 @@ void parse_command(xcb_connection_t *conn, const char *command) {
 
         /* Is it a jump to a specified workspae,row,col? */
         if (STARTS_WITH(command, "jump ")) {
-                jump_to_container(conn, command + strlen("jump "));
+                const char *arguments = command + strlen("jump ");
+                if (arguments[0] == '"')
+                        jump_to_window(conn, arguments);
+                else jump_to_container(conn, arguments);
                 return;
         }
 
index c3e45c51d7642eb2561cdf55fa14f9e59e59f981..2ac0beb56cddb9aa3fa3ea73492d0c038ea75ef6 100644 (file)
@@ -213,11 +213,13 @@ char *convert_utf8_to_ucs2(char *input, int *real_strlen) {
        int rc = iconv(conversion_descriptor, (void*)&input, &input_size, &output, &output_size);
         if (rc == (size_t)-1) {
                 perror("Converting to UCS-2 failed");
-               *real_strlen = 0;
+                if (real_strlen != NULL)
+                       *real_strlen = 0;
                 return NULL;
        }
 
-       *real_strlen = ((buffer_size - output_size) / 2) - 1;
+        if (real_strlen != NULL)
+               *real_strlen = ((buffer_size - output_size) / 2) - 1;
 
        return buffer;
 }