diff options
-rw-r--r-- | meson.build | 4 | ||||
-rw-r--r-- | src/config.c | 34 | ||||
-rw-r--r-- | src/desktop_vec.c | 2 | ||||
-rw-r--r-- | src/entry.c | 1 | ||||
-rw-r--r-- | src/entry.h | 15 | ||||
-rw-r--r-- | src/entry_backend/harfbuzz.c | 20 | ||||
-rw-r--r-- | src/entry_backend/pango.c | 20 | ||||
-rw-r--r-- | src/fuzzy_match.c | 12 | ||||
-rw-r--r-- | src/input.c | 64 | ||||
-rw-r--r-- | src/main.c | 6 | ||||
-rw-r--r-- | src/string_vec.c | 2 | ||||
-rw-r--r-- | src/unicode.c (renamed from src/utf8.c) | 39 | ||||
-rw-r--r-- | src/unicode.h | 27 | ||||
-rw-r--r-- | src/utf8.h | 23 | ||||
-rw-r--r-- | test/utf8.c | 1 |
15 files changed, 135 insertions, 135 deletions
diff --git a/meson.build b/meson.build index 1d518d3..f56add3 100644 --- a/meson.build +++ b/meson.build @@ -108,7 +108,7 @@ common_sources = files( 'src/shm.c', 'src/string_vec.c', 'src/surface.c', - 'src/utf8.c', + 'src/unicode.c', 'src/wlr-layer-shell-unstable-v1.c', 'src/xmalloc.c', ) @@ -120,7 +120,7 @@ compgen_sources = files( 'src/log.c', 'src/mkdirp.c', 'src/string_vec.c', - 'src/utf8.c', + 'src/unicode.c', 'src/xmalloc.c' ) diff --git a/src/config.c b/src/config.c index 4305c3e..1fca428 100644 --- a/src/config.c +++ b/src/config.c @@ -10,6 +10,7 @@ #include "config.h" #include "log.h" #include "nelem.h" +#include "unicode.h" #include "xmalloc.h" /* Maximum number of config file errors before we give up */ @@ -77,7 +78,7 @@ static uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent, static uint32_t parse_anchor(const char *filename, size_t lineno, const char *str, bool *err); static bool parse_bool(const char *filename, size_t lineno, const char *str, bool *err); -static wchar_t parse_wchar(const char *filename, size_t lineno, const char *str, bool *err); +static uint32_t parse_char(const char *filename, size_t lineno, const char *str, bool *err); static struct color parse_color(const char *filename, size_t lineno, const char *str, bool *err); static uint32_t parse_uint32(const char *filename, size_t lineno, const char *str, bool *err); static int32_t parse_int32(const char *filename, size_t lineno, const char *str, bool *err); @@ -414,18 +415,11 @@ bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const tofi->window.entry.hide_input = parse_bool(filename, lineno, value, &err); } else if (strcasecmp(option, "hidden-character") == 0) { /* Unicode handling is ugly. */ - wchar_t wc = parse_wchar(filename, lineno, value, &err); - size_t buf_len = N_ELEM(tofi->window.entry.hidden_character_mb) + 1; - char *buf = xmalloc(buf_len); - size_t char_len = snprintf(buf, buf_len, "%lc", wc); - if (char_len >= buf_len) { - PARSE_ERROR(filename, lineno, "Character \"%lc\" is too large.\n", value); - err = true; - } else { - memcpy(tofi->window.entry.hidden_character_mb, buf, char_len); - tofi->window.entry.hidden_character_mb_length = char_len; + uint32_t ch = parse_char(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.hidden_character_utf8_length = + utf32_to_utf8(ch, tofi->window.entry.hidden_character_utf8); } - free(buf); } else if (strcasecmp(option, "drun-launch") == 0) { tofi->drun_launch = parse_bool(filename, lineno, value, &err); } else if (strcasecmp(option, "drun-print-exec") == 0) { @@ -611,20 +605,14 @@ bool parse_bool(const char *filename, size_t lineno, const char *str, bool *err) return false; } -wchar_t parse_wchar(const char *filename, size_t lineno, const char *str, bool *err) +uint32_t parse_char(const char *filename, size_t lineno, const char *str, bool *err) { - size_t len = strlen(str); - wchar_t ch; - size_t ret = mbrtowc(&ch, str, len, NULL); - if (ret == (size_t)-2) { - return 0; - } else if (ret != len) { + char *tmp = utf8_compose(str); + uint32_t ch = utf8_to_utf32(tmp); + if (*utf8_next_char(tmp) != '\0') { PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as a character.\n", str); - if (err) { - *err = true; - } - return 0; } + free(tmp); return ch; } diff --git a/src/desktop_vec.c b/src/desktop_vec.c index f559551..102dd67 100644 --- a/src/desktop_vec.c +++ b/src/desktop_vec.c @@ -4,7 +4,7 @@ #include "fuzzy_match.h" #include "log.h" #include "string_vec.h" -#include "utf8.h" +#include "unicode.h" #include "xmalloc.h" static bool match_current_desktop(char * const *desktop_list, gsize length); diff --git a/src/entry.c b/src/entry.c index e308a3c..3d0c284 100644 --- a/src/entry.c +++ b/src/entry.c @@ -1,6 +1,5 @@ #include <cairo/cairo.h> #include <math.h> -#include <wchar.h> #include <unistd.h> #include "entry.h" #include "log.h" diff --git a/src/entry.h b/src/entry.h index 6b547e0..caf8c00 100644 --- a/src/entry.h +++ b/src/entry.h @@ -5,7 +5,7 @@ #include "entry_backend/harfbuzz.h" #include <cairo/cairo.h> -#include <wchar.h> +#include <uchar.h> #include "color.h" #include "desktop_vec.h" #include "history.h" @@ -27,11 +27,10 @@ struct entry { } cairo[2]; int index; - wchar_t input[MAX_INPUT_LENGTH]; - /* Assume maximum of 4 bytes per wchar_t (for UTF-8) */ - char input_mb[4*MAX_INPUT_LENGTH]; - uint32_t input_length; - uint32_t input_mb_length; + uint32_t input_utf32[MAX_INPUT_LENGTH]; + char input_utf8[4*MAX_INPUT_LENGTH]; + uint32_t input_utf32_length; + uint32_t input_utf8_length; uint32_t selection; uint32_t first_result; @@ -50,8 +49,8 @@ struct entry { bool drun; bool horizontal; bool hide_input; - char hidden_character_mb[4]; - uint32_t hidden_character_mb_length; + char hidden_character_utf8[6]; + uint8_t hidden_character_utf8_length; uint32_t num_results; uint32_t num_results_drawn; uint32_t last_num_results_drawn; diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c index 734d305..5d9ba99 100644 --- a/src/entry_backend/harfbuzz.c +++ b/src/entry_backend/harfbuzz.c @@ -5,7 +5,7 @@ #include "../entry.h" #include "../log.h" #include "../nelem.h" -#include "../utf8.h" +#include "../unicode.h" #include "../xmalloc.h" /* @@ -232,12 +232,12 @@ void entry_backend_harfbuzz_update(struct entry *entry) hb_buffer_clear_contents(buffer); setup_hb_buffer(buffer); if (entry->hide_input) { - size_t char_len = N_ELEM(entry->hidden_character_mb); - for (size_t i = 0; i < entry->input_length; i++) { - hb_buffer_add_utf8(buffer, entry->hidden_character_mb, char_len, 0, char_len); + size_t char_len = N_ELEM(entry->hidden_character_utf8); + for (size_t i = 0; i < entry->input_utf32_length; i++) { + hb_buffer_add_utf8(buffer, entry->hidden_character_utf8, char_len, 0, char_len); } } else { - hb_buffer_add_utf8(buffer, entry->input_mb, -1, 0, -1); + hb_buffer_add_utf8(buffer, entry->input_utf8, -1, 0, -1); } hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0); extents = render_hb_buffer(cr, buffer); @@ -368,17 +368,17 @@ void entry_backend_harfbuzz_update(struct entry *entry) char *match = NULL; char *postmatch = NULL; cairo_text_extents_t subextents; - if (entry->input_mb_length > 0 && entry->selection_highlight_color.a != 0) { - char *match_pos = utf8_strcasestr(prematch, entry->input_mb); + if (entry->input_utf8_length > 0 && entry->selection_highlight_color.a != 0) { + char *match_pos = utf8_strcasestr(prematch, entry->input_utf8); if (match_pos != NULL) { match = xstrdup(result); prematch_len = (match_pos - prematch); prematch[prematch_len] = '\0'; - postmatch_len = strlen(result) - prematch_len - entry->input_mb_length; + postmatch_len = strlen(result) - prematch_len - entry->input_utf8_length; if (postmatch_len > 0) { postmatch = xstrdup(result); } - match[entry->input_mb_length + prematch_len] = '\0'; + match[entry->input_utf8_length + prematch_len] = '\0'; } } @@ -438,7 +438,7 @@ void entry_backend_harfbuzz_update(struct entry *entry) cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); hb_buffer_clear_contents(buffer); setup_hb_buffer(buffer); - hb_buffer_add_utf8(buffer, &postmatch[entry->input_mb_length + prematch_len], -1, 0, -1); + hb_buffer_add_utf8(buffer, &postmatch[entry->input_utf8_length + prematch_len], -1, 0, -1); hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0); subextents = render_hb_buffer(cr, buffer); diff --git a/src/entry_backend/pango.c b/src/entry_backend/pango.c index 1cc7628..d533742 100644 --- a/src/entry_backend/pango.c +++ b/src/entry_backend/pango.c @@ -4,7 +4,7 @@ #include "../entry.h" #include "../log.h" #include "../nelem.h" -#include "../utf8.h" +#include "../unicode.h" #include "../xmalloc.h" #undef MAX @@ -91,16 +91,16 @@ void entry_backend_pango_update(struct entry *entry) * Pango needs to be passed the whole text at once, so we need * to manually replicate the replacement character in a buffer. */ - static char buf[sizeof(entry->input_mb)]; - uint32_t char_len = entry->hidden_character_mb_length; - for (size_t i = 0; i < entry->input_length; i++) { + static char buf[sizeof(entry->input_utf8)]; + uint32_t char_len = entry->hidden_character_utf8_length; + for (size_t i = 0; i < entry->input_utf32_length; i++) { for (size_t j = 0; j < char_len; j++) { - buf[i * char_len + j] = entry->hidden_character_mb[j]; + buf[i * char_len + j] = entry->hidden_character_utf8[j]; } } - pango_layout_set_text(layout, buf, char_len * entry->input_length); + pango_layout_set_text(layout, buf, char_len * entry->input_utf32_length); } else { - pango_layout_set_text(layout, entry->input_mb, -1); + pango_layout_set_text(layout, entry->input_utf8, -1); } pango_cairo_update_layout(cr, layout); pango_cairo_show_layout(cr, layout); @@ -178,11 +178,11 @@ void entry_backend_pango_update(struct entry *entry) } else { ssize_t prematch_len = -1; ssize_t postmatch_len = -1; - size_t match_len = entry->input_mb_length; + size_t match_len = entry->input_utf8_length; PangoRectangle ink_subrect; PangoRectangle logical_subrect; - if (entry->input_mb_length > 0 && entry->selection_highlight_color.a != 0) { - char *match_pos = utf8_strcasestr(str, entry->input_mb); + if (entry->input_utf8_length > 0 && entry->selection_highlight_color.a != 0) { + char *match_pos = utf8_strcasestr(str, entry->input_utf8); if (match_pos != NULL) { prematch_len = (match_pos - str); postmatch_len = strlen(str) - prematch_len - match_len; diff --git a/src/fuzzy_match.c b/src/fuzzy_match.c index 2b6518f..e9f1dcb 100644 --- a/src/fuzzy_match.c +++ b/src/fuzzy_match.c @@ -5,7 +5,7 @@ #include <string.h> #include "fuzzy_match.h" -#include "utf8.h" +#include "unicode.h" #include "xmalloc.h" #undef MAX @@ -146,7 +146,7 @@ int32_t fuzzy_match_recurse( } const char *match = str; - uint32_t search = utf8_get_char(pattern); + uint32_t search = utf8_to_utf32(pattern); int32_t best_score = INT32_MIN; @@ -208,18 +208,18 @@ int32_t compute_score(int32_t jump, bool first_char, const char *restrict match) int32_t score = 0; - const uint32_t cur = utf8_get_char(match); + const uint32_t cur = utf8_to_utf32(match); /* Apply bonuses. */ if (!first_char && jump == 0) { score += adjacency_bonus; } if (!first_char || jump > 0) { - const uint32_t prev = utf8_get_char(utf8_prev_char(match)); - if (utf8_isupper(cur) && utf8_islower(prev)) { + const uint32_t prev = utf8_to_utf32(utf8_prev_char(match)); + if (utf32_isupper(cur) && utf32_islower(prev)) { score += camel_bonus; } - if (utf8_isalnum(cur) && !utf8_isalnum(prev)) { + if (utf32_isalnum(cur) && !utf32_isalnum(prev)) { score += separator_bonus; } } diff --git a/src/input.c b/src/input.c index a343aef..2099863 100644 --- a/src/input.c +++ b/src/input.c @@ -1,9 +1,8 @@ #include <linux/input-event-codes.h> -#include <wctype.h> #include "input.h" #include "nelem.h" #include "tofi.h" -#include "utf8.h" +#include "unicode.h" static void add_character(struct tofi *tofi, xkb_keycode_t keycode); @@ -32,7 +31,7 @@ void input_handle_keypress(struct tofi *tofi, xkb_keycode_t keycode) uint32_t ch = xkb_state_key_get_utf32( tofi->xkb_state, keycode); - if (utf8_isprint(ch)) { + if (utf32_isprint(ch)) { add_character(tofi, keycode); } else if (sym == XKB_KEY_BackSpace) { delete_character(tofi); @@ -101,7 +100,7 @@ void add_character(struct tofi *tofi, xkb_keycode_t keycode) { struct entry *entry = &tofi->window.entry; - if (entry->input_length >= N_ELEM(entry->input) - 1) { + if (entry->input_utf32_length >= N_ELEM(entry->input_utf32) - 1) { /* No more room for input */ return; } @@ -112,22 +111,20 @@ void add_character(struct tofi *tofi, xkb_keycode_t keycode) keycode, buf, sizeof(buf)); - wchar_t ch; - mbtowc(&ch, buf, sizeof(buf)); - entry->input[entry->input_length] = ch; - entry->input_length++; - entry->input[entry->input_length] = L'\0'; - memcpy(&entry->input_mb[entry->input_mb_length], + entry->input_utf32[entry->input_utf32_length] = utf8_to_utf32(buf); + entry->input_utf32_length++; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + memcpy(&entry->input_utf8[entry->input_utf8_length], buf, N_ELEM(buf)); - entry->input_mb_length += len; + entry->input_utf8_length += len; if (entry->drun) { - struct string_vec results = desktop_vec_filter(&entry->apps, entry->input_mb, tofi->fuzzy_match); + struct string_vec results = desktop_vec_filter(&entry->apps, entry->input_utf8, tofi->fuzzy_match); string_vec_destroy(&entry->results); entry->results = results; } else { struct string_vec tmp = entry->results; - entry->results = string_vec_filter(&entry->results, entry->input_mb, tofi->fuzzy_match); + entry->results = string_vec_filter(&entry->results, entry->input_utf8, tofi->fuzzy_match); string_vec_destroy(&tmp); } @@ -138,18 +135,19 @@ void refresh_results(struct tofi *tofi) { struct entry *entry = &tofi->window.entry; - const wchar_t *src = entry->input; - size_t siz = wcsrtombs( - entry->input_mb, - &src, - N_ELEM(entry->input_mb), - NULL); - entry->input_mb_length = siz; + size_t bytes_written = 0; + for (size_t i = 0; i < entry->input_utf32_length; i++) { + bytes_written += utf32_to_utf8( + entry->input_utf32[i], + &entry->input_utf8[bytes_written]); + } + entry->input_utf8[bytes_written] = '\0'; + entry->input_utf8_length = bytes_written; string_vec_destroy(&entry->results); if (entry->drun) { - entry->results = desktop_vec_filter(&entry->apps, entry->input_mb, tofi->fuzzy_match); + entry->results = desktop_vec_filter(&entry->apps, entry->input_utf8, tofi->fuzzy_match); } else { - entry->results = string_vec_filter(&entry->commands, entry->input_mb, tofi->fuzzy_match); + entry->results = string_vec_filter(&entry->commands, entry->input_utf8, tofi->fuzzy_match); } reset_selection(tofi); @@ -159,13 +157,13 @@ void delete_character(struct tofi *tofi) { struct entry *entry = &tofi->window.entry; - if (entry->input_length == 0) { + if (entry->input_utf32_length == 0) { /* No input to delete. */ return; } - entry->input_length--; - entry->input[entry->input_length] = L'\0'; + entry->input_utf32_length--; + entry->input_utf32[entry->input_utf32_length] = U'\0'; refresh_results(tofi); } @@ -174,18 +172,18 @@ void delete_word(struct tofi *tofi) { struct entry *entry = &tofi->window.entry; - if (entry->input_length == 0) { + if (entry->input_utf32_length == 0) { /* No input to delete. */ return; } - while (entry->input_length > 0 && iswspace(entry->input[entry->input_length - 1])) { - entry->input_length--; + while (entry->input_utf32_length > 0 && utf32_isspace(entry->input_utf32[entry->input_utf32_length - 1])) { + entry->input_utf32_length--; } - while (entry->input_length > 0 && !iswspace(entry->input[entry->input_length - 1])) { - entry->input_length--; + while (entry->input_utf32_length > 0 && !utf32_isspace(entry->input_utf32[entry->input_utf32_length - 1])) { + entry->input_utf32_length--; } - entry->input[entry->input_length] = L'\0'; + entry->input_utf32[entry->input_utf32_length] = U'\0'; refresh_results(tofi); } @@ -194,8 +192,8 @@ void clear_input(struct tofi *tofi) { struct entry *entry = &tofi->window.entry; - entry->input_length = 0; - entry->input[0] = L'\0'; + entry->input_utf32_length = 0; + entry->input_utf32[0] = U'\0'; refresh_results(tofi); } @@ -12,8 +12,6 @@ #include <unistd.h> #include <wayland-client.h> #include <wayland-util.h> -#include <wchar.h> -#include <wctype.h> #include <xkbcommon/xkbcommon.h> #include "tofi.h" #include "compgen.h" @@ -816,7 +814,7 @@ static bool do_submit(struct tofi *tofi) if (tofi->require_match || entry->drun) { return false; } else { - printf("%ls\n", entry->input); + printf("%s\n", entry->input_utf8); return true; } } @@ -874,7 +872,7 @@ int main(int argc, char *argv[]) .font_name = "Sans", .font_size = 24, .prompt_text = "run: ", - .hidden_character_mb = "*", + .hidden_character_utf8 = u8"*", .padding_top = 8, .padding_bottom = 8, .padding_left = 8, diff --git a/src/string_vec.c b/src/string_vec.c index 2ef40a2..a30a570 100644 --- a/src/string_vec.c +++ b/src/string_vec.c @@ -6,7 +6,7 @@ #include <sys/mman.h> #include "fuzzy_match.h" #include "string_vec.h" -#include "utf8.h" +#include "unicode.h" #include "xmalloc.h" static int cmpstringp(const void *restrict a, const void *restrict b) diff --git a/src/utf8.c b/src/unicode.c index bd0c425..ad8a777 100644 --- a/src/utf8.c +++ b/src/unicode.c @@ -1,42 +1,52 @@ #include <string.h> -#include "utf8.h" +#include "unicode.h" -uint32_t utf8_isprint(uint32_t c) +uint8_t utf32_to_utf8(uint32_t c, char *buf) +{ + return g_unichar_to_utf8(c, buf); +} + +uint32_t utf8_to_utf32(const char *s) +{ + return g_utf8_get_char(s); +} + +uint32_t utf32_isprint(uint32_t c) { return g_unichar_isprint(c); } -uint32_t utf8_isupper(uint32_t c) +uint32_t utf32_isspace(uint32_t c) +{ + return g_unichar_isspace(c); +} + +uint32_t utf32_isupper(uint32_t c) { return g_unichar_isupper(c); } -uint32_t utf8_islower(uint32_t c) +uint32_t utf32_islower(uint32_t c) { return g_unichar_islower(c); } -uint32_t utf8_isalnum(uint32_t c) +uint32_t utf32_isalnum(uint32_t c) { return g_unichar_isalnum(c); } -uint32_t utf8_toupper(uint32_t c) +uint32_t utf32_toupper(uint32_t c) { return g_unichar_toupper(c); } -uint32_t utf8_tolower(uint32_t c) +uint32_t utf32_tolower(uint32_t c) { return g_unichar_tolower(c); } -uint32_t utf8_get_char(const char *s) -{ - return g_utf8_get_char(s); -} - char *utf8_next_char(const char *s) { return g_utf8_next_char(s); @@ -95,3 +105,8 @@ char *utf8_normalize(const char *s) { return g_utf8_normalize(s, -1, G_NORMALIZE_DEFAULT); } + +char *utf8_compose(const char *s) +{ + return g_utf8_normalize(s, -1, G_NORMALIZE_DEFAULT_COMPOSE); +} diff --git a/src/unicode.h b/src/unicode.h new file mode 100644 index 0000000..45631d9 --- /dev/null +++ b/src/unicode.h @@ -0,0 +1,27 @@ +#ifndef UNICODE_H +#define UNICODE_H + +#include <glib.h> +#include <stdint.h> + +uint8_t utf32_to_utf8(uint32_t c, char *buf); +uint32_t utf8_to_utf32(const char *s); + +uint32_t utf32_isprint(uint32_t c); +uint32_t utf32_isspace(uint32_t c); +uint32_t utf32_isupper(uint32_t c); +uint32_t utf32_islower(uint32_t c); +uint32_t utf32_isalnum(uint32_t c); +uint32_t utf32_toupper(uint32_t c); +uint32_t utf32_tolower(uint32_t c); + +char *utf8_next_char(const char *s); +char *utf8_prev_char(const char *s); +char *utf8_strchr(const char *s, uint32_t c); +char *utf8_strcasechr(const char *s, uint32_t c); +size_t utf8_strlen(const char *s); +char *utf8_strcasestr(const char * restrict haystack, const char * restrict needle); +char *utf8_normalize(const char *s); +char *utf8_compose(const char *s); + +#endif /* UNICODE_H */ diff --git a/src/utf8.h b/src/utf8.h deleted file mode 100644 index fb0fc5f..0000000 --- a/src/utf8.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef UTF8_H -#define UTF8_H - -#include <glib.h> -#include <stdint.h> - -uint32_t utf8_isprint(uint32_t c); -uint32_t utf8_isupper(uint32_t c); -uint32_t utf8_islower(uint32_t c); -uint32_t utf8_isalnum(uint32_t c); -uint32_t utf8_toupper(uint32_t c); -uint32_t utf8_tolower(uint32_t c); - -uint32_t utf8_get_char(const char *s); -char *utf8_next_char(const char *s); -char *utf8_prev_char(const char *s); -char *utf8_strchr(const char *s, uint32_t c); -char *utf8_strcasechr(const char *s, uint32_t c); -size_t utf8_strlen(const char *s); -char *utf8_strcasestr(const char * restrict haystack, const char * restrict needle); -char *utf8_normalize(const char *s); - -#endif /* UTF8_H */ diff --git a/test/utf8.c b/test/utf8.c index 494e75b..f18eca0 100644 --- a/test/utf8.c +++ b/test/utf8.c @@ -5,7 +5,6 @@ #include <string.h> #include "fuzzy_match.h" #include "tap.h" -#include "utf8.h" void is_simple_match(const char *pattern, const char *str, const char *message) { |