#include "nt/providers.h" #include #include #include #include #include static void check(bool value,const char *message) { if (!value) throw std::runtime_error(message); } int main(int argc,char **argv) { try { check(argc==3 || argc==4,"bootstrap, keyboard fixture and optional focus case required"); const std::string mode=argc==4 ? argv[3] : "normal"; std::ifstream file(argv[2],std::ios::binary | std::ios::ate); check(bool(file) && file.tellg()>0,"cannot read keyboard fixture"); std::vector bytes(size_t(file.tellg())); file.seekg(0); check(bool(file.read(reinterpret_cast(bytes.data()),std::streamsize(bytes.size()))),"short keyboard fixture"); auto *context=xkb_context_new(XKB_CONTEXT_NO_FLAGS); check(context,"cannot create reference keyboard context"); xkb_rule_names names{}; names.layout="us"; const auto serialize=[&] { auto *map=xkb_keymap_new_from_names(context,&names,XKB_KEYMAP_COMPILE_NO_FLAGS); check(map,"cannot create reference keyboard map"); auto *text=xkb_keymap_get_as_string(map,XKB_KEYMAP_FORMAT_TEXT_V1); check(text,"cannot serialize keyboard map"); const std::string serialized(text); std::free(text); xkb_keymap_unref(map); return serialized; }; const std::string keymap=serialize(); // The same keys under another layout: the held g key becomes i, and its release still has to end the g press. names.variant="dvorak"; const std::string remapped=serialize(); xkb_context_unref(context); raven::ModuleSet modules; auto &entry=modules.add("keyboard.exe",bytes); unsigned frames=0,removed=0; uint64_t window=0; bool typed=false,closed=false; raven::DesktopPresentation presentation; presentation.present=[&](uint64_t handle,std::string_view title,uint32_t width,uint32_t height,std::span pixels) { check(handle && (!window || window==handle) && width==32 && height==32 && pixels.size()==4096,"keyboard surface identity differs"); window=handle; check(title==(frames<2 ? "Raven native paint" : "Keyboard: g"),"guest character did not update the caption"); if (frames) for (unsigned y=0;y<32;++y) for (unsigned x=0;x<32;++x) { const auto at=(y*32+x)*4; check(pixels[at]==x*7 && pixels[at+1]==(31-y)*7 && pixels[at+2]==192,"keyboard workflow damaged pixels"); } ++frames; }; presentation.remove=[&](uint64_t handle) { check(handle==window,"removed wrong keyboard surface"); ++removed; }; presentation.poll=[&](std::chrono::milliseconds) { std::vector events; if (frames==2 && !typed) { typed=true; raven::DesktopEvent layout{0,0}; layout.kind=raven::DesktopEventKind::keymap; layout.keymap=keymap; events.push_back(std::move(layout)); raven::DesktopEvent focused{window,0,0,0,raven::DesktopEventKind::keyboard_focus}; if (mode!="normal" && mode!="remap") focused.held_keys={34,125}; if (mode=="duplicate") focused.held_keys={34,34}; if (mode=="outofrange") focused.held_keys={256}; if (mode=="nofocus") focused.window=0; events.push_back(std::move(focused)); if (mode=="held") { events.push_back({window,34,0,0,raven::DesktopEventKind::key}); events.push_back({window,125,1,0,raven::DesktopEventKind::key}); events.push_back({window,125,0,0,raven::DesktopEventKind::key}); } events.push_back({window,34,1,0,raven::DesktopEventKind::key}); if (mode=="remap") { raven::DesktopEvent change{0,0}; change.kind=raven::DesktopEventKind::keymap; change.keymap=remapped; events.push_back(std::move(change)); } events.push_back({window,34,0,0,raven::DesktopEventKind::key}); } else if (frames==3 && !closed) { closed=true; events.push_back({window,0x10}); } return events; }; if (mode!="normal" && mode!="held" && mode!="remap") { bool rejected=false; try { raven::run_native_process(modules,entry,argv[1],std::chrono::seconds(5),{},{},presentation); } catch (const std::runtime_error &error) { const std::string expected=mode=="nofocus" ? "keyboard focus requires its visible guest window" : "invalid initial keyboard key state"; check(error.what()==expected,"initial keyboard state failed for an unexpected reason"); rejected=true; } check(rejected,"invalid initial keyboard state succeeded"); } else { const auto result=raven::run_native_process(modules,entry,argv[1],std::chrono::seconds(5),{},{},presentation); check(result.stop==raven::NativeStop::returned && !result.value && typed && closed && frames==3 && removed==1,"SDK keyboard workflow failed"); } } catch (const std::exception &error) { std::cerr<