2 * Copyright © 2013 Ran Benita
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:
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
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.
29 xkb_x11_setup_xkb_extension(xcb_connection_t *conn,
30 uint16_t major_xkb_version,
31 uint16_t minor_xkb_version,
32 enum xkb_x11_setup_xkb_extension_flags flags,
33 uint16_t *major_xkb_version_out,
34 uint16_t *minor_xkb_version_out,
35 uint8_t *base_event_out,
36 uint8_t *base_error_out)
38 uint8_t base_event, base_error;
39 uint16_t server_major, server_minor;
41 if (flags & ~(XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS)) {
42 /* log_err_func(ctx, "unrecognized flags: %#x\n", flags); */
47 const xcb_query_extension_reply_t *reply =
48 xcb_get_extension_data(conn, &xcb_xkb_id);
50 /* log_err_func(ctx, "failed to query for XKB extension\n"); */
54 if (!reply->present) {
55 /* log_err_func(ctx, "failed to start using XKB extension: not available in server\n"); */
59 base_event = reply->first_event;
60 base_error = reply->first_error;
64 xcb_generic_error_t *error = NULL;
65 xcb_xkb_use_extension_cookie_t cookie =
66 xcb_xkb_use_extension(conn, major_xkb_version, minor_xkb_version);
67 xcb_xkb_use_extension_reply_t *reply =
68 xcb_xkb_use_extension_reply(conn, cookie, &error);
71 /* log_err_func(ctx, */
72 /* "failed to start using XKB extension: error code %d\n", */
73 /* error ? error->error_code : -1); */
78 if (!reply->supported) {
79 /* log_err_func(ctx, */
80 /* "failed to start using XKB extension: server doesn't support version %d.%d\n", */
81 /* major_xkb_version, minor_xkb_version); */
86 server_major = reply->serverMajor;
87 server_minor = reply->serverMinor;
93 * The XkbUseExtension() in libX11 has a *bunch* of legacy stuff, but
94 * it doesn't seem like any of it is useful to us.
97 if (major_xkb_version_out)
98 *major_xkb_version_out = server_major;
99 if (minor_xkb_version_out)
100 *minor_xkb_version_out = server_minor;
102 *base_event_out = base_event;
104 *base_error_out = base_error;
110 xkb_x11_get_core_keyboard_device_id(xcb_connection_t *conn)
113 xcb_xkb_get_device_info_cookie_t cookie =
114 xcb_xkb_get_device_info(conn, XCB_XKB_ID_USE_CORE_KBD,
116 xcb_xkb_get_device_info_reply_t *reply =
117 xcb_xkb_get_device_info_reply(conn, cookie, NULL);
122 device_id = reply->deviceID;
127 struct x11_atom_cache {
129 * Invalidate the cache based on the XCB connection.
130 * X11 atoms are actually not per connection or client, but per X server
131 * session. But better be safe just in case we survive an X server restart.
133 xcb_connection_t *conn;
141 static struct x11_atom_cache *
142 get_cache(struct xkb_context *ctx, xcb_connection_t *conn)
144 if (!ctx->x11_atom_cache) {
145 ctx->x11_atom_cache = calloc(1, sizeof(struct x11_atom_cache));
147 /* Can be NULL in case the malloc failed. */
148 struct x11_atom_cache *cache = ctx->x11_atom_cache;
149 if (cache && cache->conn != conn) {
157 x11_atom_interner_init(struct x11_atom_interner *interner,
158 struct xkb_context *ctx, xcb_connection_t *conn)
160 interner->had_error = false;
162 interner->conn = conn;
163 interner->num_pending = 0;
164 interner->num_copies = 0;
165 interner->num_escaped = 0;
169 x11_atom_interner_adopt_atom(struct x11_atom_interner *interner,
170 const xcb_atom_t atom, xkb_atom_t *out)
174 /* Can be NULL in case the malloc failed. */
175 struct x11_atom_cache *cache = get_cache(interner->ctx, interner->conn);
179 /* Already in the cache? */
181 for (size_t c = 0; c < cache->len; c++) {
182 if (cache->cache[c].from == atom) {
183 *out = cache->cache[c].to;
189 /* Already pending? */
190 for (size_t i = 0; i < interner->num_pending; i++) {
191 if (interner->pending[i].from == atom) {
192 if (interner->num_copies == ARRAY_SIZE(interner->copies)) {
193 x11_atom_interner_round_trip(interner);
197 size_t idx = interner->num_copies++;
198 interner->copies[idx].from = atom;
199 interner->copies[idx].out = out;
204 /* We have to send a GetAtomName request */
205 if (interner->num_pending == ARRAY_SIZE(interner->pending)) {
206 x11_atom_interner_round_trip(interner);
207 assert(interner->num_pending < ARRAY_SIZE(interner->pending));
209 size_t idx = interner->num_pending++;
210 interner->pending[idx].from = atom;
211 interner->pending[idx].out = out;
212 interner->pending[idx].cookie = xcb_get_atom_name(interner->conn, atom);
216 x11_atom_interner_adopt_atoms(struct x11_atom_interner *interner,
217 const xcb_atom_t *from, xkb_atom_t *to,
220 for (size_t i = 0; i < count; i++) {
221 x11_atom_interner_adopt_atom(interner, from[i], &to[i]);
225 void x11_atom_interner_round_trip(struct x11_atom_interner *interner) {
226 struct xkb_context *ctx = interner->ctx;
227 xcb_connection_t *conn = interner->conn;
229 /* Can be NULL in case the malloc failed. */
230 struct x11_atom_cache *cache = get_cache(ctx, conn);
232 for (size_t i = 0; i < interner->num_pending; i++) {
233 xcb_get_atom_name_reply_t *reply;
235 reply = xcb_get_atom_name_reply(conn, interner->pending[i].cookie, NULL);
237 interner->had_error = true;
240 xcb_atom_t x11_atom = interner->pending[i].from;
241 xkb_atom_t atom = xkb_atom_intern(ctx,
242 xcb_get_atom_name_name(reply),
243 xcb_get_atom_name_name_length(reply));
246 if (cache && cache->len < ARRAY_SIZE(cache->cache)) {
247 size_t idx = cache->len++;
248 cache->cache[idx].from = x11_atom;
249 cache->cache[idx].to = atom;
252 *interner->pending[i].out = atom;
254 for (size_t j = 0; j < interner->num_copies; j++) {
255 if (interner->copies[j].from == x11_atom)
256 *interner->copies[j].out = atom;
260 for (size_t i = 0; i < interner->num_escaped; i++) {
261 xcb_get_atom_name_reply_t *reply;
264 char **out = interner->escaped[i].out;
266 reply = xcb_get_atom_name_reply(conn, interner->escaped[i].cookie, NULL);
267 *interner->escaped[i].out = NULL;
269 interner->had_error = true;
271 length = xcb_get_atom_name_name_length(reply);
272 name = xcb_get_atom_name_name(reply);
274 *out = strndup(name, length);
277 interner->had_error = true;
279 XkbEscapeMapName(*out);
284 interner->num_pending = 0;
285 interner->num_copies = 0;
286 interner->num_escaped = 0;
290 x11_atom_interner_get_escaped_atom_name(struct x11_atom_interner *interner,
291 xcb_atom_t atom, char **out)
297 size_t idx = interner->num_escaped++;
298 /* There can only be a fixed number of calls to this function "in-flight",
299 * thus we assert this number. Increase the array size if this assert fails.
301 assert(idx < ARRAY_SIZE(interner->escaped));
302 interner->escaped[idx].out = out;
303 interner->escaped[idx].cookie = xcb_get_atom_name(interner->conn, atom);