mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-05 21:56:33 +00:00
KeyboardMap::decode threw on a keycode beyond the range of the loaded keymap, and the exception ended the whole process. xdotool binds such a keycode for a moment to type a character the layout lacks, so typing one closed the running program. Treat these keycodes like the keys the layout gives no symbol. Codes above 255 and a missing keymap still fail. Assisted-by: Claude Code:claude-opus-5-5 AI-Scope: Reproduced the crash, wrote the change and its regression test from a request to fix the failures found in a live Bitvise test.
58 lines
3.3 KiB
C++
58 lines
3.3 KiB
C++
#include "keyboard.h"
|
|
#include <xkbcommon/xkbcommon.h>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
static void check(bool value,const char *message)
|
|
{
|
|
if (!value) throw std::runtime_error(message);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
try {
|
|
auto *context=xkb_context_new(XKB_CONTEXT_NO_FLAGS); check(context,"cannot create reference XKB context");
|
|
for (const auto *layout : {"us","de"}) {
|
|
xkb_rule_names names{}; names.layout=layout;
|
|
auto *map=xkb_keymap_new_from_names(context,&names,XKB_KEYMAP_COMPILE_NO_FLAGS); check(map,"cannot build reference layout");
|
|
auto *text=xkb_keymap_get_as_string(map,XKB_KEYMAP_FORMAT_TEXT_V1); check(text,"cannot serialize reference layout");
|
|
raven::KeyboardMap keyboard; keyboard.load(text); std::free(text);
|
|
const uint32_t events[]{125,126,127,69,70},virtual_keys[]{0x5b,0x5c,0x5d,0x90,0x91},scans[]{91,92,93,69,70};
|
|
for (size_t i=0;i<5;++i) {
|
|
const auto key=keyboard.decode(events[i]);
|
|
check(key.code==virtual_keys[i] && key.scan==scans[i] && !key.character && key.extended==(i<3),"system key translation differs");
|
|
}
|
|
// Keys the layout gives no symbol (the Japanese Ro key, a vendor key) pass with no virtual key instead of failing.
|
|
const auto ro=keyboard.decode(89),vendor=keyboard.decode(141);
|
|
check(!ro.code && ro.scan==89 && !ro.character && !vendor.code && !vendor.scan && !vendor.character,"key without a symbol differs");
|
|
const auto g=keyboard.decode(34),y=keyboard.decode(21);
|
|
check(g.code==71 && g.scan==34 && g.character=='g' && !g.extended,"G keyboard contract differs");
|
|
check(y.code==(layout[0]=='u' ? 89U : 90U) && y.character==(layout[0]=='u' ? 'y' : 'z'),"layout-dependent virtual key differs");
|
|
const auto shift=xkb_keymap_mod_get_index(map,XKB_MOD_NAME_SHIFT),control=xkb_keymap_mod_get_index(map,XKB_MOD_NAME_CTRL);
|
|
check(shift<32 && control<32,"reference modifier index exceeds ABI");
|
|
keyboard.modifiers(1U<<shift,0,0,0);
|
|
check(keyboard.decode(34).character=='G' && keyboard.mouse_modifiers()==4,"shift translation differs");
|
|
keyboard.modifiers(1U<<control,0,0,0);
|
|
check(keyboard.decode(34).character==7 && keyboard.mouse_modifiers()==8,"control translation differs");
|
|
keyboard.modifiers(0,0,0,0);
|
|
bool rejected=false;
|
|
try { keyboard.load("invalid keymap"); } catch (const std::runtime_error &) { rejected=true; }
|
|
check(rejected && keyboard.decode(34).character=='g',"failed keymap reload damaged state");
|
|
rejected=false;
|
|
try { keyboard.decode(999); } catch (const std::runtime_error &) { rejected=true; }
|
|
check(rejected,"out-of-range keyboard code succeeded");
|
|
xkb_keymap_unref(map);
|
|
}
|
|
xkb_context_unref(context);
|
|
// A keycode past the end of the keymap, such as one xdotool binds for a moment, passes with no virtual key instead of failing.
|
|
raven::KeyboardMap small;
|
|
small.load("xkb_keymap { xkb_keycodes { <AC05> = 42; }; xkb_types { type \"ONE_LEVEL\" { modifiers=none; level_name[Level1]=\"Any\"; }; }; "
|
|
"xkb_compatibility { }; xkb_symbols { key <AC05> { [ g ] }; }; };");
|
|
check(small.decode(34).code==71,"small keymap differs");
|
|
const auto beyond=small.decode(200),before=small.decode(0);
|
|
check(!beyond.code && !beyond.character && !before.code && !before.character,"key outside the keymap differs");
|
|
} catch (const std::exception &error) { std::cerr<<error.what()<<'\n'; return 1; }
|
|
return 0;
|
|
}
|