i3bar: implement custom workspace numbers config
Patch status: needinfo
Patch by Tony Crisci
Long description:
Implement the configuration option within the bar config directive for custom workspace numbers with the directive `workspace_numbers custom`. This directive strips the workspace name of the number prefix and delimiter. When the workspace name consists only of the number, it will default to show the number. For example: * "2:5" -> "5" * "4:$" -> "$" * "8" -> "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/533/raw.patch | git am
b/docs/userguide
41 |
@@ -1191,6 +1191,31 @@ bar { |
42 |
} |
43 |
------------------------ |
44 |
|
45 |
+=== Workspace numbers |
46 |
+ |
47 |
+Specifies how workspace numbers should be displayed within the workspace |
48 |
+buttons. This is useful if you want to have a named workspace that stays in |
49 |
+order on the bar according to its number without displaying the number prefix. |
50 |
+ |
51 |
+When +workspace_numbers+ is set to +custom+, any workspace that has a name of |
52 |
+the form "[n]:[NAME]" will display only the name. You could use this, for |
53 |
+instance, to display Roman numerals rather than digits by naming your |
54 |
+workspaces to "1:I", "2:II", "3:III", "4:IV", ... |
55 |
+ |
56 |
+The default is to display the full name within the workspace button. |
57 |
+ |
58 |
+*Syntax*: |
59 |
+---------------------------------- |
60 |
+workspace_numbers <default|custom> |
61 |
+---------------------------------- |
62 |
+ |
63 |
+*Example*: |
64 |
+---------------------------- |
65 |
+bar { |
66 |
+ workspace_numbers custom |
67 |
+} |
68 |
+---------------------------- |
69 |
+ |
70 |
=== Binding Mode indicator |
71 |
|
72 |
Specifies whether the current binding mode indicator should be shown or not. |
b/i3bar/include/config.h
77 |
@@ -27,6 +27,7 @@ typedef struct config_t { |
78 |
struct xcb_color_strings_t colors; |
79 |
bool disable_binding_mode_indicator; |
80 |
bool disable_ws; |
81 |
+ char *ws_numbers; |
82 |
char *bar_id; |
83 |
char *command; |
84 |
char *fontname; |
b/i3bar/include/workspaces.h
89 |
@@ -31,7 +31,8 @@ void free_workspaces(void); |
90 |
|
91 |
struct i3_ws { |
92 |
int num; /* The internal number of the ws */ |
93 |
- i3String *name; /* The name of the ws */ |
94 |
+ char *canonical_name; /* The true name of the ws according to the ipc */ |
95 |
+ i3String *name; /* The name of the ws that is displayed on the bar */ |
96 |
int name_width; /* The rendered width of the name */ |
97 |
bool visible; /* If the ws is currently visible on an output */ |
98 |
bool focused; /* If the ws is currently focused */ |
b/i3bar/src/config.c
103 |
@@ -154,6 +154,14 @@ static int config_string_cb(void *params_, const unsigned char *val, unsigned in |
104 |
return 1; |
105 |
} |
106 |
|
107 |
+ if (!strcmp(cur_key, "workspace_numbers")) { |
108 |
+ DLOG("workspace_numbers = %.*s\n",len, val); |
109 |
+ FREE(config.ws_numbers); |
110 |
+ sasprintf(&config.ws_numbers, "%.*s", len, val); |
111 |
+ return 1; |
112 |
+ } |
113 |
+ |
114 |
+ |
115 |
#define COLOR(json_name, struct_name) \ |
116 |
do { \ |
117 |
if (!strcmp(cur_key, #json_name)) { \ |
b/i3bar/src/workspaces.c
122 |
@@ -113,14 +113,38 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne |
123 |
char *output_name; |
124 |
|
125 |
if (!strcmp(params->cur_key, "name")) { |
126 |
- /* Save the name */ |
127 |
- params->workspaces_walk->name = i3string_from_utf8_with_length((const char *)val, len); |
128 |
+ const char *ws_name = (const char*)val; |
129 |
+ params->workspaces_walk->canonical_name = strndup(ws_name, len); |
130 |
+ |
131 |
+ if (config.ws_numbers && strcmp(config.ws_numbers, "custom") == 0) { |
132 |
+ /* Special case: strip off the workspace number */ |
133 |
+ static char ws_num[10]; |
134 |
+ |
135 |
+ snprintf(ws_num, sizeof(ws_num), "%d", atoi(ws_name)); |
136 |
+ |
137 |
+ /* Calculate the length of the number str in the name */ |
138 |
+ int offset = strspn(ws_name, ws_num); |
139 |
+ |
140 |
+ /* Also strip off the conventional ws name delimiter */ |
141 |
+ if (offset && ws_name[offset] == ':') |
142 |
+ offset += 1; |
143 |
+ |
144 |
+ /* Offset may be equal to length, in which case display the number */ |
145 |
+ params->workspaces_walk->name = (offset < len |
146 |
+ ? i3string_from_utf8_with_length(ws_name + offset, len - offset) |
147 |
+ : i3string_from_utf8(ws_num)); |
148 |
+ |
149 |
+ } else { |
150 |
+ /* Default case: just save the name */ |
151 |
+ params->workspaces_walk->name = i3string_from_utf8_with_length(ws_name, len); |
152 |
+ } |
153 |
|
154 |
/* Save its rendered width */ |
155 |
params->workspaces_walk->name_width = |
156 |
predict_text_width(params->workspaces_walk->name); |
157 |
|
158 |
- DLOG("Got Workspace %s, name_width: %d, glyphs: %zu\n", |
159 |
+ DLOG("Got Workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n", |
160 |
+ params->workspaces_walk->canonical_name, |
161 |
i3string_as_utf8(params->workspaces_walk->name), |
162 |
params->workspaces_walk->name_width, |
163 |
i3string_get_num_glyphs(params->workspaces_walk->name)); |
164 |
@@ -267,6 +291,7 @@ void free_workspaces(void) { |
165 |
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) { |
166 |
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { |
167 |
I3STRING_FREE(ws_walk->name); |
168 |
+ FREE(ws_walk->canonical_name); |
169 |
} |
170 |
FREE_TAILQ(outputs_walk->workspaces, i3_ws); |
171 |
} |
b/i3bar/src/xcb.c
176 |
@@ -418,7 +418,7 @@ void handle_button(xcb_button_press_event_t *event) { |
177 |
* buffer, then we copy character by character. */ |
178 |
int num_quotes = 0; |
179 |
size_t namelen = 0; |
180 |
- const char *utf8_name = i3string_as_utf8(cur_ws->name); |
181 |
+ const char *utf8_name = cur_ws->canonical_name; |
182 |
for (const char *walk = utf8_name; *walk != '\0'; walk++) { |
183 |
if (*walk == '"') |
184 |
num_quotes++; |
b/include/config.h
189 |
@@ -267,6 +267,10 @@ struct Barconfig { |
190 |
* zero. */ |
191 |
bool hide_workspace_buttons; |
192 |
|
193 |
+ /** Custom workspace numbers? Configuration option is 'workspace_numbers |
194 |
+ * custom'. */ |
195 |
+ char *workspace_numbers; |
196 |
+ |
197 |
/** Hide mode button? Configuration option is 'binding_mode_indicator no' |
198 |
* but we invert the bool for the same reason as hide_workspace_buttons.*/ |
199 |
bool hide_binding_mode_indicator; |
b/parser-specs/config.spec
204 |
@@ -358,6 +358,7 @@ state BAR: |
205 |
'font' -> BAR_FONT |
206 |
'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR |
207 |
'workspace_buttons' -> BAR_WORKSPACE_BUTTONS |
208 |
+ 'workspace_numbers' -> BAR_WORKSPACE_NUMBERS |
209 |
'verbose' -> BAR_VERBOSE |
210 |
'colors' -> BAR_COLORS_BRACE |
211 |
'}' |
212 |
@@ -420,6 +421,10 @@ state BAR_WORKSPACE_BUTTONS: |
213 |
value = word |
214 |
-> call cfg_bar_workspace_buttons($value); BAR |
215 |
|
216 |
+state BAR_WORKSPACE_NUMBERS: |
217 |
+ value = word |
218 |
+ -> call cfg_bar_workspace_numbers($value); BAR |
219 |
+ |
220 |
state BAR_VERBOSE: |
221 |
value = word |
222 |
-> call cfg_bar_verbose($value); BAR |
b/src/config_directives.c
227 |
@@ -517,6 +517,11 @@ CFGFUN(bar_workspace_buttons, const char *value) { |
228 |
current_bar.hide_workspace_buttons = !eval_boolstr(value); |
229 |
} |
230 |
|
231 |
+CFGFUN(bar_workspace_numbers, const char *value) { |
232 |
+ FREE(current_bar.workspace_numbers); |
233 |
+ current_bar.workspace_numbers = sstrdup(value); |
234 |
+} |
235 |
+ |
236 |
CFGFUN(bar_finish) { |
237 |
DLOG("\t new bar configuration finished, saving.\n"); |
238 |
/* Generate a unique ID for this bar if not already configured */ |
b/src/ipc.c
243 |
@@ -514,6 +514,8 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { |
244 |
ystr("workspace_buttons"); |
245 |
y(bool, !config->hide_workspace_buttons); |
246 |
|
247 |
+ YSTR_IF_SET(workspace_numbers); |
248 |
+ |
249 |
ystr("binding_mode_indicator"); |
250 |
y(bool, !config->hide_binding_mode_indicator); |
251 |
|
b/testcases/t/201-config-parser.t
256 |
@@ -627,7 +627,7 @@ EOT |
257 |
|
258 |
$expected = <<'EOT'; |
259 |
cfg_bar_output(LVDS-1) |
260 |
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'verbose', 'colors', '}' |
261 |
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'workspace_numbers', 'verbose', 'colors', '}' |
262 |
ERROR: CONFIG: (in file <stdin>) |
263 |
ERROR: CONFIG: Line 1: bar { |
264 |
ERROR: CONFIG: Line 2: output LVDS-1 |