From 70eaa7910847e737f304b6a8a1e6871904883784 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 18:51:58 -0400 Subject: [PATCH 01/29] Makes an attempt to use X11 KeySyms. Rather than hard-coding a mapping. --- OSBindings/Qt/mainwindow.cpp | 133 ++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 6836844f1..5b20f52f3 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -878,84 +878,101 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) { // 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. -std::optional MainWindow::keyForEvent(QKeyEvent *event) { - // Workaround for X11: assume PC-esque mapping from ::nativeScanCode to symbols. - // - // Yucky, ugly, harcoded yuck. TODO: work out how `xmodmap -pke` seems to derive these codes at runtime. +#ifdef Q_OS_LINUX +#define HAS_X11 +#endif +#ifdef HAS_X11 +#include +#include +#endif + +std::optional MainWindow::keyForEvent(QKeyEvent *event) { + // Workaround for X11: assume PC-esque mapping. + +#ifdef HAS_X11 if(QGuiApplication::platformName() == QLatin1String("xcb")) { + // Convert from a machine-specific keycode to a more general keysym. + int keysyms = 0; + const KeySym *const keysyms = XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysyms); + const KeySym keysym = keysyms[0]; + XFree(keysyms); + #define BIND(code, key) case code: return Inputs::Keyboard::Key::key; - switch(event->nativeScanCode()) { + switch(keysym) { default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; - BIND(1, Escape); - BIND(67, F1); BIND(68, F2); BIND(69, F3); BIND(70, F4); BIND(71, F5); - BIND(72, F6); BIND(73, F7); BIND(74, F8); BIND(75, F9); BIND(76, F10); - BIND(95, F11); BIND(96, F12); - BIND(107, PrintScreen); - BIND(78, ScrollLock); - BIND(127, Pause); + BIND(XK_Escape, Escape); + BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); + BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); + BIND(XK_F11, F11); BIND(XK_F12, F12); + BIND(XK_Sys_Req, PrintScreen); + BIND(XK_Scroll_Lock, ScrollLock); + BIND(XK_Pause, Pause); - BIND(49, BackTick); - BIND(10, k1); BIND(11, k2); BIND(12, k3); BIND(13, k4); BIND(14, k5); - BIND(15, k6); BIND(16, k7); BIND(17, k8); BIND(18, k9); BIND(19, k0); - BIND(20, Hyphen); - BIND(21, Equals); - BIND(22, Backspace); + BIND(XK_grave, BackTick); + BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); + BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); + BIND(XK_minus, Hyphen); + BIND(XK_equal, Equals); + BIND(XK_BackSpace, Backspace); - BIND(23, Tab); - BIND(24, Q); BIND(25, W); BIND(26, E); BIND(27, R); BIND(28, T); - BIND(29, Y); BIND(30, U); BIND(31, I); BIND(32, O); BIND(33, P); - BIND(34, OpenSquareBracket); - BIND(35, CloseSquareBracket); - BIND(51, Backslash); + BIND(XK_Tab, Tab); + BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); + BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); + BIND(XK_bracketleft, OpenSquareBracket); + BIND(XK_bracketright, CloseSquareBracket); + BIND(XK_backslash, Backslash); - BIND(66, CapsLock); - BIND(38, A); BIND(39, S); BIND(40, D); BIND(41, F); BIND(42, G); - BIND(43, H); BIND(44, J); BIND(45, K); BIND(46, L); - BIND(47, Semicolon); - BIND(48, Quote); - BIND(36, Enter); + BIND(XK_Caps_Lock, CapsLock); + BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); + BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); + BIND(XK_semicolon, Semicolon); + BIND(XK_apostrophe, Quote); + BIND(XK_Return, Enter); - BIND(50, LeftShift); - BIND(52, Z); BIND(53, X); BIND(54, C); BIND(55, V); - BIND(56, B); BIND(57, N); BIND(58, M); - BIND(59, Comma); - BIND(60, FullStop); - BIND(61, ForwardSlash); - BIND(62, RightShift); + BIND(XK_Shift_L, LeftShift); + BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); + BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); + BIND(XK_comma, Comma); + BIND(XK_period, FullStop); + BIND(XK_slash, ForwardSlash); + BIND(XK_Shift_R, RightShift); - BIND(105, LeftControl); - BIND(204, LeftOption); - BIND(205, LeftMeta); - BIND(65, Space); - BIND(108, RightOption); + BIND(XK_Control_L, LeftControl); + BIND(XK_Control_R, RightControl); + BIND(XK_Alt_L, LeftOption); + BIND(XK_Alt_R, RightOption); + BIND(XK_Meta_L, LeftMeta); + BIND(XK_Meta_R, RightMeta); + BIND(XK_space, Space); - BIND(113, Left); BIND(114, Right); BIND(111, Up); BIND(116, Down); + BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); - BIND(118, Insert); - BIND(119, Delete); - BIND(110, Home); - BIND(115, End); + BIND(XK_Insert, Insert); + BIND(XK_Delete, Delete); + BIND(XK_Home, Home); + BIND(XK_End, End); - BIND(77, NumLock); + BIND(XK_Num_Lock, NumLock); - BIND(106, KeypadSlash); - BIND(63, KeypadAsterisk); - BIND(91, KeypadDelete); - BIND(79, Keypad7); BIND(80, Keypad8); BIND(81, Keypad9); BIND(86, KeypadPlus); - BIND(83, Keypad4); BIND(84, Keypad5); BIND(85, Keypad6); BIND(82, KeypadMinus); - BIND(87, Keypad1); BIND(88, Keypad2); BIND(89, Keypad3); BIND(104, KeypadEnter); - BIND(90, Keypad0); - BIND(129, KeypadDecimalPoint); - BIND(125, KeypadEquals); + BIND(XK_KP_Divide, KeypadSlash); + BIND(XK_KP_Multiply, KeypadAsterisk); + BIND(XK_KP_Delete, KeypadDelete); + BIND(XP_KP_7, Keypad7); BIND(XP_KP_8, Keypad8); BIND(XP_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); + BIND(XP_KP_4, Keypad4); BIND(XP_KP_5, Keypad5); BIND(XP_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); + BIND(XP_KP_1, Keypad1); BIND(XP_KP_2, Keypad2); BIND(XP_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); + BIND(XP_KP_0, Keypad0); + BIND(XK_KP_Decimal, KeypadDecimalPoint); + BIND(XK_KP_Equal, KeypadEquals); - BIND(146, Help); + BIND(XK_Help, Help); } #undef BIND } +#endif // Fall back on a limited, faulty adaptation. #define BIND2(qtKey, clkKey) case Qt::qtKey: return Inputs::Keyboard::Key::clkKey; From 056a036712d2cafa2cc3d9903bf12de989535a7a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:31:13 -0400 Subject: [PATCH 02/29] Add missing fallthrough declaration. --- Machines/Apple/ADB/Keyboard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Apple/ADB/Keyboard.cpp b/Machines/Apple/ADB/Keyboard.cpp index 11c231a6f..9a9ad556f 100644 --- a/Machines/Apple/ADB/Keyboard.cpp +++ b/Machines/Apple/ADB/Keyboard.cpp @@ -16,6 +16,7 @@ void Keyboard::perform_command(const Command &command) { switch(command.type) { case Command::Type::Reset: modifiers_ = 0xffff; + [[fallthrough]]; case Command::Type::Flush: { std::lock_guard lock_guard(keys_mutex_); pending_events_.clear(); From 48ad4d4c4cce9b29b6a4958928025b4ff66e5509 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:31:39 -0400 Subject: [PATCH 03/29] Make it overt that this can't return without a value. --- Machines/Sinclair/Keyboard/Keyboard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Sinclair/Keyboard/Keyboard.cpp b/Machines/Sinclair/Keyboard/Keyboard.cpp index f0cd23b39..240c8f5e6 100644 --- a/Machines/Sinclair/Keyboard/Keyboard.cpp +++ b/Machines/Sinclair/Keyboard/Keyboard.cpp @@ -280,6 +280,7 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const { case Machine::ZX81: return table_lookup_sequence_for_character(zx81_key_sequences, character); + default: case Machine::ZXSpectrum: return table_lookup_sequence_for_character(spectrum_key_sequences, character); } From 5a468530753e72b158a2375ce4d5b5a2303a4ac2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:32:16 -0400 Subject: [PATCH 04/29] `t` may be unused, per the `if constexpr`. --- Reflection/Struct.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 24dbd489d..761b878ec 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -323,7 +323,7 @@ template class StructImpl: public Struct { } private: - template bool declare_reflectable(Type *t, const std::string &name) { + template bool declare_reflectable([[maybe_unused]] Type *t, const std::string &name) { if constexpr (std::is_base_of::value) { Reflection::Struct *const str = static_cast(t); declare_emplace(str, name); From 1b39b1712505d11308f5e3c1b5675e80749a3c72 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:33:25 -0400 Subject: [PATCH 05/29] Make sure `size_t` is visible. --- Reflection/Struct.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 761b878ec..36c74f6a8 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include From 8ebce466dbde4492c5d32410445a55613cfc58f2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:35:23 -0400 Subject: [PATCH 06/29] Be overt in empty std::string construction. --- Reflection/Struct.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 36c74f6a8..464860ade 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -319,7 +319,7 @@ template class StructImpl: public Struct { if(iterator != contents_.end()) { return iterator->first; } else { - return ""; + return std::string(); } } From c8ab18f2b6972acb095f0d6dd05e7a8f3807066b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:38:50 -0400 Subject: [PATCH 07/29] Add overt fallthrough. --- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index bcee2de17..cba648d6e 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -420,6 +420,7 @@ template class ConcreteMachine: break; } } + [[fallthrough]]; case PartialMachineCycle::Read: if constexpr (model == Model::SixteenK) { From 9c29655da237826988bb355eca3f288693a7aa50 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:43:22 -0400 Subject: [PATCH 08/29] Add x11extras as per use of . --- OSBindings/Qt/ClockSignal.pro | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index 65055ae4d..a3502f93b 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -1,5 +1,10 @@ QT += core gui multimedia widgets +# Add x11extras if on an appropriate platform; +# this is used in X11 to workaround Qt's terrible +# keyboard obstructionism. +Qt += x11extras + # Be specific about C++17 but also try the vaguer C++1z for older # versions of Qt. CONFIG += c++17 From 4774676e2a6836d7f6c67bbfc851483f4063ac1a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 21:09:01 -0400 Subject: [PATCH 09/29] Correct keypad symbols, push X11 into a namespace. --- OSBindings/Qt/mainwindow.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 5b20f52f3..b5957b852 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -884,7 +884,14 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) { #ifdef HAS_X11 #include -#include + +// X11 collides with my 'Time' namespace; that's probably my fault for lazy namespace +// selection, but this fixes the problem. +namespace X11 { +#include +#include +} + #endif std::optional MainWindow::keyForEvent(QKeyEvent *event) { @@ -893,10 +900,10 @@ std::optional MainWindow::keyForEvent(QKeyEvent *event) { #ifdef HAS_X11 if(QGuiApplication::platformName() == QLatin1String("xcb")) { // Convert from a machine-specific keycode to a more general keysym. - int keysyms = 0; - const KeySym *const keysyms = XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysyms); - const KeySym keysym = keysyms[0]; - XFree(keysyms); + int keysym_count = 0; + KeySym *const keysyms = X11::XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysym_count); + const auto keysym = keysyms[0]; + X11::XFree(keysyms); #define BIND(code, key) case code: return Inputs::Keyboard::Key::key; @@ -960,9 +967,9 @@ std::optional MainWindow::keyForEvent(QKeyEvent *event) { BIND(XK_KP_Divide, KeypadSlash); BIND(XK_KP_Multiply, KeypadAsterisk); BIND(XK_KP_Delete, KeypadDelete); - BIND(XP_KP_7, Keypad7); BIND(XP_KP_8, Keypad8); BIND(XP_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); - BIND(XP_KP_4, Keypad4); BIND(XP_KP_5, Keypad5); BIND(XP_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); - BIND(XP_KP_1, Keypad1); BIND(XP_KP_2, Keypad2); BIND(XP_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); + BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); + BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); + BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); BIND(XP_KP_0, Keypad0); BIND(XK_KP_Decimal, KeypadDecimalPoint); BIND(XK_KP_Equal, KeypadEquals); From 0bf6b765d36c9751847443c29a0020e588523ceb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 21:11:47 -0400 Subject: [PATCH 10/29] Further namespace/name corrections. --- OSBindings/Qt/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index b5957b852..232e65413 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -901,7 +901,7 @@ std::optional MainWindow::keyForEvent(QKeyEvent *event) { if(QGuiApplication::platformName() == QLatin1String("xcb")) { // Convert from a machine-specific keycode to a more general keysym. int keysym_count = 0; - KeySym *const keysyms = X11::XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysym_count); + X11::KeySym *const keysyms = X11::XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysym_count); const auto keysym = keysyms[0]; X11::XFree(keysyms); @@ -970,7 +970,7 @@ std::optional MainWindow::keyForEvent(QKeyEvent *event) { BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); - BIND(XP_KP_0, Keypad0); + BIND(XK_KP_0, Keypad0); BIND(XK_KP_Decimal, KeypadDecimalPoint); BIND(XK_KP_Equal, KeypadEquals); From 1465b0ee4d7d134a3afe02796d7bd382eb956b27 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 21:15:20 -0400 Subject: [PATCH 11/29] Shunt X11 code to bottom of file, to avoid #include interference. --- OSBindings/Qt/mainwindow.cpp | 379 +++++++++++++++++------------------ 1 file changed, 189 insertions(+), 190 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 232e65413..99ec1986a 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -851,196 +851,6 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) { processEvent(event); } -// 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 - -// X11 collides with my 'Time' namespace; that's probably my fault for lazy namespace -// selection, but this fixes the problem. -namespace X11 { -#include -#include -} - -#endif - -std::optional MainWindow::keyForEvent(QKeyEvent *event) { - // Workaround for X11: assume PC-esque mapping. - -#ifdef HAS_X11 - if(QGuiApplication::platformName() == QLatin1String("xcb")) { - // Convert from a machine-specific keycode to a more general keysym. - int keysym_count = 0; - X11::KeySym *const keysyms = X11::XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysym_count); - const auto keysym = keysyms[0]; - X11::XFree(keysyms); - -#define BIND(code, key) case code: return Inputs::Keyboard::Key::key; - - switch(keysym) { - default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; - - BIND(XK_Escape, Escape); - BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); - BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); - BIND(XK_F11, F11); BIND(XK_F12, F12); - BIND(XK_Sys_Req, PrintScreen); - BIND(XK_Scroll_Lock, ScrollLock); - BIND(XK_Pause, Pause); - - BIND(XK_grave, BackTick); - BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); - BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); - BIND(XK_minus, Hyphen); - BIND(XK_equal, Equals); - BIND(XK_BackSpace, Backspace); - - BIND(XK_Tab, Tab); - BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); - BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); - BIND(XK_bracketleft, OpenSquareBracket); - BIND(XK_bracketright, CloseSquareBracket); - BIND(XK_backslash, Backslash); - - BIND(XK_Caps_Lock, CapsLock); - BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); - BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); - BIND(XK_semicolon, Semicolon); - BIND(XK_apostrophe, Quote); - BIND(XK_Return, Enter); - - BIND(XK_Shift_L, LeftShift); - BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); - BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); - BIND(XK_comma, Comma); - BIND(XK_period, FullStop); - BIND(XK_slash, ForwardSlash); - BIND(XK_Shift_R, RightShift); - - BIND(XK_Control_L, LeftControl); - BIND(XK_Control_R, RightControl); - BIND(XK_Alt_L, LeftOption); - BIND(XK_Alt_R, RightOption); - BIND(XK_Meta_L, LeftMeta); - BIND(XK_Meta_R, RightMeta); - BIND(XK_space, Space); - - BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); - - BIND(XK_Insert, Insert); - BIND(XK_Delete, Delete); - BIND(XK_Home, Home); - BIND(XK_End, End); - - BIND(XK_Num_Lock, NumLock); - - BIND(XK_KP_Divide, KeypadSlash); - BIND(XK_KP_Multiply, KeypadAsterisk); - BIND(XK_KP_Delete, KeypadDelete); - BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); - BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); - BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); - BIND(XK_KP_0, Keypad0); - BIND(XK_KP_Decimal, KeypadDecimalPoint); - BIND(XK_KP_Equal, KeypadEquals); - - BIND(XK_Help, Help); - } - -#undef BIND - } -#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 -} - - bool MainWindow::processEvent(QKeyEvent *event) { if(!machine) return true; @@ -1466,3 +1276,192 @@ void MainWindow::updateStatusBarText() { } statusBar()->showMessage(fullText); } + +// 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 + +// X11 collides with my 'Time' namespace; that's probably my fault for lazy namespace +// selection, but this fixes the problem. +namespace X11 { +#include +#include +} + +#endif + +std::optional MainWindow::keyForEvent(QKeyEvent *event) { + // Workaround for X11: assume PC-esque mapping. + +#ifdef HAS_X11 + if(QGuiApplication::platformName() == QLatin1String("xcb")) { + // Convert from a machine-specific keycode to a more general keysym. + int keysym_count = 0; + X11::KeySym *const keysyms = X11::XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysym_count); + const auto keysym = keysyms[0]; + X11::XFree(keysyms); + +#define BIND(code, key) case code: return Inputs::Keyboard::Key::key; + + switch(keysym) { + default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; + + BIND(XK_Escape, Escape); + BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); + BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); + BIND(XK_F11, F11); BIND(XK_F12, F12); + BIND(XK_Sys_Req, PrintScreen); + BIND(XK_Scroll_Lock, ScrollLock); + BIND(XK_Pause, Pause); + + BIND(XK_grave, BackTick); + BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); + BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); + BIND(XK_minus, Hyphen); + BIND(XK_equal, Equals); + BIND(XK_BackSpace, Backspace); + + BIND(XK_Tab, Tab); + BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); + BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); + BIND(XK_bracketleft, OpenSquareBracket); + BIND(XK_bracketright, CloseSquareBracket); + BIND(XK_backslash, Backslash); + + BIND(XK_Caps_Lock, CapsLock); + BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); + BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); + BIND(XK_semicolon, Semicolon); + BIND(XK_apostrophe, Quote); + BIND(XK_Return, Enter); + + BIND(XK_Shift_L, LeftShift); + BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); + BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); + BIND(XK_comma, Comma); + BIND(XK_period, FullStop); + BIND(XK_slash, ForwardSlash); + BIND(XK_Shift_R, RightShift); + + BIND(XK_Control_L, LeftControl); + BIND(XK_Control_R, RightControl); + BIND(XK_Alt_L, LeftOption); + BIND(XK_Alt_R, RightOption); + BIND(XK_Meta_L, LeftMeta); + BIND(XK_Meta_R, RightMeta); + BIND(XK_space, Space); + + BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); + + BIND(XK_Insert, Insert); + BIND(XK_Delete, Delete); + BIND(XK_Home, Home); + BIND(XK_End, End); + + BIND(XK_Num_Lock, NumLock); + + BIND(XK_KP_Divide, KeypadSlash); + BIND(XK_KP_Multiply, KeypadAsterisk); + BIND(XK_KP_Delete, KeypadDelete); + BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); + BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); + BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); + BIND(XK_KP_0, Keypad0); + BIND(XK_KP_Decimal, KeypadDecimalPoint); + BIND(XK_KP_Equal, KeypadEquals); + + BIND(XK_Help, Help); + } + +#undef BIND + } +#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 +} From 685140a4c22b75314569306402a717604c30476b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 21:18:14 -0400 Subject: [PATCH 12/29] Correct Qt -> QT. --- OSBindings/Qt/ClockSignal.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index a3502f93b..71bedb6ae 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -3,7 +3,8 @@ QT += core gui multimedia widgets # Add x11extras if on an appropriate platform; # this is used in X11 to workaround Qt's terrible # keyboard obstructionism. -Qt += x11extras +QT += x11extras +LIBS += -lx11 # Be specific about C++17 but also try the vaguer C++1z for older # versions of Qt. From 91963414822b65f2492572b41ab4815a32ff88a0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 21:45:53 -0400 Subject: [PATCH 13/29] Retrenches: it seems `nativeVirtualKey` does what I want. Hooray! --- OSBindings/Qt/ClockSignal.pro | 6 - OSBindings/Qt/mainwindow.cpp | 364 ++++++++++++++++------------------ 2 files changed, 175 insertions(+), 195 deletions(-) diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index 71bedb6ae..65055ae4d 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -1,11 +1,5 @@ QT += core gui multimedia widgets -# Add x11extras if on an appropriate platform; -# this is used in X11 to workaround Qt's terrible -# keyboard obstructionism. -QT += x11extras -LIBS += -lx11 - # Be specific about C++17 but also try the vaguer C++1z for older # versions of Qt. CONFIG += c++17 diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 99ec1986a..21f020dcc 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -851,6 +851,181 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) { processEvent(event); } +// 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 +#endif + +std::optional MainWindow::keyForEvent(QKeyEvent *event) { + // Workaround for X11: assume PC-esque mapping. + +#ifdef HAS_X11 + if(QGuiApplication::platformName() == QLatin1String("xcb")) { +#define BIND(code, key) case code: return Inputs::Keyboard::Key::key; + + switch(keysym->nativeVirtualKey()) { + default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; + + BIND(XK_Escape, Escape); + BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); + BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); + BIND(XK_F11, F11); BIND(XK_F12, F12); + BIND(XK_Sys_Req, PrintScreen); + BIND(XK_Scroll_Lock, ScrollLock); + BIND(XK_Pause, Pause); + + BIND(XK_grave, BackTick); + BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); + BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); + BIND(XK_minus, Hyphen); + BIND(XK_equal, Equals); + BIND(XK_BackSpace, Backspace); + + BIND(XK_Tab, Tab); + BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); + BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); + BIND(XK_bracketleft, OpenSquareBracket); + BIND(XK_bracketright, CloseSquareBracket); + BIND(XK_backslash, Backslash); + + BIND(XK_Caps_Lock, CapsLock); + BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); + BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); + BIND(XK_semicolon, Semicolon); + BIND(XK_apostrophe, Quote); + BIND(XK_Return, Enter); + + BIND(XK_Shift_L, LeftShift); + BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); + BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); + BIND(XK_comma, Comma); + BIND(XK_period, FullStop); + BIND(XK_slash, ForwardSlash); + BIND(XK_Shift_R, RightShift); + + BIND(XK_Control_L, LeftControl); + BIND(XK_Control_R, RightControl); + BIND(XK_Alt_L, LeftOption); + BIND(XK_Alt_R, RightOption); + BIND(XK_Meta_L, LeftMeta); + BIND(XK_Meta_R, RightMeta); + BIND(XK_space, Space); + + BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); + + BIND(XK_Insert, Insert); + BIND(XK_Delete, Delete); + BIND(XK_Home, Home); + BIND(XK_End, End); + + BIND(XK_Num_Lock, NumLock); + + BIND(XK_KP_Divide, KeypadSlash); + BIND(XK_KP_Multiply, KeypadAsterisk); + BIND(XK_KP_Delete, KeypadDelete); + BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); + BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); + BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); + BIND(XK_KP_0, Keypad0); + BIND(XK_KP_Decimal, KeypadDecimalPoint); + BIND(XK_KP_Equal, KeypadEquals); + + BIND(XK_Help, Help); + } + +#undef BIND + } +#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 +} + bool MainWindow::processEvent(QKeyEvent *event) { if(!machine) return true; @@ -1276,192 +1451,3 @@ void MainWindow::updateStatusBarText() { } statusBar()->showMessage(fullText); } - -// 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 - -// X11 collides with my 'Time' namespace; that's probably my fault for lazy namespace -// selection, but this fixes the problem. -namespace X11 { -#include -#include -} - -#endif - -std::optional MainWindow::keyForEvent(QKeyEvent *event) { - // Workaround for X11: assume PC-esque mapping. - -#ifdef HAS_X11 - if(QGuiApplication::platformName() == QLatin1String("xcb")) { - // Convert from a machine-specific keycode to a more general keysym. - int keysym_count = 0; - X11::KeySym *const keysyms = X11::XGetKeyboardMapping(QX11Info::display(), event->nativeScanCode(), 1, &keysym_count); - const auto keysym = keysyms[0]; - X11::XFree(keysyms); - -#define BIND(code, key) case code: return Inputs::Keyboard::Key::key; - - switch(keysym) { - default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; - - BIND(XK_Escape, Escape); - BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); - BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); - BIND(XK_F11, F11); BIND(XK_F12, F12); - BIND(XK_Sys_Req, PrintScreen); - BIND(XK_Scroll_Lock, ScrollLock); - BIND(XK_Pause, Pause); - - BIND(XK_grave, BackTick); - BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); - BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); - BIND(XK_minus, Hyphen); - BIND(XK_equal, Equals); - BIND(XK_BackSpace, Backspace); - - BIND(XK_Tab, Tab); - BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); - BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); - BIND(XK_bracketleft, OpenSquareBracket); - BIND(XK_bracketright, CloseSquareBracket); - BIND(XK_backslash, Backslash); - - BIND(XK_Caps_Lock, CapsLock); - BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); - BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); - BIND(XK_semicolon, Semicolon); - BIND(XK_apostrophe, Quote); - BIND(XK_Return, Enter); - - BIND(XK_Shift_L, LeftShift); - BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); - BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); - BIND(XK_comma, Comma); - BIND(XK_period, FullStop); - BIND(XK_slash, ForwardSlash); - BIND(XK_Shift_R, RightShift); - - BIND(XK_Control_L, LeftControl); - BIND(XK_Control_R, RightControl); - BIND(XK_Alt_L, LeftOption); - BIND(XK_Alt_R, RightOption); - BIND(XK_Meta_L, LeftMeta); - BIND(XK_Meta_R, RightMeta); - BIND(XK_space, Space); - - BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); - - BIND(XK_Insert, Insert); - BIND(XK_Delete, Delete); - BIND(XK_Home, Home); - BIND(XK_End, End); - - BIND(XK_Num_Lock, NumLock); - - BIND(XK_KP_Divide, KeypadSlash); - BIND(XK_KP_Multiply, KeypadAsterisk); - BIND(XK_KP_Delete, KeypadDelete); - BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); - BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); - BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); - BIND(XK_KP_0, Keypad0); - BIND(XK_KP_Decimal, KeypadDecimalPoint); - BIND(XK_KP_Equal, KeypadEquals); - - BIND(XK_Help, Help); - } - -#undef BIND - } -#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 -} From 0cf1c9040a2d5fdfc1ea9a303c5f500bedaf580a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:31:13 -0400 Subject: [PATCH 14/29] Add missing fallthrough declaration. --- Machines/Apple/ADB/Keyboard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Apple/ADB/Keyboard.cpp b/Machines/Apple/ADB/Keyboard.cpp index 11c231a6f..9a9ad556f 100644 --- a/Machines/Apple/ADB/Keyboard.cpp +++ b/Machines/Apple/ADB/Keyboard.cpp @@ -16,6 +16,7 @@ void Keyboard::perform_command(const Command &command) { switch(command.type) { case Command::Type::Reset: modifiers_ = 0xffff; + [[fallthrough]]; case Command::Type::Flush: { std::lock_guard lock_guard(keys_mutex_); pending_events_.clear(); From 2c18bb45082fe670a8e7a954e1f54feb26123ab6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:31:39 -0400 Subject: [PATCH 15/29] Make it overt that this can't return without a value. --- Machines/Sinclair/Keyboard/Keyboard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Sinclair/Keyboard/Keyboard.cpp b/Machines/Sinclair/Keyboard/Keyboard.cpp index f0cd23b39..240c8f5e6 100644 --- a/Machines/Sinclair/Keyboard/Keyboard.cpp +++ b/Machines/Sinclair/Keyboard/Keyboard.cpp @@ -280,6 +280,7 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const { case Machine::ZX81: return table_lookup_sequence_for_character(zx81_key_sequences, character); + default: case Machine::ZXSpectrum: return table_lookup_sequence_for_character(spectrum_key_sequences, character); } From 00679b6135c504fe27413600236dda3b3baf59a7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:32:16 -0400 Subject: [PATCH 16/29] `t` may be unused, per the `if constexpr`. --- Reflection/Struct.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 24dbd489d..761b878ec 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -323,7 +323,7 @@ template class StructImpl: public Struct { } private: - template bool declare_reflectable(Type *t, const std::string &name) { + template bool declare_reflectable([[maybe_unused]] Type *t, const std::string &name) { if constexpr (std::is_base_of::value) { Reflection::Struct *const str = static_cast(t); declare_emplace(str, name); From 95f427291972648c42a524f753e0d213fbffa517 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:33:25 -0400 Subject: [PATCH 17/29] Make sure `size_t` is visible. --- Reflection/Struct.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 761b878ec..36c74f6a8 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include From 32e3dd71b154d48862f7d3e3fd93499314794bd3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:35:23 -0400 Subject: [PATCH 18/29] Be overt in empty std::string construction. --- Reflection/Struct.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 36c74f6a8..464860ade 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -319,7 +319,7 @@ template class StructImpl: public Struct { if(iterator != contents_.end()) { return iterator->first; } else { - return ""; + return std::string(); } } From 052e284c337049f21b228ae1c5d3910d99afc0c1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 May 2021 20:38:50 -0400 Subject: [PATCH 19/29] Add overt fallthrough. --- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index bcee2de17..cba648d6e 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -420,6 +420,7 @@ template class ConcreteMachine: break; } } + [[fallthrough]]; case PartialMachineCycle::Read: if constexpr (model == Model::SixteenK) { From 4858cfce6bae02c03c21531f1bc69350e6d01b57 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 18:56:10 -0400 Subject: [PATCH 20/29] Starts to factor out the keyboard mapper. The more easily to clarify as to #includes, etc, and to allow for a relevant constructor. --- OSBindings/Qt/keyboard.cpp | 176 ++++++++++++++++++++++++++++++++++ OSBindings/Qt/keyboard.h | 15 +++ OSBindings/Qt/mainwindow.cpp | 177 +---------------------------------- OSBindings/Qt/mainwindow.h | 3 +- 4 files changed, 194 insertions(+), 177 deletions(-) create mode 100644 OSBindings/Qt/keyboard.cpp create mode 100644 OSBindings/Qt/keyboard.h diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp new file mode 100644 index 000000000..09226f93e --- /dev/null +++ b/OSBindings/Qt/keyboard.cpp @@ -0,0 +1,176 @@ +#include "mainwindow.h" + +// 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 +#endif + +std::optional MainWindow::keyForEvent(QKeyEvent *event) { + // Workaround for X11: assume PC-esque mapping. + +#ifdef HAS_X11 + if(QGuiApplication::platformName() == QLatin1String("xcb")) { +#define BIND(code, key) case code: return Inputs::Keyboard::Key::key; + + switch(keysym->nativeVirtualKey()) { + default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; + + BIND(XK_Escape, Escape); + BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); + BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); + BIND(XK_F11, F11); BIND(XK_F12, F12); + BIND(XK_Sys_Req, PrintScreen); + BIND(XK_Scroll_Lock, ScrollLock); + BIND(XK_Pause, Pause); + + BIND(XK_grave, BackTick); + BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); + BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); + BIND(XK_minus, Hyphen); + BIND(XK_equal, Equals); + BIND(XK_BackSpace, Backspace); + + BIND(XK_Tab, Tab); + BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); + BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); + BIND(XK_bracketleft, OpenSquareBracket); + BIND(XK_bracketright, CloseSquareBracket); + BIND(XK_backslash, Backslash); + + BIND(XK_Caps_Lock, CapsLock); + BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); + BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); + BIND(XK_semicolon, Semicolon); + BIND(XK_apostrophe, Quote); + BIND(XK_Return, Enter); + + BIND(XK_Shift_L, LeftShift); + BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); + BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); + BIND(XK_comma, Comma); + BIND(XK_period, FullStop); + BIND(XK_slash, ForwardSlash); + BIND(XK_Shift_R, RightShift); + + BIND(XK_Control_L, LeftControl); + BIND(XK_Control_R, RightControl); + BIND(XK_Alt_L, LeftOption); + BIND(XK_Alt_R, RightOption); + BIND(XK_Meta_L, LeftMeta); + BIND(XK_Meta_R, RightMeta); + BIND(XK_space, Space); + + BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); + + BIND(XK_Insert, Insert); + BIND(XK_Delete, Delete); + BIND(XK_Home, Home); + BIND(XK_End, End); + + BIND(XK_Num_Lock, NumLock); + + BIND(XK_KP_Divide, KeypadSlash); + BIND(XK_KP_Multiply, KeypadAsterisk); + BIND(XK_KP_Delete, KeypadDelete); + BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); + BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); + BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); + BIND(XK_KP_0, Keypad0); + BIND(XK_KP_Decimal, KeypadDecimalPoint); + BIND(XK_KP_Equal, KeypadEquals); + + BIND(XK_Help, Help); + } + +#undef BIND + } +#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 +} diff --git a/OSBindings/Qt/keyboard.h b/OSBindings/Qt/keyboard.h new file mode 100644 index 000000000..ba991789f --- /dev/null +++ b/OSBindings/Qt/keyboard.h @@ -0,0 +1,15 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + + +class KeyboardMapper { + public: + KeyboardMapper(); + std::optional keyForEvent(QKeyEvent *); + + private: +}; + +#endif // MAINWINDOW_H diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 21f020dcc..8b96cbcdb 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -851,185 +851,10 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) { processEvent(event); } -// 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 -#endif - -std::optional MainWindow::keyForEvent(QKeyEvent *event) { - // Workaround for X11: assume PC-esque mapping. - -#ifdef HAS_X11 - if(QGuiApplication::platformName() == QLatin1String("xcb")) { -#define BIND(code, key) case code: return Inputs::Keyboard::Key::key; - - switch(keysym->nativeVirtualKey()) { - default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; - - BIND(XK_Escape, Escape); - BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); - BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); - BIND(XK_F11, F11); BIND(XK_F12, F12); - BIND(XK_Sys_Req, PrintScreen); - BIND(XK_Scroll_Lock, ScrollLock); - BIND(XK_Pause, Pause); - - BIND(XK_grave, BackTick); - BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); - BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); - BIND(XK_minus, Hyphen); - BIND(XK_equal, Equals); - BIND(XK_BackSpace, Backspace); - - BIND(XK_Tab, Tab); - BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); - BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); - BIND(XK_bracketleft, OpenSquareBracket); - BIND(XK_bracketright, CloseSquareBracket); - BIND(XK_backslash, Backslash); - - BIND(XK_Caps_Lock, CapsLock); - BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); - BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); - BIND(XK_semicolon, Semicolon); - BIND(XK_apostrophe, Quote); - BIND(XK_Return, Enter); - - BIND(XK_Shift_L, LeftShift); - BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); - BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); - BIND(XK_comma, Comma); - BIND(XK_period, FullStop); - BIND(XK_slash, ForwardSlash); - BIND(XK_Shift_R, RightShift); - - BIND(XK_Control_L, LeftControl); - BIND(XK_Control_R, RightControl); - BIND(XK_Alt_L, LeftOption); - BIND(XK_Alt_R, RightOption); - BIND(XK_Meta_L, LeftMeta); - BIND(XK_Meta_R, RightMeta); - BIND(XK_space, Space); - - BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); - - BIND(XK_Insert, Insert); - BIND(XK_Delete, Delete); - BIND(XK_Home, Home); - BIND(XK_End, End); - - BIND(XK_Num_Lock, NumLock); - - BIND(XK_KP_Divide, KeypadSlash); - BIND(XK_KP_Multiply, KeypadAsterisk); - BIND(XK_KP_Delete, KeypadDelete); - BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); - BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); - BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); - BIND(XK_KP_0, Keypad0); - BIND(XK_KP_Decimal, KeypadDecimalPoint); - BIND(XK_KP_Equal, KeypadEquals); - - BIND(XK_Help, Help); - } - -#undef BIND - } -#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 -} - bool MainWindow::processEvent(QKeyEvent *event) { if(!machine) return true; - const auto key = keyForEvent(event); + const auto key = keyMapper.keyForEvent(event); if(!key) return true; const bool isPressed = event->type() == QEvent::KeyPress; diff --git a/OSBindings/Qt/mainwindow.h b/OSBindings/Qt/mainwindow.h index 441612213..ec25e1470 100644 --- a/OSBindings/Qt/mainwindow.h +++ b/OSBindings/Qt/mainwindow.h @@ -12,6 +12,7 @@ #include "timer.h" #include "ui_mainwindow.h" #include "functionthread.h" +#include "keyboard.h" #include "../../Analyser/Static/StaticAnalyser.hpp" #include "../../Machines/Utility/MachineForTarget.hpp" @@ -144,7 +145,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat QMenu *inputMenu = nullptr; - std::optional keyForEvent(QKeyEvent *); + KeyboardMapper keyMapper; void register_led(const std::string &) override; void set_led_status(const std::string &, bool) override; From 22b29e77a7ad103b0cc68eca7a2748356aa288be Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 19:06:25 -0400 Subject: [PATCH 21/29] Add keyboard.cpp/h to the Qt project. --- OSBindings/Qt/ClockSignal.pro | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index 65055ae4d..9facd4d00 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -134,10 +134,11 @@ SOURCES += \ $$SRC/Storage/Tape/Formats/*.cpp \ $$SRC/Storage/Tape/Parsers/*.cpp \ \ - main.cpp \ - mainwindow.cpp \ - scantargetwidget.cpp \ - timer.cpp + main.cpp \ + mainwindow.cpp \ + scantargetwidget.cpp \ + timer.cpp \ + keyboard.cpp HEADERS += \ $$SRC/Activity/*.hpp \ @@ -275,12 +276,13 @@ HEADERS += \ $$SRC/Storage/Tape/Formats/*.hpp \ $$SRC/Storage/Tape/Parsers/*.hpp \ \ - audiobuffer.h \ - functionthread.h \ - mainwindow.h \ - scantargetwidget.h \ - settings.h \ - timer.h + audiobuffer.h \ + functionthread.h \ + mainwindow.h \ + scantargetwidget.h \ + settings.h \ + keyboard.h \ + timer.h FORMS += \ mainwindow.ui From 6fe947b8b948eda391d8d6c45df7eac0e4db78e5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 19:17:23 -0400 Subject: [PATCH 22/29] Fix class name, add constructor. --- OSBindings/Qt/keyboard.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp index 09226f93e..a8f843bb5 100644 --- a/OSBindings/Qt/keyboard.cpp +++ b/OSBindings/Qt/keyboard.cpp @@ -35,14 +35,19 @@ #include #endif -std::optional MainWindow::keyForEvent(QKeyEvent *event) { +KeyboardMapper::KeyboardMapper() { +#ifdef HAS_X11 +#endif +} + +std::optional KeyboardMapper::keyForEvent(QKeyEvent *event) { // Workaround for X11: assume PC-esque mapping. #ifdef HAS_X11 if(QGuiApplication::platformName() == QLatin1String("xcb")) { #define BIND(code, key) case code: return Inputs::Keyboard::Key::key; - switch(keysym->nativeVirtualKey()) { + switch(event->nativeVirtualKey()) { default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; BIND(XK_Escape, Escape); From 335e839b314d8a8ca5eff9de18a96fbe6a0e6419 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 21:35:08 -0400 Subject: [PATCH 23/29] Wrangles a single working call to XKeysymToKeycode. --- OSBindings/Qt/ClockSignal.pro | 6 +++++- OSBindings/Qt/keyboard.cpp | 11 +++++++++-- OSBindings/Qt/keyboard.h | 7 +++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index 9facd4d00..b483d10fa 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -12,7 +12,11 @@ CONFIG += object_parallel_to_source INCLUDEPATH += $$[QT_INSTALL_PREFIX]/src/3rdparty/zlib LIBS += -lz -# Add flags (i) to identify that this is a Qt build; and +# If targetting X11, link against that. +QT += x11extras +LIBS += -lX11 + +# Add flags (i) to identify that this is a Qt build; and # (ii) to disable asserts in release builds. DEFINES += TARGET_QT QMAKE_CXXFLAGS_RELEASE += -DNDEBUG diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp index a8f843bb5..038bd3841 100644 --- a/OSBindings/Qt/keyboard.cpp +++ b/OSBindings/Qt/keyboard.cpp @@ -1,4 +1,7 @@ -#include "mainwindow.h" +#include "keyboard.h" + +#include +#include // Qt is the worst. // @@ -32,11 +35,15 @@ #endif #ifdef HAS_X11 +#include + +#include #include #endif KeyboardMapper::KeyboardMapper() { #ifdef HAS_X11 + qDebug() << "F1 " << XKeysymToKeycode(QX11Info::display(), XK_Escape); #endif } @@ -47,7 +54,7 @@ std::optional KeyboardMapper::keyForEvent(QKeyEvent *even if(QGuiApplication::platformName() == QLatin1String("xcb")) { #define BIND(code, key) case code: return Inputs::Keyboard::Key::key; - switch(event->nativeVirtualKey()) { + switch(event->nativeScanCode()) { /* TODO */ default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; BIND(XK_Escape, Escape); diff --git a/OSBindings/Qt/keyboard.h b/OSBindings/Qt/keyboard.h index ba991789f..c0bd1b9b0 100644 --- a/OSBindings/Qt/keyboard.h +++ b/OSBindings/Qt/keyboard.h @@ -1,8 +1,10 @@ #ifndef KEYBOARD_H #define KEYBOARD_H -#include - +#include +#include +#include +#include "../../Inputs/Keyboard.hpp" class KeyboardMapper { public: @@ -10,6 +12,7 @@ class KeyboardMapper { std::optional keyForEvent(QKeyEvent *); private: + std::map keyByKeySym; }; #endif // MAINWINDOW_H From 77071b3c69f8337216cab0fbceb5fc6ac4f9b0a3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 21:41:59 -0400 Subject: [PATCH 24/29] Adds KeySym -> key lookup. --- OSBindings/Qt/keyboard.cpp | 158 ++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 74 deletions(-) diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp index 038bd3841..24ddba8d4 100644 --- a/OSBindings/Qt/keyboard.cpp +++ b/OSBindings/Qt/keyboard.cpp @@ -43,7 +43,87 @@ KeyboardMapper::KeyboardMapper() { #ifdef HAS_X11 - qDebug() << "F1 " << XKeysymToKeycode(QX11Info::display(), XK_Escape); + 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 } @@ -52,79 +132,9 @@ std::optional KeyboardMapper::keyForEvent(QKeyEvent *even #ifdef HAS_X11 if(QGuiApplication::platformName() == QLatin1String("xcb")) { -#define BIND(code, key) case code: return Inputs::Keyboard::Key::key; - - switch(event->nativeScanCode()) { /* TODO */ - default: qDebug() << "Unmapped" << event->nativeScanCode(); return {}; - - BIND(XK_Escape, Escape); - BIND(XK_F1, F1); BIND(XK_F2, F2); BIND(XK_F3, F3); BIND(XK_F4, F4); BIND(XK_F5, F5); - BIND(XK_F6, F6); BIND(XK_F7, F7); BIND(XK_F8, F8); BIND(XK_F9, F9); BIND(XK_F10, F10); - BIND(XK_F11, F11); BIND(XK_F12, F12); - BIND(XK_Sys_Req, PrintScreen); - BIND(XK_Scroll_Lock, ScrollLock); - BIND(XK_Pause, Pause); - - BIND(XK_grave, BackTick); - BIND(XK_1, k1); BIND(XK_2, k2); BIND(XK_3, k3); BIND(XK_4, k4); BIND(XK_5, k5); - BIND(XK_6, k6); BIND(XK_7, k7); BIND(XK_8, k8); BIND(XK_9, k9); BIND(XK_0, k0); - BIND(XK_minus, Hyphen); - BIND(XK_equal, Equals); - BIND(XK_BackSpace, Backspace); - - BIND(XK_Tab, Tab); - BIND(XK_Q, Q); BIND(XK_W, W); BIND(XK_E, E); BIND(XK_R, R); BIND(XK_T, T); - BIND(XK_Y, Y); BIND(XK_U, U); BIND(XK_I, I); BIND(XK_O, O); BIND(XK_P, P); - BIND(XK_bracketleft, OpenSquareBracket); - BIND(XK_bracketright, CloseSquareBracket); - BIND(XK_backslash, Backslash); - - BIND(XK_Caps_Lock, CapsLock); - BIND(XK_A, A); BIND(XK_S, S); BIND(XK_D, D); BIND(XK_F, F); BIND(XK_G, G); - BIND(XK_H, H); BIND(XK_J, J); BIND(XK_K, K); BIND(XK_L, L); - BIND(XK_semicolon, Semicolon); - BIND(XK_apostrophe, Quote); - BIND(XK_Return, Enter); - - BIND(XK_Shift_L, LeftShift); - BIND(XK_Z, Z); BIND(XK_X, X); BIND(XK_C, C); BIND(XK_V, V); - BIND(XK_B, B); BIND(XK_N, N); BIND(XK_M, M); - BIND(XK_comma, Comma); - BIND(XK_period, FullStop); - BIND(XK_slash, ForwardSlash); - BIND(XK_Shift_R, RightShift); - - BIND(XK_Control_L, LeftControl); - BIND(XK_Control_R, RightControl); - BIND(XK_Alt_L, LeftOption); - BIND(XK_Alt_R, RightOption); - BIND(XK_Meta_L, LeftMeta); - BIND(XK_Meta_R, RightMeta); - BIND(XK_space, Space); - - BIND(XK_Left, Left); BIND(XK_Right, Right); BIND(XK_Up, Up); BIND(XK_Down, Down); - - BIND(XK_Insert, Insert); - BIND(XK_Delete, Delete); - BIND(XK_Home, Home); - BIND(XK_End, End); - - BIND(XK_Num_Lock, NumLock); - - BIND(XK_KP_Divide, KeypadSlash); - BIND(XK_KP_Multiply, KeypadAsterisk); - BIND(XK_KP_Delete, KeypadDelete); - BIND(XK_KP_7, Keypad7); BIND(XK_KP_8, Keypad8); BIND(XK_KP_9, Keypad9); BIND(XK_KP_Add, KeypadPlus); - BIND(XK_KP_4, Keypad4); BIND(XK_KP_5, Keypad5); BIND(XK_KP_6, Keypad6); BIND(XK_KP_Subtract, KeypadMinus); - BIND(XK_KP_1, Keypad1); BIND(XK_KP_2, Keypad2); BIND(XK_KP_3, Keypad3); BIND(XK_KP_Enter, KeypadEnter); - BIND(XK_KP_0, Keypad0); - BIND(XK_KP_Decimal, KeypadDecimalPoint); - BIND(XK_KP_Equal, KeypadEquals); - - BIND(XK_Help, Help); - } - -#undef BIND + const auto sym = keyByKeySym.find(event->nativeScancode()); + if(sym == keyByKeySym.end()) return std::nullopt; + return sym->destination; } #endif From 9820591ba45b62a69f9101f40bc5a6c57f6c638d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 21:46:34 -0400 Subject: [PATCH 25/29] Corrects enum references. --- OSBindings/Qt/keyboard.cpp | 109 +++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp index 24ddba8d4..3d190befa 100644 --- a/OSBindings/Qt/keyboard.cpp +++ b/OSBindings/Qt/keyboard.cpp @@ -48,72 +48,73 @@ KeyboardMapper::KeyboardMapper() { Inputs::Keyboard::Key destination; }; + using Key = Inputs::Keyboard::Key; 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_F1, Key::F1}, {XK_F2, Key::F2}, {XK_F3, Key::F3}, {XK_F4, Key::F4}, {XK_F5, Key::F5}, + {XK_F6, Key::F6}, {XK_F7, Key::F7}, {XK_F8, Key::F8}, {XK_F9, Key::F9}, {XK_F10, Key::F10}, + {XK_F11, Key::F11}, {XK_F12, Key::F12}, + {XK_Sys_Req, Key::PrintScreen}, + {XK_Scroll_Lock, Key::ScrollLock}, + {XK_Pause, Key::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_grave, Key::BackTick}, + {XK_1, Key::k1}, {XK_2, Key::k2}, {XK_3, Key::k3}, {XK_4, Key::k4}, {XK_5, Key::k5}, + {XK_6, Key::k6}, {XK_7, Key::k7}, {XK_8, Key::k8}, {XK_9, Key::k9}, {XK_0, Key::k0}, + {XK_minus, Key::Hyphen}, + {XK_equal, Key::Equals}, + {XK_BackSpace, Key::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_Tab, Key::Tab}, + {XK_Q, Key::Q}, {XK_W, Key::W}, {XK_E, Key::E}, {XK_R, Key::R}, {XK_T, Key::T}, + {XK_Y, Key::Y}, {XK_U, Key::U}, {XK_I, Key::I}, {XK_O, Key::O}, {XK_P, Key::P}, + {XK_bracketleft, Key::OpenSquareBracket}, + {XK_bracketright, Key::CloseSquareBracket}, + {XK_backslash, Key::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_Caps_Lock, Key::CapsLock}, + {XK_A, Key::A}, {XK_S, Key::S}, {XK_D, Key::D}, {XK_F, Key::F}, {XK_G, Key::G}, + {XK_H, Key::H}, {XK_J, Key::J}, {XK_K, Key::K}, {XK_L, Key::L}, + {XK_semicolon, Key::Semicolon}, + {XK_apostrophe, Key::Quote}, + {XK_Return, Key::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_Shift_L, Key::LeftShift}, + {XK_Z, Key::Z}, {XK_X, Key::X}, {XK_C, Key::C}, {XK_V, Key::V}, + {XK_B, Key::B}, {XK_N, Key::N}, {XK_M, Key::M}, + {XK_comma, Key::Comma}, + {XK_period, Key::FullStop}, + {XK_slash, Key::ForwardSlash}, + {XK_Shift_R, Key::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_Control_L, Key::LeftControl}, + {XK_Control_R, Key::RightControl}, + {XK_Alt_L, Key::LeftOption}, + {XK_Alt_R, Key::RightOption}, + {XK_Meta_L, Key::LeftMeta}, + {XK_Meta_R, Key::RightMeta}, + {XK_space, Key::Space}, - {XK_Left, Left}, {XK_Right, Right}, {XK_Up, Up}, {XK_Down, Down}, + {XK_Left, Key::Left}, {XK_Right, Key::Right}, {XK_Up, Key::Up}, {XK_Down, Key::Down}, - {XK_Insert, Insert}, - {XK_Delete, Delete}, - {XK_Home, Home}, - {XK_End, End}, + {XK_Insert, Key::Insert}, + {XK_Delete, Key::Delete}, + {XK_Home, Key::Home}, + {XK_End, Key::End}, - {XK_Num_Lock, NumLock}, + {XK_Num_Lock, Key::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_KP_Divide, Key::KeypadSlash}, + {XK_KP_Multiply, Key::KeypadAsterisk}, + {XK_KP_Delete, Key::KeypadDelete}, + {XK_KP_7, Key::Keypad7}, {XK_KP_8, Key::Keypad8}, {XK_KP_9, Key::Keypad9}, {XK_KP_Add, Key::KeypadPlus}, + {XK_KP_4, Key::Keypad4}, {XK_KP_5, Key::Keypad5}, {XK_KP_6, Key::Keypad6}, {XK_KP_Subtract, Key::KeypadMinus}, + {XK_KP_1, Key::Keypad1}, {XK_KP_2, Key::Keypad2}, {XK_KP_3, Key::Keypad3}, {XK_KP_Enter, Key::KeypadEnter}, + {XK_KP_0, Key::Keypad0}, + {XK_KP_Decimal, Key::KeypadDecimalPoint}, + {XK_KP_Equal, Key::KeypadEquals}, - {XK_Help, Help}, + {XK_Help, Key::Help}, {0} }; From 1bae70bcf8f8f70f8f069d22ff5fb54f3df29b3e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 21:49:01 -0400 Subject: [PATCH 26/29] Correct capitalisation. --- OSBindings/Qt/keyboard.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp index 3d190befa..b8e1d4a6e 100644 --- a/OSBindings/Qt/keyboard.cpp +++ b/OSBindings/Qt/keyboard.cpp @@ -50,7 +50,7 @@ KeyboardMapper::KeyboardMapper() { using Key = Inputs::Keyboard::Key; constexpr DesiredMapping mappings[] = { - {XK_Escape, Escape}, + {XK_Escape, Key::Escape}, {XK_F1, Key::F1}, {XK_F2, Key::F2}, {XK_F3, Key::F3}, {XK_F4, Key::F4}, {XK_F5, Key::F5}, {XK_F6, Key::F6}, {XK_F7, Key::F7}, {XK_F8, Key::F8}, {XK_F9, Key::F9}, {XK_F10, Key::F10}, {XK_F11, Key::F11}, {XK_F12, Key::F12}, @@ -133,9 +133,9 @@ std::optional KeyboardMapper::keyForEvent(QKeyEvent *even #ifdef HAS_X11 if(QGuiApplication::platformName() == QLatin1String("xcb")) { - const auto sym = keyByKeySym.find(event->nativeScancode()); + const auto sym = keyByKeySym.find(event->nativeScanCode()); if(sym == keyByKeySym.end()) return std::nullopt; - return sym->destination; + return sym->second; } #endif From 6cb23ec5be60f68d6a82df629243459fb547c61e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 21:58:54 -0400 Subject: [PATCH 27/29] Tidy up and comment. --- OSBindings/Qt/keyboard.cpp | 135 +++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 66 deletions(-) diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp index b8e1d4a6e..45210e3fd 100644 --- a/OSBindings/Qt/keyboard.cpp +++ b/OSBindings/Qt/keyboard.cpp @@ -5,7 +5,7 @@ // 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. +// Assume your keyboard has a key labelled both . and >, as on US and UK keyboards. Call it the dot key. // Perform the following: // 1. press dot key; // 2. press shift key; @@ -27,8 +27,7 @@ // // 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. +// You can't. Qt is the worst. SDL doesn't have this problem, including in X11, but I don't want the non-Qt dependency. #ifdef Q_OS_LINUX #define HAS_X11 @@ -50,92 +49,96 @@ KeyboardMapper::KeyboardMapper() { using Key = Inputs::Keyboard::Key; constexpr DesiredMapping mappings[] = { - {XK_Escape, Key::Escape}, - {XK_F1, Key::F1}, {XK_F2, Key::F2}, {XK_F3, Key::F3}, {XK_F4, Key::F4}, {XK_F5, Key::F5}, - {XK_F6, Key::F6}, {XK_F7, Key::F7}, {XK_F8, Key::F8}, {XK_F9, Key::F9}, {XK_F10, Key::F10}, - {XK_F11, Key::F11}, {XK_F12, Key::F12}, - {XK_Sys_Req, Key::PrintScreen}, - {XK_Scroll_Lock, Key::ScrollLock}, - {XK_Pause, Key::Pause}, + {XK_Escape, Key::Escape}, + {XK_F1, Key::F1}, {XK_F2, Key::F2}, {XK_F3, Key::F3}, {XK_F4, Key::F4}, {XK_F5, Key::F5}, + {XK_F6, Key::F6}, {XK_F7, Key::F7}, {XK_F8, Key::F8}, {XK_F9, Key::F9}, {XK_F10, Key::F10}, + {XK_F11, Key::F11}, {XK_F12, Key::F12}, + {XK_Sys_Req, Key::PrintScreen}, + {XK_Scroll_Lock, Key::ScrollLock}, + {XK_Pause, Key::Pause}, - {XK_grave, Key::BackTick}, - {XK_1, Key::k1}, {XK_2, Key::k2}, {XK_3, Key::k3}, {XK_4, Key::k4}, {XK_5, Key::k5}, - {XK_6, Key::k6}, {XK_7, Key::k7}, {XK_8, Key::k8}, {XK_9, Key::k9}, {XK_0, Key::k0}, - {XK_minus, Key::Hyphen}, - {XK_equal, Key::Equals}, - {XK_BackSpace, Key::Backspace}, + {XK_grave, Key::BackTick}, + {XK_1, Key::k1}, {XK_2, Key::k2}, {XK_3, Key::k3}, {XK_4, Key::k4}, {XK_5, Key::k5}, + {XK_6, Key::k6}, {XK_7, Key::k7}, {XK_8, Key::k8}, {XK_9, Key::k9}, {XK_0, Key::k0}, + {XK_minus, Key::Hyphen}, + {XK_equal, Key::Equals}, + {XK_BackSpace, Key::Backspace}, - {XK_Tab, Key::Tab}, - {XK_Q, Key::Q}, {XK_W, Key::W}, {XK_E, Key::E}, {XK_R, Key::R}, {XK_T, Key::T}, - {XK_Y, Key::Y}, {XK_U, Key::U}, {XK_I, Key::I}, {XK_O, Key::O}, {XK_P, Key::P}, - {XK_bracketleft, Key::OpenSquareBracket}, - {XK_bracketright, Key::CloseSquareBracket}, - {XK_backslash, Key::Backslash}, + {XK_Tab, Key::Tab}, + {XK_Q, Key::Q}, {XK_W, Key::W}, {XK_E, Key::E}, {XK_R, Key::R}, {XK_T, Key::T}, + {XK_Y, Key::Y}, {XK_U, Key::U}, {XK_I, Key::I}, {XK_O, Key::O}, {XK_P, Key::P}, + {XK_bracketleft, Key::OpenSquareBracket}, + {XK_bracketright, Key::CloseSquareBracket}, + {XK_backslash, Key::Backslash}, - {XK_Caps_Lock, Key::CapsLock}, - {XK_A, Key::A}, {XK_S, Key::S}, {XK_D, Key::D}, {XK_F, Key::F}, {XK_G, Key::G}, - {XK_H, Key::H}, {XK_J, Key::J}, {XK_K, Key::K}, {XK_L, Key::L}, - {XK_semicolon, Key::Semicolon}, - {XK_apostrophe, Key::Quote}, - {XK_Return, Key::Enter}, + {XK_Caps_Lock, Key::CapsLock}, + {XK_A, Key::A}, {XK_S, Key::S}, {XK_D, Key::D}, {XK_F, Key::F}, {XK_G, Key::G}, + {XK_H, Key::H}, {XK_J, Key::J}, {XK_K, Key::K}, {XK_L, Key::L}, + {XK_semicolon, Key::Semicolon}, + {XK_apostrophe, Key::Quote}, + {XK_Return, Key::Enter}, - {XK_Shift_L, Key::LeftShift}, - {XK_Z, Key::Z}, {XK_X, Key::X}, {XK_C, Key::C}, {XK_V, Key::V}, - {XK_B, Key::B}, {XK_N, Key::N}, {XK_M, Key::M}, - {XK_comma, Key::Comma}, - {XK_period, Key::FullStop}, - {XK_slash, Key::ForwardSlash}, - {XK_Shift_R, Key::RightShift}, + {XK_Shift_L, Key::LeftShift}, + {XK_Z, Key::Z}, {XK_X, Key::X}, {XK_C, Key::C}, {XK_V, Key::V}, + {XK_B, Key::B}, {XK_N, Key::N}, {XK_M, Key::M}, + {XK_comma, Key::Comma}, + {XK_period, Key::FullStop}, + {XK_slash, Key::ForwardSlash}, + {XK_Shift_R, Key::RightShift}, - {XK_Control_L, Key::LeftControl}, - {XK_Control_R, Key::RightControl}, - {XK_Alt_L, Key::LeftOption}, - {XK_Alt_R, Key::RightOption}, - {XK_Meta_L, Key::LeftMeta}, - {XK_Meta_R, Key::RightMeta}, - {XK_space, Key::Space}, + {XK_Control_L, Key::LeftControl}, + {XK_Control_R, Key::RightControl}, + {XK_Alt_L, Key::LeftOption}, + {XK_Alt_R, Key::RightOption}, + {XK_Meta_L, Key::LeftMeta}, + {XK_Meta_R, Key::RightMeta}, + {XK_space, Key::Space}, - {XK_Left, Key::Left}, {XK_Right, Key::Right}, {XK_Up, Key::Up}, {XK_Down, Key::Down}, + {XK_Left, Key::Left}, {XK_Right, Key::Right}, {XK_Up, Key::Up}, {XK_Down, Key::Down}, - {XK_Insert, Key::Insert}, - {XK_Delete, Key::Delete}, - {XK_Home, Key::Home}, - {XK_End, Key::End}, + {XK_Insert, Key::Insert}, + {XK_Delete, Key::Delete}, + {XK_Home, Key::Home}, + {XK_End, Key::End}, - {XK_Num_Lock, Key::NumLock}, + {XK_Num_Lock, Key::NumLock}, - {XK_KP_Divide, Key::KeypadSlash}, - {XK_KP_Multiply, Key::KeypadAsterisk}, - {XK_KP_Delete, Key::KeypadDelete}, - {XK_KP_7, Key::Keypad7}, {XK_KP_8, Key::Keypad8}, {XK_KP_9, Key::Keypad9}, {XK_KP_Add, Key::KeypadPlus}, - {XK_KP_4, Key::Keypad4}, {XK_KP_5, Key::Keypad5}, {XK_KP_6, Key::Keypad6}, {XK_KP_Subtract, Key::KeypadMinus}, - {XK_KP_1, Key::Keypad1}, {XK_KP_2, Key::Keypad2}, {XK_KP_3, Key::Keypad3}, {XK_KP_Enter, Key::KeypadEnter}, - {XK_KP_0, Key::Keypad0}, - {XK_KP_Decimal, Key::KeypadDecimalPoint}, - {XK_KP_Equal, Key::KeypadEquals}, + {XK_KP_Divide, Key::KeypadSlash}, + {XK_KP_Multiply, Key::KeypadAsterisk}, + {XK_KP_Delete, Key::KeypadDelete}, + {XK_KP_7, Key::Keypad7}, {XK_KP_8, Key::Keypad8}, {XK_KP_9, Key::Keypad9}, {XK_KP_Add, Key::KeypadPlus}, + {XK_KP_4, Key::Keypad4}, {XK_KP_5, Key::Keypad5}, {XK_KP_6, Key::Keypad6}, {XK_KP_Subtract, Key::KeypadMinus}, + {XK_KP_1, Key::Keypad1}, {XK_KP_2, Key::Keypad2}, {XK_KP_3, Key::Keypad3}, {XK_KP_Enter, Key::KeypadEnter}, + {XK_KP_0, Key::Keypad0}, + {XK_KP_Decimal, Key::KeypadDecimalPoint}, + {XK_KP_Equal, Key::KeypadEquals}, - {XK_Help, Key::Help}, + {XK_Help, Key::Help}, - {0} + {0} }; + // Extra level of nonsense here: + // + // (1) assume a PC-esque keyboard, with a close-to-US/UK layout; + // (2) from there, use any of the X11 KeySyms I'd expect to be achievable from each physical key to + // look up the X11 KeyCode; + // (3) henceforth, map from X11 KeyCode to the Inputs::Keyboard::Key. const DesiredMapping *mapping = mappings; while(mapping->source != 0) { - const auto sym = XKeysymToKeycode(QX11Info::display(), mapping->source); - keyByKeySym[sym] = mapping->destination; + const auto code = XKeysymToKeycode(QX11Info::display(), mapping->source); + keyByKeySym[code] = mapping->destination; ++mapping; } #endif } std::optional 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->second; + const auto key = keyByKeySym.find(event->nativeScanCode()); + if(key == keyByKeySym.end()) return std::nullopt; + return key->second; } #endif From 5f6c08b7e012c2ba99dfa8416913e782708747fb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 22:00:50 -0400 Subject: [PATCH 28/29] Avoid partial struct instantiation. --- OSBindings/Qt/keyboard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OSBindings/Qt/keyboard.cpp b/OSBindings/Qt/keyboard.cpp index 45210e3fd..48fc00c4b 100644 --- a/OSBindings/Qt/keyboard.cpp +++ b/OSBindings/Qt/keyboard.cpp @@ -43,7 +43,7 @@ KeyboardMapper::KeyboardMapper() { #ifdef HAS_X11 struct DesiredMapping { - KeySym source; + KeySym source = 0; Inputs::Keyboard::Key destination; }; @@ -115,7 +115,7 @@ KeyboardMapper::KeyboardMapper() { {XK_Help, Key::Help}, - {0} + {} }; // Extra level of nonsense here: From ef50967793be7256db7e00232b640968b4dcfd2b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 5 May 2021 22:17:24 -0400 Subject: [PATCH 29/29] Limit X11 linkage to Linux. --- OSBindings/Qt/ClockSignal.pro | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index b483d10fa..c31d162d6 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -13,8 +13,10 @@ INCLUDEPATH += $$[QT_INSTALL_PREFIX]/src/3rdparty/zlib LIBS += -lz # If targetting X11, link against that. -QT += x11extras -LIBS += -lX11 +linux { + QT += x11extras + LIBS += -lX11 +} # Add flags (i) to identify that this is a Qt build; and # (ii) to disable asserts in release builds.