Code owners
Assign users and groups as approvers for specific file changes. Learn more.
InputDevice.cpp 15.03 KiB
//
// vncflinger - Copyright (C) 2021 Stefanie Kondik
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#define LOG_TAG "VNCFlinger:InputDevice"
#include <utils/Log.h>
#include <future>
#include "InputDevice.h"
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <linux/uinput.h>
using namespace android;
static bool touch = false;
static const struct UInputOptions {
int cmd;
int bit;
} kOptions[] = {
{UI_SET_EVBIT, EV_KEY},
{UI_SET_EVBIT, EV_REP},
{UI_SET_EVBIT, EV_REL},
{UI_SET_RELBIT, REL_X},
{UI_SET_RELBIT, REL_Y},
{UI_SET_RELBIT, REL_WHEEL},
{UI_SET_EVBIT, EV_ABS},
{UI_SET_ABSBIT, ABS_X},
{UI_SET_ABSBIT, ABS_Y},
{UI_SET_EVBIT, EV_SYN},
{UI_SET_PROPBIT, INPUT_PROP_DIRECT},
};
static const struct UInputOptions mOptions[] = {
{UI_SET_EVBIT, EV_KEY},
{UI_SET_EVBIT, EV_REP},
{UI_SET_EVBIT, EV_REL},
{UI_SET_RELBIT, REL_X},
{UI_SET_RELBIT, REL_Y},
{UI_SET_RELBIT, REL_WHEEL},
{UI_SET_EVBIT, EV_SYN},
};
status_t InputDevice::start_async(uint32_t width, uint32_t height) {
// don't block the caller since this can take a few seconds
std::async(&InputDevice::start, this, width, height);
return NO_ERROR;
}
status_t InputDevice::start(uint32_t width, uint32_t height) {
Mutex::Autolock _l(mLock);
mLeftClicked = mMiddleClicked = mRightClicked = false;
mLastX = mLastY = 0;
struct input_id id = {
BUS_VIRTUAL, /* Bus type */
1, /* Vendor */
1, /* Product */
4, /* Version */
};
if (mFD >= 0) {
ALOGE("Input device already open!");
return NO_INIT;
}
mFD = open(UINPUT_DEVICE, O_WRONLY | O_NONBLOCK);
if (mFD < 0) {
ALOGE("Failed to open %s: err=%d", UINPUT_DEVICE, mFD);
return NO_INIT;
}
const auto options = touch ? kOptions : mOptions;
unsigned int idx = 0;
for (idx = 0; idx < (touch ? sizeof(kOptions) : sizeof(mOptions)) / (touch ? sizeof(kOptions[0]) : sizeof(mOptions[0])); idx++) {
if (ioctl(mFD, options[idx].cmd, options[idx].bit) < 0) {
ALOGE("uinput ioctl failed: %d %d", options[idx].cmd, options[idx].bit);
goto err_ioctl;
}
}
for (idx = 0; idx < KEY_MAX; idx++) {
if (!touch && idx == BTN_TOUCH)
continue;
if (ioctl(mFD, UI_SET_KEYBIT, idx) < 0) {
ALOGE("UI_SET_KEYBIT failed");
goto err_ioctl;
}
}
memset(&mUserDev, 0, sizeof(mUserDev));
strncpy(mUserDev.name, "VNC-RemoteInput", UINPUT_MAX_NAME_SIZE);
mUserDev.id = id;
if (touch) {
mUserDev.absmin[ABS_X] = 0;
mUserDev.absmax[ABS_X] = width;
mUserDev.absmin[ABS_Y] = 0;
mUserDev.absmax[ABS_Y] = height;
}
if (write(mFD, &mUserDev, sizeof(mUserDev)) != sizeof(mUserDev)) {
ALOGE("Failed to configure uinput device");
goto err_ioctl;
}
if (ioctl(mFD, UI_DEV_CREATE) == -1) {
ALOGE("UI_DEV_CREATE failed");
goto err_ioctl;
}
mOpened = true;
ALOGD("Virtual input device created successfully (%dx%d)", width, height);
return NO_ERROR;
err_ioctl:
int prev_errno = errno;
::close(mFD);
errno = prev_errno;
mFD = -1;
return NO_INIT;
}
status_t InputDevice::reconfigure(uint32_t width, uint32_t height) {
stop();
return start_async(width, height);
}
status_t InputDevice::stop() {
Mutex::Autolock _l(mLock);
mOpened = false;
if (mFD < 0) {
return OK;
}
ioctl(mFD, UI_DEV_DESTROY);
close(mFD);
mFD = -1;
return OK;
}
status_t InputDevice::inject(uint16_t type, uint16_t code, int32_t value) {
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0); /* This should not be able to fail ever.. */
event.type = type;
event.code = code;
event.value = value;
if (write(mFD, &event, sizeof(event)) != sizeof(event)) return BAD_VALUE;
return OK;
}
status_t InputDevice::injectSyn(uint16_t type, uint16_t code, int32_t value) {
if (inject(type, code, value) != OK) {
return BAD_VALUE;
}
return inject(EV_SYN, SYN_REPORT, 0);
}
status_t InputDevice::movePointer(int32_t x, int32_t y) {
ALOGV("movePointer: x=%d y=%d", x, y);
mLastX = x;
mLastY = y;
if (inject(EV_REL, REL_X, x) != OK) {
return BAD_VALUE;
}
return injectSyn(EV_REL, REL_Y, y);
}
status_t InputDevice::setPointer(int32_t x, int32_t y) {
ALOGV("setPointer: x=%d y=%d", x, y);
if (!touch) {
if (inject(EV_REL, REL_X, x - mLastX) != OK) {
return BAD_VALUE;
}
if (inject(EV_REL, REL_Y, y - mLastY) != OK) {
return BAD_VALUE;
}
mLastX = x;
mLastY = y;
return OK;
}
mLastX = x;
mLastY = y;
if (inject(EV_ABS, ABS_X, x) != OK) {
return BAD_VALUE;
}
return injectSyn(EV_ABS, ABS_Y, y);
}
status_t InputDevice::press(uint16_t code) {
return inject(EV_KEY, code, 1);
}
status_t InputDevice::release(uint16_t code) {
return inject(EV_KEY, code, 0);
}
status_t InputDevice::click(uint16_t code) {
if (press(code) != OK) {
return BAD_VALUE;
}
return release(code);
}
void InputDevice::keyEvent(bool down, uint32_t key) {
int code;
int sh = 0;
int alt = 0;
Mutex::Autolock _l(mLock);
if (!mOpened) return;
if ((code = keysym2scancode(key, &sh, &alt))) {
int ret = 0;
if (key && down) {
if (sh) press(42); // left shift
if (alt) press(56); // left alt
inject(EV_SYN, SYN_REPORT, 0);
ret = press(code);
if (ret != 0) {
ALOGE("Error: %d (%s)\n", errno, strerror(errno));
}
inject(EV_SYN, SYN_REPORT, 0);
ret = release(code);
if (ret != 0) {
ALOGE("Error: %d (%s)\n", errno, strerror(errno));
}
inject(EV_SYN, SYN_REPORT, 0);
if (alt) release(56); // left alt
if (sh) release(42); // left shift
inject(EV_SYN, SYN_REPORT, 0);
}
}
}
void InputDevice::pointerEvent(int buttonMask, int x, int y) {
Mutex::Autolock _l(mLock);
if (!mOpened) return;
ALOGV("pointerEvent: buttonMask=%x x=%d y=%d", buttonMask, x, y);
int32_t diffX = x - mLastX;
int32_t diffY = y - mLastY;
mLastX = x;
mLastY = y;
if (!touch) {
inject(EV_REL, REL_X, diffX);
inject(EV_REL, REL_Y, diffY);
inject(EV_SYN, SYN_REPORT, 0);
}
if ((buttonMask & 1) && !mLeftClicked) { // left btn clicked
mLeftClicked = true;
if (touch) {
inject(EV_ABS, ABS_X, x);
inject(EV_ABS, ABS_Y, y);
inject(EV_KEY, BTN_TOUCH, 1);
} else {
inject(EV_KEY, BTN_LEFT, 1);
}
inject(EV_SYN, SYN_REPORT, 0);
} else if (!(buttonMask & 1) && mLeftClicked) { // left btn released
mLeftClicked = false;
if (touch) {
inject(EV_ABS, ABS_X, x);
inject(EV_ABS, ABS_Y, y);
inject(EV_KEY, BTN_TOUCH, 0);
} else {
inject(EV_KEY, BTN_LEFT, 0);
}
inject(EV_SYN, SYN_REPORT, 0);
}
if ((buttonMask & 4) && !mRightClicked) // right btn clicked
{
mRightClicked = true;
if (touch) {
press(158); // back key
} else {
inject(EV_KEY, BTN_RIGHT, 1);
}
inject(EV_SYN, SYN_REPORT, 0);
} else if (!(buttonMask & 4) && mRightClicked) // right button released
{
mRightClicked = false;
if (touch) {
release(158);
} else {
inject(EV_KEY, BTN_RIGHT, 0);
}
inject(EV_SYN, SYN_REPORT, 0);
}
if ((buttonMask & 2) && !mMiddleClicked) { // mid btn clicked
mMiddleClicked = true;
if (touch) {
press(KEY_END);
} else {
inject(EV_KEY, BTN_MIDDLE, 1);
}
inject(EV_SYN, SYN_REPORT, 0);
} else if (!(buttonMask & 2) && mMiddleClicked) // mid btn released
{
mMiddleClicked = false;
if (touch) {
release(KEY_END);
} else {
inject(EV_KEY, BTN_MIDDLE, 0);
}
inject(EV_SYN, SYN_REPORT, 0);
}
if (buttonMask & 8) {
inject(EV_REL, REL_WHEEL, 1);
inject(EV_SYN, SYN_REPORT, 0);
}
if (buttonMask & 0x10) {
inject(EV_REL, REL_WHEEL, -1);
inject(EV_SYN, SYN_REPORT, 0);
}
}
// q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m
static const int qwerty[] = {30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50,
49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44};
// ,!,",#,$,%,&,',(,),*,+,,,-,.,/
static const int spec1[] = {57, 2, 40, 4, 5, 6, 8, 40, 10, 11, 9, 13, 51, 12, 52, 52};
static const int spec1sh[] = {0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1};
// :,;,<,=,>,?,@
static const int spec2[] = {39, 39, 227, 13, 228, 53, 3};
static const int spec2sh[] = {1, 0, 1, 1, 1, 1, 1};
// [,\,],^,_,`
static const int spec3[] = {26, 43, 27, 7, 12, 399};
static const int spec3sh[] = {0, 0, 0, 1, 1, 0};
// {,|,},~
static const int spec4[] = {26, 43, 27, 215, 14};
static const int spec4sh[] = {1, 1, 1, 1, 0};
int InputDevice::keysym2scancode(uint32_t c, int* sh, int* alt) {
int real = 1;
if ('a' <= c && c <= 'z') return qwerty[c - 'a'];
if ('A' <= c && c <= 'Z') {
(*sh) = 1;
return qwerty[c - 'A'];
}
if ('1' <= c && c <= '9') return c - '1' + 2;
if (c == '0') return 11;
if (32 <= c && c <= 47) {
(*sh) = spec1sh[c - 32];
return spec1[c - 32];
}
if (58 <= c && c <= 64) {
(*sh) = spec2sh[c - 58];
return spec2[c - 58];
}
if (91 <= c && c <= 96) {
(*sh) = spec3sh[c - 91];
return spec3[c - 91];
}
if (123 <= c && c <= 127) {
(*sh) = spec4sh[c - 123];
return spec4[c - 123];
}
switch (c) {
case 0xff08:
return 14; // backspace
case 0xff09:
return 15; // tab
case 1:
(*alt) = 1;
return 34; // ctrl+a
case 3:
(*alt) = 1;
return 46; // ctrl+c
case 4:
(*alt) = 1;
return 32; // ctrl+d
case 18:
(*alt) = 1;
return 31; // ctrl+r
case 0xff0D:
return 28; // enter
case 0xff1B:
return 158; // esc -> back
case 0xFF51:
return 105; // left -> DPAD_LEFT
case 0xFF53:
return 106; // right -> DPAD_RIGHT
case 0xFF54:
return 108; // down -> DPAD_DOWN
case 0xFF52:
return 103; // up -> DPAD_UP
// case 360:
// return 232;// end -> DPAD_CENTER (ball click)
case 0xff50:
return KEY_HOME; // home
case 0xffff:
return 158; // del -> back
case 0xff55:
return 229; // PgUp -> menu
case 0xffcf:
return 127; // F2 -> search
case 0xffe3:
return 127; // left ctrl -> search
case 0xff56:
return 61; // PgUp -> call
case 0xff57:
return 107; // End -> endcall
case 0xffc2:
return 211; // F5 -> focus
case 0xffc3:
return 212; // F6 -> camera
case 0xffc4:
return 150; // F7 -> explorer
case 0xffc5:
return 155; // F8 -> envelope
case 50081:
case 225:
(*alt) = 1;
if (real) return 48; // a with acute
return 30; // a with acute -> a with ring above
case 50049:
case 193:
(*sh) = 1;
(*alt) = 1;
if (real) return 48; // A with acute
return 30; // A with acute -> a with ring above
case 50089:
case 233:
(*alt) = 1;
return 18; // e with acute
case 50057:
case 201:
(*sh) = 1;
(*alt) = 1;
return 18; // E with acute
case 50093:
case 0xffbf:
(*alt) = 1;
if (real) return 36; // i with acute
return 23; // i with acute -> i with grave
case 50061:
case 205:
(*sh) = 1;
(*alt) = 1;
if (real) return 36; // I with acute
return 23; // I with acute -> i with grave
case 50099:
case 243:
(*alt) = 1;
if (real) return 16; // o with acute
return 24; // o with acute -> o with grave
case 50067:
case 211:
(*sh) = 1;
(*alt) = 1;
if (real) return 16; // O with acute
return 24; // O with acute -> o with grave
case 50102:
case 246:
(*alt) = 1;
return 25; // o with diaeresis
case 50070:
case 214:
(*sh) = 1;
(*alt) = 1;
return 25; // O with diaeresis
case 50577:
case 245:
(*alt) = 1;
if (real) return 19; // Hungarian o
return 25; // Hungarian o -> o with diaeresis
case 50576:
case 213:
(*sh) = 1;
(*alt) = 1;
if (real) return 19; // Hungarian O
return 25; // Hungarian O -> O with diaeresis
case 50106:
// case 0xffbe:
// (*alt)=1;
// if (real)
// return 17; //u with acute
// return 22; //u with acute -> u with grave
case 50074:
case 218:
(*sh) = 1;
(*alt) = 1;
if (real) return 17; // U with acute
return 22; // U with acute -> u with grave
case 50108:
case 252:
(*alt) = 1;
return 47; // u with diaeresis
case 50076:
case 220:
(*sh) = 1;
(*alt) = 1;
return 47; // U with diaeresis
case 50609:
case 251:
(*alt) = 1;
if (real) return 45; // Hungarian u
return 47; // Hungarian u -> u with diaeresis
case 50608:
case 219:
(*sh) = 1;
(*alt) = 1;
if (real) return 45; // Hungarian U
return 47; // Hungarian U -> U with diaeresis
}
return 0;
}