// 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 #include #include #include #include #include #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 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; } jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) { return -1; } // Load classes here, which uses the correct ClassLoader. current_ctx_clazz = find_class(env, "org/golang/app/GoNativeActivity"); current_ctx_clazz = (jclass)(*env)->NewGlobalRef(env, current_ctx_clazz); return JNI_VERSION_1_6; } 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. JavaVM* current_vm = activity->vm; jobject current_ctx = activity->clazz; setCurrentContext(current_vm, (*env)->NewGlobalRef(env, current_ctx)); // Set TMPDIR. jmethodID gettmpdir = find_method(env, current_ctx_clazz, "getTmpdir", "()Ljava/lang/String;"); jstring jpath = (jstring)(*env)->CallObjectMethod(env, current_ctx, 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. 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; 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; }