gint/src/keysc/getkey.c

141 lines
3.1 KiB
C
Raw Normal View History

//---
// gint:keysc:getkey - High-level keyboard monitoring function
//---
#include <gint/keyboard.h>
#include <gint/gint.h>
#include <gint/defs/types.h>
#ifdef FX9860G
#include <gint/drivers/t6k11.h>
#endif
2019-07-17 00:23:21 +02:00
/* Delay between a key press and the first repeat, in scan intervals */
static int rep_first = 64;
/* Delay between subsequent repeats, in scan intervals */
2019-07-18 01:29:12 +02:00
static int rep_next = 8;
/* getkey() - wait for a pressed key */
key_event_t getkey(void)
{
return getkey_opt(GETKEY_DEFAULT, NULL);
}
/* 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;
/* Last pressed key (only this key may be repeated) */
2019-07-17 00:23:21 +02:00
static int rep_key = 0;
/* Number of repeats already emitted */
2019-07-17 00:23:21 +02:00
static int rep_count = 0;
/* Keyboard time when the key was pressed */
2019-07-17 00:23:21 +02:00
static int rep_time = 0;
core, tmu: add gint_switch(), return to menu, and improve timer code * Add the gint_switch() function which executes user-provided code from the system (CASIOWIN) context. * Added interrupt masks to the core context (should have been there long ago). * Added the gint_osmenu() function that switches out of gint to invoke GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This uses many CASIOWIN syscalls, but we don't care because gint is unloaded. Trickery is used to catch the key following the return in the add-in and/or display a new application frame before GetKeyWait() even finishes after coming back. This is only available on fx9860g for now. * Removed any public syscall definition to clear up interfaces. * Patched the DMA interruption problem in a weird way on fxcg50, a driver function will be used to do that properly eventually. * Changed the driver model to save driver contexts in preallocated spaces instead of on the stack for overall less risk. * Enabled return-to-menu with the MENU key on fx9860g in getkey(). * Changed the keyboard driver to emit releases before presses, as a return-to-menu acts as a press+release of different keys in a single driver frame, which confuses getkey(). * Fixed a really stupid bug in memcpy() that made the function really not work. Improvements in the timer driver: * Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>. * Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead. Only interrupt gate entries are left hardcoded. * Discovered that not only every write to the TCNT or TCR of an ETMU takes about 1/32k of a second (hinting at registers being powered by the same clock as the timer), but every write occuring while a previous write is pending is *lost*. This led to terrible bugs when switching ETMU contexts too fast in gint_switch(). * Removed an internal timer_address() function. * Overall simplified the handling of timers and the initialization step.
2020-05-10 14:03:41 +02:00
/* 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))
{
rep_key = 0;
rep_count = 0;
rep_time = 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
if(opt & GETKEY_BACKLIGHT && key == KEY_OPTN && shift)
{
t6k11_backlight(-1);
shift = 0;
continue;
}
#endif
core, tmu: add gint_switch(), return to menu, and improve timer code * Add the gint_switch() function which executes user-provided code from the system (CASIOWIN) context. * Added interrupt masks to the core context (should have been there long ago). * Added the gint_osmenu() function that switches out of gint to invoke GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This uses many CASIOWIN syscalls, but we don't care because gint is unloaded. Trickery is used to catch the key following the return in the add-in and/or display a new application frame before GetKeyWait() even finishes after coming back. This is only available on fx9860g for now. * Removed any public syscall definition to clear up interfaces. * Patched the DMA interruption problem in a weird way on fxcg50, a driver function will be used to do that properly eventually. * Changed the driver model to save driver contexts in preallocated spaces instead of on the stack for overall less risk. * Enabled return-to-menu with the MENU key on fx9860g in getkey(). * Changed the keyboard driver to emit releases before presses, as a return-to-menu acts as a press+release of different keys in a single driver frame, which confuses getkey(). * Fixed a really stupid bug in memcpy() that made the function really not work. Improvements in the timer driver: * Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>. * Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead. Only interrupt gate entries are left hardcoded. * Discovered that not only every write to the TCNT or TCR of an ETMU takes about 1/32k of a second (hinting at registers being powered by the same clock as the timer), but every write occuring while a previous write is pending is *lost*. This led to terrible bugs when switching ETMU contexts too fast in gint_switch(). * Removed an internal timer_address() function. * Overall simplified the handling of timers and the initialization step.
2020-05-10 14:03:41 +02:00
/* Return to menu */
if(opt & GETKEY_MENU && key == KEY_MENU && !(alpha || shift))
{
core, tmu: add gint_switch(), return to menu, and improve timer code * Add the gint_switch() function which executes user-provided code from the system (CASIOWIN) context. * Added interrupt masks to the core context (should have been there long ago). * Added the gint_osmenu() function that switches out of gint to invoke GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This uses many CASIOWIN syscalls, but we don't care because gint is unloaded. Trickery is used to catch the key following the return in the add-in and/or display a new application frame before GetKeyWait() even finishes after coming back. This is only available on fx9860g for now. * Removed any public syscall definition to clear up interfaces. * Patched the DMA interruption problem in a weird way on fxcg50, a driver function will be used to do that properly eventually. * Changed the driver model to save driver contexts in preallocated spaces instead of on the stack for overall less risk. * Enabled return-to-menu with the MENU key on fx9860g in getkey(). * Changed the keyboard driver to emit releases before presses, as a return-to-menu acts as a press+release of different keys in a single driver frame, which confuses getkey(). * Fixed a really stupid bug in memcpy() that made the function really not work. Improvements in the timer driver: * Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>. * Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead. Only interrupt gate entries are left hardcoded. * Discovered that not only every write to the TCNT or TCR of an ETMU takes about 1/32k of a second (hinting at registers being powered by the same clock as the timer), but every write occuring while a previous write is pending is *lost*. This led to terrible bugs when switching ETMU contexts too fast in gint_switch(). * Removed an internal timer_address() function. * Overall simplified the handling of timers and the initialization step.
2020-05-10 14:03:41 +02:00
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;
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;
2019-07-17 00:23:21 +02:00
/* Check that the last pressed key can be repeated */
2019-07-17 00:23:21 +02:00
int arrow = (rep_key == KEY_LEFT || rep_key == KEY_RIGHT ||
rep_key == KEY_UP || rep_key == KEY_DOWN);
2019-07-17 00:23:21 +02:00
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);
2019-07-18 01:29:12 +02:00
int target = (rep_count) ? rep_next : rep_first;
if(duration < target) break;
2019-07-18 01:29:12 +02:00
rep_time += target;
2019-07-18 01:29:12 +02:00
rep_count++;
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;
break;
}
}
/* getkey_repeat() - set repeat delays for getkey() */
void getkey_repeat(int first, int next)
{
2019-07-17 00:23:21 +02:00
rep_first = (first * KEYBOARD_SCAN_FREQUENCY) / 1000;
rep_next = (next * KEYBOARD_SCAN_FREQUENCY) / 1000;
}