Skip to content

Better keyboard support in inputs #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS" OR IOS)
set(CMAKE_OSX_ARCHITECTURES "arm64")
else()
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
set(CMAKE_OSX_ARCHITECTURES "x86_64")
endif()

project(DevTools VERSION 1.0.0)
Expand Down
85 changes: 85 additions & 0 deletions src/backend.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <cocos2d.h>
#include <Geode/modify/CCTouchDispatcher.hpp>
#include <Geode/modify/CCMouseDispatcher.hpp>
#include <Geode/modify/CCKeyboardDispatcher.hpp>
#include <Geode/modify/CCIMEDispatcher.hpp>
#include "platform/platform.hpp"
#include "DevTools.hpp"
Expand Down Expand Up @@ -356,3 +357,87 @@ class $modify(CCIMEDispatcher) {
io.AddKeyEvent(ImGuiKey_Backspace, false);
}
};

ImGuiKey cocosToImGuiKey(cocos2d::enumKeyCodes key) {
if (key >= KEY_A && key <= KEY_Z) {
return static_cast<ImGuiKey>(ImGuiKey_A + (key - KEY_A));
}
if (key >= KEY_Zero && key <= KEY_Nine) {
return static_cast<ImGuiKey>(ImGuiKey_0 + (key - KEY_Zero));
}
switch (key) {
case KEY_Up: return ImGuiKey_UpArrow;
case KEY_Down: return ImGuiKey_DownArrow;
case KEY_Left: return ImGuiKey_LeftArrow;
case KEY_Right: return ImGuiKey_RightArrow;

case KEY_Control: return ImGuiKey_ModCtrl;
case KEY_LeftWindowsKey: return ImGuiKey_ModSuper;
case KEY_Shift: return ImGuiKey_ModShift;
case KEY_Alt: return ImGuiKey_ModAlt;
case KEY_Enter: return ImGuiKey_Enter;

case KEY_Home: return ImGuiKey_Home;
case KEY_End: return ImGuiKey_End;
// macos uses delete instead of backspace for some reason
#ifndef GEODE_IS_MACOS
case KEY_Delete: return ImGuiKey_Delete;
#endif
case KEY_Escape: return ImGuiKey_Escape;

// KEY_Control and KEY_Shift aren't called on android like windows or mac
#ifdef GEODE_IS_ANDROID
case KEY_LeftControl: return ImGuiKey_ModCtrl;
case KEY_RightContol: return ImGuiKey_ModCtrl;
case KEY_LeftShift: return ImGuiKey_ModShift;
case KEY_RightShift: return ImGuiKey_ModShift;
#endif

default: return ImGuiKey_None;
}
}

#ifndef GEODE_IS_IOS
class $modify(CCKeyboardDispatcher) {
bool dispatchKeyboardMSG(enumKeyCodes key, bool down, bool repeat) {
auto& io = ImGui::GetIO();
const auto imKey = cocosToImGuiKey(key);
if (imKey != ImGuiKey_None) {
io.AddKeyEvent(imKey, down);
}

// CCIMEDispatcher stuff doesn't get called on android unless the virtual keyboard would be up.
// Similarly, CCKeyboardDispatcher doesn't get called if the virtual keyboard would be up.
#ifdef GEODE_IS_ANDROID
if (down) {
char c = 0;
if (key >= KEY_A && key <= KEY_Z) {
c = static_cast<char>(key);
if (!io.KeyShift) {
c = static_cast<char>(tolower(c));
}
} else if (key >= KEY_Zero && key <= KEY_Nine) {
c = static_cast<char>('0' + (key - KEY_Zero));
} else if (key == KEY_Space) {
c = ' ';
}

if (c != 0) {
std::string str(1, c);
io.AddInputCharactersUTF8(str.c_str());
}
}
if (key == KEY_Backspace) {
io.AddKeyEvent(ImGuiKey_Backspace, true);
io.AddKeyEvent(ImGuiKey_Backspace, false);
}
#endif

if (io.WantCaptureKeyboard) {
return false;
} else {
return CCKeyboardDispatcher::dispatchKeyboardMSG(key, down, repeat);
}
}
};
#endif
61 changes: 61 additions & 0 deletions src/platform/Mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,65 @@
else return fmt::format("{:#x}", addr - base);
}

