#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) {
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();
}
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;
}