Add input handling class
The Input class depends on the RFB server and V4L2 video classes, so
add outlines for those as well.
Change-Id: I2826f3da78dee10826e378dfc2c773b891da1f03
Signed-off-by: Eddie James <eajames@linux.ibm.com>
diff --git a/ikvm_input.cpp b/ikvm_input.cpp
new file mode 100644
index 0000000..31a7be4
--- /dev/null
+++ b/ikvm_input.cpp
@@ -0,0 +1,363 @@
+#include "ikvm_input.hpp"
+
+#include "ikvm_server.hpp"
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <rfb/keysym.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/File/error.hpp>
+
+#include "scancodes.h"
+
+namespace ikvm
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::File::Error;
+
+const char Input::keyboardID = 1;
+const char Input::pointerID = 2;
+
+const char Input::shiftCtrlMap[NUM_MODIFIER_BITS] = {
+ 0x02, // left shift
+ 0x20, // right shift
+ 0x01, // left control
+ 0x10 // right control
+};
+
+const char Input::metaAltMap[NUM_MODIFIER_BITS] = {
+ 0x08, // left meta
+ (char)0x80, // right meta
+ 0x04, // left alt
+ 0x40 // right alt
+};
+
+Input::Input(const std::string& p) :
+ keyboardReport{0}, pointerReport{0}, path(p)
+{
+ fd = open(path.c_str(), O_RDWR);
+ if (fd < 0)
+ {
+ log<level::ERR>("Failed to open input device",
+ entry("PATH=%s", path.c_str()),
+ entry("ERROR=%s", strerror(errno)));
+ elog<Open>(
+ xyz::openbmc_project::Common::File::Open::ERRNO(errno),
+ xyz::openbmc_project::Common::File::Open::PATH(path.c_str()));
+ }
+
+ // set the HID identifier byte because device is combined pointer/keyboard
+ keyboardReport[0] = keyboardID;
+ pointerReport[0] = pointerID;
+}
+
+Input::~Input()
+{
+ close(fd);
+}
+
+void Input::keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl)
+{
+ Server::ClientData* cd = (Server::ClientData*)cl->clientData;
+ Input* input = cd->input;
+
+ if (down)
+ {
+ char sc = keyToScancode(key);
+
+ if (sc)
+ {
+ if (input->keysDown.find(key) == input->keysDown.end())
+ {
+ for (unsigned int i = 3; i < REPORT_LENGTH; ++i)
+ {
+ if (!input->keyboardReport[i])
+ {
+ input->keyboardReport[i] = sc;
+ input->keysDown.insert(std::make_pair(key, i));
+ input->sendKeyboard = true;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ char mod = keyToMod(key);
+
+ if (mod)
+ {
+ input->keyboardReport[1] |= 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
+ {
+ char mod = keyToMod(key);
+
+ if (mod)
+ {
+ input->keyboardReport[1] &= ~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[1] = buttonMask & 0xFF;
+
+ if (x >= 0 && (unsigned int)x < video.getWidth())
+ {
+ unsigned short xx = x * ((SHRT_MAX + 1) / video.getWidth());
+
+ memcpy(&input->pointerReport[2], &xx, 2);
+ }
+
+ if (y >= 0 && (unsigned int)y < video.getHeight())
+ {
+ unsigned short yy = y * ((SHRT_MAX + 1) / video.getHeight());
+
+ memcpy(&input->pointerReport[4], &yy, 2);
+ }
+
+ input->sendPointer = true;
+ rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
+}
+
+void Input::sendRaw(char* data, int size)
+{
+ if (write(fd, data, size) != size)
+ {
+ log<level::ERR>("Failed to write report",
+ entry("ERROR=%s", strerror(errno)));
+ }
+}
+
+void Input::sendReport()
+{
+ if (sendKeyboard)
+ {
+ if (write(fd, keyboardReport, REPORT_LENGTH) != REPORT_LENGTH)
+ {
+ log<level::ERR>("Failed to write keyboard report",
+ entry("ERROR=%s", strerror(errno)));
+ }
+
+ sendKeyboard = false;
+ }
+
+ if (sendPointer)
+ {
+ if (write(fd, pointerReport, POINTER_LENGTH) != POINTER_LENGTH)
+ {
+ log<level::ERR>("Failed to write pointer report",
+ entry("ERROR=%s", strerror(errno)));
+ }
+
+ sendPointer = false;
+ }
+}
+
+char Input::keyToMod(rfbKeySym key)
+{
+ char 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;
+}
+
+char Input::keyToScancode(rfbKeySym key)
+{
+ char 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