Add support for missing keys and mouse buttons, rewrite numpad input ()

* add missing key codes and mouse buttons to enumKeyCodes

* rewrite numpad key detection

* add some INTL keys

* ifdef the hook for desktop only

* MacOS support

* same-line brackets

* unindent the enum by one indent

* Manual Objective-C method hooks

* Android keys

* Incorrect enum field casing

* the pch..

* ios support is not real :(

* iOS support

---------

Co-authored-by: Fleeym <61891787+Fleeym@users.noreply.github.com>
This commit is contained in:
SpaghettDev 2025-03-11 15:16:33 -05:00 committed by GitHub
parent 96deea0ef8
commit ca2ece2aa8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 543 additions and 1 deletions
loader
CMakeLists.txt
include/Geode/cocos/robtop/keyboard_dispatcher
src

View file

@ -91,6 +91,8 @@ file(GLOB SOURCES CONFIGURE_DEPENDS
file(GLOB OBJC_SOURCES
src/platform/Objcpp.mm
src/load.mm
src/hooks/AddExtraKeys.mm
)
set_source_files_properties(${OBJC_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)

View file

@ -14,7 +14,6 @@ NS_CC_BEGIN
*/
typedef enum
{
// this one might not actually exist in gd itself
KEY_Unknown = -0x01,
KEY_None = 0x00,
KEY_Backspace = 0x08,
@ -198,6 +197,28 @@ typedef enum
CONTROLLER_RTHUMBSTICK_DOWN = 0x40F,
CONTROLLER_RTHUMBSTICK_LEFT = 0x411,
CONTROLLER_RTHUMBSTICK_RIGHT = 0x413,
// Geode additions
KEY_GraveAccent = 0x1000,
KEY_OEMEqual = 0x1001,
KEY_LeftBracket = 0x1002,
KEY_RightBracket = 0x1003,
KEY_Backslash = 0x1004,
KEY_Semicolon = 0x1005,
KEY_Apostrophe = 0x1006,
KEY_Slash = 0x1007,
KEY_Equal = 0x1008,
KEY_NumEnter = 0x1009,
// Keys used by some non-US keyboard layouts
KEY_World1 = 0x100A,
KEY_World2 = 0x100B,
// Mouse buttons (excluding clicks)
MOUSE_4 = 0x1100,
MOUSE_5 = 0x1101,
MOUSE_6 = 0x1102,
MOUSE_7 = 0x1103,
MOUSE_8 = 0x1104
} enumKeyCodes;
// @note RobTop Addition

View file

@ -0,0 +1,206 @@
#include <Geode/DefaultInclude.hpp>
#ifdef GEODE_IS_WINDOWS
#include <Geode/cocos/robtop/glfw/glfw3.h>
#include <Geode/cocos/robtop/keyboard_dispatcher/CCKeyboardDispatcher.h>
#include <Geode/cocos/robtop/keyboard_dispatcher/CCKeyboardDelegate.h>
#include <Geode/cocos/text_input_node/CCIMEDispatcher.h>
#include <Geode/modify/Modify.hpp>
#include <Geode/modify/CCEGLView.hpp>
#include <Geode/modify/CCKeyboardDispatcher.hpp>
using namespace geode::prelude;
class $modify(GeodeCCEGLView, CCEGLView) {
void onGLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
bool extraKey = isExtraKey(key);
bool numpad = isKeyNumpad(key);
if (!extraKey && !numpad) {
return CCEGLView::onGLFWKeyCallback(window, key, scancode, action, mods);
}
if (CCIMEDispatcher::sharedDispatcher()->hasDelegate()) {
return CCEGLView::onGLFWKeyCallback(window, key, scancode, action, mods);
}
bool down = action == 1 || action == 2;
bool repeat = action == 2;
enumKeyCodes keyCode = enumKeyCodes::KEY_Unknown;
if (extraKey) {
keyCode = this->extraKeyToKeyCode(key);
}
if (numpad) {
keyCode = this->numpadToKeyCode(key);
}
CCKeyboardDispatcher::get()->dispatchKeyboardMSG(keyCode, down, repeat);
}
void onGLFWMouseCallBack(GLFWwindow* window, int button, int action, int mods) {
if (!isExtraMouseButton(button)) {
return CCEGLView::onGLFWMouseCallBack(window, button, action, mods);
}
bool down = action == 1;
// mouse buttons never repeat
bool repeat = false;
enumKeyCodes keyCode = this->mouseButtonToKeyCode(button);
CCKeyboardDispatcher::get()->dispatchKeyboardMSG(keyCode, down, repeat);
}
bool isExtraMouseButton(int code) {
return code > GLFW_MOUSE_BUTTON_3;
}
enumKeyCodes mouseButtonToKeyCode(int button) {
switch (button) {
case GLFW_MOUSE_BUTTON_4:
return enumKeyCodes::MOUSE_4;
case GLFW_MOUSE_BUTTON_5:
return enumKeyCodes::MOUSE_5;
case GLFW_MOUSE_BUTTON_6:
return enumKeyCodes::MOUSE_6;
case GLFW_MOUSE_BUTTON_7:
return enumKeyCodes::MOUSE_7;
case GLFW_MOUSE_BUTTON_8:
return enumKeyCodes::MOUSE_8;
default:
return enumKeyCodes::KEY_Unknown;
}
}
bool isExtraKey(int code) {
switch (code) {
case GLFW_KEY_WORLD_1:
case GLFW_KEY_WORLD_2:
case GLFW_KEY_SEMICOLON:
case GLFW_KEY_APOSTROPHE:
case GLFW_KEY_SLASH:
case GLFW_KEY_EQUAL:
case GLFW_KEY_LEFT_BRACKET:
case GLFW_KEY_BACKSLASH:
case GLFW_KEY_RIGHT_BRACKET:
case GLFW_KEY_GRAVE_ACCENT:
return true;
default:
return false;
}
}
enumKeyCodes extraKeyToKeyCode(int key) {
switch (key) {
case GLFW_KEY_SEMICOLON:
return enumKeyCodes::KEY_Semicolon;
case GLFW_KEY_APOSTROPHE:
return enumKeyCodes::KEY_Apostrophe;
case GLFW_KEY_SLASH:
return enumKeyCodes::KEY_Slash;
case GLFW_KEY_EQUAL:
return enumKeyCodes::KEY_OEMEqual;
case GLFW_KEY_LEFT_BRACKET:
return enumKeyCodes::KEY_LeftBracket;
case GLFW_KEY_BACKSLASH:
return enumKeyCodes::KEY_Backslash;
case GLFW_KEY_RIGHT_BRACKET:
return enumKeyCodes::KEY_RightBracket;
case GLFW_KEY_GRAVE_ACCENT:
return enumKeyCodes::KEY_GraveAccent;
case GLFW_KEY_WORLD_1:
return enumKeyCodes::KEY_World1;
case GLFW_KEY_WORLD_2:
return enumKeyCodes::KEY_World2;
default:
return enumKeyCodes::KEY_Unknown;
}
}
bool isKeyNumpad(int code) {
return code >= GLFW_KEY_KP_0 && code <= GLFW_KEY_KP_EQUAL;
}
enumKeyCodes numpadToKeyCode(int key) {
switch (key) {
case GLFW_KEY_KP_0:
return enumKeyCodes::KEY_NumPad0;
case GLFW_KEY_KP_1:
return enumKeyCodes::KEY_NumPad1;
case GLFW_KEY_KP_2:
return enumKeyCodes::KEY_NumPad2;
case GLFW_KEY_KP_3:
return enumKeyCodes::KEY_NumPad3;
case GLFW_KEY_KP_4:
return enumKeyCodes::KEY_NumPad4;
case GLFW_KEY_KP_5:
return enumKeyCodes::KEY_NumPad5;
case GLFW_KEY_KP_6:
return enumKeyCodes::KEY_NumPad6;
case GLFW_KEY_KP_7:
return enumKeyCodes::KEY_NumPad7;
case GLFW_KEY_KP_8:
return enumKeyCodes::KEY_NumPad8;
case GLFW_KEY_KP_9:
return enumKeyCodes::KEY_NumPad9;
case GLFW_KEY_KP_DECIMAL:
return enumKeyCodes::KEY_Decimal;
case GLFW_KEY_KP_DIVIDE:
return enumKeyCodes::KEY_Divide;
case GLFW_KEY_KP_MULTIPLY:
return enumKeyCodes::KEY_Multiply;
case GLFW_KEY_KP_SUBTRACT:
return enumKeyCodes::KEY_Subtract;
case GLFW_KEY_KP_ADD:
return enumKeyCodes::KEY_Add;
case GLFW_KEY_KP_ENTER:
return enumKeyCodes::KEY_NumEnter;
case GLFW_KEY_KP_EQUAL:
return enumKeyCodes::KEY_Equal;
default:
return enumKeyCodes::KEY_Unknown;
}
}
};
class $modify(CCKeyboardDispatcher) {
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("CCKeyboardDispatcher new keys")
const char* keyToString(enumKeyCodes key) {
if (key < 0x1000) {
return CCKeyboardDispatcher::keyToString(key);
}
switch (key) {
case KEY_GraveAccent:
return "`";
case KEY_OEMEqual:
return "=";
case KEY_LeftBracket:
return "[";
case KEY_RightBracket:
return "]";
case KEY_Backslash:
return "\\";
case KEY_Semicolon:
return ";";
case KEY_Apostrophe:
return "'";
case KEY_Slash:
return "/";
case KEY_NumEnter:
return "NumEnter";
case KEY_World1:
return "INTL-1";
case KEY_World2:
return "INTL-2";
case MOUSE_4:
return "Mouse 4";
case MOUSE_5:
return "Mouse 5";
case MOUSE_6:
return "Mouse 6";
case MOUSE_7:
return "Mouse 7";
case MOUSE_8:
return "Mouse 8";
default:
return CCKeyboardDispatcher::keyToString(KEY_Unknown);
}
}
};
#endif

View file

@ -0,0 +1,286 @@
#include <Geode/DefaultInclude.hpp>
#import <Cocoa/Cocoa.h>
#include <objc/runtime.h>
#include <Carbon/Carbon.h>
#ifdef GEODE_IS_IOS
#include <AppKit/AppKit.h>
#endif
#include <Geode/cocos/robtop/keyboard_dispatcher/CCKeyboardDispatcher.h>
#include <Geode/cocos/robtop/keyboard_dispatcher/CCKeyboardDelegate.h>
#import <Geode/cocos/platform/mac/EAGLView.h>
#include <Geode/cocos/text_input_node/CCIMEDispatcher.h>
#include <Geode/modify/Modify.hpp>
#include <Geode/modify/CCKeyboardDispatcher.hpp>
using namespace geode::prelude;
// https://github.com/SpaghettDev/BetterInputs/blob/44f249cd94f4cc19fca4de570dfab28f4efa3db8/src/platform/macos.mm#L121
// we use this instead of [event keyCode] because the returned value of keyCode for letters is keyboard locale-specific
int normalizedCodeFromEvent(NSEvent* event) {
if ([[event characters] length] > 0) {
return [[event characters] characterAtIndex:0];
} else if ([[event charactersIgnoringModifiers] length] > 0) {
return [[event charactersIgnoringModifiers] characterAtIndex:0];
}
return 0;
}
enumKeyCodes mouseButtonToKeyCode(NSEvent* event) {
switch ([event buttonNumber]) {
case 2:
return enumKeyCodes::MOUSE_4;
case 3:
return enumKeyCodes::MOUSE_5;
case 4:
return enumKeyCodes::MOUSE_6;
case 5:
return enumKeyCodes::MOUSE_7;
case 6:
return enumKeyCodes::MOUSE_8;
default:
return enumKeyCodes::KEY_Unknown;
}
}
bool isExtraMouseButton(NSEvent* event) {
return [event buttonNumber] > 1;
}
enumKeyCodes extraKeyToKeyCode(NSEvent* event) {
switch (normalizedCodeFromEvent(event)) {
case ';':
return enumKeyCodes::KEY_Semicolon;
case '\'':
return enumKeyCodes::KEY_Apostrophe;
case '/':
return enumKeyCodes::KEY_Slash;
case '=':
return enumKeyCodes::KEY_OEMEqual;
case '[':
return enumKeyCodes::KEY_LeftBracket;
case '\\':
return enumKeyCodes::KEY_Backslash;
case ']':
return enumKeyCodes::KEY_RightBracket;
case '`':
return enumKeyCodes::KEY_GraveAccent;
default:
break;
}
switch ([event keyCode]) {
case kVK_ISO_Section:
return enumKeyCodes::KEY_World2;
default:
break;
}
return enumKeyCodes::KEY_Unknown;
}
bool isExtraKey(NSEvent* event) {
return extraKeyToKeyCode(event) != enumKeyCodes::KEY_Unknown;
}
enumKeyCodes numpadToKeyCode(NSEvent* event) {
switch (normalizedCodeFromEvent(event)) {
case '0':
return enumKeyCodes::KEY_NumPad0;
case '1':
return enumKeyCodes::KEY_NumPad1;
case '2':
return enumKeyCodes::KEY_NumPad2;
case '3':
return enumKeyCodes::KEY_NumPad3;
case '4':
return enumKeyCodes::KEY_NumPad4;
case '5':
return enumKeyCodes::KEY_NumPad5;
case '6':
return enumKeyCodes::KEY_NumPad6;
case '7':
return enumKeyCodes::KEY_NumPad7;
case '8':
return enumKeyCodes::KEY_NumPad8;
case '9':
return enumKeyCodes::KEY_NumPad9;
case '.':
return enumKeyCodes::KEY_Decimal;
case '/':
return enumKeyCodes::KEY_Divide;
case '*':
return enumKeyCodes::KEY_Multiply;
case '-':
return enumKeyCodes::KEY_Subtract;
case '+':
return enumKeyCodes::KEY_Add;
case 3:
return enumKeyCodes::KEY_NumEnter;
case '=':
return enumKeyCodes::KEY_Equal;
default:
return enumKeyCodes::KEY_Unknown;
}
}
bool isKeyNumpad(NSEvent* event) {
return ([event modifierFlags] & NSEventModifierFlagNumericPad) != 0;
}
void keyDownExecHook(EAGLView* self, SEL sel, NSEvent* event) {
bool extraKey = isExtraKey(event);
bool numpad = isKeyNumpad(event);
if (!extraKey && !numpad) {
GEODE_MACOS([self performSelector:sel withObject:event]);
return;
}
if (CCIMEDispatcher::sharedDispatcher()->hasDelegate()) {
GEODE_MACOS([self performSelector:sel withObject:event]);
return;
}
enumKeyCodes keyCode = enumKeyCodes::KEY_Unknown;
if (extraKey) {
keyCode = extraKeyToKeyCode(event);
}
if (numpad) {
keyCode = numpadToKeyCode(event);
}
CCKeyboardDispatcher::get()->dispatchKeyboardMSG(keyCode, true, [event isARepeat]);
}
void keyUpExecHook(EAGLView* self, SEL sel, NSEvent* event) {
bool extraKey = isExtraKey(event);
bool numpad = isKeyNumpad(event);
if (!extraKey && !numpad) {
GEODE_MACOS([self performSelector:sel withObject:event]);
return;
}
if (CCIMEDispatcher::sharedDispatcher()->hasDelegate()) {
GEODE_MACOS([self performSelector:sel withObject:event]);
return;
}
enumKeyCodes keyCode = enumKeyCodes::KEY_Unknown;
if (extraKey) {
keyCode = extraKeyToKeyCode(event);
}
if (numpad) {
keyCode = numpadToKeyCode(event);
}
CCKeyboardDispatcher::get()->dispatchKeyboardMSG(keyCode, false, [event isARepeat]);
}
void mouseDownExecHook(EAGLView* self, SEL sel, NSEvent* event) {
if (!isExtraMouseButton(event)) {
GEODE_MACOS([self performSelector:sel withObject:event]);
return;
}
enumKeyCodes keyCode = mouseButtonToKeyCode(event);
CCKeyboardDispatcher::get()->dispatchKeyboardMSG(keyCode, true, false);
}
void mouseUpExecHook(EAGLView* self, SEL sel, NSEvent* event) {
if (!isExtraMouseButton(event)) {
GEODE_MACOS([self performSelector:sel withObject:event]);
return;
}
enumKeyCodes keyCode = mouseButtonToKeyCode(event);
CCKeyboardDispatcher::get()->dispatchKeyboardMSG(keyCode, false, false);
}
#ifdef GEODE_IS_MACOS
class $modify(CCKeyboardDispatcher) {
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("CCKeyboardDispatcher new keys")
const char* keyToString(enumKeyCodes key) {
if (key < 0x1000) {
return CCKeyboardDispatcher::keyToString(key);
}
switch (key) {
case KEY_GraveAccent:
return "`";
case KEY_OEMEqual:
return "=";
case KEY_LeftBracket:
return "[";
case KEY_RightBracket:
return "]";
case KEY_Backslash:
return "\\";
case KEY_Semicolon:
return ";";
case KEY_Apostrophe:
return "'";
case KEY_Slash:
return "/";
case KEY_NumEnter:
return "NumEnter";
case KEY_World1:
return "INTL-1";
case KEY_World2:
return "INTL-2";
case MOUSE_4:
return "Mouse 4";
case MOUSE_5:
return "Mouse 5";
case MOUSE_6:
return "Mouse 6";
case MOUSE_7:
return "Mouse 7";
case MOUSE_8:
return "Mouse 8";
default:
return CCKeyboardDispatcher::keyToString(KEY_Unknown);
}
}
};
#endif
#define HOOK_OBJC_METHOD(klass, methodName) \
auto methodName##Addr = class_getInstanceMethod(klass, @selector(methodName:)); \
static_cast<void>(Mod::get()->hook(reinterpret_cast<void*>(method_getImplementation(methodName##Addr)), &methodName##Hook, #klass " " #methodName));
__attribute__((constructor)) void initialize_newKeyboardMSGKeysHooks() {
#if defined(GEODE_IS_MACOS)
auto eaglView = objc_getClass("EAGLView");
HOOK_OBJC_METHOD(eaglView, keyDownExec);
HOOK_OBJC_METHOD(eaglView, keyUpExec);
HOOK_OBJC_METHOD(eaglView, mouseDownExec);
HOOK_OBJC_METHOD(eaglView, mouseUpExec);
#elif defined(GEODE_IS_IOS)
@autoreleasepool
{
[NSEvent addGlobalMonitorForEventsMatchingMask:NSEventTypeKeyDown handler: ^(NSEvent* event) {
keyDownExecHook(nullptr, 0, event);
}];
[NSEvent addGlobalMonitorForEventsMatchingMask:NSEventTypeKeyUp handler: ^(NSEvent* event) {
keyUpExecHook(nullptr, 0, event);
}];
[NSEvent
addGlobalMonitorForEventsMatchingMask:NSEventTypeLeftMouseDown | NSEventTypeRightMouseDown | NSEventTypeOtherMouseDown
handler: ^(NSEvent* event) {
mouseDownExecHook(nullptr, 0, event);
}
];
[NSEvent
addGlobalMonitorForEventsMatchingMask:NSEventTypeLeftMouseUp | NSEventTypeRightMouseUp | NSEventTypeOtherMouseUp
handler: ^(NSEvent* event) {
mouseUpExecHook(nullptr, 0, event);
}
];
}
#endif
}

View file

@ -84,6 +84,33 @@ namespace {
{AKEYCODE_BUTTON_L1, cocos2d::CONTROLLER_LB},
{AKEYCODE_BUTTON_R2, cocos2d::CONTROLLER_RT},
{AKEYCODE_BUTTON_L2, cocos2d::CONTROLLER_LT},
// Geode Additions
{AKEYCODE_SEMICOLON, cocos2d::KEY_Semicolon},
{AKEYCODE_APOSTROPHE, cocos2d::KEY_Apostrophe},
{AKEYCODE_SLASH, cocos2d::KEY_Slash},
{AKEYCODE_EQUALS, cocos2d::KEY_OEMEqual},
{AKEYCODE_LEFT_BRACKET, cocos2d::KEY_LeftBracket},
{AKEYCODE_BACKSLASH, cocos2d::KEY_Backslash},
{AKEYCODE_RIGHT_BRACKET, cocos2d::KEY_RightBracket},
{AKEYCODE_GRAVE, cocos2d::KEY_GraveAccent},
{AKEYCODE_NUMPAD_0, cocos2d::KEY_NumPad0},
{AKEYCODE_NUMPAD_1, cocos2d::KEY_NumPad1},
{AKEYCODE_NUMPAD_2, cocos2d::KEY_NumPad2},
{AKEYCODE_NUMPAD_3, cocos2d::KEY_NumPad3},
{AKEYCODE_NUMPAD_4, cocos2d::KEY_NumPad4},
{AKEYCODE_NUMPAD_5, cocos2d::KEY_NumPad5},
{AKEYCODE_NUMPAD_6, cocos2d::KEY_NumPad6},
{AKEYCODE_NUMPAD_7, cocos2d::KEY_NumPad7},
{AKEYCODE_NUMPAD_8, cocos2d::KEY_NumPad8},
{AKEYCODE_NUMPAD_9, cocos2d::KEY_NumPad9},
{AKEYCODE_NUMPAD_DOT, cocos2d::KEY_Decimal},
{AKEYCODE_NUMPAD_DIVIDE, cocos2d::KEY_Divide},
{AKEYCODE_NUMPAD_MULTIPLY, cocos2d::KEY_Multiply},
{AKEYCODE_NUMPAD_SUBTRACT, cocos2d::KEY_Subtract},
{AKEYCODE_NUMPAD_ADD, cocos2d::KEY_Add},
{AKEYCODE_NUMPAD_ENTER, cocos2d::KEY_NumEnter},
{AKEYCODE_NUMPAD_EQUALS, cocos2d::KEY_Equal}
};
cocos2d::enumKeyCodes translateAndroidKeyCodeToWindows(int keyCode) {