From 910677f7ff77a858c1efb44a3a0808a6454deb84 Mon Sep 17 00:00:00 2001 From: Lephe Date: Fri, 5 Mar 2021 14:49:24 +0100 Subject: [PATCH] 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. --- include/gint/keyboard.h | 51 ++++++----- src/keysc/getkey.c | 194 +++++++++++----------------------------- src/keysc/getkey.h | 11 --- src/keysc/keydev.c | 10 ++- src/keysc/keysc.c | 4 - 5 files changed, 88 insertions(+), 182 deletions(-) delete mode 100644 src/keysc/getkey.h diff --git a/include/gint/keyboard.h b/include/gint/keyboard.h index a7f7317..4007f32 100644 --- a/include/gint/keyboard.h +++ b/include/gint/keyboard.h @@ -210,8 +210,8 @@ enum { /* Repeat arrow keys, or even all keys */ GETKEY_REP_ARROWS = 0x10, GETKEY_REP_ALL = 0x20, - /* Enable repeat event filtering; see getkey_repeat_filter() */ - GETKEY_REP_FILTER = 0x40, + /* Enable custom repeat profiles; see getkey_set_repeat_profile() */ + GETKEY_REP_PROFILE = 0x40, /* No modifiers */ GETKEY_NONE = 0x00, @@ -219,6 +219,10 @@ enum { 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() 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) */ 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 - 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. +/* getkey_set_repeat_profile(): Set the repeat profile function - The time elapsed since the last accepted repeat is passed to the filter - function; this time must be larger than the repeat time set with - getkey_repeat() for getkey() and getkey_opt() to consider a repeat, but it - can be much longer if some repeat events were previously filtered out. + The repeat profile is called by getkey() and getkey_opt() when a key is + pressed or held, and getkey() is planning to repeat it. The profile decides + whether such repetition is allowed, and if so, how long it shoud take. The + profile has access to the following information: - @key Key that is about to be repeated - @duration Duration since last accepted repeat - @count Number of previous repeats + @key Key for which a repetition is being considered + @duration Duration since the key was first pressed (us) + @count Number of previous repeats (0 on the first call) - The repeat function must either return: - * 0, in which case the even is accepted - * A positive number of milliseconds, in which case the event is tentatively - re-emitted after that time (the filter function will be called again) - * A negative number, in which case the event is dropped and further repeats - are denied. + The profile function must either return a positive number of microseconds to + wait until the next repeat, or 0 to block the repeat indefinitely. Note that + the keyboard device typically updates every 7-8 ms, timings are tracked in + microseconds only to limit deviations. - By default the filter function is NULL, which accepts all repeat events. - This behavior can be restored explicitly by calling with function=NULL. */ -void getkey_repeat_filter(int (*filter)(int key, int duration, int count)); + Setting a repeat profile overrides GETKEY_REP_ARROWS, GETKEY_REP_ALL, and + the repeat delays. Calling with profile=NULL restores this behavior. + + 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 diff --git a/src/keysc/getkey.c b/src/keysc/getkey.c index bfc15ff..adfdce3 100644 --- a/src/keysc/getkey.c +++ b/src/keysc/getkey.c @@ -3,23 +3,31 @@ //--- #include +#include #include #include -#include "getkey.h" #ifdef FX9860G #include #endif -/* Delay between a key press and the first repeat, in scan intervals */ -static int rep_first = 51; -/* Delay between subsequent repeats, in scan intervals */ -static int rep_next = 5; -/* Same in milliseconds (values supplied by the user */ -static int rep_first_ms = 400, rep_next_ms = 40; +/* Delay before first repeat and subsequent repeats, in milliseconds */ +static int rep_first = 400, rep_next = 40; +/* Repeat profile function */ +static getkey_profile_t repeat_profile = NULL; +/* Repeater specification */ +static enum { NONE, ALL, ARROWS, FILTER } repeat_mode; -/* Repeat filter function */ -static int (*filter_function)(int key, int duration, int count) = NULL; +static int getkey_repeater(int key, int duration, int count) +{ + 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 */ key_event_t getkey(void) @@ -30,159 +38,61 @@ key_event_t getkey(void) /* getkey_opt(): Enhanced getkey() */ key_event_t getkey_opt(int opt, volatile int *timeout) { - key_event_t ev; - int shift = 0, alpha = 0, key = 0; + keydev_t *d = keydev_std(); + keydev_transform_t tr0 = keydev_transform(d); + key_event_t e; - /* Last pressed key (only this key may be repeated) */ - static int rep_key = 0; - /* Number of repeats already emitted */ - static int rep_count = 0; - /* Keyboard time when the key was pressed */ - static int rep_time = 0; - /* Additional repeat delay set by the filtering function */ - static int rep_delay = 0; + int o = KEYDEV_TR_REPEATS | + KEYDEV_TR_DELETE_MODIFIERS | + KEYDEV_TR_DELETE_RELEASES | + ((opt & GETKEY_MOD_SHIFT) ? KEYDEV_TR_DELAYED_SHIFT : 0) | + ((opt & GETKEY_MOD_ALPHA) ? KEYDEV_TR_DELAYED_ALPHA : 0); + keydev_set_transform(d, (keydev_transform_t){ o, getkey_repeater }); - /* Reset the state if the repeated key went up while getkey() was not - aware (this happens when different keyboard primitives are used) */ - if(rep_key && !keydown(rep_key)) + repeat_mode = NONE; + if(opt & GETKEY_REP_ARROWS) repeat_mode = ARROWS; + if(opt & GETKEY_REP_ALL) repeat_mode = ALL; + if(opt & GETKEY_REP_PROFILE && repeat_profile) repeat_mode = FILTER; + + while(1) { - rep_key = 0; - rep_count = 0; - rep_time = 0; - rep_delay = 0; - } + e = keydev_read(d); + if(e.type == KEYEV_NONE && timeout && *timeout) break; - 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 - 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); - shift = 0; - continue; - } #endif - /* Return to menu */ - if(opt & GETKEY_MENU && key == KEY_MENU && !(alpha || shift)) - { + /* Return-to-menu */ + else if((opt & GETKEY_MENU) && e.type == KEYEV_DOWN && + e.key == KEY_MENU && !e.shift && !e.alpha) gint_osmenu(); - continue; - } - /* Update modifiers */ - 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; + else if(e.type == KEYEV_DOWN || e.type == KEYEV_HOLD) break; } + + keydev_set_transform(d, tr0); + return e; } /* getkey_repeat(): Set repeat delays for getkey() */ void getkey_repeat(int first, int next) { - rep_first_ms = first; - rep_next_ms = next; - - rep_first = (first * keysc_scan_frequency()) / 1000; - rep_next = (next * keysc_scan_frequency()) / 1000; + rep_first = first; + rep_next = next; } -/* getkey_repeat_filter(): Set the repeat filter function */ -void getkey_repeat_filter(int (*filter)(int key, int duration, int count)) +/* getkey_repeat_profile(): Get the current repeat profile function */ +getkey_profile_t getkey_repeat_profile(void) { - filter_function = filter; + return repeat_profile; } -/* Refresh repeat delays after a change in keyboard scan frequency */ -void getkey_refresh_delays(void) +/* getkey_set_repeat_profile(): Set the repeat profile function */ +void getkey_set_repeat_profile(getkey_profile_t profile) { - getkey_repeat(rep_first_ms, rep_next_ms); + repeat_profile = profile; } diff --git a/src/keysc/getkey.h b/src/keysc/getkey.h deleted file mode 100644 index 7437e56..0000000 --- a/src/keysc/getkey.h +++ /dev/null @@ -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 */ diff --git a/src/keysc/keydev.c b/src/keysc/keydev.c index 3d87411..21a65bf 100644 --- a/src/keysc/keydev.c +++ b/src/keysc/keydev.c @@ -304,7 +304,10 @@ key_event_t keydev_read(keydev_t *d) { 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) { @@ -322,7 +325,10 @@ key_event_t keydev_read(keydev_t *d) { 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) { diff --git a/src/keysc/keysc.c b/src/keysc/keysc.c index 8c180ae..dda3fa8 100644 --- a/src/keysc/keysc.c +++ b/src/keysc/keysc.c @@ -14,8 +14,6 @@ #include #include -#include "getkey.h" - #include /* 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_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);