]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'role-criterion' into next
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 18 Sep 2011 15:10:05 +0000 (16:10 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 18 Sep 2011 15:10:05 +0000 (16:10 +0100)
15 files changed:
docs/userguide
include/atoms.xmacro
include/data.h
include/window.h
src/cfgparse.l
src/cfgparse.y
src/cmdparse.l
src/cmdparse.y
src/handlers.c
src/manage.c
src/match.c
src/window.c
testcases/complete-run.pl
testcases/t/65-for_window.t
testcases/t/lib/i3test.pm

index 184848aa2526111f891f6a7a960b9724d9e09bb8..49a967c2ae7a6e8c2d196701ba11d9d1ac6dbf19 100644 (file)
@@ -754,6 +754,8 @@ class::
        Compares the window class (the second part of WM_CLASS)
 instance::
        Compares the window instance (the first part of WM_CLASS)
+window_role::
+       Compares the window role (WM_WINDOW_ROLE).
 id::
        Compares the X11 window ID, which you can get via +xwininfo+ for example.
 title::
@@ -764,9 +766,9 @@ con_id::
        Compares the i3-internal container ID, which you can get via the IPC
        interface. Handy for scripting.
 
-The criteria +class+, +instance+, +title+ 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+ and +mark+ are actually
+regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for
+information on how to use them.
 
 === Splitting containers
 
index 300a8abaea48ae0976b1feaacfd9730717e06742..7f83a7ee835346256b8a36afc94eb717b17dd83a 100644 (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)
index 1db7c4428a16bd22da48267bf72062ca6bab496b..f6052b9f81154ed98aece23cd086e1792dfb33ba 100644 (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,
index fe282aa08208f2bc8d9e55888b3b1575fb041852..cb9fbbc50c9072b359a3d5f2121dc565a88e6834 100644 (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
index e5dd29c0c972f5d946cacae28019b059aa4a349f..021e351633fcebf7725d6e31b987b693e3b8955a 100644 (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; }
index 57f943c8a26ad7d229843d57236701059cf1bed6..016953fd4c6f119f700a9dfab25cfacf0cdb43a1 100644 (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);
index c7c64e356f2b74978520a003b16c5faf5f39d823..968b7e52e512b22845e1ae99ef461e16d193726d 100644 (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; }
index 650a2eb883caf928b9cbf5694bdb0c82eb7a040f..0a99c224d5b64c6fe19187d6dd8e18e92b51daec 100644 (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);
index b346799033a3ffa75418a0079301b02bcde06178..36cf039fdc3bcb600ee7e682ae152f92471c4bfa 100644 (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 client’s 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) {
index 9ee3dd72a8add59d806dc5a8ede7e3819d40da41..35055d17ea04e7787efe40dde42e6cb6fdf3e751 100644 (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);
index 3514aceece2a95ca0a6d112575e6fb593c68a487..745989ba6ef378c5b59755f0eac8a01a6a8a39b5 100644 (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);
 }
index 3dd6645630757ed5566b1bdc9e763d9d4ed79c0e..4b8b6614a863ed507ef40c7cecb6037402cecaa2 100644 (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);
+}
index 6f00877cbf694e7b59582accbf2024db0e55f6bc..8f740d8caf2965cafb5bc4b535bb8f02ace36b71 100755 (executable)
@@ -242,7 +242,7 @@ sub take_job {
 
         my $output;
         my $parser = TAP::Parser->new({
-            exec => [ 'sh', '-c', "DISPLAY=$display /usr/bin/perl -It/lib $test" ],
+            exec => [ 'sh', '-c', qq|DISPLAY=$display LOGPATH="$logpath" /usr/bin/perl -It/lib $test| ],
             spool => IO::Scalar->new(\$output),
             merge => 1,
         });
index fb4c28123242141a70f79a41d9674614b7a0168f..1746d117459b9e7f60cca17801bc24ef996f8c37 100644 (file)
@@ -349,5 +349,108 @@ is($content[0]->{border}, 'normal', 'normal border');
 
 exit_gracefully($process->pid);
 
+##############################################################
+# 8: check that the role criterion works properly
+##############################################################
+
+# this configuration is broken because "asdf" is not a valid integer
+# the for_window should therefore recognize this error and don’t add the
+# assignment
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [window_role="i3test"] border none
+EOT
+
+$process = launch_with_config($config);
+
+$tmp = fresh_workspace;
+
+$window = $x->root->create_child(
+    class => WINDOW_CLASS_INPUT_OUTPUT,
+    rect => [ 0, 0, 30, 30 ],
+    background_color => '#00ff00',
+);
+
+$window->_create;
+
+my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
+my $atomtype = $x->atom(name => 'STRING');
+$x->change_property(
+  PROP_MODE_REPLACE,
+  $window->id,
+  $atomname->id,
+  $atomtype->id,
+  8,
+  length("i3test") + 1,
+  "i3test\x00"
+);
+
+$window->name('usethis');
+$window->map;
+sleep 0.25;
+
+@content = @{get_ws_content($tmp)};
+cmp_ok(@content, '==', 1, 'one node on this workspace now');
+is($content[0]->{border}, 'none', 'no border (window_role)');
+
+exit_gracefully($process->pid);
+
+##############################################################
+# 9: another test for the window_role, but this time it changes
+#    *after* the window has been mapped
+##############################################################
+
+# this configuration is broken because "asdf" is not a valid integer
+# the for_window should therefore recognize this error and don’t add the
+# assignment
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [window_role="i3test"] border none
+EOT
+
+$process = launch_with_config($config);
+
+$tmp = fresh_workspace;
+
+$window = $x->root->create_child(
+    class => WINDOW_CLASS_INPUT_OUTPUT,
+    rect => [ 0, 0, 30, 30 ],
+    background_color => '#00ff00',
+);
+
+$window->_create;
+
+$window->name('usethis');
+$window->map;
+sleep 0.25;
+
+@content = @{get_ws_content($tmp)};
+cmp_ok(@content, '==', 1, 'one node on this workspace now');
+is($content[0]->{border}, 'normal', 'normal border (window_role 2)');
+
+$atomname = $x->atom(name => 'WM_WINDOW_ROLE');
+$atomtype = $x->atom(name => 'STRING');
+$x->change_property(
+  PROP_MODE_REPLACE,
+  $window->id,
+  $atomname->id,
+  $atomtype->id,
+  8,
+  length("i3test") + 1,
+  "i3test\x00"
+);
+
+$x->flush;
+
+sleep 0.25;
+
+@content = @{get_ws_content($tmp)};
+cmp_ok(@content, '==', 1, 'one node on this workspace now');
+is($content[0]->{border}, 'none', 'no border (window_role 2)');
+
+exit_gracefully($process->pid);
+
 
 done_testing;
index 749b89b77d4df4290cb8aecdffd3a28b73999e6e..3d2d85afbc944823b08fac0b253dd9314687551c 100644 (file)
@@ -259,7 +259,10 @@ sub launch_with_config {
     say $fh "ipc-socket $tmp_socket_path";
     close($fh);
 
-    my $i3cmd = "exec " . abs_path("../i3") . " -V -d all --disable-signalhandler -c $tmpfile >/dev/null 2>/dev/null";
+    # Use $ENV{LOGPATH}, gets set in complete-run.pl. We append instead of
+    # overwrite because there might be multiple instances of i3 running during
+    # one test case.
+    my $i3cmd = "exec " . abs_path("../i3") . " -V -d all --disable-signalhandler -c $tmpfile >>$ENV{LOGPATH} 2>&1";
     my $process = Proc::Background->new($i3cmd);
     sleep 1;