i3 - improved tiling WM


i3bar: implement custom workspace numbers config

Patch status: needinfo

Patch by Tony Crisci

Long description:

Implement the configuration option within the bar config directive to
disable workspace numbers with the directive `workspace_numbers no`.

This directive strips the workspace name of the number prefix and
delimiter. For example:

* "2:5" -> "5"
* "4:$" -> "$"
* "8" -> " "

This allows customization of i3bar for alternate ordering of workspaces
which has a legitimate use for alternate keyboard layouts such as
Dvorak.

fixes #1131

To apply this patch, use:
curl http://cr.i3wm.org/patch/509/raw.patch | git am

b/i3bar/include/config.h

36
@@ -27,6 +27,7 @@ typedef struct config_t {
37
     struct xcb_color_strings_t colors;
38
     bool         disable_binding_mode_indicator;
39
     bool         disable_ws;
40
+    bool         disable_ws_numbers;
41
     char         *bar_id;
42
     char         *command;
43
     char         *fontname;

b/i3bar/include/workspaces.h

48
@@ -31,7 +31,8 @@ void free_workspaces(void);
49
 
50
 struct i3_ws {
51
     int                num;         /* The internal number of the ws */
52
-    i3String           *name;       /* The name of the ws */
53
+    char          *canonical_name;  /* The true name of the ws according to the ipc */
54
+    i3String           *name;       /* The name of the ws that is displayed on the bar */
55
     int                name_width;  /* The rendered width of the name */
56
     bool               visible;     /* If the ws is currently visible on an output */
57
     bool               focused;     /* If the ws is currently focused */

b/i3bar/src/config.c

62
@@ -201,6 +201,12 @@ static int config_boolean_cb(void *params_, int val) {
63
         return 1;
64
     }
65
 
66
+    if (!strcmp(cur_key, "workspace_numbers")) {
67
+        DLOG("workspace_numbers = %d\n", val);
68
+        config.disable_ws_numbers = !val;
69
+        return 1;
70
+    }
71
+
72
     if (!strcmp(cur_key, "verbose")) {
73
         DLOG("verbose = %d\n", val);
74
         config.verbose = val;

b/i3bar/src/workspaces.c

79
@@ -103,24 +103,49 @@ static int workspaces_integer_cb(void *params_, long val) {
80
  * Parse a string (name, output)
81
  *
82
  */
83
-#if YAJL_MAJOR >= 2
84
 static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) {
85
-#else
86
-static int workspaces_string_cb(void *params_, const unsigned char *val, unsigned int len) {
87
-#endif
88
         struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
89
 
90
         char *output_name;
91
 
92
         if (!strcmp(params->cur_key, "name")) {
93
-            /* Save the name */
94
-            params->workspaces_walk->name = i3string_from_utf8_with_length((const char *)val, len);
95
+            const char *ws_name = (const char*)val;
96
+            params->workspaces_walk->canonical_name = strndup(ws_name, len);
97
+
98
+            if (config.disable_ws_numbers) {
99
+                /* Special case: strip off the workspace number */
100
+                char *ws_num = malloc(sizeof(char) * 10);
101
+                sprintf(ws_num, "%d", atoi(ws_name));
102
+
103
+                /* Calculate the length of the number str in the name */
104
+                int offset = strspn(ws_name, ws_num);
105
+
106
+                /* Also strip off one of the conventional ws name delimiters */
107
+                if (offset
108
+                        && (ws_name[offset] == ':'
109
+                            || ws_name[offset] == ' '
110
+                            || ws_name[offset] == '|')) {
111
+                        offset += 1;
112
+                    }
113
+
114
+                /* Offset may be equal to length, in which case just display a
115
+                 * space */
116
+                params->workspaces_walk->name = (offset < len
117
+                        ? i3string_from_utf8_with_length(ws_name + offset, len - offset)
118
+                        : i3string_from_utf8(" "));
119
+
120
+                FREE(ws_num);
121
+            } else {
122
+                /* Default case: just save the name */
123
+                params->workspaces_walk->name = i3string_from_utf8_with_length(ws_name, len);
124
+            }
125
 
126
             /* Save its rendered width */
127
             params->workspaces_walk->name_width =
128
                 predict_text_width(params->workspaces_walk->name);
129
 
130
-            DLOG("Got Workspace %s, name_width: %d, glyphs: %zu\n",
131
+            DLOG("Got Workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
132
+                 params->workspaces_walk->canonical_name,
133
                  i3string_as_utf8(params->workspaces_walk->name),
134
                  params->workspaces_walk->name_width,
135
                  i3string_get_num_glyphs(params->workspaces_walk->name));
136
@@ -267,6 +292,7 @@ void free_workspaces(void) {
137
         if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
138
             TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
139
                 I3STRING_FREE(ws_walk->name);
140
+                FREE(ws_walk->canonical_name);
141
             }
142
             FREE_TAILQ(outputs_walk->workspaces, i3_ws);
143
         }

b/i3bar/src/xcb.c

148
@@ -406,7 +406,7 @@ void handle_button(xcb_button_press_event_t *event) {
149
      * buffer, then we copy character by character. */
150
     int num_quotes = 0;
151
     size_t namelen = 0;
152
-    const char *utf8_name = i3string_as_utf8(cur_ws->name);
153
+    const char *utf8_name = cur_ws->canonical_name;
154
     for (const char *walk = utf8_name; *walk != '\0'; walk++) {
155
         if (*walk == '"')
156
             num_quotes++;

b/include/config.h

161
@@ -267,6 +267,11 @@ struct Barconfig {
162
      * zero. */
163
     bool hide_workspace_buttons;
164
 
165
+    /** Hide workspace numbers? Configuration option is 'workspace_numbers no'
166
+     * but we invert the bool. This is for supporting custom keyboard layouts.
167
+     * */
168
+    bool hide_workspace_numbers;
169
+
170
     /** Hide mode button? Configuration option is 'binding_mode_indicator no'
171
      * but we invert the bool for the same reason as hide_workspace_buttons.*/
172
     bool hide_binding_mode_indicator;

b/parser-specs/config.spec

177
@@ -358,6 +358,7 @@ state BAR:
178
   'font'                   -> BAR_FONT
179
   'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
180
   'workspace_buttons'      -> BAR_WORKSPACE_BUTTONS
181
+  'workspace_numbers'      -> BAR_WORKSPACE_NUMBERS
182
   'verbose'                -> BAR_VERBOSE
183
   'colors'                 -> BAR_COLORS_BRACE
184
   '}'
185
@@ -420,6 +421,10 @@ state BAR_WORKSPACE_BUTTONS:
186
   value = word
187
       -> call cfg_bar_workspace_buttons($value); BAR
188
 
189
+state BAR_WORKSPACE_NUMBERS:
190
+  value = word
191
+      -> call cfg_bar_workspace_numbers($value); BAR
192
+
193
 state BAR_VERBOSE:
194
   value = word
195
       -> call cfg_bar_verbose($value); BAR

b/src/config_directives.c

200
@@ -517,6 +517,10 @@ CFGFUN(bar_workspace_buttons, const char *value) {
201
     current_bar.hide_workspace_buttons = !eval_boolstr(value);
202
 }
203
 
204
+CFGFUN(bar_workspace_numbers, const char *value) {
205
+    current_bar.hide_workspace_numbers = !eval_boolstr(value);
206
+}
207
+
208
 CFGFUN(bar_finish) {
209
     DLOG("\t new bar configuration finished, saving.\n");
210
     /* Generate a unique ID for this bar if not already configured */

b/src/ipc.c

215
@@ -747,6 +747,9 @@ IPC_HANDLER(get_bar_config) {
216
         ystr("workspace_buttons");
217
         y(bool, !config->hide_workspace_buttons);
218
 
219
+        ystr("workspace_numbers");
220
+        y(bool, !config->hide_workspace_numbers);
221
+
222
         ystr("binding_mode_indicator");
223
         y(bool, !config->hide_binding_mode_indicator);
224