| #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> |
| |
| 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) { |
| Event event; |
| bool result = gHub->getEvent(&event.deviceId, |
| &event.type, |
| &event.scancode, &event.keycode, &event.flags, |
| &event.value, &event.when); |
| if (result) { |
| 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(); |
| } |
| |
| gDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
| |
| EGLint majorVersion; |
| EGLint minorVersion; |
| |
| eglInitialize(gDisplay, &majorVersion, &minorVersion); |
| checkEGLError("eglInitialize"); |
| |
| EGLint configRequest[] = { |
| EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, EGL_BLUE_SIZE, 5, |
| EGL_DEPTH_SIZE, 16, |
| EGL_STENCIL_SIZE, 0, |
| EGL_NONE |
| }; |
| |
| EGLConfig config; |
| int numConfigs = 0; |
| eglChooseConfig(gDisplay, configRequest, &config, 1, &numConfigs); |
| checkEGLError("eglChooseConfig"); |
| |
| gSurface = eglCreateWindowSurface(gDisplay, config, |
| android_createDisplaySurface(), NULL); |
| |
| checkEGLError("eglMapWindowSurface"); |
| |
| 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; |
| } |