// Below is adapted from BetterInputs - thanks Spaghett
#ifdef GEODE_IS_MACOS

#include <Carbon/Carbon.h>
#import <objc/runtime.h>

#include <Geode/cocos/platform/mac/CCEventDispatcher.h>
#import <Geode/cocos/platform/mac/EAGLView.h>
#include <Geode/loader/ModEvent.hpp>
#include <Geode/loader/Log.hpp>

#include <imgui.h>

#define OBJC_SWIZZLE(klass, type, cleanFuncName, funcName) \
do { \
auto cleanFuncName ## Method = class_getInstanceMethod(objc_getClass(#klass), @selector(funcName)); \
cleanFuncName ## OIMP = reinterpret_cast<type>(method_getImplementation(cleanFuncName ## Method)); \
method_setImplementation(cleanFuncName ## Method, reinterpret_cast<IMP>(&cleanFuncName)); \
geode::log::debug("Swizzled Objective C Method '" #klass " " #funcName "'"); \
} while(0)

using key_event_t = void(*)(EAGLView*, SEL, NSEvent*);

static key_event_t flagsChangedExecOIMP;
void flagsChangedExec(EAGLView* self, SEL sel, NSEvent* event)
{

flagsChangedExecOIMP(self, sel, event);

auto& io = ImGui::GetIO();
const NSEventModifierFlags flags = [event modifierFlags];

static NSEventModifierFlags previousFlags = 0;
NSEventModifierFlags changedFlags = flags ^ previousFlags;

if (changedFlags & NSEventModifierFlagControl) {
bool isPressed = flags & NSEventModifierFlagControl;
io.AddKeyEvent(ImGuiKey_ModCtrl, isPressed);
}
if (changedFlags & NSEventModifierFlagOption) {
bool isPressed = flags & NSEventModifierFlagOption;
io.AddKeyEvent(ImGuiKey_ModAlt, isPressed);
}
if (changedFlags & NSEventModifierFlagCommand) {
bool isPressed = flags & NSEventModifierFlagCommand;
io.AddKeyEvent(ImGuiKey_ModSuper, isPressed);
}
if (changedFlags & NSEventModifierFlagShift) {
bool isPressed = flags & NSEventModifierFlagShift;
io.AddKeyEvent(ImGuiKey_ModShift, isPressed);
}

previousFlags = flags;
}

$on_mod(Loaded)
{
OBJC_SWIZZLE(EAGLView, key_event_t, flagsChangedExec, flagsChanged:);
}

#endif
#endif
34 changes: 1 addition & 33 deletions src/platform/Win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,6 @@
using namespace cocos2d;
using namespace geode;

ImGuiKey keyFromGLFW(int key) {
if (key >= GLFW_KEY_0 && key <= GLFW_KEY_9) {
return static_cast<ImGuiKey>(ImGuiKey_0 + (key - GLFW_KEY_0));
} else if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z) {
return static_cast<ImGuiKey>(ImGuiKey_A + (key - GLFW_KEY_A));
}
switch (key) {
case GLFW_KEY_SPACE: return ImGuiKey_Space;
case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace;
case GLFW_KEY_COMMA: return ImGuiKey_Comma;
case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow;
case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow;
case GLFW_KEY_UP: return ImGuiKey_UpArrow;
case GLFW_KEY_DOWN: return ImGuiKey_DownArrow;
case GLFW_KEY_ESCAPE: return ImGuiKey_Escape;
case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift;
case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift;
case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl;
case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt;
// TODO: rest :-)
}
return ImGuiKey_None;
}

class $modify(CCEGLView) {
void updateWindow(int width, int height) {
shouldUpdateGDRenderBuffer() = true;
Expand All @@ -46,15 +22,7 @@ class $modify(CCEGLView) {
DevTools::get()->destroy();
CCEGLView::toggleFullScreen(value, borderless, fix);
DevTools::get()->setup();
}

//todo: i dont care someone else can figure it out, it completely breaks keyboard support
/*void onGLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
//auto& io = ImGui::GetIO();
CCEGLView::onGLFWKeyCallback(window, key, scancode, action, mods);
// in practice this is only used for arrow keys
//io.AddKeyEvent(keyFromGLFW(key), action != GLFW_RELEASE);
}*/
}
};

#include "utils.hpp"
Expand Down