From 32c10a19f28d348ab43232e0fb5a8591b73540c7 Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Wed, 8 Apr 2020 10:58:18 +0000 Subject: [PATCH] Add "machine" criterion to match WM_CLIENT_MACHINE Closes #3981 Add "%machine" title_format placeholder Add "machine" to the IPC and layout saving/restoring --- RELEASE-NOTES-next | 2 ++ docs/ipc | 3 ++- docs/layout-saving | 6 +++--- docs/userguide | 13 ++++++++++--- include/data.h | 4 ++++ include/window.h | 6 ++++++ parser-specs/commands.spec | 1 + parser-specs/config.spec | 1 + src/con.c | 7 ++++++- src/handlers.c | 14 +++++++++++++- src/ipc.c | 2 ++ src/load_layout.c | 3 +++ src/manage.c | 5 ++++- src/match.c | 10 ++++++++++ src/restore_layout.c | 1 + src/window.c | 18 ++++++++++++++++++ 16 files changed, 86 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES-next b/RELEASE-NOTES-next index 4f45b145..45c92721 100644 --- a/RELEASE-NOTES-next +++ b/RELEASE-NOTES-next @@ -17,6 +17,8 @@ strongly encouraged to upgrade. • i3bar: use first bar config by default • i3-dump-log -f now uses UNIX sockets instead of pthreads. The UNIX socket approach should be more reliable and also more portable. + • Allow for_window to match against WM_CLIENT_MACHINE + • Add %machine placeholder (WM_CLIENT_MACHINE) to title_format ┌────────────────────────────┐ │ Bugfixes │ diff --git a/docs/ipc b/docs/ipc index 8377795d..e130094c 100644 --- a/docs/ipc +++ b/docs/ipc @@ -362,7 +362,8 @@ window (integer):: X11-related tools display (usually in hex). window_properties (map):: This optional field contains all available X11 window properties from the - following list: *title*, *instance*, *class*, *window_role* and *transient_for*. + following list: *title*, *instance*, *class*, *window_role*, *machine* + and *transient_for*. window_type (string):: The window type (_NET_WM_WINDOW_TYPE). Possible values are undefined, normal, dialog, utility, toolbar, splash, menu, dropdown_menu, popup_menu, tooltip and diff --git a/docs/layout-saving b/docs/layout-saving index 4f0ffccf..380ffff8 100644 --- a/docs/layout-saving +++ b/docs/layout-saving @@ -185,9 +185,9 @@ Therefore, if you just start Emacs via dmenu, it will not get swallowed by that container. Only if you start Emacs with the proper instance name (+emacs24 --name notmuch+), it will get swallowed. -You can match on "class", "instance", "window_role" and "title". All values are -case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click into a -window to see its properties: +You can match on "class", "instance", "window_role", "title" and "machine". All +values are case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click +into a window to see its properties: -------------------------------------------------------------------------------- $ xprop diff --git a/docs/userguide b/docs/userguide index 37343df9..a727c040 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1896,6 +1896,10 @@ window_type:: Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are +normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+, +popup_menu+, +tooltip+ and +notification+. +machine:: + Compares the name of the machine the client window is running on + (WM_CLIENT_MACHINE). Usually, it is equal to the hostname of the local + machine, but it may differ if remote X11 apps are used. id:: Compares the X11 window ID, which you can get via +xwininfo+ for example. title:: @@ -1933,9 +1937,9 @@ tiling_from:: tiling are matched. With "user", only windows that the user made tiling are matched. -The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are -actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for -information on how to use them. +The criteria +class+, +instance+, +role+, +title+, +workspace+, +machine+ and ++mark+ are actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc +perlre+ for information on how to use them. [[exec]] === Executing applications (exec) @@ -2584,6 +2588,9 @@ and the following placeholders which will be replaced: +%instance+:: The X11 window instance (first part of WM_CLASS). This corresponds to the +instance+ criterion, see <>. ++%machine+:: + The X11 name of the machine (WM_CLIENT_MACHINE). This corresponds to the + +machine+ criterion, see <>. Using the <> directive, you can set the title format for any window based on <>. diff --git a/include/data.h b/include/data.h index e411129f..1d47af64 100644 --- a/include/data.h +++ b/include/data.h @@ -414,6 +414,9 @@ struct Window { * for_window. */ char *role; + /** WM_CLIENT_MACHINE of the window */ + char *machine; + /** Flag to force re-rendering the decoration upon changes */ bool name_x_changed; @@ -500,6 +503,7 @@ struct Match { struct regex *mark; struct regex *window_role; struct regex *workspace; + struct regex *machine; xcb_atom_t window_type; enum { U_DONTCHECK = -1, diff --git a/include/window.h b/include/window.h index 6673e835..858bf0cd 100644 --- a/include/window.h +++ b/include/window.h @@ -95,3 +95,9 @@ void window_update_hints(i3Window *win, xcb_get_property_reply_t *prop, bool *ur * */ void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style); + +/** + * Updates the WM_CLIENT_MACHINE + * + */ +void window_update_machine(i3Window *win, xcb_get_property_reply_t *prop); diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index 4db81680..6a15ad19 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -54,6 +54,7 @@ state CRITERIA: ctype = 'title' -> CRITERION ctype = 'urgent' -> CRITERION ctype = 'workspace' -> CRITERION + ctype = 'machine' -> CRITERION ctype = 'tiling', 'floating' -> call cmd_criteria_add($ctype, NULL); CRITERIA ']' -> call cmd_criteria_match_windows(); INITIAL diff --git a/parser-specs/config.spec b/parser-specs/config.spec index bb9e226e..7d7b9989 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -191,6 +191,7 @@ state CRITERIA: ctype = 'title' -> CRITERION ctype = 'urgent' -> CRITERION ctype = 'workspace' -> CRITERION + ctype = 'machine' -> CRITERION ctype = 'floating_from' -> CRITERION_FROM ctype = 'tiling_from' -> CRITERION_FROM ctype = 'tiling', 'floating' diff --git a/src/con.c b/src/con.c index 1f0dbed3..9f0eb7ad 100644 --- a/src/con.c +++ b/src/con.c @@ -2304,20 +2304,25 @@ i3String *con_parse_title_format(Con *con) { char *title; char *class; char *instance; + char *machine; if (win == NULL) { title = pango_escape_markup(con_get_tree_representation(con)); class = sstrdup("i3-frame"); instance = sstrdup("i3-frame"); + machine = sstrdup(""); } else { title = pango_escape_markup(sstrdup((win->name == NULL) ? "" : i3string_as_utf8(win->name))); class = pango_escape_markup(sstrdup((win->class_class == NULL) ? "" : win->class_class)); instance = pango_escape_markup(sstrdup((win->class_instance == NULL) ? "" : win->class_instance)); + machine = pango_escape_markup(sstrdup((win->machine == NULL) ? "" : win->machine)); } placeholder_t placeholders[] = { {.name = "%title", .value = title}, {.name = "%class", .value = class}, - {.name = "%instance", .value = instance}}; + {.name = "%instance", .value = instance}, + {.name = "%machine", .value = machine}, + }; const size_t num = sizeof(placeholders) / sizeof(placeholder_t); char *formatted_str = format_placeholders(con->title_format, &placeholders[0], num); diff --git a/src/handlers.c b/src/handlers.c index eba5fe29..fe967773 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1086,6 +1086,16 @@ static bool handle_class_change(Con *con, xcb_get_property_reply_t *prop) { return true; } +/* + * Handles the WM_CLIENT_MACHINE property for assignments and criteria selection. + * + */ +static bool handle_machine_change(Con *con, xcb_get_property_reply_t *prop) { + window_update_machine(con->window, prop); + con = remanage_window(con); + return true; +} + /* * Handles the _MOTIF_WM_HINTS property of specifing window deocration settings. * @@ -1197,6 +1207,7 @@ static struct property_handler_t property_handlers[] = { {0, UINT_MAX, handle_strut_partial_change}, {0, UINT_MAX, handle_window_type}, {0, UINT_MAX, handle_i3_floating}, + {0, 128, handle_machine_change}, {0, 5 * sizeof(uint64_t), handle_motif_hints_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) @@ -1219,7 +1230,8 @@ void property_handlers_init(void) { property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL; property_handlers[9].atom = A__NET_WM_WINDOW_TYPE; property_handlers[10].atom = A_I3_FLOATING_WINDOW; - property_handlers[11].atom = A__MOTIF_WM_HINTS; + property_handlers[11].atom = XCB_ATOM_WM_CLIENT_MACHINE; + property_handlers[12].atom = A__MOTIF_WM_HINTS; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { diff --git a/src/ipc.c b/src/ipc.c index d69ecb6e..1ebe15d0 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -557,6 +557,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { DUMP_PROPERTY("class", class_class); DUMP_PROPERTY("instance", class_instance); DUMP_PROPERTY("window_role", role); + DUMP_PROPERTY("machine", machine); if (con->window->name != NULL) { ystr("title"); @@ -646,6 +647,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { DUMP_REGEX(instance); DUMP_REGEX(window_role); DUMP_REGEX(title); + DUMP_REGEX(machine); #undef DUMP_REGEX y(map_close); diff --git a/src/load_layout.c b/src/load_layout.c index bb91ffa4..3d8033e8 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -285,6 +285,9 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) { } else if (strcasecmp(last_key, "title") == 0) { current_swallow->title = regex_new(sval); swallow_is_empty = false; + } else if (strcasecmp(last_key, "machine") == 0) { + current_swallow->machine = regex_new(sval); + swallow_is_empty = false; } else { ELOG("swallow key %s unknown\n", last_key); } diff --git a/src/manage.c b/src/manage.c index da23ab3e..8ea820de 100644 --- a/src/manage.c +++ b/src/manage.c @@ -116,7 +116,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki utf8_title_cookie, title_cookie, class_cookie, leader_cookie, transient_cookie, role_cookie, startup_id_cookie, wm_hints_cookie, - wm_normal_hints_cookie, motif_wm_hints_cookie, wm_user_time_cookie, wm_desktop_cookie; + wm_normal_hints_cookie, motif_wm_hints_cookie, wm_user_time_cookie, wm_desktop_cookie, + wm_machine_cookie; geomc = xcb_get_geometry(conn, d); @@ -189,6 +190,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t)); wm_user_time_cookie = GET_PROPERTY(A__NET_WM_USER_TIME, UINT32_MAX); wm_desktop_cookie = GET_PROPERTY(A__NET_WM_DESKTOP, UINT32_MAX); + wm_machine_cookie = GET_PROPERTY(XCB_ATOM_WM_CLIENT_MACHINE, UINT32_MAX); i3Window *cwindow = scalloc(1, sizeof(i3Window)); cwindow->id = window; @@ -211,6 +213,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki border_style_t motif_border_style = BS_NORMAL; window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style); window_update_normal_hints(cwindow, xcb_get_property_reply(conn, wm_normal_hints_cookie, NULL), geom); + window_update_machine(cwindow, xcb_get_property_reply(conn, wm_machine_cookie, NULL)); xcb_get_property_reply_t *type_reply = xcb_get_property_reply(conn, wm_type_cookie, NULL); xcb_get_property_reply_t *state_reply = xcb_get_property_reply(conn, state_cookie, NULL); diff --git a/src/match.c b/src/match.c index 6ac312e5..65646f9e 100644 --- a/src/match.c +++ b/src/match.c @@ -47,6 +47,7 @@ bool match_is_empty(Match *match) { match->instance == NULL && match->window_role == NULL && match->workspace == NULL && + match->machine == NULL && match->urgent == U_DONTCHECK && match->id == XCB_NONE && match->window_type == UINT32_MAX && @@ -130,6 +131,8 @@ bool match_matches_window(Match *match, i3Window *window) { } } + CHECK_WINDOW_FIELD(machine, machine, str); + Con *con = NULL; if (match->urgent == U_LATEST) { /* if the window isn't urgent, no sense in searching */ @@ -273,6 +276,7 @@ void match_free(Match *match) { regex_free(match->mark); regex_free(match->window_role); regex_free(match->workspace); + regex_free(match->machine); } /* @@ -390,6 +394,12 @@ void match_parse_property(Match *match, const char *ctype, const char *cvalue) { return; } + if (strcmp(ctype, "machine") == 0) { + regex_free(match->machine); + match->machine = regex_new(cvalue); + return; + } + if (strcmp(ctype, "tiling") == 0) { match->window_mode = WM_TILING; return; diff --git a/src/restore_layout.c b/src/restore_layout.c index c51bfcbe..78f0e097 100644 --- a/src/restore_layout.c +++ b/src/restore_layout.c @@ -153,6 +153,7 @@ static void update_placeholder_contents(placeholder_state *state) { APPEND_REGEX(instance); APPEND_REGEX(window_role); APPEND_REGEX(title); + APPEND_REGEX(machine); if (serialized == NULL) { DLOG("This swallows specification is not serializable?!\n"); diff --git a/src/window.c b/src/window.c index bee3fa66..2c2f6c0a 100644 --- a/src/window.c +++ b/src/window.c @@ -466,3 +466,21 @@ void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, bo #undef MWM_DECOR_BORDER #undef MWM_DECOR_TITLE } + +/* + * Updates the WM_CLIENT_MACHINE + * + */ +void window_update_machine(i3Window *win, xcb_get_property_reply_t *prop) { + if (prop == NULL || xcb_get_property_value_length(prop) == 0) { + DLOG("WM_CLIENT_MACHINE not set.\n"); + FREE(prop); + return; + } + + FREE(win->machine); + win->machine = sstrndup((char *)xcb_get_property_value(prop), xcb_get_property_value_length(prop)); + LOG("WM_CLIENT_MACHINE changed to \"%s\"\n", win->machine); + + free(prop); +}