keysc: simpler keyboard device with more consistent repeats

* Stop trying to be smart and generate repeats on the fly; this breaks
  time consistency. Also if repeats are not handled in time this causes
  infinite loops.
* Move rarely-used functions to external files, simplify stuff, get rid
  of internal driver events; saves ~1 kB per add-in overall.
This commit is contained in:
Lephe 2022-04-23 13:34:41 +01:00
parent 0c2935055e
commit e57efb5e37
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
10 changed files with 273 additions and 269 deletions

View file

@ -85,7 +85,12 @@ set(SOURCES_COMMON
src/keysc/iokbd.c src/keysc/iokbd.c
src/keysc/keycodes.c src/keysc/keycodes.c
src/keysc/keydev.c src/keysc/keydev.c
src/keysc/keydev_idle.c
src/keysc/keydev_process_key.c
src/keysc/keydown_all.c
src/keysc/keydown_any.c
src/keysc/keysc.c src/keysc/keysc.c
src/keysc/scan_frequency.c
# Memory allocator # Memory allocator
src/kmalloc/arena_gint.c src/kmalloc/arena_gint.c
src/kmalloc/arena_osheap.c src/kmalloc/arena_osheap.c

View file

@ -14,19 +14,6 @@ extern "C" {
/* Size of the buffer event queue */ /* Size of the buffer event queue */
#define KEYBOARD_QUEUE_SIZE 32 #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 /* Event transforms
Every keyboard input device has a built-in event stream editor that can Every keyboard input device has a built-in event stream editor that can
@ -84,14 +71,26 @@ typedef struct {
enum { enum {
/* Delayed SHIFT: Pressing then immediately releasing SHIFT when the /* Delayed SHIFT: Pressing then immediately releasing SHIFT when the
keyboard is idle applies SHIFT to the next repeat streak. */ keyboard is idle applies SHIFT to the next repeat streak. */
KEYDEV_TR_DELAYED_SHIFT = 0x01, KEYDEV_TR_DELAYED_SHIFT = 0x01, /* = GETKEY_MOD_SHIFT */
/* Delayed ALPHA: Idem with the ALPHA key */ /* Delayed ALPHA: Idem with the ALPHA key */
KEYDEV_TR_DELAYED_ALPHA = 0x02, KEYDEV_TR_DELAYED_ALPHA = 0x02, /* = GETKEY_MOD_ALPHA */
/* Combination of the delayed modifiers */
KEYDEV_TR_DELAYED_MODS = KEYDEV_TR_DELAYED_SHIFT
| KEYDEV_TR_DELAYED_ALPHA,
/* Instant SHIFT: Each individual event of every repeat streak gets /* Instant SHIFT: Each individual event of every repeat streak gets
SHIFT applied if SHIFT is pressed at the time of the repeat. */ SHIFT applied if SHIFT is pressed at the time of the repeat. */
KEYDEV_TR_INSTANT_SHIFT = 0x04, KEYDEV_TR_INSTANT_SHIFT = 0x04,
/* Instant ALPHA: Idem with the ALPHA key */ /* Instant ALPHA: Idem with the ALPHA key */
KEYDEV_TR_INSTANT_ALPHA = 0x08, KEYDEV_TR_INSTANT_ALPHA = 0x08,
/* Combination of the instance modifiers */
KEYDEV_TR_INSTANT_MODS = KEYDEV_TR_INSTANT_SHIFT
| KEYDEV_TR_INSTANT_ALPHA,
/* Combination of all modifiers */
KEYDEV_TR_ALL_MODS = KEYDEV_TR_DELAYED_MODS
| KEYDEV_TR_INSTANT_MODS,
/* Repeats: Keys are repeated according to a repeat filter function */ /* Repeats: Keys are repeated according to a repeat filter function */
KEYDEV_TR_REPEATS = 0x10, KEYDEV_TR_REPEATS = 0x10,
/* Delete Modifiers: Remove modifier keys from generated events, which /* Delete Modifiers: Remove modifier keys from generated events, which
@ -143,31 +142,16 @@ typedef struct {
keydown() are shortcuts for keydev functions using the physical keyboard as keydown() are shortcuts for keydev functions using the physical keyboard as
their input. */ their input. */
typedef struct { 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 */ /* Current device time in scanning-ticks */
uint time; uint time;
/* Last time when repeats were considered */ /* Last time when repeats were considered */
uint time_repeats; uint time_repeats;
/* Event queue (circular buffer) */
keydev_event_t queue[KEYBOARD_QUEUE_SIZE];
/* Next event in queue, position after last event in queue */ /* Next event in queue, position after last event in queue */
int8_t queue_next; int8_t queue_next;
int8_t queue_end; int8_t queue_end;
/* Number of events lost because of missing queue space */ /* Number of events lost because of missing queue space */
uint events_lost; uint events_lost;
/* Crafted events waiting to be picked up */
key_event_t out[8];
int out_size;
/* Event transforms */ /* Event transforms */
keydev_transform_t tr; keydev_transform_t tr;
@ -193,6 +177,19 @@ typedef struct {
/* Delay until next repeat, set by the repeat planner (us) */ /* Delay until next repeat, set by the repeat planner (us) */
int rep_delay; int rep_delay;
/* 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];
/* Event queue (circular buffer) */
key_event_t queue[KEYBOARD_QUEUE_SIZE];
} keydev_t; } keydev_t;
/* keydev_std(): Standard keyboard input device /* keydev_std(): Standard keyboard input device
@ -218,6 +215,12 @@ void keydev_process_state(keydev_t *d, uint8_t state[12]);
devices (such as demo replays) to feed in new events. */ devices (such as demo replays) to feed in new events. */
void keydev_process_key(keydev_t *d, int keycode, bool state); void keydev_process_key(keydev_t *d, int keycode, bool state);
/* keydev_repeat_event(): Generate a repeat event if applicable
At the end of every scan tick, 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_tick(): Prepare the next tick /* keydev_tick(): Prepare the next tick
This function maintains time trackers in the device and should be called in This function maintains time trackers in the device and should be called in
each frame after the scanning is finished and the keydev_process_*() each frame after the scanning is finished and the keydev_process_*()
@ -237,13 +240,6 @@ void keydev_tick(keydev_t *d, uint us);
at the end of every tick. */ at the end of every tick. */
key_event_t keydev_unqueue_event(keydev_t *d); 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 /* keydev_idle(): Check if all keys are released
A list of keys to ignore can be specified as variable arguments. The list A list of keys to ignore can be specified as variable arguments. The list
must be terminated by a 0 keycode. */ must be terminated by a 0 keycode. */

View file

@ -44,11 +44,10 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
keydev_transform_t tr0 = keydev_transform(d); keydev_transform_t tr0 = keydev_transform(d);
key_event_t e; key_event_t e;
int o = KEYDEV_TR_REPEATS | int o = KEYDEV_TR_REPEATS +
KEYDEV_TR_DELETE_MODIFIERS | KEYDEV_TR_DELETE_MODIFIERS +
KEYDEV_TR_DELETE_RELEASES | KEYDEV_TR_DELETE_RELEASES +
((opt & GETKEY_MOD_SHIFT) ? KEYDEV_TR_DELAYED_SHIFT : 0) | (opt & (GETKEY_MOD_SHIFT + GETKEY_MOD_ALPHA));
((opt & GETKEY_MOD_ALPHA) ? KEYDEV_TR_DELAYED_ALPHA : 0);
keydev_set_transform(d, (keydev_transform_t){ o, getkey_repeater }); keydev_set_transform(d, (keydev_transform_t){ o, getkey_repeater });
repeat_mode = NONE; repeat_mode = NONE;
@ -118,5 +117,3 @@ void getkey_set_feature_function(getkey_feature_t function)
{ {
feature_function = function; feature_function = function;
} }

View file

@ -19,9 +19,9 @@ void keydev_init(keydev_t *d)
// Driver event generation // Driver event generation
//--- //---
/* queue_push(): Add an event in a device's buffer /* keydev_queue_push(): Add an event in a device's buffer
Returns false if the event cannot be pushed. */ Returns false if the event cannot be pushed. */
static bool queue_push(keydev_t *d, keydev_event_t e) bool keydev_queue_push(keydev_t *d, key_event_t ev)
{ {
int next = (d->queue_end + 1) % KEYBOARD_QUEUE_SIZE; int next = (d->queue_end + 1) % KEYBOARD_QUEUE_SIZE;
if(next == d->queue_next) if(next == d->queue_next)
@ -30,18 +30,18 @@ static bool queue_push(keydev_t *d, keydev_event_t e)
return false; return false;
} }
d->queue[d->queue_end] = e; d->queue[d->queue_end] = ev;
d->queue_end = next; d->queue_end = next;
return true; return true;
} }
/* queue_poll(): Generate key events from the buffer /* queue_poll(): Generate key events from the buffer
Sets (*e) and returns true on success, otherwise false. */ Sets (*e) and returns true on success, otherwise false. */
static bool queue_poll(keydev_t *d, keydev_event_t *e) static bool queue_poll(keydev_t *d, key_event_t *ev)
{ {
if(d->queue_next == d->queue_end) return false; if(d->queue_next == d->queue_end) return false;
*e = d->queue[d->queue_next]; *ev = d->queue[d->queue_next];
d->queue_next = (d->queue_next + 1) % KEYBOARD_QUEUE_SIZE; d->queue_next = (d->queue_next + 1) % KEYBOARD_QUEUE_SIZE;
return true; return true;
} }
@ -49,51 +49,86 @@ static bool queue_poll(keydev_t *d, keydev_event_t *e)
/* keydev_process_state(): Process the new keyboard states for events */ /* keydev_process_state(): Process the new keyboard states for events */
void keydev_process_state(keydev_t *d, uint8_t scan[12]) void keydev_process_state(keydev_t *d, uint8_t scan[12])
{ {
key_event_t ev = { 0 };
ev.time = d->time;
/* Compare new data with the internal state. Push releases before /* Compare new data with the internal state. Push releases before
presses so that a key change occurring within a single analysis presses so that a key change occurring within a single analysis
frame can be performed. This happens all the time when going back to frame can be performed. This happens all the time when going back to
the main MENU via gint_osmenu() on a keybind. */ the main MENU via gint_osmenu() on a keybind. */
ev.type = KEYEV_UP;
for(int mode = 0; mode < 2; mode++)
{
for(int row = 0; row < 12; row++) for(int row = 0; row < 12; row++)
{ {
int diff = ~scan[row] & d->state_now[row]; int diff = mode
? ~d->state_now[row] & scan[row]
: d->state_now[row] & ~scan[row];
if(!diff) continue; if(!diff) continue;
/* Update internal status if the event can be pushed */ ev.key = (row << 4);
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 }; for(int mask = 0x80; mask != 0; mask >>= 1)
if(queue_push(d, e)) d->state_now[row] |= scan[row]; {
/* Update state only if the push succeeds */
if((diff & mask) && keydev_queue_push(d, ev))
d->state_now[row] = mode
? d->state_now[row] | mask
: d->state_now[row] & ~mask;
ev.key++;
} }
} }
/* keydev_process_key(): Process a new key state for events */ ev.type = KEYEV_DOWN;
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; static bool can_repeat(keydev_t *d, int key)
if(state && !prev)
{ {
keydev_event_t e = { d->time, col, row, KEYEV_DOWN }; int tr = d->tr.enabled;
if(queue_push(d, e)) d->state_now[row] |= col; 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);
} }
else if(!state && prev)
/* keydev_repeat_event(): Generate a repeat event if applicable */
key_event_t keydev_repeat_event(keydev_t *d)
{ {
keydev_event_t e = { d->time, col, row, KEYEV_UP }; key_event_t ev = { 0 };
if(queue_push(d, e)) d->state_now[row] &= ~col; ev.time = d->time;
} /* <Repeats> is disabled */
if(!(d->tr.enabled & KEYDEV_TR_REPEATS)) return ev;
/* No key is being repeated, or it's too early */
if(!d->rep_key || d->rep_delay != 0) return ev;
/* Key is blocked by transform options modified during the streak */
if(!can_repeat(d, d->rep_key)) return ev;
/* 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 ev;
ev.type = KEYEV_HOLD;
ev.key = d->rep_key;
return ev;
} }
void keydev_tick(keydev_t *d, uint us) void keydev_tick(keydev_t *d, uint us)
{ {
/* Generate the next repeat */
key_event_t repeat = keydev_repeat_event(d);
if(repeat.type != KEYEV_NONE)
keydev_queue_push(d, repeat);
d->time++; d->time++;
if(d->rep_key != 0) if(d->rep_key != 0)
@ -108,65 +143,35 @@ void keydev_tick(keydev_t *d, uint us)
// Keyboard event generation // 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 */ /* keydev_unqueue_event(): Retrieve the next keyboard event in queue */
key_event_t keydev_unqueue_event(keydev_t *d) key_event_t keydev_unqueue_event(keydev_t *d)
{ {
/* Every device event is unfolded into up to 8 keyboard events, stored key_event_t ev = { 0 };
temporarily in the driver's structure. */ ev.time = d->time;
keydev_event_t e; if(!queue_poll(d, &ev))
key_event_t kev = { .type = KEYEV_NONE, .time = d->time }; return ev;
/* 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 */ /* Update the event state accordingly */
int row = (kev.key >> 4) ^ 1; int row = (ev.key >> 4);
int col = 0x80 >> (kev.key & 0x7); int col = 0x80 >> (ev.key & 0x7);
if(kev.type == KEYEV_DOWN) if(ev.type == KEYEV_DOWN)
{ {
d->state_queue[row] |= col; d->state_queue[row] |= col;
/* Mark this key as the currently repeating one */ /* Mark this key as the currently repeating one */
if(d->rep_key == 0 && can_repeat(d, kev.key)) if(d->rep_key == 0 && can_repeat(d, ev.key))
{ {
d->rep_key = kev.key; d->rep_key = ev.key;
d->rep_count = -1; d->rep_count = -1;
d->rep_time = 0; d->rep_time = 0;
d->rep_delay = 0; d->rep_delay = 0;
} }
} }
if(kev.type == KEYEV_UP) else if(ev.type == KEYEV_UP)
{ {
d->state_queue[row] &= ~col; d->state_queue[row] &= ~col;
/* End the current repeating streak */ /* End the current repeating streak */
if(d->rep_key == kev.key) if(d->rep_key == ev.key)
{ {
d->rep_key = 0; d->rep_key = 0;
d->rep_count = -1; d->rep_count = -1;
@ -177,66 +182,18 @@ key_event_t keydev_unqueue_event(keydev_t *d)
} }
} }
return kev; return ev;
}
/* 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 */ /* keydev_keydown(): Check if a key is down according to generated events */
bool keydev_keydown(keydev_t *d, int key) bool keydev_keydown(keydev_t *d, int key)
{ {
int row = (key >> 4) ^ 1; int row = (key >> 4);
int col = 0x80 >> (key & 0x7); int col = 0x80 >> (key & 0x7);
return (d->state_queue[row] & col) != 0; 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 // Event transforms
//--- //---
@ -274,22 +231,12 @@ key_event_t keydev_read(keydev_t *d)
while(1) 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); e = keydev_unqueue_event(d);
if(e.type == KEYEV_NONE) if(e.type == KEYEV_NONE)
return e; return e;
int k = e.key; int k = e.key;
e.mod = (opt(ALL_MODS) != 0);
if(opt(INSTANT_SHIFT) || opt(INSTANT_ALPHA)) e.mod = 1;
if(opt(DELAYED_SHIFT) || opt(DELAYED_ALPHA)) e.mod = 1;
// <Instant SHIFT> and <Instant ALPHA> // <Instant SHIFT> and <Instant ALPHA>
@ -299,7 +246,6 @@ key_event_t keydev_read(keydev_t *d)
e.shift |= keydev_keydown(d, KEY_SHIFT); e.shift |= keydev_keydown(d, KEY_SHIFT);
if(opt(INSTANT_ALPHA) && k != KEY_ALPHA) if(opt(INSTANT_ALPHA) && k != KEY_ALPHA)
e.alpha |= keydev_keydown(d, KEY_ALPHA); e.alpha |= keydev_keydown(d, KEY_ALPHA);
} }
// <Delayed SHIFT> and <Delayed ALPHA> // <Delayed SHIFT> and <Delayed ALPHA>
@ -308,10 +254,9 @@ key_event_t keydev_read(keydev_t *d)
{ {
if(e.type == KEYEV_DOWN && k == KEY_SHIFT) if(e.type == KEYEV_DOWN && k == KEY_SHIFT)
{ {
if(d->delayed_shift) if(!d->delayed_shift)
d->delayed_shift = 0;
else if(keydev_idle(d,KEY_SHIFT,0))
d->pressed_shift = 1; d->pressed_shift = 1;
d->delayed_shift = 0;
} }
else if(e.type != KEYEV_UP && k == d->rep_key) else if(e.type != KEYEV_UP && k == d->rep_key)
{ {
@ -329,10 +274,9 @@ key_event_t keydev_read(keydev_t *d)
{ {
if(e.type == KEYEV_DOWN && k == KEY_ALPHA) if(e.type == KEYEV_DOWN && k == KEY_ALPHA)
{ {
if(d->delayed_alpha) if(!d->delayed_alpha)
d->delayed_alpha = 0;
else if(keydev_idle(d,KEY_ALPHA,0))
d->pressed_alpha = 1; d->pressed_alpha = 1;
d->delayed_alpha = 0;
} }
else if(e.type != KEYEV_UP && k == d->rep_key) else if(e.type != KEYEV_UP && k == d->rep_key)
{ {

23
src/keysc/keydev_idle.c Normal file
View file

@ -0,0 +1,23 @@
#include <gint/drivers/keydev.h>
#include <stdint.h>
#include <stdarg.h>
/* 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);
int col = 0x80 >> (key & 0x7);
((uint8_t *)check)[row] &= ~col;
}
va_end(args);
return (check[0] == 0) && (check[1] == 0) && (check[2] == 0);
}

View file

@ -0,0 +1,30 @@
#include <gint/drivers/keydev.h>
#include <stdbool.h>
bool keydev_queue_push(keydev_t *d, key_event_t ev);
/* 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);
int col = 0x80 >> (keycode & 0x7);
int prev = d->state_now[row] & col;
if(state && !prev)
{
key_event_t ev = { 0 };
ev.time = d->time;
ev.type = KEYEV_DOWN;
ev.key = keycode;
if(keydev_queue_push(d, ev)) d->state_now[row] |= col;
}
else if(!state && prev)
{
key_event_t ev = { 0 };
ev.time = d->time;
ev.type = KEYEV_UP;
ev.key = keycode;
if(keydev_queue_push(d, ev)) d->state_now[row] &= ~col;
}
}

21
src/keysc/keydown_all.c Normal file
View file

@ -0,0 +1,21 @@
#include <gint/keyboard.h>
#include <stdarg.h>
/* keydown_all(): Check a set of keys for simultaneous input
Returns non-zero if all provided keys are down. The list should end with an
integer 0 as terminator. */
int keydown_all(int key, ...)
{
va_list args;
va_start(args, key);
int st = 1;
while(key && st)
{
st = keydown(key);
key = va_arg(args, int);
}
va_end(args);
return st;
}

21
src/keysc/keydown_any.c Normal file
View file

@ -0,0 +1,21 @@
#include <gint/keyboard.h>
#include <stdarg.h>
/* keydown_any(): Check a set of keys for any input
Returns nonzero if any one of the specified keys is currently pressed. THe
sequence should be terminated by a 0 integer. */
int keydown_any(int key, ...)
{
va_list args;
va_start(args, key);
int st = 0;
while(key && !st)
{
st = keydown(key);
key = va_arg(args, int);
}
va_end(args);
return st;
}

View file

@ -19,44 +19,19 @@
/* Keyboard scan frequency in Hertz. Start with 128 Hz, this frequency *must /* Keyboard scan frequency in Hertz. Start with 128 Hz, this frequency *must
be high* for the keyboard to work! Reading at low frequencies produces a lot 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. */ of artifacts. See https://www.casiopeia.net/forum/viewtopic.php?p=20592. */
static int scan_frequency = 128; int keysc_scan_Hz = 128;
/* Approximation in microseconds, used by the timer and repeat delays */ /* Approximation in microseconds, used by the timer and repeat delays */
static uint32_t scan_frequency_us = 7812; /* 1000000 / scan_frequency */ uint32_t keysc_scan_us = 7812; /* 1000000 / keysc_scan_Hz */
/* Keyboard scanner timer */ /* Keyboard scanner timer */
static int keysc_tid = -1; int keysc_tid = -1;
/* Keyboard input device for this Key Scan Interface */ /* Keyboard input device for this Key Scan Interface */
static keydev_t dev_keysc; static keydev_t keysc_dev;
/* keydev_std(): Standard keyboard input device */ /* keydev_std(): Standard keyboard input device */
keydev_t *keydev_std(void) keydev_t *keydev_std(void)
{ {
return &dev_keysc; return &keysc_dev;
}
/* keysc_scan_frequency(): Get the current keyboard scan frequency in Hertz */
int keysc_scan_frequency(void)
{
return scan_frequency;
}
/* keysc_scan_frequency_us(): Get keyboard scan delay in microseconds */
uint32_t keysc_scan_frequency_us(void)
{
return scan_frequency_us;
}
/* keysc_set_scan_frequency(): Set the keyboard scan frequency in Hertz */
void keysc_set_scan_frequency(int freq)
{
if(freq < 64) freq = 64;
if(freq > 32768) freq = 32768;
scan_frequency = freq;
scan_frequency_us = 1000000 / freq;
if(keysc_tid < 0) return;
uint32_t TCOR = timer_delay(keysc_tid, scan_frequency_us, 0);
timer_reload(keysc_tid, TCOR);
} }
/* keysc_tick(): Update the keyboard to the next state */ /* keysc_tick(): Update the keyboard to the next state */
@ -71,18 +46,21 @@ static int keysc_tick(void)
volatile uint16_t *KEYSC = (void *)0xa44b0000; volatile uint16_t *KEYSC = (void *)0xa44b0000;
uint16_t *array = (void *)&scan; uint16_t *array = (void *)&scan;
for(int i = 0; i < 6; i++) array[i] = KEYSC[i]; for(int i = 0; i < 6; i++) {
array[i] = KEYSC[i];
array[i] = (array[i] << 8) | (array[i] >> 8);
}
} }
keydev_process_state(&dev_keysc, scan); keydev_process_state(&keysc_dev, scan);
keydev_tick(&dev_keysc, scan_frequency_us); keydev_tick(&keysc_dev, keysc_scan_us);
return TIMER_CONTINUE; return TIMER_CONTINUE;
} }
/* pollevent() - poll the next keyboard event */ /* pollevent() - poll the next keyboard event */
key_event_t pollevent(void) key_event_t pollevent(void)
{ {
return keydev_unqueue_event(&dev_keysc); return keydev_unqueue_event(&keysc_dev);
} }
/* waitevent() - wait for the next keyboard event */ /* waitevent() - wait for the next keyboard event */
@ -97,7 +75,7 @@ key_event_t waitevent(volatile int *timeout)
sleep(); sleep();
} }
key_event_t ev = { .type = KEYEV_NONE, .time = dev_keysc.time }; key_event_t ev = { .type = KEYEV_NONE, .time = keysc_dev.time };
return ev; return ev;
} }
@ -107,52 +85,10 @@ void clearevents(void)
while(pollevent().type != KEYEV_NONE); while(pollevent().type != KEYEV_NONE);
} }
//---
// Immediate key access
//---
/* keydown(): Current key state */ /* keydown(): Current key state */
int keydown(int key) int keydown(int key)
{ {
return keydev_keydown(&dev_keysc, key); return keydev_keydown(&keysc_dev, key);
}
/* keydown_all(): Check a set of keys for simultaneous input
Returns non-zero if all provided keys are down. The list should end with an
integer 0 as terminator. */
int keydown_all(int key, ...)
{
va_list args;
va_start(args, key);
int st = 1;
while(key && st)
{
st = keydown(key);
key = va_arg(args, int);
}
va_end(args);
return st;
}
/* keydown_any(): Check a set of keys for any input
Returns nonzero if any one of the specified keys is currently pressed. THe
sequence should be terminated by a 0 integer. */
int keydown_any(int key, ...)
{
va_list args;
va_start(args, key);
int st = 0;
while(key && !st)
{
st = keydown(key);
key = va_arg(args, int);
}
va_end(args);
return st;
} }
//--- //---
@ -161,13 +97,13 @@ int keydown_any(int key, ...)
static void configure(void) static void configure(void)
{ {
keydev_init(&dev_keysc); keydev_init(&keysc_dev);
/* Set the default repeat times (milliseconds) */ /* Set the default repeat times (milliseconds) */
getkey_repeat(400, 40); getkey_repeat(400, 40);
/* The timer will be stopped when the timer driver is unloaded */ /* The timer will be stopped when the timer driver is unloaded */
keysc_tid = timer_configure(TIMER_ANY, scan_frequency_us, keysc_tid = timer_configure(TIMER_ANY, keysc_scan_us,
GINT_CALL(keysc_tick)); GINT_CALL(keysc_tick));
if(keysc_tid >= 0) timer_start(keysc_tid); if(keysc_tid >= 0) timer_start(keysc_tid);

View file

@ -0,0 +1,31 @@
#include <gint/keyboard.h>
#include <gint/timer.h>
extern int keysc_scan_Hz;
extern uint32_t keysc_scan_us;
extern int keysc_tid;
/* keysc_scan_frequency(): Get the current keyboard scan frequency in Hertz */
int keysc_scan_frequency(void)
{
return keysc_scan_Hz;
}
/* keysc_scan_frequency_us(): Get keyboard scan delay in microseconds */
uint32_t keysc_scan_frequency_us(void)
{
return keysc_scan_us;
}
/* keysc_set_scan_frequency(): Set the keyboard scan frequency in Hertz */
void keysc_set_scan_frequency(int freq)
{
if(freq < 64) freq = 64;
if(freq > 32768) freq = 32768;
keysc_scan_Hz = freq;
keysc_scan_us = 1000000 / freq;
if(keysc_tid < 0) return;
uint32_t TCOR = timer_delay(keysc_tid, keysc_scan_us, 0);
timer_reload(keysc_tid, TCOR);
}