github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/app/android.c (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build android 6 7 #include <android/log.h> 8 #include <dlfcn.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <stdint.h> 12 #include <string.h> 13 #include "_cgo_export.h" 14 15 #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__) 16 #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__) 17 18 static jclass find_class(JNIEnv *env, const char *class_name) { 19 jclass clazz = (*env)->FindClass(env, class_name); 20 if (clazz == NULL) { 21 (*env)->ExceptionClear(env); 22 LOG_FATAL("cannot find %s", class_name); 23 return NULL; 24 } 25 return clazz; 26 } 27 28 static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) { 29 jmethodID m = (*env)->GetMethodID(env, clazz, name, sig); 30 if (m == 0) { 31 (*env)->ExceptionClear(env); 32 LOG_FATAL("cannot find method %s %s", name, sig); 33 return 0; 34 } 35 return m; 36 } 37 38 jmethodID key_rune_method; 39 40 jint JNI_OnLoad(JavaVM* vm, void* reserved) { 41 JNIEnv* env; 42 if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) { 43 return -1; 44 } 45 46 // Load classes here, which uses the correct ClassLoader. 47 current_ctx_clazz = find_class(env, "org/golang/app/GoNativeActivity"); 48 current_ctx_clazz = (jclass)(*env)->NewGlobalRef(env, current_ctx_clazz); 49 key_rune_method = find_method(env, current_ctx_clazz, "getRune", "(III)I"); 50 51 return JNI_VERSION_1_6; 52 } 53 54 int main_running = 0; 55 56 // Entry point from our subclassed NativeActivity. 57 // 58 // By here, the Go runtime has been initialized (as we are running in 59 // -buildmode=c-shared) but the first time it is called, Go's main.main 60 // hasn't been called yet. 61 // 62 // The Activity may be created and destroyed multiple times throughout 63 // the life of a single process. Each time, onCreate is called. 64 void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) { 65 if (!main_running) { 66 JNIEnv* env = activity->env; 67 68 // Note that activity->clazz is mis-named. 69 current_vm = activity->vm; 70 current_ctx = activity->clazz; 71 72 setCurrentContext(current_vm, (*env)->NewGlobalRef(env, current_ctx)); 73 74 // Set TMPDIR. 75 jmethodID gettmpdir = find_method(env, current_ctx_clazz, "getTmpdir", "()Ljava/lang/String;"); 76 jstring jpath = (jstring)(*env)->CallObjectMethod(env, current_ctx, gettmpdir, NULL); 77 const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL); 78 if (setenv("TMPDIR", tmpdir, 1) != 0) { 79 LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno); 80 } 81 (*env)->ReleaseStringUTFChars(env, jpath, tmpdir); 82 83 // Call the Go main.main. 84 uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main"); 85 if (!mainPC) { 86 LOG_FATAL("missing main.main"); 87 } 88 callMain(mainPC); 89 main_running = 1; 90 } 91 92 // These functions match the methods on Activity, described at 93 // http://developer.android.com/reference/android/app/Activity.html 94 // 95 // Note that onNativeWindowResized is not called on resize. Avoid it. 96 // https://code.google.com/p/android/issues/detail?id=180645 97 activity->callbacks->onStart = onStart; 98 activity->callbacks->onResume = onResume; 99 activity->callbacks->onSaveInstanceState = onSaveInstanceState; 100 activity->callbacks->onPause = onPause; 101 activity->callbacks->onStop = onStop; 102 activity->callbacks->onDestroy = onDestroy; 103 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; 104 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; 105 activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded; 106 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; 107 activity->callbacks->onInputQueueCreated = onInputQueueCreated; 108 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; 109 activity->callbacks->onConfigurationChanged = onConfigurationChanged; 110 activity->callbacks->onLowMemory = onLowMemory; 111 112 onCreate(activity); 113 } 114 115 // TODO(crawshaw): Test configuration on more devices. 116 const EGLint RGB_888[] = { 117 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 118 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 119 EGL_BLUE_SIZE, 8, 120 EGL_GREEN_SIZE, 8, 121 EGL_RED_SIZE, 8, 122 EGL_DEPTH_SIZE, 16, 123 EGL_CONFIG_CAVEAT, EGL_NONE, 124 EGL_NONE 125 }; 126 127 EGLDisplay display = NULL; 128 EGLSurface surface = NULL; 129 130 char* initEGLDisplay() { 131 display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 132 if (!eglInitialize(display, 0, 0)) { 133 return "EGL initialize failed"; 134 } 135 return NULL; 136 } 137 138 char* createEGLSurface(ANativeWindow* window) { 139 char* err; 140 EGLint numConfigs, format; 141 EGLConfig config; 142 EGLContext context; 143 144 if (display == 0) { 145 if ((err = initEGLDisplay()) != NULL) { 146 return err; 147 } 148 } 149 150 if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) { 151 return "EGL choose RGB_888 config failed"; 152 } 153 if (numConfigs <= 0) { 154 return "EGL no config found"; 155 } 156 157 eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); 158 if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) { 159 return "EGL set buffers geometry failed"; 160 } 161 162 surface = eglCreateWindowSurface(display, config, window, NULL); 163 if (surface == EGL_NO_SURFACE) { 164 return "EGL create surface failed"; 165 } 166 167 const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; 168 context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); 169 170 if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { 171 return "eglMakeCurrent failed"; 172 } 173 return NULL; 174 } 175 176 char* destroyEGLSurface() { 177 if (!eglDestroySurface(display, surface)) { 178 return "EGL destroy surface failed"; 179 } 180 return NULL; 181 } 182 183 char* attachJNI(JNIEnv** envp) { 184 if (current_vm == NULL) { 185 return "current_vm not set"; 186 } 187 188 switch ((*current_vm)->GetEnv(current_vm, (void**)envp, JNI_VERSION_1_6)) { 189 case JNI_OK: 190 return NULL; 191 case JNI_EDETACHED: 192 // AttachCurrentThread is typically paired with a call to 193 // DetachCurrentThread, however attachJNI is called for 194 // the duration of the main function, which exits when the 195 // process exits. We let Unix take care of thread cleanup. 196 if ((*current_vm)->AttachCurrentThread(current_vm, envp, 0) != 0) { 197 return "cannot attach JVM"; 198 } 199 return NULL; 200 case JNI_EVERSION: 201 return "bad JNI version"; 202 default: 203 return "unknown GetEnv error"; 204 } 205 } 206 207 int32_t getKeyRune(JNIEnv* env, AInputEvent* e) { 208 return (int32_t)(*env)->CallIntMethod( 209 env, 210 current_ctx, 211 key_rune_method, 212 AInputEvent_getDeviceId(e), 213 AKeyEvent_getKeyCode(e), 214 AKeyEvent_getMetaState(e) 215 ); 216 }