| /* |
| * 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); |
| } |
| } |
| |