From 817179d8661684fe3e9a66dc44035c215559246e Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Thu, 14 Dec 2023 08:19:28 +0100 Subject: [PATCH] keysyms: Add xkb_keysym_iterator Add an efficient way to iterate over the assigned keysyms. Currently only provided for testing, so we guard it by `ENABLE_PRIVATE_APIS` in order to reduce the installed library. --- src/keysym.c | 106 +++++++++++++++++++++++++++++++++++++++++++-- src/keysym.h | 21 +++++++++ src/keysym.h.jinja | 21 +++++++++ test/keysym.c | 19 ++++++-- 4 files changed, 159 insertions(+), 8 deletions(-) diff --git a/src/keysym.c b/src/keysym.c index cd2565c..e02af1a 100644 --- a/src/keysym.c +++ b/src/keysym.c @@ -91,6 +91,14 @@ get_name(const struct name_keysym *entry) return keysym_names + entry->offset; } +/* Unnamed Unicode codepoint. */ +static inline int +get_unicode_name(xkb_keysym_t ks, char *buffer, size_t size) +{ + const int width = (ks & 0xff0000UL) ? 8 : 4; + return snprintf(buffer, size, "U%0*lX", width, ks & 0xffffffUL); +} + XKB_EXPORT int xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) { @@ -104,10 +112,8 @@ xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) return snprintf(buffer, size, "%s", get_name(&keysym_to_name[index])); /* Unnamed Unicode codepoint. */ - if (ks >= XKB_KEYSYM_UNICODE_MIN && ks <= XKB_KEYSYM_UNICODE_MAX) { - const int width = (ks & 0xff0000UL) ? 8 : 4; - return snprintf(buffer, size, "U%0*lX", width, ks & 0xffffffUL); - } + if (ks >= XKB_KEYSYM_UNICODE_MIN && ks <= XKB_KEYSYM_UNICODE_MAX) + return get_unicode_name(ks, buffer, size); /* Unnamed, non-Unicode, symbol (shouldn't generally happen). */ return snprintf(buffer, size, "0x%08x", ks); @@ -120,6 +126,98 @@ xkb_keysym_is_assigned(xkb_keysym_t ks) find_keysym_index(ks) != -1; } +struct xkb_keysym_iterator { + bool explicit; /* If true, traverse only explicitly named keysyms */ + int32_t index; /* Current index in `keysym_to_name` */ + xkb_keysym_t keysym; /* Current keysym */ +}; + +struct xkb_keysym_iterator* +xkb_keysym_iterator_new(bool iterate_only_explicit_keysyms) +{ + struct xkb_keysym_iterator* iter = calloc(1, sizeof(*iter)); + iter->explicit = iterate_only_explicit_keysyms; + iter->index = -1; + iter->keysym = XKB_KEYSYM_UNICODE_MAX; + return iter; +} + +struct xkb_keysym_iterator* +xkb_keysym_iterator_unref(struct xkb_keysym_iterator *iter) +{ + free(iter); + return NULL; +} + +xkb_keysym_t +xkb_keysym_iterator_get_keysym(struct xkb_keysym_iterator *iter) +{ + return iter->keysym; +} + +bool +xkb_keysym_iterator_is_explicitly_named(struct xkb_keysym_iterator *iter) +{ + return iter->index >= 0 && + iter->index < (int32_t)ARRAY_SIZE(keysym_to_name) && + (iter->explicit || + iter->keysym == keysym_to_name[iter->index].keysym); +} + +int +xkb_keysym_iterator_get_name(struct xkb_keysym_iterator *iter, + char *buffer, size_t size) +{ + if (iter->index < 0 || iter->index >= (int32_t)ARRAY_SIZE(keysym_to_name)) + return -1; + if (iter->explicit || iter->keysym == keysym_to_name[iter->index].keysym) + return snprintf(buffer, size, "%s", + get_name(&keysym_to_name[iter->index])); + return get_unicode_name(iter->keysym, buffer, size); +} + +/* Iterate over the *assigned* keysyms. + * + * Use: + * + * ```c + * struct xkb_keysym_iterator *iter = xkb_keysym_iterator_new(true); + * while (xkb_keysym_iterator_next(iter)) { + * ... + * } + * iter = xkb_keysym_iterator_unref(iter); + * ``` + */ +bool +xkb_keysym_iterator_next(struct xkb_keysym_iterator *iter) +{ + if (iter->index >= (int32_t)ARRAY_SIZE(keysym_to_name) - 1) + return false; + + /* Next keysym */ + if (iter->explicit || iter->keysym >= XKB_KEYSYM_UNICODE_MAX || + keysym_to_name[iter->index + 1].keysym < XKB_KEYSYM_UNICODE_MIN) { + /* Explicitly named keysyms only */ + iter->keysym = keysym_to_name[++iter->index].keysym; + assert(iter->explicit || + iter->keysym <= XKB_KEYSYM_UNICODE_MIN || + iter->keysym >= XKB_KEYSYM_UNICODE_MAX); + } else { + /* Unicode keysyms + * NOTE: Unicode keysyms are within keysym_to_name keysyms range. */ + if (iter->keysym >= keysym_to_name[iter->index].keysym) + iter->index++; + if (iter->keysym >= XKB_KEYSYM_UNICODE_MIN) { + /* Continue Unicode keysyms */ + iter->keysym++; + } else { + /* Start Unicode keysyms */ + iter->keysym = XKB_KEYSYM_UNICODE_MIN; + } + } + return true; +} + /* * Parse the numeric part of a 0xXXXX and UXXXX keysym. * Not using strtoul -- it's slower and accepts a bunch of stuff diff --git a/src/keysym.h b/src/keysym.h index 916ae71..18bdede 100644 --- a/src/keysym.h +++ b/src/keysym.h @@ -80,6 +80,27 @@ bool xkb_keysym_is_assigned(xkb_keysym_t ks); +struct xkb_keysym_iterator; + +struct xkb_keysym_iterator* +xkb_keysym_iterator_new(bool explicit); + +struct xkb_keysym_iterator* +xkb_keysym_iterator_unref(struct xkb_keysym_iterator *iter); + +bool +xkb_keysym_iterator_next(struct xkb_keysym_iterator *iter); + +xkb_keysym_t +xkb_keysym_iterator_get_keysym(struct xkb_keysym_iterator *iter); + +int +xkb_keysym_iterator_get_name(struct xkb_keysym_iterator *iter, + char *buffer, size_t size); + +bool +xkb_keysym_iterator_is_explicitly_named(struct xkb_keysym_iterator *iter); + bool xkb_keysym_is_lower(xkb_keysym_t keysym); diff --git a/src/keysym.h.jinja b/src/keysym.h.jinja index 096a61c..d67622e 100644 --- a/src/keysym.h.jinja +++ b/src/keysym.h.jinja @@ -80,6 +80,27 @@ bool xkb_keysym_is_assigned(xkb_keysym_t ks); +struct xkb_keysym_iterator; + +struct xkb_keysym_iterator* +xkb_keysym_iterator_new(bool explicit); + +struct xkb_keysym_iterator* +xkb_keysym_iterator_unref(struct xkb_keysym_iterator *iter); + +bool +xkb_keysym_iterator_next(struct xkb_keysym_iterator *iter); + +xkb_keysym_t +xkb_keysym_iterator_get_keysym(struct xkb_keysym_iterator *iter); + +int +xkb_keysym_iterator_get_name(struct xkb_keysym_iterator *iter, + char *buffer, size_t size); + +bool +xkb_keysym_iterator_is_explicitly_named(struct xkb_keysym_iterator *iter); + bool xkb_keysym_is_lower(xkb_keysym_t keysym); diff --git a/test/keysym.c b/test/keysym.c index 17488c1..f453f1f 100644 --- a/test/keysym.c +++ b/test/keysym.c @@ -166,12 +166,23 @@ main(void) assert(xkb_keysym_is_assigned(XKB_KEYSYM_MAX_ASSIGNED)); assert(!xkb_keysym_is_assigned(XKB_KEYSYM_MAX)); - for (xkb_keysym_t ks = XKB_KEYSYM_MIN; ks <= XKB_KEYSYM_MAX; ks++) { - if (!xkb_keysym_is_assigned(ks)) - continue; + struct xkb_keysym_iterator *iter = xkb_keysym_iterator_new(false); + xkb_keysym_t ks_prev = XKB_KEYSYM_MIN; + uint32_t count = 0; + uint32_t count_non_unicode = 0; + while (xkb_keysym_iterator_next(iter)) { + count++; + xkb_keysym_t ks = xkb_keysym_iterator_get_keysym(iter); + if (ks < XKB_KEYSYM_UNICODE_MIN || ks > XKB_KEYSYM_UNICODE_MAX) + count_non_unicode++; + assert(ks > ks_prev || count == 1); + ks_prev = ks; /* Check assigned keysyms bounds */ - assert(XKB_KEYSYM_MIN_ASSIGNED <= (int32_t)ks && ks <= XKB_KEYSYM_MAX_ASSIGNED); + assert((int32_t)XKB_KEYSYM_MIN_ASSIGNED <= (int32_t)ks && ks <= XKB_KEYSYM_MAX_ASSIGNED); } + iter = xkb_keysym_iterator_unref(iter); + assert(ks_prev == XKB_KEYSYM_MAX_ASSIGNED); + assert(count == XKB_KEYSYM_UNICODE_MAX - XKB_KEYSYM_UNICODE_MIN + 1 + count_non_unicode); /* Named keysyms */ assert(test_string("NoSymbol", XKB_KEY_NoSymbol));