diff --git a/include/gint/keyboard.h b/include/gint/keyboard.h index 8225954..39787dc 100644 --- a/include/gint/keyboard.h +++ b/include/gint/keyboard.h @@ -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 //--- diff --git a/src/keysc/getkey.c b/src/keysc/getkey.c index 0bcde25..d6305d4 100644 --- a/src/keysc/getkey.c +++ b/src/keysc/getkey.c @@ -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; +}