From d4f1b520c924e0951f7bb9326098ea3487e55e4a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 8 Apr 2020 19:16:31 -0700 Subject: [PATCH] Added support for press/release hardware keyboard events in iOS 13.4 --- src/events/SDL_events.c | 8 +- src/events/SDL_keyboard.c | 80 ++++++++++++++++--- src/events/SDL_keyboard_c.h | 7 ++ src/video/uikit/SDL_uikitview.m | 93 +++++++++++------------ src/video/uikit/SDL_uikitviewcontroller.m | 65 ++++++++-------- 5 files changed, 161 insertions(+), 92 deletions(-) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 6f07116bc..a6f0d2451 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -35,9 +35,9 @@ #undef SDL_PRIs64 #ifdef __WIN32__ -#define SDL_PRIs64 "I64d" +#define SDL_PRIs64 "I64d" #else -#define SDL_PRIs64 "lld" +#define SDL_PRIs64 "lld" #endif /* An arbitrary limit so we don't have unbounded growth */ @@ -678,10 +678,14 @@ SDL_PumpEvents(void) { SDL_VideoDevice *_this = SDL_GetVideoDevice(); + /* Release any keys held down from last frame */ + SDL_ReleaseAutoReleaseKeys(); + /* Get events from the video subsystem */ if (_this) { _this->PumpEvents(_this); } + #if !SDL_JOYSTICK_DISABLED /* Check for joystick state change */ if ((!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] || SDL_JoystickEventState(SDL_QUERY))) { diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c index 502a7717c..6820f0d91 100644 --- a/src/events/SDL_keyboard.c +++ b/src/events/SDL_keyboard.c @@ -33,6 +33,9 @@ /* Global keyboard information */ +#define KEYBOARD_HARDWARE 0x01 +#define KEYBOARD_AUTORELEASE 0x02 + typedef struct SDL_Keyboard SDL_Keyboard; struct SDL_Keyboard @@ -40,8 +43,10 @@ struct SDL_Keyboard /* Data common to all keyboards */ SDL_Window *focus; Uint16 modstate; + Uint8 keysource[SDL_NUM_SCANCODES]; Uint8 keystate[SDL_NUM_SCANCODES]; SDL_Keycode keymap[SDL_NUM_SCANCODES]; + SDL_bool autorelease_pending; }; static SDL_Keyboard SDL_keyboard; @@ -675,19 +680,20 @@ SDL_SetKeyboardFocus(SDL_Window * window) } } -int -SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode) +static int +SDL_SendKeyboardKeyInternal(Uint8 source, Uint8 state, SDL_Scancode scancode) { SDL_Keyboard *keyboard = &SDL_keyboard; int posted; SDL_Keymod modifier; SDL_Keycode keycode; Uint32 type; - Uint8 repeat; + Uint8 repeat = SDL_FALSE; - if (!scancode) { + if (scancode == SDL_SCANCODE_UNKNOWN) { return 0; } + #ifdef DEBUG_KEYBOARD printf("The '%s' key has been %s\n", SDL_GetScancodeName(scancode), state == SDL_PRESSED ? "pressed" : "released"); @@ -707,12 +713,20 @@ SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode) } /* Drop events that don't change state */ - repeat = (state && keyboard->keystate[scancode]); - if (keyboard->keystate[scancode] == state && !repeat) { -#if 0 - printf("Keyboard event didn't change state - dropped!\n"); -#endif - return 0; + if (state) { + if (keyboard->keystate[scancode]) { + if (!(keyboard->keysource[scancode] & source)) { + keyboard->keysource[scancode] |= source; + return 0; + } + repeat = SDL_TRUE; + } + keyboard->keysource[scancode] |= source; + } else { + if (!keyboard->keystate[scancode]) { + return 0; + } + keyboard->keysource[scancode] = 0; } /* Update internal keyboard state */ @@ -720,6 +734,10 @@ SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode) keycode = keyboard->keymap[scancode]; + if (source == KEYBOARD_AUTORELEASE) { + keyboard->autorelease_pending = SDL_TRUE; + } + /* Update modifiers state if applicable */ switch (keycode) { case SDLK_LCTRL: @@ -785,6 +803,48 @@ SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode) return (posted); } +int +SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode) +{ + return SDL_SendKeyboardKeyInternal(KEYBOARD_HARDWARE, state, scancode); +} + +int +SDL_SendKeyboardKeyAutoRelease(SDL_Scancode scancode) +{ + return SDL_SendKeyboardKeyInternal(KEYBOARD_AUTORELEASE, SDL_PRESSED, scancode); +} + +void +SDL_ReleaseAutoReleaseKeys(void) +{ + SDL_Keyboard *keyboard = &SDL_keyboard; + SDL_Scancode scancode; + + if (keyboard->autorelease_pending) { + for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_NUM_SCANCODES; ++scancode) { + if (keyboard->keysource[scancode] == KEYBOARD_AUTORELEASE) { + SDL_SendKeyboardKeyInternal(KEYBOARD_AUTORELEASE, SDL_RELEASED, scancode); + } + } + keyboard->autorelease_pending = SDL_FALSE; + } +} + +SDL_bool +SDL_HardwareKeyboardKeyPressed(void) +{ + SDL_Keyboard *keyboard = &SDL_keyboard; + SDL_Scancode scancode; + + for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_NUM_SCANCODES; ++scancode) { + if ((keyboard->keysource[scancode] & KEYBOARD_HARDWARE) != 0) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + int SDL_SendKeyboardText(const char *text) { diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h index 1faf3e225..22bc48f15 100644 --- a/src/events/SDL_keyboard_c.h +++ b/src/events/SDL_keyboard_c.h @@ -49,6 +49,13 @@ extern void SDL_SetKeyboardFocus(SDL_Window * window); /* Send a keyboard key event */ extern int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode); +extern int SDL_SendKeyboardKeyAutoRelease(SDL_Scancode scancode); + +/* Release all the autorelease keys */ +extern void SDL_ReleaseAutoReleaseKeys(void); + +/* Return true if any hardware key is pressed */ +extern SDL_bool SDL_HardwareKeyboardKeyPressed(void); /* Send keyboard text input */ extern int SDL_SendKeyboardText(const char *text); diff --git a/src/video/uikit/SDL_uikitview.m b/src/video/uikit/SDL_uikitview.m index 67c28a817..0c56b86cc 100644 --- a/src/video/uikit/SDL_uikitview.m +++ b/src/video/uikit/SDL_uikitview.m @@ -245,61 +245,64 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick; } #if TARGET_OS_TV || defined(__IPHONE_9_1) -- (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype +- (SDL_Scancode)scancodeFromPress:(UIPress*)press { - switch (presstype) { - case UIPressTypeUpArrow: - return SDL_SCANCODE_UP; - case UIPressTypeDownArrow: - return SDL_SCANCODE_DOWN; - case UIPressTypeLeftArrow: - return SDL_SCANCODE_LEFT; - case UIPressTypeRightArrow: - return SDL_SCANCODE_RIGHT; - case UIPressTypeSelect: - /* HIG says: "primary button behavior" */ - return SDL_SCANCODE_RETURN; - case UIPressTypeMenu: - /* HIG says: "returns to previous screen" */ - return SDL_SCANCODE_ESCAPE; - case UIPressTypePlayPause: - /* HIG says: "secondary button behavior" */ - return SDL_SCANCODE_PAUSE; - default: - return SDL_SCANCODE_UNKNOWN; + if (press.key != nil) { + return (SDL_Scancode)press.key.keyCode; } + + /* Presses from Apple TV remote */ + if (!SDL_AppleTVRemoteOpenedAsJoystick) { + switch (press.type) { + case UIPressTypeUpArrow: + return SDL_SCANCODE_UP; + case UIPressTypeDownArrow: + return SDL_SCANCODE_DOWN; + case UIPressTypeLeftArrow: + return SDL_SCANCODE_LEFT; + case UIPressTypeRightArrow: + return SDL_SCANCODE_RIGHT; + case UIPressTypeSelect: + /* HIG says: "primary button behavior" */ + return SDL_SCANCODE_RETURN; + case UIPressTypeMenu: + /* HIG says: "returns to previous screen" */ + return SDL_SCANCODE_ESCAPE; + case UIPressTypePlayPause: + /* HIG says: "secondary button behavior" */ + return SDL_SCANCODE_PAUSE; + default: + break; + } + } + + return SDL_SCANCODE_UNKNOWN; } - (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)event { - if (!SDL_AppleTVRemoteOpenedAsJoystick) { - for (UIPress *press in presses) { - SDL_Scancode scancode = [self scancodeFromPressType:press.type]; - SDL_SendKeyboardKey(SDL_PRESSED, scancode); - } - } + for (UIPress *press in presses) { + SDL_Scancode scancode = [self scancodeFromPress:press]; + SDL_SendKeyboardKey(SDL_PRESSED, scancode); + } [super pressesBegan:presses withEvent:event]; } - (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)event { - if (!SDL_AppleTVRemoteOpenedAsJoystick) { - for (UIPress *press in presses) { - SDL_Scancode scancode = [self scancodeFromPressType:press.type]; - SDL_SendKeyboardKey(SDL_RELEASED, scancode); - } - } + for (UIPress *press in presses) { + SDL_Scancode scancode = [self scancodeFromPress:press]; + SDL_SendKeyboardKey(SDL_RELEASED, scancode); + } [super pressesEnded:presses withEvent:event]; } - (void)pressesCancelled:(NSSet *)presses withEvent:(UIPressesEvent *)event { - if (!SDL_AppleTVRemoteOpenedAsJoystick) { - for (UIPress *press in presses) { - SDL_Scancode scancode = [self scancodeFromPressType:press.type]; - SDL_SendKeyboardKey(SDL_RELEASED, scancode); - } - } + for (UIPress *press in presses) { + SDL_Scancode scancode = [self scancodeFromPress:press]; + SDL_SendKeyboardKey(SDL_RELEASED, scancode); + } [super pressesCancelled:presses withEvent:event]; } @@ -320,20 +323,16 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick; * which better maps to swipe gestures. */ switch (gesture.direction) { case UISwipeGestureRecognizerDirectionUp: - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_UP); - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_UP); + SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_UP); break; case UISwipeGestureRecognizerDirectionDown: - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DOWN); - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DOWN); + SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_DOWN); break; case UISwipeGestureRecognizerDirectionLeft: - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LEFT); - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LEFT); + SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_LEFT); break; case UISwipeGestureRecognizerDirectionRight: - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RIGHT); - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RIGHT); + SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_RIGHT); break; } } diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m index fcb27fff9..bda8bf144 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.m +++ b/src/video/uikit/SDL_uikitviewcontroller.m @@ -317,8 +317,7 @@ SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *o } if (scancode != SDL_SCANCODE_UNKNOWN) { - SDL_SendKeyboardKey(SDL_PRESSED, scancode); - SDL_SendKeyboardKey(SDL_RELEASED, scancode); + SDL_SendKeyboardKeyAutoRelease(scancode); } } @@ -342,7 +341,7 @@ SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *o [coordinator animateAlongsideTransition:^(id context) {} completion:^(id context) { self->rotatingOrientation = NO; - }]; + }]; } #else - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { @@ -411,36 +410,38 @@ SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *o { NSUInteger len = changeText.length; if (len > 0) { - /* Go through all the characters in the string we've been sent and - * convert them to key presses */ - int i; - for (i = 0; i < len; i++) { - unichar c = [changeText characterAtIndex:i]; - SDL_Scancode code; - Uint16 mod; + if (!SDL_HardwareKeyboardKeyPressed()) { + /* Go through all the characters in the string we've been sent and + * convert them to key presses */ + int i; + for (i = 0; i < len; i++) { + unichar c = [changeText characterAtIndex:i]; + SDL_Scancode code; + Uint16 mod; - if (c < 127) { - /* Figure out the SDL_Scancode and SDL_keymod for this unichar */ - code = unicharToUIKeyInfoTable[c].code; - mod = unicharToUIKeyInfoTable[c].mod; - } else { - /* We only deal with ASCII right now */ - code = SDL_SCANCODE_UNKNOWN; - mod = 0; - } + if (c < 127) { + /* Figure out the SDL_Scancode and SDL_keymod for this unichar */ + code = unicharToUIKeyInfoTable[c].code; + mod = unicharToUIKeyInfoTable[c].mod; + } else { + /* We only deal with ASCII right now */ + code = SDL_SCANCODE_UNKNOWN; + mod = 0; + } - if (mod & KMOD_SHIFT) { - /* If character uses shift, press shift down */ - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT); - } + if (mod & KMOD_SHIFT) { + /* If character uses shift, press shift down */ + SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT); + } - /* send a keydown and keyup even for the character */ - SDL_SendKeyboardKey(SDL_PRESSED, code); - SDL_SendKeyboardKey(SDL_RELEASED, code); + /* send a keydown and keyup even for the character */ + SDL_SendKeyboardKey(SDL_PRESSED, code); + SDL_SendKeyboardKey(SDL_RELEASED, code); - if (mod & KMOD_SHIFT) { - /* If character uses shift, press shift back up */ - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT); + if (mod & KMOD_SHIFT) { + /* If character uses shift, press shift back up */ + SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT); + } } } SDL_SendKeyboardText([changeText UTF8String]); @@ -491,8 +492,7 @@ SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *o changeText = nil; if (textField.markedTextRange == nil) { /* it wants to replace text with nothing, ie a delete */ - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE); - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE); + SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_BACKSPACE); } if (textField.text.length < 16) { textField.text = obligateForBackspace; @@ -506,8 +506,7 @@ SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *o /* Terminates the editing session */ - (BOOL)textFieldShouldReturn:(UITextField*)_textField { - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN); - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN); + SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_RETURN); if (keyboardVisible && SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE)) { SDL_StopTextInput();