From 9a000393fba27847476952464e64619ae1a1b95f Mon Sep 17 00:00:00 2001
From: Daniel Mueller <deso@posteo.net>
Date: Sat, 11 Nov 2017 21:00:08 -0800
Subject: [PATCH 06/14] Rework handling of multi monitor setups

Handling of multi monitor setups in i3 is quite different from that in
awesome and other window managers. In i3 the idea is that different
workspaces can reside on different monitors ("outputs" in i3 jargon).
Although that may work in scenarios where not many named workspaces are
in use, it is not a feasible approach when solely using numbered
workspaces in a dynamically created fashion.
To address this issue, this change reworks the logic slightly. Instead
of numbering workspaces continuously over monitors we introduce
boundaries: each monitor gets workspaces that start with the number one.
The remaining logic stays the same at a high level but a couple of
adjustments have to be made internally to honor outputs when looking up
workspaces.
---
 include/workspace.h |  2 ++
 src/commands.c      | 10 +++++--
 src/workspace.c     | 64 +++++++++++++++++++++++++++++++--------------
 3 files changed, 55 insertions(+), 21 deletions(-)

diff --git a/include/workspace.h b/include/workspace.h
index 2b29ab..39496c 100644
--- a/include/workspace.h
+++ b/include/workspace.h
@@ -73,6 +73,8 @@ bool output_triggers_assignment(Output *output, struct Workspace_Assignment *ass
  */
 Con *workspace_get(const char *num);
 
+Con *workspace_get_on_output(Con *output, const char *num);
+
 /**
  * Extracts workspace names from keybindings (e.g. “web” from “bindsym $mod+1
  * workspace web”), so that when an output needs a workspace, i3 can start with
diff --git a/src/commands.c b/src/commands.c
index f90534..3ffe5d 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -344,6 +344,9 @@ void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_
     CHECK_MOVE_CON_TO_WORKSPACE;
 
     LOG("should move window to workspace %s\n", which);
+    /* get the workspace */
+    Con *out, *ws = NULL;
+    Con *output = con_get_output(focused);
 
     const long parsed_num = ws_name_to_number(which);
     if (parsed_num == -1) {
@@ -352,9 +355,12 @@ void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_
         return;
     }
 
-    Con *ws = get_existing_workspace_by_num(parsed_num);
+    TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+    GREP_FIRST(ws, output_get_content(out),
+               child->num == parsed_num && con_get_output(child) == output);
+
     if (!ws) {
-        ws = workspace_get(which);
+        ws = workspace_get_on_output(output, which);
     }
 
     if (no_auto_back_and_forth == NULL) {
diff --git a/src/workspace.c b/src/workspace.c
index 25ab17..465cf0 100644
--- a/src/workspace.c
+++ b/src/workspace.c
@@ -127,22 +127,33 @@ bool output_triggers_assignment(Output *output, struct Workspace_Assignment *ass
  * creating the workspace if necessary (by allocating the necessary amount of
  * memory and initializing the data structures correctly).
  *
+ * If output is non-NULL, the workspace lookup is restricted to that output.
+ * When creating a new workspace, the output parameter takes precedence over
+ * assignment lookup (unless an explicit assignment exists).
+ *
  */
-Con *workspace_get(const char *num) {
-    Con *workspace = get_existing_workspace_by_name(num);
+Con *workspace_get_on_output(Con *output, const char *num) {
+    Con *out, *workspace = NULL;
+
+    TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
+    GREP_FIRST(workspace, output_get_content(out), !strcasecmp(child->name, num) &&
+                                                   (output == NULL || con_get_output(child) == output));
+
     if (workspace) {
         return workspace;
     }
 
-    LOG("Creating new workspace \"%s\"\n", num);
+    LOG(“Creating new workspace \”%s\”\n”, num);
 
     /* We set workspace->num to the number if this workspace’s name begins with
-     * a positive number. Otherwise it’s a named ws and num will be 1. */
-    const int parsed_num = ws_name_to_number(num);
+     * a positive number. Otherwise it’s a named ws and num will be -1. */
+    const long parsed_num = ws_name_to_number(num);
 
-    Con *output = get_assigned_output(num, parsed_num);
+    Con *assigned = get_assigned_output(num, parsed_num);
     /* if an assignment is not found, we create this workspace on the current output */
-    if (!output) {
+    if (assigned) {
+        output = assigned;
+    } else if (!output) {
         output = con_get_output(focused);
     }
 
@@ -151,7 +162,7 @@ Con *workspace_get(const char *num) {
     workspace = con_new(NULL, NULL);
 
     char *name;
-    sasprintf(&name, "[i3 con] workspace %s", num);
+    sasprintf(&name, “[i3 con] workspace %s”, num);
     x_set_name(workspace, name);
     free(name);
 
@@ -165,12 +176,16 @@ Con *workspace_get(const char *num) {
     con_attach(workspace, output_get_content(output), false);
     _workspace_apply_default_orientation(workspace);
 
-    ipc_send_workspace_event("init", workspace, NULL);
+    ipc_send_workspace_event(“init”, workspace, NULL);
     ewmh_update_desktop_properties();
 
     return workspace;
 }
 
+Con *workspace_get(const char *num) {
+    return workspace_get_on_output(NULL, num);
+}
+
 /*
  * Extracts workspace names from keybindings (e.g. “web” from “bindsym $mod+1
  * workspace web”), so that when an output needs a workspace, i3 can start with
@@ -250,6 +265,7 @@ void extract_workspace_names_from_bindings(void) {
 Con *create_workspace_on_output(Output *output, Con *content) {
     /* add a workspace to this output */
     bool exists = true;
+    Con *out, *current;
     Con *ws = con_new(NULL, NULL);
     ws->type = CT_WORKSPACE;
 
@@ -264,16 +280,17 @@ Con *create_workspace_on_output(Output *output, Con *content) {
             continue;
         }
 
-        const int num = ws_name_to_number(target_name);
-        exists = (num == -1)
-                     ? get_existing_workspace_by_name(target_name)
-                     : get_existing_workspace_by_num(num);
+        current = NULL;
+        TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+        GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, target_name) &&
+                                                     get_output_for_con(child) == output);
+        exists = (current != NULL);
         if (!exists) {
             ws->name = sstrdup(target_name);
             /* Set ->num to the number of the workspace, if the name actually
              * is a number or starts with a number */
-            ws->num = num;
-            DLOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
+            ws->num = ws_name_to_number(ws->name);
+            LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
 
             break;
         }
@@ -285,8 +302,16 @@ Con *create_workspace_on_output(Output *output, Con *content) {
         int c = 0;
         while (exists) {
             c++;
+
+            ws->num = c;
+
             Con *assigned = get_assigned_output(NULL, c);
-            exists = (get_existing_workspace_by_num(c) || (assigned && assigned != output->con));
+            current = NULL;
+            TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+            GREP_FIRST(current, output_get_content(out), child->num == ws->num &&
+                                                         get_output_for_con(child) == output);
+            exists = (current != NULL) || (assigned && assigned != output->con);
+
             DLOG("result for ws %d: exists = %d\n", c, exists);
         }
         ws->num = c;
@@ -741,7 +766,7 @@ Con *workspace_next_on_output(void) {
             /* Ups, that will make boom somewhere. */
             return NULL;
         }
-        next = workspace_get(num_str);
+        next = workspace_get_on_output(output, num_str);
     }
 
     /* Find next named workspace. */
@@ -1120,8 +1145,9 @@ void workspace_move_to_output(Con *ws, Output *output) {
 }
 
 Con *workspace_select(const char *num) {
+    Con *output = con_get_output(focused);
     bool created = (get_existing_workspace_by_name(num) == NULL);
-    Con *workspace = workspace_get(num);
+    Con *workspace = workspace_get_on_output(output, num);
 
     /* If the workspace already exists we simply use it. */
     if (!created) {
@@ -1150,7 +1176,7 @@ Con *workspace_select(const char *num) {
          * the expected low frequency of those creations we should be fine.
          */
         created = (get_existing_workspace_by_name(num_str) == NULL);
-        (void)workspace_get(num_str);
+        (void)workspace_get_on_output(output, num_str);
     }
     return workspace;
 }
-- 
2.52.0

