*Syntax*:
------------------------------------------------------------
-assign <criteria> [→] [workspace] <workspace>
+assign <criteria> [→] [workspace] [number] <workspace>
------------------------------------------------------------
*Examples*:
# Assignment to a named workspace
assign [class="^URxvt$"] → work
+# Assign to the workspace with number 2, regardless of name
+assign [class="^URxvt$"] → number 2
+
+# You can also specify a number + name. If the workspace with number 2 exists, assign will skip the text part.
+assign [class="^URxvt$"] → number "2: work"
+
# Start urxvt -name irssi
assign [class="^URxvt$" instance="^irssi$"] → 3
----------------------
CFGFUN(focus_on_window_activation, const char *mode);
CFGFUN(show_marks, const char *value);
CFGFUN(hide_edge_borders, const char *borders);
-CFGFUN(assign, const char *workspace);
+CFGFUN(assign, const char *workspace, bool is_number);
CFGFUN(no_focus);
CFGFUN(ipc_socket, const char *path);
CFGFUN(restart_state, const char *path);
A_ANY = 0,
A_COMMAND = (1 << 0),
A_TO_WORKSPACE = (1 << 1),
- A_NO_FOCUS = (1 << 2)
+ A_NO_FOCUS = (1 << 2),
+ A_TO_WORKSPACE_NUMBER = (1 << 3)
} type;
/** the criteria to check if a window matches */
->
'workspace'
->
+ 'number'
+ -> ASSIGN_WORKSPACE_NUMBER
workspace = string
- -> call cfg_assign($workspace)
+ -> call cfg_assign($workspace, 0)
+
+state ASSIGN_WORKSPACE_NUMBER:
+ number = string
+ -> call cfg_assign($number, 1)
# no_focus <criteria>
state NO_FOCUS:
#undef APPLY_COLORS
}
-CFGFUN(assign, const char *workspace) {
+CFGFUN(assign, const char *workspace, bool is_number) {
if (match_is_empty(current_match)) {
ELOG("Match is empty, ignoring this assignment\n");
return;
}
+
+ if (is_number && ws_name_to_number(workspace) == -1) {
+ ELOG("Could not parse initial part of \"%s\" as a number.\n", workspace);
+ return;
+ }
+
DLOG("New assignment, using above criteria, to workspace \"%s\".\n", workspace);
Assignment *assignment = scalloc(1, sizeof(Assignment));
match_copy(&(assignment->match), current_match);
- assignment->type = A_TO_WORKSPACE;
+ assignment->type = is_number ? A_TO_WORKSPACE_NUMBER : A_TO_WORKSPACE;
assignment->dest.workspace = sstrdup(workspace);
TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
}
Con *wm_desktop_ws = NULL;
/* If not, check if it is assigned to a specific workspace */
- if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) {
+ if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE)) ||
+ (assignment = assignment_for(cwindow, A_TO_WORKSPACE_NUMBER))) {
DLOG("Assignment matches (%p)\n", match);
- Con *assigned_ws = workspace_get(assignment->dest.workspace, NULL);
+
+ Con *assigned_ws = NULL;
+ if (assignment->type == A_TO_WORKSPACE_NUMBER) {
+ Con *output = NULL;
+ long parsed_num = ws_name_to_number(assignment->dest.workspace);
+
+ /* This will only work for workspaces that already exist. */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ GREP_FIRST(assigned_ws, output_get_content(output), child->num == parsed_num);
+ }
+ }
+ /* A_TO_WORKSPACE type assignment or fallback from A_TO_WORKSPACE_NUMBER
+ * when the target workspace number does not exist yet. */
+ if (!assigned_ws) {
+ assigned_ws = workspace_get(assignment->dest.workspace, NULL);
+ }
+
nc = con_descend_tiling_focused(assigned_ws);
DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name);
if (nc->type == CT_WORKSPACE)
return $window;
}
+sub test_workspace_assignment {
+ my $target_ws = "@_";
+
+ # initialize the target workspace, then go to a fresh one
+ ok(!($target_ws ~~ @{get_workspace_names()}), "$target_ws does not exist yet");
+ cmd "workspace $target_ws";
+ cmp_ok(@{get_ws_content($target_ws)}, '==', 0, "no containers on $target_ws yet");
+ cmd 'open';
+ cmp_ok(@{get_ws_content($target_ws)}, '==', 1, "one container on $target_ws");
+ my $tmp = fresh_workspace;
+
+ ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+ ok($target_ws ~~ @{get_workspace_names()}, "$target_ws does not exist yet");
+
+ # We use sync_with_i3 instead of wait_for_map here because i3 will not actually
+ # map the window -- it will be assigned to a different workspace and will only
+ # be mapped once you switch to that workspace
+ my $window = open_special(dont_map => 1);
+ $window->map;
+ sync_with_i3;
+
+ ok(@{get_ws_content($tmp)} == 0, 'still no containers');
+ ok(@{get_ws_content($target_ws)} == 2, "two containers on $target_ws");
+
+ return $window
+}
+
#####################################################################
# start a window and see that it does not get assigned with an empty config
#####################################################################
exit_gracefully($pid);
#####################################################################
-# start a window and see that it gets assigned to a workspace which has content
-# already, next to the existing node.
+# start a window and see that it gets assigned to a formerly unused
+# numbered workspace
#####################################################################
-$pid = launch_with_config($config);
+my $config_numbered = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+assign [class="special"] → workspace number 2
+EOT
+
+$pid = launch_with_config($config_numbered);
-# initialize the target workspace, then go to a fresh one
-ok(!("targetws" ~~ @{get_workspace_names()}), 'targetws does not exist yet');
-cmd 'workspace targetws';
-cmp_ok(@{get_ws_content('targetws')}, '==', 0, 'no containers on targetws yet');
-cmd 'open';
-cmp_ok(@{get_ws_content('targetws')}, '==', 1, 'one container on targetws');
$tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-ok("targetws" ~~ @{get_workspace_names()}, 'targetws does not exist yet');
-
+$workspaces = get_workspace_names;
+ok(!("2" ~~ @{$workspaces}), 'workspace number 2 does not exist yet');
-# We use sync_with_i3 instead of wait_for_map here because i3 will not actually
-# map the window -- it will be assigned to a different workspace and will only
-# be mapped once you switch to that workspace
-$window = open_special(dont_map => 1);
-$window->map;
+$window = open_special;
sync_with_i3;
ok(@{get_ws_content($tmp)} == 0, 'still no containers');
-ok(@{get_ws_content('targetws')} == 2, 'two containers on targetws');
+ok("2" ~~ @{get_workspace_names()}, 'workspace number 2 exists');
+
+$window->destroy;
+
+exit_gracefully($pid);
+
+#####################################################################
+# start a window and see that it gets assigned to a numbered
+# workspace which has content already, next to the existing node.
+#####################################################################
+
+$pid = launch_with_config($config_numbered);
+
+$window = test_workspace_assignment("2");
+$window->destroy;
+
+exit_gracefully($pid);
+
+#####################################################################
+# start a window and see that it gets assigned to a numbered workspace with
+# a name which has content already, next to the existing node.
+#####################################################################
+
+$pid = launch_with_config($config_numbered);
+
+cmd 'workspace 2'; # Make sure that we are not testing for "2" again.
+$window = test_workspace_assignment("2: targetws");
+$window->destroy;
+
+exit_gracefully($pid);
+
+#####################################################################
+# start a window and see that it gets assigned to a workspace which
+# has content already, next to the existing node.
+#####################################################################
+
+$pid = launch_with_config($config);
+
+test_workspace_assignment("targetws");
exit_gracefully($pid);
$config = <<'EOT';
assign [class="^Chrome"] 4
+assign [class="^Chrome"] workspace number 3
assign [class="^Chrome"] named workspace
assign [class="^Chrome"] "quoted named workspace"
assign [class="^Chrome"] → "quoted named workspace"
$expected = <<'EOT';
cfg_criteria_add(class, ^Chrome)
-cfg_assign(4)
+cfg_assign(4, 0)
cfg_criteria_add(class, ^Chrome)
-cfg_assign(named workspace)
+cfg_assign(3, 1)
cfg_criteria_add(class, ^Chrome)
-cfg_assign(quoted named workspace)
+cfg_assign(named workspace, 0)
cfg_criteria_add(class, ^Chrome)
-cfg_assign(quoted named workspace)
+cfg_assign(quoted named workspace, 0)
+cfg_criteria_add(class, ^Chrome)
+cfg_assign(quoted named workspace, 0)
EOT
is(parser_calls($config),