/*
* Copyright © 2013 Ran Benita
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "x11-priv.h"
XKB_EXPORT 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;
}
XKB_EXPORT 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;
}
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;
}
bool
adopt_atoms(struct xkb_context *ctx, xcb_connection_t *conn,
const xcb_atom_t *from, xkb_atom_t *to, const size_t count)
{
enum { SIZE = 128 };
xcb_get_atom_name_cookie_t cookies[SIZE];
const size_t num_batches = ROUNDUP(count, SIZE) / SIZE;
/* 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++)
if (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) {
to[i] = XKB_ATOM_NONE;
continue;
}
reply = xcb_get_atom_name_reply(conn, cookies[i % SIZE], NULL);
if (!reply)
goto err_discard;
to[i] = xkb_atom_intern(ctx,
xcb_get_atom_name_name(reply),
xcb_get_atom_name_name_length(reply));
free(reply);
if (to[i] == XKB_ATOM_NONE)
goto err_discard;
continue;
/*
* 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;
}
}
return true;
}
bool
adopt_atom(struct xkb_context *ctx, xcb_connection_t *conn, xcb_atom_t atom,
xkb_atom_t *out)
{
return adopt_atoms(ctx, conn, &atom, out, 1);
}