diff --git a/src/config.c b/src/config.c index 0c02ea1..8170d9c 100644 --- a/src/config.c +++ b/src/config.c @@ -372,7 +372,8 @@ static struct section_def section_defs[] = { MT_FIELD(label_font_size, "8 50% 100", parse_relative_font_size, noop), MT_FIELD( label_symbols, "abcdefghijklmnopqrstuvwxyz", parse_str, free_str - ) + ), + MT_FIELD(keys, "", parse_str, free_str) ), SECTION( mode_floating, @@ -386,7 +387,8 @@ static struct section_def section_defs[] = { MF_FIELD(label_font_size, "12 50% 100", parse_relative_font_size, noop), MF_FIELD( label_symbols, "abcdefghijklmnopqrstuvwxyz", parse_str, free_str - ) + ), + MF_FIELD(keys, "", parse_str, free_str) ), SECTION( mode_bisect, MB_FIELD(label_color, "#fffd", parse_color, noop), diff --git a/src/config.h b/src/config.h index 1b9c7d0..b69d622 100644 --- a/src/config.h +++ b/src/config.h @@ -26,6 +26,7 @@ struct mode_tile_config { char *label_font_family; struct relative_font_size label_font_size; char *label_symbols; + char *keys; }; enum floating_mode_source { @@ -43,6 +44,7 @@ struct mode_floating_config { char *label_font_family; struct relative_font_size label_font_size; char *label_symbols; + char *keys; }; struct mode_bisect_config { diff --git a/src/label.c b/src/label.c index 26dc9f3..47f5494 100644 --- a/src/label.c +++ b/src/label.c @@ -7,46 +7,52 @@ #include #include -label_symbols_t *label_symbols_from_str(char *s) { +// Measure the length and number of symbols in s. +// Output is written to len and num_symbols. +// Returns true for errors. +static bool measure_label_symbols(char *s, int *len, int *num_symbols) { char *c = s; uint32_t r; int c_len; - int len = sizeof(label_symbols_t); - int num_symbols = 0; - while ((c_len = str_to_rune(c, &r)) > 0) { // One byte for the indices and one of the end of string (`\0`). - c += c_len; - len += c_len + 2; - num_symbols += 1; + c += c_len; + *len += c_len + 2; + *num_symbols += 1; } if (c_len < 0) { LOG_ERR("Invalid UTF-8 input."); - return NULL; + return true; } - if (num_symbols < 2) { + if (*num_symbols < 2) { LOG_ERR( - "Not enough characters (%d). Must have at least 2.", num_symbols + "Not enough characters (%d). Must have at least 2.", *num_symbols ); - return NULL; + return true; } - if (num_symbols >= 255) { - LOG_ERR("Too many characters (%d).", num_symbols); - return NULL; + if (*num_symbols >= 255) { + LOG_ERR("Too many characters (%d).", *num_symbols); + return true; } - label_symbols_t *label_symbols = malloc(len); + return false; +} - label_symbols->num_symbols = num_symbols; - unsigned char *indices = (unsigned char *)label_symbols->data; - char *str = &label_symbols->data[num_symbols]; +// Given a label symbol string and number of symbols, populate the +// provided data array as label_symbols_t.data. +static void fill_label_symbols_data(char *s, int num_symbols, char *data) { + unsigned char *indices = (unsigned char *)data; + char *str = &data[num_symbols]; - c = s; + uint32_t r; + int c_len; + + char *c = s; int str_offset = 0; for (int i = 0; i < num_symbols; i++) { c_len = str_to_rune(c, &r); @@ -57,27 +63,92 @@ label_symbols_t *label_symbols_from_str(char *s) { str_offset += c_len + 1; c += c_len; } +} + +label_symbols_t *label_symbols_from_str(char *s) { + return label_symbols_from_strs(s, s); +} + +// Set up label symbols from source string. Return NULL for errors. +// If label_symbols is NULL, allocate one. Else, only fill its display_data. +static label_symbols_t *label_symbols_init( + char *s, label_symbols_t *label_symbols +) { + int num_symbols = 0; + int len = 0; + if (measure_label_symbols(s, &len, &num_symbols)) { + return NULL; + } + + char *data; + + if (label_symbols == NULL) { + label_symbols = malloc(len + sizeof(label_symbols_t)); + label_symbols->num_symbols = num_symbols; + label_symbols->keys = NULL; + data = label_symbols->symbols; + } else { + data = label_symbols->keys = malloc(len); + if (num_symbols != label_symbols->num_symbols) { + LOG_ERR("Label display symbols must be empty or same length as label symbols."); + return NULL; + } + } + + fill_label_symbols_data(s, num_symbols, data); + return label_symbols; +} + +label_symbols_t *label_symbols_from_strs(char *symbols, char *keys) { + label_symbols_t *label_symbols = label_symbols_init(symbols, NULL); + if (label_symbols == NULL) { + return NULL; + } + + if (symbols == keys || keys[0] == '\0' || strcmp(symbols, keys) == 0) { + // When possible, don't use a second array. + label_symbols->keys = label_symbols->symbols; + } else { + void *result = label_symbols_init(keys, label_symbols); + if (result == NULL) { + label_symbols_free(label_symbols); + return NULL; + } + } return label_symbols; } void label_symbols_free(label_symbols_t *ls) { + if (ls != NULL && ls->symbols != ls->keys) { + free(ls->keys); + } free(ls); } -char *label_symbols_idx_to_ptr(label_symbols_t *label_symbols, int idx) { +char *label_symbols_idx_to_key_ptr(label_symbols_t *label_symbols, int idx) { if (idx < 0 || idx >= label_symbols->num_symbols) { LOG_ERR("Label symbols index (%d) out of bound.", idx); return NULL; } - return ((char *)label_symbols->data) + label_symbols->num_symbols + - ((unsigned char *)label_symbols->data)[idx]; + return ((char *)label_symbols->keys) + label_symbols->num_symbols + + ((unsigned char *)label_symbols->keys)[idx]; } -int label_symbols_find_idx(label_symbols_t *label_symbols, char *s) { +char *label_symbols_idx_to_display_ptr(label_symbols_t *label_symbols, int idx) { + if (idx < 0 || idx >= label_symbols->num_symbols) { + LOG_ERR("Label symbols index (%d) out of bound.", idx); + return NULL; + } + + return label_symbols->symbols + label_symbols->num_symbols + + ((unsigned char *)label_symbols->symbols)[idx]; +} + +int label_symbols_find_key_idx(label_symbols_t *label_symbols, char *s) { for (int i = 0; i < label_symbols->num_symbols; i++) { - if (strcmp(label_symbols_idx_to_ptr(label_symbols, i), s) == 0) { + if (strcmp(label_symbols_idx_to_key_ptr(label_symbols, i), s) == 0) { return i; } } @@ -106,6 +177,7 @@ void label_selection_clear(label_selection_t *label_selection) { label_selection->next = 0; } +// Convert label selection to 1-dimensional index static int label_selection_to_partial_idx(label_selection_t *label_selection) { int idx = 0; int factor = 1; @@ -170,6 +242,7 @@ int label_selection_to_idx(label_selection_t *label_selection) { return label_selection_to_partial_idx(label_selection); } +// Fill a label selection's input array to correspond to the given index int label_selection_set_from_idx(label_selection_t *label_selection, int idx) { int num_symbols = label_selection->label_symbols->num_symbols; @@ -199,27 +272,24 @@ int label_selection_incr(label_selection_t *label_selection) { return 0; } +// Max string length for a single symbol in a label static int label_symbols_max_str_len(label_symbols_t *label_symbols) { - unsigned char *indices = (unsigned char *)label_symbols->data; - int i; + int num_symbols = label_symbols->num_symbols; + unsigned char *indices = (unsigned char *)label_symbols->keys; - int max_len = 0; int curr_len = 0; + int max_len = strlen( + label_symbols_idx_to_display_ptr(label_symbols, num_symbols - 1) + ); - for (i = 1; i < label_symbols->num_symbols; i++) { + for (int i = 1; i < num_symbols; i++) { + // Compute length of symbol at index i - 1 curr_len = indices[i] - indices[i - 1] - 1; if (curr_len > max_len) { max_len = curr_len; } } - curr_len = strlen( - label_symbols_idx_to_ptr(label_symbols, label_symbols->num_symbols - 1) - ); - if (curr_len > max_len) { - max_len = curr_len; - } - return max_len; } @@ -228,13 +298,20 @@ int label_selection_str_max_len(label_selection_t *label_selection) { label_selection->len; } -void label_selection_str(label_selection_t *label_selection, char *out) { - label_symbols_t *label_symbols = label_selection->label_symbols; - for (int i = 0; i < label_selection->next; i++) { - out = stpcpy( +static char *label_selection_stpcpy_idx( + char *out, label_selection_t *label_selection, int i +) { + return stpcpy( out, - label_symbols_idx_to_ptr(label_symbols, label_selection->input[i]) + label_symbols_idx_to_display_ptr( + label_selection->label_symbols, label_selection->input[i] + ) ); +} + +void label_selection_str(label_selection_t *label_selection, char *out) { + for (int i = 0; i < label_selection->next; i++) { + out = label_selection_stpcpy_idx(out, label_selection, i); } *out = '\0'; @@ -249,20 +326,13 @@ void label_selection_str_split( cut = label_selection->next; } - label_symbols_t *label_symbols = label_selection->label_symbols; for (int i = 0; i < cut; i++) { - prefix = stpcpy( - prefix, - label_symbols_idx_to_ptr(label_symbols, label_selection->input[i]) - ); + prefix = label_selection_stpcpy_idx(prefix, label_selection, i); } *prefix = '\0'; for (int i = cut; i < label_selection->next; i++) { - suffix = stpcpy( - suffix, - label_symbols_idx_to_ptr(label_symbols, label_selection->input[i]) - ); + suffix = label_selection_stpcpy_idx(suffix, label_selection, i); } *suffix = '\0'; } diff --git a/src/label.h b/src/label.h index 676eb29..c87e150 100644 --- a/src/label.h +++ b/src/label.h @@ -4,16 +4,19 @@ #include typedef struct { - /* data data[num_symbols] - * | | - * | 4 || 0 | 2 | 4 | 6 ||`a`| 0 |`b`| 0 |`c`| 0 |`d`| 0 | - * ^ ^-----------^ ^---------------------------^ - * | offsets strings - * | + /* symbols symbols[num_symbols] + * | | + * | 4 ||xxxx|| 0 | 2 | 4 | 6 ||`a`| 0 |`b`| 0 |`c`| 0 |`d`| 0 | + * ^ ^ ^-----------^ ^---------------------------^ + * | | offsets strings + * | pointer to key data * number of symbols + * + * The keys field's data is identical in structure to the symbols field. */ unsigned char num_symbols; - char data[]; + char *keys; + char symbols[]; } label_symbols_t; typedef struct { @@ -27,17 +30,22 @@ typedef struct { // Create a `label_symbols_t` from a string of characters. // Returns `NULL` upon error. label_symbols_t *label_symbols_from_str(char *s); +// Create a `label_symbols_t` from a string of label characters and a +// possibly-empty string of key characters. +// Returns `NULL` upon error. +label_symbols_t *label_symbols_from_strs(char *symbols, char *keys); + // Free memory of a `label_symbols_t`. void label_symbols_free(label_symbols_t *ls); -// Get pointer to string of the symbol at given index. +// Get pointer to string of the key at given index. // Returns value <0 upon error. -char *label_symbols_idx_to_ptr(label_symbols_t *label_symbols, int idx); +char *label_symbols_idx_to_key_ptr(label_symbols_t *label_symbols, int idx); -// Find symbol index from given string. +// Find key index from given string. // Returns value <0 upon error. -int label_symbols_find_idx(label_symbols_t *label_symbols, char *s); +int label_symbols_find_key_idx(label_symbols_t *label_symbols, char *s); // Create a `label_selection_t`. label_selection_t * diff --git a/src/mode_floating.c b/src/mode_floating.c index 4d927d0..2ceb1d2 100644 --- a/src/mode_floating.c +++ b/src/mode_floating.c @@ -77,8 +77,10 @@ static void get_area_from_screenshot( void *floating_mode_enter(struct state *state, struct rect area) { struct floating_mode_state *ms = malloc(sizeof(*ms)); - ms->label_symbols = - label_symbols_from_str(state->config.mode_floating.label_symbols); + ms->label_symbols = label_symbols_from_strs( + state->config.mode_floating.label_symbols, + state->config.mode_floating.keys + ); if (ms->label_symbols == NULL) { ms->areas = NULL; @@ -131,7 +133,7 @@ static bool floating_mode_key( state->running = false; break; default:; - int symbol_idx = label_symbols_find_idx(ms->label_symbols, text); + int symbol_idx = label_symbols_find_key_idx(ms->label_symbols, text); if (symbol_idx < 0) { return false; } diff --git a/src/mode_tile.c b/src/mode_tile.c index 2e3af52..7c4fbd8 100644 --- a/src/mode_tile.c +++ b/src/mode_tile.c @@ -38,8 +38,10 @@ void *tile_mode_enter(struct state *state, struct rect area) { ms->sub_area_width_off = ms->area.w % ms->sub_area_columns; ms->sub_area_width = ms->area.w / ms->sub_area_columns; - ms->label_symbols = - label_symbols_from_str(state->config.mode_tile.label_symbols); + ms->label_symbols = label_symbols_from_strs( + state->config.mode_tile.label_symbols, + state->config.mode_tile.keys + ); if (ms->label_symbols == NULL) { ms->label_selection = NULL; state->running = false; @@ -102,7 +104,7 @@ static bool tile_mode_key( state->running = false; break; default:; - int symbol_idx = label_symbols_find_idx(ms->label_symbols, text); + int symbol_idx = label_symbols_find_key_idx(ms->label_symbols, text); if (symbol_idx < 0) { return false; } diff --git a/src/test_label.c b/src/test_label.c index 401b1e5..687e0f3 100644 --- a/src/test_label.c +++ b/src/test_label.c @@ -5,8 +5,8 @@ int main() { label_symbols_t *label_symbols = label_symbols_from_str("abcdé"); - if (!label_symbols) { - LOG_ERR("`label_symbolss_from_str` should not have returned null."); + if (label_symbols == NULL) { + LOG_ERR("`label_symbols_from_str` should not have returned null."); return 1; } @@ -15,7 +15,7 @@ int main() { return 2; } - char *s = label_symbols_idx_to_ptr(label_symbols, 0); + char *s = label_symbols_idx_to_key_ptr(label_symbols, 0); if (strcmp(s, "a")) { LOG_ERR("No match"); LOG_ERR("Given string: '%s'", s); @@ -27,7 +27,7 @@ int main() { }; for (int i = 0; i < sizeof(symbols) / sizeof(symbols[0]); i++) { - int symbol_idx = label_symbols_find_idx(label_symbols, symbols[i]); + int symbol_idx = label_symbols_find_key_idx(label_symbols, symbols[i]); if (symbol_idx != i) { LOG_ERR( "Wrong index %d (expected %d) for symbol '%s'", symbol_idx, i, @@ -126,7 +126,7 @@ int main() { for (int i = 0; i < 9; i++) { int symbol_idx = - label_symbols_find_idx(alt_label_symbols, alt_symbols[i]); + label_symbols_find_key_idx(alt_label_symbols, alt_symbols[i]); if (symbol_idx != i) { LOG_ERR( "Wrong index %d (expected %d) for symbol '%s'", symbol_idx, i, @@ -136,6 +136,36 @@ int main() { } } + // These have the same number of code points and different byte lenths. + label_symbols_t *display = label_symbols_from_strs("ABCDÉ", "abcde"); + label_symbols_t *display2 = label_symbols_from_strs("ABCDE", "abcdé"); + if (display == NULL || display2 == NULL) { + LOG_ERR("`label_symbols_from_strs` should not have returned null."); + return 15; + } + label_symbols_free(display2); + + LOG_WARN("A 'Label display symbols must be empty...' error is expected below."); + label_symbols_t *display3 = label_symbols_from_strs("abcd", "abcde"); + if (display3 != NULL) { + LOG_ERR("`label_symbols_from_strs` should return null for different-length strings."); + return 16; + } + label_symbols_free(display3); + + // Make sure the unicode measured in the second string + label_selection_t *display_selection = + label_selection_new(display, 100); + int display_selection_buf_size = + label_selection_str_max_len(display_selection) + 1; + if (display_selection_buf_size != 7) { + LOG_ERR( + "Wrong label_selection_str_buffer_size = %d", + display_selection_buf_size + ); + return 17; + } + label_selection_free(label_selection); label_symbols_free(label_symbols); return 0;