diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /libs/ui/EventHub.cpp | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'libs/ui/EventHub.cpp')
-rw-r--r-- | libs/ui/EventHub.cpp | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp new file mode 100644 index 0000000..f0c77ba --- /dev/null +++ b/libs/ui/EventHub.cpp @@ -0,0 +1,743 @@ +// +// Copyright 2005 The Android Open Source Project +// +// Handle events, like key input and vsync. +// +// The goal is to provide an optimized solution for Linux, not an +// implementation that works well across all platforms. We expect +// events to arrive on file descriptors, so that we can use a select() +// select() call to sleep. +// +// We can't select() on anything but network sockets in Windows, so we +// provide an alternative implementation of waitEvent for that platform. +// +#define LOG_TAG "EventHub" + +//#define LOG_NDEBUG 0 + +#include <ui/EventHub.h> +#include <hardware/power.h> + +#include <cutils/properties.h> +#include <utils/IServiceManager.h> +#include <utils/Log.h> +#include <utils/Timers.h> +#include <utils.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <memory.h> +#include <errno.h> +#include <assert.h> + +#include "KeyLayoutMap.h" + +#include <string.h> +#include <stdint.h> +#include <dirent.h> +#ifdef HAVE_INOTIFY +# include <sys/inotify.h> +#endif +#ifdef HAVE_ANDROID_OS +# include <sys/limits.h> /* not part of Linux */ +#endif +#include <sys/poll.h> +#include <sys/ioctl.h> + +/* this macro is used to tell if "bit" is set in "array" + * it selects a byte from the array, and does a boolean AND + * operation with a byte that only has the relevant bit set. + * eg. to check for the 12th bit, we do (array[1] & 1<<4) + */ +#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) + +#define ID_MASK 0x0000ffff +#define SEQ_MASK 0x7fff0000 +#define SEQ_SHIFT 16 +#define id_to_index(id) ((id&ID_MASK)+1) + +namespace android { + +static const char *WAKE_LOCK_ID = "KeyEvents"; +static const char *device_path = "/dev/input"; + +/* return the larger integer */ +static inline int max(int v1, int v2) +{ + return (v1 > v2) ? v1 : v2; +} + +EventHub::device_t::device_t(int32_t _id, const char* _path) + : id(_id), path(_path), classes(0) + , layoutMap(new KeyLayoutMap()), next(NULL) { +} + +EventHub::device_t::~device_t() { + delete layoutMap; +} + +EventHub::EventHub(void) + : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0) + , mDevicesById(0), mNumDevicesById(0) + , mOpeningDevices(0), mClosingDevices(0) + , mDevices(0), mFDs(0), mFDCount(0) +{ + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); +#ifdef EV_SW + memset(mSwitches, 0, sizeof(mSwitches)); +#endif +} + +/* + * Clean up. + */ +EventHub::~EventHub(void) +{ + release_wake_lock(WAKE_LOCK_ID); + // we should free stuff here... +} + +void EventHub::onFirstRef() +{ + mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; +} + +status_t EventHub::errorCheck() const +{ + return mError; +} + +String8 EventHub::getDeviceName(int32_t deviceId) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return String8(); + return device->name; +} + +uint32_t EventHub::getDeviceClasses(int32_t deviceId) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return 0; + return device->classes; +} + +int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, + int* outMaxValue, int* outFlat, int* outFuzz) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return -1; + + struct input_absinfo info; + + if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) { + LOGE("Error reading absolute controller %d for device %s fd %d\n", + axis, device->name.string(), mFDs[id_to_index(device->id)].fd); + return -1; + } + *outMinValue = info.minimum; + *outMaxValue = info.maximum; + *outFlat = info.flat; + *outFuzz = info.fuzz; + return 0; +} + +int EventHub::getSwitchState(int sw) const +{ +#ifdef EV_SW + if (sw >= 0 && sw <= SW_MAX) { + int32_t devid = mSwitches[sw]; + if (devid != 0) { + return getSwitchState(devid, sw); + } + } +#endif + return -1; +} + +int EventHub::getSwitchState(int32_t deviceId, int sw) const +{ +#ifdef EV_SW + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return -1; + + if (sw >= 0 && sw <= SW_MAX) { + uint8_t sw_bitmask[(SW_MAX+1)/8]; + memset(sw_bitmask, 0, sizeof(sw_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) { + return test_bit(sw, sw_bitmask) ? 1 : 0; + } + } +#endif + + return -1; +} + +int EventHub::getScancodeState(int code) const +{ + return getScancodeState(mFirstKeyboardId, code); +} + +int EventHub::getScancodeState(int32_t deviceId, int code) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return -1; + + if (code >= 0 && code <= KEY_MAX) { + uint8_t key_bitmask[(KEY_MAX+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { + return test_bit(code, key_bitmask) ? 1 : 0; + } + } + + return -1; +} + +int EventHub::getKeycodeState(int code) const +{ + return getKeycodeState(mFirstKeyboardId, code); +} + +int EventHub::getKeycodeState(int32_t deviceId, int code) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL || device->layoutMap == NULL) return -1; + + Vector<int32_t> scanCodes; + device->layoutMap->findScancodes(code, &scanCodes); + + uint8_t key_bitmask[(KEY_MAX+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { + #if 0 + for (size_t i=0; i<=KEY_MAX; i++) { + LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask)); + } + #endif + const size_t N = scanCodes.size(); + for (size_t i=0; i<N && i<=KEY_MAX; i++) { + int32_t sc = scanCodes.itemAt(i); + //LOGI("Code %d: down=%d", sc, test_bit(sc, key_bitmask)); + if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, key_bitmask)) { + return 1; + } + } + } + + return 0; +} + +EventHub::device_t* EventHub::getDevice(int32_t deviceId) const +{ + if (deviceId == 0) deviceId = mFirstKeyboardId; + int32_t id = deviceId & ID_MASK; + if (id >= mNumDevicesById || id < 0) return NULL; + device_t* dev = mDevicesById[id].device; + if (dev->id == deviceId) { + return dev; + } + return NULL; +} + +bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, + int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, + int32_t* outValue, nsecs_t* outWhen) +{ + *outDeviceId = 0; + *outType = 0; + *outScancode = 0; + *outKeycode = 0; + *outFlags = 0; + *outValue = 0; + *outWhen = 0; + + status_t err; + + fd_set readfds; + int maxFd = -1; + int cc; + int i; + int res; + int pollres; + struct input_event iev; + + // Note that we only allow one caller to getEvent(), so don't need + // to do locking here... only when adding/removing devices. + + while(1) { + + // First, report any devices that had last been added/removed. + if (mClosingDevices != NULL) { + device_t* device = mClosingDevices; + LOGV("Reporting device closed: id=0x%x, name=%s\n", + device->id, device->path.string()); + mClosingDevices = device->next; + *outDeviceId = device->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = DEVICE_REMOVED; + delete device; + return true; + } + if (mOpeningDevices != NULL) { + device_t* device = mOpeningDevices; + LOGV("Reporting device opened: id=0x%x, name=%s\n", + device->id, device->path.string()); + mOpeningDevices = device->next; + *outDeviceId = device->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = DEVICE_ADDED; + return true; + } + + release_wake_lock(WAKE_LOCK_ID); + + pollres = poll(mFDs, mFDCount, -1); + + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + + if (pollres <= 0) { + if (errno != EINTR) { + LOGW("select failed (errno=%d)\n", errno); + usleep(100000); + } + continue; + } + + //printf("poll %d, returned %d\n", mFDCount, pollres); + if(mFDs[0].revents & POLLIN) { + read_notify(mFDs[0].fd); + } + for(i = 1; i < mFDCount; i++) { + if(mFDs[i].revents) { + LOGV("revents for %d = 0x%08x", i, mFDs[i].revents); + if(mFDs[i].revents & POLLIN) { + res = read(mFDs[i].fd, &iev, sizeof(iev)); + if (res == sizeof(iev)) { + LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", + mDevices[i]->path.string(), + (int) iev.time.tv_sec, (int) iev.time.tv_usec, + iev.type, iev.code, iev.value); + *outDeviceId = mDevices[i]->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = iev.type; + *outScancode = iev.code; + if (iev.type == EV_KEY) { + err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags); + LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n", + iev.code, *outKeycode, *outFlags, err); + if (err != 0) { + *outKeycode = 0; + *outFlags = 0; + } + } else { + *outKeycode = iev.code; + } + *outValue = iev.value; + *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec); + return true; + } else { + if (res<0) { + LOGW("could not get event (errno=%d)", errno); + } else { + LOGE("could not get event (wrong size: %d)", res); + } + continue; + } + } + } + } + } +} + +/* + * Open the platform-specific input device. + */ +bool EventHub::openPlatformInput(void) +{ + /* + * Open platform-specific input device(s). + */ + int res; + + mFDCount = 1; + mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); + mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); + mFDs[0].events = POLLIN; + mDevices[0] = NULL; +#ifdef HAVE_INOTIFY + mFDs[0].fd = inotify_init(); + res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE); + if(res < 0) { + LOGE("could not add watch for %s, %s\n", device_path, strerror(errno)); + } +#else + /* + * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd. + * We allocate space for it and set it to something invalid. + */ + mFDs[0].fd = -1; +#endif + + res = scan_dir(device_path); + if(res < 0) { + LOGE("scan dir failed for %s\n", device_path); + //open_device("/dev/input/event0"); + } + + return true; +} + +// ---------------------------------------------------------------------------- + +int EventHub::open_device(const char *deviceName) +{ + int version; + int fd; + struct pollfd *new_mFDs; + device_t **new_devices; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + LOGV("Opening device: %s", deviceName); + + AutoMutex _l(mLock); + + fd = open(deviceName, O_RDWR); + if(fd < 0) { + LOGE("could not open %s, %s\n", deviceName, strerror(errno)); + return -1; + } + + if(ioctl(fd, EVIOCGVERSION, &version)) { + LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); + return -1; + } + if(ioctl(fd, EVIOCGID, &id)) { + LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno)); + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); + name[0] = '\0'; + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); + location[0] = '\0'; + } + if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno)); + idstr[0] = '\0'; + } + + int devid = 0; + while (devid < mNumDevicesById) { + if (mDevicesById[devid].device == NULL) { + break; + } + devid++; + } + if (devid >= mNumDevicesById) { + device_ent* new_devids = (device_ent*)realloc(mDevicesById, + sizeof(mDevicesById[0]) * (devid + 1)); + if (new_devids == NULL) { + LOGE("out of memory"); + return -1; + } + mDevicesById = new_devids; + mNumDevicesById = devid+1; + mDevicesById[devid].device = NULL; + mDevicesById[devid].seq = 0; + } + + mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; + if (mDevicesById[devid].seq == 0) { + mDevicesById[devid].seq = 1<<SEQ_SHIFT; + } + + new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1)); + new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1)); + if (new_mFDs == NULL || new_devices == NULL) { + LOGE("out of memory"); + return -1; + } + mFDs = new_mFDs; + mDevices = new_devices; + +#if 0 + LOGI("add device %d: %s\n", mFDCount, deviceName); + LOGI(" bus: %04x\n" + " vendor %04x\n" + " product %04x\n" + " version %04x\n", + id.bustype, id.vendor, id.product, id.version); + LOGI(" name: \"%s\"\n", name); + LOGI(" location: \"%s\"\n" + " id: \"%s\"\n", location, idstr); + LOGI(" version: %d.%d.%d\n", + version >> 16, (version >> 8) & 0xff, version & 0xff); +#endif + + device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName); + if (device == NULL) { + LOGE("out of memory"); + return -1; + } + + mFDs[mFDCount].fd = fd; + mFDs[mFDCount].events = POLLIN; + + // figure out the kinds of events the device reports + uint8_t key_bitmask[(KEY_MAX+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + LOGV("Getting keys..."); + if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) { + //LOGI("MAP\n"); + //for (int i=0; i<((KEY_MAX+1)/8); i++) { + // LOGI("%d: 0x%02x\n", i, key_bitmask[i]); + //} + for (int i=0; i<((BTN_MISC+7)/8); i++) { + if (key_bitmask[i] != 0) { + device->classes |= CLASS_KEYBOARD; + break; + } + } + } + if (test_bit(BTN_MOUSE, key_bitmask)) { + uint8_t rel_bitmask[(REL_MAX+1)/8]; + memset(rel_bitmask, 0, sizeof(rel_bitmask)); + LOGV("Getting relative controllers..."); + if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) + { + if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { + device->classes |= CLASS_TRACKBALL; + } + } + } + if (test_bit(BTN_TOUCH, key_bitmask)) { + uint8_t abs_bitmask[(ABS_MAX+1)/8]; + memset(abs_bitmask, 0, sizeof(abs_bitmask)); + LOGV("Getting absolute controllers..."); + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) + { + if (test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { + device->classes |= CLASS_TOUCHSCREEN; + } + } + } + +#ifdef EV_SW + // figure out the switches this device reports + uint8_t sw_bitmask[(SW_MAX+1)/8]; + memset(sw_bitmask, 0, sizeof(sw_bitmask)); + if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { + for (int i=0; i<EV_SW; i++) { + //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); + if (test_bit(i, sw_bitmask)) { + if (mSwitches[i] == 0) { + mSwitches[i] = device->id; + } + } + } + } +#endif + + LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", + deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); + + if ((device->classes&CLASS_KEYBOARD) != 0) { + char devname[101]; + char tmpfn[101]; + char keylayoutFilename[300]; + + // a more descriptive name + ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname); + devname[sizeof(devname)-1] = 0; + device->name = devname; + + // replace all the spaces with underscores + strcpy(tmpfn, devname); + for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) + *p = '_'; + + // find the .kl file we need for this device + const char* root = getenv("ANDROID_ROOT"); + snprintf(keylayoutFilename, sizeof(keylayoutFilename), + "%s/usr/keylayout/%s.kl", root, tmpfn); + bool defaultKeymap = false; + if (access(keylayoutFilename, R_OK)) { + snprintf(keylayoutFilename, sizeof(keylayoutFilename), + "%s/usr/keylayout/%s", root, "qwerty.kl"); + defaultKeymap = true; + } + device->layoutMap->load(keylayoutFilename); + + // tell the world about the devname (the descriptive name) + int32_t publicID; + if (!mHaveFirstKeyboard && !defaultKeymap) { + publicID = 0; + // the built-in keyboard has a well-known device ID of 0, + // this device better not go away. + mHaveFirstKeyboard = true; + mFirstKeyboardId = device->id; + } else { + publicID = device->id; + // ensure mFirstKeyboardId is set to -something-. + if (mFirstKeyboardId == 0) { + mFirstKeyboardId = device->id; + } + } + char propName[100]; + sprintf(propName, "hw.keyboards.%u.devname", publicID); + property_set(propName, devname); + + LOGI("New keyboard: publicID=%d device->id=%d devname='%s propName='%s' keylayout='%s'\n", + publicID, device->id, devname, propName, keylayoutFilename); + } + + LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", + deviceName, device, mFDCount, devid, device->classes); + + mDevicesById[devid].device = device; + device->next = mOpeningDevices; + mOpeningDevices = device; + mDevices[mFDCount] = device; + + mFDCount++; + return 0; +} + +int EventHub::close_device(const char *deviceName) +{ + AutoMutex _l(mLock); + + int i; + for(i = 1; i < mFDCount; i++) { + if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { + //LOGD("remove device %d: %s\n", i, deviceName); + device_t* device = mDevices[i]; + int count = mFDCount - i - 1; + int index = (device->id&ID_MASK); + mDevicesById[index].device = NULL; + memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); + memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); + +#ifdef EV_SW + for (int j=0; j<EV_SW; j++) { + if (mSwitches[j] == device->id) { + mSwitches[j] = 0; + } + } +#endif + + device->next = mClosingDevices; + mClosingDevices = device; + + mFDCount--; + + uint32_t publicID; + if (device->id == mFirstKeyboardId) { + LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", + device->path.string(), mFirstKeyboardId); + mFirstKeyboardId = 0; + publicID = 0; + } else { + publicID = device->id; + } + // clear the property + char propName[100]; + sprintf(propName, "hw.keyboards.%u.devname", publicID); + property_set(propName, NULL); + return 0; + } + } + LOGE("remote device: %s not found\n", deviceName); + return -1; +} + +int EventHub::read_notify(int nfd) +{ +#ifdef HAVE_INOTIFY + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + LOGW("could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + + strcpy(devname, device_path); + filename = devname + strlen(devname); + *filename++ = '/'; + + while(res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(event->len) { + strcpy(filename, event->name); + if(event->mask & IN_CREATE) { + open_device(devname); + } + else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } +#endif + return 0; +} + + +int EventHub::scan_dir(const char *dirname) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} + +}; // namespace android |