]> git.sur5r.net Git - i3/i3/commitdiff
Allow assign to workspace by number 2887/head
authorOrestis Floros <orestisf1993@gmail.com>
Mon, 28 Aug 2017 02:14:03 +0000 (05:14 +0300)
committerOrestis Floros <orestisf1993@gmail.com>
Fri, 15 Sep 2017 00:38:13 +0000 (03:38 +0300)
Makes "assign [<criteria>] workspace number <number>" work in the same
manner as "move to workspace number <number>" instead of assigning the
window to a workspace named "number <number>".

config.spec is modified to expect a 'number' string and an extra
argument is used in cfg_assign.

For workspaces that don't exist yet, workspace_get is used as a
fallback. This also allows the user to assign to "<number> <workspace>"
eg "2: work" and the full name will be used if workspace number 2
doesn't exist yet.

Fixes #2590.

docs/userguide
include/config_directives.h
include/data.h
parser-specs/config.spec
src/config_directives.c
src/manage.c
testcases/t/166-assign.t
testcases/t/201-config-parser.t

index 0d5de3b9056c86eff0e483124825d27192588e6e..52165e2dffe3b46013af118e81f5e268bd8fe81f 100644 (file)
@@ -766,7 +766,7 @@ considered.
 
 *Syntax*:
 ------------------------------------------------------------
-assign <criteria> [→] [workspace] <workspace>
+assign <criteria> [→] [workspace] [number] <workspace>
 ------------------------------------------------------------
 
 *Examples*:
@@ -783,6 +783,12 @@ assign [class="^URxvt$"] → 2
 # 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
 ----------------------
index f35666f3612000c21c4837c592a5b22261bf6d33..b729e72880d3fd5509f4992ec0d680730f650baf 100644 (file)
@@ -57,7 +57,7 @@ CFGFUN(force_display_urgency_hint, const long duration_ms);
 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);
index 69a79ade09faabf22bca2ddbbebef2243a65c14c..54333e049e2b5789ad4ec922f784037cd08eb78e 100644 (file)
@@ -546,7 +546,8 @@ struct Assignment {
         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 */
index 4aa320bf63a1b822f8c66a3f1c2e8304058d7118..665b046aeeed0701151bc2feccdd430ca26454fc 100644 (file)
@@ -151,8 +151,14 @@ state ASSIGN_WORKSPACE:
       ->
   '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:
index 7ca6e102785a674daa3741e5f884fd68ebc958ab..376397e8abe877146b9c7cb85fdd563ea832a3c0 100644 (file)
@@ -377,15 +377,21 @@ CFGFUN(color, const char *colorclass, const char *border, const char *background
 #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);
 }
index 86a361c376a856a32d6a1d90c51178dbec163e12..e35965481a9cb046b971c1717a3f8fb963316607 100644 (file)
@@ -259,9 +259,26 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
         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)
index 355058dd01314feecd131b3d13e19a919c286368..68548831bd085519f820ba591611a86bec0eabb3 100644 (file)
@@ -33,6 +33,33 @@ sub open_special {
     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
 #####################################################################
@@ -87,33 +114,67 @@ $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.
+# 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);
 
index fb3130d5135e344bba0d4cc1045539268e1ba193..e8080a73480484bfbab7e75c4877ec2c0a4102db 100644 (file)
@@ -116,6 +116,7 @@ is(parser_calls($config),
 
 $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"
@@ -123,13 +124,15 @@ EOT
 
 $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),