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 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 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 @@ -354,3 +355,83 @@ 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;
case KEY_Delete: return ImGuiKey_Delete;
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;
}
}

class $modify(CCKeyboardDispatcher) {
bool dispatchKeyboardMSG(enumKeyCodes key, bool down, bool repeat) {
log::info("{}", keyToString(key));
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);
}
}
};
58 changes: 58 additions & 0 deletions src/platform/Mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
#include <mach-o/dyld.h>
#import <Foundation/Foundation.h>

#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>

static std::vector<struct dyld_image_info const*> getAllImages() {
std::vector<struct dyld_image_info const*> images;
struct task_dyld_info dyldInfo;
Expand Down Expand Up @@ -86,4 +96,52 @@
else return fmt::format("{:#x}", addr - base);
}

#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;
}

// https://github.com/qimiko/click-on-steps/blob/d8a87e93b5407e5f2113a9715363a5255724c901/src/macos.mm#L101
$on_mod(Loaded)
{
OBJC_SWIZZLE(EAGLView, key_event_t, flagsChangedExec, flagsChanged:);
}

#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