diff options
author | Campbell Barton <ideasman42@gmail.com> | 2020-03-06 09:24:12 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2020-03-06 09:31:28 +0300 |
commit | 5be0e3430d13341feddee739997130239daf71d5 (patch) | |
tree | b8af4c1d47d40425d0703883f8efdc0c297e37ea /intern/ghost | |
parent | 73ef27f15611ccb254816e199f8c74103b3d5172 (diff) |
GHOST/Keymap: support for detecting repeat events
- Keymap items now have 'repeat' boolean which can be set
to make keymap items respond to key repeat events or not.
- Support for X11 & WIN32 (not macOS currently).
This allows for the possibility to perform actions while a key is held
and finish the action upon release.
Thanks to @Severin for review and WIN32 support.
Diffstat (limited to 'intern/ghost')
-rw-r--r-- | intern/ghost/GHOST_Types.h | 3 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_EventKey.h | 11 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_NDOFManager.cpp | 2 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemCocoa.mm | 4 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemSDL.cpp | 2 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWin32.cpp | 18 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWin32.h | 2 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemX11.cpp | 73 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemX11.h | 2 |
9 files changed, 107 insertions, 10 deletions
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 38a6a0b04d2..8bc75d01b96 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -542,6 +542,9 @@ typedef struct { char ascii; /** The unicode character. if the length is 6, not NULL terminated if all 6 are set */ char utf8_buf[6]; + + /** Generated by auto-repeat. */ + char is_repeat; } GHOST_TEventKeyData; typedef struct { diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index f42dc99aaa5..24e20b20659 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -38,12 +38,17 @@ class GHOST_EventKey : public GHOST_Event { * \param type The type of key event. * \param key The key code of the key. */ - GHOST_EventKey(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key) + GHOST_EventKey(GHOST_TUns64 msec, + GHOST_TEventType type, + GHOST_IWindow *window, + GHOST_TKey key, + bool is_repeat) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; m_keyEventData.ascii = '\0'; m_keyEventData.utf8_buf[0] = '\0'; + m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; } @@ -59,7 +64,8 @@ class GHOST_EventKey : public GHOST_Event { GHOST_IWindow *window, GHOST_TKey key, char ascii, - const char utf8_buf[6]) + const char utf8_buf[6], + bool is_repeat) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; @@ -68,6 +74,7 @@ class GHOST_EventKey : public GHOST_Event { memcpy(m_keyEventData.utf8_buf, utf8_buf, sizeof(m_keyEventData.utf8_buf)); else m_keyEventData.utf8_buf[0] = '\0'; + m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; } diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index 9999bfd7ea6..3fe61ee0aa9 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -318,7 +318,7 @@ void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key, GHOST_IWindow *window) { GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; - GHOST_EventKey *event = new GHOST_EventKey(time, type, window, key); + GHOST_EventKey *event = new GHOST_EventKey(time, type, window, key, false); #ifdef DEBUG_NDOF_BUTTONS printf("keyboard %s\n", press ? "down" : "up"); diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index b65404cf9b1..ee05505f682 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1806,7 +1806,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) if ([event type] == NSEventTypeKeyDown) { pushEvent(new GHOST_EventKey( - [event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, ascii, utf8_buf)); + [event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, ascii, utf8_buf, false)); #if 0 printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", [event keyCode], @@ -1820,7 +1820,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) } else { pushEvent(new GHOST_EventKey( - [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL)); + [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL, false)); #if 0 printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", [event keyCode], diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 7ed912b8218..656afb9d050 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -591,7 +591,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) } } - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, NULL); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, NULL, false); break; } } diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 7d2a8f5810c..5bf40ba33d0 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1047,6 +1047,20 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA if (key != GHOST_kKeyUnknown) { char utf8_char[6] = {0}; char ascii = 0; + bool is_repeat = false; + + /* Unlike on Linux, not all keys can send repeat events. E.g. modifier keys don't. */ + if (keyDown) { + if (system->m_keycode_last_repeat_key == vk) { + is_repeat = true; + } + system->m_keycode_last_repeat_key = vk; + } + else { + if (system->m_keycode_last_repeat_key == vk) { + system->m_keycode_last_repeat_key = 0; + } + } wchar_t utf16[3] = {0}; BYTE state[256] = {0}; @@ -1083,7 +1097,8 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA window, key, ascii, - utf8_char); + utf8_char, + is_repeat); // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter } @@ -1520,6 +1535,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, modifiers.clear(); system->storeModifierKeys(modifiers); system->m_wheelDeltaAccum = 0; + system->m_keycode_last_repeat_key = 0; event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window); diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index c5af3e120be..bf9d18ca380 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -414,6 +414,8 @@ class GHOST_SystemWin32 : public GHOST_System { /** The current state of the modifier keys. */ GHOST_ModifierKeys m_modifierKeys; + /** The virtual-key code (VKey) of the last press event. Used to detect repeat events. */ + unsigned short m_keycode_last_repeat_key; /** State variable set at initialization. */ bool m_hasPerformanceCounter; /** High frequency timer variable. */ diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 60d08305621..05c311077f9 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -93,6 +93,11 @@ * instead of active one. See T47228 and D1746 */ #define USE_NON_LATIN_KB_WORKAROUND +static uchar bit_is_on(const uchar *ptr, int bit) +{ + return ptr[bit >> 3] & (1 << (bit & 7)); +} + static GHOST_TKey ghost_key_from_keysym(const KeySym key); static GHOST_TKey ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCode keycode); static GHOST_TKey ghost_key_from_keysym_or_keycode(const KeySym key, @@ -196,6 +201,7 @@ GHOST_SystemX11::GHOST_SystemX11() : GHOST_System(), m_xkb_descr(NULL), m_start_ m_xkb_descr = XkbGetMap(m_display, 0, XkbUseCoreKbd); if (m_xkb_descr) { XkbGetNames(m_display, XkbKeyNamesMask, m_xkb_descr); + XkbGetControls(m_display, XkbPerKeyRepeatMask | XkbRepeatKeysMask, m_xkb_descr); } } @@ -747,7 +753,8 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) window, ghost_key_from_keysym(modifiers[i]), '\0', - NULL)); + NULL, + false)); } } } @@ -822,6 +829,64 @@ void GHOST_SystemX11::processEvent(XEvent *xe) GHOST_WindowX11 *window = findGhostWindow(xe->xany.window); GHOST_Event *g_event = NULL; + /* Detect auto-repeat. */ + bool is_repeat = false; + if (xe->type == KeyPress || xe->type == KeyRelease) { + XKeyEvent *xke = &(xe->xkey); + + /* Set to true if this key will repeat. */ + bool is_repeat_keycode = false; + + if (m_xkb_descr != NULL) { + /* Use XKB support. */ + is_repeat_keycode = ( + /* Should always be true, check just in case. */ + (xke->keycode < (XkbPerKeyBitArraySize << 3)) && + bit_is_on(m_xkb_descr->ctrls->per_key_repeat, xke->keycode)); + } + else { + /* No XKB support (filter by modifier). */ + switch (XLookupKeysym(xke, 0)) { + case XK_Shift_L: + case XK_Shift_R: + case XK_Control_L: + case XK_Control_R: + case XK_Alt_L: + case XK_Alt_R: + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + case XK_Caps_Lock: + case XK_Scroll_Lock: + case XK_Num_Lock: { + break; + } + default: { + is_repeat_keycode = true; + } + } + } + + if (is_repeat_keycode) { + if (xe->type == KeyPress) { + if (m_keycode_last_repeat_key == xke->keycode) { + is_repeat = true; + } + m_keycode_last_repeat_key = xke->keycode; + } + else { + if (m_keycode_last_repeat_key == xke->keycode) { + m_keycode_last_repeat_key = (uint)-1; + } + } + } + } + else if (xe->type == EnterNotify) { + /* We can't tell how the key state changed, clear it to avoid stuck keys. */ + m_keycode_last_repeat_key = (uint)-1; + } + #ifdef USE_XINPUT_HOTPLUG /* Hot-Plug support */ if (m_xinput_version.present) { @@ -1129,7 +1194,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } #endif - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, ascii, utf8_buf); + g_event = new GHOST_EventKey( + getMilliSeconds(), type, window, gkey, ascii, utf8_buf, is_repeat); #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* when using IM for some languages such as Japanese, @@ -1153,7 +1219,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe) /* enqueue previous character */ pushEvent(g_event); - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, '\0', &utf8_buf[i]); + g_event = new GHOST_EventKey( + getMilliSeconds(), type, window, gkey, '\0', &utf8_buf[i], is_repeat); } } diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index 67dd0789ac3..8736e20c57f 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -366,6 +366,8 @@ class GHOST_SystemX11 : public GHOST_System { unsigned int m_last_release_keycode; Time m_last_release_time; + uint m_keycode_last_repeat_key; + /** * Return the ghost window associated with the * X11 window xwind |