From 79ada6f880be9dee17dc0f5dd78401eb4c04ed27 Mon Sep 17 00:00:00 2001 From: kingminer7 <100500023+Kingminer7@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:00:32 -0500 Subject: [PATCH 1/2] iOS Implementation, add CCIMEDispatcher::Impl to header for ios inlined CCIMEDispatcher::hasDelegate --- .../cocos/text_input_node/CCIMEDispatcher.h | 7 +- loader/src/platform/ios/input.mm | 306 ++++++++++++++++++ 2 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 loader/src/platform/ios/input.mm diff --git a/loader/include/Geode/cocos/text_input_node/CCIMEDispatcher.h b/loader/include/Geode/cocos/text_input_node/CCIMEDispatcher.h index 00ace287c..387c6789e 100644 --- a/loader/include/Geode/cocos/text_input_node/CCIMEDispatcher.h +++ b/loader/include/Geode/cocos/text_input_node/CCIMEDispatcher.h @@ -26,6 +26,7 @@ THE SOFTWARE. #define __CC_IME_DISPATCHER_H__ #include "CCIMEDelegate.h" +#include NS_CC_BEGIN @@ -108,7 +109,11 @@ class CC_DLL CCIMEDispatcher private: CCIMEDispatcher(); - class Impl; + class Impl { + public: + std::list m_DelegateList; + CCIMEDelegate* m_DelegateWithIme; + }; Impl * m_pImpl; }; diff --git a/loader/src/platform/ios/input.mm b/loader/src/platform/ios/input.mm new file mode 100644 index 000000000..e19d977ea --- /dev/null +++ b/loader/src/platform/ios/input.mm @@ -0,0 +1,306 @@ +#include "Geode/loader/Log.hpp" +#include +#include +#include +#include +#include +#include +#include + +using namespace geode::prelude; + +static enumKeyCodes keyToKeyCode(int code) { + switch (code) { + case UIKeyboardHIDUsageKeyboardA: return enumKeyCodes::KEY_A; + case UIKeyboardHIDUsageKeyboardB: return enumKeyCodes::KEY_B; + case UIKeyboardHIDUsageKeyboardC: return enumKeyCodes::KEY_C; + case UIKeyboardHIDUsageKeyboardD: return enumKeyCodes::KEY_D; + case UIKeyboardHIDUsageKeyboardE: return enumKeyCodes::KEY_E; + case UIKeyboardHIDUsageKeyboardF: return enumKeyCodes::KEY_F; + case UIKeyboardHIDUsageKeyboardG: return enumKeyCodes::KEY_G; + case UIKeyboardHIDUsageKeyboardH: return enumKeyCodes::KEY_H; + case UIKeyboardHIDUsageKeyboardI: return enumKeyCodes::KEY_I; + case UIKeyboardHIDUsageKeyboardJ: return enumKeyCodes::KEY_J; + case UIKeyboardHIDUsageKeyboardK: return enumKeyCodes::KEY_K; + case UIKeyboardHIDUsageKeyboardL: return enumKeyCodes::KEY_L; + case UIKeyboardHIDUsageKeyboardM: return enumKeyCodes::KEY_M; + case UIKeyboardHIDUsageKeyboardN: return enumKeyCodes::KEY_N; + case UIKeyboardHIDUsageKeyboardO: return enumKeyCodes::KEY_O; + case UIKeyboardHIDUsageKeyboardP: return enumKeyCodes::KEY_P; + case UIKeyboardHIDUsageKeyboardQ: return enumKeyCodes::KEY_Q; + case UIKeyboardHIDUsageKeyboardR: return enumKeyCodes::KEY_R; + case UIKeyboardHIDUsageKeyboardS: return enumKeyCodes::KEY_S; + case UIKeyboardHIDUsageKeyboardT: return enumKeyCodes::KEY_T; + case UIKeyboardHIDUsageKeyboardU: return enumKeyCodes::KEY_U; + case UIKeyboardHIDUsageKeyboardV: return enumKeyCodes::KEY_V; + case UIKeyboardHIDUsageKeyboardW: return enumKeyCodes::KEY_W; + case UIKeyboardHIDUsageKeyboardX: return enumKeyCodes::KEY_X; + case UIKeyboardHIDUsageKeyboardY: return enumKeyCodes::KEY_Y; + case UIKeyboardHIDUsageKeyboardZ: return enumKeyCodes::KEY_Z; + + case UIKeyboardHIDUsageKeyboard0: return enumKeyCodes::KEY_Zero; + case UIKeyboardHIDUsageKeyboard1: return enumKeyCodes::KEY_One; + case UIKeyboardHIDUsageKeyboard2: return enumKeyCodes::KEY_Two; + case UIKeyboardHIDUsageKeyboard3: return enumKeyCodes::KEY_Three; + case UIKeyboardHIDUsageKeyboard4: return enumKeyCodes::KEY_Four; + case UIKeyboardHIDUsageKeyboard5: return enumKeyCodes::KEY_Five; + case UIKeyboardHIDUsageKeyboard6: return enumKeyCodes::KEY_Six; + case UIKeyboardHIDUsageKeyboard7: return enumKeyCodes::KEY_Seven; + case UIKeyboardHIDUsageKeyboard8: return enumKeyCodes::KEY_Eight; + case UIKeyboardHIDUsageKeyboard9: return enumKeyCodes::KEY_Nine; + + case UIKeyboardHIDUsageKeyboardF1: return enumKeyCodes::KEY_F1; + case UIKeyboardHIDUsageKeyboardF2: return enumKeyCodes::KEY_F2; + case UIKeyboardHIDUsageKeyboardF3: return enumKeyCodes::KEY_F3; + case UIKeyboardHIDUsageKeyboardF4: return enumKeyCodes::KEY_F4; + case UIKeyboardHIDUsageKeyboardF5: return enumKeyCodes::KEY_F5; + case UIKeyboardHIDUsageKeyboardF6: return enumKeyCodes::KEY_F6; + case UIKeyboardHIDUsageKeyboardF7: return enumKeyCodes::KEY_F7; + case UIKeyboardHIDUsageKeyboardF8: return enumKeyCodes::KEY_F8; + case UIKeyboardHIDUsageKeyboardF9: return enumKeyCodes::KEY_F9; + case UIKeyboardHIDUsageKeyboardF10: return enumKeyCodes::KEY_F10; + case UIKeyboardHIDUsageKeyboardF11: return enumKeyCodes::KEY_F11; + case UIKeyboardHIDUsageKeyboardF12: return enumKeyCodes::KEY_F12; + case UIKeyboardHIDUsageKeyboardF13: return enumKeyCodes::KEY_F13; + case UIKeyboardHIDUsageKeyboardF14: return enumKeyCodes::KEY_F14; + case UIKeyboardHIDUsageKeyboardF15: return enumKeyCodes::KEY_F15; + case UIKeyboardHIDUsageKeyboardF16: return enumKeyCodes::KEY_F16; + case UIKeyboardHIDUsageKeyboardF17: return enumKeyCodes::KEY_F17; + case UIKeyboardHIDUsageKeyboardF18: return enumKeyCodes::KEY_F18; + case UIKeyboardHIDUsageKeyboardF19: return enumKeyCodes::KEY_F19; + case UIKeyboardHIDUsageKeyboardF20: return enumKeyCodes::KEY_F20; + case UIKeyboardHIDUsageKeyboardF21: return enumKeyCodes::KEY_F21; + case UIKeyboardHIDUsageKeyboardF22: return enumKeyCodes::KEY_F22; + case UIKeyboardHIDUsageKeyboardF23: return enumKeyCodes::KEY_F23; + case UIKeyboardHIDUsageKeyboardF24: return enumKeyCodes::KEY_F24; + + case UIKeyboardHIDUsageKeyboardEscape: return enumKeyCodes::KEY_Escape; + case UIKeyboardHIDUsageKeyboardTab: return enumKeyCodes::KEY_Tab; + case UIKeyboardHIDUsageKeyboardSpacebar: return enumKeyCodes::KEY_Space; + case UIKeyboardHIDUsageKeyboardDeleteOrBackspace: return enumKeyCodes::KEY_Backspace; + case UIKeyboardHIDUsageKeyboardInsert: return enumKeyCodes::KEY_Insert; + case UIKeyboardHIDUsageKeyboardDeleteForward: return enumKeyCodes::KEY_Delete; + case UIKeyboardHIDUsageKeyboardHome: return enumKeyCodes::KEY_Home; + case UIKeyboardHIDUsageKeyboardEnd: return enumKeyCodes::KEY_End; + case UIKeyboardHIDUsageKeyboardPageUp: return enumKeyCodes::KEY_PageUp; + case UIKeyboardHIDUsageKeyboardPageDown: return enumKeyCodes::KEY_PageDown; + + case UIKeyboardHIDUsageKeyboardUpArrow: return enumKeyCodes::KEY_Up; + case UIKeyboardHIDUsageKeyboardDownArrow: return enumKeyCodes::KEY_Down; + case UIKeyboardHIDUsageKeyboardLeftArrow: return enumKeyCodes::KEY_Left; + case UIKeyboardHIDUsageKeyboardRightArrow: return enumKeyCodes::KEY_Right; + + case UIKeyboardHIDUsageKeyboardCapsLock: return enumKeyCodes::KEY_CapsLock; + case UIKeyboardHIDUsageKeyboardScrollLock: return enumKeyCodes::KEY_ScrollLock; + case UIKeyboardHIDUsageKeypadNumLock: return enumKeyCodes::KEY_Numlock; + case UIKeyboardHIDUsageKeyboardPrintScreen: return enumKeyCodes::KEY_PrintScreen; + case UIKeyboardHIDUsageKeyboardPause: return enumKeyCodes::KEY_Pause; + + case UIKeyboardHIDUsageKeyboardLeftShift: return enumKeyCodes::KEY_LeftShift; + case UIKeyboardHIDUsageKeyboardRightShift: return enumKeyCodes::KEY_RightShift; + case UIKeyboardHIDUsageKeyboardLeftControl: return enumKeyCodes::KEY_LeftControl; + case UIKeyboardHIDUsageKeyboardRightControl: return enumKeyCodes::KEY_RightControl; + case UIKeyboardHIDUsageKeyboardLeftAlt: return enumKeyCodes::KEY_LeftMenu; + case UIKeyboardHIDUsageKeyboardRightAlt: return enumKeyCodes::KEY_RightMenu; + case UIKeyboardHIDUsageKeyboardReturnOrEnter: return enumKeyCodes::KEY_Enter; + case UIKeyboardHIDUsageKeyboardReturn: return enumKeyCodes::KEY_Enter; + + case UIKeyboardHIDUsageKeyboardLeftGUI: return enumKeyCodes::KEY_LeftWindowsKey; + case UIKeyboardHIDUsageKeyboardRightGUI: return enumKeyCodes::KEY_RightWindowsKey; + case UIKeyboardHIDUsageKeyboardMenu: return enumKeyCodes::KEY_ApplicationsKey; + + case UIKeyboardHIDUsageKeyboardHyphen: return enumKeyCodes::KEY_OEMMinus; + case UIKeyboardHIDUsageKeyboardComma: return enumKeyCodes::KEY_OEMComma; + case UIKeyboardHIDUsageKeyboardPeriod: return enumKeyCodes::KEY_OEMPeriod; + case UIKeyboardHIDUsageKeyboardSemicolon: return enumKeyCodes::KEY_Semicolon; + case UIKeyboardHIDUsageKeyboardQuote: return enumKeyCodes::KEY_Apostrophe; + case UIKeyboardHIDUsageKeyboardSlash: return enumKeyCodes::KEY_Slash; + case UIKeyboardHIDUsageKeyboardEqualSign: return enumKeyCodes::KEY_Equal; + case UIKeyboardHIDUsageKeyboardOpenBracket: return enumKeyCodes::KEY_LeftBracket; + case UIKeyboardHIDUsageKeyboardBackslash: return enumKeyCodes::KEY_Backslash; + case UIKeyboardHIDUsageKeyboardCloseBracket: return enumKeyCodes::KEY_RightBracket; + case UIKeyboardHIDUsageKeyboardGraveAccentAndTilde: return enumKeyCodes::KEY_GraveAccent; + case UIKeyboardHIDUsageKeyboardClear: return enumKeyCodes::KEY_Clear; + case UIKeyboardHIDUsageKeyboardNonUSBackslash: return enumKeyCodes::KEY_World1; + case UIKeyboardHIDUsageKeyboardNonUSPound: return enumKeyCodes::KEY_World2; + + case UIKeyboardHIDUsageKeypad0: return enumKeyCodes::KEY_NumPad0; + case UIKeyboardHIDUsageKeypad1: return enumKeyCodes::KEY_NumPad1; + case UIKeyboardHIDUsageKeypad2: return enumKeyCodes::KEY_NumPad2; + case UIKeyboardHIDUsageKeypad3: return enumKeyCodes::KEY_NumPad3; + case UIKeyboardHIDUsageKeypad4: return enumKeyCodes::KEY_NumPad4; + case UIKeyboardHIDUsageKeypad5: return enumKeyCodes::KEY_NumPad5; + case UIKeyboardHIDUsageKeypad6: return enumKeyCodes::KEY_NumPad6; + case UIKeyboardHIDUsageKeypad7: return enumKeyCodes::KEY_NumPad7; + case UIKeyboardHIDUsageKeypad8: return enumKeyCodes::KEY_NumPad8; + case UIKeyboardHIDUsageKeypad9: return enumKeyCodes::KEY_NumPad9; + case UIKeyboardHIDUsageKeypadPeriod: return enumKeyCodes::KEY_Decimal; + case UIKeyboardHIDUsageKeypadSlash: return enumKeyCodes::KEY_Divide; + case UIKeyboardHIDUsageKeypadAsterisk: return enumKeyCodes::KEY_Multiply; + case UIKeyboardHIDUsageKeypadHyphen: return enumKeyCodes::KEY_Subtract; + case UIKeyboardHIDUsageKeypadPlus: return enumKeyCodes::KEY_Add; + case UIKeyboardHIDUsageKeypadEnter: return enumKeyCodes::KEY_NumEnter; + case UIKeyboardHIDUsageKeypadEqualSign: return enumKeyCodes::KEY_OEMEqual; + + default: return enumKeyCodes::KEY_Unknown; + } +} + +@implementation UIWindow (KeyInterceptor) + +- (void)handleKeyDown:(UIEvent*) event withPress:(UIPress*) press withTime:(double) timestamp withRepeat:(bool) isRepeat { + UIKey* key = [press key]; + + unichar nativeCode = [key keyCode]; + enumKeyCodes keyCode = keyToKeyCode(nativeCode); + CCIMEDispatcher* imeDispatcher = CCIMEDispatcher::sharedDispatcher(); + CCKeyboardDispatcher* keyboardDispatcher = CCKeyboardDispatcher::get(); + bool isDown = true; + UIKeyModifierFlags flags = [event modifierFlags]; + + KeyboardInputData::Modifiers modifiers = KeyboardInputData::Mods_None; + if ((flags & UIKeyModifierShift) != 0) { + modifiers |= KeyboardInputData::Mods_Shift; + } + if ((flags & UIKeyModifierControl) != 0) { + modifiers |= KeyboardInputData::Mods_Control; + } + if ((flags & UIKeyModifierAlternate) != 0) { + modifiers |= KeyboardInputData::Mods_Alt; + } + if ((flags & UIKeyModifierCommand) != 0) { + modifiers |= KeyboardInputData::Mods_Super; + } + + KeyboardInputData data( + keyCode, isRepeat ? KeyboardInputData::Action::Repeat : KeyboardInputData::Action::Press, + { nativeCode }, + timestamp, + modifiers + ); + + auto res = KeyboardInputEvent(keyCode).send(data); + + isDown = data.action != KeyboardInputData::Action::Release; + isRepeat = data.action == KeyboardInputData::Action::Repeat; + keyCode = data.key; + modifiers = data.modifiers; + + // TODO: Find CCIMEDispatcher::sharedDispatcher and reimplement CCIMEDispatcher::hasDelegate + if (keyCode != KEY_Unknown && (!imeDispatcher->hasDelegate() || keyCode == KEY_Escape || keyCode == KEY_Enter)) { + keyboardDispatcher->dispatchKeyboardMSG(keyCode, isDown, isRepeat, timestamp); + } + + keyboardDispatcher->updateModifierKeys( + modifiers & KeyboardInputData::Mods_Shift, + modifiers & KeyboardInputData::Mods_Control, + modifiers & KeyboardInputData::Mods_Alt, + modifiers & KeyboardInputData::Mods_Super + ); +} + +- (void)handleKeyUp:(UIEvent*) event withPress:(UIPress*) press { + UIKey* key = [press key]; + + unichar nativeCode = [key keyCode]; + enumKeyCodes keyCode = keyToKeyCode(nativeCode); + CCIMEDispatcher* imeDispatcher = CCIMEDispatcher::sharedDispatcher(); + CCKeyboardDispatcher* keyboardDispatcher = CCKeyboardDispatcher::get(); + double timestamp = [event timestamp]; + bool isRepeat = false; + bool isDown = false; + UIKeyModifierFlags flags = [event modifierFlags]; + + KeyboardInputData::Modifiers modifiers = KeyboardInputData::Mods_None; + if ((flags & UIKeyModifierShift) != 0) { + modifiers |= KeyboardInputData::Mods_Shift; + } + if ((flags & UIKeyModifierControl) != 0) { + modifiers |= KeyboardInputData::Mods_Control; + } + if ((flags & UIKeyModifierAlternate) != 0) { + modifiers |= KeyboardInputData::Mods_Alt; + } + if ((flags & UIKeyModifierCommand) != 0) { + modifiers |= KeyboardInputData::Mods_Super; + } + + KeyboardInputData data( + keyCode, KeyboardInputData::Action::Release, + { nativeCode }, + timestamp, + modifiers + ); + + auto res = KeyboardInputEvent(keyCode).send(data); + + isDown = data.action != KeyboardInputData::Action::Release; + isRepeat = data.action == KeyboardInputData::Action::Repeat; + keyCode = data.key; + modifiers = data.modifiers; + if (keyCode != KEY_Unknown && (!imeDispatcher->hasDelegate() || keyCode == KEY_Escape || keyCode == KEY_Enter)) { + keyboardDispatcher->dispatchKeyboardMSG(keyCode, isDown, isRepeat, timestamp); + } + + keyboardDispatcher->updateModifierKeys( + modifiers & KeyboardInputData::Mods_Shift, + modifiers & KeyboardInputData::Mods_Control, + modifiers & KeyboardInputData::Mods_Alt, + modifiers & KeyboardInputData::Mods_Super + ); +} + +static NSTimer* delayTimer = nil; +static NSTimer* repeatTimer = nil; +static UIEvent* repeatEvent = nil; +static UIPress* repeatPress = nil; + +// iOS doesn't have built in repeat, so we handle it ourselves +- (void)startRepeat:(UIEvent*)event withPress:(UIPress*)press { + [self stopRepeat]; + repeatEvent = event; + repeatPress = press; + delayTimer = [NSTimer scheduledTimerWithTimeInterval:.5 repeats:NO block:^(NSTimer * _Nonnull t) { + delayTimer = nil; + repeatTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 repeats:YES block:^(NSTimer * _Nonnull t2) { + [self handleKeyDown:repeatEvent withPress:repeatPress withTime:[[NSProcessInfo processInfo] systemUptime] withRepeat:true]; + }]; + }]; +} + +- (void)stopRepeat { + if (delayTimer) { [delayTimer invalidate]; delayTimer = nil; } + if (repeatTimer) { [repeatTimer invalidate]; repeatTimer = nil; } + repeatPress = nil; + repeatEvent = nil; +} + +- (void)g_sendEvent:(UIEvent*)event { + if (event.type == UIEventTypePresses || event.type == 4) { // it's sending as 4 for me, even though it should be sending as 3 (which is UIEventTypePresses) + UIPressesEvent* pe = (UIPressesEvent*)event; + for (UIPress* press in pe.allPresses) { + switch(press.phase) { + case UIPressPhaseBegan: { + [self handleKeyDown:event withPress:press withTime:[event timestamp] withRepeat:false]; + [self startRepeat:event withPress:press]; + break; + } + case UIPressPhaseEnded: { + [self handleKeyUp:event withPress:press]; + [self stopRepeat]; + break; + } + default: break; + } + } + } + [self g_sendEvent:event]; +} + +$execute { + Method m1 = class_getInstanceMethod(UIWindow.class, @selector(sendEvent:)); + Method m2 = class_getInstanceMethod(UIWindow.class, @selector(g_sendEvent:)); + method_exchangeImplementations(m1, m2); +} + +@end \ No newline at end of file From f8dfde9bc9dcf21bc5ae10246aac7d89df05befc Mon Sep 17 00:00:00 2001 From: kingminer7 <100500023+Kingminer7@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:14:06 -0500 Subject: [PATCH 2/2] add scrolling --- loader/src/platform/ios/input.mm | 36 +++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/loader/src/platform/ios/input.mm b/loader/src/platform/ios/input.mm index e19d977ea..120f3dc38 100644 --- a/loader/src/platform/ios/input.mm +++ b/loader/src/platform/ios/input.mm @@ -1,9 +1,12 @@ #include "Geode/loader/Log.hpp" #include +#include #include #include +#include #include #include +#include #include #include @@ -275,11 +278,14 @@ - (void)stopRepeat { repeatEvent = nil; } +typedef CGVector(*ScrollDeltaFunc)(id, SEL); +constexpr auto g_scrollFactor = 13.0f; + - (void)g_sendEvent:(UIEvent*)event { - if (event.type == UIEventTypePresses || event.type == 4) { // it's sending as 4 for me, even though it should be sending as 3 (which is UIEventTypePresses) + if ([event type] == UIEventTypePresses || [event type] == 4) { // it's sending as 4 for me, even though it should be sending as 3 (which is UIEventTypePresses) UIPressesEvent* pe = (UIPressesEvent*)event; - for (UIPress* press in pe.allPresses) { - switch(press.phase) { + for (UIPress* press in [pe allPresses]) { + switch([press phase]) { case UIPressPhaseBegan: { [self handleKeyDown:event withPress:press withTime:[event timestamp] withRepeat:false]; [self startRepeat:event withPress:press]; @@ -293,13 +299,33 @@ - (void)g_sendEvent:(UIEvent*)event { default: break; } } + } else if ([event type] == UIEventTypeScroll) { + // UIScrollEvent is a private class, we love apple's private APIs + Class scrollClass = NSClassFromString(@"UIScrollEvent"); + if (scrollClass && [event isKindOfClass:scrollClass]) { + SEL selector = NSSelectorFromString(@"nonAcceleratedDelta"); + if (![event respondsToSelector:selector]) { + return; + } + IMP imp = [event methodForSelector:selector]; + ScrollDeltaFunc getDelta = (ScrollDeltaFunc)imp; + CGVector delta = getDelta(event, selector); + + if (ScrollWheelEvent().send(delta.dx, delta.dy) == ListenerResult::Stop) { + return; + } + + CCDirector::get()->getMouseDispatcher()->dispatchScrollMSG( + delta.dx * g_scrollFactor, delta.dy * g_scrollFactor + ); + } } [self g_sendEvent:event]; } $execute { - Method m1 = class_getInstanceMethod(UIWindow.class, @selector(sendEvent:)); - Method m2 = class_getInstanceMethod(UIWindow.class, @selector(g_sendEvent:)); + Method m1 = class_getInstanceMethod([UIWindow class], @selector(sendEvent:)); + Method m2 = class_getInstanceMethod([UIWindow class], @selector(g_sendEvent:)); method_exchangeImplementations(m1, m2); }