aboutsummaryrefslogtreecommitdiff
path: root/src/x11/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/x11/util.c')
-rw-r--r--src/x11/util.c224
1 files changed, 129 insertions, 95 deletions
diff --git a/src/x11/util.c b/src/x11/util.c
index 660d885..cc4c71c 100644
--- a/src/x11/util.c
+++ b/src/x11/util.c
@@ -124,37 +124,6 @@ xkb_x11_get_core_keyboard_device_id(xcb_connection_t *conn)
return device_id;
}
-bool
-get_atom_name(xcb_connection_t *conn, xcb_atom_t atom, char **out)
-{
- xcb_get_atom_name_cookie_t cookie;
- xcb_get_atom_name_reply_t *reply;
- int length;
- char *name;
-
- if (atom == 0) {
- *out = NULL;
- return true;
- }
-
- cookie = xcb_get_atom_name(conn, atom);
- reply = xcb_get_atom_name_reply(conn, cookie, NULL);
- if (!reply)
- return false;
-
- length = xcb_get_atom_name_name_length(reply);
- name = xcb_get_atom_name_name(reply);
-
- *out = strndup(name, length);
- if (!*out) {
- free(reply);
- return false;
- }
-
- free(reply);
- return true;
-}
-
struct x11_atom_cache {
/*
* Invalidate the cache based on the XCB connection.
@@ -169,14 +138,9 @@ struct x11_atom_cache {
size_t len;
};
-bool
-adopt_atoms(struct xkb_context *ctx, xcb_connection_t *conn,
- const xcb_atom_t *from, xkb_atom_t *to, const size_t count)
+static struct x11_atom_cache *
+get_cache(struct xkb_context *ctx, xcb_connection_t *conn)
{
- enum { SIZE = 128 };
- xcb_get_atom_name_cookie_t cookies[SIZE];
- const size_t num_batches = ROUNDUP(count, SIZE) / SIZE;
-
if (!ctx->x11_atom_cache) {
ctx->x11_atom_cache = calloc(1, sizeof(struct x11_atom_cache));
}
@@ -186,79 +150,149 @@ adopt_atoms(struct xkb_context *ctx, xcb_connection_t *conn,
cache->conn = conn;
cache->len = 0;
}
+ return cache;
+}
- memset(to, 0, count * sizeof(*to));
-
- /* Send and collect the atoms in batches of reasonable SIZE. */
- for (size_t batch = 0; batch < num_batches; batch++) {
- const size_t start = batch * SIZE;
- const size_t stop = MIN((batch + 1) * SIZE, count);
-
- /* Send. */
- for (size_t i = start; i < stop; i++) {
- bool cache_hit = false;
- if (cache) {
- for (size_t c = 0; c < cache->len; c++) {
- if (cache->cache[c].from == from[i]) {
- to[i] = cache->cache[c].to;
- cache_hit = true;
- break;
- }
- }
+void
+x11_atom_interner_init(struct x11_atom_interner *interner,
+ struct xkb_context *ctx, xcb_connection_t *conn)
+{
+ interner->had_error = false;
+ interner->ctx = ctx;
+ interner->conn = conn;
+ interner->num_pending = 0;
+ interner->num_copies = 0;
+ interner->num_escaped = 0;
+}
+
+void
+x11_atom_interner_adopt_atom(struct x11_atom_interner *interner,
+ const xcb_atom_t atom, xkb_atom_t *out)
+{
+ *out = XKB_ATOM_NONE;
+
+ if (atom == XCB_ATOM_NONE)
+ return;
+
+ /* Can be NULL in case the malloc failed. */
+ struct x11_atom_cache *cache = get_cache(interner->ctx, interner->conn);
+
+retry:
+
+ /* Already in the cache? */
+ if (cache) {
+ for (size_t c = 0; c < cache->len; c++) {
+ if (cache->cache[c].from == atom) {
+ *out = cache->cache[c].to;
+ return;
}
- if (!cache_hit && from[i] != XCB_ATOM_NONE)
- cookies[i % SIZE] = xcb_get_atom_name(conn, from[i]);
}
+ }
- /* Collect. */
- for (size_t i = start; i < stop; i++) {
- xcb_get_atom_name_reply_t *reply;
-
- if (from[i] == XCB_ATOM_NONE)
- continue;
+ /* Already pending? */
+ for (size_t i = 0; i < interner->num_pending; i++) {
+ if (interner->pending[i].from == atom) {
+ if (interner->num_copies == ARRAY_SIZE(interner->copies)) {
+ x11_atom_interner_round_trip(interner);
+ goto retry;
+ }
- /* Was filled from cache. */
- if (to[i] != 0)
- continue;
+ size_t idx = interner->num_copies++;
+ interner->copies[idx].from = atom;
+ interner->copies[idx].out = out;
+ return;
+ }
+ }
- reply = xcb_get_atom_name_reply(conn, cookies[i % SIZE], NULL);
- if (!reply)
- goto err_discard;
+ /* We have to send a GetAtomName request */
+ if (interner->num_pending == ARRAY_SIZE(interner->pending)) {
+ x11_atom_interner_round_trip(interner);
+ assert(interner->num_pending < ARRAY_SIZE(interner->pending));
+ }
+ size_t idx = interner->num_pending++;
+ interner->pending[idx].from = atom;
+ interner->pending[idx].out = out;
+ interner->pending[idx].cookie = xcb_get_atom_name(interner->conn, atom);
+}
- to[i] = xkb_atom_intern(ctx,
- xcb_get_atom_name_name(reply),
- xcb_get_atom_name_name_length(reply));
- free(reply);
+void
+x11_atom_interner_round_trip(struct x11_atom_interner *interner) {
+ struct xkb_context *ctx = interner->ctx;
+ xcb_connection_t *conn = interner->conn;
- if (to[i] == XKB_ATOM_NONE)
- goto err_discard;
+ /* Can be NULL in case the malloc failed. */
+ struct x11_atom_cache *cache = get_cache(ctx, conn);
- if (cache && cache->len < ARRAY_SIZE(cache->cache)) {
- size_t idx = cache->len++;
- cache->cache[idx].from = from[i];
- cache->cache[idx].to = to[i];
- }
+ for (size_t i = 0; i < interner->num_pending; i++) {
+ xcb_get_atom_name_reply_t *reply;
+ reply = xcb_get_atom_name_reply(conn, interner->pending[i].cookie, NULL);
+ if (!reply) {
+ interner->had_error = true;
continue;
+ }
+ xcb_atom_t x11_atom = interner->pending[i].from;
+ xkb_atom_t atom = xkb_atom_intern(ctx,
+ xcb_get_atom_name_name(reply),
+ xcb_get_atom_name_name_length(reply));
+ free(reply);
- /*
- * If we don't discard the uncollected replies, they just
- * sit in the XCB queue waiting forever. Sad.
- */
-err_discard:
- for (size_t j = i + 1; j < stop; j++)
- if (from[j] != XCB_ATOM_NONE)
- xcb_discard_reply(conn, cookies[j % SIZE].sequence);
- return false;
+ if (cache && cache->len < ARRAY_SIZE(cache->cache)) {
+ size_t idx = cache->len++;
+ cache->cache[idx].from = x11_atom;
+ cache->cache[idx].to = atom;
+ }
+
+ *interner->pending[i].out = atom;
+
+ for (size_t j = 0; j < interner->num_copies; j++) {
+ if (interner->copies[j].from == x11_atom)
+ *interner->copies[j].out = atom;
}
}
- return true;
+ for (size_t i = 0; i < interner->num_escaped; i++) {
+ xcb_get_atom_name_reply_t *reply;
+ int length;
+ char *name;
+ char **out = interner->escaped[i].out;
+
+ reply = xcb_get_atom_name_reply(conn, interner->escaped[i].cookie, NULL);
+ *interner->escaped[i].out = NULL;
+ if (!reply) {
+ interner->had_error = true;
+ } else {
+ length = xcb_get_atom_name_name_length(reply);
+ name = xcb_get_atom_name_name(reply);
+
+ *out = strndup(name, length);
+ free(reply);
+ if (*out == NULL) {
+ interner->had_error = true;
+ } else {
+ XkbEscapeMapName(*out);
+ }
+ }
+ }
+
+ interner->num_pending = 0;
+ interner->num_copies = 0;
+ interner->num_escaped = 0;
}
-bool
-adopt_atom(struct xkb_context *ctx, xcb_connection_t *conn, xcb_atom_t atom,
- xkb_atom_t *out)
+void
+x11_atom_interner_get_escaped_atom_name(struct x11_atom_interner *interner,
+ xcb_atom_t atom, char **out)
{
- return adopt_atoms(ctx, conn, &atom, out, 1);
+ if (atom == 0) {
+ *out = NULL;
+ return;
+ }
+ size_t idx = interner->num_escaped++;
+ /* There can only be a fixed number of calls to this function "in-flight",
+ * thus we assert this number. Increase the array size if this assert fails.
+ */
+ assert(idx < ARRAY_SIZE(interner->escaped));
+ interner->escaped[idx].out = out;
+ interner->escaped[idx].cookie = xcb_get_atom_name(interner->conn, atom);
}