Allow assign to output

Implements the "assign" part of issue #2764.
This commit is contained in:
Orestis Floros 2017-09-16 20:54:44 +03:00
parent 45d1e51857
commit a35854ddf4
7 changed files with 166 additions and 4 deletions

View File

@ -760,6 +760,10 @@ title change. As i3 will get the title as soon as the application maps the
window (mapping means actually displaying it on the screen), youd need to have window (mapping means actually displaying it on the screen), youd need to have
to match on 'Firefox' in this case. to match on 'Firefox' in this case.
You can also assign a window to show up on a specific output. You can use RandR
names such as +VGA1+ or names relative to the output with the currently focused
workspace such as +left+ and +down+.
Assignments are processed by i3 in the order in which they appear in the config Assignments are processed by i3 in the order in which they appear in the config
file. The first one which matches the window wins and later assignments are not file. The first one which matches the window wins and later assignments are not
considered. considered.
@ -767,6 +771,7 @@ considered.
*Syntax*: *Syntax*:
------------------------------------------------------------ ------------------------------------------------------------
assign <criteria> [→] [workspace] [number] <workspace> assign <criteria> [→] [workspace] [number] <workspace>
assign <criteria> [→] output left|right|up|down|primary|<output>
------------------------------------------------------------ ------------------------------------------------------------
*Examples*: *Examples*:
@ -791,9 +796,20 @@ assign [class="^URxvt$"] → number "2: work"
# Start urxvt -name irssi # Start urxvt -name irssi
assign [class="^URxvt$" instance="^irssi$"] → 3 assign [class="^URxvt$" instance="^irssi$"] → 3
# Assign urxvt to the output right of the current one
assign [class="^URxvt$"] → output right
# Assign urxvt to the primary output
assign [class="^URxvt$"] → output primary
---------------------- ----------------------
Note that the arrow is not required, it just looks good :-). If you decide to Note that you might not have a primary output configured yet. To do so, run:
-------------------------
xrandr --output <output> --primary
-------------------------
Also, 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 To get the class and instance, you can use +xprop+. After clicking on the

View File

@ -57,6 +57,7 @@ CFGFUN(force_display_urgency_hint, const long duration_ms);
CFGFUN(focus_on_window_activation, const char *mode); CFGFUN(focus_on_window_activation, const char *mode);
CFGFUN(show_marks, const char *value); CFGFUN(show_marks, const char *value);
CFGFUN(hide_edge_borders, const char *borders); CFGFUN(hide_edge_borders, const char *borders);
CFGFUN(assign_output, const char *output);
CFGFUN(assign, const char *workspace, bool is_number); CFGFUN(assign, const char *workspace, bool is_number);
CFGFUN(no_focus); CFGFUN(no_focus);
CFGFUN(ipc_socket, const char *path); CFGFUN(ipc_socket, const char *path);

View File

