Skip to content

Commit

Permalink
keysyms: Add xkb_keysym_iterator
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wismill committed Jan 10, 2024
1 parent 20b302c commit 671d1ae
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 8 deletions.
106 changes: 102 additions & 4 deletions src/keysym.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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);
Expand All @@ -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
Expand Down
21 changes: 21 additions & 0 deletions src/keysym.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
21 changes: 21 additions & 0 deletions src/keysym.h.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
19 changes: 15 additions & 4 deletions test/keysym.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down

0 comments on commit 671d1ae

Please sign in to comment.