#include <stdio.h> #include <unistd.h> #include <EGL/egl.h> #include <GLES/gl.h> #include <pthread.h> #include <ui/EventHub.h> #include <ui/FramebufferNativeWindow.h> #include <ui/EGLUtils.h> extern void AndroidInitArgs(int argc, char** argv); extern int AndroidInit(); extern int AndroidMotionEvent(unsigned long long eventTime, int action, float x, float y, float pressure, float size, int deviceId); extern int AndroidEvent(int type, int value); extern int AndroidStep(int width, int height); static int gDisplayWidth; static int gDisplayHeight; static EGLDisplay gDisplay; static EGLSurface gSurface; static EGLContext gContext; void checkEGLError(const char* msg) { unsigned int error = eglGetError(); if (error != EGL_SUCCESS) { fprintf(stderr, "%s: error %u\n", msg, error); } } void checkGLError(const char* msg) { unsigned int error = glGetError(); if (error != GL_NO_ERROR) { fprintf(stderr, "%s: error 0x%04X\n", msg, error); } } static android::sp<android::EventHub> gHub; class EventQueue { private: class Lock { public: Lock(pthread_mutex_t& mutex) { m_pMutex = &mutex; pthread_mutex_lock(m_pMutex); } ~Lock() { pthread_mutex_unlock(m_pMutex); } void wait(pthread_cond_t& cond) { pthread_cond_wait(&cond, m_pMutex); } void signal(pthread_cond_t& cond) { pthread_cond_signal(&cond); } private: pthread_mutex_t* m_pMutex; }; public: static const int MOTION_ACTION_DOWN = 0; static const int MOTION_ACTION_UP = 1; static const int MOTION_ACTION_MOVE = 2; // Platform-specific event types. static const int EV_DEVICE_ADDED = android::EventHub::DEVICE_ADDED; static const int EV_DEVICE_REMOVED = android::EventHub::DEVICE_REMOVED; struct Event { int32_t deviceId; int32_t type; int32_t scancode; int32_t keycode; uint32_t flags; int32_t value; nsecs_t when; }; EventQueue() { m_Head = 0; m_Count = 0; pthread_mutex_init(&m_mutex, NULL); pthread_cond_init(&m_space_available, NULL); startEventThread(); } // Returns NULL if no event available. // Call recycleEvent when you're done with the event Event* getEvent() { Event* result = NULL; Lock lock(m_mutex); if (m_Count > 0) { result = m_Events + m_Head; } return result; } void recycleEvent(Event* pEvent) { Lock lock(m_mutex); if (pEvent == m_Events + m_Head && m_Count > 0) { m_Head = incQueue(m_Head); m_Count--; lock.signal(m_space_available); } } private: inline size_t incQueue(size_t index) { return modQueue(index + 1); } inline size_t modQueue(size_t index) { return index & EVENT_SIZE_MASK; } void startEventThread() { pthread_create( &m_eventThread, NULL, &staticEventThreadMain, this); } static void* staticEventThreadMain(void* arg) { return ((EventQueue*) arg)->eventThreadMain(); } void* eventThreadMain() { gHub = new android::EventHub(); while(true) { android::RawEvent rawEvent; bool result = gHub->getEvent(& rawEvent); if (result) { Event event; event.deviceId = rawEvent.deviceId; event.when = rawEvent.when; event.type = rawEvent.type; event.value = rawEvent.value; event.keycode = rawEvent.keyCode; event.scancode = rawEvent.scanCode; event.flags = rawEvent.flags; Lock lock(m_mutex); while( m_Count == MAX_EVENTS) { lock.wait(m_space_available); } m_Events[modQueue(m_Head + m_Count)] = event; m_Count = incQueue(m_Count); } } return NULL; } static const size_t MAX_EVENTS = 16; static const size_t EVENT_SIZE_MASK = 0xf; pthread_t m_eventThread; pthread_mutex_t m_mutex; pthread_cond_t m_space_available; unsigned int m_Head; unsigned int m_Count; Event m_Events[MAX_EVENTS]; }; bool gNoEvents; EventQueue* gpEventQueue; int init(int argc, char** argv) { for(int i = 0; i < argc; i++) { char* p = argv[i]; if (strcmp(p, "-noevents") == 0) { printf("-noevents: will not look for events.\n"); gNoEvents = true; } } if (! gNoEvents) { gpEventQueue = new EventQueue(); } EGLNativeWindowType window = android_createDisplaySurface(); gDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); EGLint majorVersion; EGLint minorVersion; eglInitialize(gDisplay, &majorVersion, &minorVersion); checkEGLError("eglInitialize"); EGLint configRequest[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT|EGL_WINDOW_BIT, EGL_DEPTH_SIZE, 16, EGL_NONE }; EGLConfig config; android::EGLUtils::selectConfigForNativeWindow(gDisplay, configRequest, window, &config); gSurface = eglCreateWindowSurface(gDisplay, config, window, NULL); eglQuerySurface(gDisplay, gSurface, EGL_WIDTH, &gDisplayWidth); eglQuerySurface(gDisplay, gSurface, EGL_HEIGHT, &gDisplayHeight); fprintf(stderr, "display width = %d, height = %d\n", gDisplayWidth, gDisplayHeight); gContext = eglCreateContext(gDisplay, config, NULL, NULL); checkEGLError("eglCreateContext"); eglMakeCurrent(gDisplay, gSurface, gSurface, gContext); checkEGLError("eglMakeCurrent"); printf("vendor : %s\n", glGetString(GL_VENDOR)); printf("renderer : %s\n", glGetString(GL_RENDERER)); printf("version : %s\n", glGetString(GL_VERSION)); printf("extensions: %s\n", glGetString(GL_EXTENSIONS)); return 0; } // Quick and dirty implementation of absolute pointer events... bool lastAbsDown = false; bool absDown = false; bool absChanged = false; unsigned long long absDownTime = 0; int absX = 0; int absY = 0; int absPressure = 0; int absSize = 0; int lastAbsX = 0; int lastAbsY = 0; int lastAbsPressure = 0; int lastAbsSize = 0; void checkEvents() { if (gpEventQueue == NULL) { return; } while(true) { EventQueue::Event* pEvent = gpEventQueue->getEvent(); if (pEvent == NULL) { return; } #if 1 printf("Event deviceId: %d, type: %d, scancode: %d, keyCode: %d, flags: %d, value: %d, when: %llu\n", pEvent->deviceId, pEvent->type, pEvent->scancode, pEvent->keycode, pEvent->flags, pEvent->value, pEvent->when); #endif switch (pEvent->type) { case EV_KEY: // Keyboard input if (pEvent->scancode == BTN_TOUCH) { absDown = pEvent->value != 0; absChanged = true; } else { AndroidEvent(pEvent->value, pEvent->keycode); } break; case EV_ABS: if (pEvent->scancode == ABS_X) { absX = pEvent->value; absChanged = true; } else if (pEvent->scancode == ABS_Y) { absY = pEvent->value; absChanged = true; } else if (pEvent->scancode == ABS_PRESSURE) { absPressure = pEvent->value; absChanged = true; } else if (pEvent->scancode == ABS_TOOL_WIDTH) { absSize = pEvent->value; absChanged = true; } case EV_SYN: { if (absChanged) { absChanged = false; int action; if (absDown != lastAbsDown) { lastAbsDown = absDown; if (absDown) { action = EventQueue::MOTION_ACTION_DOWN; absDownTime = pEvent->when; } else { action = EventQueue::MOTION_ACTION_UP; absX = lastAbsX; absY = lastAbsY; absPressure = lastAbsPressure; absSize = lastAbsSize; } } else { action = EventQueue::MOTION_ACTION_MOVE; } float scaledX = absX; float scaledY = absY; float scaledPressure = 1.0f; float scaledSize = 0; #if 0 if (di != null) { if (di.absX != null) { scaledX = ((scaledX-di.absX.minValue) / di.absX.range) * (mDisplay.getWidth()-1); } if (di.absY != null) { scaledY = ((scaledY-di.absY.minValue) / di.absY.range) * (mDisplay.getHeight()-1); } if (di.absPressure != null) { scaledPressure = ((absPressure-di.absPressure.minValue) / (float)di.absPressure.range); } if (di.absSize != null) { scaledSize = ((absSize-di.absSize.minValue) / (float)di.absSize.range); } } #endif unsigned long long whenMS = pEvent->when / 1000000; AndroidMotionEvent(whenMS, action, scaledX, scaledY, scaledPressure, scaledSize, pEvent->deviceId); lastAbsX = absX; lastAbsY = absY; } } break; default: break; } gpEventQueue->recycleEvent(pEvent); } } int main(int argc, char** argv) { fprintf(stderr, "Welcome to stand-alone Android quake.\n"); AndroidInitArgs(argc, argv); int result = init(argc, argv); if (result) { return result; } if (!AndroidInit()) { return 1; } while(true) { AndroidStep(gDisplayWidth, gDisplayHeight); checkGLError("AndroidStep"); eglSwapBuffers(gDisplay, gSurface); checkEGLError("eglSwapBuffers"); checkEvents(); } return 0; }