diff --git a/CMakeLists.txt b/CMakeLists.txt index fc31095..5813339 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) # Single source of truth for the version: CI passes -DVMSIG_VERSION=${TAG#v}, so the project # version (-> libvgpu-perception SONAME/.so version) and the .deb version come from one tag. -set(VMSIG_VERSION "0.3.12" CACHE STRING "Release version (MAJOR.MINOR.PATCH); CI passes the tag") +set(VMSIG_VERSION "0.3.13" CACHE STRING "Release version (MAJOR.MINOR.PATCH); CI passes the tag") project(vmsig VERSION ${VMSIG_VERSION} LANGUAGES C) set(CMAKE_C_STANDARD 17) @@ -276,6 +276,13 @@ target_include_directories(vmsig_uinputlayouttest PRIVATE target_compile_options(vmsig_uinputlayouttest PRIVATE -Wall -Wextra) add_test(NAME uinputlayout COMMAND vmsig_uinputlayouttest) +add_executable(vmsig_keymaptest src/test/test_keymap.c) +target_link_libraries(vmsig_keymaptest PRIVATE vmsig) +target_include_directories(vmsig_keymaptest PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/si/input/include) +target_compile_options(vmsig_keymaptest PRIVATE -Wall -Wextra) +add_test(NAME keymap COMMAND vmsig_keymaptest) + add_executable(vmsig_memwritetest src/test/test_memwrite.c) target_link_libraries(vmsig_memwritetest PRIVATE vmsig Threads::Threads) target_include_directories(vmsig_memwritetest PRIVATE diff --git a/src/si/input/keymap.c b/src/si/input/keymap.c index da97e32..705fadb 100644 --- a/src/si/input/keymap.c +++ b/src/si/input/keymap.c @@ -62,6 +62,7 @@ const vmctl_keymap VMCTL_KEYS[] = { { KEY_DOT, "dot" }, { KEY_SLASH, "slash" }, { KEY_RIGHTSHIFT, "shift_r" }, + { KEY_KPASTERISK, "kp_multiply" }, { KEY_LEFTALT, "alt" }, { KEY_SPACE, "spc" }, { KEY_CAPSLOCK, "caps_lock" }, @@ -77,10 +78,25 @@ const vmctl_keymap VMCTL_KEYS[] = { { KEY_F10, "f10" }, { KEY_NUMLOCK, "num_lock" }, { KEY_SCROLLLOCK, "scroll_lock" }, + { KEY_KP7, "kp_7" }, + { KEY_KP8, "kp_8" }, + { KEY_KP9, "kp_9" }, + { KEY_KPMINUS, "kp_subtract" }, + { KEY_KP4, "kp_4" }, + { KEY_KP5, "kp_5" }, + { KEY_KP6, "kp_6" }, + { KEY_KPPLUS, "kp_add" }, + { KEY_KP1, "kp_1" }, + { KEY_KP2, "kp_2" }, + { KEY_KP3, "kp_3" }, + { KEY_KP0, "kp_0" }, + { KEY_KPDOT, "kp_decimal" }, { KEY_102ND, "less" }, { KEY_F11, "f11" }, { KEY_F12, "f12" }, + { KEY_KPENTER, "kp_enter" }, { KEY_RIGHTCTRL, "ctrl_r" }, + { KEY_KPSLASH, "kp_divide" }, { KEY_SYSRQ, "print" }, { KEY_RIGHTALT, "alt_r" }, { KEY_HOME, "home" }, @@ -94,7 +110,9 @@ const vmctl_keymap VMCTL_KEYS[] = { { KEY_INSERT, "insert" }, { KEY_DELETE, "delete" }, { KEY_POWER, "power" }, + { KEY_KPEQUAL, "kp_equals" }, { KEY_PAUSE, "pause" }, + { KEY_KPCOMMA, "kp_comma" }, { KEY_LEFTMETA, "meta_l" }, { KEY_RIGHTMETA, "meta_r" }, { KEY_SLEEP, "sleep" }, diff --git a/src/test/test_keymap.c b/src/test/test_keymap.c new file mode 100644 index 0000000..0b5a13b --- /dev/null +++ b/src/test/test_keymap.c @@ -0,0 +1,55 @@ +/* test_keymap.c — VMCTL_KEYS invariants. The table is the single source of truth for BOTH the + * uinput keybits (UI_SET_KEYBIT per entry) and the QMP qcode lookup, and it is searched with + * bsearch — so it MUST stay strictly ascending by evdev, and a missing entry silently drops the + * key on both paths. This test pins the ordering invariant and the keypad block that was added. */ +#include "keymap.h" + +#include +#include +#include + +static int g_fail = 0; +#define CHECK(cond, msg) do { if (!(cond)) { printf(" FAIL: %s\n", (msg)); g_fail = 1; } } while (0) + +/* vmctl_evdev_to_qcode(evdev) must resolve to `want`. */ +static void expect_qcode(int evdev, const char* want) { + const char* got = vmctl_evdev_to_qcode(evdev); + if (!got || strcmp(got, want) != 0) { + printf(" FAIL: evdev %d -> %s (want %s)\n", evdev, got ? got : "(null)", want); + g_fail = 1; + } +} + +int main(void) { + /* 1. strictly ascending by evdev => no duplicates AND a valid bsearch precondition. */ + for (int i = 1; i < VMCTL_KEYS_LEN; i++) + CHECK(VMCTL_KEYS[i].evdev > VMCTL_KEYS[i - 1].evdev, + "VMCTL_KEYS must be strictly ascending by evdev (bsearch precondition + no dup)"); + + /* 2. every entry round-trips through the bsearch lookup in its current order. */ + for (int i = 0; i < VMCTL_KEYS_LEN; i++) + CHECK(vmctl_evdev_to_qcode(VMCTL_KEYS[i].evdev) == VMCTL_KEYS[i].qcode, + "every evdev resolves to its own qcode"); + + /* 3. keypad block -> correct QEMU QKeyCodes (the gap that was fixed). */ + expect_qcode(KEY_KP0, "kp_0"); expect_qcode(KEY_KP1, "kp_1"); expect_qcode(KEY_KP2, "kp_2"); + expect_qcode(KEY_KP3, "kp_3"); expect_qcode(KEY_KP4, "kp_4"); expect_qcode(KEY_KP5, "kp_5"); + expect_qcode(KEY_KP6, "kp_6"); expect_qcode(KEY_KP7, "kp_7"); expect_qcode(KEY_KP8, "kp_8"); + expect_qcode(KEY_KP9, "kp_9"); + expect_qcode(KEY_KPPLUS, "kp_add"); + expect_qcode(KEY_KPMINUS, "kp_subtract"); + expect_qcode(KEY_KPASTERISK, "kp_multiply"); + expect_qcode(KEY_KPSLASH, "kp_divide"); + expect_qcode(KEY_KPENTER, "kp_enter"); + expect_qcode(KEY_KPDOT, "kp_decimal"); + expect_qcode(KEY_KPEQUAL, "kp_equals"); + expect_qcode(KEY_KPCOMMA, "kp_comma"); + + /* 4. NumLock toggle is present; a main-block key still resolves; an unmapped code is NULL. */ + expect_qcode(KEY_NUMLOCK, "num_lock"); + expect_qcode(KEY_A, "a"); + CHECK(vmctl_evdev_to_qcode(KEY_FN) == NULL, "an unmapped evdev returns NULL"); + + printf("keymap tests: %s\n", g_fail ? "FAIL" : "PASS"); + return g_fail ? 1 : 0; +}