Edit

kc3-lang/libxkbcommon/src/x11/util.c

Branch :

  • Show log

    Commit

  • Author : Ran Benita
    Date : 2025-01-25 07:00:43
    Hash : a380ba52
    Message : Move XKB_EXPORT to headers The Windows dllexport annotation wants to be on the declarations, not the definitions. Signed-off-by: Ran Benita <ran@unusedvar.com>

  • src/x11/util.c
  • /*
     * Copyright © 2013 Ran Benita
     * SPDX-License-Identifier: MIT
     */
    
    #include "config.h"
    
    #include "x11-priv.h"
    
    int
    xkb_x11_setup_xkb_extension(xcb_connection_t *conn,
                                uint16_t major_xkb_version,
                                uint16_t minor_xkb_version,
                                enum xkb_x11_setup_xkb_extension_flags flags,
                                uint16_t *major_xkb_version_out,
                                uint16_t *minor_xkb_version_out,
                                uint8_t *base_event_out,
                                uint8_t *base_error_out)
    {
        uint8_t base_event, base_error;
        uint16_t server_major, server_minor;
    
        if (flags & ~(XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS)) {
            /* log_err_func(ctx, "unrecognized flags: %#x\n", flags); */
            return 0;
        }
    
        {
            const xcb_query_extension_reply_t *reply =
                xcb_get_extension_data(conn, &xcb_xkb_id);
            if (!reply) {
                /* log_err_func(ctx, "failed to query for XKB extension\n"); */
                return 0;
            }
    
            if (!reply->present) {
                /* log_err_func(ctx, "failed to start using XKB extension: not available in server\n"); */
                return 0;
            }
    
            base_event = reply->first_event;
            base_error = reply->first_error;
        }
    
        {
            xcb_generic_error_t *error = NULL;
            xcb_xkb_use_extension_cookie_t cookie =
                xcb_xkb_use_extension(conn, major_xkb_version, minor_xkb_version);
            xcb_xkb_use_extension_reply_t *reply =
                xcb_xkb_use_extension_reply(conn, cookie, &error);
    
            if (!reply) {
                /* log_err_func(ctx, */
                /*              "failed to start using XKB extension: error code %d\n", */
                /*              error ? error->error_code : -1); */
                free(error);
                return 0;
            }
    
            if (!reply->supported) {
                /* log_err_func(ctx, */
                /*              "failed to start using XKB extension: server doesn't support version %d.%d\n", */
                /*              major_xkb_version, minor_xkb_version); */
                free(reply);
                return 0;
            }
    
            server_major = reply->serverMajor;
            server_minor = reply->serverMinor;
    
            free(reply);
        }
    
        /*
        * The XkbUseExtension() in libX11 has a *bunch* of legacy stuff, but
        * it doesn't seem like any of it is useful to us.
        */
    
        if (major_xkb_version_out)
            *major_xkb_version_out = server_major;
        if (minor_xkb_version_out)
            *minor_xkb_version_out = server_minor;
        if (base_event_out)
            *base_event_out = base_event;
        if (base_error_out)
            *base_error_out = base_error;
    
        return 1;
    }
    
    int32_t
    xkb_x11_get_core_keyboard_device_id(xcb_connection_t *conn)
    {
        int32_t device_id;
        xcb_xkb_get_device_info_cookie_t cookie =
            xcb_xkb_get_device_info(conn, XCB_XKB_ID_USE_CORE_KBD,
                                    0, 0, 0, 0, 0, 0);
        xcb_xkb_get_device_info_reply_t *reply =
            xcb_xkb_get_device_info_reply(conn, cookie, NULL);
    
        if (!reply)
            return -1;
    
        device_id = reply->deviceID;
        free(reply);
        return device_id;
    }
    
    struct x11_atom_cache {
        /*
         * Invalidate the cache based on the XCB connection.
         * X11 atoms are actually not per connection or client, but per X server
         * session. But better be safe just in case we survive an X server restart.
         */
        xcb_connection_t *conn;
        struct {
            xcb_atom_t from;
            xkb_atom_t to;
        } cache[256];
        size_t len;
    };
    
    static struct x11_atom_cache *
    get_cache(struct xkb_context *ctx, xcb_connection_t *conn)
    {
        if (!ctx->x11_atom_cache) {
            ctx->x11_atom_cache = calloc(1, sizeof(struct x11_atom_cache));
        }
        /* Can be NULL in case the malloc failed. */
        struct x11_atom_cache *cache = ctx->x11_atom_cache;
        if (cache && cache->conn != conn) {
            cache->conn = conn;
            cache->len = 0;
        }
        return cache;
    }
    
    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;
                }
            }
        }
    
        /* 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;
                }
    
                size_t idx = interner->num_copies++;
                interner->copies[idx].from = atom;
                interner->copies[idx].out = out;
                return;
            }
        }
    
        /* 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);
    }
    
    void
    x11_atom_interner_round_trip(struct x11_atom_interner *interner) {
        struct xkb_context *ctx = interner->ctx;
        xcb_connection_t *conn = interner->conn;
    
        /* Can be NULL in case the malloc failed. */
        struct x11_atom_cache *cache = get_cache(ctx, conn);
    
        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 (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;
            }
        }
    
        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;
    }
    
    void
    x11_atom_interner_get_escaped_atom_name(struct x11_atom_interner *interner,
                                            xcb_atom_t atom, char **out)
    {
        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);
    }