@ -557,7 +557,8 @@ struct Assignment {
A_COMMAND = (1 << 0), A_COMMAND = (1 << 0),
A_TO_WORKSPACE = (1 << 1), A_TO_WORKSPACE = (1 << 1),
A_NO_FOCUS = (1 << 2), A_NO_FOCUS = (1 << 2),
A_TO_WORKSPACE_NUMBER = (1 << 3) A_TO_WORKSPACE_NUMBER = (1 << 3),
A_TO_OUTPUT = (1 << 4)
} type; } type;
/** the criteria to check if a window matches */ /** the criteria to check if a window matches */
@ -567,6 +568,7 @@ struct Assignment {
union { union {
char *command; char *command;
char *workspace; char *workspace;
char *output;
} dest; } dest;
TAILQ_ENTRY(Assignment) TAILQ_ENTRY(Assignment)

View File

@ -141,7 +141,7 @@ state FOR_WINDOW_COMMAND:
command = string command = string
-> call cfg_for_window($command) -> call cfg_for_window($command)
# assign <criteria> [→] workspace # assign <criteria> [→] [workspace | output] <name>
state ASSIGN: state ASSIGN:
'[' '['
-> call cfg_criteria_init(ASSIGN_WORKSPACE); CRITERIA -> call cfg_criteria_init(ASSIGN_WORKSPACE); CRITERIA
@ -149,6 +149,8 @@ state ASSIGN:
state ASSIGN_WORKSPACE: state ASSIGN_WORKSPACE:
'→' '→'
-> ->
'output'
-> ASSIGN_OUTPUT
'workspace' 'workspace'
-> ->
'number' 'number'
@ -156,6 +158,10 @@ state ASSIGN_WORKSPACE:
workspace = string workspace = string
-> call cfg_assign($workspace, 0) -> call cfg_assign($workspace, 0)
state ASSIGN_OUTPUT:
output = string
-> call cfg_assign_output($output)
state ASSIGN_WORKSPACE_NUMBER: state ASSIGN_WORKSPACE_NUMBER:
number = string number = string
-> call cfg_assign($number, 1) -> call cfg_assign($number, 1)

View File

@ -377,6 +377,20 @@ CFGFUN(color, const char *colorclass, const char *border, const char *background
#undef APPLY_COLORS #undef APPLY_COLORS
} }
CFGFUN(assign_output, const char *output) {
if (match_is_empty(current_match)) {
ELOG("Match is empty, ignoring this assignment\n");
return;
}
DLOG("New assignment, using above criteria, to output \"%s\".\n", output);
Assignment *assignment = scalloc(1, sizeof(Assignment));
match_copy(&(assignment->match), current_match);
assignment->type = A_TO_OUTPUT;
assignment->dest.output = sstrdup(output);
TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
}
CFGFUN(assign, const char *workspace, bool is_number) { CFGFUN(assign, const char *workspace, bool is_number) {
if (match_is_empty(current_match)) { if (match_is_empty(current_match)) {
ELOG("Match is empty, ignoring this assignment\n"); ELOG("Match is empty, ignoring this assignment\n");

View File

@ -322,6 +322,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
} else } else
nc = tree_open_con(NULL, cwindow); nc = tree_open_con(NULL, cwindow);
} }
if ((assignment = assignment_for(cwindow, A_TO_OUTPUT))) {
con_move_to_output_name(nc, assignment->dest.output, true);
}
} else { } else {
/* M_BELOW inserts the new window as a child of the one which was /* M_BELOW inserts the new window as a child of the one which was
* matched (e.g. dock areas) */ * matched (e.g. dock areas) */

View File

@ -203,8 +203,127 @@ my $content = get_ws($tmp);
ok(@{$content->{nodes}} == 0, 'no tiling cons'); ok(@{$content->{nodes}} == 0, 'no tiling cons');
ok(@{$content->{floating_nodes}} == 1, 'one floating con'); ok(@{$content->{floating_nodes}} == 1, 'one floating con');
$window->destroy; kill_all_windows;
exit_gracefully($pid);
#####################################################################
# test assignments to named outputs
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
workspace ws-0 output fake-0
workspace ws-1 output fake-1
workspace ws-2 output fake-2
workspace ws-3 output fake-3
assign [class="special-0"] output fake-0
assign [class="special-1"] output fake-1
assign [class="special-2"] output fake-2
assign [class="special-3"] output fake-3
assign [class="special-4"] output invalid
EOT
$pid = launch_with_config($config);
sub open_in_output {
my ($num, $expected_count) = @_;
my $ws = "ws-$num";
my $class = "special-$num";
my $output = "fake-$num";
is_num_children($ws, $expected_count - 1,
"before: " . ($expected_count - 1) . " containers on output $output");
$window = open_special(wm_class => $class);
sync_with_i3;
is_num_children($ws, $expected_count,
"after: $expected_count containers on output $output");
}
cmd "workspace ws-0";
open_in_output(0, 1);
my $focused = $x->input_focus;
open_in_output(1, 1);
is($x->input_focus, $focused, 'focus remains on output fake-0');
open_in_output(2, 1);
is($x->input_focus, $focused, 'focus remains on output fake-0');
for my $i (1 .. 5){
open_in_output(3, $i);
is($x->input_focus, $focused, 'focus remains on output fake-0');
}
# Check invalid output
$tmp = fresh_workspace;
open_special(wm_class => "special-4");
sync_with_i3;
is_num_children($tmp, 1, 'window assigned to invalid output opened in current workspace');
open_special(wm_class => "special-3");
sync_with_i3;
is_num_children($tmp, 1, 'but window assigned to valid output did not');
kill_all_windows;
exit_gracefully($pid);
#####################################################################
# Test assignments to outputs with relative names
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
workspace left-top output fake-0
workspace right-top output fake-1
workspace right-bottom output fake-2
workspace left-bottom output fake-3
assign [class="current"] output current
assign [class="left"] output left
assign [class="right"] output right
assign [class="up"] output up
assign [class="down"] output down
EOT
$pid = launch_with_config($config);
cmd 'workspace left-top';
is_num_children('left-top', 0, 'no childreon on left-top');
for my $i (1 .. 5){
open_special(wm_class => 'current');
}
sync_with_i3;
is_num_children('left-top', 5, 'windows opened in current workspace');
is_num_children('right-top', 0, 'no children on right-top');
open_special(wm_class => 'right');
sync_with_i3;
is_num_children('right-top', 1, 'one child on right-top');
is_num_children('left-bottom', 0, 'no children on left-bottom');
open_special(wm_class => 'down');
sync_with_i3;
is_num_children('left-bottom', 1, 'one child on left-bottom');
cmd 'workspace right-bottom';
open_special(wm_class => 'up');
sync_with_i3;
is_num_children('right-top', 2, 'two children on right-top');
open_special(wm_class => 'left');
sync_with_i3;
is_num_children('left-bottom', 2, 'two children on left-bottom');
kill_all_windows;
exit_gracefully($pid); exit_gracefully($pid);
##################################################################### #####################################################################