582 lines
16 KiB
C
582 lines
16 KiB
C
/**
|
|
* Copyright © 2012 Intel Corporation
|
|
* Copyright © 2012 Ran Benita <ran234@gmail.com>
|
|
*
|
|
* 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.
|
|
*
|
|
* Author: Daniel Stone <daniel@fooishbar.org>
|
|
*/
|
|
|
|
/************************************************************
|
|
* Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this
|
|
* software and its documentation for any purpose and without
|
|
* fee is hereby granted, provided that the above copyright
|
|
* notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting
|
|
* documentation, and that the name of Silicon Graphics not be
|
|
* used in advertising or publicity pertaining to distribution
|
|
* of the software without specific prior written permission.
|
|
* Silicon Graphics makes no representation about the suitability
|
|
* of this software for any purpose. It is provided "as is"
|
|
* without any express or implied warranty.
|
|
*
|
|
* SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
|
* GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* ********************************************************/
|
|
|
|
#include "config.h"
|
|
|
|
#include "keymap.h"
|
|
#include "text.h"
|
|
|
|
XKB_EXPORT struct xkb_keymap *
|
|
xkb_keymap_ref(struct xkb_keymap *keymap)
|
|
{
|
|
keymap->refcnt++;
|
|
return keymap;
|
|
}
|
|
|
|
XKB_EXPORT void
|
|
xkb_keymap_unref(struct xkb_keymap *keymap)
|
|
{
|
|
if (!keymap || --keymap->refcnt > 0)
|
|
return;
|
|
|
|
if (keymap->keys) {
|
|
struct xkb_key *key;
|
|
xkb_keys_foreach(key, keymap) {
|
|
if (key->groups) {
|
|
for (unsigned i = 0; i < key->num_groups; i++) {
|
|
if (key->groups[i].levels) {
|
|
for (unsigned j = 0; j < XkbKeyNumLevels(key, i); j++)
|
|
if (key->groups[i].levels[j].num_syms > 1)
|
|
free(key->groups[i].levels[j].u.syms);
|
|
free(key->groups[i].levels);
|
|
}
|
|
}
|
|
free(key->groups);
|
|
}
|
|
}
|
|
free(keymap->keys);
|
|
}
|
|
if (keymap->types) {
|
|
for (unsigned i = 0; i < keymap->num_types; i++) {
|
|
free(keymap->types[i].entries);
|
|
free(keymap->types[i].level_names);
|
|
}
|
|
free(keymap->types);
|
|
}
|
|
free(keymap->sym_interprets);
|
|
free(keymap->key_aliases);
|
|
free(keymap->group_names);
|
|
free(keymap->keycodes_section_name);
|
|
free(keymap->symbols_section_name);
|
|
free(keymap->types_section_name);
|
|
free(keymap->compat_section_name);
|
|
xkb_context_unref(keymap->ctx);
|
|
free(keymap);
|
|
}
|
|
|
|
static const struct xkb_keymap_format_ops *
|
|
get_keymap_format_ops(enum xkb_keymap_format format)
|
|
{
|
|
static const struct xkb_keymap_format_ops *keymap_format_ops[] = {
|
|
[XKB_KEYMAP_FORMAT_TEXT_V1] = &text_v1_keymap_format_ops,
|
|
};
|
|
|
|
if ((int) format < 0 || (int) format >= (int) ARRAY_SIZE(keymap_format_ops))
|
|
return NULL;
|
|
|
|
return keymap_format_ops[(int) format];
|
|
}
|
|
|
|
XKB_EXPORT struct xkb_keymap *
|
|
xkb_keymap_new_from_names(struct xkb_context *ctx,
|
|
const struct xkb_rule_names *rmlvo_in,
|
|
enum xkb_keymap_compile_flags flags)
|
|
{
|
|
struct xkb_keymap *keymap;
|
|
struct xkb_rule_names rmlvo;
|
|
const enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1;
|
|
const struct xkb_keymap_format_ops *ops;
|
|
|
|
ops = get_keymap_format_ops(format);
|
|
if (!ops || !ops->keymap_new_from_names) {
|
|
log_err_func(ctx, "unsupported keymap format: %d\n", format);
|
|
return NULL;
|
|
}
|
|
|
|
if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
|
|
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
|
|
return NULL;
|
|
}
|
|
|
|
keymap = xkb_keymap_new(ctx, format, flags);
|
|
if (!keymap)
|
|
return NULL;
|
|
|
|
if (rmlvo_in)
|
|
rmlvo = *rmlvo_in;
|
|
else
|
|
memset(&rmlvo, 0, sizeof(rmlvo));
|
|
xkb_context_sanitize_rule_names(ctx, &rmlvo);
|
|
|
|
if (!ops->keymap_new_from_names(keymap, &rmlvo)) {
|
|
xkb_keymap_unref(keymap);
|
|
return NULL;
|
|
}
|
|
|
|
return keymap;
|
|
}
|
|
|
|
XKB_EXPORT struct xkb_keymap *
|
|
xkb_keymap_new_from_string(struct xkb_context *ctx,
|
|
const char *string,
|
|
enum xkb_keymap_format format,
|
|
enum xkb_keymap_compile_flags flags)
|
|
{
|
|
return xkb_keymap_new_from_buffer(ctx, string, strlen(string),
|
|
format, flags);
|
|
}
|
|
|
|
XKB_EXPORT struct xkb_keymap *
|
|
xkb_keymap_new_from_buffer(struct xkb_context *ctx,
|
|
const char *buffer, size_t length,
|
|
enum xkb_keymap_format format,
|
|
enum xkb_keymap_compile_flags flags)
|
|
{
|
|
struct xkb_keymap *keymap;
|
|
const struct xkb_keymap_format_ops *ops;
|
|
|
|
ops = get_keymap_format_ops(format);
|
|
if (!ops || !ops->keymap_new_from_string) {
|
|
log_err_func(ctx, "unsupported keymap format: %d\n", format);
|
|
return NULL;
|
|
}
|
|
|
|
if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
|
|
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
|
|
return NULL;
|
|
}
|
|
|
|
if (!buffer) {
|
|
log_err_func1(ctx, "no buffer specified\n");
|
|
return NULL;
|
|
}
|
|
|
|
keymap = xkb_keymap_new(ctx, format, flags);
|
|
if (!keymap)
|
|
return NULL;
|
|
|
|
if (!ops->keymap_new_from_string(keymap, buffer, length)) {
|
|
xkb_keymap_unref(keymap);
|
|
return NULL;
|
|
}
|
|
|
|
return keymap;
|
|
}
|
|
|
|
XKB_EXPORT struct xkb_keymap *
|
|
xkb_keymap_new_from_file(struct xkb_context *ctx,
|
|
FILE *file,
|
|
enum xkb_keymap_format format,
|
|
enum xkb_keymap_compile_flags flags)
|
|
{
|
|
struct xkb_keymap *keymap;
|
|
const struct xkb_keymap_format_ops *ops;
|
|
|
|
ops = get_keymap_format_ops(format);
|
|
if (!ops || !ops->keymap_new_from_file) {
|
|
log_err_func(ctx, "unsupported keymap format: %d\n", format);
|
|
return NULL;
|
|
}
|
|
|
|
if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
|
|
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
|
|
return NULL;
|
|
}
|
|
|
|
if (!file) {
|
|
log_err_func1(ctx, "no file specified\n");
|
|
return NULL;
|
|
}
|
|
|
|
keymap = xkb_keymap_new(ctx, format, flags);
|
|
if (!keymap)
|
|
return NULL;
|
|
|
|
if (!ops->keymap_new_from_file(keymap, file)) {
|
|
xkb_keymap_unref(keymap);
|
|
return NULL;
|
|
}
|
|
|
|
return keymap;
|
|
}
|
|
|
|
XKB_EXPORT char *
|
|
xkb_keymap_get_as_string(struct xkb_keymap *keymap,
|
|
enum xkb_keymap_format format)
|
|
{
|
|
const struct xkb_keymap_format_ops *ops;
|
|
|
|
if (format == XKB_KEYMAP_USE_ORIGINAL_FORMAT)
|
|
format = keymap->format;
|
|
|
|
ops = get_keymap_format_ops(format);
|
|
if (!ops || !ops->keymap_get_as_string) {
|
|
log_err_func(keymap->ctx, "unsupported keymap format: %d\n", format);
|
|
return NULL;
|
|
}
|
|
|
|
return ops->keymap_get_as_string(keymap);
|
|
}
|
|
|
|
/**
|
|
* Returns the total number of modifiers active in the keymap.
|
|
*/
|
|
XKB_EXPORT xkb_mod_index_t
|
|
xkb_keymap_num_mods(struct xkb_keymap *keymap)
|
|
{
|
|
return keymap->mods.num_mods;
|
|
}
|
|
|
|
/**
|
|
* Return the name for a given modifier.
|
|
*/
|
|
XKB_EXPORT const char *
|
|
xkb_keymap_mod_get_name(struct xkb_keymap *keymap, xkb_mod_index_t idx)
|
|
{
|
|
if (idx >= keymap->mods.num_mods)
|
|
return NULL;
|
|
|
|
return xkb_atom_text(keymap->ctx, keymap->mods.mods[idx].name);
|
|
}
|
|
|
|
/**
|
|
* Returns the index for a named modifier.
|
|
*/
|
|
XKB_EXPORT xkb_mod_index_t
|
|
xkb_keymap_mod_get_index(struct xkb_keymap *keymap, const char *name)
|
|
{
|
|
xkb_atom_t atom;
|
|
|
|
atom = xkb_atom_lookup(keymap->ctx, name);
|
|
if (atom == XKB_ATOM_NONE)
|
|
return XKB_MOD_INVALID;
|
|
|
|
return XkbModNameToIndex(&keymap->mods, atom, MOD_BOTH);
|
|
}
|
|
|
|
/**
|
|
* Return the total number of active groups in the keymap.
|
|
*/
|
|
XKB_EXPORT xkb_layout_index_t
|
|
xkb_keymap_num_layouts(struct xkb_keymap *keymap)
|
|
{
|
|
return keymap->num_groups;
|
|
}
|
|
|
|
/**
|
|
* Returns the name for a given group.
|
|
*/
|
|
XKB_EXPORT const char *
|
|
xkb_keymap_layout_get_name(struct xkb_keymap *keymap, xkb_layout_index_t idx)
|
|
{
|
|
if (idx >= keymap->num_group_names)
|
|
return NULL;
|
|
|
|
return xkb_atom_text(keymap->ctx, keymap->group_names[idx]);
|
|
}
|
|
|
|
/**
|
|
* Returns the index for a named layout.
|
|
*/
|
|
XKB_EXPORT xkb_layout_index_t
|
|
xkb_keymap_layout_get_index(struct xkb_keymap *keymap, const char *name)
|
|
{
|
|
xkb_atom_t atom = xkb_atom_lookup(keymap->ctx, name);
|
|
xkb_layout_index_t i;
|
|
|
|
if (atom == XKB_ATOM_NONE)
|
|
return XKB_LAYOUT_INVALID;
|
|
|
|
for (i = 0; i < keymap->num_group_names; i++)
|
|
if (keymap->group_names[i] == atom)
|
|
return i;
|
|
|
|
return XKB_LAYOUT_INVALID;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of layouts active for a particular key.
|
|
*/
|
|
XKB_EXPORT xkb_layout_index_t
|
|
xkb_keymap_num_layouts_for_key(struct xkb_keymap *keymap, xkb_keycode_t kc)
|
|
{
|
|
const struct xkb_key *key = XkbKey(keymap, kc);
|
|
|
|
if (!key)
|
|
return 0;
|
|
|
|
return key->num_groups;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of levels active for a particular key and layout.
|
|
*/
|
|
XKB_EXPORT xkb_level_index_t
|
|
xkb_keymap_num_levels_for_key(struct xkb_keymap *keymap, xkb_keycode_t kc,
|
|
xkb_layout_index_t layout)
|
|
{
|
|
const struct xkb_key *key = XkbKey(keymap, kc);
|
|
|
|
if (!key)
|
|
return 0;
|
|
|
|
layout = XkbWrapGroupIntoRange(layout, key->num_groups,
|
|
key->out_of_range_group_action,
|
|
key->out_of_range_group_number);
|
|
if (layout == XKB_LAYOUT_INVALID)
|
|
return 0;
|
|
|
|
return XkbKeyNumLevels(key, layout);
|
|
}
|
|
|
|
/**
|
|
* Return the total number of LEDs in the keymap.
|
|
*/
|
|
XKB_EXPORT xkb_led_index_t
|
|
xkb_keymap_num_leds(struct xkb_keymap *keymap)
|
|
{
|
|
return keymap->num_leds;
|
|
}
|
|
|
|
/**
|
|
* Returns the name for a given LED.
|
|
*/
|
|
XKB_EXPORT const char *
|
|
xkb_keymap_led_get_name(struct xkb_keymap *keymap, xkb_led_index_t idx)
|
|
{
|
|
if (idx >= keymap->num_leds)
|
|
return NULL;
|
|
|
|
return xkb_atom_text(keymap->ctx, keymap->leds[idx].name);
|
|
}
|
|
|
|
/**
|
|
* Returns the index for a named LED.
|
|
*/
|
|
XKB_EXPORT xkb_led_index_t
|
|
xkb_keymap_led_get_index(struct xkb_keymap *keymap, const char *name)
|
|
{
|
|
xkb_atom_t atom = xkb_atom_lookup(keymap->ctx, name);
|
|
xkb_led_index_t i;
|
|
const struct xkb_led *led;
|
|
|
|
if (atom == XKB_ATOM_NONE)
|
|
return XKB_LED_INVALID;
|
|
|
|
xkb_leds_enumerate(i, led, keymap)
|
|
if (led->name == atom)
|
|
return i;
|
|
|
|
return XKB_LED_INVALID;
|
|
}
|
|
|
|
XKB_EXPORT size_t
|
|
xkb_keymap_key_get_mods_for_level(struct xkb_keymap *keymap,
|
|
xkb_keycode_t kc,
|
|
xkb_layout_index_t layout,
|
|
xkb_level_index_t level,
|
|
xkb_mod_mask_t *masks_out,
|
|
size_t masks_size)
|
|
{
|
|
const struct xkb_key *key = XkbKey(keymap, kc);
|
|
if (!key)
|
|
return 0;
|
|
|
|
layout = XkbWrapGroupIntoRange(layout, key->num_groups,
|
|
key->out_of_range_group_action,
|
|
key->out_of_range_group_number);
|
|
if (layout == XKB_LAYOUT_INVALID)
|
|
return 0;
|
|
|
|
if (level >= XkbKeyNumLevels(key, layout))
|
|
return 0;
|
|
|
|
const struct xkb_key_type *type = key->groups[layout].type;
|
|
|
|
size_t count = 0;
|
|
|
|
/*
|
|
* If the active set of modifiers doesn't match any explicit entry of
|
|
* the key type, the resulting level is 0 (i.e. Level 1).
|
|
* So, if we are asked to find the modifiers for level==0, we can offer
|
|
* an ~infinite supply, which is not very workable.
|
|
* What we do instead, is special case the empty set of modifiers for
|
|
* this purpose. If the empty set isn't explicit mapped to a level, we
|
|
* take it to map to Level 1.
|
|
* This is almost always what we want. If applicable, given it priority
|
|
* over other ways to generate the level.
|
|
*/
|
|
if (level == 0) {
|
|
bool empty_mapped = false;
|
|
for (unsigned i = 0; i < type->num_entries && count < masks_size; i++)
|
|
if (entry_is_active(&type->entries[i]) &&
|
|
type->entries[i].mods.mask == 0) {
|
|
empty_mapped = true;
|
|
break;
|
|
}
|
|
if (!empty_mapped && count < masks_size) {
|
|
masks_out[count++] = 0;
|
|
}
|
|
}
|
|
|
|
/* Now search explicit mappings. */
|
|
for (unsigned i = 0; i < type->num_entries && count < masks_size; i++) {
|
|
if (entry_is_active(&type->entries[i]) &&
|
|
type->entries[i].level == level) {
|
|
masks_out[count++] = type->entries[i].mods.mask;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* As below, but takes an explicit layout/level rather than state.
|
|
*/
|
|
XKB_EXPORT int
|
|
xkb_keymap_key_get_syms_by_level(struct xkb_keymap *keymap,
|
|
xkb_keycode_t kc,
|
|
xkb_layout_index_t layout,
|
|
xkb_level_index_t level,
|
|
const xkb_keysym_t **syms_out)
|
|
{
|
|
const struct xkb_key *key = XkbKey(keymap, kc);
|
|
int num_syms;
|
|
|
|
if (!key)
|
|
goto err;
|
|
|
|
layout = XkbWrapGroupIntoRange(layout, key->num_groups,
|
|
key->out_of_range_group_action,
|
|
key->out_of_range_group_number);
|
|
if (layout == XKB_LAYOUT_INVALID)
|
|
goto err;
|
|
|
|
if (level >= XkbKeyNumLevels(key, layout))
|
|
goto err;
|
|
|
|
num_syms = key->groups[layout].levels[level].num_syms;
|
|
if (num_syms == 0)
|
|
goto err;
|
|
|
|
if (num_syms == 1)
|
|
*syms_out = &key->groups[layout].levels[level].u.sym;
|
|
else
|
|
*syms_out = key->groups[layout].levels[level].u.syms;
|
|
|
|
return num_syms;
|
|
|
|
err:
|
|
*syms_out = NULL;
|
|
return 0;
|
|
}
|
|
|
|
XKB_EXPORT xkb_keycode_t
|
|
xkb_keymap_min_keycode(struct xkb_keymap *keymap)
|
|
{
|
|
return keymap->min_key_code;
|
|
}
|
|
|
|
XKB_EXPORT xkb_keycode_t
|
|
xkb_keymap_max_keycode(struct xkb_keymap *keymap)
|
|
{
|
|
return keymap->max_key_code;
|
|
}
|
|
|
|
XKB_EXPORT void
|
|
xkb_keymap_key_for_each(struct xkb_keymap *keymap, xkb_keymap_key_iter_t iter,
|
|
void *data)
|
|
{
|
|
struct xkb_key *key;
|
|
|
|
xkb_keys_foreach(key, keymap)
|
|
iter(keymap, key->keycode, data);
|
|
}
|
|
|
|
XKB_EXPORT const char *
|
|
xkb_keymap_key_get_name(struct xkb_keymap *keymap, xkb_keycode_t kc)
|
|
{
|
|
const struct xkb_key *key = XkbKey(keymap, kc);
|
|
|
|
if (!key)
|
|
return NULL;
|
|
|
|
return xkb_atom_text(keymap->ctx, key->name);
|
|
}
|
|
|
|
XKB_EXPORT xkb_keycode_t
|
|
xkb_keymap_key_by_name(struct xkb_keymap *keymap, const char *name)
|
|
{
|
|
struct xkb_key *key;
|
|
xkb_atom_t atom;
|
|
|
|
atom = xkb_atom_lookup(keymap->ctx, name);
|
|
if (atom) {
|
|
xkb_atom_t ratom = XkbResolveKeyAlias(keymap, atom);
|
|
if (ratom)
|
|
atom = ratom;
|
|
}
|
|
if (!atom)
|
|
return XKB_KEYCODE_INVALID;
|
|
|
|
xkb_keys_foreach(key, keymap) {
|
|
if (key->name == atom)
|
|
return key->keycode;
|
|
}
|
|
|
|
return XKB_KEYCODE_INVALID;
|
|
}
|
|
|
|
/**
|
|
* Simple boolean specifying whether or not the key should repeat.
|
|
*/
|
|
XKB_EXPORT int
|
|
xkb_keymap_key_repeats(struct xkb_keymap *keymap, xkb_keycode_t kc)
|
|
{
|
|
const struct xkb_key *key = XkbKey(keymap, kc);
|
|
|
|
if (!key)
|
|
return 0;
|
|
|
|
return key->repeats;
|
|
}
|