keyboard: add custom repeat filters for full repeat control

This change introduces a new getkey_repeat_filter() function that can be
used to individually accept, deny or delay repeat events for specific
keys and timings.
This commit is contained in:
Lephe 2020-08-05 11:50:32 +02:00
parent a4d23ef7ad
commit 4288dc27d9
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
2 changed files with 70 additions and 5 deletions

View file

@ -200,11 +200,13 @@ 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,
/* No modifiers */
GETKEY_NONE = 0x00,
/* Default settings of getkey() */
GETKEY_DEFAULT = 0x1f,
GETKEY_DEFAULT = 0x5f,
};
/* getkey_opt(): Enhanced getkey()
@ -244,6 +246,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
This function is called by getkey() and getkey_opt() every time a repeat
event occurs when GETKEY_REPEAT_FILTER. The function can decide whether to
keep, delay it or drop it entirely. 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
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.
@key Key that is about to be repeated
@duration Duration since last accepted repeat
@count Number of previous repeats
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.
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));
//---
// Key code functions
//---

View file

@ -15,13 +15,16 @@ static int rep_first = 64;
/* Delay between subsequent repeats, in scan intervals */
static int rep_next = 8;
/* getkey() - wait for a pressed key */
/* Repeat filter function */
static int (*filter_function)(int key, int duration, int count) = NULL;
/* getkey(): Wait for a key press */
key_event_t getkey(void)
{
return getkey_opt(GETKEY_DEFAULT, NULL);
}
/* getkey_opt() - enhanced getkey() */
/* getkey_opt(): Enhanced getkey() */
key_event_t getkey_opt(int opt, volatile int *timeout)
{
key_event_t ev;
@ -33,6 +36,8 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
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;
/* Reset the state if the repeated key went up while getkey() was not
aware (this happens when different keyboard primitives are used) */
@ -41,6 +46,7 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
rep_key = 0;
rep_count = 0;
rep_time = 0;
rep_delay = 0;
}
while(1) switch((ev = pollevent()).type)
@ -85,6 +91,7 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
rep_key = key;
rep_count = 0;
rep_time = ev.time;
rep_delay = 0;
ev.mod = 1;
ev.shift = shift;
@ -108,11 +115,33 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
/* If the key is key pressed long enough, create a new event */
int duration = (int16_t)(ev.time - rep_time);
int target = (rep_count) ? rep_next : rep_first;
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 * KEYBOARD_SCAN_FREQUENCY) / 1000;
rep_delay += s;
break;
}
/* Accepts repeat (fallthrough) */
}
rep_time += target;
rep_count++;
rep_delay = 0;
ev.mod = 1;
ev.shift = shift;
@ -128,13 +157,20 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
rep_key = 0;
rep_count = 0;
rep_time = 0;
rep_delay = 0;
break;
}
}
/* getkey_repeat() - set repeat delays for getkey() */
/* getkey_repeat(): Set repeat delays for getkey() */
void getkey_repeat(int first, int next)
{
rep_first = (first * KEYBOARD_SCAN_FREQUENCY) / 1000;
rep_next = (next * KEYBOARD_SCAN_FREQUENCY) / 1000;
}
/* getkey_repeat_filter(): Set the repeat filter function */
void getkey_repeat_filter(int (*filter)(int key, int duration, int count))
{
filter_function = filter;
}