diff --git a/.ci/AppImageBuilder.yml b/.ci/AppImageBuilder.yml index 3d83e2851..39997a7a8 100644 --- a/.ci/AppImageBuilder.yml +++ b/.ci/AppImageBuilder.yml @@ -67,6 +67,8 @@ AppDir: - libxcb-shape0 # if QT:BOOL=ON - 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 26fd24a76..fc7290f4a 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -316,6 +316,9 @@ then pacman -S --needed --noconfirm "$pkg" done fi + + # Clean pacman cache when running under Jenkins to save disk space. + [ "$CI" = "true" ] && rm -rf /var/cache/pacman/pkg # Generate a new freetype DLL for this architecture. rm -f "$freetype_dll" @@ -584,7 +587,7 @@ else # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. libpkgs="" longest_libpkg=0 - for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev + for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libxkbcommon-x11-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev do libpkgs="$libpkgs $pkg:$arch_deb" length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) @@ -629,7 +632,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 +951,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/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index be42cbae6..27c7ea204 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -252,7 +252,10 @@ jobs: slug: -Qt packages: >- qtbase5-dev + qtbase5-private-dev qttools5-dev + libevdev-dev + libxkbcommon-x11-dev steps: - name: Install dependencies 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/cpu/x86.c b/src/cpu/x86.c index eb1dc463f..47250045f 100644 --- a/src/cpu/x86.c +++ b/src/cpu/x86.c @@ -29,6 +29,7 @@ #include <86box/device.h> #include <86box/dma.h> #include <86box/io.h> +#include <86box/keyboard.h> #include <86box/mem.h> #include <86box/rom.h> #include <86box/nmi.h> @@ -265,9 +266,12 @@ reset_common(int hard) if (is286) { loadcs(0xF000); cpu_state.pc = 0xFFF0; - rammask = cpu_16bitbus ? 0xFFFFFF : 0xFFFFFFFF; - if (is6117) - rammask |= 0x03000000; + if (hard) { + rammask = cpu_16bitbus ? 0xFFFFFF : 0xFFFFFFFF; + if (is6117) + rammask |= 0x03000000; + mem_a20_key = mem_a20_alt = mem_a20_state = 0; + } } idt.base = 0; cpu_state.flags = 2; @@ -315,6 +319,10 @@ reset_common(int hard) cache_index = 0; memset(_tr, 0x00, sizeof(_tr)); memset(_cache, 0x00, sizeof(_cache)); + + /* If we have an AT or PS/2 keyboard controller, make sure the A20 state + is correct. */ + kbc_at_a20_reset(); } if (!is286) diff --git a/src/device/keyboard.c b/src/device/keyboard.c index 74bf3f67e..197281992 100644 --- a/src/device/keyboard.c +++ b/src/device/keyboard.c @@ -66,6 +66,7 @@ static uint8_t fake_shift_needed(uint16_t scan) { switch (scan) { + case 0x137: /* Yes, Print Screen requires the fake shifts. */ case 0x147: case 0x148: case 0x149: @@ -125,9 +126,13 @@ key_process(uint16_t scan, int down) void keyboard_input(int down, uint16_t scan) { + /* Special case for E1 1D, translate it to 0100 - special case. */ + if ((scan >> 8) == 0xe1) { + if ((scan & 0xff) == 0x1d) + scan = 0x0100; /* Translate E0 xx scan codes to 01xx because we use 512-byte arrays for states and scan code sets. */ - if ((scan >> 8) == 0xe0) { + } else if ((scan >> 8) == 0xe0) { scan &= 0x00ff; scan |= 0x0100; /* extended key code */ } else if ((scan >> 8) != 0x01) diff --git a/src/device/keyboard_at.c b/src/device/keyboard_at.c index 4035a8fc6..194339c72 100644 --- a/src/device/keyboard_at.c +++ b/src/device/keyboard_at.c @@ -37,7 +37,6 @@ #include <86box/mem.h> #include <86box/device.h> #include <86box/machine.h> -#include <86box/m_xt_xi8088.h> #include <86box/m_at_t3100e.h> #include <86box/fdd.h> #include <86box/fdc.h> @@ -81,71 +80,114 @@ #define KBC_VEN_IBM_MCA 0x08 #define KBC_VEN_QUADTEL 0x0c #define KBC_VEN_TOSHIBA 0x10 -#define KBC_VEN_XI8088 0x14 -#define KBC_VEN_IBM_PS1 0x18 -#define KBC_VEN_ACER 0x1c -#define KBC_VEN_INTEL_AMI 0x20 -#define KBC_VEN_OLIVETTI 0x24 -#define KBC_VEN_NCR 0x28 -#define KBC_VEN_SAMSUNG 0x2c -#define KBC_VEN_ALI 0x30 -#define KBC_VEN_PHOENIX 0x3c +#define KBC_VEN_IBM_PS1 0x14 +#define KBC_VEN_ACER 0x18 +#define KBC_VEN_INTEL_AMI 0x1c +#define KBC_VEN_OLIVETTI 0x20 +#define KBC_VEN_NCR 0x24 +#define KBC_VEN_PHOENIX 0x28 +#define KBC_VEN_ALI 0x2c +#define KBC_VEN_TG 0x30 +#define KBC_VEN_TG_GREEN 0x34 #define KBC_VEN_MASK 0x3c enum { KBC_STATE_RESET = 0, - KBC_STATE_NORMAL, + KBC_STATE_MAIN_IBF, + KBC_STATE_MAIN_KBD, + KBC_STATE_MAIN_MOUSE, + KBC_STATE_MAIN_BOTH, + KBC_STATE_KBC_OUT, + KBC_STATE_KBC_PARAM, + KBC_STATE_SEND_KBD, KBC_STATE_KBD, + KBC_STATE_SEND_MOUSE, KBC_STATE_MOUSE }; +#define KBC_STATE_SCAN_KBD KBC_STATE_KBD +#define KBC_STATE_SCAN_MOUSE KBC_STATE_MOUSE + +enum { + DEV_STATE_RESET = 0, + DEV_STATE_MAIN_1, + DEV_STATE_MAIN_2, + DEV_STATE_MAIN_CMD, + DEV_STATE_MAIN_OUT, + DEV_STATE_MAIN_WANT_IN, + DEV_STATE_MAIN_IN, + DEV_STATE_MAIN_WANT_RESET, + DEV_STATE_RESET_OUT +}; typedef struct { - uint8_t command, status, ib, out, - old_out, secr_phase, mem_addr, input_port, - output_port, old_output_port, key_command, output_locked, - ami_stat, want60, key_wantdata, ami_flags, - key_wantcmd, key_dat, mouse_wantcmd, mouse_dat, - kbc_state, kbd_state, mouse_state, pad; + /* Controller. */ + uint8_t pci, kbc_state, command, want60, + status, ib, out, old_out, + secr_phase, mem_addr, input_port, output_port, + old_output_port, output_locked, ami_stat, ami_flags, + key_ctrl_queue_start, key_ctrl_queue_end; - uint16_t irq_levels, pad0; + /* Keyboard. */ + uint8_t kbd_state, key_command, key_wantdata, key_wantcmd, + key_dat, kbd_last_scan_code, sc_or, + key_cmd_queue_start, key_cmd_queue_end, key_queue_start, key_queue_end; + /* Mouse. */ + uint8_t mouse_state, mouse_wantcmd, mouse_dat, mouse_cmd_queue_start, mouse_cmd_queue_end, + mouse_queue_start, mouse_queue_end; + + /* Controller. */ uint8_t mem[0x100]; - int out_new, out_new_mouse, - reset_delay, mouse_reset_delay; + /* Controller - internal FIFO for the purpose of commands with multi-byte output. */ + uint8_t key_ctrl_queue[64]; + /* Keyboard - command response FIFO. */ + uint8_t key_cmd_queue[16]; + + /* Keyboard - scan FIFO. */ + uint8_t key_queue[16]; + + /* Mouse - command response FIFO. */ + uint8_t mouse_cmd_queue[16]; + + /* Mouse - scan FIFO. */ + uint8_t mouse_queue[16]; + + /* Controller. */ + uint16_t irq_levels, pad1; + + /* Keyboard. */ + int out_new, reset_delay; + + /* Mouse. */ + int out_new_mouse, mouse_reset_delay; + + /* Controller. */ uint32_t flags; + /* Controller (main timer). */ + pc_timer_t send_delay_timer; + + /* Controller (P2 pulse callback timer). */ pc_timer_t pulse_cb; uint8_t (*write60_ven)(void *p, uint8_t val); uint8_t (*write64_ven)(void *p, uint8_t val); - - pc_timer_t send_delay_timer; } atkbd_t; -/* bit 0 = repeat, bit 1 = makes break code? */ +/* Global keyboard flags for scan code set 3: + bit 0 = repeat, bit 1 = makes break code? */ uint8_t keyboard_set3_flags[512]; uint8_t keyboard_set3_all_repeat; uint8_t keyboard_set3_all_break; -/* Bits 0 - 1 = scan code set, bit 6 = translate or not. */ -uint8_t keyboard_mode = 0x42; +/* Global keyboard mode: + Bits 0 - 1 = scan code set, bit 6 = translate or not. */ +uint8_t keyboard_mode = 0x42; -static uint8_t key_ctrl_queue[64]; -static int key_ctrl_queue_start = 0, key_ctrl_queue_end = 0; -static uint8_t key_queue[16]; -static int key_queue_start = 0, key_queue_end = 0; -static uint8_t key_cmd_queue[16]; -static int key_cmd_queue_start = 0, key_cmd_queue_end = 0; -uint8_t mouse_queue[16]; -int mouse_queue_start = 0, mouse_queue_end = 0; -uint8_t mouse_cmd_queue[16]; -int mouse_cmd_queue_start = 0, mouse_cmd_queue_end = 0; -static uint8_t kbd_last_scan_code; static void (*mouse_write)(uint8_t val, void *priv) = NULL; static void *mouse_p = NULL; -static uint8_t sc_or = 0; static atkbd_t *SavedKbd = NULL; // FIXME: remove!!! --FvK /* Non-translated to translated scan codes. */ @@ -584,6 +626,7 @@ static const scancode scancode_set3[512] = { static void add_data_kbd(uint16_t val); +// #define ENABLE_KEYBOARD_AT_LOG 1 #ifdef ENABLE_KEYBOARD_AT_LOG int keyboard_at_do_log = ENABLE_KEYBOARD_AT_LOG; @@ -634,29 +677,31 @@ set_scancode_map(atkbd_t *dev) static void kbc_queue_reset(uint8_t channel) { + atkbd_t *dev = SavedKbd; + switch (channel) { case 1: - key_queue_start = key_queue_end = 0; - memset(key_queue, 0x00, sizeof(key_queue)); + dev->key_queue_start = dev->key_queue_end = 0; + memset(dev->key_queue, 0x00, sizeof(dev->key_queue)); /* FALLTHROUGH */ case 4: - key_cmd_queue_start = key_cmd_queue_end = 0; - memset(key_cmd_queue, 0x00, sizeof(key_cmd_queue)); + dev->key_cmd_queue_start = dev->key_cmd_queue_end = 0; + memset(dev->key_cmd_queue, 0x00, sizeof(dev->key_cmd_queue)); break; case 2: - mouse_queue_start = mouse_queue_end = 0; - memset(mouse_queue, 0x00, sizeof(mouse_queue)); + dev->mouse_queue_start = dev->mouse_queue_end = 0; + memset(dev->mouse_queue, 0x00, sizeof(dev->mouse_queue)); /* FALLTHROUGH */ case 3: - mouse_cmd_queue_start = mouse_cmd_queue_end = 0; - memset(mouse_cmd_queue, 0x00, sizeof(mouse_cmd_queue)); + dev->mouse_cmd_queue_start = dev->mouse_cmd_queue_end = 0; + memset(dev->mouse_cmd_queue, 0x00, sizeof(dev->mouse_cmd_queue)); break; case 0: default: - key_ctrl_queue_start = key_ctrl_queue_end = 0; - memset(key_ctrl_queue, 0x00, sizeof(key_ctrl_queue)); + dev->key_ctrl_queue_start = dev->key_ctrl_queue_end = 0; + memset(dev->key_ctrl_queue, 0x00, sizeof(dev->key_ctrl_queue)); } } @@ -665,30 +710,30 @@ kbc_queue_add(atkbd_t *dev, uint8_t val, uint8_t channel) { switch (channel) { case 4: - kbd_log("ATkbc: key_cmd_queue[%02X] = %02X;\n", key_cmd_queue_end, val); - key_cmd_queue[key_cmd_queue_end] = val; - key_cmd_queue_end = (key_cmd_queue_end + 1) & 0xf; + kbd_log("ATkbc: dev->key_cmd_queue[%02X] = %02X;\n", dev->key_cmd_queue_end, val); + dev->key_cmd_queue[dev->key_cmd_queue_end] = val; + dev->key_cmd_queue_end = (dev->key_cmd_queue_end + 1) & 0xf; break; case 3: - kbd_log("ATkbc: mouse_cmd_queue[%02X] = %02X;\n", mouse_cmd_queue_end, val); - mouse_cmd_queue[mouse_cmd_queue_end] = val; - mouse_cmd_queue_end = (mouse_cmd_queue_end + 1) & 0xf; + kbd_log("ATkbc: dev->mouse_cmd_queue[%02X] = %02X;\n", dev->mouse_cmd_queue_end, val); + dev->mouse_cmd_queue[dev->mouse_cmd_queue_end] = val; + dev->mouse_cmd_queue_end = (dev->mouse_cmd_queue_end + 1) & 0xf; break; case 2: - kbd_log("ATkbc: mouse_queue[%02X] = %02X;\n", mouse_queue_end, val); - mouse_queue[mouse_queue_end] = val; - mouse_queue_end = (mouse_queue_end + 1) & 0xf; + kbd_log("ATkbc: dev->mouse_queue[%02X] = %02X;\n", dev->mouse_queue_end, val); + dev->mouse_queue[dev->mouse_queue_end] = val; + dev->mouse_queue_end = (dev->mouse_queue_end + 1) & 0xf; break; case 1: - kbd_log("ATkbc: key_queue[%02X] = %02X;\n", key_queue_end, val); - key_queue[key_queue_end] = val; - key_queue_end = (key_queue_end + 1) & 0xf; + kbd_log("ATkbc: dev->key_queue[%02X] = %02X;\n", dev->key_queue_end, val); + dev->key_queue[dev->key_queue_end] = val; + dev->key_queue_end = (dev->key_queue_end + 1) & 0xf; break; case 0: default: - kbd_log("ATkbc: key_ctrl_queue[%02X] = %02X;\n", key_ctrl_queue_end, val); - key_ctrl_queue[key_ctrl_queue_end] = val; - key_ctrl_queue_end = (key_ctrl_queue_end + 1) & 0x3f; + kbd_log("ATkbc: dev->key_ctrl_queue[%02X] = %02X;\n", dev->key_ctrl_queue_end, val); + dev->key_ctrl_queue[dev->key_ctrl_queue_end] = val; + dev->key_ctrl_queue_end = (dev->key_ctrl_queue_end + 1) & 0x3f; break; } } @@ -698,22 +743,123 @@ kbc_irq(atkbd_t *dev, uint16_t irq, int raise) { picint_common(irq, (dev->flags & KBC_TYPE_MASK) > KBC_TYPE_PS2_NOREF, raise); if (raise) - dev->irq_levels |= irq; + dev->irq_levels = irq; else dev->irq_levels &= ~irq; } +static int +kbc_translate(uint8_t val) +{ + atkbd_t *dev = SavedKbd; + int xt_mode = (keyboard_mode & 0x20) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF); + int translate = (keyboard_mode & 0x40); + uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; + int ret = - 1; + + translate = translate || (keyboard_mode & 0x40) || xt_mode; + translate = translate || ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2); + + /* Allow for scan code translation. */ + if (translate && (val == 0xf0)) { + kbd_log("ATkbd: translate is on, F0 prefix detected\n"); + dev->sc_or = 0x80; + return ret; + } + + /* Skip break code if translated make code has bit 7 set. */ + if (translate && (dev->sc_or == 0x80) && (nont_to_t[val] & 0x80)) { + kbd_log("ATkbd: translate is on, skipping scan code: %02X (original: F0 %02X)\n", nont_to_t[val], val); + dev->sc_or = 0; + return ret; + } + + /* Test for T3100E 'Fn' key (Right Alt / Right Ctrl) */ + if ((dev != NULL) && (kbc_ven == KBC_VEN_TOSHIBA) && + (keyboard_recv(0x138) || keyboard_recv(0x11d))) switch (val) { + case 0x4f: + t3100e_notify_set(0x01); + break; /* End */ + case 0x50: + t3100e_notify_set(0x02); + break; /* Down */ + case 0x51: + t3100e_notify_set(0x03); + break; /* PgDn */ + case 0x52: + t3100e_notify_set(0x04); + break; /* Ins */ + case 0x53: + t3100e_notify_set(0x05); + break; /* Del */ + case 0x54: + t3100e_notify_set(0x06); + break; /* SysRQ */ + case 0x45: + t3100e_notify_set(0x07); + break; /* NumLock */ + case 0x46: + t3100e_notify_set(0x08); + break; /* ScrLock */ + case 0x47: + t3100e_notify_set(0x09); + break; /* Home */ + case 0x48: + t3100e_notify_set(0x0a); + break; /* Up */ + case 0x49: + t3100e_notify_set(0x0b); + break; /* PgUp */ + case 0x4a: + t3100e_notify_set(0x0c); + break; /* Keypad - */ + case 0x4b: + t3100e_notify_set(0x0d); + break; /* Left */ + case 0x4c: + t3100e_notify_set(0x0e); + break; /* KP 5 */ + case 0x4d: + t3100e_notify_set(0x0f); + break; /* Right */ + } + + kbd_log("ATkbd: translate is %s, ", translate ? "on" : "off"); +#ifdef ENABLE_KEYBOARD_AT_LOG + kbd_log("scan code: "); + if (translate) { + kbd_log("%02X (original: ", (nont_to_t[val] | dev->sc_or)); + if (dev->sc_or == 0x80) + kbd_log("F0 "); + kbd_log("%02X)\n", val); + } else + kbd_log("%02X\n", val); +#endif + + ret = translate ? (nont_to_t[val] | dev->sc_or) : val; + + if (dev->sc_or == 0x80) + dev->sc_or = 0; + + return ret; +} + static void add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_hi) { uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; + int temp = (channel == 1) ? kbc_translate(val) : val; - if ((kbc_ven == KBC_VEN_AMI) || ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF)) + if (temp == -1) + return; + + if ((kbc_ven == KBC_VEN_AMI) || (kbc_ven == KBC_VEN_TG) || + (kbc_ven == KBC_VEN_TG_GREEN) || ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF)) stat_hi |= ((dev->input_port & 0x80) ? 0x10 : 0x00); else stat_hi |= 0x10; - kbd_log("ATkbc: Adding %02X to front...\n", val); + kbd_log("ATkbc: Adding %02X to front on channel %i...\n", temp, channel); dev->status = (dev->status & ~0xf0) | STAT_OFULL | stat_hi; /* WARNING: On PS/2, all IRQ's are level-triggered, but the IBM PS/2 KBC firmware is explicitly @@ -733,57 +879,39 @@ add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_ } else if (dev->mem[0x20] & 0x01) picintlevel(1 << 1); /* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */ - dev->out = val; + dev->out = temp; } static void add_data_kbd_cmd_queue(atkbd_t *dev, uint8_t val) { - if ((dev->reset_delay > 0) || (key_cmd_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", (dev->reset_delay > 0), (key_cmd_queue_end >= 16)); + if ((dev->reset_delay > 0) || (dev->key_cmd_queue_end >= 16)) { + kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", (dev->reset_delay > 0), (dev->key_cmd_queue_end >= 16)); return; } - kbd_log("ATkbc: key_cmd_queue[%02X] = %02X;\n", key_cmd_queue_end, val); + kbd_log("ATkbc: dev->key_cmd_queue[%02X] = %02X;\n", dev->key_cmd_queue_end, val); kbc_queue_add(dev, val, 4); - kbd_last_scan_code = val; + dev->kbd_last_scan_code = val; } static void add_data_kbd_queue(atkbd_t *dev, uint8_t val) { - if (!keyboard_scan || (dev->reset_delay > 0) || (key_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i, %i\n", !keyboard_scan, (dev->reset_delay > 0), (key_queue_end >= 16)); + if (!keyboard_scan || (dev->reset_delay > 0) || (dev->key_queue_end >= 16)) { + kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i, %i\n", !keyboard_scan, (dev->reset_delay > 0), (dev->key_queue_end >= 16)); return; } - kbd_log("ATkbc: key_queue[%02X] = %02X;\n", key_queue_end, val); + kbd_log("ATkbc: key_queue[%02X] = %02X;\n", dev->key_queue_end, val); kbc_queue_add(dev, val, 1); - kbd_last_scan_code = val; + dev->kbd_last_scan_code = val; } static void add_data_kbd_front(atkbd_t *dev, uint8_t val) { - int xt_mode = (keyboard_mode & 0x20) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF); - int translate = (keyboard_mode & 0x40); - uint8_t send; - if (dev->reset_delay) return; - translate = translate || (keyboard_mode & 0x40) || xt_mode; - translate = translate || ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2); - - if (translate) - send = nont_to_t[val]; - else - send = val; - - add_data_kbd_cmd_queue(dev, send); -} - -static void -add_data_kbd_raw(atkbd_t *dev, uint8_t val) -{ add_data_kbd_cmd_queue(dev, val); } @@ -804,244 +932,489 @@ set_enable_mouse(atkbd_t *dev, uint8_t enable) dev->mem[0x20] |= (enable ? 0x00 : 0x20); } +static void +kbc_ibf_process(atkbd_t *dev) +{ + /* IBF set, process both commands and data. */ + dev->status &= ~STAT_IFULL; + dev->kbc_state = KBC_STATE_MAIN_IBF; + if (dev->status & STAT_CD) + kbc_process_cmd(dev); + else { + set_enable_kbd(dev, 1); + dev->key_wantcmd = 1; + dev->key_dat = dev->ib; + dev->kbc_state = KBC_STATE_SEND_KBD; + } +} + +static void +kbc_scan_kbd_at(atkbd_t *dev) +{ + if (!(dev->mem[0x20] & 0x10)) { + /* Both OBF and IBF clear and keyboard is enabled. */ + /* XT mode. */ + if (dev->mem[0x20] & 0x20) { + if (dev->out_new != -1) { + add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00); + dev->out_new = -1; + dev->kbc_state = KBC_STATE_MAIN_IBF; + } else if (dev->status & STAT_IFULL) + kbc_ibf_process(dev); + /* AT mode. */ + } else { + // dev->t = dev->mem[0x28]; + if (dev->mem[0x2e] != 0x00) { + // if (!(dev->t & 0x02)) + // return; + dev->mem[0x2e] = 0x00; + } + dev->output_port &= 0xbf; + if (dev->out_new != -1) { + /* In our case, we never have noise on the line, so we can simplify this. */ + /* Read data from the keyboard. */ + if (dev->mem[0x20] & 0x40) { + if ((dev->mem[0x20] & 0x08) || (dev->input_port & 0x80)) + add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00); + dev->mem[0x2d] = (dev->out_new == 0xf0) ? 0x80 : 0x00; + } else + add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00); + dev->out_new = -1; + dev->kbc_state = KBC_STATE_MAIN_IBF; + } + } + } +} + +static void write_output(atkbd_t *dev, uint8_t val); + +static void +kbc_poll_at(atkbd_t *dev) +{ + switch (dev->kbc_state) { + case KBC_STATE_RESET: + if (dev->status & STAT_IFULL) { + dev->status = ((dev->status & 0x0f) | 0x10) & ~STAT_IFULL; + if ((dev->status & STAT_CD) && (dev->ib == 0xaa)) + kbc_process_cmd(dev); + } + break; + case KBC_STATE_MAIN_IBF: + default: + if (dev->status & STAT_OFULL) { + /* OBF set, wait until it is cleared but still process commands. */ + if ((dev->status & STAT_IFULL) && (dev->status & STAT_CD)) { + dev->status &= ~STAT_IFULL; + kbc_process_cmd(dev); + } + } else if (dev->status & STAT_IFULL) + kbc_ibf_process(dev); + else if (!(dev->mem[0x20] & 0x10)) + dev->kbc_state = KBC_STATE_MAIN_KBD; + break; + case KBC_STATE_MAIN_KBD: + case KBC_STATE_MAIN_BOTH: + if (dev->status & STAT_IFULL) + kbc_ibf_process(dev); + else { + (void) kbc_scan_kbd_at(dev); + dev->kbc_state = KBC_STATE_MAIN_IBF; + } + break; + case KBC_STATE_KBC_OUT: + /* Keyboard controller command want to output multiple bytes. */ + if (dev->status & STAT_IFULL) { + /* Data from host aborts dumping. */ + dev->kbc_state = KBC_STATE_MAIN_IBF; + kbc_ibf_process(dev); + } + /* Do not continue dumping until OBF is clear. */ + if (!(dev->status & STAT_OFULL)) { + kbd_log("ATkbc: %02X coming from channel 0\n", dev->key_ctrl_queue[dev-.key_ctrl_queue_start]); + add_to_kbc_queue_front(dev, dev->key_ctrl_queue[dev->key_ctrl_queue_start], 0, 0x00); + dev->key_ctrl_queue_start = (dev->key_ctrl_queue_start + 1) & 0x3f; + if (dev->key_ctrl_queue_start == dev->key_ctrl_queue_end) + dev->kbc_state = KBC_STATE_MAIN_IBF; + } + break; + case KBC_STATE_KBC_PARAM: + /* Keyboard controller command wants data, wait for said data. */ + if (dev->status & STAT_IFULL) { + /* Command written, abort current command. */ + if (dev->status & STAT_CD) + dev->kbc_state = KBC_STATE_MAIN_IBF; + + dev->status &= ~STAT_IFULL; + kbc_process_cmd(dev); + } + break; + case KBC_STATE_SEND_KBD: + if (!dev->key_wantcmd) + dev->kbc_state = KBC_STATE_SCAN_KBD; + break; + case KBC_STATE_SCAN_KBD: + kbc_scan_kbd_at(dev); + break; + } +} + +/* + Correct Procedure: + 1. Controller asks the device (keyboard or mouse) for a byte. + 2. The device, unless it's in the reset or command states, sees if there's anything to give it, + and if yes, begins the transfer. + 3. The controller checks if there is a transfer, if yes, transfers the byte and sends it to the host, + otherwise, checks the next device, or if there is no device left to check, checks if IBF is full + and if yes, processes it. + */ +static int +kbc_scan_kbd_ps2(atkbd_t *dev) +{ + if (dev->out_new != -1) { + kbd_log("ATkbc: %02X coming from channel 1\n", dev->out_new & 0xff); + add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00); + dev->out_new = -1; + dev->kbc_state = KBC_STATE_MAIN_IBF; + return 1; + } + + return 0; +} + +static int +kbc_scan_aux_ps2(atkbd_t *dev) +{ + if (dev->out_new_mouse != -1) { + kbd_log("ATkbc: %02X coming from channel 2\n", dev->out_new_mouse & 0xff); + add_to_kbc_queue_front(dev, dev->out_new_mouse, 2, 0x00); + dev->out_new_mouse = -1; + dev->kbc_state = KBC_STATE_MAIN_IBF; + return 1; + } + + return 0; +} + +static void +kbc_poll_ps2(atkbd_t *dev) +{ + switch (dev->kbc_state) { + case KBC_STATE_RESET: + // pclog("KBC_STATE_RESET\n"); + if (dev->status & STAT_IFULL) { + dev->status = ((dev->status & 0x0f) | 0x10) & ~STAT_IFULL; + if ((dev->status & STAT_CD) && (dev->ib == 0xaa)) + kbc_process_cmd(dev); + } + break; + case KBC_STATE_MAIN_IBF: + default: + // pclog("KBC_STATE_MAIN_IBF\n"); + if (dev->status & STAT_IFULL) + kbc_ibf_process(dev); + else if (!(dev->status & STAT_OFULL)) { + if (dev->mem[0x20] & 0x20) { + if (!(dev->mem[0x20] & 0x10)) { + dev->output_port &= 0xbf; + dev->kbc_state = KBC_STATE_MAIN_KBD; + } + } else { + dev->output_port &= 0xf7; + if (dev->mem[0x20] & 0x10) + dev->kbc_state = KBC_STATE_MAIN_MOUSE; + else { + dev->output_port &= 0xbf; + dev->kbc_state = KBC_STATE_MAIN_BOTH; + } + } + } + break; + case KBC_STATE_MAIN_KBD: + // pclog("KBC_STATE_MAIN_KBD\n"); + if (dev->status & STAT_IFULL) + kbc_ibf_process(dev); + else { + (void) kbc_scan_kbd_ps2(dev); + dev->kbc_state = KBC_STATE_MAIN_IBF; + } + break; + case KBC_STATE_MAIN_MOUSE: + // pclog("KBC_STATE_MAIN_MOUSE\n"); + if (dev->status & STAT_IFULL) + kbc_ibf_process(dev); + else { + (void) kbc_scan_aux_ps2(dev); + dev->kbc_state = KBC_STATE_MAIN_IBF; + } + break; + case KBC_STATE_MAIN_BOTH: + // pclog("KBC_STATE_MAIN_BOTH\n"); + if (kbc_scan_kbd_ps2(dev)) + dev->kbc_state = KBC_STATE_MAIN_IBF; + else + dev->kbc_state = KBC_STATE_MAIN_MOUSE; + break; + case KBC_STATE_KBC_OUT: + // pclog("KBC_STATE_KBC_OUT\n"); + /* Keyboard controller command want to output multiple bytes. */ + if (dev->status & STAT_IFULL) { + /* Data from host aborts dumping. */ + dev->kbc_state = KBC_STATE_MAIN_IBF; + kbc_ibf_process(dev); + } + /* Do not continue dumping until OBF is clear. */ + if (!(dev->status & STAT_OFULL)) { + kbd_log("ATkbc: %02X coming from channel 0\n", dev->out_new & 0xff); + add_to_kbc_queue_front(dev, dev->key_ctrl_queue[dev->key_ctrl_queue_start], 0, 0x00); + dev->key_ctrl_queue_start = (dev->key_ctrl_queue_start + 1) & 0x3f; + if (dev->key_ctrl_queue_start == dev->key_ctrl_queue_end) + dev->kbc_state = KBC_STATE_MAIN_IBF; + } + break; + case KBC_STATE_KBC_PARAM: + // pclog("KBC_STATE_KBC_PARAM\n"); + /* Keyboard controller command wants data, wait for said data. */ + if (dev->status & STAT_IFULL) { + /* Command written, abort current command. */ + if (dev->status & STAT_CD) + dev->kbc_state = KBC_STATE_MAIN_IBF; + + dev->status &= ~STAT_IFULL; + kbc_process_cmd(dev); + } + break; + case KBC_STATE_SEND_KBD: + if (!dev->key_wantcmd) + dev->kbc_state = KBC_STATE_SCAN_KBD; + break; + case KBC_STATE_SCAN_KBD: + // pclog("KBC_STATE_SCAN_KBD\n"); + (void) kbc_scan_kbd_ps2(dev); + break; + case KBC_STATE_SEND_MOUSE: + if (!dev->mouse_wantcmd) + dev->kbc_state = KBC_STATE_SCAN_MOUSE; + break; + case KBC_STATE_SCAN_MOUSE: + // pclog("KBC_STATE_SCAN_MOUSE\n"); + (void) kbc_scan_aux_ps2(dev); + break; + } +} + +static void +kbc_poll_kbd(atkbd_t *dev) +{ + switch (dev->kbd_state) { + case DEV_STATE_RESET: + /* Reset state. */ + if (dev->reset_delay) { + dev->reset_delay--; + if (!dev->reset_delay) { + kbd_log("ATkbc: Sending AA on keyboard reset...\n"); + add_data_kbd_front(dev, 0xaa); + dev->kbd_state = DEV_STATE_RESET_OUT; + } + } + break; + case DEV_STATE_MAIN_1: + /* Process the command if needed and then return to main loop #2. */ + if (dev->key_wantcmd) { + kbd_log("ATkbc: Processing keyboard command...\n"); + kbc_queue_reset(4); + // dev->out_new = -1; + kbd_process_cmd(dev); + dev->key_wantcmd = 0; + } else + dev->kbd_state = DEV_STATE_MAIN_2; + break; + case DEV_STATE_MAIN_2: + /* Output from scan queue if needed and then return to main loop #1. */ + if (keyboard_scan && (dev->out_new == -1) && (dev->key_queue_start != dev->key_queue_end)) { + kbd_log("ATkbc: %02X (DATA) on channel 1\n", dev->key_queue[key_queue_start]); + dev->out_new = dev->key_queue[dev->key_queue_start]; + dev->key_queue_start = (dev->key_queue_start + 1) & 0xf; + } + if (!keyboard_scan || (dev->key_queue_start == dev->key_queue_end)) + dev->kbd_state = DEV_STATE_MAIN_1; + break; + case DEV_STATE_MAIN_OUT: + case DEV_STATE_RESET_OUT: + /* Output command response and then return to main loop #2. */ + if ((dev->out_new == -1) && (dev->key_cmd_queue_start != dev->key_cmd_queue_end)) { + kbd_log("ATkbc: %02X (CMD ) on channel 1\n", key_cmd_queue[key_cmd_queue_start]); + dev->out_new = dev->key_cmd_queue[dev->key_cmd_queue_start]; + dev->key_cmd_queue_start = (dev->key_cmd_queue_start + 1) & 0xf; + } + if (dev->key_cmd_queue_start == dev->key_cmd_queue_end) + dev->kbd_state = (dev->kbd_state == DEV_STATE_RESET_OUT) ? DEV_STATE_MAIN_1 : DEV_STATE_MAIN_2; + break; + case DEV_STATE_MAIN_WANT_IN: + /* Output command response and then wait for host data. */ + if ((dev->out_new == -1) && (dev->key_cmd_queue_start != dev->key_cmd_queue_end)) { + kbd_log("ATkbc: %02X (CMD ) on channel 1\n", dev->key_cmd_queue[dev->key_cmd_queue_start]); + dev->out_new = dev->key_cmd_queue[dev->key_cmd_queue_start]; + dev->key_cmd_queue_start = (dev->key_cmd_queue_start + 1) & 0xf; + } + if (dev->key_cmd_queue_start == dev->key_cmd_queue_end) + dev->kbd_state = DEV_STATE_MAIN_IN; + break; + case DEV_STATE_MAIN_IN: + /* Wait for host data. */ + if (dev->key_wantcmd) { + kbd_log("ATkbc: Processing keyboard command...\n"); + kbc_queue_reset(4); + // dev->out_new = -1; + kbd_process_cmd(dev); + dev->key_wantcmd = 0; + } + break; + case DEV_STATE_MAIN_WANT_RESET: + /* Output command response and then go to the reset state. */ + if ((dev->out_new == -1) && (dev->key_cmd_queue_start != dev->key_cmd_queue_end)) { + kbd_log("ATkbc: %02X (CMD ) on channel 1\n", dev->key_cmd_queue[dev->key_cmd_queue_start]); + dev->out_new = dev->key_cmd_queue[dev->key_cmd_queue_start]; + dev->key_cmd_queue_start = (dev->key_cmd_queue_start + 1) & 0xf; + } + if (dev->key_cmd_queue_start == dev->key_cmd_queue_end) + dev->kbd_state = DEV_STATE_RESET; + break; + } +} + +static void +kbc_poll_aux(atkbd_t *dev) +{ + switch (dev->mouse_state) { +#if 0 + case DEV_STATE_RESET: + /* Reset state. */ + if (dev->mouse_reset_delay) { + dev->mouse_reset_delay--; + if (!dev->mouse_reset_delay) { + kbd_log("ATkbc: Sending AA 00 on mouse reset...\n"); + keyboard_at_adddata_mouse_cmd(0xaa); + keyboard_at_adddata_mouse_cmd(0x00); + dev->mouse_state = DEV_STATE_RESET_OUT; + } + } + break; +#endif + case DEV_STATE_MAIN_1: + /* Process the command if needed and then return to main loop #2. */ + if (dev->mouse_wantcmd) { + kbd_log("ATkbc: Processing mouse command...\n"); + kbc_queue_reset(3); + // dev->out_new_mouse = -1; + dev->mouse_state = DEV_STATE_MAIN_OUT; + mouse_write(dev->mouse_dat, mouse_p); + if ((dev->mouse_dat == 0xe8) || (dev->mouse_dat == 0xf3)) + dev->mouse_state = DEV_STATE_MAIN_WANT_IN; + dev->mouse_wantcmd = 0; + } else + dev->mouse_state = DEV_STATE_MAIN_2; + break; + case DEV_STATE_MAIN_2: + /* Output from scan queue if needed and then return to main loop #1. */ + if (mouse_scan && (dev->out_new_mouse == -1) && (dev->mouse_queue_start != dev->mouse_queue_end)) { + kbd_log("ATkbc: %02X (DATA) on channel 2\n", dev->mouse_queue[dev->mouse_queue_start]); + dev->out_new_mouse = dev->mouse_queue[dev->mouse_queue_start]; + dev->mouse_queue_start = (dev->mouse_queue_start + 1) & 0xf; + } + if (!mouse_scan || (dev->mouse_queue_start == dev->mouse_queue_end)) + dev->mouse_state = DEV_STATE_MAIN_1; + break; + case DEV_STATE_MAIN_OUT: + case DEV_STATE_RESET_OUT: + /* Output command response and then return to main loop #2. */ + if ((dev->out_new_mouse == -1) && (dev->mouse_cmd_queue_start != dev->mouse_cmd_queue_end)) { + kbd_log("ATkbc: %02X (CMD ) on channel 2\n", dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]); + dev->out_new_mouse = dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]; + dev->mouse_cmd_queue_start = (dev->mouse_cmd_queue_start + 1) & 0xf; + } + if (dev->mouse_cmd_queue_start == dev->mouse_cmd_queue_end) + dev->mouse_state = (dev->mouse_state == DEV_STATE_RESET_OUT) ? DEV_STATE_MAIN_1 : DEV_STATE_MAIN_2; + break; + case DEV_STATE_MAIN_WANT_IN: + /* Output command response and then wait for host data. */ + if ((dev->out_new_mouse == -1) && (dev->mouse_cmd_queue_start != dev->mouse_cmd_queue_end)) { + kbd_log("ATkbc: %02X (CMD ) on channel 2\n", dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]); + dev->out_new_mouse = dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]; + dev->mouse_cmd_queue_start = (dev->mouse_cmd_queue_start + 1) & 0xf; + } + if (dev->mouse_cmd_queue_start == dev->mouse_cmd_queue_end) + dev->mouse_state = DEV_STATE_MAIN_IN; + break; + case DEV_STATE_MAIN_IN: + /* Wait for host data. */ + if (dev->mouse_wantcmd) { + kbd_log("ATkbc: Processing mouse command...\n"); + kbc_queue_reset(3); + // dev->out_new_mouse = -1; + dev->mouse_state = DEV_STATE_MAIN_OUT; + mouse_write(dev->mouse_dat, mouse_p); + dev->mouse_wantcmd = 0; + } + break; + case DEV_STATE_MAIN_WANT_RESET: + /* Output command response and then go to the reset state. */ + if ((dev->out_new_mouse == -1) && (dev->mouse_cmd_queue_start != dev->mouse_cmd_queue_end)) { + kbd_log("ATkbc: %02X (CMD ) on channel 2\n", dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]); + dev->out_new_mouse = dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]; + dev->mouse_cmd_queue_start = (dev->mouse_cmd_queue_start + 1) & 0xf; + } + if (dev->mouse_cmd_queue_start == dev->mouse_cmd_queue_end) + dev->mouse_state = DEV_STATE_RESET; + break; + } +} + /* TODO: State machines for controller, keyboard, and mouse. */ static void kbd_poll(void *priv) { atkbd_t *dev = (atkbd_t *) priv; - int mouse_enabled; timer_advance_u64(&dev->send_delay_timer, (100ULL * TIMER_USEC)); - mouse_enabled = !(dev->mem[0x20] & 0x20) && ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF); + /* TODO: Use a fuction pointer for this (also needed to the AMI KBC mode switching) + and implement the password security state. */ + if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) + kbc_poll_ps2(dev); + else + kbc_poll_at(dev); - switch (dev->kbc_state) { - /* Reset state. */ - case KBC_STATE_RESET: - if ((dev->status & STAT_IFULL) && (dev->status & STAT_CD) && (dev->ib == 0xaa)) - kbc_process_cmd(dev); - break; - /* Process commands and/or monitor the attached devices. */ - case KBC_STATE_NORMAL: - if (!(dev->status & STAT_OFULL)) { - if (dev->status & STAT_IFULL) { - dev->status &= ~STAT_IFULL; - if ((dev->status & STAT_CD) || dev->want60) - kbc_process_cmd(dev); - else if (!(dev->status & STAT_CD) && !dev->want60) { - dev->status &= ~STAT_IFULL; - set_enable_kbd(dev, 1); - kbc_queue_reset(4); - dev->key_wantcmd = 1; - dev->key_dat = dev->ib; - dev->kbc_state = KBC_STATE_KBD; - } - } else { - if (key_ctrl_queue_start != key_ctrl_queue_end) { - kbd_log("ATkbc: %02X coming from channel 0\n", dev->out_new & 0xff); - add_to_kbc_queue_front(dev, key_ctrl_queue[key_ctrl_queue_start], 0, 0x00); - key_ctrl_queue_start = (key_ctrl_queue_start + 1) & 0x3f; - } else if (mouse_enabled && (dev->out_new_mouse != -1)) { - kbd_log("ATkbc: %02X coming from channel 2\n", dev->out_new_mouse); - add_to_kbc_queue_front(dev, dev->out_new_mouse, 2, 0x00); - dev->out_new_mouse = -1; - } else if (!(dev->mem[0x20] & 0x10) && (dev->out_new != -1)) { - kbd_log("ATkbc: %02X coming from channel 1\n", dev->out_new); - add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00); - dev->out_new = -1; - } - } - } - break; - /* Wait for keyboard command response. */ - case KBC_STATE_KBD: - if (!(dev->mem[0x20] & 0x10) && (dev->out_new != -1)) { - kbd_log("ATkbc: %02X coming from channel 1\n", dev->out_new); - add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00); - dev->out_new = -1; - dev->kbc_state = KBC_STATE_NORMAL; - } - break; - /* Wait for keyboard mouse response. */ - case KBC_STATE_MOUSE: - if (mouse_enabled && (dev->out_new_mouse != -1)) { - kbd_log("ATkbc: %02X coming from channel 2\n", dev->out_new_mouse); - add_to_kbc_queue_front(dev, dev->out_new_mouse, 2, 0x00); - dev->out_new_mouse = -1; - dev->kbc_state = KBC_STATE_NORMAL; - } - break; - } + kbc_poll_kbd(dev); - if (dev->reset_delay) { - dev->reset_delay--; - if (!dev->reset_delay) { - kbd_log("ATkbc: Sending AA on keyboard reset...\n"); - add_data_kbd_front(dev, 0xaa); - } - } else if (dev->key_wantcmd) { - if ((key_cmd_queue_start == key_cmd_queue_end) && (dev->out_new == -1) && (dev->reset_delay == 0)) { - kbd_log("ATkbc: Processing keyboard command...\n"); - kbd_process_cmd(dev); - dev->key_wantcmd = 0; - } - return; - } - if (dev->out_new == -1) { - if (key_cmd_queue_start != key_cmd_queue_end) { - kbd_log("ATkbc: %02X (CMD ) on channel 1\n", key_cmd_queue[key_cmd_queue_start]); - dev->out_new = key_cmd_queue[key_cmd_queue_start]; - key_cmd_queue_start = (key_cmd_queue_start + 1) & 0xf; - } else if (key_queue_start != key_queue_end) { - kbd_log("ATkbc: %02X (DATA) on channel 1\n", key_queue[key_queue_start]); - dev->out_new = key_queue[key_queue_start]; - key_queue_start = (key_queue_start + 1) & 0xf; - } - } - - if (dev->mouse_reset_delay) { - dev->mouse_reset_delay--; - if (!dev->mouse_reset_delay) { - kbd_log("ATkbc: Sending AA 00 on mouse reset...\n"); - // keyboard_at_adddata_mouse_cmd(0xaa); - // keyboard_at_adddata_mouse_cmd(0x00); - } - } else if (dev->mouse_wantcmd) { - if ((mouse_cmd_queue_start == mouse_cmd_queue_end) && (dev->out_new == -1) && (dev->mouse_reset_delay == 0)) { - mouse_write(dev->mouse_dat, mouse_p); - // if (dev->mouse_dat == 0xff) - // dev->mouse_reset_delay = RESET_DELAY_TIME; - dev->mouse_wantcmd = 0; - } - } - if (dev->out_new_mouse == -1) { - if (mouse_cmd_queue_start != mouse_cmd_queue_end) { - kbd_log("ATkbc: %02X (CMD ) on channel 2\n", mouse_cmd_queue[mouse_cmd_queue_start]); - dev->out_new_mouse = mouse_cmd_queue[mouse_cmd_queue_start]; - mouse_cmd_queue_start = (mouse_cmd_queue_start + 1) & 0xf; - } else if (mouse_queue_start != mouse_queue_end) { - kbd_log("ATkbc: %02X (DATA) on channel 2\n", mouse_queue[mouse_queue_start]); - dev->out_new_mouse = mouse_queue[mouse_queue_start]; - mouse_queue_start = (mouse_queue_start + 1) & 0xf; - } - } + if (((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) && mouse_write) + kbc_poll_aux(dev); } static void add_data_vals(atkbd_t *dev, uint8_t *val, uint8_t len) { - int xt_mode = (keyboard_mode & 0x20) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF); - int translate = (keyboard_mode & 0x40); int i; - uint8_t or = 0; - uint8_t send; if (dev->reset_delay) return; - translate = translate || (keyboard_mode & 0x40) || xt_mode; - translate = translate || ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2); - - for (i = 0; i < len; i++) { - if (translate) { - if (val[i] == 0xf0) { - or = 0x80; - continue; - } - send = nont_to_t[val[i]] | or ; - if (or == 0x80) - or = 0; - } else - send = val[i]; - - add_data_kbd_queue(dev, send); - } + for (i = 0; i < len; i++) + add_data_kbd_queue(dev, val[i]); } static void add_data_kbd(uint16_t val) { atkbd_t *dev = SavedKbd; - int xt_mode = (keyboard_mode & 0x20) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF); - int translate = (keyboard_mode & 0x40); uint8_t fake_shift[4]; uint8_t num_lock = 0, shift_states = 0; - uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; if (dev->reset_delay) return; - translate = translate || (keyboard_mode & 0x40) || xt_mode; - translate = translate || ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2); - keyboard_get_states(NULL, &num_lock, NULL); shift_states = keyboard_get_shift() & STATE_SHIFT_MASK; - /* Allow for scan code translation. */ - if (translate && (val == 0xf0)) { - kbd_log("ATkbd: translate is on, F0 prefix detected\n"); - sc_or = 0x80; - return; - } - - /* Skip break code if translated make code has bit 7 set. */ - if (translate && (sc_or == 0x80) && (val & 0x80)) { - kbd_log("ATkbd: translate is on, skipping scan code: %02X (original: F0 %02X)\n", nont_to_t[val], val); - sc_or = 0; - return; - } - - /* Test for T3100E 'Fn' key (Right Alt / Right Ctrl) */ - if ((dev != NULL) && (kbc_ven == KBC_VEN_TOSHIBA) && (keyboard_recv(0x138) || keyboard_recv(0x11d))) - switch (val) { - case 0x4f: - t3100e_notify_set(0x01); - break; /* End */ - case 0x50: - t3100e_notify_set(0x02); - break; /* Down */ - case 0x51: - t3100e_notify_set(0x03); - break; /* PgDn */ - case 0x52: - t3100e_notify_set(0x04); - break; /* Ins */ - case 0x53: - t3100e_notify_set(0x05); - break; /* Del */ - case 0x54: - t3100e_notify_set(0x06); - break; /* SysRQ */ - case 0x45: - t3100e_notify_set(0x07); - break; /* NumLock */ - case 0x46: - t3100e_notify_set(0x08); - break; /* ScrLock */ - case 0x47: - t3100e_notify_set(0x09); - break; /* Home */ - case 0x48: - t3100e_notify_set(0x0a); - break; /* Up */ - case 0x49: - t3100e_notify_set(0x0b); - break; /* PgUp */ - case 0x4A: - t3100e_notify_set(0x0c); - break; /* Keypad -*/ - case 0x4B: - t3100e_notify_set(0x0d); - break; /* Left */ - case 0x4C: - t3100e_notify_set(0x0e); - break; /* KP 5 */ - case 0x4D: - t3100e_notify_set(0x0f); - break; /* Right */ - } - - kbd_log("ATkbd: translate is %s, ", translate ? "on" : "off"); switch (val) { case FAKE_LSHIFT_ON: kbd_log("fake left shift on, scan code: "); @@ -1189,23 +1562,9 @@ add_data_kbd(uint16_t val) break; default: -#ifdef ENABLE_KEYBOARD_AT_LOG - kbd_log("scan code: "); - if (translate) { - kbd_log("%02X (original: ", (nont_to_t[val] | sc_or)); - if (sc_or == 0x80) - kbd_log("F0 "); - kbd_log("%02X)\n", val); - } else - kbd_log("%02X\n", val); -#endif - - add_data_kbd_queue(dev, translate ? (nont_to_t[val] | sc_or) : val); + add_data_kbd_queue(dev, val); break; } - - if (sc_or == 0x80) - sc_or = 0; } static void @@ -1284,13 +1643,14 @@ write_cmd(atkbd_t *dev, uint8_t val) /* ISA AT keyboard controllers use bit 5 for keyboard mode (1 = PC/XT, 2 = AT); PS/2 (and EISA/PCI) keyboard controllers use it as the PS/2 mouse enable switch. The AMIKEY firmware apparently uses this bit for something else. */ - if ((kbc_ven == KBC_VEN_AMI) || ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF)) { + if ((kbc_ven == KBC_VEN_AMI) || (kbc_ven == KBC_VEN_TG) || + (kbc_ven == KBC_VEN_TG_GREEN) || ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF)) { keyboard_mode &= ~CCB_PCMODE; kbd_log("ATkbc: mouse interrupt is now %s\n", (val & 0x02) ? "enabled" : "disabled"); } - if ((kbc_ven == KBC_VEN_AMI) || ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF)) { + if ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF) { /* Update the output port to mirror the IBF and OBF bits, if active. */ write_output(dev, (dev->output_port & 0x0f) | ((val & 0x03) << 4) | ((val & 0x20) ? 0xc0 : 0x00)); } @@ -1392,7 +1752,16 @@ write64_generic(void *priv, uint8_t val) 0, 0x00); dev->input_port = ((dev->input_port + 1) & 3) | (dev->input_port & 0xfc); } else { - if (((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) && ((dev->flags & KBC_VEN_MASK) != KBC_VEN_INTEL_AMI)) + if ((kbc_ven == KBC_VEN_TG) || (kbc_ven == KBC_VEN_TG_GREEN)) { + /* Bit 3, 2: + 1, 1: TriGem logo; + 1, 0: Garbled logo; + 0, 1: Epson logo; + 0, 0: Generic AMI logo. */ + if (dev->pci) + fixed_bits |= 8; + add_to_kbc_queue_front(dev, dev->input_port | fixed_bits, 0, 0x00); + } else if (((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) && ((dev->flags & KBC_VEN_MASK) != KBC_VEN_INTEL_AMI)) #if 0 add_to_kbc_queue_front(dev, (dev->input_port | fixed_bits) & (((dev->flags & KBC_VEN_MASK) == KBC_VEN_ACER) ? 0xeb : 0xef), 0, 0x00); @@ -1409,15 +1778,15 @@ write64_generic(void *priv, uint8_t val) if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { kbd_log("ATkbc: write mouse output buffer\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; return 0; } break; case 0xd4: /* write to mouse */ kbd_log("ATkbc: write to mouse\n"); - set_enable_mouse(dev, 1); - kbc_queue_reset(3); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; return 0; case 0xf0 ... 0xff: @@ -1454,6 +1823,7 @@ write60_ami(void *priv, uint8_t val) if (dev->secr_phase == 1) { dev->mem_addr = val; dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; dev->secr_phase = 2; } else if (dev->secr_phase == 2) { dev->mem[dev->mem_addr] = val; @@ -1490,27 +1860,36 @@ write64_ami(void *priv, uint8_t val) case 0x40 ... 0x5f: kbd_log("ATkbc: AMI - alias write to %08X\n", dev->command); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; return 0; case 0xa0: /* copyright message */ kbc_queue_add(dev, 0x28, 0); kbc_queue_add(dev, 0x00, 0); + dev->kbc_state = KBC_STATE_KBC_OUT; break; case 0xa1: /* get controller version */ kbd_log("ATkbc: AMI - get controller version\n"); - if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { + if ((kbc_ven == KBC_VEN_TG) || (kbc_ven == KBC_VEN_TG_GREEN)) + add_to_kbc_queue_front(dev, 'Z', 0, 0x00); + else if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { if (kbc_ven == KBC_VEN_ALI) add_to_kbc_queue_front(dev, 'F', 0, 0x00); else if ((dev->flags & KBC_VEN_MASK) == KBC_VEN_INTEL_AMI) add_to_kbc_queue_front(dev, '5', 0, 0x00); - else if (is_pentium) + else if (cpu_64bitbus) add_to_kbc_queue_front(dev, 'R', 0, 0x00); + else if (is486) + add_to_kbc_queue_front(dev, 'P', 0, 0x00); else add_to_kbc_queue_front(dev, 'H', 0, 0x00); - } else if (is386 && !is486) - add_to_kbc_queue_front(dev, 'B', 0, 0x00); - else if (!is386) + } else if (is386 && !is486) { + if (cpu_16bitbus) + add_to_kbc_queue_front(dev, 'D', 0, 0x00); + else + add_to_kbc_queue_front(dev, 'B', 0, 0x00); + } else if (!is386) add_to_kbc_queue_front(dev, '8', 0, 0x00); else add_to_kbc_queue_front(dev, 'F', 0, 0x00); @@ -1549,6 +1928,7 @@ write64_ami(void *priv, uint8_t val) } else { kbd_log("ATkbc: get extended controller RAM\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; } return 0; @@ -1591,6 +1971,7 @@ write64_ami(void *priv, uint8_t val) } else { kbd_log("ATkbc: set extended controller RAM\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; dev->secr_phase = 1; } return 0; @@ -1631,6 +2012,7 @@ write64_ami(void *priv, uint8_t val) case 0xc1: /* write input port */ kbd_log("ATkbc: AMI MegaKey - write input port\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; return 0; case 0xc4: @@ -1765,6 +2147,7 @@ write64_quadtel(void *priv, uint8_t val) case 0xcf: /*??? - sent by MegaPC BIOS*/ kbd_log("ATkbc: ??? - sent by MegaPC BIOS\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; return 0; } @@ -1829,6 +2212,7 @@ write64_toshiba(void *priv, uint8_t val) case 0xb6: /* T3100e: Set colour / mono byte */ kbd_log("ATkbc: T3100e: Set colour / mono byte\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; return 0; case 0xb7: /* T3100e: Emulate PS/2 keyboard */ @@ -1876,18 +2260,48 @@ write64_toshiba(void *priv, uint8_t val) static void kbd_key_reset(atkbd_t *dev, int do_fa) { + dev->out_new = -1; kbc_queue_reset(1); - kbd_last_scan_code = 0x00; + + dev->kbd_last_scan_code = 0x00; /* Set scan code set to 2. */ keyboard_mode = (keyboard_mode & 0xfc) | 0x02; set_scancode_map(dev); - if (do_fa) - add_data_kbd_raw(dev, 0xfa); - keyboard_scan = 1; + + dev->sc_or = 0; + + if (do_fa) + add_data_kbd_front(dev, 0xfa); + dev->reset_delay = RESET_DELAY_TIME; + + if (do_fa) + dev->kbd_state = DEV_STATE_MAIN_WANT_RESET; + else + dev->kbd_state = DEV_STATE_RESET; +} + +static void +kbd_aux_reset(atkbd_t *dev, int do_fa) +{ + dev->out_new_mouse = -1; + kbc_queue_reset(2); + + mouse_scan = 1; + + if (!do_fa) + dev->mouse_state = DEV_STATE_MAIN_1; +} + +void +keyboard_at_mouse_reset(void) +{ + atkbd_t *dev = SavedKbd; + + kbd_aux_reset(dev, 1); } static void @@ -1895,8 +2309,7 @@ kbd_process_cmd(void *priv) { atkbd_t *dev = (atkbd_t *) priv; - /* Write data to keyboard. */ - dev->mem[0x20] &= ~0x10; + dev->kbd_state = DEV_STATE_MAIN_OUT; if (dev->key_wantdata) { dev->key_wantdata = 0; @@ -1908,19 +2321,19 @@ kbd_process_cmd(void *priv) */ if (dev->key_dat == dev->key_command) { /* Respond NAK and ignore it. */ - add_data_kbd_raw(dev, 0xfe); + add_data_kbd_front(dev, 0xfe); dev->key_command = 0x00; return; } switch (dev->key_command) { case 0xed: /* set/reset LEDs */ - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); kbd_log("ATkbd: set LEDs [%02x]\n", dev->key_dat); break; case 0xf0: /* get/set scancode set */ - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); if (dev->key_dat == 0) { kbd_log("Get scan code set: %02X\n", keyboard_mode & 3); add_data_kbd_front(dev, keyboard_mode & 3); @@ -1935,12 +2348,12 @@ kbd_process_cmd(void *priv) break; case 0xf3: /* set typematic rate/delay */ - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); break; default: kbd_log("ATkbd: bad keyboard 0060 write %02X command %02X\n", dev->key_dat, dev->key_command); - add_data_kbd_raw(dev, 0xfe); + add_data_kbd_front(dev, 0xfe); break; } @@ -1954,19 +2367,20 @@ kbd_process_cmd(void *priv) switch (dev->key_dat) { case 0x00 ... 0x7f: kbd_log("ATkbd: invalid command %02X\n", dev->key_dat); - add_data_kbd_raw(dev, 0xfe); + add_data_kbd_front(dev, 0xfe); break; case 0xed: /* set/reset LEDs */ kbd_log("ATkbd: set/reset leds\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); dev->key_wantdata = 1; + dev->kbd_state = DEV_STATE_MAIN_WANT_IN; break; case 0xee: /* diagnostic echo */ kbd_log("ATkbd: ECHO\n"); - add_data_kbd_raw(dev, 0xee); + add_data_kbd_front(dev, 0xee); break; case 0xef: /* NOP (reserved for future use) */ @@ -1975,8 +2389,9 @@ kbd_process_cmd(void *priv) case 0xf0: /* get/set scan code set */ kbd_log("ATkbd: scan code set\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); dev->key_wantdata = 1; + dev->kbd_state = DEV_STATE_MAIN_WANT_IN; break; case 0xf2: /* read ID */ @@ -1984,20 +2399,21 @@ kbd_process_cmd(void *priv) kbd_log("ATkbd: read keyboard id\n"); /* TODO: After keyboard type selection is implemented, make this return the correct keyboard ID for the selected type. */ - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); add_data_kbd_front(dev, 0xab); add_data_kbd_front(dev, 0x83); break; case 0xf3: /* set typematic rate/delay */ kbd_log("ATkbd: set typematic rate/delay\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); dev->key_wantdata = 1; + dev->kbd_state = DEV_STATE_MAIN_WANT_IN; break; case 0xf4: /* enable keyboard */ kbd_log("ATkbd: enable keyboard\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); keyboard_scan = 1; break; @@ -2007,7 +2423,7 @@ kbd_process_cmd(void *priv) keyboard_scan = (dev->key_dat == 0xf6); kbd_log("dev->key_dat = %02X, keyboard_scan = %i, dev->mem[0x20] = %02X\n", dev->key_dat, keyboard_scan, dev->mem[0]); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); keyboard_set3_all_break = 0; keyboard_set3_all_repeat = 0; @@ -2018,32 +2434,32 @@ kbd_process_cmd(void *priv) case 0xf7: /* set all keys to repeat */ kbd_log("ATkbd: set all keys to repeat\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); keyboard_set3_all_break = 1; break; case 0xf8: /* set all keys to give make/break codes */ kbd_log("ATkbd: set all keys to give make/break codes\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); keyboard_set3_all_break = 1; break; case 0xf9: /* set all keys to give make codes only */ kbd_log("ATkbd: set all keys to give make codes only\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); keyboard_set3_all_break = 0; break; case 0xfa: /* set all keys to repeat and give make/break codes */ kbd_log("ATkbd: set all keys to repeat and give make/break codes\n"); - add_data_kbd_raw(dev, 0xfa); + add_data_kbd_front(dev, 0xfa); keyboard_set3_all_repeat = 1; keyboard_set3_all_break = 1; break; case 0xfe: /* resend last scan code */ - kbd_log("ATkbd: reset last scan code\n"); - add_data_kbd_raw(dev, kbd_last_scan_code); + kbd_log("ATkbd: resend last scan code\n"); + add_data_kbd_front(dev, dev->kbd_last_scan_code); break; case 0xff: /* reset */ @@ -2073,6 +2489,10 @@ kbc_process_cmd(void *priv) if (dev->status & STAT_CD) { /* Controller command. */ dev->want60 = 0; + dev->kbc_state = KBC_STATE_MAIN_IBF; + + /* Clear the keyboard controller queue. */ + kbc_queue_reset(0); switch (dev->ib) { /* Read data from KBC memory. */ @@ -2083,13 +2503,22 @@ kbc_process_cmd(void *priv) /* Write data to KBC memory. */ case 0x60 ... 0x7f: dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; break; case 0xaa: /* self-test */ kbd_log("ATkbc: self-test\n"); if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { - dev->status = 0x60; + if (dev->kbc_state != KBC_STATE_RESET) { + kbd_log("ATkbc: self-test reinitialization\n"); + /* Yes, the firmware has an OR, but we need to make sure to keep any forcibly lowered bytes lowered. */ + /* TODO: Proper P1 implementation, with OR and AND flags in the machine table. */ + dev->input_port = dev->input_port & 0xff; + write_output(dev, 0x4b); + } + + dev->status = (dev->status & 0x0f) | 0x60; dev->mem[0x20] = 0x30; dev->mem[0x21] = 0x01; @@ -2105,11 +2534,13 @@ kbc_process_cmd(void *priv) } else { if (dev->kbc_state != KBC_STATE_RESET) { kbd_log("ATkbc: self-test reinitialization\n"); - dev->input_port |= 0xff; + /* Yes, the firmware has an OR, but we need to make sure to keep any forcibly lowered bytes lowered. */ + /* TODO: Proper P1 implementation, with OR and AND flags in the machine table. */ + dev->input_port = dev->input_port & 0xff; write_output(dev, 0xcf); } - dev->status = 0x60; + dev->status = (dev->status & 0x0f) | 0x60; dev->mem[0x20] = 0x10; dev->mem[0x21] = 0x01; @@ -2123,31 +2554,14 @@ kbc_process_cmd(void *priv) dev->mem[0x2c] = 0x15; } - kbc_queue_reset(0); - dev->kbc_state = KBC_STATE_NORMAL; - - add_to_kbc_queue_front(dev, 0x55, 0, 0x00); - -#if 0 - write_output(dev, ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) ? 0x4b : 0xcf); - - /* Always reinitialize all queues - the real hardware pulls keyboard and mouse - clocks high, which stops keyboard scanning. */ - kbd_log("ATkbc: self-test reinitialization\n"); dev->out_new = dev->out_new_mouse = -1; - for (i = 0; i < 3; i++) - kbc_queue_reset(i); - keyboard_scan = 0; - mouse_scan = 0; - kbd_last_scan_code = 0x00; - dev->status &= ~STAT_OFULL; + kbc_queue_reset(0); - if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) - write_cmd(dev, 0x30 | STAT_SYSFLAG); - else - write_cmd(dev, 0x10 | STAT_SYSFLAG); - add_to_kbc_queue_front(dev, 0x55, 0, 0x00); -#endif + // dev->kbc_state = KBC_STATE_MAIN_IBF; + dev->kbc_state = KBC_STATE_KBC_OUT; + + // add_to_kbc_queue_front(dev, 0x55, 0, 0x00); + kbc_queue_add(dev, 0x55, 0); break; case 0xab: /* interface test */ @@ -2168,6 +2582,7 @@ kbc_process_cmd(void *priv) kbc_queue_add(dev, cmd_ac_conv[dev->mem[i + 0x20] & 0x0f], 0); kbc_queue_add(dev, 0x39, 0); } + dev->kbc_state = KBC_STATE_KBC_OUT; } break; @@ -2184,6 +2599,7 @@ kbc_process_cmd(void *priv) case 0xc7: /* set port1 bits */ kbd_log("ATkbc: Phoenix - set port1 bits\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; break; case 0xca: /* read keyboard mode */ @@ -2194,6 +2610,7 @@ kbc_process_cmd(void *priv) case 0xcb: /* set keyboard mode */ kbd_log("ATkbc: AMI - set keyboard mode\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; break; case 0xd0: /* read output port */ @@ -2207,11 +2624,13 @@ kbc_process_cmd(void *priv) case 0xd1: /* write output port */ kbd_log("ATkbc: write output port\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; break; case 0xd2: /* write keyboard output buffer */ kbd_log("ATkbc: write keyboard output buffer\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; break; case 0xdd: /* disable A20 address line */ @@ -2245,6 +2664,7 @@ kbc_process_cmd(void *priv) } else if (dev->want60) { /* Write data to controller. */ dev->want60 = 0; + dev->kbc_state = KBC_STATE_MAIN_IBF; switch (dev->command) { case 0x60 ... 0x7f: @@ -2291,10 +2711,9 @@ kbc_process_cmd(void *priv) if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { set_enable_mouse(dev, 1); if (mouse_write) { - kbc_queue_reset(3); dev->mouse_wantcmd = 1; dev->mouse_dat = dev->ib; - dev->kbc_state = KBC_STATE_MOUSE; + dev->kbc_state = KBC_STATE_SEND_MOUSE; } else add_to_kbc_queue_front(dev, 0xfe, 2, 0x40); } @@ -2340,6 +2759,7 @@ kbd_write(uint16_t port, uint8_t val, void *priv) } write_output(dev, val | 0x01); dev->want60 = 0; + dev->kbc_state = KBC_STATE_MAIN_IBF; return; } break; @@ -2349,6 +2769,7 @@ kbd_write(uint16_t port, uint8_t val, void *priv) if (val == 0xd1) { kbd_log("ATkbc: write output port\n"); dev->want60 = 1; + dev->kbc_state = KBC_STATE_KBC_PARAM; dev->command = 0xd1; return; } @@ -2377,7 +2798,7 @@ kbd_read(uint16_t port, void *priv) This also means that in AT mode, the IRQ is level-triggered. */ if ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF) picintc(1 << 1); - else if ((dev->flags & KBC_TYPE_MASK) > KBC_TYPE_PS2_NOREF) { + else if (pic_get_pci_flag() || ((dev->flags & KBC_TYPE_MASK) > KBC_TYPE_PS2_NOREF)) { /* PS/2 MCA: Latched as level-sensitive until port 0x60 is read (and with it, OBF is cleared), in accordance with the IBM PS/2 Model 80 Technical Reference Manual. */ kbc_irq(dev, dev->irq_levels, 0); @@ -2412,7 +2833,7 @@ kbd_reset(void *priv) dev->key_wantdata = 0; /* Set up the correct Video Type bits. */ - if ((kbc_ven == KBC_VEN_XI8088) || (kbc_ven == KBC_VEN_ACER)) + if (!is286 || (kbc_ven == KBC_VEN_ACER)) dev->input_port = video_is_mda() ? 0xb0 : 0xf0; else dev->input_port = video_is_mda() ? 0xf0 : 0xb0; @@ -2429,9 +2850,9 @@ kbd_reset(void *priv) dev->out_new = dev->out_new_mouse = -1; for (i = 0; i < 3; i++) kbc_queue_reset(i); - kbd_last_scan_code = 0; + dev->kbd_last_scan_code = 0; - sc_or = 0; + dev->sc_or = 0; memset(keyboard_set3_flags, 0, 512); @@ -2440,6 +2861,7 @@ kbd_reset(void *priv) dev->ami_flags = ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) ? 0x01 : 0x00; dev->ami_stat |= 0x02; + dev->output_port = 0xcd; if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { write_output(dev, 0x4b); } else { @@ -2453,7 +2875,10 @@ kbd_reset(void *priv) dev->kbc_state = KBC_STATE_RESET; /* Reset the keyboard. */ - // kbd_key_reset(dev, 0); + kbd_key_reset(dev, 0); + + /* Reset the mouse. */ + kbd_aux_reset(dev, 0); } /* Reset the AT keyboard - this is needed for the PCI TRC and is done @@ -2464,6 +2889,20 @@ keyboard_at_reset(void) kbd_reset(SavedKbd); } +void +kbc_at_a20_reset(void) +{ + if (SavedKbd) { + SavedKbd->output_port = 0xcd; + if ((SavedKbd->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { + write_output(SavedKbd, 0x4b); + } else { + /* The real thing writes CF and then AND's it with BF. */ + write_output(SavedKbd, 0x8f); + } + } +} + static void kbd_close(void *priv) { @@ -2493,6 +2932,10 @@ kbd_init(const device_t *info) memset(dev, 0x00, sizeof(atkbd_t)); dev->flags = info->local; + dev->pci = !!(info->flags & DEVICE_PCI); + + /* We need this, sadly. */ + SavedKbd = dev; video_reset(gfxcard[0]); kbd_reset(dev); @@ -2512,7 +2955,6 @@ kbd_init(const device_t *info) case KBC_VEN_GENERIC: case KBC_VEN_NCR: case KBC_VEN_IBM_PS1: - case KBC_VEN_XI8088: dev->write64_ven = write64_generic; break; @@ -2522,8 +2964,9 @@ kbd_init(const device_t *info) case KBC_VEN_AMI: case KBC_VEN_INTEL_AMI: - case KBC_VEN_SAMSUNG: case KBC_VEN_ALI: + case KBC_VEN_TG: + case KBC_VEN_TG_GREEN: dev->write60_ven = write60_ami; dev->write64_ven = write64_ami; break; @@ -2543,9 +2986,6 @@ kbd_init(const device_t *info) break; } - /* We need this, sadly. */ - SavedKbd = dev; - return (dev); } @@ -2577,11 +3017,11 @@ const device_t keyboard_at_ami_device = { .config = NULL }; -const device_t keyboard_at_samsung_device = { - .name = "PC/AT Keyboard (Samsung)", - .internal_name = "keyboard_at_samsung", +const device_t keyboard_at_tg_ami_device = { + .name = "PC/AT Keyboard (TriGem AMI)", + .internal_name = "keyboard_at_tg_ami", .flags = 0, - .local = KBC_TYPE_ISA | KBC_VEN_SAMSUNG, + .local = KBC_TYPE_ISA | KBC_VEN_TG, .init = kbd_init, .close = kbd_close, .reset = kbd_reset, @@ -2679,7 +3119,7 @@ const device_t keyboard_ps2_xi8088_device = { .name = "PS/2 Keyboard (Xi8088)", .internal_name = "keyboard_ps2_xi8088", .flags = 0, - .local = KBC_TYPE_PS2_1 | KBC_VEN_XI8088, + .local = KBC_TYPE_PS2_1 | KBC_VEN_GENERIC, .init = kbd_init, .close = kbd_close, .reset = kbd_reset, @@ -2703,6 +3143,20 @@ const device_t keyboard_ps2_ami_device = { .config = NULL }; +const device_t keyboard_ps2_tg_ami_device = { + .name = "PS/2 Keyboard (TriGem AMI)", + .internal_name = "keyboard_ps2_tg_ami", + .flags = 0, + .local = KBC_TYPE_PS2_NOREF | KBC_VEN_TG, + .init = kbd_init, + .close = kbd_close, + .reset = kbd_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + const device_t keyboard_ps2_mca_device = { .name = "PS/2 Keyboard", .internal_name = "keyboard_ps2_mca", @@ -2801,6 +3255,20 @@ const device_t keyboard_ps2_intel_ami_pci_device = { .config = NULL }; +const device_t keyboard_ps2_tg_ami_pci_device = { + .name = "PS/2 Keyboard (TriGem AMI)", + .internal_name = "keyboard_ps2_tg_ami_pci", + .flags = DEVICE_PCI, + .local = KBC_TYPE_PS2_NOREF | KBC_VEN_TG, + .init = kbd_init, + .close = kbd_close, + .reset = kbd_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + const device_t keyboard_ps2_acer_pci_device = { .name = "PS/2 Keyboard (Acer 90M002A)", .internal_name = "keyboard_ps2_acer_pci", @@ -2835,8 +3303,8 @@ keyboard_at_adddata_mouse(uint8_t val) { atkbd_t *dev = SavedKbd; - if (!mouse_scan || (dev->mouse_reset_delay > 0) || (mouse_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i, %i\n", !mouse_scan, (dev->mouse_reset_delay > 0), (mouse_queue_end >= 16)); + if (!mouse_scan || (dev->mouse_reset_delay > 0) || (dev->mouse_queue_end >= 16)) { + kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i, %i\n", !mouse_scan, (dev->mouse_reset_delay > 0), (dev->mouse_queue_end >= 16)); return; } kbc_queue_add(dev, val, 2); @@ -2847,23 +3315,19 @@ keyboard_at_adddata_mouse_cmd(uint8_t val) { atkbd_t *dev = SavedKbd; - if ((dev->mouse_reset_delay > 0) || (mouse_cmd_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", (dev->mouse_reset_delay > 0), (mouse_cmd_queue_end >= 16)); + if ((dev->mouse_reset_delay > 0) || (dev->mouse_cmd_queue_end >= 16)) { + kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", (dev->mouse_reset_delay > 0), (dev->mouse_cmd_queue_end >= 16)); return; } kbc_queue_add(dev, val, 3); } -void -keyboard_at_mouse_reset(void) -{ - kbc_queue_reset(2); -} - uint8_t keyboard_at_mouse_pos(void) { - return ((mouse_queue_end - mouse_queue_start) & 0xf); + atkbd_t *dev = SavedKbd; + + return ((dev->mouse_queue_end - dev->mouse_queue_start) & 0xf); } void diff --git a/src/device/mouse.c b/src/device/mouse.c index 46bbc4ac2..13d9999c7 100644 --- a/src/device/mouse.c +++ b/src/device/mouse.c @@ -152,8 +152,8 @@ mouse_close(void) static void mouse_timer_poll(void *priv) { - /* Poll at 3600 Hz. */ - timer_on_auto(&mouse_timer, 277.0 + (7.0 / 9.0)); + /* Poll at 255 Hz, maximum supported by PS/2 mic. */ + timer_on_auto(&mouse_timer, 1000000.0 / 255.0); #ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */ if (gdbstub_step == GDBSTUB_EXEC) @@ -186,8 +186,8 @@ mouse_reset(void) timer_add(&mouse_timer, mouse_timer_poll, NULL, 0); - /* Poll at 3600 Hz. */ - timer_on_auto(&mouse_timer, 277.0 + (7.0 / 9.0)); + /* Poll at 255 Hz, maximum supported by PS/2 mic. */ + timer_on_auto(&mouse_timer, 1000000.0 / 255.0); } /* Callback from the hardware driver. */ diff --git a/src/device/mouse_ps2.c b/src/device/mouse_ps2.c index 1fb2442ac..1c8e0334d 100644 --- a/src/device/mouse_ps2.c +++ b/src/device/mouse_ps2.c @@ -85,15 +85,24 @@ static void ps2_report_coordinates(mouse_t *dev, int cmd) { uint8_t buff[3] = { 0x08, 0x00, 0x00 }; + int temp_z; - if (dev->x > 255) + if (dev->x > 255) { dev->x = 255; - if (dev->x < -256) + buff[0] |= 0x40; + } + if (dev->x < -256) { dev->x = -256; - if (dev->y > 255) + buff[0] |= 0x40; + } + if (dev->y > 255) { dev->y = 255; - if (dev->y < -256) + buff[0] |= 0x80; + } + if (dev->y < -256) { dev->y = -256; + buff[0] |= 0x80; + } if (dev->z < -8) dev->z = -8; if (dev->z > 7) @@ -124,13 +133,16 @@ ps2_report_coordinates(mouse_t *dev, int cmd) keyboard_at_adddata_mouse(buff[2]); } if (dev->flags & FLAG_INTMODE) { - int temp_z = dev->z; + temp_z = dev->z & 0x0f; if ((dev->flags & FLAG_5BTN)) { - temp_z &= 0xF; if (mouse_buttons & 8) temp_z |= 0x10; if (mouse_buttons & 16) temp_z |= 0x20; + } else { + /* The wheel coordinate is sign-extended. */ + if (temp_z & 0x08) + temp_z |= 0xf0; } if (cmd) keyboard_at_adddata_mouse_cmd(temp_z); @@ -262,16 +274,15 @@ mouse_reset: dev->last_data[5] = val; - if (dev->last_data[0] == 0xf3 && dev->last_data[1] == 0xc8 - && dev->last_data[2] == 0xf3 && dev->last_data[3] == 0xc8 - && dev->last_data[4] == 0xf3 && dev->last_data[5] == 0x50 - && mouse_get_buttons() == 5) { - dev->flags |= FLAG_INTMODE | FLAG_5BTN; - } else if (dev->last_data[0] == 0xf3 && dev->last_data[1] == 0xc8 - && dev->last_data[2] == 0xf3 && dev->last_data[3] == 0x64 - && dev->last_data[4] == 0xf3 && dev->last_data[5] == 0x50) { + if ((dev->last_data[0] == 0xf3) && (dev->last_data[1] == 0xc8) && + (dev->last_data[2] == 0xf3) && (dev->last_data[3] == 0x64) && + (dev->last_data[4] == 0xf3) && (dev->last_data[5] == 0x50)) dev->flags |= FLAG_INTMODE; - } + + if ((dev->flags & FLAG_INTMODE) && (dev->last_data[0] == 0xf3) && (dev->last_data[1] == 0xc8) && + (dev->last_data[2] == 0xf3) && (dev->last_data[3] == 0xc8) && + (dev->last_data[4] == 0xf3) && (dev->last_data[5] == 0x50)) + dev->flags |= FLAG_5BTN; } } diff --git a/src/include/86box/keyboard.h b/src/include/86box/keyboard.h index 2ff0325cb..0fd25dbd0 100644 --- a/src/include/86box/keyboard.h +++ b/src/include/86box/keyboard.h @@ -159,7 +159,7 @@ extern const device_t keyboard_xt_zenith_device; extern const device_t keyboard_xtclone_device; extern const device_t keyboard_at_device; extern const device_t keyboard_at_ami_device; -extern const device_t keyboard_at_samsung_device; +extern const device_t keyboard_at_tg_ami_device; extern const device_t keyboard_at_toshiba_device; extern const device_t keyboard_at_olivetti_device; extern const device_t keyboard_at_ncr_device; @@ -168,6 +168,8 @@ extern const device_t keyboard_ps2_ps1_device; extern const device_t keyboard_ps2_ps1_pci_device; extern const device_t keyboard_ps2_xi8088_device; extern const device_t keyboard_ps2_ami_device; +extern const device_t keyboard_ps2_tg_ami_device; +extern const device_t keyboard_ps2_tg_ami_green_device; extern const device_t keyboard_ps2_olivetti_device; extern const device_t keyboard_ps2_mca_device; extern const device_t keyboard_ps2_mca_2_device; @@ -177,6 +179,7 @@ extern const device_t keyboard_ps2_ami_pci_device; extern const device_t keyboard_ps2_intel_ami_pci_device; extern const device_t keyboard_ps2_acer_pci_device; extern const device_t keyboard_ps2_ali_pci_device; +extern const device_t keyboard_ps2_tg_ami_pci_device; #endif /*EMU_DEVICE_H*/ extern void keyboard_init(void); @@ -207,6 +210,7 @@ extern void keyboard_at_set_mode(int ps2); extern uint8_t keyboard_at_get_mouse_scan(void); extern void keyboard_at_set_mouse_scan(uint8_t val); extern void keyboard_at_reset(void); +extern void kbc_at_a20_reset(void); #ifdef __cplusplus } diff --git a/src/include/86box/pic.h b/src/include/86box/pic.h index 318ce2fad..52bc920e4 100644 --- a/src/include/86box/pic.h +++ b/src/include/86box/pic.h @@ -43,6 +43,7 @@ extern void pic_elcr_write(uint16_t port, uint8_t val, void *priv); extern uint8_t pic_elcr_read(uint16_t port, void *priv); extern void pic_set_shadow(int sh); +extern int pic_get_pci_flag(void); extern void pic_set_pci_flag(int pci); extern void pic_set_pci(void); extern void pic_init(void); diff --git a/src/machine/m_at_386dx_486.c b/src/machine/m_at_386dx_486.c index 0ee7b51a2..7768202da 100644 --- a/src/machine/m_at_386dx_486.c +++ b/src/machine/m_at_386dx_486.c @@ -220,7 +220,7 @@ machine_at_spc6000a_init(const machine_t *model) if (fdc_type == FDC_INTERNAL) device_add(&fdc_at_device); - device_add(&keyboard_at_samsung_device); + device_add(&keyboard_at_ami_device); return ret; } @@ -404,7 +404,7 @@ machine_at_acerv10_init(const machine_t *model) machine_at_common_init(model); device_add(&sis_85c461_device); - device_add(&keyboard_ps2_ami_pci_device); + device_add(&keyboard_ps2_acer_pci_device); device_add(&ide_isa_2ch_device); if (fdc_type == FDC_INTERNAL) @@ -1657,7 +1657,7 @@ machine_at_actionpc2600_init(const machine_t *model) device_add(&umc_8886af_device); device_add(&um8669f_device); device_add(&intel_flash_bxt_device); - device_add(&keyboard_at_ami_device); + device_add(&keyboard_ps2_tg_ami_device); return ret; } @@ -1782,7 +1782,7 @@ machine_at_tg486gp_init(const machine_t *model) device_add(&ali1435_device); device_add(&sst_flash_29ee010_device); - device_add(&keyboard_ps2_ami_device); + device_add(&keyboard_ps2_tg_ami_device); return ret; } @@ -1806,7 +1806,7 @@ machine_at_tg486g_init(const machine_t *model) device_add(&sis_85c471_device); device_add(&ide_isa_device); device_add(&fdc37c651_ide_device); - device_add(&keyboard_ps2_intel_ami_pci_device); + device_add(&keyboard_ps2_tg_ami_pci_device); return ret; } diff --git a/src/machine/m_at_socket5.c b/src/machine/m_at_socket5.c index 842554713..9a45e71bf 100644 --- a/src/machine/m_at_socket5.c +++ b/src/machine/m_at_socket5.c @@ -237,7 +237,7 @@ machine_at_hawk_init(const machine_t *model) pci_register_slot(0x13, PCI_CARD_NORMAL, 2, 3, 4, 1); pci_register_slot(0x12, PCI_CARD_NORMAL, 3, 4, 1, 2); pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); - device_add(&keyboard_ps2_ami_pci_device); + device_add(&keyboard_ps2_tg_ami_pci_device); device_add(&i430fx_device); device_add(&piix_device); device_add(&fdc37c665_device); diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 9047342e8..3a11769f8 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -7036,7 +7036,7 @@ const machine_t machines[] = { .min_multi = 0, .max_multi = 0 }, - .bus_flags = MACHINE_PCI, + .bus_flags = MACHINE_PS2_PCI, .flags = MACHINE_IDE_DUAL | MACHINE_APM, .ram = { .min = 1024, diff --git a/src/mem/mem.c b/src/mem/mem.c index 1af83c844..9de4a670f 100644 --- a/src/mem/mem.c +++ b/src/mem/mem.c @@ -2530,11 +2530,12 @@ void mem_a20_init(void) { if (is286) { - rammask = cpu_16bitbus ? 0xefffff : 0xffefffff; + mem_a20_key = mem_a20_alt = mem_a20_state = 0; + rammask = cpu_16bitbus ? 0xffffff : 0xffffffff; if (is6117) rammask |= 0x03000000; flushmmucache(); - mem_a20_state = mem_a20_key | mem_a20_alt; + // mem_a20_state = mem_a20_key | mem_a20_alt; } else { rammask = 0xfffff; flushmmucache(); diff --git a/src/pic.c b/src/pic.c index bdff7f59c..9920a23e9 100644 --- a/src/pic.c +++ b/src/pic.c @@ -284,6 +284,12 @@ pic_set_shadow(int sh) shadow = sh; } +int +pic_get_pci_flag(void) +{ + return pic_pci; +} + void pic_set_pci_flag(int pci) { diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 1cb26a239..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) @@ -373,6 +373,21 @@ if (UNIX AND NOT APPLE AND NOT HAIKU) target_link_libraries(ui PUBLIC PkgConfig::LIBEVDEV) target_sources(ui PRIVATE evdev_mouse.cpp) endif() + pkg_check_modules(XKBCOMMON IMPORTED_TARGET xkbcommon) + if (XKBCOMMON_FOUND) + target_compile_definitions(ui PRIVATE XKBCOMMON) + target_link_libraries(ui PUBLIC PkgConfig::XKBCOMMON) + target_sources(ui PRIVATE xkbcommon_keyboard.cpp) + + if (X11_xcb_FOUND) + pkg_check_modules(XKBCOMMON_X11 IMPORTED_TARGET xkbcommon-x11) + if (XKBCOMMON_X11_FOUND) + target_compile_definitions(ui PRIVATE XKBCOMMON_X11) + target_link_libraries(ui PRIVATE X11::xcb PUBLIC PkgConfig::XKBCOMMON_X11) + target_sources(ui PRIVATE xkbcommon_x11_keyboard.cpp) + endif() + endif() + endif() find_package(ECM NO_MODULE) if (ECM_FOUND) @@ -387,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/be_keyboard.hpp b/src/qt/be_keyboard.hpp new file mode 100644 index 000000000..cc2bbabac --- /dev/null +++ b/src/qt/be_keyboard.hpp @@ -0,0 +1,112 @@ +static std::unordered_map be_keycodes = { + {B_F1_KEY, 0x3b}, + {B_F2_KEY, 0x3c}, + {B_F3_KEY, 0x3d}, + {B_F4_KEY, 0x3e}, + {B_F5_KEY, 0x3f}, + {B_F6_KEY, 0x40}, + {B_F7_KEY, 0x41}, + {B_F8_KEY, 0x42}, + {B_F9_KEY, 0x43}, + {B_F10_KEY, 0x44}, + {B_F11_KEY, 0x57}, + {B_F12_KEY, 0x58}, + {B_PRINT_KEY, 0x137}, + {B_SCROLL_KEY, 0x46}, + {B_PAUSE_KEY, 0x145}, + {B_KATAKANA_HIRAGANA, 0x70}, + {B_HANKAKU_ZENKAKU, 0x76}, + + {0x01, 0x01}, /* Escape */ + {0x11, 0x29}, + {0x12, 0x02}, + {0x13, 0x03}, + {0x14, 0x04}, + {0x15, 0x05}, + {0x16, 0x06}, + {0x17, 0x07}, + {0x18, 0x08}, + {0x19, 0x09}, + {0x1a, 0x0a}, + {0x1b, 0x0b}, + {0x1c, 0x0c}, + {0x1d, 0x0d}, + {0x1e, 0x0e}, /* Backspace */ + {0x1f, 0x152}, /* Insert */ + {0x20, 0x147}, /* Home */ + {0x21, 0x149}, /* Page Up */ + {0x22, 0x45}, + {0x23, 0x135}, + {0x24, 0x37}, + {0x25, 0x4a}, + {0x26, 0x0f}, /* Tab */ + {0x27, 0x10}, + {0x28, 0x11}, + {0x29, 0x12}, + {0x2a, 0x13}, + {0x2b, 0x14}, + {0x2c, 0x15}, + {0x2d, 0x16}, + {0x2e, 0x17}, + {0x2f, 0x18}, + {0x30, 0x19}, + {0x31, 0x1a}, + {0x32, 0x1b}, + {0x33, 0x2b}, + {0x34, 0x153}, /* Delete */ + {0x35, 0x14f}, /* End */ + {0x36, 0x151}, /* Page Down */ + {0x37, 0x47}, + {0x38, 0x48}, + {0x39, 0x49}, + {0x3a, 0x4e}, + {0x3b, 0x3a}, + {0x3c, 0x1e}, + {0x3d, 0x1f}, + {0x3e, 0x20}, + {0x3f, 0x21}, + {0x40, 0x22}, + {0x41, 0x23}, + {0x42, 0x24}, + {0x43, 0x25}, + {0x44, 0x26}, + {0x45, 0x27}, + {0x46, 0x28}, + {0x47, 0x1c}, /* Enter */ + {0x48, 0x4b}, + {0x49, 0x4c}, + {0x4a, 0x4d}, + {0x4b, 0x2a}, + {0x4c, 0x2c}, + {0x4d, 0x2d}, + {0x4e, 0x2e}, + {0x4f, 0x2f}, + {0x50, 0x30}, + {0x51, 0x31}, + {0x52, 0x32}, + {0x53, 0x33}, + {0x54, 0x34}, + {0x55, 0x35}, + {0x56, 0x36}, + {0x57, 0x148}, /* up arrow */ + {0x58, 0x51}, + {0x59, 0x50}, + {0x5a, 0x4f}, + {0x5b, 0x11c}, + {0x5c, 0x1d}, + {0x5d, 0x38}, + {0x5e, 0x39}, /* space bar */ + {0x5f, 0x138}, + {0x60, 0x11d}, + {0x61, 0x14b}, /* left arrow */ + {0x62, 0x150}, /* down arrow */ + {0x63, 0x14d}, /* right arrow */ + {0x64, 0x52}, + {0x65, 0x53}, + {0x66, 0x15b}, + {0x67, 0x15c}, + {0x68, 0x15d}, + {0x69, 0x56}, + {0x7e, 0x137}, /* System Request */ + {0x7f, 0x145}, /* Break */ +}; diff --git a/src/qt/cocoa_keyboard.hpp b/src/qt/cocoa_keyboard.hpp new file mode 100644 index 000000000..eaf0cdfe0 --- /dev/null +++ b/src/qt/cocoa_keyboard.hpp @@ -0,0 +1,129 @@ +static std::array cocoa_keycodes = { /* key names in parentheses are not declared by Apple headers */ + 0x1e, /* ANSI_A */ + 0x1f, /* ANSI_S */ + 0x20, /* ANSI_D */ + 0x21, /* ANSI_F */ + 0x23, /* ANSI_H */ + 0x22, /* ANSI_G */ + 0x2c, /* ANSI_Z */ + 0x2d, /* ANSI_X */ + 0x2e, /* ANSI_C */ + 0x2f, /* ANSI_V */ + 0x56, /* ISO_Section */ + 0x30, /* ANSI_B */ + 0x10, /* ANSI_Q */ + 0x11, /* ANSI_W */ + 0x12, /* ANSI_E */ + 0x13, /* ANSI_R */ + 0x15, /* ANSI_Y */ + 0x14, /* ANSI_T */ + 0x02, /* ANSI_1 */ + 0x03, /* ANSI_2 */ + 0x04, /* ANSI_3 */ + 0x05, /* ANSI_4 */ + 0x07, /* ANSI_6 */ + 0x06, /* ANSI_5 */ + 0x0d, /* ANSI_Equal */ + 0x0a, /* ANSI_9 */ + 0x08, /* ANSI_7 */ + 0x0c, /* ANSI_Minus */ + 0x09, /* ANSI_8 */ + 0x0b, /* ANSI_0 */ + 0x1b, /* ANSI_RightBracket */ + 0x18, /* ANSI_O */ + 0x16, /* ANSI_U */ + 0x1a, /* ANSI_LeftBracket */ + 0x17, /* ANSI_I */ + 0x19, /* ANSI_P */ + 0x1c, /* Return */ + 0x26, /* ANSI_L */ + 0x24, /* ANSI_J */ + 0x28, /* ANSI_Quote */ + 0x25, /* ANSI_K */ + 0x27, /* ANSI_Semicolon */ + 0x2b, /* ANSI_Backslash */ + 0x33, /* ANSI_Comma */ + 0x35, /* ANSI_Slash */ + 0x31, /* ANSI_N */ + 0x32, /* ANSI_M */ + 0x34, /* ANSI_Period */ + 0x0f, /* Tab */ + 0x39, /* Space */ + 0x29, /* ANSI_Grave */ + 0x0e, /* Delete => Backspace */ + 0x11c, /* (ANSI_KeypadEnter) */ + 0x01, /* Escape */ + 0x15c, /* (RightCommand) => Right Windows */ + 0x15b, /* (Left)Command => Left Windows */ + 0x2a, /* Shift */ + 0x3a, /* CapsLock */ + 0x38, /* Option */ + 0x1d, /* Control */ + 0x36, /* RightShift */ + 0x138, /* RightOption */ + 0x11d, /* RightControl */ + 0x15c, /* Function */ + 0x5e, /* F17 => F14 */ + 0x53, /* ANSI_KeypadDecimal */ + 0, + 0x37, /* ANSI_KeypadMultiply */ + 0, + 0x4e, /* ANSI_KeypadPlus */ + 0, + 0x45, /* ANSI_KeypadClear => Num Lock (location equivalent) */ + 0x130, /* VolumeUp */ + 0x12e, /* VolumeDown */ + 0x120, /* Mute */ + 0x135, /* ANSI_KeypadDivide */ + 0x11c, /* ANSI_KeypadEnter */ + 0, + 0x4a, /* ANSI_KeypadMinus */ + 0x5f, /* F18 => F15 */ + 0, /* F19 */ + 0x59, /* ANSI_KeypadEquals */ + 0x52, /* ANSI_Keypad0 */ + 0x4f, /* ANSI_Keypad1 */ + 0x50, /* ANSI_Keypad2 */ + 0x51, /* ANSI_Keypad3 */ + 0x4b, /* ANSI_Keypad4 */ + 0x4c, /* ANSI_Keypad5 */ + 0x4d, /* ANSI_Keypad6 */ + 0x47, /* ANSI_Keypad7 */ + 0, /* F20 */ + 0x48, /* ANSI_Keypad8 */ + 0x49, /* ANSI_Keypad9 */ + 0x7d, /* JIS_Yen */ + 0x73, /* JIS_Underscore */ + 0x5c, /* JIS_KeypadComma */ + 0x3f, /* F5 */ + 0x40, /* F6 */ + 0x41, /* F7 */ + 0x3d, /* F3 */ + 0x42, /* F8 */ + 0x43, /* F9 */ + 0x7b, /* JIS_Eisu => muhenkan (location equivalent) */ + 0x57, /* F11 */ + 0x79, /* JIS_Kana => henkan (location equivalent) */ + 0x137, /* F13 => SysRq (location equivalent) */ + 0x5d, /* F16 => F13 */ + 0x46, /* F14 => Scroll Lock (location equivalent) */ + 0, + 0x44, /* F10 */ + 0x15d, /* (Menu) */ + 0x58, /* F12 */ + 0, + 0x145, /* F15 => Pause (location equivalent) */ + 0x152, /* Help => Insert (location equivalent) */ + 0x147, /* Home */ + 0x149, /* PageUp */ + 0x153, /* ForwardDelete */ + 0x3e, /* F4 */ + 0x14f, /* End */ + 0x3c, /* F2 */ + 0x151, /* PageDown */ + 0x3b, /* F1 */ + 0x14b, /* LeftArrow */ + 0x14d, /* RightArrow */ + 0x150, /* DownArrow */ + 0x148, /* UpArrow */ +}; diff --git a/src/qt/evdev_keyboard.cpp b/src/qt/evdev_keyboard.cpp new file mode 100644 index 000000000..9dbb4127a --- /dev/null +++ b/src/qt/evdev_keyboard.cpp @@ -0,0 +1,163 @@ +/* + * 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 = { + {184, 0x46}, /* F14 => Scroll Lock (for Apple keyboards) */ + {86, 0x56}, /* 102ND */ + {87, 0x57}, /* F11 */ + {88, 0x58}, /* F12 */ + {186, 0x5d}, /* F16 => F13 */ + {187, 0x5e}, /* F17 => F14 */ + {188, 0x5f}, /* F18 => 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 */ + {183, 0x137}, /* F13 => SysRq (for Apple keyboards) */ + {100, 0x138}, /* RIGHTALT */ + {119, 0x145}, /* PAUSE */ + {411, 0x145}, /* BREAK */ + {185, 0x145}, /* F15 => Pause (for Apple keyboards) */ + {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. */ + {634, 0x54}, /* SELECTIVE_SCREENSHOT# => Alt+SysRq */ + {117, 0x59}, /* KPEQUAL */ + {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 */ + {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 e0a272436..0c5e204ce 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -96,8 +96,17 @@ extern int qt_nvr_save(void); #include "qt_util.hpp" #if defined __unix__ && !defined __HAIKU__ -# ifdef WAYLAND -# include "wl_mouse.hpp" +# 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 @@ -106,6 +115,7 @@ extern int qt_nvr_save(void); #endif #ifdef Q_OS_MACOS +# include "cocoa_keyboard.hpp" // The namespace is required to avoid clashing typedefs; we only use this // header for its #defines anyway. namespace IOKit { @@ -116,6 +126,7 @@ namespace IOKit { #ifdef __HAIKU__ # include # include +# include "be_keyboard.hpp" extern MainWindow *main_window; @@ -648,6 +659,20 @@ MainWindow::MainWindow(QWidget *parent) } else { ui->actionCursor_Puck->setChecked(true); } + +#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 } void @@ -882,652 +907,67 @@ 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, - 0x1F, - 0x20, - 0x21, - 0x23, - 0x22, - 0x2C, - 0x2D, - 0x2E, - 0x2F, - 0x2B, - 0x30, - 0x10, - 0x11, - 0x12, - 0x13, - 0x15, - 0x14, - 0x02, - 0x03, - 0x04, - 0x05, - 0x07, - 0x06, - 0x0D, - 0x0A, - 0x08, - 0x0C, - 0x09, - 0x0B, - 0x1B, - 0x18, - 0x16, - 0x1A, - 0x17, - 0x19, - 0x1C, - 0x26, - 0x24, - 0x28, - 0x25, - 0x27, - 0x2B, - 0x33, - 0x35, - 0x31, - 0x32, - 0x34, - 0x0F, - 0x39, - 0x29, - 0x0E, - 0x11C, - 0x01, - 0x15C, - 0x15B, - 0x2A, - 0x3A, - 0x38, - 0x1D, - 0x36, - 0x138, - 0x11D, - 0x15C, - 0, - 0x53, - 0, - 0x37, - 0, - 0x4E, - 0, - 0x45, - 0x130, - 0x12E, - 0x120, - 0x135, - 0x11C, - 0, - 0x4A, - 0, - 0, - 0, - 0x52, - 0x4F, - 0x50, - 0x51, - 0x4B, - 0x4C, - 0x4D, - 0x47, - 0, - 0x48, - 0x49, - 0, - 0, - 0, - 0x3F, - 0x40, - 0x41, - 0x3D, - 0x42, - 0x43, - 0, - 0x57, - 0, - 0x137, - 0, - 0x46, - 0, - 0x44, - 0x15D, - 0x58, - 0, - 0, // Pause/Break key. - 0x152, - 0x147, - 0x149, - 0x153, - 0x3E, - 0x14F, - 0x3C, - 0x151, - 0x3B, - 0x14B, - 0x14D, - 0x150, - 0x148, - 0, -}; -#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 }, - { B_F1_KEY, 0x3B }, - { B_F2_KEY, 0x3C }, - { B_F3_KEY, 0x3D }, - { B_F4_KEY, 0x3E }, - { B_F5_KEY, 0x3F }, - { B_F6_KEY, 0x40 }, - { B_F7_KEY, 0x41 }, - { B_F8_KEY, 0x42 }, - { B_F9_KEY, 0x43 }, - { B_F10_KEY, 0x44 }, - { B_F11_KEY, 0x57 }, - { B_F12_KEY, 0x58 }, - { 0x11, 0x29 }, - { 0x12, 0x02 }, - { 0x13, 0x03 }, - { 0x14, 0x04 }, - { 0x15, 0x05 }, - { 0x16, 0x06 }, - { 0x17, 0x07 }, - { 0x18, 0x08 }, - { 0x19, 0x09 }, - { 0x1A, 0x0A }, - { 0x1B, 0x0B }, - { 0x1C, 0x0C }, - { 0x1D, 0x0D }, - { 0x1E, 0x0E }, - { 0x1F, 0x152}, - { 0x20, 0x147}, - { 0x21, 0x149}, - { 0x22, 0x45 }, - { 0x23, 0x135}, - { 0x24, 0x37 }, - { 0x25, 0x4A }, - { 0x26, 0x0F }, - { 0x27, 0x10 }, - { 0x28, 0x11 }, - { 0x29, 0x12 }, - { 0x2A, 0x13 }, - { 0x2B, 0x14 }, - { 0x2C, 0x15 }, - { 0x2D, 0x16 }, - { 0x2E, 0x17 }, - { 0x2F, 0x18 }, - { 0x30, 0x19 }, - { 0x31, 0x1A }, - { 0x32, 0x1B }, - { 0x33, 0x2B }, - { 0x34, 0x153}, - { 0x35, 0x14F}, - { 0x36, 0x151}, - { 0x37, 0x47 }, - { 0x38, 0x48 }, - { 0x39, 0x49 }, - { 0x3A, 0x4E }, - { 0x3B, 0x3A }, - { 0x3C, 0x1E }, - { 0x3D, 0x1F }, - { 0x3E, 0x20 }, - { 0x3F, 0x21 }, - { 0x40, 0x22 }, - { 0x41, 0x23 }, - { 0x42, 0x24 }, - { 0x43, 0x25 }, - { 0x44, 0x26 }, - { 0x45, 0x27 }, - { 0x46, 0x28 }, - { 0x47, 0x1C }, - { 0x48, 0x4B }, - { 0x49, 0x4C }, - { 0x4A, 0x4D }, - { 0x4B, 0x2A }, - { 0x4C, 0x2C }, - { 0x4D, 0x2D }, - { 0x4E, 0x2E }, - { 0x4F, 0x2F }, - { 0x50, 0x30 }, - { 0x51, 0x31 }, - { 0x52, 0x32 }, - { 0x53, 0x33 }, - { 0x54, 0x34 }, - { 0x55, 0x35 }, - { 0x56, 0x36 }, - { 0x57, 0x148}, - { 0x58, 0x51 }, - { 0x59, 0x50 }, - { 0x5A, 0x4F }, - { 0x5B, 0x11C}, - { 0x5C, 0x1D }, - { 0x5D, 0x38 }, - { 0x5E, 0x39 }, - { 0x5F, 0x138}, - { 0x60, 0x11D}, - { 0x61, 0x14B}, - { 0x62, 0x150}, - { 0x63, 0x14D}, - { 0x64, 0x52 }, - { 0x65, 0x53 }, - - { 0x0e, 0x137}, - { 0x0f, 0x46 }, - { 0x66, 0x15B}, - { 0x67, 0x15C}, - { 0x68, 0x15D}, - { 0x69, 0x56 } -}; -#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) +void +MainWindow::processKeyboardInput(bool down, uint32_t keycode) { - uint16_t finalkeycode = 0; -#if defined(Q_OS_WINDOWS) - finalkeycode = (keycode & 0xFFFF); +#if defined(Q_OS_WINDOWS) /* non-raw input */ + keycode &= 0xffff; #elif defined(Q_OS_MACOS) - finalkeycode = darwin_to_xt[keycode]; + keycode = (keycode < 127) ? cocoa_keycodes[keycode] : 0; #elif defined(__HAIKU__) - finalkeycode = be_to_xt[keycode]; + keycode = be_keycodes[keycode]; #else - 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]; - } +# ifdef XKBCOMMON + if (xkbcommon_keymap) + keycode = xkbcommon_translate(keycode); + else +# endif +# ifdef EVDEV_KEYBOARD_HPP + keycode = evdev_translate(keycode - 8); +# else + keycode = 0; +# endif #endif - if (rctrl_is_lalt && finalkeycode == 0x11D) - finalkeycode = 0x38; - return finalkeycode; + + /* Apply special cases. */ + switch (keycode) { + case 0x54: /* Alt + Print Screen (special case, i.e. evdev SELECTIVE_SCREENSHOT) */ + /* Send Alt as well. */ + if (down) { + keyboard_input(down, 0x38); + } else { + keyboard_input(down, keycode); + keycode = 0x38; + } + break; + + case 0x11d: /* Right Ctrl */ + if (rctrl_is_lalt) + keycode = 0x38; /* map to Left Alt */ + break; + + case 0x137: /* Print Screen */ + if (keyboard_recv(0x38) || keyboard_recv(0x138)) { /* Alt+ */ + keycode = 0x54; + } else if (down) { + keyboard_input(down, 0x12a); + } else { + keyboard_input(down, keycode); + keycode = 0x12a; + } + break; + + case 0x145: /* Pause */ + if (keyboard_recv(0x1d) || keyboard_recv(0x11d)) { /* Ctrl+ */ + keycode = 0x146; + } else { + keyboard_input(down, 0xe11d); + keycode &= 0x00ff; + } + break; + } + + keyboard_input(down, keycode); } #ifdef Q_OS_MACOS @@ -1545,6 +985,7 @@ static std::unordered_map mac_modifiers_to_xt = { { NX_DEVICE_ALPHASHIFT_STATELESS_MASK, 0x3A }, { NX_DEVICERCTLKEYMASK, 0x11D}, }; +static bool mac_iso_swap = false; void MainWindow::processMacKeyboardInput(bool down, const QKeyEvent *event) @@ -1585,11 +1026,62 @@ MainWindow::processMacKeyboardInput(bool down, const QKeyEvent *event) // It's possible that other lock keys get delivered in this way, but // standard Apple keyboards don't have them, so this is untested. if (event->key() == Qt::Key_CapsLock) { - keyboard_input(1, 0x3A); - keyboard_input(0, 0x3A); + keyboard_input(1, 0x3a); + keyboard_input(0, 0x3a); } } else { - keyboard_input(down, x11_keycode_to_keysym(event->nativeVirtualKey())); + /* Apple ISO keyboards are notorious for swapping ISO_Section and ANSI_Grave + on *some* layouts and/or models. While macOS can sort this mess out at + keymap level, it still provides applications with unfiltered, ambiguous + keycodes, so we have to disambiguate them by making some bold assumptions + about the user's keyboard layout based on the OS-provided key mappings. */ + auto nvk = event->nativeVirtualKey(); + if ((nvk == 0x0a) || (nvk == 0x32)) { + /* Flaws: + - Layouts with `~ on ISO_Section are partially detected due to a conflict with ANSI + - Czech and Slovak are not detected as they have <> ANSI_Grave and \| ISO_Section (differing from PC actually) + - Italian is partially detected due to \| conflicting with Brazilian + - Romanian third level ANSI_Grave is unknown + - Russian clusters <>, plusminus and paragraph into a four-level ANSI_Grave, with the aforementioned `~ on ISO_Section */ + auto key = event->key(); + if ((nvk == 0x32) && ( /* system reports ANSI_Grave for ISO_Section keys: */ + (key == Qt::Key_Less) || (key == Qt::Key_Greater) || /* Croatian, French, German, Icelandic, Italian, Norwegian, Portuguese, Spanish, Spanish Latin America, Turkish Q */ + (key == Qt::Key_Ugrave) || /* French Canadian */ + (key == Qt::Key_Icircumflex) || /* Romanian */ + (key == Qt::Key_Iacute) || /* Hungarian */ + (key == Qt::Key_BracketLeft) || (key == Qt::Key_BracketRight) || /* Russian upper two levels */ + (key == Qt::Key_W) /* Turkish F */ + )) + mac_iso_swap = true; + else if ((nvk == 0x0a) && ( /* system reports ISO_Section for ANSI_Grave keys: */ + (key == Qt::Key_paragraph) || (key == Qt::Key_plusminus) || /* Arabic, British, Bulgarian, Danish shifted, Dutch, Greek, Hebrew, Hungarian shifted, International English, Norwegian shifted, Portuguese, Russian lower two levels, Swiss unshifted, Swedish unshifted, Turkish F */ + (key == Qt::Key_At) || (key == Qt::Key_NumberSign) || /* Belgian, French */ + (key == Qt::Key_Apostrophe) || /* Brazilian unshifted */ + (key == Qt::Key_QuoteDbl) || /* Brazilian shifted, Turkish Q unshifted */ + (key == Qt::Key_QuoteLeft) || /* Croatian (right quote unknown) */ + (key == Qt::Key_Dollar) || /* Danish unshifted */ + (key == Qt::Key_AsciiCircum) || (key == 0x1ffffff) || /* German unshifted (0x1ffffff according to one tester), Polish unshifted */ + (key == Qt::Key_degree) || /* German shifted, Icelandic unshifted, Spanish Latin America shifted, Swiss shifted, Swedish shifted */ + (key == Qt::Key_0) || /* Hungarian unshifted */ + (key == Qt::Key_diaeresis) || /* Icelandic shifted */ + (key == Qt::Key_acute) || /* Norwegian unshifted */ + (key == Qt::Key_Asterisk) || /* Polish shifted */ + (key == Qt::Key_masculine) || (key == Qt::Key_ordfeminine) || /* Spanish (masculine unconfirmed) */ + (key == Qt::Key_Eacute) || /* Turkish Q shifted */ + (key == Qt::Key_Slash) /* French Canadian unshifted, Ukrainian shifted */ + )) + mac_iso_swap = true; +#if 0 + if (down) { + QMessageBox questionbox(QMessageBox::Icon::Information, QString("Mac key swap test"), QString("nativeVirtualKey 0x%1\nnativeScanCode 0x%2\nkey 0x%3\nmac_iso_swap %4").arg(nvk, 0, 16).arg(event->nativeScanCode(), 0, 16).arg(key, 0, 16).arg(mac_iso_swap ? "yes" : "no"), QMessageBox::Ok, this); + questionbox.exec(); + } +#endif + if (mac_iso_swap) + nvk = (nvk == 0x0a) ? 0x32 : 0x0a; + } + + processKeyboardInput(down, nvk); } } #endif @@ -1733,23 +1225,10 @@ void MainWindow::keyPressEvent(QKeyEvent *event) { if (send_keyboard_input && !(kbd_req_capture && !mouse_capture)) { - // Windows keys in Qt have one-to-one mapping. - if (event->key() == Qt::Key_Pause && !keyboard_recv(0x38) && !keyboard_recv(0x138)) { - if ((keyboard_recv(0x1D) || keyboard_recv(0x11D))) { - keyboard_input(1, 0x46); - } else { - keyboard_input(0, 0xE1); - keyboard_input(0, 0x1D); - keyboard_input(0, 0x45); - keyboard_input(0, 0xE1); - keyboard_input(1, 0x1D); - keyboard_input(1, 0x45); - } - } else #ifdef Q_OS_MACOS - processMacKeyboardInput(true, event); + processMacKeyboardInput(true, event); #else - keyboard_input(1, x11_keycode_to_keysym(event->nativeScanCode())); + processKeyboardInput(true, event->nativeScanCode()); #endif } @@ -1797,7 +1276,7 @@ MainWindow::keyReleaseEvent(QKeyEvent *event) #ifdef Q_OS_MACOS processMacKeyboardInput(false, event); #else - keyboard_input(0, x11_keycode_to_keysym(event->nativeScanCode())); + processKeyboardInput(false, event->nativeScanCode()); #endif } diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index bf4a30f1d..826f75475 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -155,6 +155,7 @@ private: std::unique_ptr status; std::shared_ptr mm; + void processKeyboardInput(bool down, uint32_t keycode); #ifdef Q_OS_MACOS uint32_t last_modifiers = 0; void processMacKeyboardInput(bool down, const QKeyEvent *event); diff --git a/src/qt/xkbcommon_keyboard.cpp b/src/qt/xkbcommon_keyboard.cpp new file mode 100644 index 000000000..788087998 --- /dev/null +++ b/src/qt/xkbcommon_keyboard.cpp @@ -0,0 +1,234 @@ +/* + * 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 keyboard input module. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +extern "C" { +#include +}; + +#include +#include +#include "evdev_keyboard.hpp" + +#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'))) + +static std::unordered_map xkb_keycodes = { + {"ESC", 0x01}, + {"AE01", 0x02}, + {"AE02", 0x03}, + {"AE03", 0x04}, + {"AE04", 0x05}, + {"AE05", 0x06}, + {"AE06", 0x07}, + {"AE07", 0x08}, + {"AE08", 0x09}, + {"AE09", 0x0a}, + {"AE10", 0x0b}, + {"AE11", 0x0c}, + {"AE12", 0x0d}, + {"BKSP", 0x0e}, + + {"TAB", 0x0f}, + {"AD01", 0x10}, + {"AD02", 0x11}, + {"AD03", 0x12}, + {"AD04", 0x13}, + {"AD05", 0x14}, + {"AD06", 0x15}, + {"AD07", 0x16}, + {"AD08", 0x17}, + {"AD09", 0x18}, + {"AD10", 0x19}, + {"AD11", 0x1a}, + {"AD12", 0x1b}, + {"RTRN", 0x1c}, + {"LNFD", 0x1c}, /* linefeed => Enter */ + + {"LCTL", 0x1d}, + {"AC01", 0x1e}, + {"AC02", 0x1f}, + {"AC03", 0x20}, + {"AC04", 0x21}, + {"AC05", 0x22}, + {"AC06", 0x23}, + {"AC07", 0x24}, + {"AC08", 0x25}, + {"AC09", 0x26}, + {"AC10", 0x27}, + {"AC11", 0x28}, + + {"TLDE", 0x29}, + {"LFSH", 0x2a}, + {"BKSL", 0x2b}, + {"AB01", 0x2c}, + {"AB02", 0x2d}, + {"AB03", 0x2e}, + {"AB04", 0x2f}, + {"AB05", 0x30}, + {"AB06", 0x31}, + {"AB07", 0x32}, + {"AB08", 0x33}, + {"AB09", 0x34}, + {"AB10", 0x35}, + {"RTSH", 0x36}, + + {"KPMU", 0x37}, + {"LALT", 0x38}, + {"SPCE", 0x39}, + {"CAPS", 0x3a}, + {"FK01", 0x3b}, + {"FK02", 0x3c}, + {"FK03", 0x3d}, + {"FK04", 0x3e}, + {"FK05", 0x3f}, + {"FK06", 0x40}, + {"FK07", 0x41}, + {"FK08", 0x42}, + {"FK09", 0x43}, + {"FK10", 0x44}, + + {"NMLK", 0x45}, + {"SCLK", 0x46}, + {"FK14", 0x46}, /* F14 => Scroll Lock (for Apple keyboards) */ + {"KP7", 0x47}, + {"KP8", 0x48}, + {"KP9", 0x49}, + {"KPSU", 0x4a}, + {"KP4", 0x4b}, + {"KP5", 0x4c}, + {"KP6", 0x4d}, + {"KPAD", 0x4e}, + {"KP1", 0x4f}, + {"KP2", 0x50}, + {"KP3", 0x51}, + {"KP0", 0x52}, + {"KPDL", 0x53}, + + {"LSGT", 0x56}, + {"FK11", 0x57}, + {"FK12", 0x58}, + {"FK16", 0x5d}, /* F16 => F13 */ + {"FK17", 0x5e}, /* F17 => F14 */ + {"FK18", 0x5f}, /* F18 => F15 */ + + /* Japanese keys. */ + {"JPCM", 0x5c}, /* Num, */ + {"KPDC", 0x5c}, + {"HKTG", 0x70}, /* hiragana-katakana toggle */ + {"AB11", 0x73}, /* \_ and Brazilian /? */ + {"HZTG", 0x76}, /* hankaku-zenkaku toggle */ + {"HIRA", 0x77}, + {"KATA", 0x78}, + {"HENK", 0x79}, + {"KANA", 0x79}, /* kana => henkan (for Apple keyboards) */ + {"MUHE", 0x7b}, + {"EISU", 0x7b}, /* eisu => muhenkan (for Apple keyboards) */ + {"AE13", 0x7d}, /* \| */ + {"KPPT", 0x7e}, /* Brazilian Num. */ + {"I06", 0x7e}, /* alias of KPPT on keycodes/xfree86 (i.e. X11 forwarding) */ + + /* Korean keys. */ + {"HJCV", 0xf1}, /* hancha toggle */ + {"HNGL", 0xf2}, /* latin toggle */ + + {"KPEN", 0x11c}, + {"RCTL", 0x11d}, + {"KPDV", 0x135}, + {"PRSC", 0x137}, + {"SYRQ", 0x137}, + {"FK13", 0x137}, /* F13 => SysRq (for Apple keyboards) */ + {"RALT", 0x138}, + {"ALGR", 0x138}, + {"LVL3", 0x138}, /* observed on TigerVNC with AltGr-enabled layout */ + {"PAUS", 0x145}, + {"FK15", 0x145}, /* F15 => Pause (for Apple keyboards) */ + {"BRK", 0x145}, + {"HOME", 0x147}, + {"UP", 0x148}, + {"PGUP", 0x149}, + {"LEFT", 0x14b}, + {"RGHT", 0x14d}, + {"END", 0x14f}, + {"DOWN", 0x150}, + {"PGDN", 0x151}, + {"INS", 0x152}, + {"DELE", 0x153}, + + {"LWIN", 0x15b}, + {"LMTA", 0x15b}, + {"RWIN", 0x15c}, + {"RMTA", 0x15c}, + {"MENU", 0x15d}, + {"COMP", 0x15d}, /* Compose as Menu */ + + /* Multimedia keys. Same notes as evdev_keyboard apply here. */ + {"KPEQ", 0x59}, /* Num= */ + {"FRNT", 0x101}, /* # Logitech Task Select */ + {"UNDO", 0x108}, /* # */ + {"PAST", 0x10a}, /* # Paste */ + {"FIND", 0x112}, /* # Logitech */ + {"CUT", 0x117}, /* # */ + {"COPY", 0x118}, /* # */ + {"MUTE", 0x120}, + {"VOL-", 0x12e}, + {"VOL+", 0x130}, + {"HELP", 0x13b}, + {"OPEN", 0x13f}, + {"POWR", 0x15e}, + {"STOP", 0x168}, +}; +struct xkb_keymap *xkbcommon_keymap = nullptr; + +void +xkbcommon_init(struct xkb_keymap *keymap) +{ + if (keymap) + xkbcommon_keymap = keymap; +} + +void +xkbcommon_close() +{ + xkbcommon_keymap = nullptr; +} + +uint16_t +xkbcommon_translate(uint32_t keycode) +{ + const char *key_name = xkb_keymap_key_get_name(xkbcommon_keymap, keycode); + + /* 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 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); +#if 0 + else + 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 new file mode 100644 index 000000000..c3b28eeec --- /dev/null +++ b/src/qt/xkbcommon_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 xkbcommon keyboard input module. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +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..4db4971ee --- /dev/null +++ b/src/qt/xkbcommon_wl_keyboard.cpp @@ -0,0 +1,241 @@ +/* + * 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; + } + + if (seat->keymap) { + static struct xkb_keymap *keymap = seat->keymap; + seat->keymap = NULL; + xkbcommon_wl_set_keymap(); + xkb_keymap_unref(keymap); + } + + 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"; + + 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 new file mode 100644 index 000000000..8e0167518 --- /dev/null +++ b/src/qt/xkbcommon_x11_keyboard.cpp @@ -0,0 +1,86 @@ +/* + * 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-x11 keyboard input module. + * + * Heavily inspired by libxkbcommon interactive-x11.c + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +extern "C" { +/* xkb.h has identifiers named "explicit", which is a C++ keyword now... */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define explicit explicit_ +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#include +#undef explicit + +#include +}; +#include "xkbcommon_keyboard.hpp" + +#include +#include +#include + +void +xkbcommon_x11_init() +{ + xcb_connection_t *conn; + struct xkb_context *ctx; + int32_t core_kbd_device_id; + struct xkb_keymap *keymap; + + conn = (xcb_connection_t *) QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("connection"); + if (!conn) { + qWarning() << "XKB Keyboard: X server connection failed"; + return; + } + + int ret = xkb_x11_setup_xkb_extension(conn, + XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + NULL, NULL, NULL, NULL); + if (!ret) { + qWarning() << "XKB Keyboard: XKB extension setup failed"; + return; + } + + ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!ctx) { + qWarning() << "XKB Keyboard: XKB context creation failed"; + return; + } + + core_kbd_device_id = xkb_x11_get_core_keyboard_device_id(conn); + if (core_kbd_device_id == -1) { + qWarning() << "XKB Keyboard: Core keyboard device not found"; + goto err_ctx; + } + + keymap = xkb_x11_keymap_new_from_device(ctx, conn, core_kbd_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!keymap) { + qWarning() << "XKB Keyboard: Keymap loading failed"; + goto err_ctx; + } + + xkbcommon_init(keymap); + return; + +err_ctx: + xkb_context_unref(ctx); +} diff --git a/src/qt/xkbcommon_x11_keyboard.hpp b/src/qt/xkbcommon_x11_keyboard.hpp new file mode 100644 index 000000000..d8c063acb --- /dev/null +++ b/src/qt/xkbcommon_x11_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-x11 keyboard input module. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +void xkbcommon_x11_init();