437 lines
11 KiB
C
437 lines
11 KiB
C
/*
|
|
* Copyright 2007 The Android Open Source Project
|
|
*
|
|
* Input event device.
|
|
*/
|
|
#include "Common.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/input.h>
|
|
|
|
|
|
/*
|
|
* Input event device state.
|
|
*/
|
|
typedef struct EventState {
|
|
struct input_id ident;
|
|
|
|
char* name;
|
|
char* location;
|
|
char* idstr;
|
|
int protoVersion;
|
|
} EventState;
|
|
|
|
/*
|
|
* Key bit mask, for EVIOCGBIT(EV_KEY).
|
|
*
|
|
* (For now, just pretend to be a "goldfish" like the emulator.)
|
|
*/
|
|
static const unsigned char gKeyBitMask[64] = {
|
|
// These bits indicate which keys the device has
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
// These bits indicate other capabilities, such
|
|
// as whether it's a trackball or a touchscreen
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // touchscreen
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
/*
|
|
* Abs bit mask, for EVIOCGBIT(EV_ABS).
|
|
*
|
|
* Pretend to be a normal single touch panel
|
|
*/
|
|
static const unsigned char gAbsBitMask[64] = {
|
|
// these bits indicate the capabilities of the touch screen
|
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ABS_X, ABS_Y
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
/*
|
|
* Set some stuff up.
|
|
*/
|
|
static void configureInitialState(const char* pathName, EventState* eventState)
|
|
{
|
|
/*
|
|
* Swim like a goldfish.
|
|
*/
|
|
eventState->ident.bustype = 0;
|
|
eventState->ident.vendor = 0;
|
|
eventState->ident.product = 0;
|
|
eventState->ident.version = 0;
|
|
|
|
eventState->name = strdup(gWrapSim.keyMap);
|
|
eventState->location = strdup("");
|
|
eventState->idstr = strdup("");
|
|
eventState->protoVersion = 0x010000;
|
|
}
|
|
|
|
/*
|
|
* Free up the state structure.
|
|
*/
|
|
static void freeState(EventState* eventState)
|
|
{
|
|
if (eventState != NULL) {
|
|
free(eventState->name);
|
|
free(eventState->location);
|
|
free(eventState->idstr);
|
|
free(eventState);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle one of the EVIOCGABS requests.
|
|
*
|
|
* Currently not doing much here.
|
|
*/
|
|
static void handleAbsGet(int reqIdx, void* argp)
|
|
{
|
|
struct input_absinfo info;
|
|
|
|
switch (reqIdx) {
|
|
case ABS_X:
|
|
wsLog(" req for abs X\n");
|
|
break;
|
|
case ABS_Y:
|
|
wsLog(" req for abs Y\n");
|
|
break;
|
|
case ABS_PRESSURE:
|
|
wsLog(" req for abs PRESSURE\n");
|
|
break;
|
|
case ABS_TOOL_WIDTH:
|
|
wsLog(" req for abs TOOL_WIDTH\n");
|
|
break;
|
|
default:
|
|
wsLog(" req for unexpected event abs 0x%02x\n", reqIdx);
|
|
break;
|
|
}
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
memcpy(argp, &info, sizeof(struct input_absinfo));
|
|
}
|
|
|
|
/*
|
|
* Return the next available input event.
|
|
*
|
|
* We just pass this through to the real "read", since "fd" is real.
|
|
*/
|
|
static ssize_t readEvent(FakeDev* dev, int fd, void* buf, size_t count)
|
|
{
|
|
return _ws_read(fd, buf, count);
|
|
}
|
|
|
|
/*
|
|
* Somebody is trying to write to the event pipe. This can be used to set
|
|
* the state of LED.
|
|
*/
|
|
static ssize_t writeEvent(FakeDev* dev, int fd, const void* buf, size_t count)
|
|
{
|
|
const struct input_event* piev;
|
|
|
|
if (count == sizeof(*piev)) {
|
|
piev = (const struct input_event*) buf;
|
|
|
|
if (piev->type == EV_LED) {
|
|
wsLog("%s: set LED code=%d value=%d\n",
|
|
dev->debugName, piev->code, piev->value);
|
|
} else {
|
|
wsLog("%s: writeEvent got %d bytes, type=%d\n",
|
|
dev->debugName, count, piev->type);
|
|
}
|
|
} else {
|
|
wsLog("%s: warning: writeEvent got %d bytes, not sure why\n",
|
|
dev->debugName, count);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
* Handle event ioctls.
|
|
*/
|
|
static int ioctlEvent(FakeDev* dev, int fd, int request, void* argp)
|
|
{
|
|
EventState* state = (EventState*) dev->state;
|
|
unsigned int urequest = (unsigned int) request;
|
|
|
|
wsLog("%s: ioctl(0x%x, %p)\n", dev->debugName, urequest, argp);
|
|
|
|
if (_IOC_TYPE(urequest) != _IOC_TYPE(EVIOCGVERSION)) {
|
|
wsLog("%s: inappropriate ioctl 0x%08x\n", dev->debugName, urequest);
|
|
return -1;
|
|
}
|
|
|
|
if (urequest == EVIOCGVERSION) {
|
|
*(int*)argp = state->protoVersion;
|
|
} else if (urequest == EVIOCGID) {
|
|
memcpy(argp, &state->ident, sizeof(struct input_id));
|
|
} else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGNAME(0))) {
|
|
int maxLen = _IOC_SIZE(urequest);
|
|
int strLen = (int) strlen(state->name);
|
|
if (strLen >= maxLen) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
memcpy(argp, state->name, strLen+1);
|
|
return strLen;
|
|
} else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGPHYS(0))) {
|
|
int maxLen = _IOC_SIZE(urequest);
|
|
int strLen = (int) strlen(state->location);
|
|
if (strLen >= maxLen) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
memcpy(argp, state->location, strLen+1);
|
|
return strLen;
|
|
} else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGUNIQ(0))) {
|
|
/* device doesn't seem to support this, neither will we */
|
|
return -1;
|
|
} else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGBIT(EV_KEY,0))) {
|
|
/* keys */
|
|
int maxLen = _IOC_SIZE(urequest);
|
|
if (maxLen > (int) sizeof(gKeyBitMask))
|
|
maxLen = sizeof(gKeyBitMask);
|
|
memcpy(argp, gKeyBitMask, maxLen);
|
|
} else if (_IOC_NR(urequest) == _IOC_NR(EVIOCGBIT(EV_REL,0))) {
|
|
/* relative controllers (trackball) */
|
|
int maxLen = _IOC_SIZE(urequest);
|
|
memset(argp, 0xff, maxLen);
|
|
} else if (!getenv("NOTOUCH") && _IOC_NR(urequest) == _IOC_NR(EVIOCGBIT(EV_ABS,0))) {
|
|
// absolute controllers (touch screen)
|
|
int maxLen = _IOC_SIZE(urequest);
|
|
if (maxLen > (int) sizeof(gAbsBitMask))
|
|
maxLen = sizeof(gAbsBitMask);
|
|
memcpy(argp, gAbsBitMask, maxLen);
|
|
|
|
} else if (_IOC_NR(urequest) >= _IOC_NR(EVIOCGABS(ABS_X)) &&
|
|
_IOC_NR(urequest) <= _IOC_NR(EVIOCGABS(ABS_MAX)))
|
|
{
|
|
/* get abs value / limits */
|
|
int reqIdx = _IOC_NR(urequest) - _IOC_NR(EVIOCGABS(ABS_X));
|
|
handleAbsGet(reqIdx, argp);
|
|
} else {
|
|
wsLog("GLITCH: UNKNOWN ioctl request 0x%x on %s\n",
|
|
urequest, dev->debugName);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Free up our state before closing down the fake descriptor.
|
|
*/
|
|
static int closeEvent(FakeDev* dev, int fd)
|
|
{
|
|
freeState((EventState*)dev->state);
|
|
dev->state = NULL;
|
|
if (gWrapSim.keyInputDevice == dev) {
|
|
gWrapSim.keyInputDevice = NULL;
|
|
wsLog("Sim input device closed\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Open an input event device.
|
|
*/
|
|
FakeDev* wsOpenDevEvent(const char* pathName, int flags)
|
|
{
|
|
FakeDev* newDev = wsCreateRealFakeDev(pathName);
|
|
if (newDev != NULL) {
|
|
newDev->read = readEvent;
|
|
newDev->write = writeEvent;
|
|
newDev->ioctl = ioctlEvent;
|
|
newDev->close = closeEvent;
|
|
|
|
EventState* eventState = calloc(1, sizeof(EventState));
|
|
|
|
configureInitialState(pathName, eventState);
|
|
newDev->state = eventState;
|
|
|
|
/*
|
|
* First one opened becomes the place where we queue up input
|
|
* events from the simulator. This approach will fail if the
|
|
* app opens the device, then opens it a second time for input,
|
|
* then closes the first. The app doesn't currently do this (though
|
|
* it does do quick opens to fiddle with LEDs).
|
|
*/
|
|
if (gWrapSim.keyInputDevice == NULL) {
|
|
gWrapSim.keyInputDevice = newDev;
|
|
wsLog("Device %p / %d will receive sim input events\n",
|
|
newDev, newDev->fd);
|
|
}
|
|
}
|
|
|
|
return newDev;
|
|
}
|
|
|
|
/*
|
|
* Write a key event.
|
|
*/
|
|
static int sendKeyEvent(FakeDev* dev, int code, int isDown)
|
|
{
|
|
struct input_event iev;
|
|
ssize_t actual;
|
|
|
|
gettimeofday(&iev.time, NULL);
|
|
iev.type = EV_KEY;
|
|
iev.code = code;
|
|
iev.value = (isDown != 0) ? 1 : 0;
|
|
|
|
actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
|
|
if (actual != (ssize_t) sizeof(iev)) {
|
|
wsLog("WARNING: send key event partial write (%d of %d)\n",
|
|
actual, sizeof(iev));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write an absolute (touch screen) event.
|
|
*/
|
|
static int sendAbsButton(FakeDev* dev, int x, int y, int isDown)
|
|
{
|
|
struct input_event iev;
|
|
ssize_t actual;
|
|
|
|
wsLog("absButton x=%d y=%d down=%d\n", x, y, isDown);
|
|
|
|
gettimeofday(&iev.time, NULL);
|
|
iev.type = EV_KEY;
|
|
iev.code = BTN_TOUCH;
|
|
iev.value = (isDown != 0) ? 1 : 0;
|
|
|
|
actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
|
|
if (actual != (ssize_t) sizeof(iev)) {
|
|
wsLog("WARNING: send touch event partial write (%d of %d)\n",
|
|
actual, sizeof(iev));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write an absolute (touch screen) event.
|
|
*/
|
|
static int sendAbsMovement(FakeDev* dev, int x, int y)
|
|
{
|
|
struct input_event iev;
|
|
ssize_t actual;
|
|
|
|
wsLog("absMove x=%d y=%d\n", x, y);
|
|
|
|
gettimeofday(&iev.time, NULL);
|
|
iev.type = EV_ABS;
|
|
iev.code = ABS_X;
|
|
iev.value = x;
|
|
|
|
actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
|
|
if (actual != (ssize_t) sizeof(iev)) {
|
|
wsLog("WARNING: send abs movement event partial X write (%d of %d)\n",
|
|
actual, sizeof(iev));
|
|
return -1;
|
|
}
|
|
|
|
iev.code = ABS_Y;
|
|
iev.value = y;
|
|
|
|
actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
|
|
if (actual != (ssize_t) sizeof(iev)) {
|
|
wsLog("WARNING: send abs movement event partial Y write (%d of %d)\n",
|
|
actual, sizeof(iev));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Not quite sure what this is for, but the emulator does it.
|
|
*/
|
|
static int sendAbsSyn(FakeDev* dev)
|
|
{
|
|
struct input_event iev;
|
|
ssize_t actual;
|
|
|
|
gettimeofday(&iev.time, NULL);
|
|
iev.type = EV_SYN;
|
|
iev.code = 0;
|
|
iev.value = 0;
|
|
|
|
actual = _ws_write(dev->otherFd, &iev, sizeof(iev));
|
|
if (actual != (ssize_t) sizeof(iev)) {
|
|
wsLog("WARNING: send abs movement syn (%d of %d)\n",
|
|
actual, sizeof(iev));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Send a key event to the fake key event device.
|
|
*
|
|
* We have to translate the simulator key event into one or more device
|
|
* key events.
|
|
*/
|
|
void wsSendSimKeyEvent(int key, int isDown)
|
|
{
|
|
FakeDev* dev;
|
|
EventState* state;
|
|
|
|
dev = gWrapSim.keyInputDevice;
|
|
if (dev == NULL)
|
|
return;
|
|
|
|
sendKeyEvent(dev, key, isDown);
|
|
}
|
|
|
|
/*
|
|
* Send a touch-screen event to the fake key event device.
|
|
*
|
|
* We have to translate the simulator key event into one or more device
|
|
* key events.
|
|
*/
|
|
void wsSendSimTouchEvent(int action, int x, int y)
|
|
{
|
|
FakeDev* dev;
|
|
EventState* state;
|
|
|
|
dev = gWrapSim.keyInputDevice;
|
|
if (dev == NULL)
|
|
return;
|
|
|
|
if (action == kTouchDown) {
|
|
sendAbsMovement(dev, x, y);
|
|
sendAbsButton(dev, x, y, 1);
|
|
sendAbsSyn(dev);
|
|
} else if (action == kTouchUp) {
|
|
sendAbsButton(dev, x, y, 0);
|
|
sendAbsSyn(dev);
|
|
} else if (action == kTouchDrag) {
|
|
sendAbsMovement(dev, x, y);
|
|
sendAbsSyn(dev);
|
|
} else {
|
|
wsLog("WARNING: unexpected sim touch action %d\n", action);
|
|
}
|
|
}
|
|
|