2019-12-14 03:58:22 +00:00
|
|
|
#include "os.h"
|
2018-11-27 04:30:00 +00:00
|
|
|
#include <stdio.h>
|
2020-05-29 22:27:49 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
2020-06-25 22:35:33 +00:00
|
|
|
#include <pthread.h>
|
2019-12-10 21:15:12 +00:00
|
|
|
#include <EGL/egl.h>
|
|
|
|
#include <EGL/eglext.h>
|
2020-06-25 19:27:18 +00:00
|
|
|
|
|
|
|
// This is probably bad, but makes things easier to build
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wformat-pedantic"
|
|
|
|
#include <android_native_app_glue.c>
|
|
|
|
#pragma clang diagnostic pop
|
2018-11-16 12:44:20 +00:00
|
|
|
|
2020-01-29 05:42:44 +00:00
|
|
|
static struct {
|
2020-06-10 00:00:33 +00:00
|
|
|
struct android_app* app;
|
|
|
|
JNIEnv* jni;
|
2020-01-29 05:42:44 +00:00
|
|
|
EGLDisplay display;
|
|
|
|
EGLContext context;
|
2020-08-19 03:09:06 +00:00
|
|
|
EGLConfig config;
|
2020-01-29 05:42:44 +00:00
|
|
|
EGLSurface surface;
|
2020-06-10 00:00:33 +00:00
|
|
|
quitCallback onQuit;
|
2020-08-07 01:42:12 +00:00
|
|
|
keyboardCallback onKeyboardEvent;
|
2020-08-08 18:36:09 +00:00
|
|
|
textCallback onTextEvent;
|
2020-01-29 05:42:44 +00:00
|
|
|
} state;
|
|
|
|
|
2020-01-29 05:38:44 +00:00
|
|
|
static void onAppCmd(struct android_app* app, int32_t cmd) {
|
2020-07-04 02:17:39 +00:00
|
|
|
if (cmd == APP_CMD_DESTROY && state.onQuit) {
|
|
|
|
state.onQuit();
|
2020-06-10 00:00:33 +00:00
|
|
|
}
|
2020-01-29 05:38:44 +00:00
|
|
|
}
|
|
|
|
|
2020-08-07 01:42:12 +00:00
|
|
|
static int32_t onInputEvent(struct android_app* app, AInputEvent* event) {
|
|
|
|
if (AInputEvent_getType(event) != AINPUT_EVENT_TYPE_KEY || !state.onKeyboardEvent) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ButtonAction action;
|
|
|
|
switch (AKeyEvent_getAction(event)) {
|
|
|
|
case AKEY_EVENT_ACTION_DOWN: action = BUTTON_PRESSED; break;
|
|
|
|
case AKEY_EVENT_ACTION_UP: action = BUTTON_RELEASED; break;
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyCode key;
|
|
|
|
switch (AKeyEvent_getKeyCode(event)) {
|
|
|
|
case AKEYCODE_A: key = KEY_A; break;
|
2020-08-11 02:52:09 +00:00
|
|
|
case AKEYCODE_B: key = KEY_B; break;
|
|
|
|
case AKEYCODE_C: key = KEY_C; break;
|
2020-08-07 01:42:12 +00:00
|
|
|
case AKEYCODE_D: key = KEY_D; break;
|
|
|
|
case AKEYCODE_E: key = KEY_E; break;
|
2020-08-11 02:52:09 +00:00
|
|
|
case AKEYCODE_F: key = KEY_F; break;
|
|
|
|
case AKEYCODE_G: key = KEY_G; break;
|
|
|
|
case AKEYCODE_H: key = KEY_H; break;
|
|
|
|
case AKEYCODE_I: key = KEY_I; break;
|
|
|
|
case AKEYCODE_J: key = KEY_J; break;
|
|
|
|
case AKEYCODE_K: key = KEY_K; break;
|
|
|
|
case AKEYCODE_L: key = KEY_L; break;
|
|
|
|
case AKEYCODE_M: key = KEY_M; break;
|
|
|
|
case AKEYCODE_N: key = KEY_N; break;
|
|
|
|
case AKEYCODE_O: key = KEY_O; break;
|
|
|
|
case AKEYCODE_P: key = KEY_P; break;
|
|
|
|
case AKEYCODE_Q: key = KEY_Q; break;
|
|
|
|
case AKEYCODE_R: key = KEY_R; break;
|
|
|
|
case AKEYCODE_S: key = KEY_S; break;
|
|
|
|
case AKEYCODE_T: key = KEY_T; break;
|
|
|
|
case AKEYCODE_U: key = KEY_U; break;
|
|
|
|
case AKEYCODE_V: key = KEY_V; break;
|
|
|
|
case AKEYCODE_W: key = KEY_W; break;
|
|
|
|
case AKEYCODE_X: key = KEY_X; break;
|
|
|
|
case AKEYCODE_Y: key = KEY_Y; break;
|
|
|
|
case AKEYCODE_Z: key = KEY_Z; break;
|
|
|
|
case AKEYCODE_0: key = KEY_0; break;
|
|
|
|
case AKEYCODE_1: key = KEY_1; break;
|
|
|
|
case AKEYCODE_2: key = KEY_2; break;
|
|
|
|
case AKEYCODE_3: key = KEY_3; break;
|
|
|
|
case AKEYCODE_4: key = KEY_4; break;
|
|
|
|
case AKEYCODE_5: key = KEY_5; break;
|
|
|
|
case AKEYCODE_6: key = KEY_6; break;
|
|
|
|
case AKEYCODE_7: key = KEY_7; break;
|
|
|
|
case AKEYCODE_8: key = KEY_8; break;
|
|
|
|
case AKEYCODE_9: key = KEY_9; break;
|
|
|
|
|
|
|
|
case AKEYCODE_SPACE: key = KEY_SPACE; break;
|
|
|
|
case AKEYCODE_ENTER: key = KEY_ENTER; break;
|
|
|
|
case AKEYCODE_TAB: key = KEY_TAB; break;
|
|
|
|
case AKEYCODE_ESCAPE: key = KEY_ESCAPE; break;
|
|
|
|
case AKEYCODE_DEL: key = KEY_BACKSPACE; break;
|
2020-08-07 01:42:12 +00:00
|
|
|
case AKEYCODE_DPAD_UP: key = KEY_UP; break;
|
|
|
|
case AKEYCODE_DPAD_DOWN: key = KEY_DOWN; break;
|
|
|
|
case AKEYCODE_DPAD_LEFT: key = KEY_LEFT; break;
|
|
|
|
case AKEYCODE_DPAD_RIGHT: key = KEY_RIGHT; break;
|
2020-09-08 15:23:17 +00:00
|
|
|
case AKEYCODE_MOVE_HOME: key = KEY_HOME; break;
|
2020-08-11 02:52:09 +00:00
|
|
|
case AKEYCODE_MOVE_END: key = KEY_END; break;
|
|
|
|
case AKEYCODE_PAGE_UP: key = KEY_PAGE_UP; break;
|
|
|
|
case AKEYCODE_PAGE_DOWN: key = KEY_PAGE_DOWN; break;
|
|
|
|
case AKEYCODE_INSERT: key = KEY_INSERT; break;
|
|
|
|
case AKEYCODE_FORWARD_DEL: key = KEY_DELETE; break;
|
|
|
|
case AKEYCODE_F1: key = KEY_F1; break;
|
|
|
|
case AKEYCODE_F2: key = KEY_F2; break;
|
|
|
|
case AKEYCODE_F3: key = KEY_F3; break;
|
|
|
|
case AKEYCODE_F4: key = KEY_F4; break;
|
2020-08-07 01:42:12 +00:00
|
|
|
case AKEYCODE_F5: key = KEY_F5; break;
|
2020-08-11 02:52:09 +00:00
|
|
|
case AKEYCODE_F6: key = KEY_F6; break;
|
|
|
|
case AKEYCODE_F7: key = KEY_F7; break;
|
|
|
|
case AKEYCODE_F8: key = KEY_F8; break;
|
|
|
|
case AKEYCODE_F9: key = KEY_F9; break;
|
|
|
|
case AKEYCODE_F10: key = KEY_F10; break;
|
|
|
|
case AKEYCODE_F11: key = KEY_F11; break;
|
|
|
|
case AKEYCODE_F12: key = KEY_F12; break;
|
|
|
|
|
|
|
|
case AKEYCODE_GRAVE: key = KEY_BACKTICK; break;
|
|
|
|
case AKEYCODE_MINUS: key = KEY_MINUS; break;
|
|
|
|
case AKEYCODE_EQUALS: key = KEY_EQUALS; break;
|
|
|
|
case AKEYCODE_LEFT_BRACKET: key = KEY_LEFT_BRACKET; break;
|
|
|
|
case AKEYCODE_RIGHT_BRACKET: key = KEY_RIGHT_BRACKET; break;
|
|
|
|
case AKEYCODE_BACKSLASH: key = KEY_BACKSLASH; break;
|
|
|
|
case AKEYCODE_SEMICOLON: key = KEY_SEMICOLON; break;
|
|
|
|
case AKEYCODE_APOSTROPHE: key = KEY_APOSTROPHE; break;
|
|
|
|
case AKEYCODE_COMMA: key = KEY_COMMA; break;
|
|
|
|
case AKEYCODE_PERIOD: key = KEY_PERIOD; break;
|
|
|
|
case AKEYCODE_SLASH: key = KEY_SLASH; break;
|
|
|
|
|
|
|
|
case AKEYCODE_CTRL_LEFT: key = KEY_LEFT_CONTROL; break;
|
2020-08-29 21:27:00 +00:00
|
|
|
case AKEYCODE_SHIFT_LEFT: key = KEY_LEFT_SHIFT; break;
|
2020-08-11 02:52:09 +00:00
|
|
|
case AKEYCODE_ALT_LEFT: key = KEY_LEFT_ALT; break;
|
2020-08-19 20:03:06 +00:00
|
|
|
case AKEYCODE_META_LEFT: key = KEY_LEFT_OS; break;
|
2020-08-29 21:27:00 +00:00
|
|
|
case AKEYCODE_CTRL_RIGHT: key = KEY_RIGHT_CONTROL; break;
|
|
|
|
case AKEYCODE_SHIFT_RIGHT: key = KEY_RIGHT_SHIFT; break;
|
|
|
|
case AKEYCODE_ALT_RIGHT: key = KEY_RIGHT_ALT; break;
|
2020-08-19 20:03:06 +00:00
|
|
|
case AKEYCODE_META_RIGHT: key = KEY_RIGHT_OS; break;
|
2020-08-29 21:27:00 +00:00
|
|
|
|
2020-08-11 02:52:09 +00:00
|
|
|
case AKEYCODE_CAPS_LOCK: key = KEY_CAPS_LOCK; break;
|
|
|
|
case AKEYCODE_SCROLL_LOCK: key = KEY_SCROLL_LOCK; break;
|
|
|
|
case AKEYCODE_NUM_LOCK: key = KEY_NUM_LOCK; break;
|
|
|
|
|
2020-08-07 01:42:12 +00:00
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-07 20:15:45 +00:00
|
|
|
uint32_t scancode = AKeyEvent_getScanCode(event);
|
|
|
|
bool repeat = AKeyEvent_getRepeatCount(event) > 0;
|
|
|
|
|
|
|
|
state.onKeyboardEvent(action, key, scancode, repeat);
|
2020-08-07 01:42:12 +00:00
|
|
|
|
2020-08-08 18:36:09 +00:00
|
|
|
// Text event
|
|
|
|
if (action == BUTTON_PRESSED && state.onTextEvent) {
|
|
|
|
jclass jKeyEvent = (*state.jni)->FindClass(state.jni, "android/view/KeyEvent");
|
|
|
|
jmethodID jgetUnicodeChar = (*state.jni)->GetMethodID(state.jni, jKeyEvent, "getUnicodeChar", "(I)I");
|
|
|
|
jmethodID jKeyEventInit = (*state.jni)->GetMethodID(state.jni, jKeyEvent, "<init>", "(II)V");
|
|
|
|
jobject jevent = (*state.jni)->NewObject(state.jni, jKeyEvent, jKeyEventInit, AKEY_EVENT_ACTION_DOWN, AKeyEvent_getKeyCode(event));
|
|
|
|
uint32_t codepoint = (*state.jni)->CallIntMethod(state.jni, jevent, jgetUnicodeChar, AKeyEvent_getMetaState(event));
|
|
|
|
if (codepoint > 0) {
|
|
|
|
state.onTextEvent(codepoint);
|
|
|
|
}
|
2020-09-17 02:36:32 +00:00
|
|
|
(*state.jni)->DeleteLocalRef(state.jni, jevent);
|
|
|
|
(*state.jni)->DeleteLocalRef(state.jni, jKeyEvent);
|
2020-08-08 18:36:09 +00:00
|
|
|
}
|
|
|
|
|
2020-08-07 01:42:12 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-06-25 22:35:33 +00:00
|
|
|
int main(int argc, char** argv);
|
|
|
|
|
2020-01-29 05:38:44 +00:00
|
|
|
void android_main(struct android_app* app) {
|
2020-06-10 00:00:33 +00:00
|
|
|
state.app = app;
|
|
|
|
(*app->activity->vm)->AttachCurrentThread(app->activity->vm, &state.jni, NULL);
|
2020-07-04 02:17:39 +00:00
|
|
|
lovrPlatformOpenConsole();
|
2020-01-29 05:38:44 +00:00
|
|
|
app->onAppCmd = onAppCmd;
|
2020-08-07 01:42:12 +00:00
|
|
|
app->onInputEvent = onInputEvent;
|
2020-01-29 05:38:44 +00:00
|
|
|
main(0, NULL);
|
2020-06-10 00:00:33 +00:00
|
|
|
(*app->activity->vm)->DetachCurrentThread(app->activity->vm);
|
2020-01-29 05:38:44 +00:00
|
|
|
}
|
|
|
|
|
2018-11-16 12:44:20 +00:00
|
|
|
bool lovrPlatformInit() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrPlatformDestroy() {
|
2020-01-29 05:42:44 +00:00
|
|
|
if (state.display) eglMakeCurrent(state.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
if (state.surface) eglDestroySurface(state.display, state.surface);
|
|
|
|
if (state.context) eglDestroyContext(state.display, state.context);
|
|
|
|
if (state.display) eglTerminate(state.display);
|
2020-06-10 00:00:33 +00:00
|
|
|
memset(&state, 0, sizeof(state));
|
2018-11-16 12:44:20 +00:00
|
|
|
}
|
|
|
|
|
2019-05-21 03:44:39 +00:00
|
|
|
const char* lovrPlatformGetName() {
|
|
|
|
return "Android";
|
|
|
|
}
|
|
|
|
|
2020-05-29 22:27:49 +00:00
|
|
|
static uint64_t epoch;
|
2020-07-02 16:43:27 +00:00
|
|
|
#define NS_PER_SEC 1000000000ULL
|
2020-05-29 22:27:49 +00:00
|
|
|
|
|
|
|
static uint64_t getTime() {
|
|
|
|
struct timespec t;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &t);
|
|
|
|
return (uint64_t) t.tv_sec * NS_PER_SEC + (uint64_t) t.tv_nsec;
|
|
|
|
}
|
|
|
|
|
|
|
|
double lovrPlatformGetTime() {
|
|
|
|
return (getTime() - epoch) / (double) NS_PER_SEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrPlatformSetTime(double time) {
|
|
|
|
epoch = getTime() - (uint64_t) (time * NS_PER_SEC + .5);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrPlatformSleep(double seconds) {
|
|
|
|
seconds += .5e-9;
|
|
|
|
struct timespec t;
|
|
|
|
t.tv_sec = seconds;
|
|
|
|
t.tv_nsec = (seconds - t.tv_sec) * NS_PER_SEC;
|
|
|
|
while (nanosleep(&t, &t));
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:44:20 +00:00
|
|
|
void lovrPlatformPollEvents() {
|
2020-07-04 02:17:39 +00:00
|
|
|
// Notes about polling:
|
|
|
|
// - Stop polling if a destroy is requested to give the application a chance to shut down.
|
|
|
|
// Otherwise this loop would still wait for an event and the app would seem unresponsive.
|
|
|
|
// - Block if the app is paused or no window is present
|
|
|
|
// - If the app was active and becomes inactive after an event, break instead of waiting for
|
|
|
|
// another event. This gives the main loop a chance to respond (e.g. exit VR mode).
|
|
|
|
while (!state.app->destroyRequested) {
|
|
|
|
int events;
|
|
|
|
struct android_poll_source* source;
|
|
|
|
int timeout = (state.app->window && state.app->activityState == APP_CMD_RESUME) ? 0 : -1;
|
|
|
|
if (ALooper_pollAll(timeout, NULL, &events, (void**) &source) >= 0) {
|
|
|
|
if (source) {
|
|
|
|
source->process(state.app, source);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (timeout == 0 && (!state.app->window || state.app->activityState != APP_CMD_RESUME)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
2020-06-10 00:00:33 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-16 12:44:20 +00:00
|
|
|
}
|
|
|
|
|
2020-07-04 02:17:39 +00:00
|
|
|
// To make regular printing work, a thread makes a pipe and redirects stdout and stderr to the write
|
|
|
|
// end of the pipe. The read end of the pipe is forwarded to __android_log_write.
|
|
|
|
static struct {
|
|
|
|
int handles[2];
|
|
|
|
pthread_t thread;
|
|
|
|
} log;
|
|
|
|
|
|
|
|
static void* log_main(void* data) {
|
|
|
|
int* fd = data;
|
|
|
|
pipe(fd);
|
|
|
|
dup2(fd[1], STDOUT_FILENO);
|
|
|
|
dup2(fd[1], STDERR_FILENO);
|
|
|
|
setvbuf(stdout, 0, _IOLBF, 0);
|
|
|
|
setvbuf(stderr, 0, _IONBF, 0);
|
|
|
|
ssize_t length;
|
|
|
|
char buffer[1024];
|
|
|
|
while ((length = read(fd[0], buffer, sizeof(buffer) - 1)) > 0) {
|
|
|
|
buffer[length] = '\0';
|
|
|
|
__android_log_write(ANDROID_LOG_DEBUG, "LOVR", buffer);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-17 01:55:21 +00:00
|
|
|
void lovrPlatformOpenConsole() {
|
2020-07-04 02:17:39 +00:00
|
|
|
pthread_create(&log.thread, NULL, log_main, log.handles);
|
|
|
|
pthread_detach(log.thread);
|
2019-06-17 01:55:21 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 19:28:40 +00:00
|
|
|
size_t lovrPlatformGetHomeDirectory(char* buffer, size_t size) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t lovrPlatformGetDataDirectory(char* buffer, size_t size) {
|
2020-06-26 00:11:56 +00:00
|
|
|
const char* path = state.app->activity->externalDataPath;
|
|
|
|
size_t length = strlen(path);
|
|
|
|
if (length >= size) return 0;
|
|
|
|
memcpy(buffer, path, length);
|
|
|
|
buffer[length] = '\0';
|
|
|
|
return length;
|
2020-06-25 19:28:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t lovrPlatformGetWorkingDirectory(char* buffer, size_t size) {
|
|
|
|
return getcwd(buffer, size) ? strlen(buffer) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t lovrPlatformGetExecutablePath(char* buffer, size_t size) {
|
2020-06-26 00:11:56 +00:00
|
|
|
ssize_t length = readlink("/proc/self/exe", buffer, size - 1);
|
|
|
|
if (length >= 0) {
|
|
|
|
buffer[length] = '\0';
|
|
|
|
return length;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2020-06-25 19:28:40 +00:00
|
|
|
}
|
|
|
|
|
2020-07-29 22:30:14 +00:00
|
|
|
size_t lovrPlatformGetBundlePath(char* buffer, size_t size, const char** root) {
|
2020-06-25 19:28:40 +00:00
|
|
|
jobject activity = state.app->activity->clazz;
|
|
|
|
jclass class = (*state.jni)->GetObjectClass(state.jni, activity);
|
|
|
|
jmethodID getPackageCodePath = (*state.jni)->GetMethodID(state.jni, class, "getPackageCodePath", "()Ljava/lang/String;");
|
|
|
|
if (!getPackageCodePath) {
|
2020-09-17 02:39:27 +00:00
|
|
|
(*state.jni)->DeleteLocalRef(state.jni, class);
|
2020-06-25 19:28:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
jstring jpath = (*state.jni)->CallObjectMethod(state.jni, activity, getPackageCodePath);
|
2020-09-17 02:39:27 +00:00
|
|
|
(*state.jni)->DeleteLocalRef(state.jni, class);
|
2020-06-25 19:28:40 +00:00
|
|
|
if ((*state.jni)->ExceptionOccurred(state.jni)) {
|
|
|
|
(*state.jni)->ExceptionClear(state.jni);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-17 02:39:27 +00:00
|
|
|
size_t length = (*state.jni)->GetStringUTFLength(state.jni, jpath);
|
2020-06-25 19:28:40 +00:00
|
|
|
if (length >= size) return 0;
|
2020-09-17 02:39:27 +00:00
|
|
|
|
|
|
|
const char* path = (*state.jni)->GetStringUTFChars(state.jni, jpath, NULL);
|
2020-06-25 19:28:40 +00:00
|
|
|
memcpy(buffer, path, length);
|
|
|
|
buffer[length] = '\0';
|
2020-09-17 02:39:27 +00:00
|
|
|
(*state.jni)->ReleaseStringUTFChars(state.jni, jpath, path);
|
2020-07-29 22:30:14 +00:00
|
|
|
*root = "/assets";
|
2020-06-25 19:28:40 +00:00
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2020-08-17 06:55:12 +00:00
|
|
|
bool lovrPlatformCreateWindow(const WindowFlags* flags) {
|
2020-01-29 05:42:44 +00:00
|
|
|
if (state.display) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((state.display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eglInitialize(state.display, NULL, NULL) == EGL_FALSE) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLConfig configs[1024];
|
|
|
|
EGLint configCount;
|
|
|
|
if (eglGetConfigs(state.display, configs, sizeof(configs) / sizeof(configs[0]), &configCount) == EGL_FALSE) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const EGLint attributes[] = {
|
|
|
|
EGL_RED_SIZE, 8,
|
|
|
|
EGL_GREEN_SIZE, 8,
|
|
|
|
EGL_BLUE_SIZE, 8,
|
|
|
|
EGL_ALPHA_SIZE, 8,
|
|
|
|
EGL_DEPTH_SIZE, 0,
|
|
|
|
EGL_STENCIL_SIZE, 0,
|
|
|
|
EGL_SAMPLES, 0,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
2020-08-19 03:09:06 +00:00
|
|
|
state.config = 0;
|
|
|
|
for (EGLint i = 0; i < configCount && state.config == 0; i++) {
|
2020-01-29 05:42:44 +00:00
|
|
|
EGLint value, mask;
|
|
|
|
|
|
|
|
mask = EGL_OPENGL_ES3_BIT_KHR;
|
|
|
|
if (!eglGetConfigAttrib(state.display, configs[i], EGL_RENDERABLE_TYPE, &value) || (value & mask) != mask) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
mask = EGL_PBUFFER_BIT | EGL_WINDOW_BIT;
|
|
|
|
if (!eglGetConfigAttrib(state.display, configs[i], EGL_SURFACE_TYPE, &value) || (value & mask) != mask) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-29 22:27:49 +00:00
|
|
|
for (size_t a = 0; a < sizeof(attributes) / sizeof(attributes[0]); a += 2) {
|
2020-01-29 05:42:44 +00:00
|
|
|
if (attributes[a] == EGL_NONE) {
|
2020-08-19 03:09:06 +00:00
|
|
|
state.config = configs[i];
|
2020-01-29 05:42:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!eglGetConfigAttrib(state.display, configs[i], attributes[a], &value) || value != attributes[a + 1]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLint contextAttributes[] = {
|
|
|
|
EGL_CONTEXT_CLIENT_VERSION, 3,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
2020-08-19 03:09:06 +00:00
|
|
|
if ((state.context = eglCreateContext(state.display, state.config, EGL_NO_CONTEXT, contextAttributes)) == EGL_NO_CONTEXT) {
|
2020-01-29 05:42:44 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLint surfaceAttributes[] = {
|
|
|
|
EGL_WIDTH, 16,
|
|
|
|
EGL_HEIGHT, 16,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
2020-08-19 03:09:06 +00:00
|
|
|
if ((state.surface = eglCreatePbufferSurface(state.display, state.config, surfaceAttributes)) == EGL_NO_SURFACE) {
|
2020-02-05 00:48:03 +00:00
|
|
|
eglDestroyContext(state.display, state.context);
|
2020-01-29 05:42:44 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eglMakeCurrent(state.display, state.surface, state.surface, state.context) == EGL_FALSE) {
|
2020-02-05 00:48:03 +00:00
|
|
|
eglDestroySurface(state.display, state.surface);
|
|
|
|
eglDestroyContext(state.display, state.context);
|
2020-01-29 05:42:44 +00:00
|
|
|
}
|
2020-07-02 16:43:27 +00:00
|
|
|
|
2018-11-16 12:44:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-29 22:27:49 +00:00
|
|
|
bool lovrPlatformHasWindow() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:44:20 +00:00
|
|
|
void lovrPlatformGetWindowSize(int* width, int* height) {
|
2019-05-26 15:17:49 +00:00
|
|
|
if (width) *width = 0;
|
|
|
|
if (height) *height = 0;
|
2018-11-16 12:44:20 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 22:27:49 +00:00
|
|
|
void lovrPlatformGetFramebufferSize(int* width, int* height) {
|
|
|
|
*width = 0;
|
|
|
|
*height = 0;
|
|
|
|
}
|
|
|
|
|
2020-08-29 22:59:26 +00:00
|
|
|
void lovrPlatformSetSwapInterval(int interval) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:44:20 +00:00
|
|
|
void lovrPlatformSwapBuffers() {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
2019-12-10 21:15:12 +00:00
|
|
|
void* lovrPlatformGetProcAddress(const char* function) {
|
2019-12-18 01:20:09 +00:00
|
|
|
return (void*) eglGetProcAddress(function);
|
2019-12-10 21:15:12 +00:00
|
|
|
}
|
|
|
|
|
2020-06-10 00:00:33 +00:00
|
|
|
void lovrPlatformOnQuitRequest(quitCallback callback) {
|
|
|
|
state.onQuit = callback;
|
2018-11-16 12:44:20 +00:00
|
|
|
}
|
|
|
|
|
2020-01-28 05:10:27 +00:00
|
|
|
void lovrPlatformOnWindowFocus(windowFocusCallback callback) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:44:20 +00:00
|
|
|
void lovrPlatformOnWindowResize(windowResizeCallback callback) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrPlatformOnMouseButton(mouseButtonCallback callback) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
2019-06-06 03:22:51 +00:00
|
|
|
void lovrPlatformOnKeyboardEvent(keyboardCallback callback) {
|
2020-08-07 01:42:12 +00:00
|
|
|
state.onKeyboardEvent = callback;
|
2019-06-06 03:22:51 +00:00
|
|
|
}
|
|
|
|
|
2020-08-08 18:36:09 +00:00
|
|
|
void lovrPlatformOnTextEvent(textCallback callback) {
|
|
|
|
state.onTextEvent = callback;
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:44:20 +00:00
|
|
|
void lovrPlatformGetMousePosition(double* x, double* y) {
|
|
|
|
*x = *y = 0.;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrPlatformSetMouseMode(MouseMode mode) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
|
|
|
bool lovrPlatformIsMouseDown(MouseButton button) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool lovrPlatformIsKeyDown(KeyCode key) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-06-10 00:00:33 +00:00
|
|
|
|
2020-06-25 19:28:40 +00:00
|
|
|
// Private, must be declared manually to use
|
|
|
|
|
2020-06-10 00:00:33 +00:00
|
|
|
struct ANativeActivity* lovrPlatformGetActivity() {
|
|
|
|
return state.app->activity;
|
|
|
|
}
|
|
|
|
|
2020-07-04 02:17:39 +00:00
|
|
|
int lovrPlatformGetActivityState() {
|
|
|
|
return state.app->activityState;
|
|
|
|
}
|
|
|
|
|
2020-06-10 00:00:33 +00:00
|
|
|
ANativeWindow* lovrPlatformGetNativeWindow() {
|
2020-07-04 02:17:39 +00:00
|
|
|
return state.app->window;
|
2020-06-10 00:00:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
JNIEnv* lovrPlatformGetJNI() {
|
|
|
|
return state.jni;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLDisplay lovrPlatformGetEGLDisplay() {
|
|
|
|
return state.display;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLContext lovrPlatformGetEGLContext() {
|
|
|
|
return state.context;
|
|
|
|
}
|
|
|
|
|
2020-08-19 03:09:06 +00:00
|
|
|
EGLContext lovrPlatformGetEGLConfig() {
|
|
|
|
return state.config;
|
|
|
|
}
|
|
|
|
|
2020-06-10 00:00:33 +00:00
|
|
|
EGLSurface lovrPlatformGetEGLSurface() {
|
|
|
|
return state.surface;
|
|
|
|
}
|