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 */
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

View file

@ -3,23 +3,31 @@
//---
#include <gint/keyboard.h>
#include <gint/drivers/keydev.h>
#include <gint/gint.h>
#include <gint/defs/types.h>
#include "getkey.h"
#ifdef FX9860G
#include <gint/drivers/t6k11.h>
#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;
}

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)
{
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)
{

View file

@ -14,8 +14,6 @@
#include <gint/drivers/iokbd.h>
#include <gint/hardware.h>
#include "getkey.h"
#include <stdarg.h>
/* 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);