Merge branch 'new-assign' into next
This commit is contained in:
commit
2ef54057e6
|
@ -431,7 +431,7 @@ change their border style, for example.
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
-----------------------------
|
-----------------------------
|
||||||
for_window [criteria] command
|
for_window <criteria> command
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
|
@ -478,37 +478,59 @@ configuration file and run it before starting i3 (for example in your
|
||||||
|
|
||||||
[[assign_workspace]]
|
[[assign_workspace]]
|
||||||
|
|
||||||
Specific windows can be matched by window class and/or window title. It is
|
To automatically make a specific window show up on a specific workspace, you
|
||||||
recommended that you match on window classes instead of window titles whenever
|
can use an *assignment*. You can match windows by using any criteria,
|
||||||
possible because some applications first create their window, and then worry
|
see <<command_criteria>>. It is recommended that you match on window classes
|
||||||
about setting the correct title. Firefox with Vimperator comes to mind. The
|
(and instances, when appropriate) instead of window titles whenever possible
|
||||||
window starts up being named Firefox, and only when Vimperator is loaded does
|
because some applications first create their window, and then worry about
|
||||||
the title change. As i3 will get the title as soon as the application maps the
|
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
|
window (mapping means actually displaying it on the screen), you’d need to have
|
||||||
to match on 'Firefox' in this case.
|
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*:
|
*Syntax*:
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
assign ["]window class[/window title]["] [→] [workspace]
|
assign <criteria> [→] workspace
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
----------------------
|
----------------------
|
||||||
assign urxvt 2
|
# Assign URxvt terminals to workspace 2
|
||||||
assign urxvt → 2
|
assign [class="URxvt"] 2
|
||||||
assign urxvt → work
|
|
||||||
assign "urxvt" → 2
|
# Same thing, but more precise (exact match instead of substring)
|
||||||
assign "urxvt/VIM" → 3
|
assign [class="^URxvt$"] 2
|
||||||
assign "gecko" → 4
|
|
||||||
|
# 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
|
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.
|
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
|
=== Automatically starting applications on i3 startup
|
||||||
|
|
||||||
By using the +exec+ keyword outside a keybinding, you can configure
|
By using the +exec+ keyword outside a keybinding, you can configure
|
||||||
|
@ -721,6 +743,9 @@ which have the class Firefox, use:
|
||||||
*Example*:
|
*Example*:
|
||||||
------------------------------------
|
------------------------------------
|
||||||
bindsym mod+x [class="Firefox"] kill
|
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:
|
The criteria which are currently implemented are:
|
||||||
|
|
|
@ -75,6 +75,14 @@ EOL (\r?\n)
|
||||||
|
|
||||||
|
|
||||||
<FOR_WINDOW_COND>"]" { yy_pop_state(); return ']'; }
|
<FOR_WINDOW_COND>"]" { yy_pop_state(); return ']'; }
|
||||||
|
<ASSIGN_COND>"[" {
|
||||||
|
/* 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 '[';
|
||||||
|
}
|
||||||
<EAT_WHITESPACE>[ \t]* { yy_pop_state(); }
|
<EAT_WHITESPACE>[ \t]* { yy_pop_state(); }
|
||||||
<WANT_QSTRING>\"[^\"]+\" {
|
<WANT_QSTRING>\"[^\"]+\" {
|
||||||
yy_pop_state();
|
yy_pop_state();
|
||||||
|
@ -194,7 +202,7 @@ title { yy_push_state(WANT_QSTRING); return TOK_TITLE;
|
||||||
yylval.string = copy;
|
yylval.string = copy;
|
||||||
return QUOTEDSTRING;
|
return QUOTEDSTRING;
|
||||||
}
|
}
|
||||||
<ASSIGN_COND>[^ \t\"]+ { BEGIN(ASSIGN_TARGET_COND); yylval.string = sstrdup(yytext); return STR_NG; }
|
<ASSIGN_COND>[^ \t\"\[]+ { BEGIN(ASSIGN_TARGET_COND); yylval.string = sstrdup(yytext); return STR_NG; }
|
||||||
<BINDSYM_COND>[a-zA-Z0-9_]+ { yylval.string = sstrdup(yytext); return WORD; }
|
<BINDSYM_COND>[a-zA-Z0-9_]+ { yylval.string = sstrdup(yytext); return WORD; }
|
||||||
[a-zA-Z]+ { yylval.string = sstrdup(yytext); return WORD; }
|
[a-zA-Z]+ { yylval.string = sstrdup(yytext); return WORD; }
|
||||||
. { return (int)yytext[0]; }
|
. { return (int)yytext[0]; }
|
||||||
|
|
|
@ -240,6 +240,18 @@ static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
|
||||||
configerror_pid = -1;
|
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
|
* 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
|
* 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 *child = smalloc(sizeof(ev_child));
|
||||||
ev_child_init(child, &nagbar_exited, configerror_pid, 0);
|
ev_child_init(child, &nagbar_exited, configerror_pid, 0);
|
||||||
ev_child_start(main_loop, child);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1058,8 +1076,13 @@ workspace_name:
|
||||||
assign:
|
assign:
|
||||||
TOKASSIGN window_class STR
|
TOKASSIGN window_class STR
|
||||||
{
|
{
|
||||||
/* TODO: the assign command also needs some kind of new syntax where we
|
/* This is the old, deprecated form of assignments. It’s provided for
|
||||||
* just use criteria. Then deprecate the old form */
|
* 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);
|
printf("assignment of %s to *%s*\n", $2, $3);
|
||||||
char *workspace = $3;
|
char *workspace = $3;
|
||||||
char *criteria = $2;
|
char *criteria = $2;
|
||||||
|
@ -1071,11 +1094,23 @@ assign:
|
||||||
char *separator = NULL;
|
char *separator = NULL;
|
||||||
if ((separator = strchr(criteria, '/')) != NULL) {
|
if ((separator = strchr(criteria, '/')) != NULL) {
|
||||||
*(separator++) = '\0';
|
*(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);
|
printf(" title = %s\n", separator);
|
||||||
}
|
}
|
||||||
if (*criteria != '\0') {
|
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);
|
printf(" class = %s\n", criteria);
|
||||||
}
|
}
|
||||||
free(criteria);
|
free(criteria);
|
||||||
|
@ -1107,6 +1142,15 @@ assign:
|
||||||
assignment->dest.workspace = workspace;
|
assignment->dest.workspace = workspace;
|
||||||
TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
|
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:
|
window_class:
|
||||||
|
|
12
src/main.c
12
src/main.c
|
@ -163,6 +163,14 @@ static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
|
||||||
DLOG("Done\n");
|
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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
//parse_cmd("[ foo ] attach, attach ; focus");
|
//parse_cmd("[ foo ] attach, attach ; focus");
|
||||||
int screens;
|
int screens;
|
||||||
|
@ -529,5 +537,9 @@ int main(int argc, char *argv[]) {
|
||||||
start_application(exec_always->command);
|
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);
|
ev_loop(main_loop, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,46 @@ exit_gracefully($process->pid);
|
||||||
|
|
||||||
sleep 0.25;
|
sleep 0.25;
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# make sure that assignments are case-insensitive in the old syntax.
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
$config = <<EOT;
|
||||||
|
# i3 config file (v4)
|
||||||
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
|
assign "special" → ~
|
||||||
|
EOT
|
||||||
|
|
||||||
|
$process = launch_with_config($config);
|
||||||
|
|
||||||
|
$tmp = fresh_workspace;
|
||||||
|
|
||||||
|
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
|
||||||
|
my $workspaces = get_workspace_names;
|
||||||
|
ok(!("targetws" ~~ @{$workspaces}), 'targetws does not exist yet');
|
||||||
|
|
||||||
|
my $window = $x->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
|
# regression test: dock clients with floating assignments should not crash
|
||||||
# (instead, nothing should happen - dock clients can’t float)
|
# (instead, nothing should happen - dock clients can’t float)
|
||||||
|
@ -200,7 +240,9 @@ $tmp = fresh_workspace;
|
||||||
|
|
||||||
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
|
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
|
||||||
my @docked = get_dock_clients;
|
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(
|
my $window = $x->root->create_child(
|
||||||
class => WINDOW_CLASS_INPUT_OUTPUT,
|
class => WINDOW_CLASS_INPUT_OUTPUT,
|
||||||
|
@ -219,7 +261,7 @@ my $content = get_ws($tmp);
|
||||||
ok(@{$content->{nodes}} == 0, 'no tiling cons');
|
ok(@{$content->{nodes}} == 0, 'no tiling cons');
|
||||||
ok(@{$content->{floating_nodes}} == 0, 'one floating con');
|
ok(@{$content->{floating_nodes}} == 0, 'one floating con');
|
||||||
@docked = get_dock_clients;
|
@docked = get_dock_clients;
|
||||||
is(@docked, 1, 'no dock clients yet');
|
is(@docked, 2, 'two dock clients now');
|
||||||
|
|
||||||
$window->destroy;
|
$window->destroy;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue