Home | History | Annotate | Download | only in x11
      1 /*
      2  * Copyright  2013 Ran Benita
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 #include "x11-priv.h"
     25 
     26 XKB_EXPORT int
     27 xkb_x11_setup_xkb_extension(xcb_connection_t *conn,
     28                             uint16_t major_xkb_version,
     29                             uint16_t minor_xkb_version,
     30                             enum xkb_x11_setup_xkb_extension_flags flags,
     31                             uint16_t *major_xkb_version_out,
     32                             uint16_t *minor_xkb_version_out,
     33                             uint8_t *base_event_out,
     34                             uint8_t *base_error_out)
     35 {
     36     uint8_t base_event, base_error;
     37     uint16_t server_major, server_minor;
     38 
     39     if (flags & ~(XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS)) {
     40         /* log_err_func(ctx, "unrecognized flags: %#x\n", flags); */
     41         return 0;
     42     }
     43 
     44     {
     45         const xcb_query_extension_reply_t *reply =
     46             xcb_get_extension_data(conn, &xcb_xkb_id);
     47         if (!reply) {
     48             /* log_err_func(ctx, "failed to query for XKB extension\n"); */
     49             return 0;
     50         }
     51 
     52         if (!reply->present) {
     53             /* log_err_func(ctx, "failed to start using XKB extension: not available in server\n"); */
     54             return 0;
     55         }
     56 
     57         base_event = reply->first_event;
     58         base_error = reply->first_error;
     59     }
     60 
     61     {
     62         xcb_generic_error_t *error = NULL;
     63         xcb_xkb_use_extension_cookie_t cookie =
     64             xcb_xkb_use_extension(conn, major_xkb_version, minor_xkb_version);
     65         xcb_xkb_use_extension_reply_t *reply =
     66             xcb_xkb_use_extension_reply(conn, cookie, &error);
     67 
     68         if (!reply) {
     69             /* log_err_func(ctx, */
     70             /*              "failed to start using XKB extension: error code %d\n", */
     71             /*              error ? error->error_code : -1); */
     72             free(error);
     73             return 0;
     74         }
     75 
     76         if (!reply->supported) {
     77             /* log_err_func(ctx, */
     78             /*              "failed to start using XKB extension: server doesn't support version %d.%d\n", */
     79             /*              major_xkb_version, minor_xkb_version); */
     80             free(reply);
     81             return 0;
     82         }
     83 
     84         server_major = reply->serverMajor;
     85         server_minor = reply->serverMinor;
     86 
     87         free(reply);
     88     }
     89 
     90     /*
     91     * The XkbUseExtension() in libX11 has a *bunch* of legacy stuff, but
     92     * it doesn't seem like any of it is useful to us.
     93     */
     94 
     95     if (major_xkb_version_out)
     96         *major_xkb_version_out = server_major;
     97     if (minor_xkb_version_out)
     98         *minor_xkb_version_out = server_minor;
     99     if (base_event_out)
    100         *base_event_out = base_event;
    101     if (base_error_out)
    102         *base_error_out = base_error;
    103 
    104     return 1;
    105 }
    106 
    107 XKB_EXPORT int32_t
    108 xkb_x11_get_core_keyboard_device_id(xcb_connection_t *conn)
    109 {
    110     int32_t device_id;
    111     xcb_xkb_get_device_info_cookie_t cookie =
    112         xcb_xkb_get_device_info(conn, XCB_XKB_ID_USE_CORE_KBD,
    113                                 0, 0, 0, 0, 0, 0);
    114     xcb_xkb_get_device_info_reply_t *reply =
    115         xcb_xkb_get_device_info_reply(conn, cookie, NULL);
    116 
    117     if (!reply)
    118         return -1;
    119 
    120     device_id = reply->deviceID;
    121     free(reply);
    122     return device_id;
    123 }
    124 
    125 bool
    126 get_atom_name(xcb_connection_t *conn, xcb_atom_t atom, char **out)
    127 {
    128     xcb_get_atom_name_cookie_t cookie;
    129     xcb_get_atom_name_reply_t *reply;
    130     int length;
    131     char *name;
    132 
    133     if (atom == 0) {
    134         *out = NULL;
    135         return true;
    136     }
    137 
    138     cookie = xcb_get_atom_name(conn, atom);
    139     reply = xcb_get_atom_name_reply(conn, cookie, NULL);
    140     if (!reply)
    141         return false;
    142 
    143     length = xcb_get_atom_name_name_length(reply);
    144     name = xcb_get_atom_name_name(reply);
    145 
    146     *out = strndup(name, length);
    147     if (!*out) {
    148         free(reply);
    149         return false;
    150     }
    151 
    152     free(reply);
    153     return true;
    154 }
    155 
    156 bool
    157 adopt_atoms(struct xkb_context *ctx, xcb_connection_t *conn,
    158             const xcb_atom_t *from, xkb_atom_t *to, const size_t count)
    159 {
    160     enum { SIZE = 128 };
    161     xcb_get_atom_name_cookie_t cookies[SIZE];
    162     const size_t num_batches = ROUNDUP(count, SIZE) / SIZE;
    163 
    164     /* Send and collect the atoms in batches of reasonable SIZE. */
    165     for (size_t batch = 0; batch < num_batches; batch++) {
    166         const size_t start = batch * SIZE;
    167         const size_t stop = min((batch + 1) * SIZE, count);
    168 
    169         /* Send. */
    170         for (size_t i = start; i < stop; i++)
    171             if (from[i] != XCB_ATOM_NONE)
    172                 cookies[i % SIZE] = xcb_get_atom_name(conn, from[i]);
    173 
    174         /* Collect. */
    175         for (size_t i = start; i < stop; i++) {
    176             xcb_get_atom_name_reply_t *reply;
    177 
    178             if (from[i] == XCB_ATOM_NONE) {
    179                 to[i] = XKB_ATOM_NONE;
    180                 continue;
    181             }
    182 
    183             reply = xcb_get_atom_name_reply(conn, cookies[i % SIZE], NULL);
    184             if (!reply)
    185                 goto err_discard;
    186 
    187             to[i] = xkb_atom_intern(ctx,
    188                                     xcb_get_atom_name_name(reply),
    189                                     xcb_get_atom_name_name_length(reply));
    190             free(reply);
    191 
    192             if (to[i] == XKB_ATOM_NONE)
    193                 goto err_discard;
    194 
    195             continue;
    196 
    197             /*
    198              * If we don't discard the uncollected replies, they just
    199              * sit in the XCB queue waiting forever. Sad.
    200              */
    201 err_discard:
    202             for (size_t j = i + 1; j < stop; j++)
    203                 if (from[j] != XCB_ATOM_NONE)
    204                     xcb_discard_reply(conn, cookies[j % SIZE].sequence);
    205             return false;
    206         }
    207     }
    208 
    209     return true;
    210 }
    211 
    212 bool
    213 adopt_atom(struct xkb_context *ctx, xcb_connection_t *conn, xcb_atom_t atom,
    214            xkb_atom_t *out)
    215 {
    216     return adopt_atoms(ctx, conn, &atom, out, 1);
    217 }
    218