355 lines
9.7 KiB
C
355 lines
9.7 KiB
C
|
#pragma clang diagnostic push
|
||
|
#pragma ide diagnostic ignored "readability-isolate-declaration"
|
||
|
#pragma ide diagnostic ignored "cppcoreguidelines-avoid-magic-numbers"
|
||
|
#include <unistd.h>
|
||
|
#include <jni.h>
|
||
|
#include <android/native_window_jni.h>
|
||
|
#include <pthread.h>
|
||
|
#include <poll.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <wayland-server.h>
|
||
|
#include "backend.h"
|
||
|
#include "log.h"
|
||
|
|
||
|
static void checkEGLError(int line) {
|
||
|
char* error = NULL;
|
||
|
switch(eglGetError()) {
|
||
|
#define E(code, desc) case code: error = desc; break;
|
||
|
case EGL_SUCCESS: return; // "No error"
|
||
|
E(EGL_NOT_INITIALIZED, "EGL not initialized or failed to initialize");
|
||
|
E(EGL_BAD_ACCESS, "Resource inaccessible");
|
||
|
E(EGL_BAD_ALLOC, "Cannot allocate resources");
|
||
|
E(EGL_BAD_ATTRIBUTE, "Unrecognized attribute or attribute value");
|
||
|
E(EGL_BAD_CONTEXT, "Invalid EGL context");
|
||
|
E(EGL_BAD_CONFIG, "Invalid EGL frame buffer configuration");
|
||
|
E(EGL_BAD_CURRENT_SURFACE, "Current surface is no longer valid");
|
||
|
E(EGL_BAD_DISPLAY, "Invalid EGL display");
|
||
|
E(EGL_BAD_SURFACE, "Invalid surface");
|
||
|
E(EGL_BAD_MATCH, "Inconsistent arguments");
|
||
|
E(EGL_BAD_PARAMETER, "Invalid argument");
|
||
|
E(EGL_BAD_NATIVE_PIXMAP, "Invalid native pixmap");
|
||
|
E(EGL_BAD_NATIVE_WINDOW, "Invalid native window");
|
||
|
E(EGL_CONTEXT_LOST, "Context lost");
|
||
|
#undef E
|
||
|
default: error = "Unknown error";
|
||
|
}
|
||
|
LOGE("EGL: %s after %d", error, line);
|
||
|
}
|
||
|
#define checkEGLError() checkEGLError(__LINE__)
|
||
|
|
||
|
enum {
|
||
|
ACTION_KEY = 1,
|
||
|
ACTION_POINTER = 2,
|
||
|
ACTION_WIN_CHANGE = 3,
|
||
|
#ifdef __ANDROID__
|
||
|
ACTION_JNI_WIN_CHANGE = 4,
|
||
|
#endif
|
||
|
};
|
||
|
static const EGLint ctxattr[] = {
|
||
|
EGL_CONTEXT_CLIENT_VERSION, 2, // Request opengl ES2.0
|
||
|
EGL_NONE
|
||
|
};
|
||
|
|
||
|
static const EGLint sfcattr[] = {
|
||
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||
|
EGL_BLUE_SIZE, 8,
|
||
|
EGL_GREEN_SIZE, 8,
|
||
|
EGL_RED_SIZE, 8,
|
||
|
EGL_NONE
|
||
|
};
|
||
|
|
||
|
typedef struct {
|
||
|
uint8_t type;
|
||
|
union {
|
||
|
struct {
|
||
|
uint8_t state;
|
||
|
uint16_t key;
|
||
|
} key;
|
||
|
struct {
|
||
|
uint16_t state, button;
|
||
|
uint32_t x, y;
|
||
|
} pointer;
|
||
|
struct {
|
||
|
EGLDisplay dpy;
|
||
|
EGLNativeWindowType win;
|
||
|
uint32_t width, height;
|
||
|
} window_change;
|
||
|
#ifdef __ANDROID__
|
||
|
struct {
|
||
|
JavaVM* jvm;
|
||
|
jobject surface;
|
||
|
uint32_t width, height;
|
||
|
} jni_window_change;
|
||
|
#endif
|
||
|
};
|
||
|
} lorie_event;
|
||
|
|
||
|
struct backend_android {
|
||
|
EGLDisplay dpy;
|
||
|
EGLContext ctx;
|
||
|
EGLNativeDisplayType win;
|
||
|
EGLConfig config;
|
||
|
EGLSurface sfc;
|
||
|
|
||
|
struct callbacks callbacks;
|
||
|
|
||
|
int fds[2];
|
||
|
};
|
||
|
|
||
|
struct backend_android *backend = NULL;
|
||
|
|
||
|
void lorie_key_event(void __unused *b, uint8_t state, uint16_t key) {
|
||
|
if (!backend) return;
|
||
|
|
||
|
lorie_event e;
|
||
|
memset(&e, 0, sizeof(lorie_event));
|
||
|
|
||
|
e.type = ACTION_KEY;
|
||
|
e.key.state = state;
|
||
|
e.key.key = key;
|
||
|
|
||
|
write(backend->fds[0], &e, sizeof(lorie_event));
|
||
|
}
|
||
|
|
||
|
void lorie_pointer_event(void __unused *b, uint8_t state, uint16_t button, uint32_t x, uint32_t y) {
|
||
|
if (!backend) return;
|
||
|
|
||
|
lorie_event e;
|
||
|
memset(&e, 0, sizeof(lorie_event));
|
||
|
|
||
|
e.type = ACTION_POINTER;
|
||
|
e.pointer.state = state;
|
||
|
e.pointer.button = button;
|
||
|
e.pointer.x = x;
|
||
|
e.pointer.y = y;
|
||
|
|
||
|
write(backend->fds[0], &e, sizeof(lorie_event));
|
||
|
}
|
||
|
|
||
|
void lorie_window_change_event(void __unused *b, EGLDisplay dpy, EGLNativeWindowType win, uint32_t width, uint32_t height) {
|
||
|
if (!backend) return;
|
||
|
|
||
|
lorie_event e;
|
||
|
memset(&e, 0, sizeof(lorie_event));
|
||
|
|
||
|
e.type = ACTION_WIN_CHANGE;
|
||
|
e.window_change.dpy = dpy;
|
||
|
e.window_change.win = win;
|
||
|
e.window_change.width = width;
|
||
|
e.window_change.height = height;
|
||
|
|
||
|
write(backend->fds[0], &e, sizeof(lorie_event));
|
||
|
}
|
||
|
|
||
|
void lorie_jni_window_change_event(void __unused *b, JavaVM* jvm, jobject surface, uint32_t width, uint32_t height) {
|
||
|
if (!backend) return;
|
||
|
|
||
|
lorie_event e;
|
||
|
memset(&e, 0, sizeof(lorie_event));
|
||
|
|
||
|
e.type = ACTION_JNI_WIN_CHANGE;
|
||
|
e.jni_window_change.jvm = jvm;
|
||
|
e.jni_window_change.surface = surface;
|
||
|
e.jni_window_change.width = width;
|
||
|
e.jni_window_change.height = height;
|
||
|
|
||
|
write(backend->fds[0], &e, sizeof(lorie_event));
|
||
|
}
|
||
|
|
||
|
static int has_data(int socket) {
|
||
|
int count = 0;
|
||
|
ioctl(socket, FIONREAD, &count);
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
JNIEXPORT jlong JNICALL
|
||
|
Java_com_termux_wtermux_LorieService_createLorieThread(JNIEnv __unused *env, jobject __unused instance) {
|
||
|
pthread_t thread;
|
||
|
//void *server = NULL;
|
||
|
|
||
|
setenv("XDG_RUNTIME_DIR", "/data/data/com.termux/files/usr/tmp", 1);
|
||
|
pthread_create(&thread, NULL, (void *(*)(void *))lorie_start, NULL);
|
||
|
while(backend == NULL) { usleep(10); }
|
||
|
|
||
|
//return (jlong) compositor;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
JNIEXPORT void JNICALL
|
||
|
Java_com_termux_wtermux_LorieService_windowChanged(JNIEnv *env, jobject __unused instance, jlong __unused jcompositor, jobject jsurface, jint width, jint height) {
|
||
|
if (backend == NULL) return;
|
||
|
#if 1
|
||
|
EGLNativeWindowType win = ANativeWindow_fromSurface(env, jsurface);
|
||
|
if (win == NULL) {
|
||
|
LOGE("Surface is invalid");
|
||
|
lorie_window_change_event(backend, NULL, NULL, (uint32_t) width, (uint32_t) height);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
lorie_window_change_event(backend, NULL, win, (uint32_t) width,
|
||
|
(uint32_t) height);
|
||
|
#else
|
||
|
JavaVM* jvm;
|
||
|
(*env)->GetJavaVM(env, &jvm);
|
||
|
if (jvm == NULL) {
|
||
|
LOGE("Error getting jvm instance");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
jobject surface = (*env)->NewGlobalRef(env, jsurface);
|
||
|
if (surface == NULL) {
|
||
|
LOGE("Error creating global reference of Surface object");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
lorie_jni_window_change_event(backend, jvm, surface, width, height);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
JNIEXPORT void JNICALL
|
||
|
Java_com_termux_wtermux_LorieService_onTouch(JNIEnv __unused *env, jobject __unused instance, jlong __unused jcompositor, jint type, jint button, jint x, jint y) {
|
||
|
if (backend == NULL) return;
|
||
|
lorie_pointer_event(backend, (uint8_t) type, (uint16_t) button, (uint32_t) x, (uint32_t) y);
|
||
|
}
|
||
|
|
||
|
JNIEXPORT void JNICALL
|
||
|
Java_com_termux_wtermux_LorieService_onKey(JNIEnv __unused *env, jobject __unused instance, jlong __unused compositor,
|
||
|
jint type, jint key) {
|
||
|
|
||
|
lorie_key_event(backend, (uint8_t) type, (uint16_t) key);
|
||
|
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void backend_init (struct callbacks *callbacks) {
|
||
|
backend = calloc (1, sizeof(struct backend_android));
|
||
|
if (backend == NULL) {
|
||
|
LOGE("Can not allocate backend_android");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
backend->callbacks = *callbacks;
|
||
|
|
||
|
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, backend->fds)) {
|
||
|
|
||
|
}
|
||
|
|
||
|
// Setup EGL
|
||
|
EGLint num_configs_returned;
|
||
|
backend->dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY); checkEGLError();
|
||
|
eglInitialize (backend->dpy, NULL, NULL); checkEGLError();
|
||
|
eglBindAPI (EGL_OPENGL_ES_API);
|
||
|
|
||
|
eglChooseConfig (backend->dpy, sfcattr, &backend->config, 1, &num_configs_returned); checkEGLError();
|
||
|
backend->ctx = eglCreateContext (backend->dpy, backend->config, EGL_NO_CONTEXT, ctxattr); checkEGLError();
|
||
|
eglMakeCurrent(backend->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, backend->ctx); checkEGLError();
|
||
|
}
|
||
|
|
||
|
EGLDisplay __unused backend_get_egl_display (void) {
|
||
|
return backend->dpy;
|
||
|
}
|
||
|
|
||
|
void backend_swap_buffers (void) {
|
||
|
eglSwapBuffers(backend->dpy, backend->sfc); checkEGLError();
|
||
|
}
|
||
|
|
||
|
void backend_dispatch_nonblocking (void)
|
||
|
{
|
||
|
lorie_event ev;
|
||
|
|
||
|
while (has_data(backend->fds[1]) >= (int) sizeof(lorie_event)) {
|
||
|
read(backend->fds[1], &ev, sizeof(lorie_event));
|
||
|
switch (ev.type) {
|
||
|
case ACTION_KEY:
|
||
|
if (backend->callbacks.key)
|
||
|
backend->callbacks.key(ev.key.key, ev.key.state);
|
||
|
break;
|
||
|
case ACTION_POINTER:
|
||
|
if (backend->callbacks.mouse_motion)
|
||
|
backend->callbacks.mouse_motion(ev.pointer.x, ev.pointer.y);
|
||
|
|
||
|
if (ev.pointer.state != WL_POINTER_MOTION) {
|
||
|
if (backend->callbacks.mouse_button)
|
||
|
backend->callbacks.mouse_button(ev.pointer.button, ev.pointer.state);
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_WIN_CHANGE:
|
||
|
if (ev.window_change.win != backend->win) {
|
||
|
if (backend->sfc)
|
||
|
eglDestroySurface(backend->dpy, backend->sfc);
|
||
|
backend->sfc = eglCreateWindowSurface(backend->dpy, backend->config, ev.window_change.win, NULL); checkEGLError();
|
||
|
backend->win = ev.window_change.win;
|
||
|
}
|
||
|
checkEGLError();
|
||
|
eglMakeCurrent(backend->dpy, backend->sfc, backend->sfc, backend->ctx); checkEGLError();
|
||
|
|
||
|
if (backend->callbacks.resize)
|
||
|
backend->callbacks.resize(ev.window_change.width, ev.window_change.height);
|
||
|
break;
|
||
|
#if 0
|
||
|
#ifdef __ANDROID__
|
||
|
case ACTION_JNI_WIN_CHANGE:
|
||
|
{
|
||
|
JavaVM* jvm = ev.jni_window_change.jvm;
|
||
|
JNIEnv* env;
|
||
|
(*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);
|
||
|
if (!jvm || !env) {
|
||
|
weston_log("Failed getting JNIEnv\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
b->edpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||
|
b->ewin = ANativeWindow_fromSurface(env, ev.jni_window_change.surface);
|
||
|
if (b->ewin == NULL) {
|
||
|
weston_log("Error getting EGLNativeWindowType from Java Surface object\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
struct weston_mode mode = b->output->mode;
|
||
|
|
||
|
if (mode.width == ev.jni_window_change.width &&
|
||
|
mode.height == ev.jni_window_change.height)
|
||
|
break;
|
||
|
|
||
|
mode.width = ev.jni_window_change.width;
|
||
|
mode.height = ev.jni_window_change.height;
|
||
|
|
||
|
if (weston_output_mode_set_native(&b->output->base,
|
||
|
&mode, b->output->scale) < 0)
|
||
|
weston_log("Mode switch failed\n");
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
default:break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void backend_wait_for_events (int wayland_fd) {
|
||
|
if (!backend) return;
|
||
|
struct pollfd fds[2] = {{backend->fds[1], POLLIN}, {wayland_fd, POLLIN}};
|
||
|
poll (fds, 2, -1);
|
||
|
}
|
||
|
|
||
|
void __unused backend_get_keymap (int __unused *fd, int __unused *size) {
|
||
|
|
||
|
}
|
||
|
|
||
|
long backend_get_timestamp (void) {
|
||
|
struct timespec t;
|
||
|
clock_gettime (CLOCK_MONOTONIC, &t);
|
||
|
return t.tv_sec * 1000 + t.tv_nsec / 1000000;
|
||
|
}
|
||
|
|
||
|
void backend_get_dimensions(uint32_t *width, uint32_t *height) {
|
||
|
if (width) *width = 480;
|
||
|
if (height) *height = 800;
|
||
|
}
|
||
|
#pragma clang diagnostic pop
|