#include "ikvm_input.hpp" #include "ikvm_server.hpp" #include #include #include #include #include #include #include #include #include #include #include "scancodes.hpp" namespace ikvm { using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::File::Error; Input::Input(const std::string& kbdPath, const std::string& ptrPath) : keyboardFd(-1), pointerFd(-1), keyboardReport{0}, pointerReport{0}, keyboardPath(kbdPath), pointerPath(ptrPath) { if (!keyboardPath.empty()) { keyboardFd = open(keyboardPath.c_str(), O_RDWR | O_CLOEXEC); if (keyboardFd < 0) { log("Failed to open input device", entry("PATH=%s", keyboardPath.c_str()), entry("ERROR=%s", strerror(errno))); elog(xyz::openbmc_project::Common::File::Open::ERRNO(errno), xyz::openbmc_project::Common::File::Open::PATH( keyboardPath.c_str())); } } if (!pointerPath.empty()) { pointerFd = open(pointerPath.c_str(), O_RDWR | O_CLOEXEC); if (pointerFd < 0) { log("Failed to open input device", entry("PATH=%s", pointerPath.c_str()), entry("ERROR=%s", strerror(errno))); elog(xyz::openbmc_project::Common::File::Open::ERRNO(errno), xyz::openbmc_project::Common::File::Open::PATH( pointerPath.c_str())); } } } Input::~Input() { if (keyboardFd >= 0) { close(keyboardFd); } if (pointerFd >= 0) { close(pointerFd); } } void Input::keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl) { Server::ClientData* cd = (Server::ClientData*)cl->clientData; Input* input = cd->input; if (down) { uint8_t sc = keyToScancode(key); if (sc) { if (input->keysDown.find(key) == input->keysDown.end()) { for (unsigned int i = 2; i < KEY_REPORT_LENGTH; ++i) { if (!input->keyboardReport[i]) { input->keyboardReport[i] = sc; input->keysDown.insert(std::make_pair(key, i)); input->sendKeyboard = true; break; } } } } else { uint8_t mod = keyToMod(key); if (mod) { input->keyboardReport[0] |= mod; input->sendKeyboard = true; } } } else { auto it = input->keysDown.find(key); if (it != input->keysDown.end()) { input->keyboardReport[it->second] = 0; input->keysDown.erase(it); input->sendKeyboard = true; } else { uint8_t mod = keyToMod(key); if (mod) { input->keyboardReport[0] &= ~mod; input->sendKeyboard = true; } } } } void Input::pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl) { Server::ClientData* cd = (Server::ClientData*)cl->clientData; Input* input = cd->input; Server* server = (Server*)cl->screen->screenData; const Video& video = server->getVideo(); input->pointerReport[0] = buttonMask & 0xFF; if (x >= 0 && (unsigned int)x < video.getWidth()) { uint16_t xx = x * ((SHRT_MAX + 1) / video.getWidth()); memcpy(&input->pointerReport[1], &xx, 2); } if (y >= 0 && (unsigned int)y < video.getHeight()) { uint16_t yy = y * ((SHRT_MAX + 1) / video.getHeight()); memcpy(&input->pointerReport[3], &yy, 2); } input->sendPointer = true; rfbDefaultPtrAddEvent(buttonMask, x, y, cl); } void Input::sendWakeupPacket() { uint8_t wakeupReport[PTR_REPORT_LENGTH] = {0}; uint16_t xy = SHRT_MAX / 2; if (pointerFd < 0) { return; } memcpy(&wakeupReport[1], &xy, 2); memcpy(&wakeupReport[3], &xy, 2); if (write(pointerFd, wakeupReport, PTR_REPORT_LENGTH) != PTR_REPORT_LENGTH) { log("Failed to write report", entry("ERROR=%s", strerror(errno))); } } void Input::sendReport() { if (sendKeyboard && keyboardFd >= 0) { if (write(keyboardFd, keyboardReport, KEY_REPORT_LENGTH) != KEY_REPORT_LENGTH) { log("Failed to write keyboard report", entry("ERROR=%s", strerror(errno))); } sendKeyboard = false; } if (sendPointer && pointerFd >= 0) { if (write(pointerFd, pointerReport, PTR_REPORT_LENGTH) != PTR_REPORT_LENGTH) { log("Failed to write pointer report", entry("ERROR=%s", strerror(errno))); } sendPointer = false; } } uint8_t Input::keyToMod(rfbKeySym key) { uint8_t mod = 0; if (key >= XK_Shift_L && key <= XK_Control_R) { mod = shiftCtrlMap[key - XK_Shift_L]; } else if (key >= XK_Meta_L && key <= XK_Alt_R) { mod = metaAltMap[key - XK_Meta_L]; } return mod; } uint8_t Input::keyToScancode(rfbKeySym key) { uint8_t scancode = 0; if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z')) { scancode = USBHID_KEY_A + ((key & 0x5F) - 'A'); } else if (key >= '1' && key <= '9') { scancode = USBHID_KEY_1 + (key - '1'); } else if (key >= XK_F1 && key <= XK_F12) { scancode = USBHID_KEY_F1 + (key - XK_F1); } else { switch (key) { case XK_exclam: scancode = USBHID_KEY_1; break; case XK_at: scancode = USBHID_KEY_2; break; case XK_numbersign: scancode = USBHID_KEY_3; break; case XK_dollar: scancode = USBHID_KEY_4; break; case XK_percent: scancode = USBHID_KEY_5; break; case XK_asciicircum: scancode = USBHID_KEY_6; break; case XK_ampersand: scancode = USBHID_KEY_7; break; case XK_asterisk: scancode = USBHID_KEY_8; break; case XK_parenleft: scancode = USBHID_KEY_9; break; case XK_0: case XK_parenright: scancode = USBHID_KEY_0; break; case XK_Return: scancode = USBHID_KEY_RETURN; break; case XK_Escape: scancode = USBHID_KEY_ESC; break; case XK_BackSpace: scancode = USBHID_KEY_BACKSPACE; break; case XK_Tab: scancode = USBHID_KEY_TAB; break; case XK_space: scancode = USBHID_KEY_SPACE; break; case XK_minus: case XK_underscore: scancode = USBHID_KEY_MINUS; break; case XK_plus: case XK_equal: scancode = USBHID_KEY_EQUAL; break; case XK_bracketleft: case XK_braceleft: scancode = USBHID_KEY_LEFTBRACE; break; case XK_bracketright: case XK_braceright: scancode = USBHID_KEY_RIGHTBRACE; break; case XK_backslash: case XK_bar: scancode = USBHID_KEY_BACKSLASH; break; case XK_colon: case XK_semicolon: scancode = USBHID_KEY_SEMICOLON; break; case XK_quotedbl: case XK_apostrophe: scancode = USBHID_KEY_APOSTROPHE; break; case XK_grave: case XK_asciitilde: scancode = USBHID_KEY_GRAVE; break; case XK_comma: case XK_less: scancode = USBHID_KEY_COMMA; break; case XK_period: case XK_greater: scancode = USBHID_KEY_DOT; break; case XK_slash: case XK_question: scancode = USBHID_KEY_SLASH; break; case XK_Caps_Lock: scancode = USBHID_KEY_CAPSLOCK; break; case XK_Print: scancode = USBHID_KEY_PRINT; break; case XK_Scroll_Lock: scancode = USBHID_KEY_SCROLLLOCK; break; case XK_Pause: scancode = USBHID_KEY_PAUSE; break; case XK_Insert: scancode = USBHID_KEY_INSERT; break; case XK_Home: scancode = USBHID_KEY_HOME; break; case XK_Page_Up: scancode = USBHID_KEY_PAGEUP; break; case XK_Delete: scancode = USBHID_KEY_DELETE; break; case XK_End: scancode = USBHID_KEY_END; break; case XK_Page_Down: scancode = USBHID_KEY_PAGEDOWN; break; case XK_Right: scancode = USBHID_KEY_RIGHT; break; case XK_Left: scancode = USBHID_KEY_LEFT; break; case XK_Down: scancode = USBHID_KEY_DOWN; break; case XK_Up: scancode = USBHID_KEY_UP; break; case XK_Num_Lock: scancode = USBHID_KEY_NUMLOCK; break; } } return scancode; } } // namespace ikvm