mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
2771 lines
90 KiB
C
2771 lines
90 KiB
C
/* $Id: keyboard.c,v 1.9 2010/06/05 14:14:08 fredette Exp $ */
|
|
|
|
/* generic/keyboard.c - generic keyboard implementation support: */
|
|
|
|
/*
|
|
* Copyright (c) 2003 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: keyboard.c,v 1.9 2010/06/05 14:14:08 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include <tme/generic/keyboard.h>
|
|
#include <tme/hash.h>
|
|
#include <tme/misc.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
/* macros: */
|
|
|
|
/* the shortest possible time, in milliseconds, between two human
|
|
transitions on the same key: */
|
|
#define TME_KEYBOARD_SHORTEST_DOUBLE_MSEC (80)
|
|
|
|
/* input stage zero uses a slightly expanded set of event types: */
|
|
#define TME_KEYBOARD_EVENT_IN0_RELEASE_USER (0)
|
|
#define TME_KEYBOARD_EVENT_IN0_PRESS_USER (1)
|
|
#define TME_KEYBOARD_EVENT_IN0_RELEASE_AUTO (2)
|
|
#define TME_KEYBOARD_EVENT_IN0_PRESS_AUTO (3)
|
|
#if TME_KEYBOARD_EVENT_RELEASE != TME_KEYBOARD_EVENT_IN0_RELEASE_USER
|
|
#error "TME_KEYBOARD_EVENT_RELEASE must be 0"
|
|
#endif
|
|
#if TME_KEYBOARD_EVENT_PRESS != TME_KEYBOARD_EVENT_IN0_PRESS_USER
|
|
#error "TME_KEYBOARD_EVENT_PRESS must be 1"
|
|
#endif
|
|
|
|
/* this macro turns an input stage zero pressed value into the
|
|
corresponding release event type: */
|
|
#define TME_KEYBOARD_IN0_RELEASE_EVENT(pressed) ((pressed) ^ 1)
|
|
|
|
/* these macros evaluate to nonzero iff a keyval is pressed in the
|
|
different stages: */
|
|
#define TME_KEYBOARD_PRESSED_IN0(keysym) \
|
|
((keysym)->tme_keysym_state_in0_pressed)
|
|
#define TME_KEYBOARD_PRESSED_IN1(keysym) \
|
|
((keysym)->tme_keysym_state_in1_keymode.tme_keymode_state_pressed)
|
|
#define _TME_KEYBOARD_PRESSED_IN2(keysym, prev) \
|
|
((keysym)->tme_keysym_state_in2_pressed \
|
|
|| (!(keysym)->tme_keysym_state_in2_released \
|
|
&& prev))
|
|
#define TME_KEYBOARD_PRESSED_IN2(keysym) \
|
|
_TME_KEYBOARD_PRESSED_IN2(keysym, TME_KEYBOARD_PRESSED_IN1(keysym))
|
|
#define _TME_KEYBOARD_PRESSED_OUT0(keysym, prev)\
|
|
((keysym)->tme_keysym_state_out0_pressed \
|
|
|| (!(keysym)->tme_keysym_state_out0_released\
|
|
&& prev))
|
|
#define TME_KEYBOARD_PRESSED_OUT0(keysym) \
|
|
_TME_KEYBOARD_PRESSED_OUT0(keysym, TME_KEYBOARD_PRESSED_IN2(keysym))
|
|
#define TME_KEYBOARD_PRESSED_OUT1(keycode) \
|
|
((keycode)->tme_keycode_state_keymode.tme_keymode_state_pressed)
|
|
|
|
/* types: */
|
|
|
|
struct tme_keyboard_buffer_int;
|
|
struct tme_keysym_state;
|
|
|
|
/* keymode state: */
|
|
struct tme_keymode_state {
|
|
|
|
/* keys that may be autorepeating are kept on a linked list: */
|
|
struct tme_keymode_state *tme_keymode_state_next;
|
|
|
|
/* the state for this keysym. technically, since output stage one
|
|
deals in keycodes, this really should be a void * and point to
|
|
the keysym state for input stage one, and point to the keycode
|
|
state for output stage one.
|
|
|
|
however, avoiding the void * allows us to avoid some function
|
|
pointer casting, and in the output stage one case we don't care
|
|
which of the many keysyms that may map to the same keycode is
|
|
stored here - we just immediately grab the keycode state out of
|
|
that keysym: */
|
|
struct tme_keysym_state *tme_keymode_state_keysym;
|
|
|
|
/* the keymode mode: */
|
|
int tme_keymode_state_mode;
|
|
|
|
/* this is nonzero iff the key is pressed in the physical sense: */
|
|
int tme_keymode_state_pressed;
|
|
|
|
/* the last time this key was released: */
|
|
tme_uint32_t tme_keymode_state_last_release;
|
|
|
|
/* this is nonzero iff a genuine release should be ignored: */
|
|
int tme_keymode_state_ignore_release;
|
|
};
|
|
|
|
/* a keymode stage: */
|
|
struct tme_keymode_stage {
|
|
|
|
/* the global keymode: */
|
|
int tme_keymode_stage_global_mode;
|
|
|
|
/* the list of keymode states for keys that must not autorepeat: */
|
|
struct tme_keymode_state *tme_keymode_stage_no_autorepeats;
|
|
|
|
/* the next stage: */
|
|
int (*tme_keymode_stage_next) _TME_P((struct tme_keyboard_buffer_int *,
|
|
struct tme_keysym_state *,
|
|
tme_uint32_t));
|
|
};
|
|
|
|
/* keycode state: */
|
|
struct tme_keycode_state {
|
|
|
|
/* the keycode: */
|
|
tme_keyboard_keyval_t tme_keycode_state_keycode;
|
|
|
|
/* the keycode keymode state: */
|
|
struct tme_keymode_state tme_keycode_state_keymode;
|
|
};
|
|
|
|
/* keysym state. one of these is kept for every keysym controlled by
|
|
one or more input or output stages: */
|
|
struct tme_keysym_state {
|
|
|
|
/* this keysym: */
|
|
tme_keyboard_keyval_t tme_keysym_state_keysym;
|
|
|
|
/* input stage zero: */
|
|
|
|
/* if greater than TME_KEYBOARD_MODIFIER_NONE, this is the modifier
|
|
that this keysym is attached to in input stage zero: */
|
|
int tme_keysym_state_in0_modifier;
|
|
|
|
/* the keysym states for all keys attached to the same modifier in
|
|
input stage zero are kept on a linked list: */
|
|
struct tme_keysym_state *tme_keysym_state_in0_modifier_next;
|
|
|
|
/* this is nonzero iff the keysym is being pressed in input stage
|
|
zero. it's really either FALSE, or
|
|
TME_KEYBOARD_EVENT_IN0_PRESS_USER, or
|
|
TME_KEYBOARD_EVENT_IN0_PRESS_AUTO, which is why the last two are
|
|
nonzero: */
|
|
unsigned int tme_keysym_state_in0_pressed;
|
|
|
|
/* the last time this keysym was pressed in input stage zero. this
|
|
is a time in milliseconds: */
|
|
tme_uint32_t tme_keysym_state_in0_press_time;
|
|
|
|
/* input stage one: */
|
|
|
|
/* the input stage one keymode state: */
|
|
struct tme_keymode_state tme_keysym_state_in1_keymode;
|
|
|
|
/* input stage two: */
|
|
|
|
/* this is nonzero iff the keysym is being released in input stage
|
|
two: */
|
|
unsigned int tme_keysym_state_in2_released;
|
|
|
|
/* this is nonzero iff the keysym is being pressed in input stage
|
|
two: */
|
|
unsigned int tme_keysym_state_in2_pressed;
|
|
|
|
/* output stage zero: */
|
|
|
|
/* if non-NULL, this is the keycode that this keysym is mapped to in
|
|
output stage zero: */
|
|
struct tme_keycode_state *tme_keysym_state_out0_keycode;
|
|
|
|
/* if this keysym is not attached to any modifier on the output
|
|
stage zero, it may require that certain output side modifiers be
|
|
set or clear in order for the keycode to mean the given keysym: */
|
|
tme_keyboard_modifiers_t tme_keysym_state_out0_modifiers_set;
|
|
tme_keyboard_modifiers_t tme_keysym_state_out0_modifiers_clear;
|
|
|
|
/* iff greater than TME_KEYBOARD_MODIFIER_NONE, this is the modifier
|
|
that this keysym is attached to in output stage zero: */
|
|
int tme_keysym_state_out0_modifier;
|
|
|
|
/* the keysym states for all keys attached to the same modifier in
|
|
output stage zero are kept on a linked list: */
|
|
struct tme_keysym_state *tme_keysym_state_out0_modifier_next;
|
|
|
|
/* this is nonzero iff the keysym is being released in output stage
|
|
zero: */
|
|
unsigned int tme_keysym_state_out0_released;
|
|
|
|
/* this is nonzero iff the keysym is being pressed in output stage
|
|
zero: */
|
|
unsigned int tme_keysym_state_out0_pressed;
|
|
|
|
/* if this keysym is pressed in the output stage zero but required
|
|
output stage zero modifier changes to be so, this is the list of
|
|
those changes. since most keyboards generate the same keysym
|
|
using the same modifier(s), this list will usually be empty: */
|
|
struct tme_keysym_state **tme_keysym_state_out0_keysyms;
|
|
unsigned int *tme_keysym_state_out0_press_flags;
|
|
|
|
/* output stage one: */
|
|
|
|
/* this is nonzero iff the next release seen by output stage one
|
|
will not affect the output modifiers mask: */
|
|
int tme_keysym_state_out1_ignore_release;
|
|
};
|
|
|
|
/* keyboard macros are necessary because it's almost certain that the
|
|
keyboard you want to emulate has keysyms that your keyboard doesn't
|
|
have. one keyboard macro takes a *sequence* of one or more pressed
|
|
keysyms to a *set* of one or more released and pressed keysyms.
|
|
|
|
all keysym macros are kept in a single tree, where a single branch
|
|
represents the next keysym in the sequences for one or more macros.
|
|
|
|
a node in the macros tree is active iff the keysyms on the path
|
|
from the root node have been pressed in sequence and remain
|
|
pressed. if there are any macros at all, the root node is always
|
|
active: */
|
|
struct tme_keyboard_macro {
|
|
|
|
/* a pointer up to our parent node, and the keysym on the branch
|
|
from our parent to us. for the root node, these are NULL and
|
|
TME_KEYBOARD_KEYVAL_UNDEF, respectively: */
|
|
struct tme_keyboard_macro *tme_keyboard_macro_parent;
|
|
tme_keyboard_keyval_t tme_keyboard_macro_keysym;
|
|
|
|
/* all active nodes are on a list. the root node is always active,
|
|
and it must be the last node on this list - making the is-active
|
|
test for all other nodes as simple as testing this pointer
|
|
against NULL: */
|
|
struct tme_keyboard_macro *tme_keyboard_macro_active_next;
|
|
|
|
/* non-leaf nodes branch out by keysym: */
|
|
tme_hash_t tme_keyboard_macro_branches;
|
|
|
|
/* leaf nodes contain the set of keysyms that the recognized
|
|
sequence maps to. in addition to presses of one or more new
|
|
keysyms, this will normally include releases of some or all of
|
|
the keysyms in the original sequence: */
|
|
unsigned int tme_keyboard_macro_length;
|
|
struct tme_keysym_state **tme_keyboard_macro_keysyms;
|
|
unsigned int *tme_keyboard_macro_press_flags;
|
|
};
|
|
|
|
/* an internal keyboard buffer: */
|
|
struct tme_keyboard_buffer_int {
|
|
|
|
/* the public keyboard buffer. this must be first: */
|
|
struct tme_keyboard_buffer tme_keyboard_buffer;
|
|
#define tme_keyboard_buffer_int_size tme_keyboard_buffer.tme_keyboard_buffer_size
|
|
#define tme_keyboard_buffer_int_head tme_keyboard_buffer.tme_keyboard_buffer_head
|
|
#define tme_keyboard_buffer_int_tail tme_keyboard_buffer.tme_keyboard_buffer_tail
|
|
#define tme_keyboard_buffer_int_events tme_keyboard_buffer.tme_keyboard_buffer_events
|
|
#define tme_keyboard_buffer_int_log_handle tme_keyboard_buffer.tme_keyboard_buffer_log_handle
|
|
|
|
/* the keysyms state, common to all stages: */
|
|
tme_hash_t tme_keyboard_buffer_int_keysyms_state;
|
|
|
|
/* input stage zero: */
|
|
|
|
/* this is nonzero iff input stage zero has modifier information,
|
|
and it's actually the mask of modifiers that we have keysyms for: */
|
|
unsigned int tme_keyboard_buffer_int_in0_have_modifiers;
|
|
|
|
/* the lists of keysyms that are attached to input stage zero
|
|
modifiers: */
|
|
struct tme_keysym_state *tme_keyboard_buffer_int_in0_modkeys[TME_KEYBOARD_MODIFIER_MAX + 1];
|
|
|
|
/* the current input stage zero modifiers mask: */
|
|
tme_keyboard_modifiers_t tme_keyboard_buffer_int_in0_modifiers;
|
|
|
|
/* the current input stage zero pressed keycodes, mapped to their
|
|
corresponding struct tme_keysym_states: */
|
|
tme_hash_t tme_keyboard_buffer_int_in0_keycodes;
|
|
|
|
/* input stage one: */
|
|
|
|
/* the input stage one keymode stage: */
|
|
struct tme_keymode_stage tme_keyboard_buffer_int_in1_keymode_stage;
|
|
|
|
/* input stage two: */
|
|
|
|
/* this is NULL iff input stage two is a passthrough, else this is
|
|
the list of active nodes in the input stage two keysym macros
|
|
tree: */
|
|
struct tme_keyboard_macro *tme_keyboard_buffer_int_in2_macros_active;
|
|
|
|
/* the root of the input stage two keysym macros tree: */
|
|
struct tme_keyboard_macro tme_keyboard_buffer_int_in2_macros_root;
|
|
|
|
/* output stage zero: */
|
|
|
|
/* this is nonzero iff output stage zero is just a passthrough: */
|
|
unsigned int tme_keyboard_buffer_int_out0_passthrough;
|
|
|
|
/* the output stage zero keycodes: */
|
|
tme_hash_t tme_keyboard_buffer_int_out0_keycodes;
|
|
|
|
/* this is nonzero iff the output stage zero lock modifier is to be
|
|
treated as caps lock: */
|
|
int tme_keyboard_buffer_int_out0_lock_is_caps;
|
|
|
|
/* any output stage zero modifier that the Num_Lock keysym is
|
|
attached to: */
|
|
int tme_keyboard_buffer_int_out0_mod_num_lock;
|
|
|
|
/* the lists of keysyms that are output stage zero modifiers: */
|
|
struct tme_keysym_state *tme_keyboard_buffer_int_out0_modkeys[TME_KEYBOARD_MODIFIER_MAX + 1];
|
|
|
|
/* the current output stage zero modifiers mask: */
|
|
tme_keyboard_modifiers_t tme_keyboard_buffer_int_out0_modifiers;
|
|
|
|
/* output stage one: */
|
|
|
|
/* the output stage one keymode stage: */
|
|
struct tme_keymode_stage tme_keyboard_buffer_int_out1_keymode_stage;
|
|
};
|
|
|
|
/* prototypes: */
|
|
static int _tme_keyboard_buffer_in2 _TME_P((struct tme_keyboard_buffer_int *,
|
|
struct tme_keysym_state *,
|
|
tme_uint32_t));
|
|
static int _tme_keyboard_buffer_out1_bottom _TME_P((struct tme_keyboard_buffer_int *,
|
|
struct tme_keysym_state *,
|
|
tme_uint32_t));
|
|
|
|
/* this is for debugging only: */
|
|
#if 0
|
|
static void
|
|
_tme_keyboard_debug(const struct tme_keyboard_buffer_int *buffer,
|
|
const char *stage,
|
|
tme_keyboard_keyval_t keyval,
|
|
int is_press,
|
|
tme_uint32_t event_time)
|
|
{
|
|
struct tme_log_handle *handle;
|
|
const char *string;
|
|
extern const char *_tme_gtk_keyboard_keyval_name _TME_P((tme_keyboard_keyval_t));
|
|
|
|
handle = buffer->tme_keyboard_buffer_int_log_handle;
|
|
if (handle == NULL) {
|
|
return;
|
|
}
|
|
|
|
string = _tme_gtk_keyboard_keyval_name(keyval);
|
|
if (string == NULL) {
|
|
string = "???";
|
|
}
|
|
|
|
tme_log(handle, 100, TME_OK,
|
|
(handle,
|
|
"%s event: time %lu key %lu (%s) %s",
|
|
stage,
|
|
(unsigned long) event_time,
|
|
(unsigned long) keyval,
|
|
string,
|
|
(is_press
|
|
? "press"
|
|
: "release")));
|
|
}
|
|
#else
|
|
#define _tme_keyboard_debug(b, s, k, p, t) \
|
|
do { } while(/* CONSTCOND */ 0)
|
|
#endif
|
|
|
|
/* this creates a new keyboard buffer: */
|
|
struct tme_keyboard_buffer *
|
|
tme_keyboard_buffer_new(unsigned int size)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
struct tme_keymode_stage *stage;
|
|
struct tme_keyboard_macro *root;
|
|
int modifier;
|
|
|
|
/* round the buffer size up to a power of two: */
|
|
if (size & (size - 1)) {
|
|
do {
|
|
size &= (size - 1);
|
|
} while (size & (size - 1));
|
|
size <<= 1;
|
|
}
|
|
|
|
/* allocate the buffer: */
|
|
buffer = tme_new0(struct tme_keyboard_buffer_int, 1);
|
|
|
|
/* set the buffer size: */
|
|
buffer->tme_keyboard_buffer_int_size = size;
|
|
|
|
/* set the head and tail pointers: */
|
|
buffer->tme_keyboard_buffer_int_head = 0;
|
|
buffer->tme_keyboard_buffer_int_tail = 0;
|
|
|
|
/* allocate the buffer events: */
|
|
buffer->tme_keyboard_buffer_int_events
|
|
= tme_new(struct tme_keyboard_event, size);
|
|
|
|
/* for now there is no log handle: */
|
|
buffer->tme_keyboard_buffer_int_log_handle = NULL;
|
|
|
|
/* create the common keysyms state: */
|
|
buffer->tme_keyboard_buffer_int_keysyms_state
|
|
= tme_hash_new(tme_direct_hash,
|
|
tme_direct_compare,
|
|
(tme_hash_data_t) NULL);
|
|
|
|
/* input stage zero begins with no modifiers: */
|
|
buffer->tme_keyboard_buffer_int_in0_have_modifiers = FALSE;
|
|
|
|
/* initialize the input stage zero modifier keys lists: */
|
|
for (modifier = 0;
|
|
modifier <= TME_KEYBOARD_MODIFIER_MAX;
|
|
modifier++) {
|
|
buffer->tme_keyboard_buffer_int_in0_modkeys[modifier] = NULL;
|
|
}
|
|
|
|
/* initialize the input stage zero modifiers mask: */
|
|
buffer->tme_keyboard_buffer_int_in0_modifiers = 0;
|
|
|
|
/* initialize the input stage one keycodes hash: */
|
|
buffer->tme_keyboard_buffer_int_in0_keycodes
|
|
= tme_hash_new(tme_direct_hash,
|
|
tme_direct_compare,
|
|
(tme_hash_data_t) NULL);
|
|
|
|
/* initialize the input stage one keymode stage: */
|
|
stage = &buffer->tme_keyboard_buffer_int_in1_keymode_stage;
|
|
stage->tme_keymode_stage_global_mode = 0;
|
|
stage->tme_keymode_stage_no_autorepeats = NULL;
|
|
stage->tme_keymode_stage_next = _tme_keyboard_buffer_in2;
|
|
|
|
/* input stage two begins with no macros, so not even the root of
|
|
the macros tree is active: */
|
|
buffer->tme_keyboard_buffer_int_in2_macros_active = NULL;
|
|
|
|
/* create the root of the input stage two keysym macros tree: */
|
|
root = &buffer->tme_keyboard_buffer_int_in2_macros_root;
|
|
root->tme_keyboard_macro_parent = NULL;
|
|
root->tme_keyboard_macro_keysym = TME_KEYBOARD_KEYVAL_UNDEF;
|
|
root->tme_keyboard_macro_active_next = NULL;
|
|
root->tme_keyboard_macro_branches
|
|
= tme_hash_new(tme_direct_hash,
|
|
tme_direct_compare,
|
|
(tme_hash_data_t) NULL);
|
|
|
|
/* output stage zero begins as a passthrough: */
|
|
buffer->tme_keyboard_buffer_int_out0_passthrough = TRUE;
|
|
|
|
/* initialize the output stage zero keycodes: */
|
|
buffer->tme_keyboard_buffer_int_out0_keycodes
|
|
= tme_hash_new(tme_direct_hash,
|
|
tme_direct_compare,
|
|
(tme_hash_data_t) NULL);
|
|
|
|
/* the output stage zero lock modifier is assumed to be a Shift lock: */
|
|
buffer->tme_keyboard_buffer_int_out0_lock_is_caps
|
|
= FALSE;
|
|
|
|
/* initialize the output stage zero modifier that the Num_Lock
|
|
keysym is attached to: */
|
|
buffer->tme_keyboard_buffer_int_out0_mod_num_lock
|
|
= TME_KEYBOARD_MODIFIER_NONE;
|
|
|
|
/* initialize the output stage zero modifier keys lists: */
|
|
for (modifier = 0;
|
|
modifier <= TME_KEYBOARD_MODIFIER_MAX;
|
|
modifier++) {
|
|
buffer->tme_keyboard_buffer_int_out0_modkeys[modifier] = NULL;
|
|
}
|
|
|
|
/* initialize the output stage zero modifiers mask: */
|
|
buffer->tme_keyboard_buffer_int_out0_modifiers = 0;
|
|
|
|
/* initialize the output stage one keymode stage: */
|
|
stage = &buffer->tme_keyboard_buffer_int_out1_keymode_stage;
|
|
stage->tme_keymode_stage_global_mode = 0;
|
|
stage->tme_keymode_stage_no_autorepeats = NULL;
|
|
stage->tme_keymode_stage_next = _tme_keyboard_buffer_out1_bottom;
|
|
|
|
/* done: */
|
|
return (&buffer->tme_keyboard_buffer);
|
|
}
|
|
|
|
/* this destroys an entry in the common keysyms state: */
|
|
static void
|
|
_tme_keysym_state_destroy(tme_hash_data_t __keysym,
|
|
tme_hash_data_t _keysym,
|
|
void *_junk)
|
|
{
|
|
struct tme_keysym_state *keysym;
|
|
|
|
/* recover the keysym state: */
|
|
keysym = (struct tme_keysym_state *) _keysym;
|
|
|
|
/* if this keysym state has output stage zero keysym changes, free
|
|
them: */
|
|
if (keysym->tme_keysym_state_out0_keysyms != NULL) {
|
|
tme_free(keysym->tme_keysym_state_out0_keysyms);
|
|
tme_free(keysym->tme_keysym_state_out0_press_flags);
|
|
}
|
|
|
|
/* free the state itself: */
|
|
tme_free(keysym);
|
|
}
|
|
|
|
/* this recursively destroys the input stage two keysym macros tree: */
|
|
static void
|
|
_tme_keyboard_macro_destroy(tme_hash_data_t _keysym,
|
|
tme_hash_data_t _macro,
|
|
void *_junk)
|
|
{
|
|
struct tme_keyboard_macro *macro;
|
|
|
|
/* get this macro: */
|
|
macro = (struct tme_keyboard_macro *) _macro;
|
|
|
|
/* if this is a leaf node: */
|
|
if (macro->tme_keyboard_macro_branches == NULL) {
|
|
|
|
/* free the keysyms and flags: */
|
|
tme_free(macro->tme_keyboard_macro_keysyms);
|
|
tme_free(macro->tme_keyboard_macro_press_flags);
|
|
}
|
|
|
|
/* otherwise, recurse: */
|
|
else {
|
|
tme_hash_foreach(macro->tme_keyboard_macro_branches,
|
|
_tme_keyboard_macro_destroy,
|
|
NULL);
|
|
tme_hash_destroy(macro->tme_keyboard_macro_branches);
|
|
}
|
|
|
|
/* free this tree node: */
|
|
tme_free(macro);
|
|
}
|
|
|
|
/* this destroys an entry in the output stage zero keycodes state: */
|
|
static void
|
|
_tme_keycode_state_destroy(tme_hash_data_t __keycode,
|
|
tme_hash_data_t _keycode,
|
|
void *_junk)
|
|
{
|
|
struct tme_keycode_state *keycode;
|
|
|
|
/* recover the keycode state: */
|
|
keycode = (struct tme_keycode_state *) _keycode;
|
|
|
|
/* free the state itself: */
|
|
tme_free(keycode);
|
|
}
|
|
|
|
/* this destroys a keyboard buffer: */
|
|
void
|
|
tme_keyboard_buffer_destroy(struct tme_keyboard_buffer *_buffer)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* free the events: */
|
|
tme_free(buffer->tme_keyboard_buffer_int_events);
|
|
|
|
/* destroy the common keysyms state: */
|
|
tme_hash_foreach(buffer->tme_keyboard_buffer_int_keysyms_state,
|
|
_tme_keysym_state_destroy,
|
|
NULL);
|
|
tme_hash_destroy(buffer->tme_keyboard_buffer_int_keysyms_state);
|
|
|
|
/* destroy the input stage two keysym macros tree: */
|
|
tme_hash_foreach(buffer->tme_keyboard_buffer_int_in2_macros_root.tme_keyboard_macro_branches,
|
|
_tme_keyboard_macro_destroy,
|
|
NULL);
|
|
tme_hash_destroy(buffer->tme_keyboard_buffer_int_in2_macros_root.tme_keyboard_macro_branches);
|
|
|
|
/* destroy the output stage zero keycodes: */
|
|
tme_hash_foreach(buffer->tme_keyboard_buffer_int_out0_keycodes,
|
|
_tme_keycode_state_destroy,
|
|
NULL);
|
|
tme_hash_destroy(buffer->tme_keyboard_buffer_int_out0_keycodes);
|
|
|
|
/* destroy the buffer itself: */
|
|
tme_free(buffer);
|
|
}
|
|
|
|
/* this gets the state for a keysym, creating a new state if one
|
|
doesn't exists yet: */
|
|
static struct tme_keysym_state *
|
|
_tme_keysym_state_get(struct tme_keyboard_buffer_int *buffer,
|
|
tme_keyboard_keyval_t _keysym)
|
|
{
|
|
struct tme_keysym_state *keysym;
|
|
|
|
/* look up the state for this keysym: */
|
|
keysym
|
|
= ((struct tme_keysym_state *)
|
|
tme_hash_lookup(buffer->tme_keyboard_buffer_int_keysyms_state,
|
|
tme_keyboard_hash_data_from_keyval(_keysym)));
|
|
|
|
/* if the state doesn't exist, allocate it: */
|
|
if (keysym == NULL) {
|
|
keysym = tme_new0(struct tme_keysym_state, 1);
|
|
|
|
/* initialize all fields that might not be properly initialized as
|
|
all-bits-zero: */
|
|
keysym->tme_keysym_state_keysym = _keysym;
|
|
keysym->tme_keysym_state_in0_modifier = TME_KEYBOARD_MODIFIER_NONE;
|
|
keysym->tme_keysym_state_in1_keymode.tme_keymode_state_keysym = keysym;
|
|
keysym->tme_keysym_state_out0_keycode = NULL;
|
|
keysym->tme_keysym_state_out0_modifier = TME_KEYBOARD_MODIFIER_NONE;
|
|
keysym->tme_keysym_state_out0_keysyms = NULL;
|
|
keysym->tme_keysym_state_out0_press_flags = NULL;
|
|
|
|
/* insert this state into the hash: */
|
|
tme_hash_insert(buffer->tme_keyboard_buffer_int_keysyms_state,
|
|
tme_keyboard_hash_data_from_keyval(_keysym),
|
|
(tme_hash_data_t) keysym);
|
|
}
|
|
|
|
/* done: */
|
|
return (keysym);
|
|
}
|
|
|
|
/* this changes the set of keysyms that are attached to an input stage
|
|
zero modifier: */
|
|
int
|
|
tme_keyboard_buffer_in_modifier(struct tme_keyboard_buffer *_buffer,
|
|
int modifier,
|
|
const tme_keyboard_keyval_t *modkeys)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
struct tme_keysym_state *mod_keysym, **_mod_keysym;
|
|
tme_keyboard_keyval_t keysym;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* this must be a valid modifier: */
|
|
assert (modifier > TME_KEYBOARD_MODIFIER_NONE
|
|
&& modifier <= TME_KEYBOARD_MODIFIER_MAX);
|
|
|
|
/* remove all currently attached keysyms from this modifier: */
|
|
for (mod_keysym = buffer->tme_keyboard_buffer_int_in0_modkeys[modifier];
|
|
mod_keysym != NULL;
|
|
mod_keysym = mod_keysym->tme_keysym_state_in0_modifier_next) {
|
|
mod_keysym->tme_keysym_state_in0_modifier
|
|
= TME_KEYBOARD_MODIFIER_NONE;
|
|
}
|
|
|
|
/* attach all of these new keysyms to this modifier: */
|
|
_mod_keysym = &buffer->tme_keyboard_buffer_int_in0_modkeys[modifier];
|
|
for (; (keysym = *(modkeys++)) != TME_KEYBOARD_KEYVAL_UNDEF; ) {
|
|
mod_keysym = _tme_keysym_state_get(buffer, keysym);
|
|
mod_keysym->tme_keysym_state_in0_modifier = modifier;
|
|
*_mod_keysym = mod_keysym;
|
|
_mod_keysym = &mod_keysym->tme_keysym_state_in0_modifier_next;
|
|
}
|
|
*_mod_keysym = NULL;
|
|
|
|
/* input stage zero now has modifier information: */
|
|
buffer->tme_keyboard_buffer_int_in0_have_modifiers
|
|
|= (1 << modifier);
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this changes a keysym's input stage one keymode: */
|
|
int
|
|
tme_keyboard_buffer_in_mode(struct tme_keyboard_buffer *_buffer,
|
|
tme_keyboard_keyval_t _keysym, int mode)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
struct tme_keysym_state *keysym;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* there's no such thing as a global input keymode: */
|
|
assert (_keysym != TME_KEYBOARD_KEYVAL_UNDEF);
|
|
|
|
/* TME_KEYBOARD_MODE_UNLOCK and TME_KEYBOARD_MODE_LOCK cannot be
|
|
combined with any other bits: */
|
|
if ((mode
|
|
& (TME_KEYBOARD_MODE_UNLOCK
|
|
| TME_KEYBOARD_MODE_LOCK))
|
|
&& (mode
|
|
& (mode - 1))) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* none of the TME_KEYBOARD_MODE_FLAG_NO_AUTOREPEATS,
|
|
TME_KEYBOARD_MODE_FLAG_NO_RELEASES, and
|
|
TME_KEYBOARD_MODE_FLAG_LOCK_SOFT flags can be set
|
|
without TME_KEYBOARD_MODE_PASSTHROUGH: */
|
|
if ((mode
|
|
& (TME_KEYBOARD_MODE_FLAG_NO_AUTOREPEATS
|
|
| TME_KEYBOARD_MODE_FLAG_NO_RELEASES
|
|
| TME_KEYBOARD_MODE_FLAG_LOCK_SOFT))
|
|
&& !(mode
|
|
& TME_KEYBOARD_MODE_PASSTHROUGH)) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* you cannot specify that an input key must not release, or
|
|
that it soft locks: */
|
|
if (mode
|
|
& (TME_KEYBOARD_MODE_FLAG_NO_RELEASES
|
|
| TME_KEYBOARD_MODE_FLAG_LOCK_SOFT)) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* look up this keysym and set the input stage one mode: */
|
|
keysym = _tme_keysym_state_get(buffer, _keysym);
|
|
keysym->tme_keysym_state_in1_keymode.tme_keymode_state_mode = mode;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this adds an input stage two keysym macro: */
|
|
int
|
|
tme_keyboard_buffer_in_macro(struct tme_keyboard_buffer *_buffer,
|
|
const tme_keyboard_keyval_t *keysyms_lhs,
|
|
const tme_keyboard_keyval_t *keysyms_rhs)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
unsigned int count_lhs, count_rhs;
|
|
unsigned int keysym_i, keysym_j, keysym_count;
|
|
tme_keyboard_keyval_t keysym;
|
|
struct tme_keysym_state **keysyms;
|
|
unsigned int *press_flags;
|
|
int rc;
|
|
struct tme_keyboard_macro *macro, *macro_next;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* count the number of keysyms on both sides: */
|
|
for (count_lhs = 0;
|
|
keysyms_lhs[count_lhs] != TME_KEYBOARD_KEYVAL_UNDEF;
|
|
count_lhs++);
|
|
for (count_rhs = 0;
|
|
keysyms_rhs[count_rhs] != TME_KEYBOARD_KEYVAL_UNDEF;
|
|
count_rhs++);
|
|
|
|
/* there must be some left-hand side and some right-hand side: */
|
|
if (count_lhs == 0
|
|
|| count_rhs == 0) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* create the final keysyms and press-flags arrays for the macro. any
|
|
keysym on the left hand side that is also on the right hand side
|
|
becomes a press, else a release, and any keysym on the right hand
|
|
side that isn't on the left hand side becomes a press: */
|
|
keysyms = tme_new(struct tme_keysym_state *, count_lhs + count_rhs);
|
|
press_flags = tme_new(unsigned int, count_lhs + count_rhs);
|
|
keysym_count = 0;
|
|
for (keysym_i = 0;
|
|
keysym_i < count_lhs;
|
|
keysym_i++) {
|
|
keysym = keysyms_lhs[keysym_i];
|
|
|
|
/* see if this keysym is on the right hand side: */
|
|
for (keysym_j = 0;
|
|
keysym_j < count_rhs;
|
|
keysym_j++) {
|
|
if (keysym == keysyms_rhs[keysym_j]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* set this keysym and press-flags: */
|
|
keysyms[keysym_count] = _tme_keysym_state_get(buffer, keysym);
|
|
press_flags[keysym_count] = (keysym_j < count_rhs);
|
|
keysym_count++;
|
|
}
|
|
for (keysym_j = 0;
|
|
keysym_j < count_rhs;
|
|
keysym_j++) {
|
|
keysym = keysyms_rhs[keysym_j];
|
|
|
|
/* see if this keysym is on the left hand side: */
|
|
for (keysym_i = 0;
|
|
keysym_i < count_lhs;
|
|
keysym_i++) {
|
|
if (keysym == keysyms_lhs[keysym_i]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* set this keysym and press-flags: */
|
|
if (keysym_i == count_lhs) {
|
|
keysyms[keysym_count] = _tme_keysym_state_get(buffer, keysym);
|
|
press_flags[keysym_count] = TRUE;
|
|
keysym_count++;
|
|
}
|
|
}
|
|
|
|
/* the last keysym in any macro's right hand side must be a press: */
|
|
if (!press_flags[keysym_count - 1]) {
|
|
tme_free(keysyms);
|
|
tme_free(press_flags);
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* add this keysym macro to the macros tree. this macro's sequence
|
|
(i.e., its left-hand side) cannot be strictly longer, or strictly
|
|
shorter, or the same as any existing macro's sequence: */
|
|
macro = &buffer->tme_keyboard_buffer_int_in2_macros_root;
|
|
rc = TME_OK;
|
|
for (keysym_i = 0;
|
|
;
|
|
keysym_i++) {
|
|
|
|
/* if we handled all left-hand side keysyms: */
|
|
if (keysym_i == count_lhs) {
|
|
|
|
/* if this node is already a non-leaf node, then this macro's
|
|
sequence is strictly shorter than an existing macro's
|
|
sequence: */
|
|
if (macro->tme_keyboard_macro_branches != NULL) {
|
|
rc = EEXIST;
|
|
}
|
|
|
|
/* otherwise, if this node is already a leaf node, then this
|
|
macro's sequence is the same as an existing macro's sequence: */
|
|
else if (macro->tme_keyboard_macro_length > 0) {
|
|
rc = EEXIST;
|
|
}
|
|
|
|
/* stop no matter what: */
|
|
break;
|
|
}
|
|
|
|
/* if this node has no branch set: */
|
|
if (macro->tme_keyboard_macro_branches == NULL) {
|
|
|
|
/* if this node is already a leaf node, then this macro's
|
|
sequence is strictly longer than an existing macro's
|
|
sequence: */
|
|
if (macro->tme_keyboard_macro_length > 0) {
|
|
rc = EEXIST;
|
|
break;
|
|
}
|
|
|
|
/* otherwise, create a branch set for this node: */
|
|
macro->tme_keyboard_macro_branches
|
|
= tme_hash_new(tme_direct_hash,
|
|
tme_direct_compare,
|
|
(tme_hash_data_t) NULL);
|
|
}
|
|
|
|
/* get the keysym: */
|
|
keysym = keysyms_lhs[keysym_i];
|
|
|
|
/* look up this keysym in the branch set for this node: */
|
|
macro_next
|
|
= ((struct tme_keyboard_macro *)
|
|
tme_hash_lookup(macro->tme_keyboard_macro_branches,
|
|
tme_keyboard_hash_data_from_keyval(keysym)));
|
|
|
|
/* if this keysym is a new branch, create a new macros tree node: */
|
|
if (macro_next == NULL) {
|
|
macro_next = tme_new0(struct tme_keyboard_macro, 1);
|
|
macro_next->tme_keyboard_macro_parent = macro;
|
|
macro_next->tme_keyboard_macro_keysym = keysym;
|
|
tme_hash_insert(macro->tme_keyboard_macro_branches,
|
|
tme_keyboard_hash_data_from_keyval(keysym),
|
|
(tme_hash_data_t) macro_next);
|
|
}
|
|
|
|
/* advance in the tree: */
|
|
macro = macro_next;
|
|
}
|
|
|
|
/* if this sequence couldn't be added to the sequences tree: */
|
|
if (rc != TME_OK) {
|
|
tme_free(keysyms);
|
|
tme_free(press_flags);
|
|
return (rc);
|
|
}
|
|
|
|
/* finish this leaf node in the macros tree: */
|
|
macro->tme_keyboard_macro_length = keysym_count;
|
|
macro->tme_keyboard_macro_keysyms = keysyms;
|
|
macro->tme_keyboard_macro_press_flags = press_flags;
|
|
|
|
/* if this is the first keysym macro added, set the root of the
|
|
keysym macros tree as active, making input stage two no longer a
|
|
passthrough: */
|
|
if (buffer->tme_keyboard_buffer_int_in2_macros_active
|
|
== NULL) {
|
|
buffer->tme_keyboard_buffer_int_in2_macros_active
|
|
= &buffer->tme_keyboard_buffer_int_in2_macros_root;
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this adds a single output stage zero keysym map entry: */
|
|
int
|
|
tme_keyboard_buffer_out_map(struct tme_keyboard_buffer *_buffer,
|
|
_tme_const struct tme_keyboard_map *map)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
struct tme_keysym_state *keysym;
|
|
struct tme_keycode_state *keycode;
|
|
struct tme_keymode_state *keymode;
|
|
int modifier;
|
|
tme_keyboard_modifiers_t modifiers_set, modifiers_clear;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* the keysym must be defined: */
|
|
assert (map->tme_keyboard_map_keysym
|
|
!= TME_KEYBOARD_KEYVAL_UNDEF);
|
|
|
|
/* get the state for this keysym: */
|
|
keysym = _tme_keysym_state_get(buffer, map->tme_keyboard_map_keysym);
|
|
|
|
/* this keysym must not already have an output side keycode: */
|
|
if (keysym->tme_keysym_state_out0_keycode != NULL) {
|
|
return (EEXIST);
|
|
}
|
|
|
|
/* lookup this keycode: */
|
|
keycode
|
|
= ((struct tme_keycode_state *)
|
|
tme_hash_lookup(buffer->tme_keyboard_buffer_int_out0_keycodes,
|
|
tme_keyboard_hash_data_from_keyval(map->tme_keyboard_map_keycode)));
|
|
|
|
/* if this keycode is new, allocate, initialize and add a structure
|
|
for it: */
|
|
if (keycode == NULL) {
|
|
|
|
/* allocate the keycode state: */
|
|
keycode = tme_new0(struct tme_keycode_state, 1);
|
|
|
|
/* initialize any parts of the keycode state that might not be
|
|
properly initialized as all-bits-zero. note that the keysym
|
|
stored in the keycode keymode state is the first keysym mapped
|
|
to the keycode: */
|
|
keycode->tme_keycode_state_keycode = map->tme_keyboard_map_keycode;
|
|
keymode = &keycode->tme_keycode_state_keymode;
|
|
keymode->tme_keymode_state_keysym = keysym;
|
|
|
|
/* add the keycode structure: */
|
|
tme_hash_insert(buffer->tme_keyboard_buffer_int_out0_keycodes,
|
|
tme_keyboard_hash_data_from_keyval(map->tme_keyboard_map_keycode),
|
|
(tme_hash_data_t) keycode);
|
|
}
|
|
|
|
/* set this keycode on this keysym: */
|
|
keysym->tme_keysym_state_out0_keycode = keycode;
|
|
|
|
/* if this keysym is attached to an output side modifier: */
|
|
modifier = map->tme_keyboard_map_modifier;
|
|
if (modifier != TME_KEYBOARD_MODIFIER_NONE) {
|
|
|
|
/* attach this keysym to the output side modifier: */
|
|
keysym->tme_keysym_state_out0_modifier
|
|
= modifier;
|
|
keysym->tme_keysym_state_out0_modifier_next
|
|
= buffer->tme_keyboard_buffer_int_out0_modkeys[modifier];
|
|
buffer->tme_keyboard_buffer_int_out0_modkeys[modifier]
|
|
= keysym;
|
|
|
|
/* dispatch on any special keysym note: */
|
|
switch (map->tme_keyboard_map_keysym_note) {
|
|
default: assert(FALSE);
|
|
case TME_KEYBOARD_KEYSYM_NOTE_UNDEF:
|
|
break;
|
|
case TME_KEYBOARD_KEYSYM_NOTE_CAPS_LOCK:
|
|
if (modifier == TME_KEYBOARD_MODIFIER_LOCK) {
|
|
buffer->tme_keyboard_buffer_int_out0_lock_is_caps = TRUE;
|
|
}
|
|
break;
|
|
case TME_KEYBOARD_KEYSYM_NOTE_SHIFT_LOCK:
|
|
break;
|
|
case TME_KEYBOARD_KEYSYM_NOTE_NUM_LOCK:
|
|
buffer->tme_keyboard_buffer_int_out0_mod_num_lock = modifier;
|
|
break;
|
|
}
|
|
|
|
/* this keysym cannot require any output side modifiers to be set
|
|
or clear: */
|
|
assert (map->tme_keyboard_map_modifiers_set == 0
|
|
&& map->tme_keyboard_map_modifiers_clear == 0);
|
|
}
|
|
|
|
/* remember the output stage zero modifiers that must be set or
|
|
clear for this mapping to work: */
|
|
modifiers_set = map->tme_keyboard_map_modifiers_set;
|
|
modifiers_clear = map->tme_keyboard_map_modifiers_clear;
|
|
assert ((modifiers_set & modifiers_clear) == 0);
|
|
|
|
/* if this keysym is lowercase, it also requires the shift modifier
|
|
to be clear: */
|
|
if (modifiers_clear
|
|
& (1 << TME_KEYBOARD_MODIFIER_LOCK)) {
|
|
modifiers_clear
|
|
|= (1 << TME_KEYBOARD_MODIFIER_SHIFT);
|
|
}
|
|
|
|
keysym->tme_keysym_state_out0_modifiers_set = modifiers_set;
|
|
keysym->tme_keysym_state_out0_modifiers_clear = modifiers_clear;
|
|
|
|
/* output stage zero is no longer a passthrough: */
|
|
buffer->tme_keyboard_buffer_int_out0_passthrough = FALSE;
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this changes a keycode's output stage one mode: */
|
|
int
|
|
tme_keyboard_buffer_out_mode(struct tme_keyboard_buffer *_buffer,
|
|
tme_keyboard_keyval_t _keycode, int mode)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
struct tme_keycode_state *keycode;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* TME_KEYBOARD_MODE_UNLOCK and TME_KEYBOARD_MODE_LOCK cannot be
|
|
combined with any other bits: */
|
|
if ((mode
|
|
& (TME_KEYBOARD_MODE_UNLOCK
|
|
| TME_KEYBOARD_MODE_LOCK))
|
|
&& (mode
|
|
& (mode - 1))) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* none of the TME_KEYBOARD_MODE_FLAG_NO_AUTOREPEATS,
|
|
TME_KEYBOARD_MODE_FLAG_NO_RELEASES, and
|
|
TME_KEYBOARD_MODE_FLAG_LOCK_SOFT flags can be set
|
|
without TME_KEYBOARD_MODE_PASSTHROUGH: */
|
|
if ((mode
|
|
& (TME_KEYBOARD_MODE_FLAG_NO_AUTOREPEATS
|
|
| TME_KEYBOARD_MODE_FLAG_NO_RELEASES
|
|
| TME_KEYBOARD_MODE_FLAG_LOCK_SOFT))
|
|
&& !(mode
|
|
& TME_KEYBOARD_MODE_PASSTHROUGH)) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* you cannot specify that an output key must be unlocked: */
|
|
if (mode & TME_KEYBOARD_MODE_UNLOCK) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* if we are setting the mode on a particular keycode: */
|
|
if (_keycode != TME_KEYBOARD_KEYVAL_UNDEF) {
|
|
keycode
|
|
= ((struct tme_keycode_state *)
|
|
tme_hash_lookup(buffer->tme_keyboard_buffer_int_out0_keycodes,
|
|
tme_keyboard_hash_data_from_keyval(_keycode)));
|
|
if (keycode == NULL) {
|
|
return (ENOENT);
|
|
}
|
|
keycode->tme_keycode_state_keymode.tme_keymode_state_mode = mode;
|
|
}
|
|
|
|
/* otherwise, we are setting the mode on the whole keyboard: */
|
|
else {
|
|
|
|
/* you can't set TME_KEYBOARD_MODE_GLOBAL at the global level: */
|
|
if (mode == TME_KEYBOARD_MODE_GLOBAL) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* set the output stage one global mode: */
|
|
buffer->tme_keyboard_buffer_int_out1_keymode_stage
|
|
.tme_keymode_stage_global_mode = mode;
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this fixes the keyboard's output stage zero modifiers when they get
|
|
out of sync with the emulated software reading the output keyboard: */
|
|
tme_keyboard_modifiers_t
|
|
tme_keyboard_buffer_out_modifiers(struct tme_keyboard_buffer *_buffer,
|
|
tme_keyboard_modifiers_t modifiers_clear,
|
|
tme_keyboard_modifiers_t modifiers_set)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* update the modifiers: */
|
|
return (buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
= ((buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
& ~modifiers_clear)
|
|
| modifiers_set));
|
|
}
|
|
|
|
/* this parses a single keysym macro: */
|
|
int
|
|
tme_keyboard_parse_macro(const char *string,
|
|
tme_keyboard_keysym_lookup_t keysym_lookup,
|
|
void *keysym_lookup_private,
|
|
tme_keyboard_keyval_t **_keysyms_lhs,
|
|
tme_keyboard_keyval_t **_keysyms_rhs)
|
|
{
|
|
char **tokens;
|
|
int tokens_count, equals_token;
|
|
int token_i;
|
|
tme_keyboard_keyval_t keysym, *keysyms_lhs, *keysyms_rhs;
|
|
struct tme_keyboard_lookup lookup;
|
|
unsigned int count_lhs, count_rhs;
|
|
int rc;
|
|
|
|
/* tokenize this line: */
|
|
tokens = tme_misc_tokenize(string, '#', &tokens_count);
|
|
keysyms_lhs = tme_new(tme_keyboard_keyval_t, tokens_count);
|
|
keysyms_rhs = tme_new(tme_keyboard_keyval_t, tokens_count);
|
|
count_lhs = 0;
|
|
count_rhs = 0;
|
|
|
|
/* start the lookup structure: */
|
|
lookup.tme_keyboard_lookup_context_length = 0;
|
|
lookup.tme_keyboard_lookup_context = NULL;
|
|
|
|
/* all of the tokens must be valid keysyms, except for a single
|
|
mandatory "=" token, which must not be the first or last token: */
|
|
equals_token = -1;
|
|
rc = TME_OK;
|
|
for (token_i = 0;
|
|
token_i < tokens_count;
|
|
token_i++) {
|
|
|
|
/* check for an "=" token: */
|
|
if (!strcmp(tokens[token_i], "=")) {
|
|
if (equals_token >= 0
|
|
|| token_i == 0
|
|
|| token_i + 1 == tokens_count) {
|
|
rc = EINVAL;
|
|
break;
|
|
}
|
|
equals_token = token_i;
|
|
continue;
|
|
}
|
|
|
|
/* a token on the left hand side must be a keysym that the
|
|
caller can generate directly: */
|
|
if (equals_token < 0) {
|
|
|
|
/* get the keysym for this token: */
|
|
lookup.tme_keyboard_lookup_string = tokens[token_i];
|
|
lookup.tme_keyboard_lookup_flags = TME_KEYBOARD_LOOKUP_FLAG_OK_DIRECT;
|
|
keysym = (*keysym_lookup)(keysym_lookup_private, &lookup);
|
|
if (keysym == TME_KEYBOARD_KEYVAL_UNDEF) {
|
|
rc = ENOENT;
|
|
break;
|
|
}
|
|
keysyms_lhs[count_lhs++] = keysym;
|
|
}
|
|
|
|
/* otherwise, a token on the right hand side is either a keysym
|
|
that the caller can generate directly, or the caller must
|
|
be able to allocate a unique value for it: */
|
|
else {
|
|
|
|
/* get the keysym for this token: */
|
|
lookup.tme_keyboard_lookup_string = tokens[token_i];
|
|
lookup.tme_keyboard_lookup_flags = (TME_KEYBOARD_LOOKUP_FLAG_OK_DIRECT
|
|
| TME_KEYBOARD_LOOKUP_FLAG_OK_ALLOC
|
|
| TME_KEYBOARD_LOOKUP_FLAG_OK_ALLOC_NOW);
|
|
keysym = (*keysym_lookup)(keysym_lookup_private, &lookup);
|
|
assert (keysym != TME_KEYBOARD_KEYVAL_UNDEF);
|
|
keysyms_rhs[count_rhs++] = keysym;
|
|
}
|
|
}
|
|
|
|
/* if this macro didn't parse correctly: */
|
|
if (rc != TME_OK) {
|
|
tme_free_string_array(tokens, -1);
|
|
tme_free(keysyms_lhs);
|
|
tme_free(keysyms_rhs);
|
|
return (rc);
|
|
}
|
|
|
|
/* finish the sides of the macro: */
|
|
keysyms_lhs[count_lhs] = TME_KEYBOARD_KEYVAL_UNDEF;
|
|
keysyms_rhs[count_rhs] = TME_KEYBOARD_KEYVAL_UNDEF;
|
|
|
|
/* done: */
|
|
*_keysyms_lhs = keysyms_lhs;
|
|
*_keysyms_rhs = keysyms_rhs;
|
|
tme_free_string_array(tokens, -1);
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this parses a single keysym map entry: */
|
|
int
|
|
tme_keyboard_parse_map(const char *string,
|
|
tme_keyboard_keysym_lookup_t keysym_lookup,
|
|
void *keysym_lookup_private,
|
|
struct tme_keyboard_map *map)
|
|
{
|
|
char **tokens, *p1, c;
|
|
int tokens_count;
|
|
int token_i;
|
|
tme_keyboard_keyval_t keycode;
|
|
int modifier, attached_modifier;
|
|
tme_keyboard_modifiers_t modifiers_set, modifiers_clear;
|
|
struct tme_keyboard_lookup lookup;
|
|
int rc;
|
|
|
|
/* tokenize this line: */
|
|
tokens = tme_misc_tokenize(string, '#', &tokens_count);
|
|
rc = TME_OK;
|
|
|
|
/* there must be at least three tokens. the second token must be an
|
|
equals sign, and the third token must be an integer that isn't
|
|
TME_KEYBOARD_KEYVAL_UNDEF: */
|
|
if (tokens_count < 3
|
|
|| strcmp(tokens[1], "=")
|
|
|| ((keycode = strtoul(tokens[2], &p1, 0))
|
|
== TME_KEYBOARD_KEYVAL_UNDEF)
|
|
|| p1 == tokens[2]
|
|
|| *p1 != '\0') {
|
|
rc = EINVAL;
|
|
}
|
|
|
|
/* any tokens after the third must all be modifier names: */
|
|
else {
|
|
attached_modifier = TME_KEYBOARD_MODIFIER_NONE;
|
|
modifiers_set = 0;
|
|
modifiers_clear = 0;
|
|
for (token_i = 3;
|
|
token_i < tokens_count;
|
|
token_i++) {
|
|
|
|
/* a token might be prefixed with '+' or '!': */
|
|
p1 = tokens[token_i];
|
|
c = *p1;
|
|
if (c == '+'
|
|
|| c == '!') {
|
|
p1++;
|
|
}
|
|
|
|
/* turn this token into a real modifier: */
|
|
if (!strcmp(p1, "shift")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_SHIFT;
|
|
}
|
|
else if (!strcmp(p1, "lock")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_LOCK;
|
|
}
|
|
else if (!strcmp(p1, "control")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_CONTROL;
|
|
}
|
|
else if (!strcmp(p1, "mod1")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_MOD1;
|
|
}
|
|
else if (!strcmp(p1, "mod2")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_MOD2;
|
|
}
|
|
else if (!strcmp(p1, "mod3")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_MOD3;
|
|
}
|
|
else if (!strcmp(p1, "mod4")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_MOD4;
|
|
}
|
|
else if (!strcmp(p1, "mod5")) {
|
|
modifier = TME_KEYBOARD_MODIFIER_MOD5;
|
|
}
|
|
else {
|
|
rc = EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* if a modifier is prefixed with '+', it must be the only
|
|
modifier token in the map entry, and it indicates that the
|
|
keycode is attached to that modifier in this map: */
|
|
if (c == '+') {
|
|
|
|
/* if this is not the only modifier token in the map entry: */
|
|
if (tokens_count != 4) {
|
|
rc = EINVAL;
|
|
break;
|
|
}
|
|
|
|
attached_modifier = modifier;
|
|
}
|
|
|
|
/* otherwise, if a modifier name is prefixed with '!', this
|
|
modifier must be clear for this keycode to mean this keysym: */
|
|
else if (c == '!') {
|
|
modifiers_clear |= (1 << modifier);
|
|
}
|
|
|
|
/* otherwise, this modifier must be set for this keycode to mean
|
|
this keysym: */
|
|
else {
|
|
modifiers_set |= (1 << modifier);
|
|
}
|
|
}
|
|
|
|
/* the modifiers that must be set and the modifiers that must be
|
|
clear cannot overlap: */
|
|
if (modifiers_set & modifiers_clear) {
|
|
rc = EINVAL;
|
|
}
|
|
|
|
/* if no error has been encountered yet: */
|
|
if (rc == TME_OK) {
|
|
|
|
/* make the keyboard map entry. we very deliberately set all
|
|
bytes not allocated to structure members to all-bits-zero, so
|
|
that identical keysym contexts truly are identical: */
|
|
memset(map, 0, sizeof(*map));
|
|
map->tme_keyboard_map_keycode = keycode;
|
|
map->tme_keyboard_map_modifier = attached_modifier;
|
|
map->tme_keyboard_map_modifiers_set = modifiers_set;
|
|
map->tme_keyboard_map_modifiers_clear = modifiers_clear;
|
|
|
|
/* take note of a special keysym: */
|
|
if (!strcmp(tokens[0], "Caps_Lock")) {
|
|
map->tme_keyboard_map_keysym_note
|
|
= TME_KEYBOARD_KEYSYM_NOTE_CAPS_LOCK;
|
|
}
|
|
else if (!strcmp(tokens[0], "Shift_Lock")) {
|
|
map->tme_keyboard_map_keysym_note
|
|
= TME_KEYBOARD_KEYSYM_NOTE_SHIFT_LOCK;
|
|
}
|
|
else if (!strcmp(tokens[0], "Num_Lock")) {
|
|
map->tme_keyboard_map_keysym_note
|
|
= TME_KEYBOARD_KEYSYM_NOTE_NUM_LOCK;
|
|
}
|
|
else {
|
|
map->tme_keyboard_map_keysym_note
|
|
= TME_KEYBOARD_KEYSYM_NOTE_UNDEF;
|
|
}
|
|
|
|
/* the caller must be able to either directly generate this
|
|
keysym or have allocated a value for it already (because a
|
|
macro was previously added than can generate it). if neither
|
|
is true, the lookup function must return
|
|
TME_KEYBOARD_KEYVAL_UNDEF, but this function still does not
|
|
fail. it is up to the caller to not add a map entry with an
|
|
undefined keysym: */
|
|
lookup.tme_keyboard_lookup_string
|
|
= tokens[0];
|
|
lookup.tme_keyboard_lookup_flags
|
|
= (TME_KEYBOARD_LOOKUP_FLAG_OK_DIRECT
|
|
| TME_KEYBOARD_LOOKUP_FLAG_OK_ALLOC);
|
|
lookup.tme_keyboard_lookup_context_length
|
|
= sizeof(*map);
|
|
lookup.tme_keyboard_lookup_context
|
|
= (tme_uint8_t *) map;
|
|
map->tme_keyboard_map_keysym
|
|
= (*keysym_lookup)(keysym_lookup_private, &lookup);
|
|
}
|
|
}
|
|
|
|
/* free the tokens: */
|
|
tme_free_string_array(tokens, -1);
|
|
|
|
/* return the parsed map: */
|
|
return (rc);
|
|
}
|
|
|
|
/* this adds an event to the event buffer: */
|
|
static int
|
|
_tme_keyboard_buffer_copyin(struct tme_keyboard_buffer_int *buffer,
|
|
_tme_const struct tme_keyboard_event *event)
|
|
{
|
|
unsigned int buffer_head, buffer_size_mask;
|
|
|
|
buffer_head = buffer->tme_keyboard_buffer_int_head;
|
|
buffer_size_mask = buffer->tme_keyboard_buffer_int_size - 1;
|
|
|
|
/* if the buffer is full: */
|
|
if (((buffer_head + 1) & buffer_size_mask)
|
|
== buffer->tme_keyboard_buffer_int_tail) {
|
|
return (EAGAIN);
|
|
}
|
|
|
|
/* put this event into the buffer: */
|
|
buffer->tme_keyboard_buffer_int_events[buffer_head]
|
|
= *event;
|
|
|
|
/* advance the head: */
|
|
buffer->tme_keyboard_buffer_int_head
|
|
= (buffer_head + 1) & buffer_size_mask;
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this adds a difference to an event time, avoiding a result of
|
|
TME_KEYBOARD_EVENT_TIME_UNDEF: */
|
|
static tme_uint32_t
|
|
_tme_keyboard_event_time_diff(tme_uint32_t event_time,
|
|
tme_int32_t diff)
|
|
{
|
|
event_time += (tme_uint32_t) diff;
|
|
if (event_time == TME_KEYBOARD_EVENT_TIME_UNDEF) {
|
|
assert (diff != 0);
|
|
event_time += (diff < 0 ? -1 : 1);
|
|
}
|
|
return (event_time);
|
|
}
|
|
|
|
/* this subtracts event_time1 from event_time0, handling the wrapping
|
|
of the tme_uint32_t milliseconds values as best as possible: */
|
|
static tme_int32_t
|
|
_tme_keyboard_event_time_subtract(tme_uint32_t event_time0,
|
|
tme_uint32_t event_time1)
|
|
{
|
|
tme_uint32_t event_time0_less_least;
|
|
tme_uint32_t event_time_diff_unwrapped;
|
|
tme_uint32_t event_time_diff_wrapped;
|
|
|
|
/* if the two times are equal: */
|
|
if (event_time0 == event_time1) {
|
|
return (0);
|
|
}
|
|
|
|
/* calculate the unwrapped and wrapped differences between the
|
|
two times: */
|
|
if (event_time0 < event_time1) {
|
|
event_time_diff_unwrapped = event_time1 - event_time0;
|
|
event_time_diff_wrapped = 0 - event_time_diff_unwrapped;
|
|
}
|
|
else {
|
|
event_time_diff_unwrapped = event_time0 - event_time1;
|
|
event_time_diff_wrapped = 0 - event_time_diff_unwrapped;
|
|
}
|
|
|
|
/* calculate the event time that is the most in the past without
|
|
being confused as being later than event_time0: */
|
|
event_time0_less_least
|
|
= (event_time0 - (((tme_uint32_t) -1) >> 1));
|
|
|
|
/* if event_time0_less_least is literally greater than event_time0,
|
|
the event time space that represents "less than event_time0" is
|
|
not a single region in the tme_uint32_t space: */
|
|
if (event_time0_less_least < event_time0) {
|
|
return ((event_time0_less_least <= event_time1
|
|
&& event_time1 < event_time0)
|
|
/* event_time1 is less than event_time0: */
|
|
? event_time_diff_unwrapped
|
|
/* event_time1 is greater than event_time0: */
|
|
: (event_time1 > event_time0
|
|
? 0 - event_time_diff_unwrapped
|
|
: 0 - event_time_diff_wrapped));
|
|
}
|
|
|
|
/* otherwise, the event time space that represents "less than
|
|
event_time0" is two regions in the tme_uint32_t space: */
|
|
else {
|
|
return ((event_time0_less_least <= event_time1
|
|
|| event_time1 < event_time0)
|
|
/* event_time1 is less than event_time0: */
|
|
? (event_time1 < event_time0
|
|
? event_time_diff_unwrapped
|
|
: event_time_diff_wrapped)
|
|
/* event_time1 is greater than event_time0: */
|
|
: 0 - event_time_diff_unwrapped);
|
|
}
|
|
}
|
|
|
|
/* this runs a keymode stage. this is used for input stage one,
|
|
and output stage one. roughly, a keymode stage tries to
|
|
guarantee that a key has a specific press/release behavior
|
|
(or "mode") for later stages. the particular modes are described
|
|
in the case below: */
|
|
static int
|
|
_tme_keymode_stage(struct tme_keyboard_buffer_int *buffer,
|
|
struct tme_keymode_stage *stage,
|
|
struct tme_keymode_state *keymode,
|
|
int is_press,
|
|
tme_uint32_t event_time)
|
|
{
|
|
struct tme_keymode_state **_auto_keymode, *auto_keymode;
|
|
int pressed_old, mode;
|
|
int rc;
|
|
|
|
/* check all keys on the no-autorepeats list: */
|
|
for (_auto_keymode = &stage->tme_keymode_stage_no_autorepeats;
|
|
(auto_keymode = *_auto_keymode) != NULL; ) {
|
|
|
|
/* if the time of the last release from the earlier stages is
|
|
undefined, that means that the last event from the earlier
|
|
stages was a press: */
|
|
if (auto_keymode->tme_keymode_state_last_release
|
|
== TME_KEYBOARD_EVENT_TIME_UNDEF) {
|
|
|
|
/* if the key we're checking happens to be the key that the
|
|
earlier stages are calling us for: */
|
|
if (auto_keymode == keymode) {
|
|
|
|
/* this must be a release on this key: */
|
|
assert (!is_press);
|
|
|
|
/* set the release time on this key: */
|
|
auto_keymode->tme_keymode_state_last_release = event_time;
|
|
|
|
/* we've taken care of this call: */
|
|
keymode = NULL;
|
|
}
|
|
}
|
|
|
|
/* otherwise, the last event from the earlier stages was a release.
|
|
if enough time has elapsed since then without a press from
|
|
earlier stages: */
|
|
else if (_tme_keyboard_event_time_subtract(event_time,
|
|
auto_keymode->tme_keymode_state_last_release)
|
|
> TME_KEYBOARD_SHORTEST_DOUBLE_MSEC) {
|
|
|
|
/* now we're sure that the key has genuinely released.
|
|
remove this key from the autorepeats list: */
|
|
*_auto_keymode = auto_keymode->tme_keymode_state_next;
|
|
auto_keymode->tme_keymode_state_next = NULL;
|
|
|
|
/* if we're supposed to ignore the genuine release: */
|
|
if (auto_keymode->tme_keymode_state_ignore_release) {
|
|
auto_keymode->tme_keymode_state_ignore_release = FALSE;
|
|
}
|
|
|
|
/* otherwise, we're not supposed to ignore the genuine release: */
|
|
else {
|
|
|
|
/* flip the pressed state of the key: */
|
|
auto_keymode->tme_keymode_state_pressed
|
|
= !auto_keymode->tme_keymode_state_pressed;
|
|
|
|
/* if this key is now pressed, or if we're allowed to
|
|
pass releases, run the next stage: */
|
|
mode = auto_keymode->tme_keymode_state_mode;
|
|
if (mode == TME_KEYBOARD_MODE_GLOBAL) {
|
|
mode = stage->tme_keymode_stage_global_mode;
|
|
}
|
|
if (auto_keymode->tme_keymode_state_pressed
|
|
|| !(mode
|
|
& TME_KEYBOARD_MODE_FLAG_NO_RELEASES)) {
|
|
rc = (*stage->tme_keymode_stage_next)
|
|
(buffer,
|
|
auto_keymode->tme_keymode_state_keysym,
|
|
_tme_keyboard_event_time_diff(event_time, -1));
|
|
assert (rc == TME_OK);
|
|
}
|
|
}
|
|
|
|
/* continue now - by removing this key from the autorepeats
|
|
list we've already advanced our position in the list for
|
|
the next iteration: */
|
|
continue;
|
|
}
|
|
|
|
/* otherwise, if the key we're checking happens to be the key that
|
|
the earlier stages are calling us for: */
|
|
else if (auto_keymode == keymode) {
|
|
|
|
/* this must be a press on this key: */
|
|
assert (is_press);
|
|
|
|
/* now we're sure that this key is autorepeating. clear
|
|
the last-release time: */
|
|
auto_keymode->tme_keymode_state_last_release
|
|
= TME_KEYBOARD_EVENT_TIME_UNDEF;
|
|
|
|
/* we've taken care of this call: */
|
|
keymode = NULL;
|
|
}
|
|
|
|
/* continue: */
|
|
_auto_keymode = &auto_keymode->tme_keymode_state_next;
|
|
}
|
|
|
|
/* return now if we already finished processing this event: */
|
|
if (keymode == NULL) {
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* get this key's mode: */
|
|
mode = keymode->tme_keymode_state_mode;
|
|
if (mode == TME_KEYBOARD_MODE_GLOBAL) {
|
|
mode = stage->tme_keymode_stage_global_mode;
|
|
}
|
|
|
|
/* remember if this key was pressed in this stage before: */
|
|
pressed_old = keymode->tme_keymode_state_pressed;
|
|
|
|
/* unlock mode unlocks a key by turning every transition into a
|
|
press transition and a release transition. think of a Caps Lock
|
|
key on an input keyboard that *physically* locks down when you
|
|
press it down - this is a nice property, because there's no
|
|
mistaking that when you get a key press event, you know the key
|
|
is both physically and *semantically* down until you get a key
|
|
release, at which time you know just the opposite.
|
|
|
|
however, this is a best case. many keyboards have Caps Lock, Num
|
|
Lock, and other keys where their semantic pressed/release state
|
|
doesn't match their physical pressed/release state. to make
|
|
everything uniform, we need to bring the best-case keyboards down
|
|
to the worst-case level: */
|
|
if (mode == TME_KEYBOARD_MODE_UNLOCK) {
|
|
|
|
/* we must not have this key as pressed: */
|
|
assert (!pressed_old);
|
|
|
|
/* press this key and run the next stage: */
|
|
keymode->tme_keymode_state_pressed = TRUE;
|
|
rc = (*stage->tme_keymode_stage_next)
|
|
(buffer,
|
|
keymode->tme_keymode_state_keysym,
|
|
_tme_keyboard_event_time_diff(event_time, -1));
|
|
assert (rc == TME_OK);
|
|
|
|
/* release this key, and make sure the next stage
|
|
gets run at the end of this function: */
|
|
keymode->tme_keymode_state_pressed = FALSE;
|
|
pressed_old = TRUE;
|
|
}
|
|
|
|
/* lock mode makes the key lock in the physical sense, like the
|
|
best-case Caps Lock described above. since our events from
|
|
earlier stages are worst-case, we need to ignore autorepeats, and
|
|
each press/release pair must toggle our notion of whether or not
|
|
the key is physically pressed: */
|
|
else if (mode == TME_KEYBOARD_MODE_LOCK) {
|
|
|
|
/* this must be a press: */
|
|
assert (is_press);
|
|
|
|
/* put this key on the no-autorepeats list: */
|
|
keymode->tme_keymode_state_last_release
|
|
= TME_KEYBOARD_EVENT_TIME_UNDEF;
|
|
keymode->tme_keymode_state_next
|
|
= stage->tme_keymode_stage_no_autorepeats;
|
|
stage->tme_keymode_stage_no_autorepeats = keymode;
|
|
|
|
/* if we have the key as pressed (locked): */
|
|
if (pressed_old) {
|
|
|
|
/* don't ignore the genuine release, when it is determined to
|
|
have happened. if this mode has
|
|
TME_KEYBOARD_MODE_FLAG_NO_RELEASES set, the genuine release
|
|
code will handle it then: */
|
|
keymode->tme_keymode_state_ignore_release = FALSE;
|
|
}
|
|
|
|
/* otherwise, we have the key as released (unlocked): */
|
|
else {
|
|
|
|
/* press this key: */
|
|
keymode->tme_keymode_state_pressed = TRUE;
|
|
|
|
/* ignore the genuine release, when it is determined
|
|
to have happened: */
|
|
keymode->tme_keymode_state_ignore_release = TRUE;
|
|
}
|
|
}
|
|
|
|
/* passthrough mode generally passes events through: */
|
|
else {
|
|
|
|
assert (!pressed_old != !is_press);
|
|
|
|
/* if this is a press: */
|
|
if (is_press) {
|
|
|
|
/* press this key: */
|
|
keymode->tme_keymode_state_pressed = TRUE;
|
|
|
|
/* if we must not pass through autorepeats: */
|
|
if (mode & TME_KEYBOARD_MODE_FLAG_NO_AUTOREPEATS) {
|
|
|
|
/* put this key on the no-autorepeats list: */
|
|
keymode->tme_keymode_state_last_release
|
|
= TME_KEYBOARD_EVENT_TIME_UNDEF;
|
|
keymode->tme_keymode_state_next
|
|
= stage->tme_keymode_stage_no_autorepeats;
|
|
stage->tme_keymode_stage_no_autorepeats = keymode;
|
|
|
|
/* don't ignore the genuine release, when it is
|
|
determined to have happened. if this mode has
|
|
TME_KEYBOARD_MODE_FLAG_NO_RELEASES set, the
|
|
genuine release code will handle it then: */
|
|
keymode->tme_keymode_state_ignore_release = FALSE;
|
|
}
|
|
}
|
|
|
|
/* otherwise, this is a release: */
|
|
else {
|
|
|
|
/* release this key: */
|
|
keymode->tme_keymode_state_pressed = FALSE;
|
|
|
|
/* if this mode has TME_KEYBOARD_MODE_FLAG_NO_RELEASES set,
|
|
we'll handle it below when we go to run the next stage: */
|
|
}
|
|
}
|
|
|
|
/* stop processing this event now if the key's pressed state
|
|
in this stage has not changed, or if the key is now released
|
|
and we're not allowed to pass releases. otherwise, run the
|
|
next stage: */
|
|
return ((keymode->tme_keymode_state_pressed
|
|
? pressed_old
|
|
: (!pressed_old
|
|
|| (mode
|
|
& TME_KEYBOARD_MODE_FLAG_NO_RELEASES)))
|
|
? TME_OK
|
|
: ((*stage->tme_keymode_stage_next)
|
|
(buffer,
|
|
keymode->tme_keymode_state_keysym,
|
|
event_time)));
|
|
}
|
|
|
|
/* this is the bottom half of output stage one. this half does
|
|
nothing except finally buffer an event for a keycode, and update
|
|
the output modifiers mask: */
|
|
static int
|
|
_tme_keyboard_buffer_out1_bottom(struct tme_keyboard_buffer_int *buffer,
|
|
struct tme_keysym_state *keysym,
|
|
tme_uint32_t event_time)
|
|
{
|
|
struct tme_keycode_state *keycode;
|
|
struct tme_keyboard_event event_buffer;
|
|
int modifier;
|
|
int is_press;
|
|
|
|
/* get the keycode: */
|
|
keycode = keysym->tme_keysym_state_out0_keycode;
|
|
|
|
/* get whether or not this is a press: */
|
|
is_press = TME_KEYBOARD_PRESSED_OUT1(keycode);
|
|
|
|
_tme_keyboard_debug(buffer,
|
|
"out1-bottom",
|
|
keysym->tme_keysym_state_keysym,
|
|
is_press,
|
|
event_time);
|
|
|
|
/* if this keysym is attached to a modifier, update the output
|
|
modifiers mask: */
|
|
/* XXX there might be a cleaner way to work the output modifier
|
|
mask. it's suspicious that stages zero and one need to share the
|
|
modifiers mask and the keysym to modifier mapping: */
|
|
modifier = keysym->tme_keysym_state_out0_modifier;
|
|
if (modifier != TME_KEYBOARD_MODIFIER_NONE) {
|
|
|
|
/* if this is a press: */
|
|
if (is_press) {
|
|
|
|
/* if the modifier is currently clear: */
|
|
if (!(buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
& (1 << modifier))) {
|
|
|
|
/* set the modifier: */
|
|
buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
|= (1 << modifier);
|
|
|
|
/* iff this keysym soft-locks, the next release will not
|
|
affect the output modifiers mask: */
|
|
keysym->tme_keysym_state_out1_ignore_release
|
|
= (keycode->tme_keycode_state_keymode.tme_keymode_state_mode
|
|
& TME_KEYBOARD_MODE_FLAG_LOCK_SOFT);
|
|
}
|
|
|
|
/* otherwise, the modifier is currently set: */
|
|
else {
|
|
|
|
/* nothing to do. even if this keysym soft-locks, we won't
|
|
clear the modifier until the release: */
|
|
}
|
|
}
|
|
|
|
/* otherwise, this is a release: */
|
|
else {
|
|
|
|
/* if we're supposed to ignore this release: */
|
|
if (keysym->tme_keysym_state_out1_ignore_release) {
|
|
keysym->tme_keysym_state_out1_ignore_release = FALSE;
|
|
}
|
|
|
|
/* otherwise, if the modifier is currently set: */
|
|
else if (buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
& (1 << modifier)) {
|
|
|
|
/* clear the modifier: */
|
|
buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
&= ~(1 << modifier);
|
|
}
|
|
|
|
/* otherwise, the modifier is currently clear: */
|
|
else {
|
|
|
|
/* nothing to do. a release would never set a modifier: */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* finally buffer this keycode: */
|
|
event_buffer.tme_keyboard_event_type
|
|
= (is_press
|
|
? TME_KEYBOARD_EVENT_PRESS
|
|
: TME_KEYBOARD_EVENT_RELEASE);
|
|
event_buffer.tme_keyboard_event_keyval
|
|
= keycode->tme_keycode_state_keycode;
|
|
event_buffer.tme_keyboard_event_keycode = 0;
|
|
event_buffer.tme_keyboard_event_time
|
|
= event_time;
|
|
event_buffer.tme_keyboard_event_modifiers
|
|
= buffer->tme_keyboard_buffer_int_out0_modifiers;
|
|
return (_tme_keyboard_buffer_copyin(buffer, &event_buffer));
|
|
}
|
|
|
|
/* this is the top half of output stage one. it is the last output
|
|
stage, that gives each keycode the specific behavior that it must
|
|
have on the output keyboard: */
|
|
static int
|
|
_tme_keyboard_buffer_out1(struct tme_keyboard_buffer_int *buffer,
|
|
struct tme_keysym_state *keysym,
|
|
tme_uint32_t event_time)
|
|
{
|
|
struct tme_keycode_state *keycode;
|
|
|
|
_tme_keyboard_debug(buffer,
|
|
"out1-top",
|
|
keysym->tme_keysym_state_keysym,
|
|
TME_KEYBOARD_PRESSED_OUT0(keysym),
|
|
event_time);
|
|
|
|
/* get the keycode state: */
|
|
keycode = keysym->tme_keysym_state_out0_keycode;
|
|
|
|
/* run the keymode stage function: */
|
|
return (_tme_keymode_stage(buffer,
|
|
&buffer->tme_keyboard_buffer_int_out1_keymode_stage,
|
|
&keycode->tme_keycode_state_keymode,
|
|
TME_KEYBOARD_PRESSED_OUT0(keysym),
|
|
event_time));
|
|
}
|
|
|
|
/* this is output stage zero. at this point, we have the final
|
|
keysyms from the input keyboard, and this stage maps those keysyms
|
|
to keycodes on the output keyboard.
|
|
|
|
sometimes, the input keyboard may generate keysyms for which the
|
|
output keyboard requires that certain modifiers be set or clear, at
|
|
a time when the output modifiers mask isn't suitable. for example,
|
|
this can happen when the input keyboard can generate a certain
|
|
keysym without shifting, when the output keyboard requires
|
|
shifting.
|
|
|
|
when this happens, this stage is responsible for simulating presses
|
|
or releases of modifiers on the output keyboard as needed to make
|
|
sure that the keysym is properly obtained when the mapped keycode
|
|
is pressed, and for undoing those changes when the mapped keycode
|
|
is released: */
|
|
static int
|
|
_tme_keyboard_buffer_out0(struct tme_keyboard_buffer_int *buffer,
|
|
struct tme_keysym_state *keysym,
|
|
tme_uint32_t event_time)
|
|
{
|
|
struct tme_keyboard_event event_buffer;
|
|
tme_keyboard_modifiers_t modifiers, modifiers_set, modifiers_clear;
|
|
int num_lock_on;
|
|
int is_press, mod_pressed_old, modifier;
|
|
struct tme_keysym_state *mod_keysym, **keysyms;
|
|
unsigned int *press_flags;
|
|
int keysym_count;
|
|
int modifier_stuck;
|
|
int rc;
|
|
|
|
_tme_keyboard_debug(buffer,
|
|
"out0",
|
|
keysym->tme_keysym_state_keysym,
|
|
TME_KEYBOARD_PRESSED_IN2(keysym),
|
|
event_time);
|
|
|
|
/* get whether or not this is a press from earlier stages: */
|
|
is_press = TME_KEYBOARD_PRESSED_IN2(keysym);
|
|
|
|
/* if output stage zero is a passthrough, just buffer an event for
|
|
the keysym and we're done processing this event: */
|
|
if (buffer->tme_keyboard_buffer_int_out0_passthrough) {
|
|
|
|
/* otherwise, simply buffer this event: */
|
|
event_buffer.tme_keyboard_event_type
|
|
= (is_press
|
|
? TME_KEYBOARD_EVENT_PRESS
|
|
: TME_KEYBOARD_EVENT_RELEASE);
|
|
event_buffer.tme_keyboard_event_keyval
|
|
= keysym->tme_keysym_state_keysym;
|
|
event_buffer.tme_keyboard_event_keycode = 0;
|
|
event_buffer.tme_keyboard_event_time
|
|
= event_time;
|
|
event_buffer.tme_keyboard_event_modifiers
|
|
= 0;
|
|
return (_tme_keyboard_buffer_copyin(buffer, &event_buffer));
|
|
}
|
|
|
|
/* if this keysym is not mapped to a keycode on the output keyboard,
|
|
we don't have to process this event any more: */
|
|
if (keysym->tme_keysym_state_out0_keycode == NULL) {
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* if this is a press from earlier stages: */
|
|
if (is_press) {
|
|
|
|
/* this keysym can't have any changes attached to it already: */
|
|
assert (keysym->tme_keysym_state_out0_keysyms == NULL);
|
|
keysyms = NULL;
|
|
press_flags = NULL;
|
|
keysym_count = 0;
|
|
|
|
/* get the current output stage zero modifiers: */
|
|
modifiers = buffer->tme_keyboard_buffer_int_out0_modifiers;
|
|
|
|
/* get the sets of modifiers that must be clear and set for this
|
|
keysym: */
|
|
modifiers_clear = keysym->tme_keysym_state_out0_modifiers_clear;
|
|
modifiers_set = keysym->tme_keysym_state_out0_modifiers_set;
|
|
assert ((modifiers_clear & modifiers_set) == 0);
|
|
assert ((modifiers_clear | modifiers_set) == 0
|
|
|| (keysym->tme_keysym_state_out0_modifier
|
|
== TME_KEYBOARD_MODIFIER_NONE));
|
|
|
|
/* if this keysym is uppercase, but the current modifiers are such
|
|
that a lowercase keysym *might* be generated: */
|
|
if ((modifiers_set & (1 << TME_KEYBOARD_MODIFIER_LOCK))
|
|
&& !(modifiers & (1 << TME_KEYBOARD_MODIFIER_LOCK))) {
|
|
|
|
/* if shift is down, we must be generating uppercase keysyms, so
|
|
forget about the lock modifier in this instance: */
|
|
if (modifiers & (1 << TME_KEYBOARD_MODIFIER_SHIFT)) {
|
|
modifiers_set &= ~(1 << TME_KEYBOARD_MODIFIER_LOCK);
|
|
}
|
|
|
|
/* otherwise, neither shift nor lock are down, so we are
|
|
generating lowercase keysyms. since this keyboard might not
|
|
have a lock modifier at all (and since it seems more
|
|
reasonable to do it this way anyways), require the shift
|
|
modifier to be set instead of the lock modifier in this
|
|
instance: */
|
|
else {
|
|
modifiers_set &= ~(1 << TME_KEYBOARD_MODIFIER_LOCK);
|
|
modifiers_set |= (1 << TME_KEYBOARD_MODIFIER_SHIFT);
|
|
}
|
|
}
|
|
|
|
/* if this output keyboard has a Num_Lock key, and this keysym is
|
|
sensitive to the Num_Lock setting, but the appropriate Num_Lock
|
|
setting is already in effect, forget about Num_Lock for this
|
|
instance: */
|
|
modifier = buffer->tme_keyboard_buffer_int_out0_mod_num_lock;
|
|
if (modifier != TME_KEYBOARD_MODIFIER_NONE
|
|
&& ((modifiers_clear
|
|
| modifiers_set)
|
|
& (1 << modifier))) {
|
|
|
|
/* determine what Num_Lock setting is currently in effect. it
|
|
is active if Num_Lock is pressed and there is no shifting
|
|
active, or if Num_Lock is released and there is shifting
|
|
active. NB that if the lock modifier is attached only to a
|
|
Shift_Lock, that modifier also counts as shifting: */
|
|
num_lock_on
|
|
= (
|
|
|
|
/* this term is 0 iff Num_Lock is pressed, else 1: */
|
|
!(modifiers
|
|
& (1 << modifier))
|
|
|
|
!=
|
|
|
|
/* this term is 0 iff shifting is active, else 1: */
|
|
!(modifiers
|
|
& ((1 << TME_KEYBOARD_MODIFIER_SHIFT)
|
|
| (buffer->tme_keyboard_buffer_int_out0_lock_is_caps
|
|
? 0
|
|
: (1 << TME_KEYBOARD_MODIFIER_LOCK)))));
|
|
|
|
/* if this keysym requires Num_Lock to be clear, but it is
|
|
not in effect, forget about Num_Lock for this instance: */
|
|
if ((modifiers_clear & (1 << modifier))
|
|
&& !num_lock_on) {
|
|
modifiers_clear &= ~(1 << modifier);
|
|
}
|
|
|
|
/* if this keysym requires Num_Lock to be set, but it is
|
|
in effect, forget about Num_Lock for this instance: */
|
|
if ((modifiers_set & (1 << modifier))
|
|
&& num_lock_on) {
|
|
modifiers_set &= ~(1 << modifier);
|
|
}
|
|
}
|
|
|
|
/* finish the set of modifiers that must be clear and set for this
|
|
keysym, but aren't: */
|
|
modifiers_clear &= modifiers;
|
|
modifiers_set &= ~modifiers;
|
|
|
|
/* try to generate release events for all keysyms attached to
|
|
output modifiers that are set, but that need to be clear: */
|
|
if (modifiers_clear != 0) {
|
|
|
|
/* loop over the modifiers that need clearing: */
|
|
for (modifier = 0;
|
|
modifier <= TME_KEYBOARD_MODIFIER_MAX;
|
|
modifier++) {
|
|
if (!(modifiers_clear & (1 << modifier))) {
|
|
continue;
|
|
}
|
|
|
|
/* try to release all of the keysyms attached to this modifier
|
|
that are pressed: */
|
|
modifier_stuck = FALSE;
|
|
for (mod_keysym = buffer->tme_keyboard_buffer_int_out0_modkeys[modifier];
|
|
mod_keysym != NULL;
|
|
mod_keysym = mod_keysym->tme_keysym_state_out0_modifier_next) {
|
|
|
|
/* XXX this is broken for modifiers that soft-lock. */
|
|
|
|
/* ignore this keysym if it isn't pressed: */
|
|
if (!TME_KEYBOARD_PRESSED_OUT0(mod_keysym)) {
|
|
continue;
|
|
}
|
|
|
|
/* grow the keysyms and press flags arrays. the keysyms
|
|
array always has one extra entry, which will be filled
|
|
with NULL to terminate the array: */
|
|
if (keysym_count == 0) {
|
|
keysyms = tme_new(struct tme_keysym_state *, 2);
|
|
press_flags = tme_new(unsigned int, 1);
|
|
}
|
|
else {
|
|
keysyms = tme_renew(struct tme_keysym_state *, keysyms, keysym_count + 2);
|
|
press_flags = tme_renew(unsigned int, press_flags, keysym_count + 1);
|
|
}
|
|
|
|
/* add this keysym to those arrays: */
|
|
keysyms[keysym_count] = mod_keysym;
|
|
press_flags[keysym_count] = FALSE;
|
|
keysym_count++;
|
|
|
|
/* release this modifier key. if this actually
|
|
releases the key, run the next stage: */
|
|
mod_keysym->tme_keysym_state_out0_released++;
|
|
if (!TME_KEYBOARD_PRESSED_OUT0(mod_keysym)) {
|
|
rc = _tme_keyboard_buffer_out1(buffer,
|
|
mod_keysym,
|
|
_tme_keyboard_event_time_diff(event_time, -1));
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
/* otherwise, this modifier is stuck on: */
|
|
else {
|
|
modifier_stuck = TRUE;
|
|
}
|
|
}
|
|
|
|
/* if we were able to release all keysyms attached to this
|
|
modifier, clear the modifier in the output keyboard mask: */
|
|
if (!modifier_stuck) {
|
|
buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
&= ~(1 << modifier);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* try to generate press events for single keysyms attached to
|
|
output modifiers that are clear, but that need to be set: */
|
|
if (modifiers_set != 0) {
|
|
|
|
/* loop over the modifiers that need setting: */
|
|
for (modifier = 0;
|
|
modifier <= TME_KEYBOARD_MODIFIER_MAX;
|
|
modifier++) {
|
|
if (!(modifiers_set & (1 << modifier))) {
|
|
continue;
|
|
}
|
|
|
|
/* try to press a single keysym attached to this modifier: */
|
|
mod_keysym = buffer->tme_keyboard_buffer_int_out0_modkeys[modifier];
|
|
assert (mod_keysym != NULL);
|
|
|
|
/* this keysym can't pressed - if it is, the modifier
|
|
should be set: */
|
|
assert (!TME_KEYBOARD_PRESSED_OUT0(mod_keysym));
|
|
|
|
/* grow the keysyms and press flags arrays. the keysyms
|
|
array always has one extra entry, which will be filled
|
|
with NULL to terminate the array: */
|
|
if (keysym_count == 0) {
|
|
keysyms = tme_new(struct tme_keysym_state *, 2);
|
|
press_flags = tme_new(unsigned int, 1);
|
|
}
|
|
else {
|
|
keysyms = tme_renew(struct tme_keysym_state *, keysyms, keysym_count + 2);
|
|
press_flags = tme_renew(unsigned int, press_flags, keysym_count + 1);
|
|
}
|
|
|
|
/* add this keysym to those arrays: */
|
|
keysyms[keysym_count] = mod_keysym;
|
|
press_flags[keysym_count] = TRUE;
|
|
keysym_count++;
|
|
|
|
/* press this modifier key. this must actually press the key,
|
|
so run the next stage: */
|
|
mod_keysym->tme_keysym_state_out0_pressed++;
|
|
assert (TME_KEYBOARD_PRESSED_OUT0(mod_keysym));
|
|
rc = _tme_keyboard_buffer_out1(buffer,
|
|
mod_keysym,
|
|
_tme_keyboard_event_time_diff(event_time, -1));
|
|
assert (rc == TME_OK);
|
|
|
|
/* set the modifier: */
|
|
buffer->tme_keyboard_buffer_int_out0_modifiers
|
|
|= (1 << modifier);
|
|
}
|
|
}
|
|
|
|
/* remember any changes we made: */
|
|
if (keysyms != NULL) {
|
|
keysyms[keysym_count] = NULL;
|
|
}
|
|
keysym->tme_keysym_state_out0_keysyms = keysyms;
|
|
keysym->tme_keysym_state_out0_press_flags = press_flags;
|
|
|
|
/* run the next stage: */
|
|
return (_tme_keyboard_buffer_out1(buffer,
|
|
keysym,
|
|
event_time));
|
|
}
|
|
|
|
/* otherwise, this is a release: */
|
|
else {
|
|
|
|
/* run the next stage: */
|
|
rc = _tme_keyboard_buffer_out1(buffer,
|
|
keysym,
|
|
event_time);
|
|
assert (rc == TME_OK);
|
|
|
|
/* if this keysym tried to make any modifier changes, undo them: */
|
|
keysyms = keysym->tme_keysym_state_out0_keysyms;
|
|
press_flags = keysym->tme_keysym_state_out0_press_flags;
|
|
if (keysyms != NULL) {
|
|
|
|
/* loop over all of the modifiers we changed: */
|
|
for (; (mod_keysym = *(keysyms++)) != NULL; ) {
|
|
|
|
/* see if this modifier is pressed now: */
|
|
mod_pressed_old = TME_KEYBOARD_PRESSED_OUT0(mod_keysym);
|
|
|
|
/* undo the change we made to this modifier: */
|
|
if (*(press_flags++)) {
|
|
mod_keysym->tme_keysym_state_out0_pressed--;
|
|
}
|
|
else {
|
|
mod_keysym->tme_keysym_state_out0_released--;
|
|
}
|
|
|
|
/* if the state of this modifier has changed, run the next
|
|
stage: */
|
|
if (TME_KEYBOARD_PRESSED_OUT0(mod_keysym) != mod_pressed_old) {
|
|
rc = _tme_keyboard_buffer_out1(buffer,
|
|
mod_keysym,
|
|
_tme_keyboard_event_time_diff(event_time, +1));
|
|
assert (rc == TME_OK);
|
|
}
|
|
}
|
|
|
|
/* forget these modifier changes: */
|
|
tme_free(keysym->tme_keysym_state_out0_keysyms);
|
|
tme_free(keysym->tme_keysym_state_out0_press_flags);
|
|
keysym->tme_keysym_state_out0_keysyms = NULL;
|
|
keysym->tme_keysym_state_out0_press_flags = NULL;
|
|
}
|
|
}
|
|
|
|
/* success: */
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this is input stage two. at this point, we have the input keyboard
|
|
cleaned up to the point where we can have macros for generating
|
|
keysyms that the input keyboard can't generate on its own: */
|
|
static int
|
|
_tme_keyboard_buffer_in2(struct tme_keyboard_buffer_int *buffer,
|
|
struct tme_keysym_state *keysym,
|
|
tme_uint32_t event_time)
|
|
{
|
|
tme_keyboard_keyval_t _keysym;
|
|
struct tme_keyboard_macro **_macro, *macro, *child, *parent;
|
|
int is_press, pressed_old, sub_pressed_old;
|
|
int update, keysym_i;
|
|
struct tme_keysym_state *sub_keysym;
|
|
int rc;
|
|
|
|
_tme_keyboard_debug(buffer,
|
|
"in2",
|
|
keysym->tme_keysym_state_keysym,
|
|
TME_KEYBOARD_PRESSED_IN1(keysym),
|
|
event_time);
|
|
|
|
/* get the keysym: */
|
|
_keysym = keysym->tme_keysym_state_keysym;
|
|
|
|
/* get whether or not this is a press from earlier stages: */
|
|
is_press = TME_KEYBOARD_PRESSED_IN1(keysym);
|
|
|
|
/* remember if this keysym was pressed in this stage before: */
|
|
pressed_old = _TME_KEYBOARD_PRESSED_IN2(keysym, !is_press);
|
|
|
|
/* visit all active macros tree nodes: */
|
|
for (_macro = &buffer->tme_keyboard_buffer_int_in2_macros_active;
|
|
(macro = *_macro) != NULL; ) {
|
|
|
|
/* if this is a press, see if it advances this macro's sequence: */
|
|
if (is_press) {
|
|
|
|
/* ignore this macros tree node if it's a leaf, or if this
|
|
keysym is not a branch out of this node to some child node: */
|
|
if (macro->tme_keyboard_macro_branches == NULL
|
|
|| (child
|
|
= ((struct tme_keyboard_macro *)
|
|
tme_hash_lookup(macro->tme_keyboard_macro_branches,
|
|
tme_keyboard_hash_data_from_keyval(_keysym)))) == NULL) {
|
|
_macro = ¯o->tme_keyboard_macro_active_next;
|
|
continue;
|
|
}
|
|
|
|
/* the child node cannot already be active. if it is, that
|
|
means that earlier stages didn't give us a release for this
|
|
keysym: */
|
|
assert (child->tme_keyboard_macro_active_next == NULL);
|
|
|
|
/* make the child node active: */
|
|
child->tme_keyboard_macro_active_next = macro;
|
|
*_macro = child;
|
|
_macro = ¯o->tme_keyboard_macro_active_next;
|
|
macro = child;
|
|
}
|
|
|
|
/* otherwise, this is a release, so see if this release cancels
|
|
any sequence: */
|
|
else {
|
|
|
|
/* ignore this macros tree node if this is not a release of a
|
|
keysym somewhere on the path from the root to this node: */
|
|
if (_keysym != macro->tme_keyboard_macro_keysym) {
|
|
for (parent = macro->tme_keyboard_macro_parent;
|
|
parent != NULL;
|
|
parent = parent->tme_keyboard_macro_parent) {
|
|
if (_keysym == parent->tme_keyboard_macro_keysym) {
|
|
break;
|
|
}
|
|
}
|
|
if (parent == NULL) {
|
|
_macro = ¯o->tme_keyboard_macro_active_next;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* make this macros tree node no longer active: */
|
|
*_macro = macro->tme_keyboard_macro_active_next;
|
|
macro->tme_keyboard_macro_active_next = NULL;
|
|
}
|
|
|
|
/* if this macro is not a leaf node, continue: */
|
|
if (macro->tme_keyboard_macro_branches != NULL) {
|
|
continue;
|
|
}
|
|
|
|
/* otherwise, this event has either activated or deactivated a
|
|
macro. either do or undo this macro's presses and releases: */
|
|
update = (is_press ? 1 : -1);
|
|
for (keysym_i = macro->tme_keyboard_macro_length;
|
|
keysym_i-- > 0; ) {
|
|
|
|
/* get this keysym's state: */
|
|
sub_keysym = macro->tme_keyboard_macro_keysyms[keysym_i];
|
|
|
|
/* remember if this keysym was pressed in this stage before: */
|
|
sub_pressed_old = TME_KEYBOARD_PRESSED_IN2(sub_keysym);
|
|
|
|
/* update this keysym's state: */
|
|
if (macro->tme_keyboard_macro_press_flags[keysym_i]) {
|
|
sub_keysym->tme_keysym_state_in2_pressed += update;
|
|
}
|
|
else {
|
|
sub_keysym->tme_keysym_state_in2_released += update;
|
|
}
|
|
|
|
/* if this keysym's pressed state in this stage has changed,
|
|
run the next stage, unless this is the keysym that we
|
|
were called with, in which case we'll handle this later: */
|
|
if (sub_keysym != keysym
|
|
&& TME_KEYBOARD_PRESSED_IN2(sub_keysym) != sub_pressed_old) {
|
|
rc = _tme_keyboard_buffer_out0(buffer, sub_keysym, event_time);
|
|
assert (rc == TME_OK);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if this keysym's pressed state in this stage has changed, run
|
|
the next stage, otherwise stop processing this event now: */
|
|
return ((TME_KEYBOARD_PRESSED_IN2(keysym) != pressed_old)
|
|
? _tme_keyboard_buffer_out0(buffer, keysym, event_time)
|
|
: TME_OK);
|
|
}
|
|
|
|
/* this is input stage one. at this point, the input keyboard is
|
|
slightly cleaned up - consecutive presses have had a release
|
|
inserted in between, releases of unpressed keys have been dropped,
|
|
and as many inferred lost events as possible have been generated.
|
|
|
|
this stage finishes cleaning up the input keyboard to the point
|
|
where macros are useful. usually, the only keysyms that will have
|
|
specific behaviors enforced here will be keysyms that are used like
|
|
modifiers in input stage two macros, but that don't behave like
|
|
modifiers on the input keyboard (i.e., they autorepeat) and so
|
|
aren't good for use in multiple-key macros: */
|
|
static int
|
|
_tme_keyboard_buffer_in1(struct tme_keyboard_buffer_int *buffer,
|
|
struct tme_keysym_state *keysym,
|
|
tme_uint32_t event_time)
|
|
{
|
|
|
|
_tme_keyboard_debug(buffer,
|
|
"in1",
|
|
keysym->tme_keysym_state_keysym,
|
|
TME_KEYBOARD_PRESSED_IN0(keysym),
|
|
event_time);
|
|
|
|
/* run the keymode stage function: */
|
|
return (_tme_keymode_stage(buffer,
|
|
&buffer->tme_keyboard_buffer_int_in1_keymode_stage,
|
|
&keysym->tme_keysym_state_in1_keymode,
|
|
TME_KEYBOARD_PRESSED_IN0(keysym),
|
|
event_time));
|
|
}
|
|
|
|
/* this is the bottom half of input stage zero. at this point, events
|
|
inferred by modifier changes have been generated, but otherwise the
|
|
input keyboard hasn't been cleaned up.
|
|
|
|
this half drops releases of keys that we don't think are pressed,
|
|
generates a release in between two consecutive presses, and tracks
|
|
the input modifier mask: */
|
|
static int
|
|
_tme_keyboard_buffer_in0_bottom(struct tme_keyboard_buffer_int *buffer,
|
|
struct tme_keysym_state *keysym,
|
|
const struct tme_keyboard_event *event)
|
|
{
|
|
struct tme_keysym_state *mod_keysym;
|
|
struct tme_keyboard_event event_pseudo;
|
|
int modifier, pressed_old;
|
|
int rc;
|
|
|
|
_tme_keyboard_debug(buffer,
|
|
"in0-bottom",
|
|
keysym->tme_keysym_state_keysym,
|
|
(event->tme_keyboard_event_type
|
|
& TME_KEYBOARD_EVENT_IN0_PRESS_USER),
|
|
event->tme_keyboard_event_time);
|
|
|
|
/* NB: event->tme_keyboard_event_modifiers is always the modifiers
|
|
*mask from immediately before* the event, i.e., if this event is
|
|
*for a modifier keysym, it does not reflect any change the press
|
|
*or release of this keysym will cause: */
|
|
|
|
/* remember if this keysym was pressed in this stage before: */
|
|
pressed_old = TME_KEYBOARD_PRESSED_IN0(keysym);
|
|
|
|
/* see if this keysym is attached to any known modifier: */
|
|
modifier = (buffer->tme_keyboard_buffer_int_in0_have_modifiers
|
|
? keysym->tme_keysym_state_in0_modifier
|
|
: TME_KEYBOARD_MODIFIER_NONE);
|
|
|
|
/* dispatch on the event type: */
|
|
switch (event->tme_keyboard_event_type) {
|
|
|
|
/* an automatic input stage zero press: */
|
|
case TME_KEYBOARD_EVENT_IN0_PRESS_AUTO:
|
|
/* this keysym must not be pressed at all: */
|
|
assert (!pressed_old);
|
|
/* FALLTHROUGH */
|
|
|
|
/* a user input stage zero press: */
|
|
case TME_KEYBOARD_EVENT_IN0_PRESS_USER:
|
|
|
|
/* if this keysym was already pressed, inject a release first -
|
|
you can't press a key twice without releasing it in between: */
|
|
if (pressed_old) {
|
|
|
|
/* make the pseudoevent. assume that the release we dropped
|
|
happened as soon as humanly possible after the press, but
|
|
never after this new press: */
|
|
event_pseudo.tme_keyboard_event_type
|
|
= TME_KEYBOARD_IN0_RELEASE_EVENT(keysym->tme_keysym_state_in0_pressed);
|
|
event_pseudo.tme_keyboard_event_time
|
|
= _tme_keyboard_event_time_diff(keysym->tme_keysym_state_in0_press_time,
|
|
TME_KEYBOARD_SHORTEST_DOUBLE_MSEC);
|
|
if (_tme_keyboard_event_time_subtract(event_pseudo.tme_keyboard_event_time,
|
|
event->tme_keyboard_event_time) <= 0) {
|
|
event_pseudo.tme_keyboard_event_time
|
|
= _tme_keyboard_event_time_diff(event->tme_keyboard_event_time, -1);
|
|
}
|
|
event_pseudo.tme_keyboard_event_modifiers
|
|
= buffer->tme_keyboard_buffer_int_in0_modifiers;
|
|
|
|
/* recurse with this pseudoevent: */
|
|
rc = _tme_keyboard_buffer_in0_bottom(buffer,
|
|
keysym,
|
|
&event_pseudo);
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
/* this keysym is now pressed: */
|
|
keysym->tme_keysym_state_in0_pressed = event->tme_keyboard_event_type;
|
|
keysym->tme_keysym_state_in0_press_time = event->tme_keyboard_event_time;
|
|
|
|
/* set any modifier in the mask: */
|
|
assert (buffer->tme_keyboard_buffer_int_in0_modifiers
|
|
== event->tme_keyboard_event_modifiers);
|
|
if (modifier != TME_KEYBOARD_MODIFIER_NONE) {
|
|
buffer->tme_keyboard_buffer_int_in0_modifiers |= (1 << modifier);
|
|
}
|
|
break;
|
|
|
|
/* an automatic input stage zero release :*/
|
|
case TME_KEYBOARD_EVENT_IN0_RELEASE_AUTO:
|
|
assert (pressed_old == TME_KEYBOARD_EVENT_IN0_PRESS_AUTO);
|
|
/* FALLTHROUGH */
|
|
|
|
/* a user input stage zero release: */
|
|
case TME_KEYBOARD_EVENT_IN0_RELEASE_USER:
|
|
|
|
/* if this keysym wasn't already pressed, stop processing this
|
|
event: */
|
|
if (!pressed_old) {
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this keysym is no longer pressed: */
|
|
keysym->tme_keysym_state_in0_pressed = FALSE;
|
|
|
|
/* if this keysym is attached to a modifier: */
|
|
if (modifier != TME_KEYBOARD_MODIFIER_NONE) {
|
|
|
|
/* if we can find no keysym attached to this modifier that is
|
|
still pressed, clear the modifier in the mask: */
|
|
for (mod_keysym = buffer->tme_keyboard_buffer_int_in0_modkeys[modifier];
|
|
mod_keysym != NULL;
|
|
mod_keysym = mod_keysym->tme_keysym_state_in0_modifier_next) {
|
|
if (mod_keysym->tme_keysym_state_in0_pressed) {
|
|
break;
|
|
}
|
|
}
|
|
if (mod_keysym == NULL) {
|
|
buffer->tme_keyboard_buffer_int_in0_modifiers &= ~(1 << modifier);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* run the next stage: */
|
|
return (_tme_keyboard_buffer_in1(buffer,
|
|
keysym,
|
|
event->tme_keyboard_event_time));
|
|
}
|
|
|
|
/* this is the top half of input stage zero. this gets raw
|
|
events from the input keyboard. if we get a modifiers mask with
|
|
each event, and we know which input keyboard keysyms are attached
|
|
to which modifiers, we can often infer presses and releases of
|
|
these keysyms when our caller has missed those events.
|
|
|
|
For example, this works well under X11 when the Caps_Lock or
|
|
Num_Lock key "locks" and its state changes while our window doesn't
|
|
have focus. When we do regain focus, we don't get press or release
|
|
events for those changes, but we can get the current modifiers
|
|
mask: */
|
|
static int
|
|
_tme_keyboard_buffer_in0(struct tme_keyboard_buffer_int *buffer,
|
|
const struct tme_keyboard_event *event)
|
|
{
|
|
struct tme_keysym_state *keysym, *mod_keysym, *other_keysym;
|
|
struct tme_keyboard_event event_pseudo;
|
|
int modifier;
|
|
tme_keyboard_modifiers_t modifiers, modifiers_set, modifiers_clear;
|
|
int rc;
|
|
|
|
_tme_keyboard_debug(buffer,
|
|
"in0-top",
|
|
event->tme_keyboard_event_keyval,
|
|
(event->tme_keyboard_event_type
|
|
== TME_KEYBOARD_EVENT_PRESS),
|
|
event->tme_keyboard_event_time);
|
|
|
|
assert (event->tme_keyboard_event_time
|
|
!= TME_KEYBOARD_EVENT_TIME_UNDEF);
|
|
|
|
/* look up this keysym: */
|
|
keysym
|
|
= ((struct tme_keysym_state *)
|
|
tme_hash_lookup(buffer->tme_keyboard_buffer_int_keysyms_state,
|
|
tme_keyboard_hash_data_from_keyval(event->tme_keyboard_event_keyval)));
|
|
|
|
|
|
/* if input stage zero has modifier information: */
|
|
if (buffer->tme_keyboard_buffer_int_in0_have_modifiers) {
|
|
|
|
/* NB: event->tme_keyboard_event_modifiers is always the modifiers
|
|
mask from immediately *before* the event, i.e., if this event
|
|
is for a modifier keysym, it does not reflect any change this
|
|
press or release of this keysym will cause to that mask: */
|
|
modifiers = event->tme_keyboard_event_modifiers;
|
|
|
|
/* get the mask of modifiers that been cleared and set unbeknownst
|
|
to us before this event: */
|
|
modifiers_clear =
|
|
(buffer->tme_keyboard_buffer_int_in0_modifiers
|
|
& ~modifiers
|
|
& buffer->tme_keyboard_buffer_int_in0_have_modifiers);
|
|
modifiers_set =
|
|
(modifiers
|
|
& ~buffer->tme_keyboard_buffer_int_in0_modifiers
|
|
& buffer->tme_keyboard_buffer_int_in0_have_modifiers);
|
|
|
|
/* generate the appropriate release events for all keysyms
|
|
attached to modifiers that have been cleared: */
|
|
if (modifiers_clear != 0) {
|
|
|
|
/* loop over the modifiers that need clearing: */
|
|
for (modifier = 0;
|
|
modifier <= TME_KEYBOARD_MODIFIER_MAX;
|
|
modifier++) {
|
|
if (!(modifiers_clear & (1 << modifier))) {
|
|
continue;
|
|
}
|
|
|
|
/* release all of the keysyms: */
|
|
for (mod_keysym = buffer->tme_keyboard_buffer_int_in0_modkeys[modifier];
|
|
mod_keysym != NULL;
|
|
mod_keysym = mod_keysym->tme_keysym_state_in0_modifier_next) {
|
|
|
|
/* ignore this keysym if it isn't pressed: */
|
|
if (!mod_keysym->tme_keysym_state_in0_pressed) {
|
|
continue;
|
|
}
|
|
|
|
/* make the pseudoevent: */
|
|
event_pseudo.tme_keyboard_event_type
|
|
= TME_KEYBOARD_IN0_RELEASE_EVENT(mod_keysym->tme_keysym_state_in0_pressed);
|
|
event_pseudo.tme_keyboard_event_time
|
|
= _tme_keyboard_event_time_diff(event->tme_keyboard_event_time, -1);
|
|
event_pseudo.tme_keyboard_event_modifiers
|
|
= buffer->tme_keyboard_buffer_int_in0_modifiers;
|
|
|
|
/* call the bottom half with this pseudoevent: */
|
|
rc = _tme_keyboard_buffer_in0_bottom(buffer,
|
|
mod_keysym,
|
|
&event_pseudo);
|
|
assert (rc == TME_OK);
|
|
}
|
|
}
|
|
|
|
/* all of the modifiers that needed clearing must now be clear: */
|
|
assert ((buffer->tme_keyboard_buffer_int_in0_modifiers
|
|
& modifiers_clear) == 0);
|
|
}
|
|
|
|
/* generate a press event for a single keysym attached to each
|
|
modifier that has been set: */
|
|
if (modifiers_set != 0) {
|
|
|
|
/* loop over the modifiers that need setting: */
|
|
for (modifier = 0;
|
|
modifier <= TME_KEYBOARD_MODIFIER_MAX;
|
|
modifier++) {
|
|
if (!(modifiers_set & (1 << modifier))) {
|
|
continue;
|
|
}
|
|
|
|
/* press the first keysym attached to this modifier: */
|
|
mod_keysym = buffer->tme_keyboard_buffer_int_in0_modkeys[modifier];
|
|
assert (mod_keysym != NULL);
|
|
|
|
/* make the pseudoevent: */
|
|
event_pseudo.tme_keyboard_event_type
|
|
= TME_KEYBOARD_EVENT_IN0_PRESS_AUTO;
|
|
event_pseudo.tme_keyboard_event_time
|
|
= _tme_keyboard_event_time_diff(event->tme_keyboard_event_time, -1);
|
|
event_pseudo.tme_keyboard_event_modifiers
|
|
= buffer->tme_keyboard_buffer_int_in0_modifiers;
|
|
|
|
/* call the bottom half with this pseudoevent: */
|
|
rc = _tme_keyboard_buffer_in0_bottom(buffer,
|
|
mod_keysym,
|
|
&event_pseudo);
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
/* all of the modifiers that needed setting must now be set: */
|
|
assert ((buffer->tme_keyboard_buffer_int_in0_modifiers
|
|
& modifiers_set) == modifiers_set);
|
|
}
|
|
|
|
/* if the keysym in the event is undefined, we don't need to
|
|
process this event any more. events with an undefined keysym
|
|
can be used whenever the caller thinks that it has dropped
|
|
keyboard events, and wants to do what it can to update the
|
|
keyboard, and it can at least get the true current modifiers
|
|
mask. this is common under X11: */
|
|
if (event->tme_keyboard_event_keyval
|
|
== TME_KEYBOARD_KEYVAL_UNDEF) {
|
|
return (TME_OK);
|
|
}
|
|
}
|
|
|
|
/* we must have a defined keysym: */
|
|
assert (event->tme_keyboard_event_keyval
|
|
!= TME_KEYBOARD_KEYVAL_UNDEF);
|
|
|
|
/* if we have no state for the keysym in this event, this keysym
|
|
isn't controlled by any of the input stages: */
|
|
if (keysym == NULL) {
|
|
|
|
/* if there are output stages, having no state for this keysym
|
|
means that this keysym doesn't exist on the output keyboard.
|
|
we can stop processing this event now: */
|
|
if (!buffer->tme_keyboard_buffer_int_out0_passthrough) {
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* otherwise, simply buffer this event: */
|
|
event_pseudo = *event;
|
|
switch (event->tme_keyboard_event_type) {
|
|
case TME_KEYBOARD_EVENT_IN0_PRESS_USER:
|
|
case TME_KEYBOARD_EVENT_IN0_PRESS_AUTO:
|
|
event_pseudo.tme_keyboard_event_type = TME_KEYBOARD_EVENT_PRESS;
|
|
break;
|
|
case TME_KEYBOARD_EVENT_IN0_RELEASE_USER:
|
|
case TME_KEYBOARD_EVENT_IN0_RELEASE_AUTO:
|
|
event_pseudo.tme_keyboard_event_type = TME_KEYBOARD_EVENT_RELEASE;
|
|
break;
|
|
default: abort();
|
|
}
|
|
event_pseudo.tme_keyboard_event_modifiers = 0;
|
|
return (_tme_keyboard_buffer_copyin(buffer, &event_pseudo));
|
|
}
|
|
|
|
/* if this event has a keycode: */
|
|
if (event->tme_keyboard_event_keycode
|
|
!= TME_KEYBOARD_KEYVAL_UNDEF) {
|
|
|
|
/* see if this keycode is already pressed in this stage: */
|
|
other_keysym
|
|
= ((struct tme_keysym_state *)
|
|
tme_hash_lookup(buffer->tme_keyboard_buffer_int_in0_keycodes,
|
|
tme_keyboard_hash_data_from_keyval(event->tme_keyboard_event_keycode)));
|
|
|
|
/* if this keycode is already pressed by another keysym in this
|
|
stage, release the other keysym first: */
|
|
if (other_keysym != NULL
|
|
&& other_keysym != keysym
|
|
&& TME_KEYBOARD_PRESSED_IN0(other_keysym)) {
|
|
|
|
/* make the pseudoevent: */
|
|
event_pseudo.tme_keyboard_event_type
|
|
= TME_KEYBOARD_IN0_RELEASE_EVENT(other_keysym->tme_keysym_state_in0_pressed);
|
|
event_pseudo.tme_keyboard_event_time
|
|
= _tme_keyboard_event_time_diff(event->tme_keyboard_event_time, -1);
|
|
event_pseudo.tme_keyboard_event_modifiers
|
|
= buffer->tme_keyboard_buffer_int_in0_modifiers;
|
|
|
|
/* call the bottom half with this pseudoevent: */
|
|
rc = _tme_keyboard_buffer_in0_bottom(buffer,
|
|
other_keysym,
|
|
&event_pseudo);
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
/* if this is a press, remember that this keycode is pressed,
|
|
else forget that this keycode is pressed: */
|
|
if (event->tme_keyboard_event_type == TME_KEYBOARD_EVENT_PRESS) {
|
|
tme_hash_insert(buffer->tme_keyboard_buffer_int_in0_keycodes,
|
|
tme_keyboard_hash_data_from_keyval(event->tme_keyboard_event_keycode),
|
|
(tme_hash_data_t) keysym);
|
|
}
|
|
else {
|
|
tme_hash_remove(buffer->tme_keyboard_buffer_int_in0_keycodes,
|
|
tme_keyboard_hash_data_from_keyval(event->tme_keyboard_event_keycode));
|
|
}
|
|
}
|
|
|
|
/* call the bottom half with this event: */
|
|
return (_tme_keyboard_buffer_in0_bottom(buffer,
|
|
keysym,
|
|
event));
|
|
}
|
|
|
|
/* this copies a keyboard event into the buffer: */
|
|
int
|
|
tme_keyboard_buffer_copyin(struct tme_keyboard_buffer *_buffer,
|
|
const struct tme_keyboard_event *event)
|
|
{
|
|
struct tme_keyboard_buffer_int *buffer;
|
|
|
|
/* recover our data structure: */
|
|
buffer = (struct tme_keyboard_buffer_int *) _buffer;
|
|
|
|
/* run input stage zero: */
|
|
return (_tme_keyboard_buffer_in0(buffer, event));
|
|
}
|
|
|
|
/* this copies a keyval out of a keyboard buffer: */
|
|
int
|
|
tme_keyboard_buffer_copyout(struct tme_keyboard_buffer *buffer,
|
|
struct tme_keyboard_event *event)
|
|
{
|
|
unsigned int buffer_tail, buffer_size_mask;
|
|
|
|
buffer_tail = buffer->tme_keyboard_buffer_tail;
|
|
buffer_size_mask = buffer->tme_keyboard_buffer_size - 1;
|
|
|
|
/* if the buffer is empty: */
|
|
if (buffer_tail == buffer->tme_keyboard_buffer_head) {
|
|
return (EAGAIN);
|
|
}
|
|
|
|
/* get an event out of the buffer: */
|
|
*event = buffer->tme_keyboard_buffer_events[buffer_tail];
|
|
|
|
/* advance the tail: */
|
|
buffer->tme_keyboard_buffer_tail = (buffer_tail + 1) & buffer_size_mask;
|
|
return (TME_OK);
|
|
}
|