#include "input/keyboard.h" #include #include #include #include "util/debug.h" namespace mmth { namespace { using yellowstone::Endpoint; using yellowstone::GetEndpointRequest; using yellowstone::YellowstoneClient; void KeyboardListenerEntry(void* keyboard_base) { reinterpret_cast(keyboard_base)->ListenLoop(); } } // namespace KeyboardListenerBase::KeyboardListenerBase() { auto server_or = PortServer::Create(); if (!server_or) { crash("Failed to create server", server_or.error()); } server_ = server_or.value(); } void KeyboardListenerBase::Register() { uint64_t dup_cap; check(ZCapDuplicate(gInitEndpointCap, kZionPerm_All, &dup_cap)); YellowstoneClient client(dup_cap); GetEndpointRequest req; req.set_endpoint_name("voyageurs"); Endpoint endpt; check(client.GetEndpoint(req, endpt)); VoyageursClient vclient(endpt.endpoint()); KeyboardListener listn; // TODO: Create a "ASSIGN_OR_CRASH" macro to simplify this. auto client_or = server_.CreateClient(); if (!client_or.ok()) { crash("Failed to create client", client_or.error()); } listn.set_port_capability(client_or.value().cap()); check(vclient.RegisterKeyboardListener(listn)); } Thread KeyboardListenerBase::Listen() { return Thread(KeyboardListenerEntry, this); } void KeyboardListenerBase::ListenLoop() { while (true) { auto scancode_or = server_.RecvChar(); if (!scancode_or.ok()) { check(scancode_or.error()); } uint8_t scancode = scancode_or.value(); if (scancode == 0xE0) { extended_on_ = true; continue; } Keycode k = ScancodeToKeycode(scancode); Action a = ScancodeToAction(scancode); HandleKeycode(k, a); } } void KeyboardListenerBase::HandleKeycode(Keycode code, Action action) { char c = '\0'; if (action == kPressed) { if (code >= kA && code <= kZ) { if (IsShift()) { const char* alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; c = alpha[code - kA]; } else { const char* alpha = "abcdefghijklmnopqrstuvwxyz"; c = alpha[code - kA]; } } else if (code >= k1 && code <= k0) { if (IsShift()) { const char* num = "!@#$%^&*()"; c = num[code - k1]; } else { const char* num = "1234567890"; c = num[code - k1]; } } else if (code >= kMinus && code <= kBacktick) { if (IsShift()) { const char* sym = "_+{}|?:\"<>~"; c = sym[code - kMinus]; } else { const char* sym = "-=[]\\/;',.`"; c = sym[code - kMinus]; } } else if (code == kEnter) { c = '\n'; } else if (code == kSpace) { c = ' '; } else if (code == kTab) { c = '\t'; } else if (code == kBackspace) { c = '\b'; } else if (code == kLShift) { lshift_ = true; } else if (code == kRShift) { rshift_ = true; } } else if (action == kReleased) { if (code == kLShift) { lshift_ = false; } else if (code == kRShift) { rshift_ = false; } } if (c != '\0') { HandleCharacter(c); } } Keycode KeyboardListenerBase::ScancodeToKeycode(uint8_t scancode) { // Cancel out the released bit. scancode &= 0x7F; if (extended_on_) { extended_on_ = false; switch (scancode) { case 0x1D: return kRCtrl; case 0x38: return kRAlt; case 0x48: return kUp; case 0x4B: return kLeft; case 0x4D: return kRight; case 0x50: return kDown; case 0x53: return kDelete; case 0x5B: return kSuper; } dbgln("Unknown extended scancode {x}", scancode); return kUnknownKeycode; } switch (scancode) { case 0x04: return kA; case 0x05: return kB; case 0x06: return kC; case 0x07: return kD; case 0x08: return kE; case 0x09: return kF; case 0x0A: return kG; case 0x0B: return kH; case 0x0C: return kI; case 0x0D: return kJ; case 0x0E: return kK; case 0x0F: return kL; case 0x10: return kM; case 0x11: return kN; case 0x12: return kO; case 0x13: return kP; case 0x14: return kQ; case 0x15: return kR; case 0x16: return kS; case 0x17: return kT; case 0x18: return kU; case 0x19: return kV; case 0x1A: return kW; case 0x1B: return kX; case 0x1C: return kY; case 0x1D: return kZ; case 0x1E: return k1; case 0x1F: return k2; case 0x20: return k3; case 0x21: return k4; case 0x22: return k5; case 0x23: return k6; case 0x24: return k7; case 0x25: return k8; case 0x26: return k9; case 0x27: return k0; case 0x28: return kEnter; case 0x29: return kEsc; case 0x2A: return kBackspace; case 0x2B: return kTab; case 0x2C: return kSpace; case 0x2D: return kMinus; case 0x2E: return kEquals; case 0x2F: return kLBrace; case 0x30: return kRBrace; case 0x31: return kBSlash; case 0x33: return kSemicolon; case 0x34: return kQuote; case 0x35: return kBacktick; case 0x36: return kComma; case 0x38: return kPeriod; case 0x39: return kEsc; // Capslock } dbgln("Unknown scancode {x}", scancode); return kUnknownKeycode; } Action KeyboardListenerBase::ScancodeToAction(uint8_t scancode) { return (scancode & 0x80) ? kReleased : kPressed; } } // namespace mmth