201 lines
5.9 KiB
C
201 lines
5.9 KiB
C
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build android
|
|
|
|
#include <android/log.h>
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "_cgo_export.h"
|
|
|
|
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__)
|
|
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__)
|
|
|
|
static jclass current_class;
|
|
|
|
static jclass find_class(JNIEnv *env, const char *class_name) {
|
|
jclass clazz = (*env)->FindClass(env, class_name);
|
|
if (clazz == NULL) {
|
|
(*env)->ExceptionClear(env);
|
|
LOG_FATAL("cannot find %s", class_name);
|
|
return NULL;
|
|
}
|
|
return clazz;
|
|
}
|
|
|
|
static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
|
|
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
|
|
if (m == 0) {
|
|
(*env)->ExceptionClear(env);
|
|
LOG_FATAL("cannot find method %s %s", name, sig);
|
|
return 0;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
static jmethodID find_static_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
|
|
jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
|
|
if (m == 0) {
|
|
(*env)->ExceptionClear(env);
|
|
LOG_FATAL("cannot find method %s %s", name, sig);
|
|
return 0;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
static jmethodID key_rune_method;
|
|
|
|
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
JNIEnv* env;
|
|
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
|
return -1;
|
|
}
|
|
|
|
return JNI_VERSION_1_6;
|
|
}
|
|
|
|
static int main_running = 0;
|
|
|
|
// Entry point from our subclassed NativeActivity.
|
|
//
|
|
// By here, the Go runtime has been initialized (as we are running in
|
|
// -buildmode=c-shared) but the first time it is called, Go's main.main
|
|
// hasn't been called yet.
|
|
//
|
|
// The Activity may be created and destroyed multiple times throughout
|
|
// the life of a single process. Each time, onCreate is called.
|
|
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
|
|
if (!main_running) {
|
|
JNIEnv* env = activity->env;
|
|
|
|
// Note that activity->clazz is mis-named.
|
|
current_class = (*env)->GetObjectClass(env, activity->clazz);
|
|
current_class = (*env)->NewGlobalRef(env, current_class);
|
|
key_rune_method = find_static_method(env, current_class, "getRune", "(III)I");
|
|
|
|
setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
|
|
|
|
// Set TMPDIR.
|
|
jmethodID gettmpdir = find_method(env, current_class, "getTmpdir", "()Ljava/lang/String;");
|
|
jstring jpath = (jstring)(*env)->CallObjectMethod(env, activity->clazz, gettmpdir, NULL);
|
|
const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
|
|
if (setenv("TMPDIR", tmpdir, 1) != 0) {
|
|
LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
|
|
}
|
|
(*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
|
|
|
|
// Call the Go main.main.
|
|
uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
|
|
if (!mainPC) {
|
|
LOG_FATAL("missing main.main");
|
|
}
|
|
callMain(mainPC);
|
|
main_running = 1;
|
|
}
|
|
|
|
// These functions match the methods on Activity, described at
|
|
// http://developer.android.com/reference/android/app/Activity.html
|
|
//
|
|
// Note that onNativeWindowResized is not called on resize. Avoid it.
|
|
// https://code.google.com/p/android/issues/detail?id=180645
|
|
activity->callbacks->onStart = onStart;
|
|
activity->callbacks->onResume = onResume;
|
|
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
|
|
activity->callbacks->onPause = onPause;
|
|
activity->callbacks->onStop = onStop;
|
|
activity->callbacks->onDestroy = onDestroy;
|
|
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
|
|
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
|
|
activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
|
|
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
|
|
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
|
|
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
|
|
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
|
|
activity->callbacks->onLowMemory = onLowMemory;
|
|
|
|
onCreate(activity);
|
|
}
|
|
|
|
// TODO(crawshaw): Test configuration on more devices.
|
|
static const EGLint RGB_888[] = {
|
|
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_DEPTH_SIZE, 16,
|
|
EGL_CONFIG_CAVEAT, EGL_NONE,
|
|
EGL_NONE
|
|
};
|
|
|
|
EGLDisplay display = NULL;
|
|
EGLSurface surface = NULL;
|
|
|
|
static char* initEGLDisplay() {
|
|
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
if (!eglInitialize(display, 0, 0)) {
|
|
return "EGL initialize failed";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char* createEGLSurface(ANativeWindow* window) {
|
|
char* err;
|
|
EGLint numConfigs, format;
|
|
EGLConfig config;
|
|
EGLContext context;
|
|
|
|
if (display == 0) {
|
|
if ((err = initEGLDisplay()) != NULL) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
|
|
return "EGL choose RGB_888 config failed";
|
|
}
|
|
if (numConfigs <= 0) {
|
|
return "EGL no config found";
|
|
}
|
|
|
|
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
|
|
if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
|
|
return "EGL set buffers geometry failed";
|
|
}
|
|
|
|
surface = eglCreateWindowSurface(display, config, window, NULL);
|
|
if (surface == EGL_NO_SURFACE) {
|
|
return "EGL create surface failed";
|
|
}
|
|
|
|
const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
|
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
|
|
|
|
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
|
|
return "eglMakeCurrent failed";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char* destroyEGLSurface() {
|
|
if (!eglDestroySurface(display, surface)) {
|
|
return "EGL destroy surface failed";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int32_t getKeyRune(JNIEnv* env, AInputEvent* e) {
|
|
return (int32_t)(*env)->CallStaticIntMethod(
|
|
env,
|
|
current_class,
|
|
key_rune_method,
|
|
AInputEvent_getDeviceId(e),
|
|
AKeyEvent_getKeyCode(e),
|
|
AKeyEvent_getMetaState(e)
|
|
);
|
|
}
|