Implement jumping to windows by matching their class / title
This commit is contained in:
parent
e295ab302b
commit
f72214725c
2
CMDMODE
2
CMDMODE
|
@ -17,7 +17,7 @@ with := <w> { [ <times> ] <where> }+ <space> <cmd>
|
||||||
|
|
||||||
oder
|
oder
|
||||||
|
|
||||||
jump := <workspace> [ <column> <row> ]
|
jump := [ "<window class>[/<window title>]" | <workspace> [ <column> <row> ] ]
|
||||||
|
|
||||||
oder
|
oder
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \
|
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \
|
||||||
CIRCLEQ_PREV(elm, field) : NULL)
|
CIRCLEQ_PREV(elm, field) : NULL)
|
||||||
#define FOR_TABLE(workspace) \
|
#define FOR_TABLE(workspace) \
|
||||||
for (int cols = 0; cols < workspace->cols; cols++) \
|
for (int cols = 0; cols < (workspace)->cols; cols++) \
|
||||||
for (int rows = 0; rows < workspace->rows; rows++)
|
for (int rows = 0; rows < (workspace)->rows; rows++)
|
||||||
#define FREE(pointer) do { \
|
#define FREE(pointer) do { \
|
||||||
if (pointer == NULL) { \
|
if (pointer == NULL) { \
|
||||||
free(pointer); \
|
free(pointer); \
|
||||||
|
|
|
@ -586,17 +586,92 @@ void show_workspace(xcb_connection_t *conn, int workspace) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Jump directly to the specified workspace, row and col.
|
* Checks if the given window class and title match the given client
|
||||||
* Great for reaching windows that you always keep in the
|
* Window title is passed as "normal" string and as UCS-2 converted string for
|
||||||
* same spot (hello irssi, I'm looking at you)
|
* matching _NET_WM_NAME capable clients as well as those using legacy hints.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void jump_to_container(xcb_connection_t *conn, const char* arg_str) {
|
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)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void jump_to_container(xcb_connection_t *conn, const char *arguments) {
|
||||||
int ws, row, col;
|
int ws, row, col;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
result = sscanf(arg_str, "%d %d %d", &ws, &col, &row);
|
result = sscanf(arguments, "%d %d %d", &ws, &col, &row);
|
||||||
LOG("Jump called with %d parameters (\"%s\")\n", result, arg_str);
|
LOG("Jump called with %d parameters (\"%s\")\n", result, arguments);
|
||||||
|
|
||||||
/* No match? Either no arguments were specified, or no numbers */
|
/* No match? Either no arguments were specified, or no numbers */
|
||||||
if (result < 1) {
|
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? */
|
/* Is it a jump to a specified workspae,row,col? */
|
||||||
if (STARTS_WITH(command, "jump ")) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
int rc = iconv(conversion_descriptor, (void*)&input, &input_size, &output, &output_size);
|
||||||
if (rc == (size_t)-1) {
|
if (rc == (size_t)-1) {
|
||||||
perror("Converting to UCS-2 failed");
|
perror("Converting to UCS-2 failed");
|
||||||
*real_strlen = 0;
|
if (real_strlen != NULL)
|
||||||
|
*real_strlen = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*real_strlen = ((buffer_size - output_size) / 2) - 1;
|
if (real_strlen != NULL)
|
||||||
|
*real_strlen = ((buffer_size - output_size) / 2) - 1;
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue