getkey: use the new keydev interface

The repeat filter was also changed in favor of a forward-acting
function, which determines repeat delays *before* the repeat actually
occurs.
This commit is contained in:
Lephe 2021-03-05 14:49:24 +01:00
parent dd564f094a
commit 910677f7ff
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
5 changed files with 88 additions and 182 deletions

View file

@ -210,8 +210,8 @@ enum {
/* Repeat arrow keys, or even all keys */ /* Repeat arrow keys, or even all keys */
GETKEY_REP_ARROWS = 0x10, GETKEY_REP_ARROWS = 0x10,
GETKEY_REP_ALL = 0x20, GETKEY_REP_ALL = 0x20,
/* Enable repeat event filtering; see getkey_repeat_filter() */ /* Enable custom repeat profiles; see getkey_set_repeat_profile() */
GETKEY_REP_FILTER = 0x40, GETKEY_REP_PROFILE = 0x40,
/* No modifiers */ /* No modifiers */
GETKEY_NONE = 0x00, GETKEY_NONE = 0x00,
@ -219,6 +219,10 @@ enum {
GETKEY_DEFAULT = 0x5f, GETKEY_DEFAULT = 0x5f,
}; };
/* getkey_profile_t: Custom repeat profile function
See getkey_set_repeat_profile() for details. */
typedef int (*getkey_profile_t)(int key, int duration, int count);
/* getkey_opt(): Enhanced getkey() /* getkey_opt(): Enhanced getkey()
This function enhances getkey() with more general features. An This function enhances getkey() with more general features. An
@ -256,32 +260,33 @@ key_event_t getkey_opt(int options, volatile int *timeout);
@next Delay between subsequent repeats (no more than one hour) */ @next Delay between subsequent repeats (no more than one hour) */
void getkey_repeat(int first, int next); void getkey_repeat(int first, int next);
/* getkey_repeat_filter(): Set the repeat filter function /* getkey_repeat_profile(): Get the current repeat profile function */
getkey_profile_t getkey_repeat_profile(void);
The repeat filter is called by getkey() and getkey_opt() every time a repeat /* getkey_set_repeat_profile(): Set the repeat profile function
event occurs when GETKEY_REP_FILTER is set. The filter can decide whether to
keep, delay or drop the event. It can also change the repeat delays with
getkey_repeat() for fully custom repeat delay curves.
The time elapsed since the last accepted repeat is passed to the filter The repeat profile is called by getkey() and getkey_opt() when a key is
function; this time must be larger than the repeat time set with pressed or held, and getkey() is planning to repeat it. The profile decides
getkey_repeat() for getkey() and getkey_opt() to consider a repeat, but it whether such repetition is allowed, and if so, how long it shoud take. The
can be much longer if some repeat events were previously filtered out. profile has access to the following information:
@key Key that is about to be repeated @key Key for which a repetition is being considered
@duration Duration since last accepted repeat @duration Duration since the key was first pressed (us)
@count Number of previous repeats @count Number of previous repeats (0 on the first call)
The repeat function must either return: The profile function must either return a positive number of microseconds to
* 0, in which case the even is accepted wait until the next repeat, or 0 to block the repeat indefinitely. Note that
* A positive number of milliseconds, in which case the event is tentatively the keyboard device typically updates every 7-8 ms, timings are tracked in
re-emitted after that time (the filter function will be called again) microseconds only to limit deviations.
* A negative number, in which case the event is dropped and further repeats
are denied.
By default the filter function is NULL, which accepts all repeat events. Setting a repeat profile overrides GETKEY_REP_ARROWS, GETKEY_REP_ALL, and
This behavior can be restored explicitly by calling with function=NULL. */ the repeat delays. Calling with profile=NULL restores this behavior.
void getkey_repeat_filter(int (*filter)(int key, int duration, int count));
This mechanism replaces a "repeat filter" that existed until gint 2.4. The
main difference is that the repeat filter was called when the repeat event
arrived, whereas the repeat profile is called one repeat earlier to schedule
the repeat exactly when needed. */
void getkey_set_repeat_profile(getkey_profile_t profile);
//--- //---
// Key code functions // Key code functions

View file

@ -3,23 +3,31 @@
//--- //---
#include <gint/keyboard.h> #include <gint/keyboard.h>
#include <gint/drivers/keydev.h>
#include <gint/gint.h> #include <gint/gint.h>
#include <gint/defs/types.h> #include <gint/defs/types.h>
#include "getkey.h"
#ifdef FX9860G #ifdef FX9860G
#include <gint/drivers/t6k11.h> #include <gint/drivers/t6k11.h>
#endif #endif
/* Delay between a key press and the first repeat, in scan intervals */ /* Delay before first repeat and subsequent repeats, in milliseconds */
static int rep_first = 51; static int rep_first = 400, rep_next = 40;
/* Delay between subsequent repeats, in scan intervals */ /* Repeat profile function */
static int rep_next = 5; static getkey_profile_t repeat_profile = NULL;
/* Same in milliseconds (values supplied by the user */ /* Repeater specification */
static int rep_first_ms = 400, rep_next_ms = 40; static enum { NONE, ALL, ARROWS, FILTER } repeat_mode;
/* Repeat filter function */ static int getkey_repeater(int key, int duration, int count)
static int (*filter_function)(int key, int duration, int count) = NULL; {
if(repeat_mode == NONE) return -1;
if(repeat_mode == FILTER) return repeat_profile(key, duration, count);
if(repeat_mode == ARROWS && key != KEY_LEFT && key != KEY_RIGHT
&& key != KEY_UP && key != KEY_DOWN) return -1;
return (count ? rep_next : rep_first) * 1000;
}
/* getkey(): Wait for a key press */ /* getkey(): Wait for a key press */
key_event_t getkey(void) key_event_t getkey(void)
@ -30,159 +38,61 @@ key_event_t getkey(void)
/* getkey_opt(): Enhanced getkey() */ /* getkey_opt(): Enhanced getkey() */
key_event_t getkey_opt(int opt, volatile int *timeout) key_event_t getkey_opt(int opt, volatile int *timeout)
{ {
key_event_t ev; keydev_t *d = keydev_std();
int shift = 0, alpha = 0, key = 0; keydev_transform_t tr0 = keydev_transform(d);
key_event_t e;
/* Last pressed key (only this key may be repeated) */ int o = KEYDEV_TR_REPEATS |
static int rep_key = 0; KEYDEV_TR_DELETE_MODIFIERS |
/* Number of repeats already emitted */ KEYDEV_TR_DELETE_RELEASES |
static int rep_count = 0; ((opt & GETKEY_MOD_SHIFT) ? KEYDEV_TR_DELAYED_SHIFT : 0) |
/* Keyboard time when the key was pressed */ ((opt & GETKEY_MOD_ALPHA) ? KEYDEV_TR_DELAYED_ALPHA : 0);
static int rep_time = 0; keydev_set_transform(d, (keydev_transform_t){ o, getkey_repeater });
/* Additional repeat delay set by the filtering function */
static int rep_delay = 0;
/* Reset the state if the repeated key went up while getkey() was not repeat_mode = NONE;
aware (this happens when different keyboard primitives are used) */ if(opt & GETKEY_REP_ARROWS) repeat_mode = ARROWS;
if(rep_key && !keydown(rep_key)) if(opt & GETKEY_REP_ALL) repeat_mode = ALL;
if(opt & GETKEY_REP_PROFILE && repeat_profile) repeat_mode = FILTER;
while(1)
{ {
rep_key = 0; e = keydev_read(d);
rep_count = 0; if(e.type == KEYEV_NONE && timeout && *timeout) break;
rep_time = 0;
rep_delay = 0;
}
while(1) switch((ev = pollevent()).type)
{
/* Key press: handle modifiers or return an event */
case KEYEV_DOWN:
key = ev.key;
if(rep_key && key != rep_key) break;
/* Handle backlight on fx9860g */
#ifdef FX9860G #ifdef FX9860G
if(opt & GETKEY_BACKLIGHT && key == KEY_OPTN && shift) /* Backlight toggle */
{ else if((opt & GETKEY_BACKLIGHT) && e.type == KEYEV_DOWN &&
e.key == KEY_OPTN && e.shift && !e.alpha)
t6k11_backlight(-1); t6k11_backlight(-1);
shift = 0;
continue;
}
#endif #endif
/* Return to menu */ /* Return-to-menu */
if(opt & GETKEY_MENU && key == KEY_MENU && !(alpha || shift)) else if((opt & GETKEY_MENU) && e.type == KEYEV_DOWN &&
{ e.key == KEY_MENU && !e.shift && !e.alpha)
gint_osmenu(); gint_osmenu();
continue;
}
/* Update modifiers */ else if(e.type == KEYEV_DOWN || e.type == KEYEV_HOLD) break;
if(opt & GETKEY_MOD_SHIFT && key == KEY_SHIFT)
{
shift ^= 1;
rep_key = 0;
continue;
}
if(opt & GETKEY_MOD_ALPHA && key == KEY_ALPHA)
{
alpha ^= 1;
rep_key = 0;
continue;
}
/* Return current event */
rep_key = key;
rep_count = 0;
rep_time = ev.time;
rep_delay = 0;
ev.mod = 1;
ev.shift = shift;
ev.alpha = alpha;
return ev;
/* If nothing happens, stop or wait for a repeat to occur */
case KEYEV_NONE:
/* Timeout has expired, return KEYEV_NONE */
if(timeout && *timeout) return ev;
/* Check that the last pressed key can be repeated */
int arrow = (rep_key == KEY_LEFT || rep_key == KEY_RIGHT ||
rep_key == KEY_UP || rep_key == KEY_DOWN);
if(!rep_key || !(
(opt & GETKEY_REP_ALL) ||
(opt & GETKEY_REP_ARROWS && arrow)
)) break;
/* If the key is key pressed long enough, create a new event */
int duration = (int16_t)(ev.time - rep_time);
if(rep_delay < 0) break;
int target = (rep_count ? rep_next : rep_first) + rep_delay;
if(duration < target) break;
/* Filter out the event if repeat filtering is on */
if(filter_function && (opt & GETKEY_REP_FILTER))
{
int s = filter_function(rep_key, duration, rep_count);
/* Drop repeats forever */
if(s < 0)
{
rep_delay = -1;
break;
}
/* Delay repeat by set amount */
if(s > 0)
{
s = (s * keysc_scan_frequency()) / 1000;
rep_delay += s;
break;
}
/* Accepts repeat (fallthrough) */
}
rep_time += target;
rep_count++;
rep_delay = 0;
ev.mod = 1;
ev.shift = shift;
ev.alpha = alpha;
ev.type = KEYEV_HOLD;
ev.key = rep_key;
return ev;
/* Reset repeating information if the repeated key is released */
case KEYEV_UP:
if(ev.key != rep_key) break;
rep_key = 0;
rep_count = 0;
rep_time = 0;
rep_delay = 0;
break;
} }
keydev_set_transform(d, tr0);
return e;
} }
/* getkey_repeat(): Set repeat delays for getkey() */ /* getkey_repeat(): Set repeat delays for getkey() */
void getkey_repeat(int first, int next) void getkey_repeat(int first, int next)
{ {
rep_first_ms = first; rep_first = first;
rep_next_ms = next; rep_next = next;
rep_first = (first * keysc_scan_frequency()) / 1000;
rep_next = (next * keysc_scan_frequency()) / 1000;
} }
/* getkey_repeat_filter(): Set the repeat filter function */ /* getkey_repeat_profile(): Get the current repeat profile function */
void getkey_repeat_filter(int (*filter)(int key, int duration, int count)) getkey_profile_t getkey_repeat_profile(void)
{ {
filter_function = filter; return repeat_profile;
} }
/* Refresh repeat delays after a change in keyboard scan frequency */ /* getkey_set_repeat_profile(): Set the repeat profile function */
void getkey_refresh_delays(void) void getkey_set_repeat_profile(getkey_profile_t profile)
{ {
getkey_repeat(rep_first_ms, rep_next_ms); repeat_profile = profile;
} }

View file

@ -1,11 +0,0 @@
//---
// gint:keysc:getkey - Internal interface to getkey()
//---
#ifndef GINT_KEYSC_GETKEY
#define GINT_KEYSC_GETKEY
/* Refresh repeat delays after a change in keyboard scan frequency */
void getkey_refresh_delays(void);
#endif /* GINT_KEYSC_GETKEY */

View file

@ -304,7 +304,10 @@ key_event_t keydev_read(keydev_t *d)
{ {
if(e.type == KEYEV_DOWN && k == KEY_SHIFT) if(e.type == KEYEV_DOWN && k == KEY_SHIFT)
{ {
d->pressed_shift |= keydev_idle(d,KEY_SHIFT,0); if(d->delayed_shift)
d->delayed_shift = 0;
else if(keydev_idle(d,KEY_SHIFT,0))
d->pressed_shift = 1;
} }
else if(e.type != KEYEV_UP && k == d->rep_key) else if(e.type != KEYEV_UP && k == d->rep_key)
{ {
@ -322,7 +325,10 @@ key_event_t keydev_read(keydev_t *d)
{ {
if(e.type == KEYEV_DOWN && k == KEY_ALPHA) if(e.type == KEYEV_DOWN && k == KEY_ALPHA)
{ {
d->pressed_alpha |= keydev_idle(d,KEY_ALPHA,0); if(d->delayed_alpha)
d->delayed_alpha = 0;
else if(keydev_idle(d,KEY_ALPHA,0))
d->pressed_alpha = 1;
} }
else if(e.type != KEYEV_UP && k == d->rep_key) else if(e.type != KEYEV_UP && k == d->rep_key)
{ {

View file

@ -14,8 +14,6 @@
#include <gint/drivers/iokbd.h> #include <gint/drivers/iokbd.h>
#include <gint/hardware.h> #include <gint/hardware.h>
#include "getkey.h"
#include <stdarg.h> #include <stdarg.h>
/* Keyboard scan frequency in Hertz. Start with 128 Hz, this frequency *must /* Keyboard scan frequency in Hertz. Start with 128 Hz, this frequency *must
@ -56,8 +54,6 @@ void keysc_set_scan_frequency(int freq)
scan_frequency = freq; scan_frequency = freq;
scan_frequency_us = 1000000 / freq; scan_frequency_us = 1000000 / freq;
getkey_refresh_delays();
if(keysc_tid < 0) return; if(keysc_tid < 0) return;
uint32_t TCOR = timer_delay(keysc_tid, scan_frequency_us, 0); uint32_t TCOR = timer_delay(keysc_tid, scan_frequency_us, 0);
timer_reload(keysc_tid, TCOR); timer_reload(keysc_tid, TCOR);