@@ -93,6 +93,12 @@ class RawInputQueue {
9393 }
9494};
9595
96+ struct KeyInfo {
97+ uint16_t vkey;
98+ uint16_t scanCode;
99+ bool isE0;
100+ };
101+
96102class KeyStateTracker {
97103private:
98104 std::unordered_map<uint32_t , bool > m_keyStates;
@@ -103,6 +109,12 @@ class KeyStateTracker {
103109 (isE0 ? 1 : 0 );
104110 }
105111
112+ static void unmakeKey (uint32_t key, uint16_t & vkey, uint16_t & scanCode, bool & isE0) {
113+ vkey = static_cast <uint16_t >(key >> 16 );
114+ scanCode = static_cast <uint16_t >((key >> 1 ) & 0xFFFF );
115+ isE0 = (key & 1 ) != 0 ;
116+ }
117+
106118public:
107119 static KeyStateTracker& get () {
108120 static KeyStateTracker instance;
@@ -153,6 +165,25 @@ class KeyStateTracker {
153165 m_keyStates[key] = isDown;
154166 return wasDown && isDown;
155167 }
168+
169+ std::vector<KeyInfo> getPressedKeys () {
170+ std::vector<KeyInfo> pressed;
171+ for (auto const & [key, isDown] : m_keyStates) {
172+ if (isDown) {
173+ uint16_t vkey, scanCode;
174+ bool isE0;
175+ unmakeKey (key, vkey, scanCode, isE0);
176+ pressed.push_back ({vkey, scanCode, isE0});
177+ }
178+ }
179+
180+ return pressed;
181+ }
182+
183+ void clear () {
184+ m_keyStates.clear ();
185+ m_currentMods = KeyboardModifier::None;
186+ }
156187};
157188
158189static HWND g_rawInputHWND, g_mainWindowHWND;
@@ -402,6 +433,10 @@ class DummyEGLView : public CCEGLView {
402433 void onGLFWCharCallback (GLFWwindow* window, unsigned int c) {
403434 CCEGLView::onGLFWCharCallback (window, c);
404435 }
436+
437+ void onGLFWWindowFocus (GLFWwindow* window, int focused) {
438+ CCEGLView::onGLFWWindowFocus (window, focused);
439+ }
405440};
406441
407442static void GLFWScrollCallback (GLFWwindow* window, double xoffset, double yoffset) {
@@ -415,7 +450,124 @@ static void GLFWCharCallback(GLFWwindow* window, unsigned int c) {
415450 static_cast <DummyEGLView*>(CCEGLView::get ())->onGLFWCharCallback (window, c);
416451}
417452
453+ static void GLFWFocusCallback (GLFWwindow* window, int focused);
454+
418455struct GeodeRawInput : Modify<GeodeRawInput, CCEGLView> {
456+ void handleKeyboardEvent (RawInputEvent const & evt) {
457+ using enum KeyboardInputData::Action;
458+ bool isDown = evt.type == RawInputEvent::Type::KeyDown;
459+
460+ enumKeyCodes keyCode = keyToKeyCode (
461+ evt.keyboard .vkey ,
462+ evt.keyboard .isE0
463+ );
464+
465+ KeyboardInputData data (
466+ keyCode,
467+ isDown ? (evt.keyboard .isRepeat ? Repeat : Press) : Release,
468+ {evt.keyboard .vkey , evt.keyboard .scanCode },
469+ evt.timestamp ,
470+ evt.mods
471+ );
472+
473+ auto result = KeyboardInputEvent (keyCode).send (data);
474+
475+ // copy values from event, if someone modifies it
476+ isDown = data.action != Release;
477+ keyCode = data.key ;
478+
479+ if (result == ListenerResult::Propagate && keyCode != KEY_Unknown) {
480+ auto * ime = CCIMEDispatcher::sharedDispatcher ();
481+ if (keyCode == enumKeyCodes::KEY_Backspace && isDown) {
482+ ime->dispatchDeleteBackward ();
483+ } else if (keyCode == enumKeyCodes::KEY_Delete && isDown) {
484+ ime->dispatchDeleteForward ();
485+ }
486+
487+ auto * keyboardDispatcher = CCKeyboardDispatcher::get ();
488+
489+ keyboardDispatcher->updateModifierKeys (
490+ data.modifiers & KeyboardModifier::Shift,
491+ data.modifiers & KeyboardModifier::Control,
492+ data.modifiers & KeyboardModifier::Alt,
493+ data.modifiers & KeyboardModifier::Super
494+ );
495+
496+ if (!ime->hasDelegate () || keyCode == KEY_Escape || keyCode == KEY_Enter) {
497+ keyboardDispatcher->dispatchKeyboardMSG (
498+ keyCode,
499+ isDown,
500+ data.action == Repeat,
501+ data.timestamp
502+ );
503+ }
504+
505+ // text pasting
506+ if (data.modifiers & KeyboardModifier::Control && keyCode == enumKeyCodes::KEY_V && isDown) {
507+ if (ime->hasDelegate ()) {
508+ this ->performSafeClipboardPaste ();
509+ }
510+ }
511+ }
512+ }
513+
514+ void handleMouseEvent (RawInputEvent const & evt) {
515+ using enum MouseInputData::Action;
516+ using enum MouseInputData::Button;
517+
518+ struct Btn {
519+ USHORT down, up;
520+ MouseInputData::Button btn;
521+ };
522+
523+ constexpr Btn btns[] = {
524+ {RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, Left},
525+ {RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP, Right},
526+ {RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, Middle},
527+ {RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, Button4},
528+ {RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP, Button5},
529+ };
530+
531+ // WinAPI can combine multiple button events into one
532+ for (auto const & b : btns) {
533+ bool isDown = (evt.mouse .flags & b.down ) != 0 ;
534+ bool isUp = (evt.mouse .flags & b.up ) != 0 ;
535+ if (isDown || isUp) {
536+ MouseInputData data (
537+ b.btn ,
538+ isDown ? Press : Release,
539+ evt.timestamp ,
540+ evt.mods
541+ );
542+
543+ auto result = MouseInputEvent ().send (data);
544+ isDown = data.action == Press;
545+
546+ // handle cocos touches
547+ if (data.button == Left && result == ListenerResult::Propagate) {
548+ int id = 0 ;
549+ if (isDown) {
550+ m_bCaptured = true ;
551+ this ->handleTouchesBegin (
552+ 1 , &id,
553+ &m_fMouseX,
554+ &m_fMouseY,
555+ data.timestamp
556+ );
557+ } else {
558+ m_bCaptured = false ;
559+ this ->handleTouchesEnd (
560+ 1 , &id,
561+ &m_fMouseX,
562+ &m_fMouseY,
563+ data.timestamp
564+ );
565+ }
566+ }
567+ }
568+ }
569+ }
570+
419571 void pumpRawInput () {
420572 bool isForeground = GetForegroundWindow () == g_mainWindowHWND;
421573 if (!isForeground) {
@@ -455,118 +607,11 @@ struct GeodeRawInput : Modify<GeodeRawInput, CCEGLView> {
455607 switch (evt.type ) {
456608 case RawInputEvent::Type::KeyDown:
457609 case RawInputEvent::Type::KeyUp: {
458- using enum KeyboardInputData::Action;
459- bool isDown = evt.type == RawInputEvent::Type::KeyDown;
460-
461- enumKeyCodes keyCode = keyToKeyCode (
462- evt.keyboard .vkey ,
463- evt.keyboard .isE0
464- );
465-
466- KeyboardInputData data (
467- keyCode,
468- isDown ? (evt.keyboard .isRepeat ? Repeat : Press) : Release,
469- {evt.keyboard .vkey , evt.keyboard .scanCode },
470- evt.timestamp ,
471- evt.mods
472- );
473-
474- auto result = KeyboardInputEvent (keyCode).send (data);
475-
476- // copy values from event, if someone modifies it
477- isDown = data.action != Release;
478- keyCode = data.key ;
479-
480- if (result == ListenerResult::Propagate && keyCode != KEY_Unknown) {
481- auto * ime = CCIMEDispatcher::sharedDispatcher ();
482- if (keyCode == enumKeyCodes::KEY_Backspace && isDown) {
483- ime->dispatchDeleteBackward ();
484- } else if (keyCode == enumKeyCodes::KEY_Delete && isDown) {
485- ime->dispatchDeleteForward ();
486- }
487-
488- auto * keyboardDispatcher = CCKeyboardDispatcher::get ();
489-
490- keyboardDispatcher->updateModifierKeys (
491- data.modifiers & KeyboardModifier::Shift,
492- data.modifiers & KeyboardModifier::Control,
493- data.modifiers & KeyboardModifier::Alt,
494- data.modifiers & KeyboardModifier::Super
495- );
496-
497- if (!ime->hasDelegate () || keyCode == KEY_Escape || keyCode == KEY_Enter) {
498- keyboardDispatcher->dispatchKeyboardMSG (
499- keyCode,
500- isDown,
501- data.action == Repeat,
502- data.timestamp
503- );
504- }
505-
506- // text pasting
507- if (data.modifiers & KeyboardModifier::Control && keyCode == enumKeyCodes::KEY_V && isDown) {
508- if (ime->hasDelegate ()) {
509- this ->performSafeClipboardPaste ();
510- }
511- }
512- }
610+ this ->handleKeyboardEvent (evt);
513611 break ;
514612 }
515613 case RawInputEvent::Type::MouseButton: {
516- using enum MouseInputData::Action;
517- using enum MouseInputData::Button;
518-
519- struct Btn {
520- USHORT down, up;
521- MouseInputData::Button btn;
522- };
523-
524- constexpr Btn btns[] = {
525- {RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, Left},
526- {RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP, Right},
527- {RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, Middle},
528- {RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, Button4},
529- {RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP, Button5},
530- };
531-
532- // WinAPI can combine multiple button events into one
533- for (auto const & b : btns) {
534- bool isDown = (evt.mouse .flags & b.down ) != 0 ;
535- bool isUp = (evt.mouse .flags & b.up ) != 0 ;
536- if (isDown || isUp) {
537- MouseInputData data (
538- b.btn ,
539- isDown ? Press : Release,
540- evt.timestamp ,
541- evt.mods
542- );
543-
544- auto result = MouseInputEvent ().send (data);
545- isDown = data.action == Press;
546-
547- // handle cocos touches
548- if (data.button == Left && result == ListenerResult::Propagate) {
549- int id = 0 ;
550- if (isDown) {
551- m_bCaptured = true ;
552- this ->handleTouchesBegin (
553- 1 , &id,
554- &m_fMouseX,
555- &m_fMouseY,
556- data.timestamp
557- );
558- } else {
559- m_bCaptured = false ;
560- this ->handleTouchesEnd (
561- 1 , &id,
562- &m_fMouseX,
563- &m_fMouseY,
564- data.timestamp
565- );
566- }
567- }
568- }
569- }
614+ this ->handleMouseEvent (evt);
570615 break ;
571616 }
572617 default :
@@ -609,12 +654,42 @@ struct GeodeRawInput : Modify<GeodeRawInput, CCEGLView> {
609654 g_mainWindowHWND = *reinterpret_cast <HWND*>(reinterpret_cast <uintptr_t >(m_pMainWindow) + 0x370 );
610655 *reinterpret_cast <GLFWscrollfun*>(reinterpret_cast <uintptr_t >(m_pMainWindow) + 0x340 ) = &GLFWScrollCallback;
611656 *reinterpret_cast <GLFWcharfun*>(reinterpret_cast <uintptr_t >(m_pMainWindow) + 0x350 ) = &GLFWCharCallback;
657+ *reinterpret_cast <GLFWwindowfocusfun*>(reinterpret_cast <uintptr_t >(m_pMainWindow) + 0x300 ) = &GLFWFocusCallback;
612658
613659 // window is created on a different thread, so it needs time to initialize
614660 queueInMainThread ([]{ attemptHookRawInput (); });
615661 }
616662};
617663
664+ static void GLFWFocusCallback (GLFWwindow* window, int focused) {
665+ auto view = CCEGLView::get ();
666+ static_cast <DummyEGLView*>(view)->onGLFWWindowFocus (window, focused);
667+ if (!focused) {
668+ // technically a race condition, but you have to be the most unlucky person alive for it to happen,
669+ // and i really didn't want to add mutexes to KeyStateTracker just for window unfocus event
670+ auto & tracker = KeyStateTracker::get ();
671+ auto pressedKeys = tracker.getPressedKeys ();
672+ tracker.clear ();
673+
674+ auto timestamp = getInputTimestamp ();
675+ for (auto const & keyInfo : pressedKeys) {
676+ static_cast <GeodeRawInput*>(view)->handleKeyboardEvent (RawInputEvent{
677+ .timestamp = timestamp,
678+ .mods = KeyboardModifier::None,
679+ .keyboard = {
680+ .vkey = keyInfo.vkey ,
681+ .scanCode = keyInfo.scanCode ,
682+ .flags = 0 ,
683+ .isE0 = keyInfo.isE0 ,
684+ .isE1 = false ,
685+ .isRepeat = false
686+ },
687+ .type = RawInputEvent::Type::KeyUp,
688+ });
689+ }
690+ }
691+ }
692+
618693struct GeodeControllerInput : Modify<GeodeControllerInput, CCApplication> {
619694 void updateControllerKeys (CXBOXController* controller, int userIndex) {
620695 if (!controller) return ;
0 commit comments