Add new state API
Add new API to deal with xkb_state objects, including xkb_state_update_key, which runs the XKB action machinery internally to calculate what exactly happens to the state when a given key is pressed or released. The canonical way to deal with keys is now: struct xkb_state *state = xkb_state_new(xkb); xkb_keysym_t *syms; int num_syms; xkb_state_update_key(state, key, is_down); num_syms = xkb_key_get_syms(state, key, &syms); More state handling API, including a way to get at or ignore preserved modifiers, is on its way. Signed-off-by: Daniel Stone <daniel@fooishbar.org>master
parent
272ccbf473
commit
ecea0d71b2
|
@ -490,6 +490,11 @@ struct xkb_state {
|
|||
unsigned char compat_lookup_mods; /* effective mods + group */
|
||||
|
||||
unsigned short ptr_buttons; /* core pointer buttons */
|
||||
|
||||
int refcnt;
|
||||
void *filters;
|
||||
int num_filters;
|
||||
struct xkb_desc *xkb;
|
||||
};
|
||||
|
||||
#define XkbStateFieldFromRec(s) XkbBuildCoreState((s)->lookup_mods,(s)->group)
|
||||
|
@ -551,8 +556,42 @@ _X_EXPORT extern xkb_keysym_t
|
|||
xkb_string_to_keysym(const char *s);
|
||||
|
||||
_X_EXPORT unsigned int
|
||||
xkb_key_get_syms(struct xkb_desc *xkb, struct xkb_state *state,
|
||||
xkb_keycode_t key, xkb_keysym_t **syms_out);
|
||||
xkb_key_get_syms(struct xkb_state *state, xkb_keycode_t key,
|
||||
xkb_keysym_t **syms_out);
|
||||
|
||||
/**
|
||||
* @defgroup state XKB state objects
|
||||
* Creation, destruction and manipulation of keyboard state objects, * representing modifier and group state.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allocates a new XKB state object for use with the given keymap.
|
||||
*/
|
||||
_X_EXPORT struct xkb_state *
|
||||
xkb_state_new(struct xkb_desc *xkb);
|
||||
|
||||
/**
|
||||
* Adds a reference to a state object, so it will not be freed until unref.
|
||||
*/
|
||||
_X_EXPORT void
|
||||
xkb_state_ref(struct xkb_state *state);
|
||||
|
||||
/**
|
||||
* Unrefs and potentially deallocates a state object; the caller must not
|
||||
* use the state object after calling this.
|
||||
*/
|
||||
_X_EXPORT void
|
||||
xkb_state_unref(struct xkb_state *state);
|
||||
|
||||
/**
|
||||
* Updates a state object to reflect the given key being pressed or released.
|
||||
*/
|
||||
_X_EXPORT void
|
||||
xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, int down);
|
||||
|
||||
/** @} */
|
||||
|
||||
_XFUNCPROTOEND
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ libxkbcommon_la_SOURCES = \
|
|||
map.c \
|
||||
maprules.c \
|
||||
misc.c \
|
||||
state.c \
|
||||
text.c \
|
||||
xkb.c \
|
||||
xkballoc.h \
|
||||
|
|
|
@ -86,4 +86,10 @@ authorization from the authors.
|
|||
#define XkmLegalSection(m) (((m)&(~XkmKeymapLegal))==0)
|
||||
#define XkmSingleSection(m) (XkmLegalSection(m)&&(((m)&(~(m)+1))==(m)))
|
||||
|
||||
extern unsigned int xkb_key_get_group(struct xkb_state *state,
|
||||
xkb_keycode_t key);
|
||||
extern unsigned int xkb_key_get_level(struct xkb_state *state,
|
||||
xkb_keycode_t key,
|
||||
unsigned int group);
|
||||
|
||||
#endif /* _XKBCOMMONINT_H_ */
|
||||
|
|
34
src/map.c
34
src/map.c
|
@ -61,11 +61,11 @@
|
|||
/**
|
||||
* Returns the level to use for the given key and state, or -1 if invalid.
|
||||
*/
|
||||
static int
|
||||
xkb_key_get_level(struct xkb_desc *xkb, struct xkb_state *state,
|
||||
xkb_keycode_t key, unsigned int group)
|
||||
unsigned int
|
||||
xkb_key_get_level(struct xkb_state *state, xkb_keycode_t key,
|
||||
unsigned int group)
|
||||
{
|
||||
struct xkb_key_type *type = XkbKeyType(xkb, key, group);
|
||||
struct xkb_key_type *type = XkbKeyType(state->xkb, key, group);
|
||||
unsigned int active_mods = state->mods & type->mods.mask;
|
||||
int i;
|
||||
|
||||
|
@ -80,18 +80,17 @@ xkb_key_get_level(struct xkb_desc *xkb, struct xkb_state *state,
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the group to use for the given key and state, or -1 if invalid,
|
||||
* taking wrapping/clamping/etc into account.
|
||||
* Returns the group to use for the given key and state, taking
|
||||
* wrapping/clamping/etc into account.
|
||||
*/
|
||||
static int
|
||||
xkb_key_get_group(struct xkb_desc *xkb, struct xkb_state *state,
|
||||
xkb_keycode_t key)
|
||||
unsigned int
|
||||
xkb_key_get_group(struct xkb_state *state, xkb_keycode_t key)
|
||||
{
|
||||
unsigned int info = XkbKeyGroupInfo(xkb, key);
|
||||
unsigned int num_groups = XkbKeyNumGroups(xkb, key);
|
||||
unsigned int info = XkbKeyGroupInfo(state->xkb, key);
|
||||
unsigned int num_groups = XkbKeyNumGroups(state->xkb, key);
|
||||
int ret = state->group;
|
||||
|
||||
if (ret < XkbKeyNumGroups(xkb, key))
|
||||
if (ret < XkbKeyNumGroups(state->xkb, key))
|
||||
return ret;
|
||||
|
||||
switch (XkbOutOfRangeGroupAction(info)) {
|
||||
|
@ -135,19 +134,20 @@ err:
|
|||
* number of symbols pointed to in syms_out.
|
||||
*/
|
||||
unsigned int
|
||||
xkb_key_get_syms(struct xkb_desc *xkb, struct xkb_state *state,
|
||||
xkb_keycode_t key, xkb_keysym_t **syms_out)
|
||||
xkb_key_get_syms(struct xkb_state *state, xkb_keycode_t key,
|
||||
xkb_keysym_t **syms_out)
|
||||
{
|
||||
struct xkb_desc *xkb = state->xkb;
|
||||
int group;
|
||||
int level;
|
||||
|
||||
if (!xkb || !state || key < xkb->min_key_code || key > xkb->max_key_code)
|
||||
if (!state || key < xkb->min_key_code || key > xkb->max_key_code)
|
||||
return -1;
|
||||
|
||||
group = xkb_key_get_group(xkb, state, key);
|
||||
group = xkb_key_get_group(state, key);
|
||||
if (group == -1)
|
||||
goto err;
|
||||
level = xkb_key_get_level(xkb, state, key, group);
|
||||
level = xkb_key_get_level(state, key, group);
|
||||
if (level == -1)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -0,0 +1,463 @@
|
|||
/************************************************************
|
||||
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.
|
||||
|
||||
********************************************************/
|
||||
|
||||
/*
|
||||
* Copyright © 2012 Intel Corporation
|
||||
*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a bastardised version of xkbActions.c from the X server which
|
||||
* does not support, for the moment:
|
||||
* - AccessX sticky/debounce/etc (will come later)
|
||||
* - pointer keys (may come later)
|
||||
* - key redirects (unlikely)
|
||||
* - messages (very unlikely)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "xkbcommon/xkbcommon.h"
|
||||
#include "XKBcommonint.h"
|
||||
|
||||
struct xkb_filter {
|
||||
struct xkb_state *state;
|
||||
union xkb_action action;
|
||||
xkb_keycode_t keycode;
|
||||
uint32_t priv;
|
||||
int (*func)(struct xkb_filter *filter, xkb_keycode_t key, int down);
|
||||
int refcnt;
|
||||
struct xkb_filter *next;
|
||||
};
|
||||
|
||||
static union xkb_action *
|
||||
xkb_key_get_action(struct xkb_state *state, xkb_keycode_t key)
|
||||
{
|
||||
int group, level;
|
||||
|
||||
if (!XkbKeyHasActions(state->xkb, key) ||
|
||||
!XkbKeycodeInRange(state->xkb, key)) {
|
||||
static union xkb_action fake;
|
||||
memset(&fake, 0, sizeof(fake));
|
||||
fake.type = XkbSA_NoAction;
|
||||
return &fake;
|
||||
}
|
||||
|
||||
group = xkb_key_get_group(state, key);
|
||||
level = xkb_key_get_level(state, key, group);
|
||||
|
||||
return XkbKeyActionEntry(state->xkb, key, level, group);
|
||||
}
|
||||
|
||||
static struct xkb_filter *
|
||||
xkb_filter_new(struct xkb_state *state)
|
||||
{
|
||||
int i;
|
||||
int old_size = state->num_filters;
|
||||
struct xkb_filter *filters = state->filters;
|
||||
|
||||
for (i = 0; i < state->num_filters; i++) {
|
||||
if (filters[i].func)
|
||||
continue;
|
||||
filters[i].state = state;
|
||||
filters[i].refcnt = 1;
|
||||
return &filters[i];
|
||||
}
|
||||
|
||||
if (state->num_filters > 0)
|
||||
state->num_filters *= 2;
|
||||
else
|
||||
state->num_filters = 4;
|
||||
filters = realloc(filters, state->num_filters * sizeof(*filters));
|
||||
if (!filters) { /* WSGO */
|
||||
state->num_filters = old_size;
|
||||
return NULL;
|
||||
}
|
||||
state->filters = filters;
|
||||
memset(&filters[old_size], 0,
|
||||
(state->num_filters - old_size) * sizeof(*filters));
|
||||
|
||||
filters[old_size].state = state;
|
||||
filters[old_size].refcnt = 1;
|
||||
|
||||
return &filters[old_size];
|
||||
}
|
||||
|
||||
/***====================================================================***/
|
||||
|
||||
static int
|
||||
xkb_filter_group_set_func(struct xkb_filter *filter, xkb_keycode_t keycode,
|
||||
int down)
|
||||
{
|
||||
if (keycode != filter->keycode) {
|
||||
filter->action.group.flags &= ~XkbSA_ClearLocks;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (down) {
|
||||
filter->refcnt++;
|
||||
return 0;
|
||||
}
|
||||
else if (--filter->refcnt > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filter->action.group.flags & XkbSA_GroupAbsolute)
|
||||
filter->state->base_group = filter->action.group.group;
|
||||
else
|
||||
filter->state->base_group = -filter->action.group.group;
|
||||
if (filter->action.group.flags & XkbSA_ClearLocks)
|
||||
filter->state->locked_group = 0;
|
||||
|
||||
filter->func = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
xkb_filter_group_set_new(struct xkb_state *state, xkb_keycode_t keycode,
|
||||
union xkb_action *action)
|
||||
{
|
||||
struct xkb_filter *filter = xkb_filter_new(state);
|
||||
|
||||
if (!filter) /* WSGO */
|
||||
return -1;
|
||||
filter->keycode = keycode;
|
||||
filter->func = xkb_filter_group_set_func;
|
||||
filter->action = *action;
|
||||
|
||||
if (action->group.flags & XkbSA_GroupAbsolute) {
|
||||
filter->action.group.group = filter->state->base_group;
|
||||
filter->state->base_group = action->group.group;
|
||||
}
|
||||
else {
|
||||
filter->state->base_group += action->group.group;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
xkb_filter_mod_set_func(struct xkb_filter *filter, xkb_keycode_t keycode,
|
||||
int down)
|
||||
{
|
||||
if (keycode != filter->keycode) {
|
||||
filter->action.mods.flags &= ~XkbSA_ClearLocks;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (down) {
|
||||
filter->refcnt++;
|
||||
return 0;
|
||||
}
|
||||
else if (--filter->refcnt > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
filter->state->base_mods &= ~(filter->action.mods.mask);
|
||||
if (filter->action.mods.flags & XkbSA_ClearLocks)
|
||||
filter->state->locked_mods &= ~filter->action.mods.mask;
|
||||
|
||||
filter->func = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
xkb_filter_mod_set_new(struct xkb_state *state, xkb_keycode_t keycode,
|
||||
union xkb_action *action)
|
||||
{
|
||||
struct xkb_filter *filter = xkb_filter_new(state);
|
||||
|
||||
if (!filter) /* WSGO */
|
||||
return -1;
|
||||
filter->keycode = keycode;
|
||||
filter->func = xkb_filter_mod_set_func;
|
||||
filter->action = *action;
|
||||
|
||||
filter->state->base_mods |= action->mods.mask;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
xkb_filter_mod_lock_func(struct xkb_filter *filter, xkb_keycode_t keycode,
|
||||
int down)
|
||||
{
|
||||
if (keycode != filter->keycode)
|
||||
return 1;
|
||||
|
||||
if (down) {
|
||||
filter->refcnt++;
|
||||
return 0;
|
||||
}
|
||||
if (--filter->refcnt > 0)
|
||||
return 0;
|
||||
|
||||
filter->state->locked_mods &= ~filter->priv;
|
||||
filter->func = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
xkb_filter_mod_lock_new(struct xkb_state *state, xkb_keycode_t keycode,
|
||||
union xkb_action *action)
|
||||
{
|
||||
struct xkb_filter *filter = xkb_filter_new(state);
|
||||
|
||||
if (!filter) /* WSGO */
|
||||
return 0;
|
||||
|
||||
filter->keycode = keycode;
|
||||
filter->func = xkb_filter_mod_lock_func;
|
||||
filter->action = *action;
|
||||
filter->priv = state->locked_mods & action->mods.mask;
|
||||
state->locked_mods |= action->mods.mask;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum xkb_key_latch_state {
|
||||
NO_LATCH,
|
||||
LATCH_KEY_DOWN,
|
||||
LATCH_PENDING,
|
||||
};
|
||||
|
||||
static int
|
||||
xkb_filter_mod_latch_func(struct xkb_filter *filter, xkb_keycode_t keycode,
|
||||
int down)
|
||||
{
|
||||
enum xkb_key_latch_state latch = filter->priv;
|
||||
|
||||
if (down && latch == LATCH_PENDING) {
|
||||
/* If this is a new keypress and we're awaiting our single latched
|
||||
* keypress, then either break the latch if any random key is pressed,
|
||||
* or promote it to a lock or plain base set if it's the same
|
||||
* modifier. */
|
||||
union xkb_action *action = xkb_key_get_action(filter->state, keycode);
|
||||
if (action->type == XkbSA_LatchMods &&
|
||||
action->mods.flags == filter->action.mods.flags &&
|
||||
action->mods.mask == filter->action.mods.mask) {
|
||||
filter->action = *action;
|
||||
if (filter->action.mods.flags & XkbSA_LatchToLock) {
|
||||
filter->action.type = XkbSA_LockMods;
|
||||
filter->func = xkb_filter_mod_lock_func;
|
||||
filter->state->locked_mods |= filter->action.mods.mask;
|
||||
}
|
||||
else {
|
||||
filter->action.type = XkbSA_SetMods;
|
||||
filter->func = xkb_filter_mod_set_func;
|
||||
filter->state->base_mods |= filter->action.mods.mask;
|
||||
}
|
||||
filter->keycode = keycode;
|
||||
filter->state->latched_mods &= ~filter->action.mods.mask;
|
||||
/* XXX beep beep! */
|
||||
return 0;
|
||||
}
|
||||
else if (((1 << action->type) & XkbSA_BreakLatch)) {
|
||||
/* XXX: This may be totally broken, we might need to break the
|
||||
* latch in the next run after this press? */
|
||||
filter->state->latched_mods &= ~filter->action.mods.mask;
|
||||
filter->func = NULL;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (!down && keycode == filter->keycode) {
|
||||
/* Our key got released. If we've set it to clear locks, and we
|
||||
* currently have the same modifiers locked, then release them and
|
||||
* don't actually latch. Else we've actually hit the latching
|
||||
* stage, so set PENDING and move our modifier from base to
|
||||
* latched. */
|
||||
if (latch == NO_LATCH ||
|
||||
((filter->action.mods.flags & XkbSA_ClearLocks) &&
|
||||
(filter->state->locked_mods & filter->action.mods.mask) ==
|
||||
filter->action.mods.mask)) {
|
||||
/* XXX: We might be a bit overenthusiastic about clearing
|
||||
* mods other filters have set here? */
|
||||
if (latch == LATCH_PENDING)
|
||||
filter->state->latched_mods &= ~filter->action.mods.mask;
|
||||
else
|
||||
filter->state->base_mods &= ~filter->action.mods.mask;
|
||||
filter->state->locked_mods &= ~filter->action.mods.mask;
|
||||
filter->func = NULL;
|
||||
}
|
||||
else {
|
||||
latch = LATCH_PENDING;
|
||||
filter->state->base_mods &= ~filter->action.mods.mask;
|
||||
filter->state->latched_mods |= filter->action.mods.mask;
|
||||
/* XXX beep beep! */
|
||||
}
|
||||
}
|
||||
else if (down && latch == LATCH_KEY_DOWN) {
|
||||
/* Someone's pressed another key while we've still got the latching
|
||||
* key held down, so keep the base modifier state active (from
|
||||
* xkb_filter_mod_latch_new), but don't trip the latch, just clear
|
||||
* it as soon as the modifier gets released. */
|
||||
latch = NO_LATCH;
|
||||
}
|
||||
|
||||
filter->priv = latch;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
xkb_filter_mod_latch_new(struct xkb_state *state, xkb_keycode_t keycode,
|
||||
union xkb_action *action)
|
||||
{
|
||||
struct xkb_filter *filter = xkb_filter_new(state);
|
||||
enum xkb_key_latch_state latch = LATCH_KEY_DOWN;
|
||||
|
||||
if (!filter) /* WSGO */
|
||||
return -1;
|
||||
filter->keycode = keycode;
|
||||
filter->priv = latch;
|
||||
filter->func = xkb_filter_mod_latch_func;
|
||||
filter->action = *action;
|
||||
|
||||
filter->state->base_mods |= action->mods.mask;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies any relevant filters to the key, first from the list of filters
|
||||
* that are currently active, then if no filter has claimed the key, possibly
|
||||
* apply a new filter from the key action.
|
||||
*/
|
||||
static void
|
||||
xkb_filter_apply_all(struct xkb_state *state, xkb_keycode_t key, int down)
|
||||
{
|
||||
struct xkb_filter *filters = state->filters;
|
||||
union xkb_action *act = NULL;
|
||||
int send = 1;
|
||||
int i;
|
||||
|
||||
/* First run through all the currently active filters and see if any of
|
||||
* them have claimed this event. */
|
||||
for (i = 0; i < state->num_filters; i++) {
|
||||
if (!filters[i].func)
|
||||
continue;
|
||||
send &= (*filters[i].func)(&filters[i], key, down);
|
||||
}
|
||||
|
||||
if (!send || !down)
|
||||
return;
|
||||
|
||||
act = xkb_key_get_action(state, key);
|
||||
switch (act->type) {
|
||||
case XkbSA_SetMods:
|
||||
send = xkb_filter_mod_set_new(state, key, act);
|
||||
break;
|
||||
case XkbSA_LatchMods:
|
||||
send = xkb_filter_mod_latch_new(state, key, act);
|
||||
break;
|
||||
case XkbSA_LockMods:
|
||||
send = xkb_filter_mod_lock_new(state, key, act);
|
||||
break;
|
||||
case XkbSA_SetGroup:
|
||||
send = xkb_filter_group_set_new(state, key, act);
|
||||
break;
|
||||
#if 0
|
||||
case XkbSA_LatchGroup:
|
||||
send = xkb_filter_mod_latch_new(state, key, act);
|
||||
break;
|
||||
case XkbSA_LockGroup:
|
||||
send = xkb_filter_group_lock_new(state, key, act);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
struct xkb_state *
|
||||
xkb_state_new(struct xkb_desc *xkb)
|
||||
{
|
||||
struct xkb_state *ret;
|
||||
if (!xkb)
|
||||
return NULL;
|
||||
|
||||
ret = calloc(sizeof(*ret), 1);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->refcnt = 1;
|
||||
ret->xkb = xkb;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
xkb_state_unref(struct xkb_state *state)
|
||||
{
|
||||
state->refcnt--;
|
||||
assert(state->refcnt >= 0);
|
||||
if (state->refcnt == 0)
|
||||
return;
|
||||
free(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a particular key event, updates the state structure to reflect the
|
||||
* new modifiers.
|
||||
*/
|
||||
void
|
||||
xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, int down)
|
||||
{
|
||||
xkb_filter_apply_all(state, key, down);
|
||||
|
||||
state->mods = (state->base_mods | state->latched_mods | state->locked_mods);
|
||||
/* FIXME: Clamp/wrap locked_group */
|
||||
state->group = state->locked_group + state->base_group +
|
||||
state->latched_group;
|
||||
/* FIXME: Clamp/wrap effective group */
|
||||
|
||||
/* FIXME: Update LED state. */
|
||||
}
|
|
@ -4,7 +4,7 @@ LDADD = $(top_builddir)/src/libxkbcommon.la
|
|||
|
||||
TESTS_ENVIRONMENT = $(SHELL)
|
||||
|
||||
check_PROGRAMS = xkey filecomp namescomp rulescomp canonicalise
|
||||
check_PROGRAMS = xkey filecomp namescomp rulescomp canonicalise state
|
||||
TESTS = $(check_PROGRAMS:=.sh)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright © 2012 Intel Corporation
|
||||
*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xdefs.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <linux/input.h>
|
||||
#include "xkbcommon/xkbcommon.h"
|
||||
#include "xkbcomp/utils.h"
|
||||
#include "XKBcommonint.h"
|
||||
|
||||
/* Offset between evdev keycodes (where KEY_ESCAPE is 1), and the evdev XKB
|
||||
* keycode set (where ESC is 9). */
|
||||
#define EVDEV_OFFSET 8
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct xkb_rule_names rmlvo;
|
||||
struct xkb_desc *xkb;
|
||||
struct xkb_state *state;
|
||||
int num_syms;
|
||||
xkb_keysym_t *syms;
|
||||
|
||||
rmlvo.rules = "evdev";
|
||||
rmlvo.model = "pc104";
|
||||
rmlvo.layout = "us";
|
||||
rmlvo.variant = NULL;
|
||||
rmlvo.options = NULL;
|
||||
|
||||
xkb = xkb_compile_keymap_from_rules(&rmlvo);
|
||||
|
||||
if (!xkb) {
|
||||
fprintf(stderr, "Failed to compile keymap\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
state = xkb_state_new(xkb);
|
||||
assert(state->mods == 0);
|
||||
assert(state->group == 0);
|
||||
|
||||
/* LCtrl down */
|
||||
xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, 1);
|
||||
assert(state->mods & ControlMask);
|
||||
|
||||
/* LCtrl + RAlt down */
|
||||
xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, 1);
|
||||
assert(state->mods & Mod1Mask);
|
||||
assert(state->locked_mods == 0);
|
||||
assert(state->latched_mods == 0);
|
||||
|
||||
/* RAlt down */
|
||||
xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, 0);
|
||||
assert(!(state->mods & ControlMask) && (state->mods & Mod1Mask));
|
||||
|
||||
/* none down */
|
||||
xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, 0);
|
||||
assert(state->mods == 0);
|
||||
assert(state->group == 0);
|
||||
|
||||
/* Caps locked */
|
||||
xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 1);
|
||||
xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 0);
|
||||
assert(state->mods & LockMask);
|
||||
assert(state->mods == state->locked_mods);
|
||||
num_syms = xkb_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms);
|
||||
assert(num_syms == 1 && syms[0] == XK_Q);
|
||||
|
||||
/* Caps unlocked */
|
||||
xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 1);
|
||||
xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 0);
|
||||
assert(state->mods == 0);
|
||||
num_syms = xkb_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms);
|
||||
assert(num_syms == 1 && syms[0] == XK_q);
|
||||
|
||||
xkb_state_unref(state);
|
||||
xkb_free_keymap(xkb);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue