android.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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. //go:build android
  5. // +build android
  6. #include <android/log.h>
  7. #include <dlfcn.h>
  8. #include <errno.h>
  9. #include <fcntl.h>
  10. #include <stdint.h>
  11. #include <string.h>
  12. #include "_cgo_export.h"
  13. #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__)
  14. #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__)
  15. static jclass current_class;
  16. static jclass find_class(JNIEnv *env, const char *class_name) {
  17. jclass clazz = (*env)->FindClass(env, class_name);
  18. if (clazz == NULL) {
  19. (*env)->ExceptionClear(env);
  20. LOG_FATAL("cannot find %s", class_name);
  21. return NULL;
  22. }
  23. return clazz;
  24. }
  25. static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
  26. jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
  27. if (m == 0) {
  28. (*env)->ExceptionClear(env);
  29. LOG_FATAL("cannot find method %s %s", name, sig);
  30. return 0;
  31. }
  32. return m;
  33. }
  34. static jmethodID find_static_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
  35. jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
  36. if (m == 0) {
  37. (*env)->ExceptionClear(env);
  38. LOG_FATAL("cannot find method %s %s", name, sig);
  39. return 0;
  40. }
  41. return m;
  42. }
  43. static jmethodID key_rune_method;
  44. jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  45. JNIEnv* env;
  46. if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
  47. return -1;
  48. }
  49. return JNI_VERSION_1_6;
  50. }
  51. static int main_running = 0;
  52. // Entry point from our subclassed NativeActivity.
  53. //
  54. // By here, the Go runtime has been initialized (as we are running in
  55. // -buildmode=c-shared) but the first time it is called, Go's main.main
  56. // hasn't been called yet.
  57. //
  58. // The Activity may be created and destroyed multiple times throughout
  59. // the life of a single process. Each time, onCreate is called.
  60. void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
  61. if (!main_running) {
  62. JNIEnv* env = activity->env;
  63. // Note that activity->clazz is mis-named.
  64. current_class = (*env)->GetObjectClass(env, activity->clazz);
  65. current_class = (*env)->NewGlobalRef(env, current_class);
  66. key_rune_method = find_static_method(env, current_class, "getRune", "(III)I");
  67. setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
  68. // Set TMPDIR.
  69. jmethodID gettmpdir = find_method(env, current_class, "getTmpdir", "()Ljava/lang/String;");
  70. jstring jpath = (jstring)(*env)->CallObjectMethod(env, activity->clazz, gettmpdir, NULL);
  71. const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
  72. if (setenv("TMPDIR", tmpdir, 1) != 0) {
  73. LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
  74. }
  75. (*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
  76. // Call the Go main.main.
  77. uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
  78. if (!mainPC) {
  79. LOG_FATAL("missing main.main");
  80. }
  81. callMain(mainPC);
  82. main_running = 1;
  83. }
  84. // These functions match the methods on Activity, described at
  85. // http://developer.android.com/reference/android/app/Activity.html
  86. //
  87. // Note that onNativeWindowResized is not called on resize. Avoid it.
  88. // https://code.google.com/p/android/issues/detail?id=180645
  89. activity->callbacks->onStart = onStart;
  90. activity->callbacks->onResume = onResume;
  91. activity->callbacks->onSaveInstanceState = onSaveInstanceState;
  92. activity->callbacks->onPause = onPause;
  93. activity->callbacks->onStop = onStop;
  94. activity->callbacks->onDestroy = onDestroy;
  95. activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
  96. activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
  97. activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
  98. activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
  99. activity->callbacks->onInputQueueCreated = onInputQueueCreated;
  100. activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
  101. activity->callbacks->onConfigurationChanged = onConfigurationChanged;
  102. activity->callbacks->onLowMemory = onLowMemory;
  103. onCreate(activity);
  104. }
  105. // TODO(crawshaw): Test configuration on more devices.
  106. static const EGLint RGB_888[] = {
  107. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  108. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  109. EGL_BLUE_SIZE, 8,
  110. EGL_GREEN_SIZE, 8,
  111. EGL_RED_SIZE, 8,
  112. EGL_DEPTH_SIZE, 16,
  113. EGL_CONFIG_CAVEAT, EGL_NONE,
  114. EGL_NONE
  115. };
  116. EGLDisplay display = NULL;
  117. EGLSurface surface = NULL;
  118. static char* initEGLDisplay() {
  119. display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  120. if (!eglInitialize(display, 0, 0)) {
  121. return "EGL initialize failed";
  122. }
  123. return NULL;
  124. }
  125. char* createEGLSurface(ANativeWindow* window) {
  126. char* err;
  127. EGLint numConfigs, format;
  128. EGLConfig config;
  129. EGLContext context;
  130. if (display == 0) {
  131. if ((err = initEGLDisplay()) != NULL) {
  132. return err;
  133. }
  134. }
  135. if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
  136. return "EGL choose RGB_888 config failed";
  137. }
  138. if (numConfigs <= 0) {
  139. return "EGL no config found";
  140. }
  141. eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
  142. if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
  143. return "EGL set buffers geometry failed";
  144. }
  145. surface = eglCreateWindowSurface(display, config, window, NULL);
  146. if (surface == EGL_NO_SURFACE) {
  147. return "EGL create surface failed";
  148. }
  149. const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
  150. context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
  151. if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
  152. return "eglMakeCurrent failed";
  153. }
  154. return NULL;
  155. }
  156. char* destroyEGLSurface() {
  157. if (!eglDestroySurface(display, surface)) {
  158. return "EGL destroy surface failed";
  159. }
  160. return NULL;
  161. }
  162. int32_t getKeyRune(JNIEnv* env, AInputEvent* e) {
  163. return (int32_t)(*env)->CallStaticIntMethod(
  164. env,
  165. current_class,
  166. key_rune_method,
  167. AInputEvent_getDeviceId(e),
  168. AKeyEvent_getKeyCode(e),
  169. AKeyEvent_getMetaState(e)
  170. );
  171. }