diff --git a/meson.build b/meson.build index d55ae08..97561fe 100644 --- a/meson.build +++ b/meson.build @@ -623,6 +623,7 @@ libxkbcommon_test_internal = static_library( 'bench/bench.h', libxkbcommon_sources, include_directories: include_directories('src', 'include'), + c_args: ['-DENABLE_PRIVATE_APIS'], ) test_dep = declare_dependency( include_directories: include_directories('src', 'include'), @@ -652,7 +653,8 @@ if get_option('enable-x11') endif test( 'keysym', - executable('test-keysym', 'test/keysym.c', dependencies: test_dep), + executable('test-keysym', 'test/keysym.c', dependencies: test_dep, + c_args: ['-DENABLE_PRIVATE_APIS']), env: test_env, ) test( diff --git a/src/keysym.c b/src/keysym.c index 192ba20..cd2565c 100644 --- a/src/keysym.c +++ b/src/keysym.c @@ -55,6 +55,36 @@ #include "keysym.h" #include "ks_tables.h" +static ssize_t +find_keysym_index(xkb_keysym_t ks) +{ + /* Lower bound: + * keysym_to_name[0].keysym + * == XKB_KEYSYM_MIN_EXPLICIT == XKB_KEYSYM_MIN == 0 + * No need to check: xkb_keysym_t is unsigned. + * + * Upper bound: + * keysym_to_name[ARRAY_SIZE(keysym_to_name) - 1].keysym + * == XKB_KEYSYM_MAX_EXPLICIT <= XKB_KEYSYM_MAX + */ + if (ks > XKB_KEYSYM_MAX_EXPLICIT) + return -1; + + ssize_t lo = 0, hi = ARRAY_SIZE(keysym_to_name) - 1; + while (hi >= lo) { + ssize_t mid = (lo + hi) / 2; + if (ks > keysym_to_name[mid].keysym) { + lo = mid + 1; + } else if (ks < keysym_to_name[mid].keysym) { + hi = mid - 1; + } else { + return mid; + } + } + + return -1; +} + static inline const char * get_name(const struct name_keysym *entry) { @@ -69,17 +99,9 @@ xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) return -1; } - int32_t lo = 0, hi = ARRAY_SIZE(keysym_to_name) - 1; - while (hi >= lo) { - int32_t mid = (lo + hi) / 2; - if (ks > keysym_to_name[mid].keysym) { - lo = mid + 1; - } else if (ks < keysym_to_name[mid].keysym) { - hi = mid - 1; - } else { - return snprintf(buffer, size, "%s", get_name(&keysym_to_name[mid])); - } - } + ssize_t index = find_keysym_index(ks); + if (index != -1) + 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) { @@ -91,6 +113,13 @@ xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) return snprintf(buffer, size, "0x%08x", ks); } +bool +xkb_keysym_is_assigned(xkb_keysym_t ks) +{ + return (XKB_KEYSYM_UNICODE_MIN <= ks && ks <= XKB_KEYSYM_UNICODE_MAX) || + find_keysym_index(ks) != -1; +} + /* * 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 03a2c3e..916ae71 100644 --- a/src/keysym.h +++ b/src/keysym.h @@ -50,6 +50,9 @@ #ifndef KEYSYM_H #define KEYSYM_H +#include +#include "xkbcommon/xkbcommon.h" + /* * NOTE: this is not defined in xkbcommon.h, because if we did, it may add * overhead for library user: when handling keysyms they would also need to @@ -74,6 +77,9 @@ /** Maximum Unicode keysym, correspoding to the maximum Unicode code point */ #define XKB_KEYSYM_UNICODE_MAX 0x0110ffff +bool +xkb_keysym_is_assigned(xkb_keysym_t ks); + bool xkb_keysym_is_lower(xkb_keysym_t keysym); diff --git a/src/keysym.h.jinja b/src/keysym.h.jinja index 762eeb4..096a61c 100644 --- a/src/keysym.h.jinja +++ b/src/keysym.h.jinja @@ -50,6 +50,9 @@ #ifndef KEYSYM_H #define KEYSYM_H +#include +#include "xkbcommon/xkbcommon.h" + /* * NOTE: this is not defined in xkbcommon.h, because if we did, it may add * overhead for library user: when handling keysyms they would also need to @@ -74,6 +77,9 @@ /** Maximum Unicode keysym, correspoding to the maximum Unicode code point */ #define XKB_KEYSYM_UNICODE_MAX 0x0110ffff +bool +xkb_keysym_is_assigned(xkb_keysym_t ks); + bool xkb_keysym_is_lower(xkb_keysym_t keysym); diff --git a/test/keysym.c b/test/keysym.c index a2b1fce..17488c1 100644 --- a/test/keysym.c +++ b/test/keysym.c @@ -147,6 +147,32 @@ main(void) assert(XKB_KEYSYM_UNICODE_MIN < XKB_KEYSYM_UNICODE_MAX); assert(XKB_KEYSYM_UNICODE_MAX <= XKB_KEYSYM_MAX_EXPLICIT); + /* Assigned keysyms */ + assert(xkb_keysym_is_assigned(XKB_KEYSYM_MIN)); + assert(xkb_keysym_is_assigned(XKB_KEYSYM_MIN_ASSIGNED)); + assert(xkb_keysym_is_assigned(XKB_KEY_space)); + assert(xkb_keysym_is_assigned(XKB_KEY_nobreakspace)); + assert(xkb_keysym_is_assigned(XKB_KEY_Aogonek)); + assert(xkb_keysym_is_assigned(XKB_KEY_Hstroke)); + assert(xkb_keysym_is_assigned(XKB_KEY_kra)); + assert(xkb_keysym_is_assigned(XKB_KEY_braille_dot_1)); + assert(xkb_keysym_is_assigned(XKB_KEY_XF86KbdLcdMenu5)); + assert(xkb_keysym_is_assigned(XKB_KEY_Shift_L)); + assert(xkb_keysym_is_assigned(XKB_KEY_XF86MonBrightnessUp)); + assert(xkb_keysym_is_assigned(XKB_KEY_VoidSymbol)); + assert(xkb_keysym_is_assigned(XKB_KEYSYM_UNICODE_MIN)); + assert(xkb_keysym_is_assigned((XKB_KEYSYM_UNICODE_MIN + XKB_KEYSYM_UNICODE_MAX) / 2)); + assert(xkb_keysym_is_assigned(XKB_KEYSYM_UNICODE_MAX)); + 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; + /* Check assigned keysyms bounds */ + assert(XKB_KEYSYM_MIN_ASSIGNED <= (int32_t)ks && ks <= XKB_KEYSYM_MAX_ASSIGNED); + } + /* Named keysyms */ assert(test_string("NoSymbol", XKB_KEY_NoSymbol)); assert(test_string("Undo", 0xFF65));