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 |