/*
* 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] = {
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,
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,
};
/*
* 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);
memset(argp, 0xff, 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);
}
}