mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-19 23:32:28 +00:00
199 lines
5.8 KiB
C++
199 lines
5.8 KiB
C++
#include "keyboard.h"
|
|
|
|
#include <QDebug>
|
|
#include <QGuiApplication>
|
|
|
|
// Qt is the worst.
|
|
//
|
|
// Assume your keyboard has a key labelled both . and >, as they do on US and UK keyboards. Call it the dot key.
|
|
// Perform the following:
|
|
// 1. press dot key;
|
|
// 2. press shift key;
|
|
// 3. release dot key;
|
|
// 4. release shift key.
|
|
//
|
|
// Per empirical testing, and key repeat aside, on both macOS and Ubuntu 19.04 that sequence will result in
|
|
// _three_ keypress events, but only _two_ key release events. You'll get presses for Qt::Key_Period, Qt::Key_Greater
|
|
// and Qt::Key_Shift. You'll get releases only for Qt::Key_Greater and Qt::Key_Shift.
|
|
//
|
|
// How can you detect at runtime that Key_Greater and Key_Period are the same physical key?
|
|
//
|
|
// You can't. On Ubuntu they have the same values for QKeyEvent::nativeScanCode(), which are unique to the key,
|
|
// but they have different ::nativeVirtualKey()s.
|
|
//
|
|
// On macOS they have the same ::nativeScanCode() only because on macOS [almost] all keys have the same
|
|
// ::nativeScanCode(). So that's not usable. They have the same ::nativeVirtualKey()s, but since that isn't true
|
|
// on Ubuntu, that's also not usable.
|
|
//
|
|
// So how can you track the physical keys on a keyboard via Qt?
|
|
//
|
|
// You can't. Qt is the worst. SDL doesn't have this problem, including in X11, but I'm not sure I want the extra
|
|
// dependency. I may need to reassess.
|
|
|
|
#ifdef Q_OS_LINUX
|
|
#define HAS_X11
|
|
#endif
|
|
|
|
#ifdef HAS_X11
|
|
#include <QX11Info>
|
|
|
|
#include <X11/Xutil.h>
|
|
#include <X11/keysym.h>
|
|
#endif
|
|
|
|
KeyboardMapper::KeyboardMapper() {
|
|
#ifdef HAS_X11
|
|
struct DesiredMapping {
|
|
KeySym source;
|
|
Inputs::Keyboard::Key destination;
|
|
};
|
|
|
|
constexpr DesiredMapping mappings[] = {
|
|
{XK_Escape, Escape},
|
|
{XK_F1, F1}, {XK_F2, F2}, {XK_F3, F3}, {XK_F4, F4}, {XK_F5, F5},
|
|
{XK_F6, F6}, {XK_F7, F7}, {XK_F8, F8}, {XK_F9, F9}, {XK_F10, F10},
|
|
{XK_F11, F11}, {XK_F12, F12},
|
|
{XK_Sys_Req, PrintScreen},
|
|
{XK_Scroll_Lock, ScrollLock},
|
|
{XK_Pause, Pause},
|
|
|
|
{XK_grave, BackTick},
|
|
{XK_1, k1}, {XK_2, k2}, {XK_3, k3}, {XK_4, k4}, {XK_5, k5},
|
|
{XK_6, k6}, {XK_7, k7}, {XK_8, k8}, {XK_9, k9}, {XK_0, k0},
|
|
{XK_minus, Hyphen},
|
|
{XK_equal, Equals},
|
|
{XK_BackSpace, Backspace},
|
|
|
|
{XK_Tab, Tab},
|
|
{XK_Q, Q}, {XK_W, W}, {XK_E, E}, {XK_R, R}, {XK_T, T},
|
|
{XK_Y, Y}, {XK_U, U}, {XK_I, I}, {XK_O, O}, {XK_P, P},
|
|
{XK_bracketleft, OpenSquareBracket},
|
|
{XK_bracketright, CloseSquareBracket},
|
|
{XK_backslash, Backslash},
|
|
|
|
{XK_Caps_Lock, CapsLock},
|
|
{XK_A, A}, {XK_S, S}, {XK_D, D}, {XK_F, F}, {XK_G, G},
|
|
{XK_H, H}, {XK_J, J}, {XK_K, K}, {XK_L, L},
|
|
{XK_semicolon, Semicolon},
|
|
{XK_apostrophe, Quote},
|
|
{XK_Return, Enter},
|
|
|
|
{XK_Shift_L, LeftShift},
|
|
{XK_Z, Z}, {XK_X, X}, {XK_C, C}, {XK_V, V},
|
|
{XK_B, B}, {XK_N, N}, {XK_M, M},
|
|
{XK_comma, Comma},
|
|
{XK_period, FullStop},
|
|
{XK_slash, ForwardSlash},
|
|
{XK_Shift_R, RightShift},
|
|
|
|
{XK_Control_L, LeftControl},
|
|
{XK_Control_R, RightControl},
|
|
{XK_Alt_L, LeftOption},
|
|
{XK_Alt_R, RightOption},
|
|
{XK_Meta_L, LeftMeta},
|
|
{XK_Meta_R, RightMeta},
|
|
{XK_space, Space},
|
|
|
|
{XK_Left, Left}, {XK_Right, Right}, {XK_Up, Up}, {XK_Down, Down},
|
|
|
|
{XK_Insert, Insert},
|
|
{XK_Delete, Delete},
|
|
{XK_Home, Home},
|
|
{XK_End, End},
|
|
|
|
{XK_Num_Lock, NumLock},
|
|
|
|
{XK_KP_Divide, KeypadSlash},
|
|
{XK_KP_Multiply, KeypadAsterisk},
|
|
{XK_KP_Delete, KeypadDelete},
|
|
{XK_KP_7, Keypad7}, {XK_KP_8, Keypad8}, {XK_KP_9, Keypad9}, {XK_KP_Add, KeypadPlus},
|
|
{XK_KP_4, Keypad4}, {XK_KP_5, Keypad5}, {XK_KP_6, Keypad6}, {XK_KP_Subtract, KeypadMinus},
|
|
{XK_KP_1, Keypad1}, {XK_KP_2, Keypad2}, {XK_KP_3, Keypad3}, {XK_KP_Enter, KeypadEnter},
|
|
{XK_KP_0, Keypad0},
|
|
{XK_KP_Decimal, KeypadDecimalPoint},
|
|
{XK_KP_Equal, KeypadEquals},
|
|
|
|
{XK_Help, Help},
|
|
|
|
{0}
|
|
};
|
|
|
|
const DesiredMapping *mapping = mappings;
|
|
while(mapping->source != 0) {
|
|
const auto sym = XKeysymToKeycode(QX11Info::display(), mapping->source);
|
|
keyByKeySym[sym] = mapping->destination;
|
|
++mapping;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::optional<Inputs::Keyboard::Key> KeyboardMapper::keyForEvent(QKeyEvent *event) {
|
|
// Workaround for X11: assume PC-esque mapping.
|
|
|
|
#ifdef HAS_X11
|
|
if(QGuiApplication::platformName() == QLatin1String("xcb")) {
|
|
const auto sym = keyByKeySym.find(event->nativeScancode());
|
|
if(sym == keyByKeySym.end()) return std::nullopt;
|
|
return sym->destination;
|
|
}
|
|
#endif
|
|
|
|
// Fall back on a limited, faulty adaptation.
|
|
#define BIND2(qtKey, clkKey) case Qt::qtKey: return Inputs::Keyboard::Key::clkKey;
|
|
#define BIND(key) BIND2(Key_##key, key)
|
|
|
|
switch(event->key()) {
|
|
default: return {};
|
|
|
|
BIND(Escape);
|
|
BIND(F1); BIND(F2); BIND(F3); BIND(F4); BIND(F5); BIND(F6);
|
|
BIND(F7); BIND(F8); BIND(F9); BIND(F10); BIND(F11); BIND(F12);
|
|
BIND2(Key_Print, PrintScreen);
|
|
BIND(ScrollLock); BIND(Pause);
|
|
|
|
BIND2(Key_AsciiTilde, BackTick);
|
|
BIND2(Key_1, k1); BIND2(Key_2, k2); BIND2(Key_3, k3); BIND2(Key_4, k4); BIND2(Key_5, k5);
|
|
BIND2(Key_6, k6); BIND2(Key_7, k7); BIND2(Key_8, k8); BIND2(Key_9, k9); BIND2(Key_0, k0);
|
|
BIND2(Key_Minus, Hyphen);
|
|
BIND2(Key_Plus, Equals);
|
|
BIND(Backspace);
|
|
|
|
BIND(Tab); BIND(Q); BIND(W); BIND(E); BIND(R); BIND(T); BIND(Y);
|
|
BIND(U); BIND(I); BIND(O); BIND(P);
|
|
BIND2(Key_BraceLeft, OpenSquareBracket);
|
|
BIND2(Key_BraceRight, CloseSquareBracket);
|
|
BIND(Backslash);
|
|
|
|
BIND(CapsLock); BIND(A); BIND(S); BIND(D); BIND(F); BIND(G);
|
|
BIND(H); BIND(J); BIND(K); BIND(L);
|
|
BIND(Semicolon);
|
|
BIND2(Key_Apostrophe, Quote);
|
|
BIND2(Key_QuoteDbl, Quote);
|
|
// TODO: something to hash?
|
|
BIND2(Key_Return, Enter);
|
|
|
|
BIND2(Key_Shift, LeftShift);
|
|
BIND(Z); BIND(X); BIND(C); BIND(V);
|
|
BIND(B); BIND(N); BIND(M);
|
|
BIND(Comma);
|
|
BIND2(Key_Period, FullStop);
|
|
BIND2(Key_Slash, ForwardSlash);
|
|
// Omitted: right shift.
|
|
|
|
BIND2(Key_Control, LeftControl);
|
|
BIND2(Key_Alt, LeftOption);
|
|
BIND2(Key_Meta, LeftMeta);
|
|
BIND(Space);
|
|
BIND2(Key_AltGr, RightOption);
|
|
|
|
BIND(Left); BIND(Right); BIND(Up); BIND(Down);
|
|
|
|
BIND(Insert); BIND(Home); BIND(PageUp); BIND(Delete); BIND(End); BIND(PageDown);
|
|
|
|
BIND(NumLock);
|
|
}
|
|
|
|
#undef BIND
|
|
#undef BIND2
|
|
}
|