diff --git a/src/86box.c b/src/86box.c index 5e1f58413..fc698a5ee 100644 --- a/src/86box.c +++ b/src/86box.c @@ -206,6 +206,7 @@ int video_fullscreen_scale_maximized = 0; /* (C) Whether also apply when maximized. */ int do_auto_pause = 0; /* (C) Auto-pause the emulator on focus loss */ +int raw_input = 0; /* (C) Use raw input */ char uuid[MAX_UUID_LEN] = { '\0' }; /* (C) UUID or machine identifier */ int other_ide_present = 0; /* IDE controllers from non-IDE cards are @@ -561,6 +562,7 @@ usage: printf("-S or --settings - show only the settings dialog\n"); #endif printf("-V or --vmname name - overrides the name of the running VM\n"); + printf("-W or --raw - uses raw input (compatibility-only outside Windows)\n"); printf("-X or --clear what - clears the 'what' (cmos/flash/both)\n"); printf("-Y or --donothing - do not show any UI or run the emulation\n"); printf("-Z or --lastvmpath - the last parameter is VM path rather than config\n"); @@ -636,6 +638,8 @@ usage: dump_missing = 1; } else if (!strcasecmp(argv[c], "--donothing") || !strcasecmp(argv[c], "-Y")) { do_nothing = 1; + } else if (!strcasecmp(argv[c], "--raw") || !strcasecmp(argv[c], "-W")) { + raw_input = 1; } else if (!strcasecmp(argv[c], "--keycodes") || !strcasecmp(argv[c], "-K")) { if ((c + 1) == argc) goto usage; diff --git a/src/device/keyboard.c b/src/device/keyboard.c index ea81e7525..2eff2159b 100644 --- a/src/device/keyboard.c +++ b/src/device/keyboard.c @@ -29,7 +29,9 @@ #include "cpu.h" -int keyboard_scan; +uint16_t scancode_map[768] = { 0 }; + +int keyboard_scan; #ifdef _WIN32 /* Windows: F8+F12 */ @@ -386,3 +388,22 @@ keyboard_ismsexit(void) return ((recv_key_ui[key_prefix_1_1] || recv_key_ui[key_prefix_1_2]) && (recv_key_ui[key_uncapture_1] || recv_key_ui[key_uncapture_2])); } + +/* This is so we can disambiguate scan codes that would otherwise conflict and get + passed on incorrectly. */ +uint16_t +convert_scan_code(uint16_t scan_code) +{ + if ((scan_code & 0xff00) == 0xe000) + scan_code = (scan_code & 0xff) | 0x0100; + + if (scan_code == 0xE11D) + scan_code = 0x0100; + /* E0 00 is sent by some USB keyboards for their special keys, as it is an + invalid scan code (it has no untranslated set 2 equivalent), we mark it + appropriately so it does not get passed through. */ + else if ((scan_code > 0x01FF) || (scan_code == 0x0100)) + scan_code = 0xFFFF; + + return scan_code; +} diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index 7d5709d30..fb4a182a0 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -165,6 +165,7 @@ extern _Atomic double mouse_y_error; /* Mouse error accumulator - Y */ #endif extern int pit_mode; /* (C) force setting PIT mode */ extern int fm_driver; /* (C) select FM sound driver */ +extern int raw_input; /* (C) Use raw input */ /* Keyboard variables for future key combination redefinition. */ extern uint16_t key_prefix_1_1; diff --git a/src/include/86box/keyboard.h b/src/include/86box/keyboard.h index f233637ff..225ab3936 100644 --- a/src/include/86box/keyboard.h +++ b/src/include/86box/keyboard.h @@ -194,8 +194,10 @@ typedef struct scancode { extern "C" { #endif -extern uint8_t keyboard_mode; -extern int keyboard_scan; +extern uint8_t keyboard_mode; +extern int keyboard_scan; + +extern uint16_t scancode_map[768]; extern void (*keyboard_send)(uint16_t val); extern void kbd_adddata_process(uint16_t val, void (*adddata)(uint16_t val)); @@ -288,6 +290,9 @@ extern uint8_t kbc_at_dev_queue_pos(atkbc_dev_t *dev, uint8_t main); extern void kbc_at_dev_queue_add(atkbc_dev_t *dev, uint8_t val, uint8_t main); extern void kbc_at_dev_reset(atkbc_dev_t *dev, int do_fa); extern atkbc_dev_t *kbc_at_dev_init(uint8_t inst); +/* This is so we can disambiguate scan codes that would otherwise conflict and get + passed on incorrectly. */ +extern uint16_t convert_scan_code(uint16_t scan_code); #ifdef __cplusplus } diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 3d32a4889..603099e68 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -47,6 +47,7 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin) # include "qt_winmanagerfilter.hpp" # include <86box/win.h> # include +# include #endif extern "C" { @@ -81,6 +82,7 @@ extern QElapsedTimer elapsed_timer; extern MainWindow *main_window; extern "C" { +#include <86box/keyboard.h> #include <86box/timer.h> #include <86box/nvr.h> extern int qt_nvr_save(void); @@ -88,6 +90,122 @@ extern int qt_nvr_save(void); void qt_set_sequence_auto_mnemonic(bool b); +#ifdef Q_OS_WINDOWS +static void +keyboard_getkeymap() +{ + const LPCSTR keyName = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout"; + const LPCSTR valueName = "Scancode Map"; + unsigned char buf[32768]; + DWORD bufSize; + HKEY hKey; + int j; + UINT32 *bufEx2; + int scMapCount; + UINT16 *bufEx; + int scancode_unmapped; + int scancode_mapped; + + /* First, prepare the default scan code map list which is 1:1. + * Remappings will be inserted directly into it. + * 512 bytes so this takes less memory, bit 9 set means E0 + * prefix. + */ + for (j = 0; j < 512; j++) + scancode_map[j] = j; + + /* Get the scan code remappings from: + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout */ + bufSize = 32768; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, 1, &hKey) == ERROR_SUCCESS) { + if (RegQueryValueExA(hKey, valueName, NULL, NULL, buf, &bufSize) == ERROR_SUCCESS) { + bufEx2 = (UINT32 *) buf; + scMapCount = bufEx2[2]; + if ((bufSize != 0) && (scMapCount != 0)) { + bufEx = (UINT16 *) (buf + 12); + for (j = 0; j < scMapCount * 2; j += 2) { + /* Each scan code is 32-bit: 16 bits of remapped scan code, + and 16 bits of original scan code. */ + scancode_unmapped = bufEx[j + 1]; + scancode_mapped = bufEx[j]; + + scancode_unmapped = convert_scan_code(scancode_unmapped); + scancode_mapped = convert_scan_code(scancode_mapped); + + /* Ignore source scan codes with prefixes other than E1 + that are not E1 1D. */ + if (scancode_unmapped != 0xFFFF) + scancode_map[scancode_unmapped] = scancode_mapped; + } + } + } + RegCloseKey(hKey); + } +} + +static LRESULT CALLBACK +emu_LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + LPKBDLLHOOKSTRUCT lpKdhs = (LPKBDLLHOOKSTRUCT) lParam; + /* Checks if CTRL was pressed. */ + BOOL bCtrlDown = GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1); + uint16_t scancode = lpKdhs->scanCode & 0x00ff; + + if (lpKdhs->flags & LLKHF_EXTENDED) + scancode |= 0x100; + + /* Translate the scan code to 9-bit */ + scancode = convert_scan_code(scancode); + + /* Remap it according to the list from the Registry */ + if ((scancode < (sizeof(scancode_map) / sizeof(scancode_map[0]))) && (scancode != scancode_map[scancode])) { + // pclog("Scan code remap: %03X -> %03X\n", scancode, scancode_map[scancode]); + scancode = scancode_map[scancode]; + } + + /* If it's not 0xFFFF, send it to the emulated + keyboard. + We use scan code 0xFFFF to mean a mapping that + has a prefix other than E0 and that is not E1 1D, + which is, for our purposes, invalid. */ + + /* Translate right CTRL to left ALT if the user has so + chosen. */ + if ((scancode == 0x11d) && rctrl_is_lalt) + scancode = 0x038; + + /* Normal scan code pass through, pass it through as is if + it's not an invalid scan code. */ + if (scancode != 0xFFFF) + keyboard_input(!(lpKdhs->flags & LLKHF_UP), scancode); + + main_window->checkFullscreenHotkey(); + + if ((lpKdhs->scanCode == 0x01) && (lpKdhs->flags & LLKHF_ALTDOWN) && + !(lpKdhs->flags & (LLKHF_UP | LLKHF_EXTENDED))) + return TRUE; + else if ((lpKdhs->scanCode == 0x01) && bCtrlDown && !(lpKdhs->flags & (LLKHF_UP | LLKHF_EXTENDED))) + return TRUE; + else if ((lpKdhs->scanCode == 0x0f) && (lpKdhs->flags & LLKHF_ALTDOWN) && + !(lpKdhs->flags & (LLKHF_UP | LLKHF_EXTENDED))) + return TRUE; + else if ((lpKdhs->scanCode == 0x0f) && bCtrlDown && !(lpKdhs->flags & (LLKHF_UP | LLKHF_EXTENDED))) + return TRUE; + else if ((lpKdhs->scanCode == 0x39) && (lpKdhs->flags & LLKHF_ALTDOWN) && + !(lpKdhs->flags & (LLKHF_UP | LLKHF_EXTENDED))) + return TRUE; + else if ((lpKdhs->scanCode == 0x3e) && (lpKdhs->flags & LLKHF_ALTDOWN) && + !(lpKdhs->flags & (LLKHF_UP | LLKHF_EXTENDED))) + return TRUE; + else if ((lpKdhs->scanCode == 0x51) && bCtrlDown && !(lpKdhs->flags & LLKHF_UP)) + return TRUE; + else if ((lpKdhs->scanCode >= 0x5b) && (lpKdhs->scanCode <= 0x5d) && (lpKdhs->flags & LLKHF_EXTENDED)) + return TRUE; + else + return CallNextHookEx(NULL, nCode, wParam, lParam); +} +#endif + void main_thread_fn() { @@ -165,6 +283,10 @@ main_thread_fn() static std::thread *main_thread; +#ifdef Q_OS_WINDOWS +static HHOOK llhook = NULL; +#endif + int main(int argc, char *argv[]) { @@ -176,6 +298,7 @@ main(int argc, char *argv[]) #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif + QApplication app(argc, argv); QLocale::setDefault(QLocale::C); @@ -193,6 +316,13 @@ main(int argc, char *argv[]) #endif elapsed_timer.start(); + for (size_t i = 0; i < sizeof(scancode_map) / sizeof(scancode_map[0]); i++) + scancode_map[i] = i; + +#ifdef Q_OS_WINDOWS + keyboard_getkeymap(); +#endif + if (!pc_init(argc, argv)) { return 0; } @@ -340,6 +470,13 @@ main(int argc, char *argv[]) }); } +#ifdef Q_OS_WINDOWS + if (!raw_input) { + llhook = SetWindowsHookEx(WH_KEYBOARD_LL, emu_LowLevelKeyboardProc, NULL, 0); + atexit([] () -> void { if (llhook) UnhookWindowsHookEx(llhook); }); + } +#endif + /* Setup raw input */ auto rawInputFilter = WindowsRawInputFilter::Register(main_window); if (rawInputFilter) { diff --git a/src/qt/qt_winrawinputfilter.cpp b/src/qt/qt_winrawinputfilter.cpp index 66d8ad8e5..4ff515c49 100644 --- a/src/qt/qt_winrawinputfilter.cpp +++ b/src/qt/qt_winrawinputfilter.cpp @@ -64,8 +64,10 @@ WindowsRawInputFilter::Register(MainWindow *window) .hwndTarget = nullptr} }; - if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) - return std::unique_ptr(nullptr); + if (raw_input && (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE)) + return std::unique_ptr(nullptr); + else if (!raw_input && RegisterRawInputDevices(&(rid[1]), 1, sizeof(rid[0])) == FALSE) + return std::unique_ptr(nullptr); std::unique_ptr inputfilter(new WindowsRawInputFilter(window)); @@ -80,11 +82,6 @@ WindowsRawInputFilter::WindowsRawInputFilter(MainWindow *window) connect(menu, &QMenu::aboutToShow, this, [=]() { menus_open++; }); connect(menu, &QMenu::aboutToHide, this, [=]() { menus_open--; }); } - - for (size_t i = 0; i < sizeof(scancode_map) / sizeof(scancode_map[0]); i++) - scancode_map[i] = i; - - keyboard_getkeymap(); } WindowsRawInputFilter::~WindowsRawInputFilter() @@ -100,7 +97,10 @@ WindowsRawInputFilter::~WindowsRawInputFilter() .hwndTarget = NULL} }; - RegisterRawInputDevices(rid, 2, sizeof(rid[0])); + if (raw_input) + RegisterRawInputDevices(rid, 2, sizeof(rid[0])); + else + RegisterRawInputDevices(&(rid[1]), 1, sizeof(rid[0])); } bool @@ -197,7 +197,7 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw) /* Remap it according to the list from the Registry */ if ((scancode < (sizeof(scancode_map) / sizeof(scancode_map[0]))) && (scancode != scancode_map[scancode])) { - pclog("Scan code remap: %03X -> %03X\n", scancode, scancode_map[scancode]); + // pclog("Scan code remap: %03X -> %03X\n", scancode, scancode_map[scancode]); scancode = scancode_map[scancode]; } @@ -221,77 +221,6 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw) } } -/* This is so we can disambiguate scan codes that would otherwise conflict and get - passed on incorrectly. */ -UINT16 -WindowsRawInputFilter::convert_scan_code(UINT16 scan_code) -{ - if ((scan_code & 0xff00) == 0xe000) - scan_code = (scan_code & 0xff) | 0x0100; - - if (scan_code == 0xE11D) - scan_code = 0x0100; - /* E0 00 is sent by some USB keyboards for their special keys, as it is an - invalid scan code (it has no untranslated set 2 equivalent), we mark it - appropriately so it does not get passed through. */ - else if ((scan_code > 0x01FF) || (scan_code == 0x0100)) - scan_code = 0xFFFF; - - return scan_code; -} - -void -WindowsRawInputFilter::keyboard_getkeymap() -{ - const LPCSTR keyName = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout"; - const LPCSTR valueName = "Scancode Map"; - unsigned char buf[32768]; - DWORD bufSize; - HKEY hKey; - int j; - UINT32 *bufEx2; - int scMapCount; - UINT16 *bufEx; - int scancode_unmapped; - int scancode_mapped; - - /* First, prepare the default scan code map list which is 1:1. - * Remappings will be inserted directly into it. - * 512 bytes so this takes less memory, bit 9 set means E0 - * prefix. - */ - for (j = 0; j < 512; j++) - scancode_map[j] = j; - - /* Get the scan code remappings from: - HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout */ - bufSize = 32768; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, 1, &hKey) == ERROR_SUCCESS) { - if (RegQueryValueExA(hKey, valueName, NULL, NULL, buf, &bufSize) == ERROR_SUCCESS) { - bufEx2 = (UINT32 *) buf; - scMapCount = bufEx2[2]; - if ((bufSize != 0) && (scMapCount != 0)) { - bufEx = (UINT16 *) (buf + 12); - for (j = 0; j < scMapCount * 2; j += 2) { - /* Each scan code is 32-bit: 16 bits of remapped scan code, - and 16 bits of original scan code. */ - scancode_unmapped = bufEx[j + 1]; - scancode_mapped = bufEx[j]; - - scancode_unmapped = convert_scan_code(scancode_unmapped); - scancode_mapped = convert_scan_code(scancode_mapped); - - /* Ignore source scan codes with prefixes other than E1 - that are not E1 1D. */ - if (scancode_unmapped != 0xFFFF) - scancode_map[scancode_unmapped] = scancode_mapped; - } - } - } - RegCloseKey(hKey); - } -} - void WindowsRawInputFilter::mouse_handle(PRAWINPUT raw) { diff --git a/src/qt/qt_winrawinputfilter.hpp b/src/qt/qt_winrawinputfilter.hpp index f687164ca..411f7841c 100644 --- a/src/qt/qt_winrawinputfilter.hpp +++ b/src/qt/qt_winrawinputfilter.hpp @@ -61,7 +61,6 @@ public: private: MainWindow *window; - uint16_t scancode_map[768]; int buttons = 0; int dx = 0; int dy = 0; @@ -73,8 +72,6 @@ private: void handle_input(HRAWINPUT input); void keyboard_handle(PRAWINPUT raw); void mouse_handle(PRAWINPUT raw); - static UINT16 convert_scan_code(UINT16 scan_code); - void keyboard_getkeymap(); }; #endif