diff --git a/.ci/AppImageBuilder.yml b/.ci/AppImageBuilder.yml index 22967e2f4..39997a7a8 100644 --- a/.ci/AppImageBuilder.yml +++ b/.ci/AppImageBuilder.yml @@ -68,6 +68,7 @@ AppDir: - libxcb-shm0 # if QT:BOOL=ON - libxcb-xfixes0 # if QT:BOOL=ON - libxkbcommon-x11-0 # if QT:BOOL=ON + - qtwayland5 # if QT:BOOL=ON - zlib1g files: exclude: diff --git a/.ci/build.sh b/.ci/build.sh index 67320c2e2..a61cdc7f9 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -629,7 +629,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(ENV{PKG_CONFIG_PATH} "") -set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig") +set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig:/usr/share/pkgconfig") include("$(realpath "$toolchain_file")") EOF @@ -948,7 +948,6 @@ else -S "$prefix" -B "$prefix_build" || exit 99 cmake --build "$prefix_build" -j$(nproc) || exit 99 cmake --install "$prefix_build" || exit 99 - cp -p "$cwd_root/archive_tmp/usr/bin/fluidsynth" fluidsynth # Build SDL2 for joystick and FAudio support, with most components # disabled to remove the dependencies on PulseAudio and libdrm. diff --git a/.gitignore b/.gitignore index 0a21bf105..acb28baf7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,8 @@ Makefile *.tar.* *.AppImage /appimage-builder-cache +/appimage-build +/AppImageBuilder-generated.yml # Visual Studio Code /.vs diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 0cce392a9..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) @@ -402,6 +402,9 @@ if (UNIX AND NOT APPLE AND NOT HAIKU) ecm_add_wayland_client_protocol(WL_SOURCE_VAR PROTOCOL ${CMAKE_SOURCE_DIR}/wl_protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1) target_include_directories(ui PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${Qt${QT_MAJOR}Gui_PRIVATE_INCLUDE_DIRS}) target_sources(ui PRIVATE ${WL_SOURCE_VAR} wl_mouse.cpp) + if (XKBCOMMON_FOUND) + target_sources(ui PRIVATE xkbcommon_wl_keyboard.cpp) + endif() target_compile_definitions(ui PRIVATE WAYLAND) endif() endif() diff --git a/src/qt/evdev_keyboard.cpp b/src/qt/evdev_keyboard.cpp new file mode 100644 index 000000000..3e66dc1ed --- /dev/null +++ b/src/qt/evdev_keyboard.cpp @@ -0,0 +1,162 @@ +/* + * 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 */ + {183, 0x5d}, /* F13 */ + {184, 0x5e}, /* F14 */ + {185, 0x5f}, /* F15 */ + + /* Japanese keys. */ + {95, 0x5c}, /* KPJPCOMMA */ + {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 */ + + /* Korean keys. */ + {123, 0xf1}, /* HANJA */ + {122, 0xf2}, /* HANGUL */ + + {96, 0x11c}, /* KPENTER */ + {97, 0x11d}, /* RIGHTCTRL */ + {98, 0x135}, /* KPSLASH */ + {99, 0x137}, /* SYSRQ */ + {100, 0x138}, /* RIGHTALT */ + {119, 0x145}, /* PAUSE */ + {411, 0x145}, /* BREAK */ + {102, 0x147}, /* HOME */ + {103, 0x148}, /* UP */ + {104, 0x149}, /* PAGEUP */ + {105, 0x14b}, /* LEFT */ + {106, 0x14d}, /* RIGHT */ + {107, 0x14f}, /* END */ + {108, 0x150}, /* DOWN */ + {109, 0x151}, /* PAGEDOWN */ + {110, 0x152}, /* INSERT */ + {111, 0x153}, /* DELETE */ + + {125, 0x15b}, /* LEFTMETA */ + {126, 0x15c}, /* RIGHTMETA */ + {127, 0x15d}, /* COMPOSE => Menu */ + + /* 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 de7bceafb..95b881e64 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -96,11 +96,17 @@ 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 # include "xkbcommon_x11_keyboard.hpp" # endif +# ifdef WAYLAND +# include "xkbcommon_wl_keyboard.hpp" +# endif # endif # include # include @@ -652,9 +658,18 @@ MainWindow::MainWindow(QWidget *parent) ui->actionCursor_Puck->setChecked(true); } -#ifdef XKBCOMMON_X11 +#ifdef XKBCOMMON +# ifdef XKBCOMMON_X11 if (QApplication::platformName().contains("xcb")) xkbcommon_x11_init(); + else +# endif +# ifdef WAYLAND + if (QApplication::platformName().contains("wayland")) + xkbcommon_wl_init(); + else +# endif + {} #endif } @@ -890,349 +905,6 @@ MainWindow::on_actionSettings_triggered() plat_pause(currentPause); } -#if defined(__unix__) && !defined(__HAIKU__) -std::array x11_to_xt_base { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x0A, - 0x0B, - 0x0C, - 0x0D, - 0x0E, - 0x0F, - 0x10, - 0x11, - 0x12, - 0x13, - 0x14, - 0x15, - 0x16, - 0x17, - 0x18, - 0x19, - 0x1A, - 0x1B, - 0x1C, - 0x1D, - 0x1E, - 0x1F, - 0x20, - 0x21, - 0x22, - 0x23, - 0x24, - 0x25, - 0x26, - 0x27, - 0x28, - 0x29, - 0x2A, - 0x2B, - 0x2C, - 0x2D, - 0x2E, - 0x2F, - 0x30, - 0x31, - 0x32, - 0x33, - 0x34, - 0x35, - 0x36, - 0x37, - 0x38, - 0x39, - 0x3A, - 0x3B, - 0x3C, - 0x3D, - 0x3E, - 0x3F, - 0x40, - 0x41, - 0x42, - 0x43, - 0x44, - 0x45, - 0x46, - 0x47, - 0x48, - 0x49, - 0x4A, - 0x4B, - 0x4C, - 0x4D, - 0x4E, - 0x4F, - 0x50, - 0x51, - 0x52, - 0x53, - 0x54, - 0x55, - 0x56, - 0x57, - 0x58, - 0x147, - 0x148, - 0x149, - 0, - 0x14B, - 0, - 0x14D, - 0x14F, - 0x150, - 0x151, - 0x152, - 0x153, - 0x11C, - 0x11D, - 0, // Pause/Break key. - 0x137, - 0x135, - 0x138, - 0, // Ditto as above comment. - 0x15B, - 0x15C, - 0x15D, -}; - -std::array x11_to_xt_2 { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x0A, - 0x0B, - 0x0C, - 0x0D, - 0x0E, - 0x0F, - 0x10, - 0x11, - 0x12, - 0x13, - 0x14, - 0x15, - 0x16, - 0x17, - 0x18, - 0x19, - 0x1A, - 0x1B, - 0x1C, - 0x1D, - 0x1E, - 0x1F, - 0x20, - 0x21, - 0x22, - 0x23, - 0x24, - 0x25, - 0x26, - 0x27, - 0x28, - 0x29, - 0x2A, - 0x2B, - 0x2C, - 0x2D, - 0x2E, - 0x2F, - 0x30, - 0x31, - 0x32, - 0x33, - 0x34, - 0x35, - 0x36, - 0x37, - 0x38, - 0x39, - 0x3A, - 0x3B, - 0x3C, - 0x3D, - 0x3E, - 0x3F, - 0x40, - 0x41, - 0x42, - 0x43, - 0x44, - 0x45, - 0x46, - 0x47, - 0x48, - 0x49, - 0x4A, - 0x4B, - 0x4C, - 0x4D, - 0x4E, - 0x4F, - 0x50, - 0x51, - 0x52, - 0x53, - 0x138, - 0x55, - 0x56, - 0x57, - 0x58, - 0x56, - 0x70, - 0x7B, - 0x7D, - 0x2B, - 0x7E, - 0, - 0x11C, - 0x11D, - 0x135, - 0x137, - 0x138, - 0, - 0x147, - 0x148, - 0x149, - 0x14B, - 0x14D, - 0x14F, - 0x150, - 0x151, - 0x152, - 0x153, - 0, - 0, /* Mute */ - 0, /* Volume Down */ - 0, /* Volume Up */ - 0, /* Power Off */ - 0, - 0, - 0, - 0, - 0, - 0x70, - 0x7B, - 0x73, - 0x15B, - 0x15C, - 0x15D -}; - -std::array x11_to_xt_vnc { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0x1D, - 0x11D, - 0x2A, - 0x36, - 0, - 0, - 0x38, - 0x138, - 0x39, - 0x0B, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x0A, - 0x0C, - 0x0D, - 0x1A, - 0x1B, - 0x27, - 0x28, - 0x29, - 0x33, - 0x34, - 0x35, - 0x2B, - 0x1E, - 0x30, - 0x2E, - 0x20, - 0x12, - 0x21, - 0x22, - 0x23, - 0x17, - 0x24, - 0x25, - 0x26, - 0x32, - 0x31, - 0x18, - 0x19, - 0x10, - 0x13, - 0x1F, - 0x14, - 0x16, - 0x2F, - 0x11, - 0x2D, - 0x15, - 0x2C, - 0x0E, - 0x1C, - 0x0F, - 0x01, - 0x153, - 0x147, - 0x14F, - 0x149, - 0x151, - 0x148, - 0x150, - 0x14B, - 0x14D, -}; -#endif - #ifdef Q_OS_MACOS std::array darwin_to_xt { 0x1E, @@ -1366,27 +1038,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 }, @@ -1497,14 +1148,10 @@ static std::unordered_map be_to_xt = { }; #endif -#if defined(__unix__) && !defined(__HAIKU__) -static std::array &selected_keycode = x11_to_xt_base; -#endif - uint16_t x11_keycode_to_keysym(uint32_t keycode) { - uint16_t finalkeycode = 0; + uint16_t finalkeycode; #if defined(Q_OS_WINDOWS) finalkeycode = (keycode & 0xFFFF); #elif defined(Q_OS_MACOS) @@ -1513,33 +1160,20 @@ x11_keycode_to_keysym(uint32_t keycode) finalkeycode = be_to_xt[keycode]; #else # ifdef XKBCOMMON - if (xkbcommon_keymap) { + if (xkbcommon_keymap) finalkeycode = xkbcommon_translate(keycode); - } else + else +# endif +# ifdef EVDEV_KEYBOARD_HPP + finalkeycode = evdev_translate(keycode - 8); +# else + finalkeycode = 0; # 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")) { - 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; - } - } - finalkeycode = selected_keycode[keycode]; - } - } #endif + /* Special case for Ctrl+Pause. */ + if ((finalkeycode == 0x145) && (keyboard_recv(0x1d) || keyboard_recv(0x11d))) + finalkeycode = 0x146; + if (rctrl_is_lalt && finalkeycode == 0x11D) finalkeycode = 0x38; return finalkeycode; diff --git a/src/qt/xkbcommon_keyboard.cpp b/src/qt/xkbcommon_keyboard.cpp index 1b4998b1f..1546b6faa 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}, @@ -53,6 +55,7 @@ std::unordered_map xkb_keycodes{ {"AD11", 0x1a}, {"AD12", 0x1b}, {"RTRN", 0x1c}, + {"LNFD", 0x1c}, /* linefeed => Enter */ {"LCTL", 0x1d}, {"AC01", 0x1e}, @@ -99,7 +102,6 @@ std::unordered_map xkb_keycodes{ {"NMLK", 0x45}, {"SCLK", 0x46}, - {"FK14", 0x46}, /* F14 as Scroll Lock */ {"KP7", 0x47}, {"KP8", 0x48}, {"KP9", 0x49}, @@ -117,12 +119,17 @@ std::unordered_map xkb_keycodes{ {"LSGT", 0x56}, {"FK11", 0x57}, {"FK12", 0x58}, + {"FK13", 0x5d}, + {"FK14", 0x5e}, + {"FK15", 0x5f}, /* Japanese keys. */ - {"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}, /* \| */ @@ -138,10 +145,9 @@ std::unordered_map xkb_keycodes{ {"KPDV", 0x135}, {"PRSC", 0x137}, {"SYRQ", 0x137}, - {"FK13", 0x137}, /* F13 as SysRq */ {"RALT", 0x138}, - {"PAUS", 0x146}, /* special case */ - {"FK15", 0x146}, /* F15 as Pause */ + {"PAUS", 0x145}, + {"BRK", 0x145}, {"HOME", 0x147}, {"UP", 0x148}, {"PGUP", 0x149}, @@ -154,104 +160,66 @@ std::unordered_map xkb_keycodes{ {"DELE", 0x153}, {"LWIN", 0x15b}, + {"LMTA", 0x15b}, {"RWIN", 0x15c}, - {"COMP", 0x15d}, + {"RMTA", 0x15c}, + {"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= */ {"FRNT", 0x101}, /* # Logitech Task Select */ - {"I224", 0x105}, /* CHAT# => Messenger/Files */ - {"I190", 0x107}, /* REDO */ - {"UNDO", 0x108}, + {"UNDO", 0x108}, /* # */ {"PAST", 0x10a}, /* # Paste */ - {"I185", 0x10b}, /* SCROLLUP# => normal speed */ - {"I173", 0x110}, /* PREVIOUSSONG */ {"FIND", 0x112}, /* # Logitech */ - {"I156", 0x113}, /* PROG1# => Word */ - {"I157", 0x114}, /* PROG2# => Excel */ - {"I210", 0x115}, /* PROG3# => Calendar */ - {"I182", 0x116}, /* EXIT# => Log Off */ - {"CUT", 0x117}, - {"COPY", 0x118}, - {"I171", 0x119}, /* NEXTSONG */ - {"I162", 0x11e}, /* CYCLEWINDOWS => Application Right (no left counterpart) */ + {"CUT", 0x117}, /* # */ + {"COPY", 0x118}, /* # */ {"MUTE", 0x120}, - {"I148", 0x121}, /* CALC */ - {"I172", 0x122}, /* PLAYPAUSE */ - {"I158", 0x123}, /* WWW# => Compaq online start */ - {"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}, - {"I180", 0x132}, /* HOMEPAGE */ - {"HELP", 0x13b}, /* # */ - {"I221", 0x13c}, /* SOUND# => My Music */ - {"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# */ - {"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 */ - {"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# */ - {"I186", 0x18b}, /* SCROLLDOWN# => normal speed */ }; struct xkb_keymap *xkbcommon_keymap = nullptr; void xkbcommon_init(struct xkb_keymap *keymap) { - xkbcommon_keymap = keymap; + if (keymap) + xkbcommon_keymap = keymap; +} + +void +xkbcommon_close() +{ + xkbcommon_keymap = NULL; } uint16_t xkbcommon_translate(uint32_t keycode) { const char *key_name = xkb_keymap_key_get_name(xkbcommon_keymap, keycode); - if (!key_name) { - qWarning() << "XKB Keyboard: Unknown keycode" << Qt::hex << keycode; - return 0; - } - std::string key_name_s(key_name); - uint16_t ret = xkb_keycodes[key_name_s]; + /* If XKB doesn't know the key name for this keycode, assume an unnamed Ixxx key. + This is useful for older XKB versions with an incomplete evdev keycode map. */ + auto key_name_s = key_name ? std::string(key_name) : QString("I%1").arg(keycode).toStdString(); + auto ret = xkb_keycodes[key_name_s]; - /* Observed with multimedia keys on a Windows X11 client. */ + /* Observed with multimedia keys on Windows VcXsrv. */ 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); + qWarning() << "XKB Keyboard: Unknown key" << Qt::hex << keycode << QString::fromStdString(key_name_s); #if 0 else - qInfo() << "XKB Keyboard: Key" << Qt::hex << keycode << "/" << QString::fromStdString(key_name_s) << "scancode" << Qt::hex << ret; + qInfo() << "XKB Keyboard: Key" << Qt::hex << keycode << QString::fromStdString(key_name_s) << "scancode" << Qt::hex << ret; #endif return ret; diff --git a/src/qt/xkbcommon_keyboard.hpp b/src/qt/xkbcommon_keyboard.hpp index afcba41bb..c3b28eeec 100644 --- a/src/qt/xkbcommon_keyboard.hpp +++ b/src/qt/xkbcommon_keyboard.hpp @@ -16,4 +16,5 @@ */ extern void *xkbcommon_keymap; void xkbcommon_init(struct xkb_keymap *keymap); +void xkbcommon_close(); uint16_t xkbcommon_translate(uint32_t keycode); diff --git a/src/qt/xkbcommon_wl_keyboard.cpp b/src/qt/xkbcommon_wl_keyboard.cpp new file mode 100644 index 000000000..c6736f2d4 --- /dev/null +++ b/src/qt/xkbcommon_wl_keyboard.cpp @@ -0,0 +1,236 @@ +/* + * 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. + * + * xkbcommon Wayland keyboard input module. + * + * Heavily inspired by libxkbcommon interactive-wayland.c + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +extern "C" { +#include +#include +#include +#include <86box/86box.h> +}; +#include "xkbcommon_keyboard.hpp" +#include +#include + +#include +#include +#include + +typedef struct { + struct wl_seat *wl_seat; + struct wl_keyboard *wl_kbd; + uint32_t version; + + struct xkb_keymap *keymap; + + struct wl_list link; +} seat_t; + +static bool wl_init_ok = false; +static struct wl_list seats; +static struct xkb_context *ctx; + +static void +xkbcommon_wl_set_keymap() +{ + /* Grab keymap from the first seat with one. */ + seat_t *seat, *tmp; + wl_list_for_each_safe(seat, tmp, &seats, link) { + if (seat->keymap) { + xkbcommon_init(seat->keymap); + return; + } + } + xkbcommon_close(); /* none found */ +} + +static void +kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format, + int fd, uint32_t size) +{ + seat_t *seat = (seat_t *) data; + + char *buf = (char *) mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (!buf) { + qWarning() << "XKB Keyboard: Failed to mmap keymap with error" << errno; + return; + } + + seat->keymap = xkb_keymap_new_from_buffer(ctx, buf, size - 1, + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(buf, size); + close(fd); + if (!seat->keymap) { + qWarning() << "XKB Keyboard: Keymap compilation failed"; + return; + } + + xkbcommon_wl_set_keymap(); +} + +static void +kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + struct wl_surface *surf, struct wl_array *keys) +{ +} + +static void +kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + struct wl_surface *surf) +{ +} + +static void +kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) +{ +} + +static void +kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ +} + +static void +kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate, + int32_t delay) +{ +} + +static const struct wl_keyboard_listener kbd_listener = { + kbd_keymap, + kbd_enter, + kbd_leave, + kbd_key, + kbd_modifiers, + kbd_repeat_info +}; + +static void +seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps) +{ + seat_t *seat = (seat_t *) data; + + if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat); + wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat); + } else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_keyboard_release(seat->wl_kbd); + else + wl_keyboard_destroy(seat->wl_kbd); + + static struct xkb_keymap *keymap = seat->keymap; + seat->keymap = NULL; + xkbcommon_wl_set_keymap(); + xkb_keymap_unref(keymap); + + seat->wl_kbd = NULL; + } +} + +static void +seat_name(void *data, struct wl_seat *wl_seat, const char *name) +{ +} + +static const struct wl_seat_listener seat_listener = { + seat_capabilities, + seat_name +}; + +static void +display_handle_global(void *data, struct wl_registry *wl_registry, uint32_t id, + const char *interface, uint32_t version) +{ + if (!strcmp(interface, "wl_seat")) { + seat_t *seat = (seat_t *) malloc(sizeof(seat_t)); + memset(seat, 0, sizeof(seat_t)); + + seat->wl_seat = (wl_seat *) wl_registry_bind(wl_registry, id, &wl_seat_interface, MIN(version, 5)); + wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); + wl_list_insert(&seats, &seat->link); + } +} + +static void +display_global_remove(void *data, struct wl_registry *wl_registry, uint32_t id) +{ + xkbcommon_close(); + + seat_t *seat, *tmp; + wl_list_for_each_safe(seat, tmp, &seats, link) { + if (seat->wl_kbd) { + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_keyboard_release(seat->wl_kbd); + else + wl_keyboard_destroy(seat->wl_kbd); + + xkb_keymap_unref(seat->keymap); + } + + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_seat_release(seat->wl_seat); + else + wl_seat_destroy(seat->wl_seat); + + wl_list_remove(&seat->link); + free(seat); + } +} + +static const struct wl_registry_listener registry_listener = { + display_handle_global, + display_global_remove +}; + +void +xkbcommon_wl_init() +{ + if (wl_init_ok) + return; + + wl_list_init(&seats); + + ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!ctx) { + qWarning() << "XKB Keyboard: XKB context creation failed"; + return; + } + + wl_display *display = (wl_display *) QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_display"); + if (display) { + auto registry = wl_display_get_registry(display); + if (registry) { + wl_registry_add_listener(registry, ®istry_listener, nullptr); + wl_display_roundtrip(display); + wl_display_roundtrip(display); + } else { + goto err_ctx; + } + } else { + goto err_ctx; + } + wl_init_ok = true; + return; + +err_ctx: + xkb_context_unref(ctx); +} diff --git a/src/qt/xkbcommon_wl_keyboard.hpp b/src/qt/xkbcommon_wl_keyboard.hpp new file mode 100644 index 000000000..a70cb9e9b --- /dev/null +++ b/src/qt/xkbcommon_wl_keyboard.hpp @@ -0,0 +1,17 @@ +/* + * 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 xkbcommon Wayland keyboard input module. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +void xkbcommon_wl_init(); diff --git a/src/qt/xkbcommon_x11_keyboard.cpp b/src/qt/xkbcommon_x11_keyboard.cpp index b9c16cc9b..8e0167518 100644 --- a/src/qt/xkbcommon_x11_keyboard.cpp +++ b/src/qt/xkbcommon_x11_keyboard.cpp @@ -33,7 +33,9 @@ extern "C" { }; #include "xkbcommon_keyboard.hpp" +#include #include +#include void xkbcommon_x11_init() @@ -43,9 +45,9 @@ xkbcommon_x11_init() int32_t core_kbd_device_id; struct xkb_keymap *keymap; - conn = xcb_connect(NULL, NULL); - if (!conn || xcb_connection_has_error(conn)) { - qWarning() << "XKB Keyboard: X server connection failed with error" << (conn ? xcb_connection_has_error(conn) : -1); + conn = (xcb_connection_t *) QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("connection"); + if (!conn) { + qWarning() << "XKB Keyboard: X server connection failed"; return; } @@ -55,13 +57,13 @@ xkbcommon_x11_init() NULL, NULL, NULL, NULL); if (!ret) { qWarning() << "XKB Keyboard: XKB extension setup failed"; - goto err_conn; + return; } ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!ctx) { qWarning() << "XKB Keyboard: XKB context creation failed"; - goto err_conn; + return; } core_kbd_device_id = xkb_x11_get_core_keyboard_device_id(conn); @@ -77,9 +79,8 @@ xkbcommon_x11_init() } xkbcommon_init(keymap); + return; err_ctx: xkb_context_unref(ctx); -err_conn: - xcb_disconnect(conn); }