From b3e1fb1f3b04290bceaa3ab7915b7d1119be93ac Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 11 Sep 2011 21:16:45 +0100 Subject: [PATCH 1/7] Make the old 'assign' case-insensitive again (+test) (Thanks aksr) --- src/cfgparse.y | 16 ++++++++++++++-- testcases/t/66-assign.t | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/cfgparse.y b/src/cfgparse.y index 868640e0..7efecc92 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -1071,11 +1071,23 @@ assign: char *separator = NULL; if ((separator = strchr(criteria, '/')) != NULL) { *(separator++) = '\0'; - match->title = regex_new(separator); + char *pattern; + if (asprintf(&pattern, "(?i)%s", separator) == -1) { + ELOG("asprintf failed\n"); + break; + } + match->title = regex_new(pattern); + free(pattern); printf(" title = %s\n", separator); } if (*criteria != '\0') { - match->class = regex_new(criteria); + char *pattern; + if (asprintf(&pattern, "(?i)%s", criteria) == -1) { + ELOG("asprintf failed\n"); + break; + } + match->class = regex_new(pattern); + free(pattern); printf(" class = %s\n", criteria); } free(criteria); diff --git a/testcases/t/66-assign.t b/testcases/t/66-assign.t index 776710e7..25855b58 100644 --- a/testcases/t/66-assign.t +++ b/testcases/t/66-assign.t @@ -182,6 +182,46 @@ exit_gracefully($process->pid); sleep 0.25; +##################################################################### +# make sure that assignments are case-insensitive in the old syntax. +##################################################################### + +$config = <root->create_child( + class => WINDOW_CLASS_INPUT_OUTPUT, + rect => [ 0, 0, 30, 30 ], + background_color => '#0000ff', +); + +$window->_create; +set_wm_class($window->id, 'SPEcial', 'SPEcial'); +$window->name('special window'); +$window->map; +sleep 0.25; + +my $content = get_ws($tmp); +ok(@{$content->{nodes}} == 0, 'no tiling cons'); +ok(@{$content->{floating_nodes}} == 1, 'one floating con'); + +$window->destroy; + +exit_gracefully($process->pid); + +sleep 0.25; + ##################################################################### # regression test: dock clients with floating assignments should not crash # (instead, nothing should happen - dock clients can’t float) From 9b671bda83f62adeb69dcbfdd1c9feea7477e06e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 11 Sep 2011 21:17:13 +0100 Subject: [PATCH 2/7] docs/userguide: provide an example of case-insensitive matching with PCRE (Thanks stfn) --- docs/userguide | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/userguide b/docs/userguide index 597cdb51..0ee56567 100644 --- a/docs/userguide +++ b/docs/userguide @@ -721,6 +721,9 @@ which have the class Firefox, use: *Example*: ------------------------------------ bindsym mod+x [class="Firefox"] kill + +# same thing, but case-insensitive +bindsym mod+x [class="(?i)firefox"] kill ------------------------------------ The criteria which are currently implemented are: From d03dffe012ca1807b396270a4b145bfb0ae6d63a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 11 Sep 2011 21:49:35 +0100 Subject: [PATCH 3/7] Kill left-over i3-nagbar processes on 'exit' --- src/cfgparse.y | 18 ++++++++++++++++++ src/main.c | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/cfgparse.y b/src/cfgparse.y index 7efecc92..ca2d0710 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -240,6 +240,18 @@ static void nagbar_exited(EV_P_ ev_child *watcher, int revents) { configerror_pid = -1; } +/* + * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal + * SIGKILL (9) to make sure there are no left-over i3-nagbar processes. + * + */ +static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) { + if (configerror_pid != -1) { + LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", configerror_pid); + kill(configerror_pid, SIGKILL); + } +} + /* * Starts an i3-nagbar process which alerts the user that his configuration * file contains one or more errors. Also offers two buttons: One to launch an @@ -283,6 +295,12 @@ static void start_configerror_nagbar(const char *config_path) { ev_child *child = smalloc(sizeof(ev_child)); ev_child_init(child, &nagbar_exited, configerror_pid, 0); ev_child_start(main_loop, child); + + /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is + * still running) */ + ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup)); + ev_cleanup_init(cleanup, nagbar_cleanup); + ev_cleanup_start(main_loop, cleanup); } /* diff --git a/src/main.c b/src/main.c index aee95f75..ea02bb6e 100644 --- a/src/main.c +++ b/src/main.c @@ -163,6 +163,14 @@ static void xkb_got_event(EV_P_ struct ev_io *w, int revents) { DLOG("Done\n"); } +/* + * Exit handler which destroys the main_loop. Will trigger cleanup handlers. + * + */ +static void i3_exit() { + ev_loop_destroy(main_loop); +} + int main(int argc, char *argv[]) { //parse_cmd("[ foo ] attach, attach ; focus"); int screens; @@ -529,5 +537,9 @@ int main(int argc, char *argv[]) { start_application(exec_always->command); } + /* Make sure to destroy the event loop to invoke the cleeanup callbacks + * when calling exit() */ + atexit(i3_exit); + ev_loop(main_loop, 0); } From e47e1008193f1f65016a61e3beeb02adc2de0773 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 11 Sep 2011 21:54:13 +0100 Subject: [PATCH 4/7] Introduce a new syntax for the 'assign' command: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using a quoted string to specify the class / title, the assign command now uses criteria, just like the for_window command or the command scopes. An example comes here: # Assign all Chromium windows (including popups) to workspace 1: www assign [class="^Chromium$"] → 1: www # Make the main browser window borderless for_window [class="^Chromium$" title=" - Chromium$"] border none This gives you more control over the matching process due to various reasons: 1) Criteria work case-sensitive by default. Use the (?i) option if you want a case-insensitive match, like this: assign [class="(?i)^ChroMIUM$"] → 1 2) class and instance of WM_CLASS can now be matched separately. For example, when starting urxvt -name irssi, xprop will report this: WM_CLASS(STRING) = "irssi", "URxvt" The first part of this is the instance ("irssi"), the second part is the class ("URxvt"). An appropriate assignment looks like this: assign [class="^URxvt$" instance="irssi"] → 2 3) You can now freely use a forward slash (/) in all strings since that is no longer used to separate class from title (in-band signaling is bad, mhkay?). --- src/cfgparse.l | 10 +++++++++- src/cfgparse.y | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/cfgparse.l b/src/cfgparse.l index e29f6efc..12840f26 100644 --- a/src/cfgparse.l +++ b/src/cfgparse.l @@ -75,6 +75,14 @@ EOL (\r?\n) "]" { yy_pop_state(); return ']'; } +"[" { + /* this is the case for the new assign syntax + * that uses criteria */ + yy_pop_state(); + yy_push_state(FOR_WINDOW_COND); + /* afterwards we will be in ASSIGN_TARGET_COND */ + return '['; + } [ \t]* { yy_pop_state(); } \"[^\"]+\" { yy_pop_state(); @@ -194,7 +202,7 @@ title { yy_push_state(WANT_QSTRING); return TOK_TITLE; yylval.string = copy; return QUOTEDSTRING; } -[^ \t\"]+ { BEGIN(ASSIGN_TARGET_COND); yylval.string = sstrdup(yytext); return STR_NG; } +[^ \t\"\[]+ { BEGIN(ASSIGN_TARGET_COND); yylval.string = sstrdup(yytext); return STR_NG; } [a-zA-Z0-9_]+ { yylval.string = sstrdup(yytext); return WORD; } [a-zA-Z]+ { yylval.string = sstrdup(yytext); return WORD; } . { return (int)yytext[0]; } diff --git a/src/cfgparse.y b/src/cfgparse.y index ca2d0710..53d23815 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -1137,6 +1137,15 @@ assign: assignment->dest.workspace = workspace; TAILQ_INSERT_TAIL(&assignments, assignment, assignments); } + | TOKASSIGN match STR + { + printf("new assignment, using above criteria, to workspace %s\n", $3); + Assignment *assignment = scalloc(sizeof(Assignment)); + assignment->match = current_match; + assignment->type = A_TO_WORKSPACE; + assignment->dest.workspace = $3; + TAILQ_INSERT_TAIL(&assignments, assignment, assignments); + } ; window_class: From d82698efa7ffa11739fc524dc36e84c357691940 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 11 Sep 2011 22:01:36 +0100 Subject: [PATCH 5/7] =?UTF-8?q?make=20the=20old=20assign=20syntax=20trigge?= =?UTF-8?q?r=20an=20i3-nagbar=20warning=20(it=E2=80=99s=20deprecated),=20a?= =?UTF-8?q?djust=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cfgparse.y | 9 +++++++-- testcases/t/66-assign.t | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/cfgparse.y b/src/cfgparse.y index 53d23815..572fe6d2 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -1076,8 +1076,13 @@ workspace_name: assign: TOKASSIGN window_class STR { - /* TODO: the assign command also needs some kind of new syntax where we - * just use criteria. Then deprecate the old form */ + /* This is the old, deprecated form of assignments. It’s provided for + * compatibility in version (4.1, 4.2, 4.3) and will be removed + * afterwards. It triggers an i3-nagbar warning starting from 4.1. */ + ELOG("You are using the old assign syntax (without criteria). " + "Please see the User's Guide for the new syntax and fix " + "your config file.\n"); + context->has_errors = true; printf("assignment of %s to *%s*\n", $2, $3); char *workspace = $3; char *criteria = $2; diff --git a/testcases/t/66-assign.t b/testcases/t/66-assign.t index 25855b58..b8366917 100644 --- a/testcases/t/66-assign.t +++ b/testcases/t/66-assign.t @@ -240,7 +240,9 @@ $tmp = fresh_workspace; ok(@{get_ws_content($tmp)} == 0, 'no containers yet'); my @docked = get_dock_clients; -is(@docked, 0, 'no dock clients yet'); +# We expect i3-nagbar as the first dock client due to using the old assign +# syntax +is(@docked, 1, 'one dock client yet'); my $window = $x->root->create_child( class => WINDOW_CLASS_INPUT_OUTPUT, @@ -259,7 +261,7 @@ my $content = get_ws($tmp); ok(@{$content->{nodes}} == 0, 'no tiling cons'); ok(@{$content->{floating_nodes}} == 0, 'one floating con'); @docked = get_dock_clients; -is(@docked, 1, 'no dock clients yet'); +is(@docked, 2, 'two dock clients now'); $window->destroy; From 332476cf0e797f22d179b6463b6da006e31f8e09 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 11 Sep 2011 22:15:05 +0100 Subject: [PATCH 6/7] docs/userguide: document the new assign syntax --- docs/userguide | 56 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/docs/userguide b/docs/userguide index 0ee56567..e38f0d12 100644 --- a/docs/userguide +++ b/docs/userguide @@ -478,37 +478,59 @@ configuration file and run it before starting i3 (for example in your [[assign_workspace]] -Specific windows can be matched by window class and/or window title. It is -recommended that you match on window classes instead of window titles whenever -possible because some applications first create their window, and then worry -about setting the correct title. Firefox with Vimperator comes to mind. The -window starts up being named Firefox, and only when Vimperator is loaded does -the title change. As i3 will get the title as soon as the application maps the +To automatically make a specific window show up on a specific workspace, you +can use an *assignment*. You can match windows by using any criteria, +see <>. It is recommended that you match on window classes +(and instances, when appropriate) instead of window titles whenever possible +because some applications first create their window, and then worry about +setting the correct title. Firefox with Vimperator comes to mind. The window +starts up being named Firefox, and only when Vimperator is loaded does the +title change. As i3 will get the title as soon as the application maps the window (mapping means actually displaying it on the screen), you’d need to have to match on 'Firefox' in this case. -You can prefix or suffix workspaces with a `~` to specify that matching clients -should be put into floating mode. If you specify only a `~`, the client will -not be put onto any workspace, but will be set floating on the current one. - *Syntax*: ------------------------------------------------------------ -assign ["]window class[/window title]["] [→] [workspace] +assign [→] workspace ------------------------------------------------------------ *Examples*: ---------------------- -assign urxvt 2 -assign urxvt → 2 -assign urxvt → work -assign "urxvt" → 2 -assign "urxvt/VIM" → 3 -assign "gecko" → 4 +# Assign URxvt terminals to workspace 2 +assign [class="URxvt"] 2 + +# Same thing, but more precise (exact match instead of substring) +assign [class="^URxvt$"] 2 + +# Same thing, but with a beautiful arrow :) +assign [class="^URxvt$"] → 2 + +# Assignment to a named workspace +assign [class="^URxvt$"] → work + +# Start urxvt -name irssi +assign [class="^URxvt$" instance="^irssi$"] → 3 ---------------------- Note that the arrow is not required, it just looks good :-). If you decide to use it, it has to be a UTF-8 encoded arrow, not `->` or something like that. +To get the class and instance, you can use +xprop+. After clicking on the +window, you will see the following output: + +*xwininfo*: +----------------------------------- +WM_CLASS(STRING) = "irssi", "URxvt" +----------------------------------- + +The first part of the WM_CLASS is the instance ("irssi" in this example), the +second part is the class ("URxvt" in this example). + +Should you have any problems with assignments, make sure to check the i3 +logfile first (see http://i3wm.org/docs/debugging.html). It includes more +details about the matching process and the window’s actual class, instance and +title when starting up. + === Automatically starting applications on i3 startup By using the +exec+ keyword outside a keybinding, you can configure From 24dbddc395c1b970b287bcc0add6c8983ef91289 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 11 Sep 2011 22:15:14 +0100 Subject: [PATCH 7/7] docs/userguide: fix the for_window syntax --- docs/userguide | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide b/docs/userguide index e38f0d12..184848aa 100644 --- a/docs/userguide +++ b/docs/userguide @@ -431,7 +431,7 @@ change their border style, for example. *Syntax*: ----------------------------- -for_window [criteria] command +for_window command ----------------------------- *Examples*: