/* * 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. * * Windows raw keyboard input handler. * * * * Author: Miran Grca, * * Copyright 2016-2018 Miran Grca. */ #define UNICODE #define _WIN32_WINNT 0x0501 #define BITMAP WINDOWS_BITMAP #include #include #undef BITMAP #include #include #include #include #include <86box/86box.h> #include <86box/device.h> #include <86box/keyboard.h> #include <86box/plat.h> #include <86box/win.h> static uint16_t scancode_map[768]; /* This is so we can disambiguate scan codes that would otherwise conflict and get passed on incorrectly. */ static UINT16 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 keyboard_getkeymap(void) { WCHAR *keyName = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout"; WCHAR *valueName = L"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 (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, 1, &hKey) == ERROR_SUCCESS) { if (RegQueryValueEx(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 keyboard_handle(PRAWINPUT raw) { USHORT scancode; static int recv_lalt = 0, recv_ralt = 0, recv_tab = 0; RAWKEYBOARD rawKB = raw->data.keyboard; scancode = rawKB.MakeCode; if (kbd_req_capture && !mouse_capture && !video_fullscreen) return; /* If it's not a scan code that starts with 0xE1 */ if (!(rawKB.Flags & RI_KEY_E1)) { if (rawKB.Flags & RI_KEY_E0) 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 != scancode_map[scancode]) pclog("Scan code remap: %03X -> %03X\n", scancode, 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. */ if ((scancode == 0x00F) && !(rawKB.Flags & RI_KEY_BREAK) && (recv_lalt || recv_ralt) && !mouse_capture) { /* We received a TAB while ALT was pressed, while the mouse is not captured, suppress the TAB and send an ALT key up. */ if (recv_lalt) { keyboard_input(0, 0x038); /* Extra key press and release so the guest is not stuck in the menu bar. */ keyboard_input(1, 0x038); keyboard_input(0, 0x038); recv_lalt = 0; } if (recv_ralt) { keyboard_input(0, 0x138); /* Extra key press and release so the guest is not stuck in the menu bar. */ keyboard_input(1, 0x138); keyboard_input(0, 0x138); recv_ralt = 0; } } else if (((scancode == 0x038) || (scancode == 0x138)) && !(rawKB.Flags & RI_KEY_BREAK) && recv_tab && !mouse_capture) { /* We received an ALT while TAB was pressed, while the mouse is not captured, suppress the ALT and send a TAB key up. */ keyboard_input(0, 0x00F); recv_tab = 0; } else { switch(scancode) { case 0x00F: recv_tab = !(rawKB.Flags & RI_KEY_BREAK); break; case 0x038: recv_lalt = !(rawKB.Flags & RI_KEY_BREAK); break; case 0x138: recv_ralt = !(rawKB.Flags & RI_KEY_BREAK); break; } /* 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(!(rawKB.Flags & RI_KEY_BREAK), scancode); } } else { if (rawKB.MakeCode == 0x1D) { scancode = scancode_map[0x100]; /* Translate E1 1D to 0x100 (which would otherwise be E0 00 but that is invalid anyway). Also, take a potential mapping into account. */ } else scancode = 0xFFFF; if (scancode != 0xFFFF) keyboard_input(!(rawKB.Flags & RI_KEY_BREAK), scancode); } }