diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index dd3bc6f79..614da7a00 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -365,7 +365,7 @@ endif() if (UNIX AND NOT APPLE AND NOT HAIKU) find_package(X11 REQUIRED) target_link_libraries(ui PRIVATE X11::X11 X11::Xi) - target_sources(ui PRIVATE xinput2_mouse.cpp) + target_sources(ui PRIVATE evdev_keyboard.cpp xinput2_mouse.cpp) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBEVDEV IMPORTED_TARGET libevdev) if (LIBEVDEV_FOUND) diff --git a/src/qt/evdev_keyboard.cpp b/src/qt/evdev_keyboard.cpp new file mode 100644 index 000000000..4eba75e26 --- /dev/null +++ b/src/qt/evdev_keyboard.cpp @@ -0,0 +1,134 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * evdev keyboard input module. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +#include +#include + +static std::unordered_map evdev_keycodes{ + {99, 0x54}, /* SYSRQ */ + {86, 0x56}, /* 102ND */ + {87, 0x57}, /* F11 */ + {88, 0x58}, /* F12 */ + {117, 0x59}, /* KPEQUAL */ + {95, 0x5c}, /* KPJPCOMMA */ + {183, 0x5d}, /* F13 */ + {184, 0x5e}, /* F14 */ + {185, 0x5f}, /* F15 */ + {93, 0x70}, /* KATAKANAHIRAGANA */ + {89, 0x73}, /* RO */ + {85, 0x76}, /* ZENKAKUHANKAKU */ + {91, 0x77}, /* HIRAGANA */ + {90, 0x78}, /* KATAKANA */ + {92, 0x79}, /* HENKAN */ + {94, 0x7b}, /* MUHENKAN */ + {124, 0x7d}, /* YEN */ + {121, 0x7e}, /* KPCOMMA */ + + /* Multimedia keys. Guideline is to try and follow the Microsoft standard, then + fill in remaining scancodes with OEM-specific keys for redundancy sake. Keys + marked with # are not translated into evdev codes by the standard atkbd driver. */ + {117, 0x59}, /* Num= */ + {418, 0x6a}, /* ZOOMIN# => Logitech */ + {420, 0x6b}, /* ZOOMRESET# => Logitech */ + {223, 0x6d}, /* CANCEL# => Logitech */ + {132, 0x101}, /* # Logitech Task Select */ + {148, 0x102}, /* PROG1# => Samsung */ + {149, 0x103}, /* PROG2# => Samsung */ + {419, 0x104}, /* ZOOMOUT# => Logitech */ + {144, 0x105}, /* FILE# => Messenger/Files */ + {216, 0x105}, /* CHAT# => Messenger/Files */ + {430, 0x105}, /* MESSENGER# */ + {182, 0x107}, /* REDO# */ + {131, 0x108}, /* UNDO# */ + {135, 0x10a}, /* PASTE# */ + {177, 0x10b}, /* SCROLLUP# => normal speed */ + {165, 0x110}, /* PREVIOUSSONG */ + {136, 0x112}, /* FIND# => Logitech */ + {421, 0x113}, /* WORDPROCESSOR# => Word */ + {423, 0x114}, /* SPREADSHEET# => Excel */ + {397, 0x115}, /* CALENDAR# */ + {433, 0x116}, /* LOGOFF# */ + {137, 0x117}, /* CUT# */ + {133, 0x118}, /* COPY# */ + {163, 0x119}, /* NEXTSONG */ + {154, 0x11e}, /* CYCLEWINDOWS => Application Right (no left counterpart) */ + {113, 0x120}, /* MUTE */ + {140, 0x121}, /* CALC */ + {164, 0x122}, /* PLAYPAUSE */ + {432, 0x123}, /* SPELLCHECK# */ + {166, 0x124}, /* STOPCD */ + {139, 0x126}, /* MENU# => Shortcut/Menu/Help for a few OEMs */ + {114, 0x12e}, /* VOL- */ + {160, 0x12f}, /* CLOSECD# => Logitech Eject */ + {161, 0x12f}, /* EJECTCD# => Logitech */ + {162, 0x12f}, /* EJECTCLOSECD# => Logitech */ + {115, 0x130}, /* VOL+ */ + {150, 0x132}, /* WWW# */ + {172, 0x132}, /* HOMEPAGE */ + {634, 0x137}, /* SELECTIVE_SCREENSHOT# => SysRq */ + {138, 0x13b}, /* HELP# */ + {213, 0x13c}, /* SOUND# => My Music/Office Home */ + {360, 0x13c}, /* VENDOR# => My Music/Office Home */ + {204, 0x13d}, /* DASHBOARD# => Task Pane */ + {181, 0x13e}, /* NEW# */ + {134, 0x13f}, /* OPEN# */ + {206, 0x140}, /* CLOSE# */ + {232, 0x141}, /* REPLY# */ + {233, 0x142}, /* FORWARDMAIL# */ + {231, 0x143}, /* SEND# */ + {151, 0x144}, /* MSDOS# */ + {112, 0x14c}, /* MACRO */ + {179, 0x14c}, /* KPLEFTPAREN# */ + {118, 0x14e}, /* KPPLUSMINUS */ + {235, 0x155}, /* DOCUMENTS# => Logitech */ + {234, 0x157}, /* SAVE# */ + {210, 0x158}, /* PRINT# */ + {116, 0x15e}, /* POWER */ + {142, 0x15f}, /* SLEEP */ + {143, 0x163}, /* WAKEUP */ + {180, 0x164}, /* KPRIGHTPAREN# */ + {212, 0x164}, /* CAMERA# => My Pictures */ + {217, 0x165}, /* SEARCH */ + {156, 0x166}, /* BOOKMARKS => Favorites */ + {364, 0x166}, /* FAVORITES# */ + {173, 0x167}, /* REFRESH */ + {128, 0x168}, /* STOP */ + {159, 0x169}, /* FORWARD */ + {158, 0x16a}, /* BACK */ + {157, 0x16b}, /* COMPUTER */ + {155, 0x16c}, /* MAIL */ + {215, 0x16c}, /* EMAIL# */ + {226, 0x16d}, /* MEDIA */ + {167, 0x178}, /* RECORD# => Logitech */ + {152, 0x17a}, /* COFFEE/SCREENLOCK# */ + {178, 0x18b}, /* SCROLLDOWN# => normal speed */ +}; + +uint16_t +evdev_translate(uint32_t keycode) +{ + /* "for 1-83 (0x01-0x53) scancode equals keycode" */ + auto ret = (keycode <= 0x53) ? keycode : evdev_keycodes[keycode]; + + if (!ret) + qWarning() << "Evdev Keyboard: Unknown key" << keycode; +#if 0 + else + qInfo() << "Evdev Keyboard: Key" << keycode << "scancode" << Qt::hex << ret; +#endif + + return ret; +} diff --git a/src/qt/evdev_keyboard.hpp b/src/qt/evdev_keyboard.hpp new file mode 100644 index 000000000..5efe5958d --- /dev/null +++ b/src/qt/evdev_keyboard.hpp @@ -0,0 +1,20 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for evdev keyboard input module. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +#ifndef EVDEV_KEYBOARD_HPP +#define EVDEV_KEYBOARD_HPP +uint16_t evdev_translate(uint32_t keycode); +#endif diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index a8957b476..8e8208dcc 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -96,6 +96,9 @@ extern int qt_nvr_save(void); #include "qt_util.hpp" #if defined __unix__ && !defined __HAIKU__ +# ifndef Q_OS_MACOS +# include "evdev_keyboard.hpp" +# endif # ifdef XKBCOMMON # include "xkbcommon_keyboard.hpp" # ifdef XKBCOMMON_X11 @@ -1378,27 +1381,6 @@ std::array darwin_to_xt { }; #endif -#if defined(__unix__) && !defined(__HAIKU__) -static std::unordered_map evdev_to_xt = { - {96, 0x11C}, - { 97, 0x11D}, - { 98, 0x135}, - { 99, 0x71 }, - { 100, 0x138}, - { 101, 0x1C }, - { 102, 0x147}, - { 103, 0x148}, - { 104, 0x149}, - { 105, 0x14B}, - { 106, 0x14D}, - { 107, 0x14F}, - { 108, 0x150}, - { 109, 0x151}, - { 110, 0x152}, - { 111, 0x153} -}; -#endif - #ifdef __HAIKU__ static std::unordered_map be_to_xt = { {0x01, 0x01 }, @@ -1528,28 +1510,25 @@ x11_keycode_to_keysym(uint32_t keycode) if (xkbcommon_keymap) { finalkeycode = xkbcommon_translate(keycode); } else +# endif +# ifdef EVDEV_KEYBOARD_HPP + if (QApplication::platformName().contains("eglfs")) { + finalkeycode = evdev_translate(keycode); + } else # endif { static Display *x11display = nullptr; - if (QApplication::platformName().contains("eglfs")) { - keycode -= 8; - if (keycode <= 88) - finalkeycode = keycode; - else - finalkeycode = evdev_to_xt[keycode]; - } else { - if (QApplication::platformName().contains("wayland")) { + if (QApplication::platformName().contains("wayland")) { + selected_keycode = x11_to_xt_2; + } else if (!x11display) { + x11display = XOpenDisplay(nullptr); + if (XKeysymToKeycode(x11display, XK_Home) == 110) { selected_keycode = x11_to_xt_2; - } else if (!x11display) { - x11display = XOpenDisplay(nullptr); - if (XKeysymToKeycode(x11display, XK_Home) == 110) { - selected_keycode = x11_to_xt_2; - } else if (XKeysymToKeycode(x11display, XK_Home) == 69) { - selected_keycode = x11_to_xt_vnc; - } + } else if (XKeysymToKeycode(x11display, XK_Home) == 69) { + selected_keycode = x11_to_xt_vnc; } - finalkeycode = selected_keycode[keycode]; } + finalkeycode = selected_keycode[keycode]; } #endif /* Special case for Ctrl+Pause. */ diff --git a/src/qt/xkbcommon_keyboard.cpp b/src/qt/xkbcommon_keyboard.cpp index e0560117b..953008453 100644 --- a/src/qt/xkbcommon_keyboard.cpp +++ b/src/qt/xkbcommon_keyboard.cpp @@ -20,10 +20,12 @@ extern "C" { #include #include +#include "evdev_keyboard.hpp" -#define IS_HEX_DIGIT(c) ((((c) >= '0') && ((c) <= '9')) || (((c) >= 'A') && ((c) <= 'F')) || (((c) >= 'a') && ((c) <= 'f'))) +#define IS_DEC_DIGIT(c) (((c) >= '0') && ((c) <= '9')) +#define IS_HEX_DIGIT(c) (IS_DEC_DIGIT(c) || (((c) >= 'A') && ((c) <= 'F')) || (((c) >= 'a') && ((c) <= 'f'))) -std::unordered_map xkb_keycodes{ +static std::unordered_map xkb_keycodes{ {"ESC", 0x01}, {"AE01", 0x02}, {"AE02", 0x03}, @@ -100,7 +102,6 @@ std::unordered_map xkb_keycodes{ {"NMLK", 0x45}, {"SCLK", 0x46}, - {"FK14", 0x46}, /* F14 => Scroll Lock */ {"KP7", 0x47}, {"KP8", 0x48}, {"KP9", 0x49}, @@ -118,19 +119,22 @@ std::unordered_map xkb_keycodes{ {"LSGT", 0x56}, {"FK11", 0x57}, {"FK12", 0x58}, + {"FK13", 0x5d}, + {"FK14", 0x5e}, + {"FK15", 0x5f}, /* Japanese keys. */ - {"HZTG", 0x29}, /* hankaku-zenkaku toggle => ~ */ - {"HKTG", 0x70}, /* hiragana-katakana toggle... */ - {"HIRA", 0x70}, /* ...and individual keys */ - {"KATA", 0x70}, + {"JPCM", 0x5c}, /* evdev KPJPCOMMA */ + {"HKTG", 0x70}, /* hiragana-katakana toggle */ {"AB11", 0x73}, /* \_ and Brazilian /? */ + {"HZTG", 0x76}, /* hankaku-zenkaku toggle */ + {"HIRA", 0x77}, + {"KATA", 0x78}, {"HENK", 0x79}, {"MUHE", 0x7b}, {"AE13", 0x7d}, /* \| */ {"KPPT", 0x7e}, /* Brazilian Num. */ {"I06", 0x7e}, /* alias of KPPT on keycodes/xfree86 (i.e. X11 forwarding) */ - {"I129", 0x7e}, /* another alias: evdev KPCOMMA */ /* Korean keys. */ {"HJCV", 0xf1}, /* hancha toggle */ @@ -141,11 +145,9 @@ std::unordered_map xkb_keycodes{ {"KPDV", 0x135}, {"PRSC", 0x137}, {"SYRQ", 0x137}, - {"FK13", 0x137}, /* F13 => SysRq */ {"RALT", 0x138}, {"PAUS", 0x145}, {"BRK", 0x145}, - {"FK15", 0x145}, /* F15 => Pause */ {"HOME", 0x147}, {"UP", 0x148}, {"PGUP", 0x149}, @@ -164,84 +166,21 @@ std::unordered_map xkb_keycodes{ {"MENU", 0x15d}, {"COMP", 0x15d}, /* Compose as Menu */ - /* Multimedia keys, using Linux evdev-specific keycodes where required. Guideline is to try - and follow the Microsoft standard, then fill in some OEM-specific keys for redundancy sake. - Keys marked with # are not translated into evdev codes by the standard atkbd driver. */ + /* Multimedia keys. Same notes as evdev_keyboard apply here. */ {"KPEQ", 0x59}, /* Num= */ - {"I426", 0x6a}, /* ZOOMIN# => Logitech */ - {"I428", 0x6b}, /* ZOOMRESET# => Logitech */ - {"I231", 0x6d}, /* CANCEL# => Logitech */ {"FRNT", 0x101}, /* # Logitech Task Select */ - {"I156", 0x102}, /* PROG1# => Samsung */ - {"I157", 0x103}, /* PROG2# => Samsung */ - {"I427", 0x104}, /* ZOOMOUT# => Logitech */ - {"I152", 0x105}, /* FILE# => Messenger/Files */ - {"I224", 0x105}, /* CHAT# => Messenger/Files */ - {"I438", 0x105}, /* MESSENGER# */ - {"I190", 0x107}, /* REDO# */ {"UNDO", 0x108}, /* # */ {"PAST", 0x10a}, /* # Paste */ - {"I185", 0x10b}, /* SCROLLUP# => normal speed */ - {"I173", 0x110}, /* PREVIOUSSONG */ {"FIND", 0x112}, /* # Logitech */ - {"I429", 0x113}, /* WORDPROCESSOR# => Word */ - {"I431", 0x114}, /* SPREADSHEET# => Excel */ - {"I405", 0x115}, /* CALENDAR# */ - {"I441", 0x116}, /* LOGOFF# */ {"CUT", 0x117}, /* # */ {"COPY", 0x118}, /* # */ - {"I171", 0x119}, /* NEXTSONG */ - {"I162", 0x11e}, /* CYCLEWINDOWS => Application Right (no left counterpart) */ {"MUTE", 0x120}, - {"I148", 0x121}, /* CALC */ - {"I172", 0x122}, /* PLAYPAUSE */ - {"I440", 0x123}, /* SPELLCHECK# */ - {"I174", 0x124}, /* STOPCD */ - {"I147", 0x126}, /* MENU# => Shortcut/Menu/Help for a few OEMs */ {"VOL-", 0x12e}, - {"I168", 0x12f}, /* CLOSECD# => Logitech Eject */ - {"I169", 0x12f}, /* EJECTCD# => Logitech */ - {"I170", 0x12f}, /* EJECTCLOSECD# => Logitech */ {"VOL+", 0x130}, - {"I158", 0x132}, /* WWW# */ - {"I180", 0x132}, /* HOMEPAGE */ - {"I642", 0x137}, /* SELECTIVE_SCREENSHOT# => SysRq */ - {"HELP", 0x13b}, /* # */ - {"I221", 0x13c}, /* SOUND# => My Music/Office Home */ - {"I368", 0x13c}, /* VENDOR# => My Music/Office Home */ - {"I212", 0x13d}, /* DASHBOARD# => Task Pane */ - {"I189", 0x13e}, /* NEW# */ - {"OPEN", 0x13f}, /* # */ - {"I214", 0x140}, /* CLOSE# */ - {"I240", 0x141}, /* REPLY# */ - {"I241", 0x142}, /* FORWARDMAIL# */ - {"I239", 0x143}, /* SEND# */ - {"I159", 0x144}, /* MSDOS# */ - {"I120", 0x14c}, /* MACRO */ - {"I187", 0x14c}, /* KPLEFTPAREN# */ - {"I126", 0x14e}, /* KPPLUSMINUS */ - {"I243", 0x155}, /* DOCUMENTS# => Logitech */ - {"I242", 0x157}, /* SAVE# */ - {"I218", 0x158}, /* PRINT# */ + {"HELP", 0x13b}, + {"OPEN", 0x13f}, {"POWR", 0x15e}, - {"I150", 0x15f}, /* SLEEP */ - {"I151", 0x163}, /* WAKEUP */ - {"I188", 0x164}, /* KPRIGHTPAREN# */ - {"I220", 0x164}, /* CAMERA# => My Pictures */ - {"I225", 0x165}, /* SEARCH */ - {"I164", 0x166}, /* BOOKMARKS => Favorites */ - {"I372", 0x166}, /* FAVORITES# */ - {"I181", 0x167}, /* REFRESH */ {"STOP", 0x168}, - {"I167", 0x169}, /* FORWARD */ - {"I166", 0x16a}, /* BACK */ - {"I165", 0x16b}, /* COMPUTER */ - {"I163", 0x16c}, /* MAIL */ - {"I223", 0x16c}, /* EMAIL# */ - {"I234", 0x16d}, /* MEDIA */ - {"I175", 0x178}, /* RECORD# => Logitech */ - {"I160", 0x17a}, /* COFFEE/SCREENLOCK# */ - {"I186", 0x18b}, /* SCROLLDOWN# => normal speed */ }; struct xkb_keymap *xkbcommon_keymap = nullptr; @@ -272,6 +211,10 @@ xkbcommon_translate(uint32_t keycode) if (!ret && (key_name_s.length() == 3) && (key_name_s[0] == 'I') && IS_HEX_DIGIT(key_name_s[1]) && IS_HEX_DIGIT(key_name_s[2])) ret = 0x100 | stoi(key_name_s.substr(1), nullptr, 16); + /* Translate unnamed evdev-specific keycodes. */ + if (!ret && (key_name_s.length() >= 2) && (key_name_s[0] == 'I') && IS_DEC_DIGIT(key_name_s[1])) + ret = evdev_translate(stoi(key_name_s.substr(1)) - 8); + if (!ret) qWarning() << "XKB Keyboard: Unknown key" << Qt::hex << keycode << QString::fromStdString(key_name_s); #if 0