Implement the window_role criterion (checks WM_WINDOW_ROLE)

Closes: #446

This is handy for matching specific windows of a multi-window application, for
example only Pidgin’s buddy list window.
next
Michael Stapelberg 2011-09-18 16:05:10 +01:00
parent 99168a84a9
commit b3adaa2983
11 changed files with 98 additions and 2 deletions

View File

@ -21,5 +21,6 @@ xmacro(UTF8_STRING)
xmacro(WM_STATE)
xmacro(WM_CLIENT_LEADER)
xmacro(WM_TAKE_FOCUS)
xmacro(WM_WINDOW_ROLE)
xmacro(I3_SOCKET_PATH)
xmacro(I3_CONFIG_PATH)

View File

@ -264,6 +264,11 @@ struct Window {
* application supports _NET_WM_NAME, in COMPOUND_TEXT otherwise). */
char *name_x;
/** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window
* sets "buddy list"). Useful to match specific windows in assignments or
* for_window. */
char *role;
/** Flag to force re-rendering the decoration upon changes */
bool name_x_changed;
@ -298,6 +303,7 @@ struct Match {
struct regex *class;
struct regex *instance;
struct regex *mark;
struct regex *role;
enum {
M_DONTCHECK = -1,
M_NODOCK = 0,

View File

@ -42,4 +42,10 @@ void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop);
*/
void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop);
/**
* Updates the WM_WINDOW_ROLE
*
*/
void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt);
#endif

View File

@ -170,6 +170,7 @@ shift { return TOKSHIFT; }
class { yy_push_state(WANT_QSTRING); return TOK_CLASS; }
instance { yy_push_state(WANT_QSTRING); return TOK_INSTANCE; }
window_role { yy_push_state(WANT_QSTRING); return TOK_WINDOW_ROLE; }
id { yy_push_state(WANT_QSTRING); return TOK_ID; }
con_id { yy_push_state(WANT_QSTRING); return TOK_CON_ID; }
con_mark { yy_push_state(WANT_QSTRING); return TOK_MARK; }

View File

@ -632,6 +632,7 @@ void parse_file(const char *f) {
%token TOK_MARK "mark"
%token TOK_CLASS "class"
%token TOK_INSTANCE "instance"
%token TOK_WINDOW_ROLE "window_role"
%token TOK_ID "id"
%token TOK_CON_ID "con_id"
%token TOK_TITLE "title"
@ -792,6 +793,12 @@ criterion:
current_match.instance = regex_new($3);
free($3);
}
| TOK_WINDOW_ROLE '=' STR
{
printf("criteria: window_role = %s\n", $3);
current_match.role = regex_new($3);
free($3);
}
| TOK_CON_ID '=' STR
{
printf("criteria: id = %s\n", $3);

View File

@ -155,6 +155,7 @@ no { return TOK_DISABLE; }
class { BEGIN(WANT_QSTRING); return TOK_CLASS; }
instance { BEGIN(WANT_QSTRING); return TOK_INSTANCE; }
window_role { BEGIN(WANT_QSTRING); return TOK_WINDOW_ROLE; }
id { BEGIN(WANT_QSTRING); return TOK_ID; }
con_id { BEGIN(WANT_QSTRING); return TOK_CON_ID; }
con_mark { BEGIN(WANT_QSTRING); return TOK_MARK; }

View File

@ -177,6 +177,7 @@ bool definitelyGreaterThan(float a, float b, float epsilon) {
%token TOK_CLASS "class"
%token TOK_INSTANCE "instance"
%token TOK_WINDOW_ROLE "window_role"
%token TOK_ID "id"
%token TOK_CON_ID "con_id"
%token TOK_TITLE "title"
@ -308,6 +309,12 @@ criterion:
current_match.instance = regex_new($3);
free($3);
}
| TOK_WINDOW_ROLE '=' STR
{
printf("criteria: window_role = %s\n", $3);
current_match.role = regex_new($3);
free($3);
}
| TOK_CON_ID '=' STR
{
printf("criteria: id = %s\n", $3);

View File

@ -557,6 +557,21 @@ static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn,
return true;
}
/*
* Called when a window changes its WM_WINDOW_ROLE.
*
*/
static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t state,
xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
Con *con;
if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
return false;
window_update_role(con->window, prop, false);
return true;
}
#if 0
/*
* Updates the clients WM_CLASS property
@ -933,7 +948,8 @@ static struct property_handler_t property_handlers[] = {
{ 0, 128, handle_windowname_change_legacy },
{ 0, UINT_MAX, handle_normal_hints },
{ 0, UINT_MAX, handle_clientleader_change },
{ 0, UINT_MAX, handle_transient_for }
{ 0, UINT_MAX, handle_transient_for },
{ 0, 128, handle_windowrole_change }
};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
@ -949,6 +965,7 @@ void property_handlers_init() {
property_handlers[3].atom = XCB_ATOM_WM_NORMAL_HINTS;
property_handlers[4].atom = A_WM_CLIENT_LEADER;
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
property_handlers[6].atom = A_WM_WINDOW_ROLE;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {

View File

@ -83,7 +83,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
utf8_title_cookie, title_cookie,
class_cookie, leader_cookie, transient_cookie;
class_cookie, leader_cookie, transient_cookie,
role_cookie;
geomc = xcb_get_geometry(conn, d);
@ -145,6 +146,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
transient_cookie = GET_PROPERTY(XCB_ATOM_WM_TRANSIENT_FOR, UINT32_MAX);
title_cookie = GET_PROPERTY(XCB_ATOM_WM_NAME, 128);
class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128);
role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128);
/* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */
DLOG("reparenting!\n");
@ -171,6 +173,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL));
window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL));
window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL));
window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true);
/* check if the window needs WM_TAKE_FOCUS */
cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS);

