mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 04:23:36 +01:00
keydev: add a keyboard device layer to abstract away globals
The new keyboard device (keydev) interface implements the kernel's view of a keyboard providing input events. Its main role is to abstract all the globals of the KEYSC driver and getkey functions into a separate object: the "keyboard device". The device implements event transformations such as modifiers and repeats, instead of leaving them to getkey. While this can seem surprising at first, a real keyboard controller is responsible for repeats and modifier actions depend on the state of the keyboard which is only tracked in real-time. In this commit, getkey() has not changed yet apart from indirectly using the keydev interface with pollevent(). It will be changed soon to use event transforms in keydev_read(), and will be left in charge of providing repeat profiles, handling return-to-menu, backlight changes and timeouts, all of which are user convenience features.
This commit is contained in:
parent
97ca5ba82f
commit
dd564f094a
7 changed files with 647 additions and 166 deletions
|
@ -29,6 +29,7 @@ set(SOURCES_COMMON
|
|||
src/keysc/getkey.c
|
||||
src/keysc/iokbd.c
|
||||
src/keysc/keycodes.c
|
||||
src/keysc/keydev.c
|
||||
src/keysc/keysc.c
|
||||
src/kprint/kprint.c
|
||||
src/kprint/kformat_fp.c
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
|
||||
access sizes silently fail - honestly you don't want this to happen */
|
||||
#define GPACKED(x) __attribute__((packed, aligned(x)))
|
||||
/* Packed enumerations */
|
||||
#define GPACKEDENUM __attribute__((packed))
|
||||
/* Transparent unions */
|
||||
#define GTRANSPARENT __attribute__((transparent_union))
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <stddef.h>
|
||||
/* For all fixed-width integer types */
|
||||
#include <stdint.h>
|
||||
/* For human-readable boolean types */
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Fixed-width types for bit fields are quite meaningless */
|
||||
typedef unsigned int uint;
|
||||
|
|
264
include/gint/drivers/keydev.h
Normal file
264
include/gint/drivers/keydev.h
Normal file
|
@ -0,0 +1,264 @@
|
|||
//---
|
||||
// gint:keydev - Kernel's keyboard input devices
|
||||
//---
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
|
||||
#ifndef GINT_KEYDEV
|
||||
#define GINT_KEYDEV
|
||||
|
||||
/* Size of the buffer event queue */
|
||||
#define KEYBOARD_QUEUE_SIZE 32
|
||||
|
||||
/* keydev_event_t: Change of state in a full row */
|
||||
typedef struct {
|
||||
/* Device time for the event */
|
||||
uint16_t time;
|
||||
/* Keys that changed state */
|
||||
uint8_t changed;
|
||||
/* Row number */
|
||||
uint8_t row :4;
|
||||
/* Type of change, either KEYEV_DOWN or KEYEV_UP */
|
||||
uint8_t kind :4;
|
||||
|
||||
} keydev_event_t;
|
||||
|
||||
/* Event transforms
|
||||
|
||||
Every keyboard input device has a built-in event stream editor that can
|
||||
transform the events in various ways to change the abstraction details, from
|
||||
the raw pollevent() with no transforms to the highest-level getkey().
|
||||
|
||||
Because many transforms like <Instant Modifiers> need to observe the state
|
||||
of the keyboard at the time of the event to transform, this process needs to
|
||||
be performed right when events are requested and cannot be done later
|
||||
without keeping around a lot of information about past states.
|
||||
|
||||
Just as switching from pollevent() to getkey() requires some caution,
|
||||
changing the transform settings while keys are pressed or modifiers are
|
||||
active minimally impacts the output of the editor. Here is a summary of the
|
||||
interactions:
|
||||
|
||||
* <Delete Modifiers> and <Delete Releases> are only concerned about the
|
||||
current event and can be turned ON and OFF at any time without problems.
|
||||
|
||||
* <Instant SHIFT> and <Instant ALPHA> only look at the state of the keyboard
|
||||
with keydev_keydown() and are not affected by other options.
|
||||
|
||||
* <Delayed SHIFT> and <Delayed ALPHA> only trigger when a SHIFT or ALPHA
|
||||
press-release is performed when the keyboard is idle.
|
||||
-> If turned ON while a key is pressed, nothing happens until a further
|
||||
SHIFT or ALPHA key press while the keyboard is idle.
|
||||
-> If turned OFF while (1) a modifier key is being pressed, or (2) a
|
||||
modifier key has been pressed and released (charging up the delayed
|
||||
modifier), the charge is lost.
|
||||
Thus turning delayed modifiers ON->OFF->ON drops the current charge.
|
||||
|
||||
* <Repeats> has the most interactions.
|
||||
-> Toggling <Instant SHIFT> or <Instant ALPHA> during a repeat takes
|
||||
effect immediately, so a streak can start with instant modifiers
|
||||
disabled, enable them mid-flight, and start producing repeating events
|
||||
with instant modifiers.
|
||||
-> Switching <Delayed SHIFT> or <Delayed ALPHA> during a modified streak
|
||||
drops the modifier immediately.
|
||||
-> Switching ON any modifier transform breaks a SHIFT or ALPHA streak.
|
||||
-> Switching OFF all modifier transforms allows a currently-pressed SHIFT
|
||||
or ALPHA key to start a repeat streak, which (due to the way candidates
|
||||
for repeats are considered) can only be used to turn a incomplete
|
||||
delayed modifier charge or an unused instant modifier into a streak.
|
||||
|
||||
Generally speaking changing options while keys are pressed is safe, but it
|
||||
is better to do it while the device is idle for more consistency.
|
||||
|
||||
pollevent() provides the raw events and bypasses the transforms. However,
|
||||
the information needed to resume transforming is still tracked when
|
||||
generating raw events: for instance, which key is a candidate for repetition
|
||||
is still tracked (by contrast, getkey() needs special code to catch up if
|
||||
pollevent() unqueued events that it needed). Delayed modifiers are not
|
||||
tracked because they can be charged only if the transform is enabled when
|
||||
the modifier key is pressed. */
|
||||
enum {
|
||||
/* Delayed SHIFT: Pressing then immediately releasing SHIFT when the
|
||||
keyboard is idle applies SHIFT to the next repeat streak. */
|
||||
KEYDEV_TR_DELAYED_SHIFT = 0x01,
|
||||
/* Delayed ALPHA: Idem with the ALPHA key */
|
||||
KEYDEV_TR_DELAYED_ALPHA = 0x02,
|
||||
/* Instant SHIFT: Each individual event of every repeat streak gets
|
||||
SHIFT applied if SHIFT is pressed at the time of the repeat. */
|
||||
KEYDEV_TR_INSTANT_SHIFT = 0x04,
|
||||
/* Instant ALPHA: Idem with the ALPHA key */
|
||||
KEYDEV_TR_INSTANT_ALPHA = 0x08,
|
||||
/* Repeats: Keys are repeated according to a repeat filter function */
|
||||
KEYDEV_TR_REPEATS = 0x10,
|
||||
/* Delete Modifiers: Remove modifier keys from generated events, which
|
||||
are generally useless when delayed/instant SHIFT/ALPHA are used */
|
||||
KEYDEV_TR_DELETE_MODIFIERS = 0x20,
|
||||
/* Delete Releases: Remove KEYEV_UP events */
|
||||
KEYDEV_TR_DELETE_RELEASES = 0x40,
|
||||
};
|
||||
|
||||
/* keydev_transform_t: Full specification for transforms on the event stream */
|
||||
typedef struct {
|
||||
/* List of enabled transforms. The order is cannot be changed because
|
||||
any other order than the default produces useless results. This is
|
||||
an OR-combination of KEYDEV_TR_* flags. */
|
||||
int enabled;
|
||||
|
||||
/* Repeater function. This function is called whenever (key) is pressed
|
||||
(KEYEV_DOWN) or repeated (KEYEV_HOLD), and should indicate how long
|
||||
to wait until the next repeat. This setting is meaningful only if
|
||||
<Repeats> is enabled.
|
||||
|
||||
-> (key) is the key currently being repeated.
|
||||
-> (duration) is the time since it was first pressed (us).
|
||||
-> (count) is the number of repeats produced so far.
|
||||
|
||||
The function should either return -1 to disable all further repeats,
|
||||
or a positive number of microseconds until the next repeat. Note
|
||||
that the precision of the delay is limited by the speed at which the
|
||||
keyboard is scanned, which is nowhere near microsecond-level. By
|
||||
default (128 Hz) the precision is about 7.8 ms. */
|
||||
int (*repeater)(int key, int duration, int count);
|
||||
|
||||
} GPACKEDENUM keydev_transform_t;
|
||||
|
||||
/* keydev_t: Keyboard device
|
||||
|
||||
This structure represents the state and settings of a keyboard device that
|
||||
provides input to the kernel and applications. The notion of keyboard device
|
||||
is useful for demo/replays that input events without the physical keyboard,
|
||||
and a couple of corner uses like control over USB.
|
||||
|
||||
The keyboard device has built-in event transformations, which modifty the
|
||||
stream of events by adding information, combining modifiers, and removing
|
||||
undesired events. Because the event transformation reky on the current state
|
||||
of the keyboard, they must be run by the driver whenever events are read, so
|
||||
they are tied to the device.
|
||||
|
||||
The default keyboard functions pollevent(), waitevent(), getkey() and
|
||||
keydown() are shortcuts for keydev functions using the physical keyboard as
|
||||
their input. */
|
||||
typedef struct {
|
||||
/* Latest state of keys we are aware of. At every processing step, the
|
||||
different between this and the fresh information is queued and this
|
||||
is updated. state_now is identical to the real state obtained from
|
||||
the device unless earlier events failed to be queued, in which case
|
||||
a difference is maintained so they will be reconsidered later. */
|
||||
GALIGNED(4) uint8_t state_now[12];
|
||||
/* State of keys based on produced events. (state_queue + queue) is
|
||||
always identical to (state_now). When the queue is empty both states
|
||||
are the same. This is the user's view of the keyboard. */
|
||||
GALIGNED(4) uint8_t state_queue[12];
|
||||
/* Current device time in scanning-ticks */
|
||||
uint time;
|
||||
/* Last time when repeats were considered */
|
||||
uint time_repeats;
|
||||
|
||||
/* Event queue (circular buffer) */
|
||||
keydev_event_t queue[KEYBOARD_QUEUE_SIZE];
|
||||
/* Next event in queue, position after last event in queue */
|
||||
int8_t queue_next;
|
||||
int8_t queue_end;
|
||||
/* Number of events lost because of missing queue space */
|
||||
uint events_lost;
|
||||
/* Crafted events waiting to be picked up */
|
||||
key_event_t out[8];
|
||||
int out_size;
|
||||
|
||||
/* Event transforms */
|
||||
keydev_transform_t tr;
|
||||
|
||||
// <Delayed Modifiers>
|
||||
|
||||
/* delayed_* is set when the delayed modifier is active (after a press/
|
||||
release). pressed_* is set between the press and the release. */
|
||||
uint pressed_shift :1;
|
||||
uint pressed_alpha :1;
|
||||
uint delayed_shift :1;
|
||||
uint delayed_alpha :1;
|
||||
uint :4;
|
||||
|
||||
// <Repeats>
|
||||
|
||||
/* Candidate key for repeats (or 0 if no key is candidate yet) */
|
||||
int rep_key;
|
||||
/* Number of repeats alreay sent */
|
||||
int rep_count;
|
||||
/* Time since key was first pressed (us) */
|
||||
int rep_time;
|
||||
/* Delay until next repeat, set by the repeat planner (us) */
|
||||
int rep_delay;
|
||||
|
||||
} keydev_t;
|
||||
|
||||
/* keydev_std(): Standard keyboard input device
|
||||
Returns the keyboard device structure for the calculator's keyboard. */
|
||||
keydev_t *keydev_std(void);
|
||||
|
||||
//---
|
||||
// Functions for keyboard device drivers
|
||||
//---
|
||||
|
||||
/* keydev_init(): Initialize a keyboard device to its idle state */
|
||||
void keydev_init(keydev_t *d);
|
||||
|
||||
/* keydev_process_state(): Process the new keyboard states for events
|
||||
This function compares the provided scanned state with the stored state and
|
||||
generates events to catch up. This should be used by scan-based keyboard
|
||||
devices after a scan. */
|
||||
void keydev_process_state(keydev_t *d, uint8_t state[12]);
|
||||
|
||||
/* keydev_process_key(): Process a new key state for events
|
||||
This function compares the provided key state with the stored state and
|
||||
generates events to catch up. This should be used by event-based keyboard
|
||||
devices (such as demo replays) to feed in new events. */
|
||||
void keydev_process_key(keydev_t *d, int keycode, bool state);
|
||||
|
||||
/* keydev_tick(): Prepare the next tick
|
||||
This function maintains time trackers in the device and should be called in
|
||||
each frame after the scanning is finished and the keydev_process_*()
|
||||
functions have been called. The timer elapsed since the last tick should
|
||||
be specified as well. */
|
||||
void keydev_tick(keydev_t *d, uint us);
|
||||
|
||||
//---
|
||||
// Low-level API to read events from the device
|
||||
//---
|
||||
|
||||
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue
|
||||
|
||||
This source provides the queued KEYEV_UP and KEYEV_DOWN events generated
|
||||
from the regular scans in chronological order. It does not generate the
|
||||
KEYEV_HOLD events though, keyev_repeat_event() must be used to obtain these
|
||||
at the end of every tick. */
|
||||
key_event_t keydev_unqueue_event(keydev_t *d);
|
||||
|
||||
/* keydev_repeat_event(): Generate a repeat event if applicable
|
||||
|
||||
At the end of every scan tick (or later if the application is unable to keep
|
||||
up), this source will generate a repeat event if the repeat transform is
|
||||
enabled and the conditions for a repeat are satisfied. */
|
||||
key_event_t keydev_repeat_event(keydev_t *d);
|
||||
|
||||
/* keydev_idle(): Check if all keys are released
|
||||
A list of keys to ignore can be specified as variable arguments. The list
|
||||
must be terminated by a 0 keycode. */
|
||||
bool keydev_idle(keydev_t *d, ...);
|
||||
|
||||
//---
|
||||
// High-level API to read from the device
|
||||
//---
|
||||
|
||||
/* keydev_keydown(): Check if a key is down according to generated events */
|
||||
bool keydev_keydown(keydev_t *d, int key);
|
||||
|
||||
/* keydev_transform(): Obtain current transform parameters */
|
||||
keydev_transform_t keydev_transform(keydev_t *d);
|
||||
|
||||
/* keydev_set_transform(): Set transform parameters */
|
||||
void keydev_set_transform(keydev_t *d, keydev_transform_t tr);
|
||||
|
||||
/* keydev_read(): Retrieve the next transformed event */
|
||||
key_event_t keydev_read(keydev_t *d);
|
||||
|
||||
#endif /* GINT_KEYDEV */
|
|
@ -114,12 +114,6 @@ enum
|
|||
KEYEV_HOLD = 3, /* A key that was pressed has been held down */
|
||||
};
|
||||
|
||||
/* Size of the buffer event queue, can be customized using gint's configure
|
||||
script before compiling the library. Better be a power of 2. */
|
||||
#ifndef KEYBOARD_QUEUE_SIZE
|
||||
#define KEYBOARD_QUEUE_SIZE 32
|
||||
#endif
|
||||
|
||||
/* Keyboard frequency analysis is a runtime setting since gint 2.4. This macro
|
||||
is preserved for compatibility until gint 3. */
|
||||
#define KEYBOARD_SCAN_FREQUENCY keysc_scan_frequency()
|
||||
|
|
349
src/keysc/keydev.c
Normal file
349
src/keysc/keydev.c
Normal file
|
@ -0,0 +1,349 @@
|
|||
//---
|
||||
// gint:keydev - Generic input handling on keyboard devices
|
||||
//---
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/drivers/keydev.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/std/string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void keydev_init(keydev_t *d)
|
||||
{
|
||||
memset(d, 0, sizeof *d);
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver event generation
|
||||
//---
|
||||
|
||||
/* queue_push(): Add an event in a device's buffer
|
||||
Returns false if the event cannot be pushed. */
|
||||
static bool queue_push(keydev_t *d, keydev_event_t e)
|
||||
{
|
||||
int next = (d->queue_end + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
if(next == d->queue_next)
|
||||
{
|
||||
d->events_lost++;
|
||||
return false;
|
||||
}
|
||||
|
||||
d->queue[d->queue_end] = e;
|
||||
d->queue_end = next;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* queue_poll(): Generate key events from the buffer
|
||||
Sets (*e) and returns true on success, otherwise false. */
|
||||
static bool queue_poll(keydev_t *d, keydev_event_t *e)
|
||||
{
|
||||
if(d->queue_next == d->queue_end) return false;
|
||||
|
||||
*e = d->queue[d->queue_next];
|
||||
d->queue_next = (d->queue_next + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* keydev_process_state(): Process the new keyboard states for events */
|
||||
void keydev_process_state(keydev_t *d, uint8_t scan[12])
|
||||
{
|
||||
/* Compare new data with the internal state. Push releases before
|
||||
presses so that a key change occurring within a single analysis
|
||||
frame can be performed. This happens all the time when going back to
|
||||
the main MENU via gint_osmenu() on a keybind. */
|
||||
for(int row = 0; row < 12; row++)
|
||||
{
|
||||
int diff = ~scan[row] & d->state_now[row];
|
||||
if(!diff) continue;
|
||||
|
||||
/* Update internal status if the event can be pushed */
|
||||
keydev_event_t e = { d->time, diff, row, KEYEV_UP };
|
||||
if(queue_push(d, e)) d->state_now[row] &= scan[row];
|
||||
}
|
||||
for(int row = 0; row < 12; row++)
|
||||
{
|
||||
int diff = scan[row] & ~d->state_now[row];
|
||||
if(!diff) continue;
|
||||
|
||||
keydev_event_t e = { d->time, diff, row, KEYEV_DOWN };
|
||||
if(queue_push(d, e)) d->state_now[row] |= scan[row];
|
||||
}
|
||||
}
|
||||
|
||||
/* keydev_process_key(): Process a new key state for events */
|
||||
void keydev_process_key(keydev_t *d, int keycode, bool state)
|
||||
{
|
||||
/* If the key has changed state, push an event */
|
||||
int row = (keycode >> 4) ^ 1;
|
||||
int col = 0x80 >> (keycode & 0x7);
|
||||
|
||||
int prev = d->state_now[row] & col;
|
||||
if(state && !prev)
|
||||
{
|
||||
keydev_event_t e = { d->time, col, row, KEYEV_DOWN };
|
||||
if(queue_push(d, e)) d->state_now[row] |= col;
|
||||
}
|
||||
else if(!state && prev)
|
||||
{
|
||||
keydev_event_t e = { d->time, col, row, KEYEV_UP };
|
||||
if(queue_push(d, e)) d->state_now[row] &= ~col;
|
||||
}
|
||||
}
|
||||
|
||||
void keydev_tick(keydev_t *d, uint us)
|
||||
{
|
||||
d->time++;
|
||||
|
||||
if(d->rep_key != 0)
|
||||
{
|
||||
if(d->rep_delay >= 0)
|
||||
d->rep_delay = max(d->rep_delay - (int)us, 0);
|
||||
d->rep_time += us;
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Keyboard event generation
|
||||
//---
|
||||
|
||||
static bool can_repeat(keydev_t *d, int key)
|
||||
{
|
||||
int tr = d->tr.enabled;
|
||||
int shift = tr & (KEYDEV_TR_DELAYED_SHIFT | KEYDEV_TR_INSTANT_SHIFT);
|
||||
int alpha = tr & (KEYDEV_TR_DELAYED_ALPHA | KEYDEV_TR_INSTANT_ALPHA);
|
||||
|
||||
return !(key == KEY_SHIFT && shift) && !(key == KEY_ALPHA && alpha);
|
||||
}
|
||||
|
||||
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue */
|
||||
key_event_t keydev_unqueue_event(keydev_t *d)
|
||||
{
|
||||
/* Every device event is unfolded into up to 8 keyboard events, stored
|
||||
temporarily in the driver's structure. */
|
||||
keydev_event_t e;
|
||||
key_event_t kev = { .type = KEYEV_NONE, .time = d->time };
|
||||
|
||||
/* If there are no events, generate some */
|
||||
if(d->out_size == 0)
|
||||
{
|
||||
if(!queue_poll(d, &e)) return kev;
|
||||
int changed = e.changed;
|
||||
kev.type = e.kind;
|
||||
|
||||
for(int code = ((e.row ^ 1) << 4) | 0x7; code & 0x7; code--)
|
||||
{
|
||||
if(changed & 1)
|
||||
{
|
||||
kev.key = code;
|
||||
d->out[d->out_size++] = kev;
|
||||
}
|
||||
changed >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return one of the available events */
|
||||
kev = d->out[--(d->out_size)];
|
||||
|
||||
/* Update the event state accordingly */
|
||||
int row = (kev.key >> 4) ^ 1;
|
||||
int col = 0x80 >> (kev.key & 0x7);
|
||||
|
||||
if(kev.type == KEYEV_DOWN)
|
||||
{
|
||||
d->state_queue[row] |= col;
|
||||
/* Mark this key as the currently repeating one */
|
||||
if(d->rep_key == 0 && can_repeat(d, kev.key))
|
||||
{
|
||||
d->rep_key = kev.key;
|
||||
d->rep_count = -1;
|
||||
d->rep_time = 0;
|
||||
d->rep_delay = 0;
|
||||
}
|
||||
}
|
||||
if(kev.type == KEYEV_UP)
|
||||
{
|
||||
d->state_queue[row] &= ~col;
|
||||
/* End the current repeating streak */
|
||||
if(d->rep_key == kev.key)
|
||||
{
|
||||
d->rep_key = 0;
|
||||
d->rep_count = -1;
|
||||
d->rep_time = -1;
|
||||
d->rep_delay = -1;
|
||||
d->delayed_shift = 0;
|
||||
d->delayed_alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return kev;
|
||||
}
|
||||
|
||||
/* keydev_repeat_event(): Generate a repeat event if applicable */
|
||||
key_event_t keydev_repeat_event(keydev_t *d)
|
||||
{
|
||||
key_event_t e = { .type = KEYEV_NONE, .time = d->time };
|
||||
/* <Repeats> is disabled */
|
||||
if(!(d->tr.enabled & KEYDEV_TR_REPEATS)) return e;
|
||||
/* No key is being repeated, or it's too early */
|
||||
if(!d->rep_key || d->rep_delay != 0) return e;
|
||||
/* Key is blocked by transform options modified during the streak */
|
||||
if(!can_repeat(d, d->rep_key)) return e;
|
||||
|
||||
/* Plan the next repeat the currently-pressed key */
|
||||
int elapsed = (int16_t)(d->time - d->rep_time);
|
||||
d->rep_delay = -1;
|
||||
d->rep_count++;
|
||||
|
||||
/* Returning < 0 will block further repeats */
|
||||
if(d->tr.repeater)
|
||||
d->rep_delay = d->tr.repeater(d->rep_key,elapsed,d->rep_count);
|
||||
|
||||
/* Don't return an event on the first call (it's a KEYEV_DOWN) */
|
||||
if(!d->rep_count) return e;
|
||||
|
||||
e.key = d->rep_key;
|
||||
e.type = KEYEV_HOLD;
|
||||
return e;
|
||||
}
|
||||
|
||||
/* keydev_keydown(): Check if a key is down according to generated events */
|
||||
bool keydev_keydown(keydev_t *d, int key)
|
||||
{
|
||||
int row = (key >> 4) ^ 1;
|
||||
int col = 0x80 >> (key & 0x7);
|
||||
|
||||
return (d->state_queue[row] & col) != 0;
|
||||
}
|
||||
|
||||
/* keydev_idle(): Check if all keys are released */
|
||||
bool keydev_idle(keydev_t *d, ...)
|
||||
{
|
||||
uint32_t *state = (void *)d->state_queue;
|
||||
uint32_t check[3] = { state[0], state[1], state[2] };
|
||||
int key;
|
||||
|
||||
va_list args;
|
||||
va_start(args, d);
|
||||
while((key = va_arg(args, int)))
|
||||
{
|
||||
int row = (key >> 4) ^ 1;
|
||||
int col = 0x80 >> (key & 0x7);
|
||||
((uint8_t *)check)[row] &= ~col;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return (check[0] == 0) && (check[1] == 0) && (check[2] == 0);
|
||||
}
|
||||
|
||||
//---
|
||||
// Event transforms
|
||||
//---
|
||||
|
||||
/* keydev_transform(): Obtain current transform parameters */
|
||||
keydev_transform_t keydev_transform(keydev_t *d)
|
||||
{
|
||||
return d->tr;
|
||||
}
|
||||
|
||||
/* keydev_set_transform(): Set transform parameters */
|
||||
void keydev_set_transform(keydev_t *d, keydev_transform_t tr)
|
||||
{
|
||||
int change = d->tr.enabled ^ tr.enabled;
|
||||
|
||||
if(change & KEYDEV_TR_DELAYED_SHIFT)
|
||||
{
|
||||
d->pressed_shift = 0;
|
||||
d->delayed_shift = 0;
|
||||
}
|
||||
if(change & KEYDEV_TR_DELAYED_ALPHA)
|
||||
{
|
||||
d->pressed_alpha = 0;
|
||||
d->delayed_alpha = 0;
|
||||
}
|
||||
|
||||
d->tr = tr;
|
||||
}
|
||||
|
||||
/* keydev_read(): Retrieve the next transformed event */
|
||||
key_event_t keydev_read(keydev_t *d)
|
||||
{
|
||||
#define opt(NAME) (d->tr.enabled & KEYDEV_TR_ ## NAME)
|
||||
key_event_t e;
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* Repeat at the end of every tick, or if we're late */
|
||||
bool empty = (d->queue_next == d->queue_end) && !d->out_size;
|
||||
bool end_of_tick = (d->time_repeats == d->time - 1) && empty;
|
||||
bool late_repeat = (d->time_repeats <= d->time - 2);
|
||||
|
||||
if(end_of_tick || late_repeat)
|
||||
e = keydev_repeat_event(d);
|
||||
if(e.type == KEYEV_NONE)
|
||||
e = keydev_unqueue_event(d);
|
||||
if(e.type == KEYEV_NONE)
|
||||
return e;
|
||||
|
||||
int k = e.key;
|
||||
|
||||
// <Instant SHIFT> and <Instant ALPHA>
|
||||
|
||||
if(e.type == KEYEV_DOWN || e.type == KEYEV_HOLD)
|
||||
{
|
||||
if(opt(INSTANT_SHIFT) && k != KEY_SHIFT)
|
||||
e.shift |= keydev_keydown(d, KEY_SHIFT);
|
||||
if(opt(INSTANT_ALPHA) && k != KEY_ALPHA)
|
||||
e.alpha |= keydev_keydown(d, KEY_ALPHA);
|
||||
|
||||
}
|
||||
|
||||
// <Delayed SHIFT> and <Delayed ALPHA>
|
||||
|
||||
if(opt(DELAYED_SHIFT))
|
||||
{
|
||||
if(e.type == KEYEV_DOWN && k == KEY_SHIFT)
|
||||
{
|
||||
d->pressed_shift |= keydev_idle(d,KEY_SHIFT,0);
|
||||
}
|
||||
else if(e.type != KEYEV_UP && k == d->rep_key)
|
||||
{
|
||||
e.shift |= d->delayed_shift;
|
||||
d->pressed_shift = 0;
|
||||
}
|
||||
else if(e.type == KEYEV_UP && d->pressed_shift)
|
||||
{
|
||||
d->pressed_shift = 0;
|
||||
d->delayed_shift = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(opt(DELAYED_ALPHA))
|
||||
{
|
||||
if(e.type == KEYEV_DOWN && k == KEY_ALPHA)
|
||||
{
|
||||
d->pressed_alpha |= keydev_idle(d,KEY_ALPHA,0);
|
||||
}
|
||||
else if(e.type != KEYEV_UP && k == d->rep_key)
|
||||
{
|
||||
e.alpha |= d->delayed_alpha;
|
||||
d->pressed_alpha = 0;
|
||||
}
|
||||
else if(e.type == KEYEV_UP && d->pressed_alpha)
|
||||
{
|
||||
d->pressed_alpha = 0;
|
||||
d->delayed_alpha = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// <Delete Modifiers>
|
||||
if(opt(DELETE_MODIFIERS) && !can_repeat(d, k)) continue;
|
||||
|
||||
// <Delete Releases>
|
||||
if(opt(DELETE_RELEASES) && e.type == KEYEV_UP) continue;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
#undef opt
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
#include <gint/timer.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/drivers/keydev.h>
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
@ -18,78 +19,23 @@
|
|||
#include <stdarg.h>
|
||||
|
||||
/* Keyboard scan frequency in Hertz. Start with 128 Hz, this frequency *must
|
||||
be high* for the keyboard to work! */
|
||||
be high* for the keyboard to work! Reading at low frequencies produces a lot
|
||||
of artifacts. See https://www.casiopeia.net/forum/viewtopic.php?p=20592. */
|
||||
static int scan_frequency = 128;
|
||||
/* Approximation in microseconds, used by the timer and repeat delays */
|
||||
static uint32_t scan_frequency_us = 7812; /* 1000000 / scan_frequency */
|
||||
|
||||
/* Keyboard scanner timer */
|
||||
static int keysc_tid = -1;
|
||||
/* Keyboard input device for this Key Scan Interface */
|
||||
static keydev_t dev_keysc;
|
||||
|
||||
//---
|
||||
// Keyboard buffer
|
||||
//---
|
||||
|
||||
/* The driver's internal state. At each step of time, this file compares the
|
||||
internal state with the hardware state and generates events accordingly.
|
||||
Events can be seen as a delta-encoding of the keyboard state over time.
|
||||
|
||||
To ensure that adding pending events to the last-read state always gives the
|
||||
internal driver state, this array is not updated if the generation of an
|
||||
event fails. (Most likely the even will be regenerated at the next scan.) */
|
||||
static volatile uint8_t state[12] = { 0 };
|
||||
|
||||
/* The driver's current event state. This state corresponds to the sum of all
|
||||
events sent to the user so far.When the event queue is empty, this is equal
|
||||
to [state]. For each generated event, this array is updated to reflect the
|
||||
user's view of the keyboard. */
|
||||
static uint8_t current[12] = { 0 };
|
||||
|
||||
/* A driver event, which is a common change in a full row */
|
||||
typedef struct
|
||||
/* keydev_std(): Standard keyboard input device */
|
||||
keydev_t *keydev_std(void)
|
||||
{
|
||||
uint16_t time; /* Locally unique time identifier */
|
||||
uint8_t changed; /* Keys that changed state */
|
||||
uint8_t row :4; /* Row number */
|
||||
uint8_t kind :4; /* Type of change, either KEYEV_DOWN or KEYEV_UP */
|
||||
|
||||
} driver_event_t;
|
||||
|
||||
/* The keyboard event buffer. This is a circular list defined by [buffer_start]
|
||||
and [buffer_end]. To avoid an ambiguity when start == end, there must always
|
||||
be at least one free entry. */
|
||||
GBSS static driver_event_t buffer[KEYBOARD_QUEUE_SIZE];
|
||||
/* Buffer bounds */
|
||||
static int buffer_start = 0;
|
||||
static int buffer_end = 0;
|
||||
|
||||
/* Current time, in keyboard-scanning ticks */
|
||||
static uint time = 0;
|
||||
|
||||
/* buffer_push(): Add an event in the keyboard buffer
|
||||
Returns non-zero if the event cannot be pushed. */
|
||||
static int buffer_push(driver_event_t ev)
|
||||
{
|
||||
int next = (buffer_end + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
if(next == buffer_start) return 1;
|
||||
|
||||
buffer[buffer_end] = ev;
|
||||
buffer_end = next;
|
||||
return 0;
|
||||
return &dev_keysc;
|
||||
}
|
||||
|
||||
/* buffer_poll(): Generate key events from the buffer
|
||||
Sets [ev] and returns zero on success, otherwise non-zero. */
|
||||
static int buffer_poll(driver_event_t *ev)
|
||||
{
|
||||
if(buffer_start == buffer_end) return 1;
|
||||
|
||||
*ev = buffer[buffer_start];
|
||||
buffer_start = (buffer_start + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---
|
||||
// Keyboard scanning
|
||||
//---
|
||||
|
||||
/* keysc_scan_frequency(): Get the current keyboard scan frequency in Hertz */
|
||||
int keysc_scan_frequency(void)
|
||||
{
|
||||
|
@ -99,9 +45,7 @@ int keysc_scan_frequency(void)
|
|||
/* keysc_scan_frequency_us(): Get keyboard scan delay in microseconds */
|
||||
uint32_t keysc_scan_frequency_us(void)
|
||||
{
|
||||
int delay = 1000000 / scan_frequency;
|
||||
if(delay == 0) delay = 1;
|
||||
return delay;
|
||||
return scan_frequency_us;
|
||||
}
|
||||
|
||||
/* keysc_set_scan_frequency(): Set the keyboard scan frequency in Hertz */
|
||||
|
@ -110,20 +54,21 @@ void keysc_set_scan_frequency(int freq)
|
|||
if(freq < 64) freq = 64;
|
||||
if(freq > 32768) freq = 32768;
|
||||
scan_frequency = freq;
|
||||
|
||||
if(keysc_tid < 0) return;
|
||||
uint32_t TCOR = timer_delay(keysc_tid, keysc_scan_frequency_us(), 0);
|
||||
timer_reload(keysc_tid, TCOR);
|
||||
scan_frequency_us = 1000000 / freq;
|
||||
|
||||
getkey_refresh_delays();
|
||||
|
||||
if(keysc_tid < 0) return;
|
||||
uint32_t TCOR = timer_delay(keysc_tid, scan_frequency_us, 0);
|
||||
timer_reload(keysc_tid, TCOR);
|
||||
}
|
||||
|
||||
/* keysc_frame(): Generate driver events from KEYSC state */
|
||||
static void keysc_frame(void)
|
||||
/* keysc_tick(): Update the keyboard to the next state */
|
||||
static int keysc_tick(void)
|
||||
{
|
||||
GALIGNED(2) uint8_t scan[12] = { 0 };
|
||||
|
||||
/* First scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
|
||||
/* Scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
|
||||
if(isSH3()) iokbd_scan(scan);
|
||||
else
|
||||
{
|
||||
|
@ -133,82 +78,15 @@ static void keysc_frame(void)
|
|||
for(int i = 0; i < 6; i++) array[i] = KEYSC[i];
|
||||
}
|
||||
|
||||
/* Compare new data with the internal state. Push releases before
|
||||
presses so that a key change occurring within a single analysis
|
||||
frame can be performed. This happens all the time when going back to
|
||||
the main MENU via gint_osmenu() on a keybind. */
|
||||
for(int row = 0; row < 12; row++)
|
||||
{
|
||||
int diff = ~scan[row] & state[row];
|
||||
if(!diff) continue;
|
||||
|
||||
/* Update internal status if the event could be pushed */
|
||||
driver_event_t ev = { time, diff, row, KEYEV_UP };
|
||||
if(!buffer_push(ev)) state[row] &= scan[row];
|
||||
}
|
||||
for(int row = 0; row < 12; row++)
|
||||
{
|
||||
int diff = scan[row] & ~state[row];
|
||||
if(!diff) continue;
|
||||
|
||||
driver_event_t ev = { time, diff, row, KEYEV_DOWN };
|
||||
if(!buffer_push(ev)) state[row] |= scan[row];
|
||||
}
|
||||
keydev_process_state(&dev_keysc, scan);
|
||||
keydev_tick(&dev_keysc, scan_frequency_us);
|
||||
return TIMER_CONTINUE;
|
||||
}
|
||||
|
||||
/* pollevent() - poll the next keyboard event */
|
||||
key_event_t pollevent(void)
|
||||
{
|
||||
/* Every time a driver event is unqueued, its key events are generated
|
||||
and put in this small buffer */
|
||||
static key_event_t events[8];
|
||||
/* Number of pending events in the previous buffer */
|
||||
static int events_pending = 0;
|
||||
|
||||
/* Use pending events first, if they exist */
|
||||
|
||||
if(events_pending > 0)
|
||||
{
|
||||
key_event_t ev = events[--events_pending];
|
||||
|
||||
/* Update the current state buffer according to [ev] */
|
||||
int row = (ev.key >> 4) ^ 1;
|
||||
int col = 0x80 >> (ev.key & 0x7);
|
||||
|
||||
if(ev.type == KEYEV_DOWN) current[row] |= col;
|
||||
if(ev.type == KEYEV_UP) current[row] &= ~col;
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
/* If not key event is pending, generate a chunk by polling the driver
|
||||
event queue */
|
||||
|
||||
driver_event_t ev;
|
||||
if(buffer_poll(&ev))
|
||||
{
|
||||
key_event_t ev = { .type = KEYEV_NONE, .time = time };
|
||||
return ev;
|
||||
}
|
||||
|
||||
/* Generate new key events and return the first of them*/
|
||||
int changed = ev.changed;
|
||||
for(int code = ((ev.row ^ 1) << 4) | 0x7; code & 0x7; code--)
|
||||
{
|
||||
if(changed & 1)
|
||||
{
|
||||
key_event_t keyev = {
|
||||
.time = ev.time,
|
||||
.type = ev.kind,
|
||||
.key = code,
|
||||
};
|
||||
events[events_pending++] = keyev;
|
||||
}
|
||||
changed >>= 1;
|
||||
}
|
||||
|
||||
/* Call recursively to avoid duplicating the event emission code */
|
||||
return pollevent();
|
||||
return keydev_unqueue_event(&dev_keysc);
|
||||
}
|
||||
|
||||
/* waitevent() - wait for the next keyboard event */
|
||||
|
@ -223,7 +101,7 @@ key_event_t waitevent(volatile int *timeout)
|
|||
sleep();
|
||||
}
|
||||
|
||||
key_event_t ev = { .type = KEYEV_NONE, .time = time };
|
||||
key_event_t ev = { .type = KEYEV_NONE, .time = dev_keysc.time };
|
||||
return ev;
|
||||
}
|
||||
|
||||
|
@ -240,10 +118,7 @@ void clearevents(void)
|
|||
/* keydown(): Current key state */
|
||||
int keydown(int key)
|
||||
{
|
||||
int row = (key >> 4) ^ 1;
|
||||
int col = 0x80 >> (key & 0x7);
|
||||
|
||||
return (current[row] & col) != 0;
|
||||
return keydev_keydown(&dev_keysc, key);
|
||||
}
|
||||
|
||||
/* keydown_all(): Check a set of keys for simultaneous input
|
||||
|
@ -288,21 +163,15 @@ int keydown_any(int key, ...)
|
|||
// Driver initialization
|
||||
//---
|
||||
|
||||
static int callback(void)
|
||||
{
|
||||
keysc_frame();
|
||||
time++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init() - setup the support timer */
|
||||
static void init(void)
|
||||
{
|
||||
keydev_init(&dev_keysc);
|
||||
|
||||
/* Set the default repeat times (milliseconds) */
|
||||
getkey_repeat(400, 40);
|
||||
|
||||
/* The timer will be stopped when the timer driver is unloaded */
|
||||
keysc_tid = timer_setup(TIMER_ANY,keysc_scan_frequency_us(),callback);
|
||||
keysc_tid = timer_setup(TIMER_ANY, scan_frequency_us, keysc_tick);
|
||||
if(keysc_tid >= 0) timer_start(keysc_tid);
|
||||
|
||||
gint[HWKBD] = HW_LOADED | (isSH3() ? HWKBD_IO : HWKBD_KSI);
|
||||
|
|
Loading…
Reference in a new issue