diff --git a/include/gint/drivers/keydev.h b/include/gint/drivers/keydev.h index f5495cc..9725e1d 100644 --- a/include/gint/drivers/keydev.h +++ b/include/gint/drivers/keydev.h @@ -207,6 +207,9 @@ typedef struct { /* Parameters for the standard repeat function */ int rep_standard_first, rep_standard_next; + /* State of keys that have changes since the last flip monitoring reset. */ + GALIGNED(4) uint8_t state_flips[12]; + } keydev_t; /* keydev_std(): Standard keyboard input device @@ -274,6 +277,17 @@ void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter); /* keydev_keydown(): Check if a key is down according to generated events */ bool keydev_keydown(keydev_t *d, int key); +/* keydev_keypressed(): Check if a key was pressed + This compares to the state at the time of the last keydev_clear_flips(). */ +bool keydev_keypressed(keydev_t *d, int key); + +/* keydev_keyreleased(): Check if a key was released + This compares to the state at the time of the last keydev_clear_flips(). */ +bool keydev_keyreleased(keydev_t *d, int key); + +/* keydev_clear_flips(): Reset flip info used by keypressed()/keyreleased() */ +void keydev_clear_flips(keydev_t *d); + /* keydev_transform(): Obtain current transform parameters */ keydev_transform_t keydev_transform(keydev_t *d); diff --git a/include/gint/keyboard.h b/include/gint/keyboard.h index edbbcee..6347507 100644 --- a/include/gint/keyboard.h +++ b/include/gint/keyboard.h @@ -60,6 +60,24 @@ extern "C" { * clearevents() reads all pending events from the input queue. + Games are often also interested in whether keys were pressed or released + _since the last frame_. gint doesn't know what a frame is, but you can track + key state flips by using cleareventflips() before reading events and + keypressed() and keyreleased() afterwards. + + * keypressed() checks if the specified key is currently down and was up at + the time of the last cleareventflips(). + * keyreleased() checks if the specified key is currently up and was down at + the time of the last cleareventflips(). + + A typical game loop might look like. cleareventflisps() must be called + _before_ clearevents(). + + cleareventflips(); + clearevents(); // or pollevent(), waitevent() + if(keydown(KEY_RIGHT)) move(); + if(keypressed(KEY_F6)) open_inventory(); + The previous functions are quite low-level. GUI programs that look like the system applications will prefer using a GetKey()-like functions that return a single key press at a time, heeds for releases, for SHIFT and ALPHA @@ -162,6 +180,12 @@ key_event_t waitevent(volatile int *timeout); /* clearevents(): Read all events waiting in the queue */ void clearevents(void); +/* cleareventflips(): Set the time reference for keypressed()/keyreleased() + The two functions keypressed() and keyreleased() will use the keyboard state + at the time this function was called to determine whether any given key was + just pressed or jut released. */ +void cleareventflips(void); + //--- // Key state functions //--- @@ -181,6 +205,16 @@ int keydown_all(int key1, ...); sequence should be terminated by a 0 integer. */ int keydown_any(int key1, ...); +/* keypressed(): Check if a key was just pressed + This function returns non-zero if the specified key is currently down, *and* + it was up at the time of the last call to cleareventflips(). */ +int keypressed(int key); + +/* keyreleased(): Check if a key was just released + This function returns non-zero if the specified key is currently up, *and* + it was down at the time of the last call to cleareventflips(). */ +int keyreleased(int key); + //--- // High-level functions //--- diff --git a/src/keysc/keydev.c b/src/keysc/keydev.c index b767505..7850d39 100644 --- a/src/keysc/keydev.c +++ b/src/keysc/keydev.c @@ -179,6 +179,7 @@ key_event_t keydev_unqueue_event(keydev_t *d) if(ev.type == KEYEV_DOWN) { d->state_queue[row] |= col; + d->state_flips[row] ^= col; /* Mark this key as the currently repeating one */ if(d->rep_key == 0 && can_repeat(d, ev.key)) { @@ -191,6 +192,7 @@ key_event_t keydev_unqueue_event(keydev_t *d) else if(ev.type == KEYEV_UP) { d->state_queue[row] &= ~col; + d->state_flips[row] ^= col; /* End the current repeating streak */ if(d->rep_key == ev.key) { @@ -219,6 +221,27 @@ bool keydev_keydown(keydev_t *d, int key) __attribute__((alias("keydev_keydown"))) bool _WEAK_keydev_keydown(keydev_t *d, int key); +bool keydev_keypressed(keydev_t *d, int key) +{ + int row = (key >> 4); + int col = 0x80 >> (key & 0x7); + + return (d->state_queue[row] & col) && (d->state_flips[row] & col); +} + +bool keydev_keyreleased(keydev_t *d, int key) +{ + int row = (key >> 4); + int col = 0x80 >> (key & 0x7); + + return !(d->state_queue[row] & col) && (d->state_flips[row] & col); +} + +void keydev_clear_flips(keydev_t *d) +{ + memset(d->state_flips, 0, sizeof d->state_flips); +} + //--- // Event transforms //--- diff --git a/src/keysc/keysc.c b/src/keysc/keysc.c index e68f193..958b5ac 100644 --- a/src/keysc/keysc.c +++ b/src/keysc/keysc.c @@ -102,12 +102,26 @@ void clearevents(void) while(pollevent().type != KEYEV_NONE); } -/* keydown(): Current key state */ +void cleareventflips(void) +{ + keydev_clear_flips(&keysc_dev); +} + int keydown(int key) { return keydev_keydown(&keysc_dev, key); } +int keypressed(int key) +{ + return keydev_keypressed(&keysc_dev, key); +} + +int keyreleased(int key) +{ + return keydev_keyreleased(&keysc_dev, key); +} + //--- // Driver initialization //---