View File

@ -39,6 +39,7 @@ bool match_is_empty(Match *match) {
match->application == NULL &&
match->class == NULL &&
match->instance == NULL &&
match->role == NULL &&
match->id == XCB_NONE &&
match->con_id == NULL &&
match->dock == -1 &&
@ -65,6 +66,7 @@ void match_copy(Match *dest, Match *src) {
DUPLICATE_REGEX(application);
DUPLICATE_REGEX(class);
DUPLICATE_REGEX(instance);
DUPLICATE_REGEX(role);
}
/*
@ -113,6 +115,16 @@ bool match_matches_window(Match *match, i3Window *window) {
}
}
if (match->role != NULL) {
if (window->role != NULL &&
regex_matches(match->role, window->role)) {
LOG("window_role matches (%s)\n", window->role);
} else {
LOG("window_role does not match\n");
return false;
}
}
if (match->dock != -1) {
LOG("match->dock = %d, window->dock = %d\n", match->dock, window->dock);
if ((window->dock == W_DOCK_TOP && match->dock == M_DOCK_TOP) ||
@ -148,6 +160,7 @@ void match_free(Match *match) {
regex_free(match->class);
regex_free(match->instance);
regex_free(match->mark);
regex_free(match->role);
/* Second step: free the regex helper struct itself */
FREE(match->title);
@ -155,4 +168,5 @@ void match_free(Match *match) {
FREE(match->class);
FREE(match->instance);
FREE(match->mark);
FREE(match->role);
}

View File

@ -215,3 +215,36 @@ void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop)
free(prop);
}
/*
* Updates the WM_WINDOW_ROLE
*
*/
void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt) {
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
DLOG("prop == NULL\n");
FREE(prop);
return;
}
char *new_role;
if (asprintf(&new_role, "%.*s", xcb_get_property_value_length(prop),
(char*)xcb_get_property_value(prop)) == -1) {
perror("asprintf()");
DLOG("Could not get WM_WINDOW_ROLE\n");
free(prop);
return;
}
FREE(win->role);
win->role = new_role;
LOG("WM_WINDOW_ROLE changed to \"%s\"\n", win->role);
if (before_mgmt) {
free(prop);
return;
}
run_assignments(win);
free(prop);
}