android.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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. // +build android
  5. #include <android/log.h>
  6. #include <dlfcn.h>
  7. #include <errno.h>
  8. #include <fcntl.h>
  9. #include <stdint.h>
  10. #include <stdbool.h>
  11. #include <string.h>
  12. #include "_cgo_export.h"
  13. #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Fyne", __VA_ARGS__)
  14. #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Fyne", __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. static jmethodID show_keyboard_method;
  45. static jmethodID hide_keyboard_method;
  46. static jmethodID show_file_open_method;
  47. static jmethodID show_file_save_method;
  48. jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  49. JNIEnv* env;
  50. if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
  51. return -1;
  52. }
  53. return JNI_VERSION_1_6;
  54. }
  55. static int main_running = 0;
  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. // Note that activity->clazz is mis-named.
  68. current_class = (*env)->GetObjectClass(env, activity->clazz);
  69. current_class = (*env)->NewGlobalRef(env, current_class);
  70. key_rune_method = find_static_method(env, current_class, "getRune", "(III)I");
  71. show_keyboard_method = find_static_method(env, current_class, "showKeyboard", "(I)V");
  72. hide_keyboard_method = find_static_method(env, current_class, "hideKeyboard", "()V");
  73. show_file_open_method = find_static_method(env, current_class, "showFileOpen", "(Ljava/lang/String;)V");
  74. show_file_save_method = find_static_method(env, current_class, "showFileSave", "(Ljava/lang/String;Ljava/lang/String;)V");
  75. setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
  76. // Set FILESDIR
  77. if (setenv("FILESDIR", activity->internalDataPath, 1) != 0) {
  78. LOG_INFO("setenv(\"FILESDIR\", \"%s\", 1) failed: %d", activity->internalDataPath, errno);
  79. }
  80. // Set TMPDIR.
  81. jmethodID gettmpdir = find_method(env, current_class, "getTmpdir", "()Ljava/lang/String;");
  82. jstring jpath = (jstring)(*env)->CallObjectMethod(env, activity->clazz, gettmpdir, NULL);
  83. const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
  84. if (setenv("TMPDIR", tmpdir, 1) != 0) {
  85. LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
  86. }
  87. (*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
  88. // Call the Go main.main.
  89. uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
  90. if (!mainPC) {
  91. LOG_FATAL("missing main.main");
  92. }
  93. callMain(mainPC);
  94. main_running = 1;
  95. }
  96. // These functions match the methods on Activity, described at
  97. // http://developer.android.com/reference/android/app/Activity.html
  98. //
  99. // Note that onNativeWindowResized is not called on resize. Avoid it.
  100. // https://code.google.com/p/android/issues/detail?id=180645
  101. activity->callbacks->onStart = onStart;
  102. activity->callbacks->onResume = onResume;
  103. activity->callbacks->onSaveInstanceState = onSaveInstanceState;
  104. activity->callbacks->onPause = onPause;
  105. activity->callbacks->onStop = onStop;
  106. activity->callbacks->onDestroy = onDestroy;
  107. activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
  108. activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
  109. activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
  110. activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
  111. activity->callbacks->onInputQueueCreated = onInputQueueCreated;
  112. activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
  113. activity->callbacks->onConfigurationChanged = onConfigurationChanged;
  114. activity->callbacks->onLowMemory = onLowMemory;
  115. onCreate(activity);
  116. }
  117. // TODO(crawshaw): Test configuration on more devices.
  118. static const EGLint RGB_888[] = {
  119. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  120. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  121. EGL_BLUE_SIZE, 8,
  122. EGL_GREEN_SIZE, 8,
  123. EGL_RED_SIZE, 8,
  124. EGL_DEPTH_SIZE, 16,
  125. EGL_CONFIG_CAVEAT, EGL_NONE,
  126. EGL_NONE
  127. };
  128. EGLDisplay display = NULL;
  129. EGLSurface surface = NULL;
  130. EGLContext context = NULL;
  131. static char* initEGLDisplay() {
  132. display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  133. if (!eglInitialize(display, 0, 0)) {
  134. return "EGL initialize failed";
  135. }
  136. return NULL;
  137. }
  138. char* createEGLSurface(ANativeWindow* window) {
  139. char* err;
  140. EGLint numConfigs, format;
  141. EGLConfig config;
  142. if (display == 0) {
  143. if ((err = initEGLDisplay()) != NULL) {
  144. return err;
  145. }
  146. }
  147. if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
  148. return "EGL choose RGB_888 config failed";
  149. }
  150. if (numConfigs <= 0) {
  151. return "EGL no config found";
  152. }
  153. eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
  154. if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
  155. return "EGL set buffers geometry failed";
  156. }
  157. surface = eglCreateWindowSurface(display, config, window, NULL);
  158. if (surface == EGL_NO_SURFACE) {
  159. return "EGL create surface failed";
  160. }
  161. if (context == NULL) {
  162. const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
  163. context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
  164. }
  165. if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
  166. return "eglMakeCurrent failed";
  167. }
  168. return NULL;
  169. }
  170. char* destroyEGLSurface() {
  171. if (!eglDestroySurface(display, surface)) {
  172. return "EGL destroy surface failed";
  173. }
  174. return NULL;
  175. }
  176. int32_t getKeyRune(JNIEnv* env, AInputEvent* e) {
  177. return (int32_t)(*env)->CallStaticIntMethod(
  178. env,
  179. current_class,
  180. key_rune_method,
  181. AInputEvent_getDeviceId(e),
  182. AKeyEvent_getKeyCode(e),
  183. AKeyEvent_getMetaState(e)
  184. );
  185. }
  186. void showKeyboard(JNIEnv* env, int keyboardType) {
  187. (*env)->CallStaticVoidMethod(
  188. env,
  189. current_class,
  190. show_keyboard_method,
  191. keyboardType
  192. );
  193. }
  194. void hideKeyboard(JNIEnv* env) {
  195. (*env)->CallStaticVoidMethod(
  196. env,
  197. current_class,
  198. hide_keyboard_method
  199. );
  200. }
  201. void showFileOpen(JNIEnv* env, char* mimes) {
  202. jstring mimesJString = (*env)->NewStringUTF(env, mimes);
  203. (*env)->CallStaticVoidMethod(
  204. env,
  205. current_class,
  206. show_file_open_method,
  207. mimesJString
  208. );
  209. }
  210. void showFileSave(JNIEnv* env, char* mimes, char* filename) {
  211. jstring mimesJString = (*env)->NewStringUTF(env, mimes);
  212. jstring filenameJString = (*env)->NewStringUTF(env, filename);
  213. (*env)->CallStaticVoidMethod(
  214. env,
  215. current_class,
  216. show_file_save_method,
  217. mimesJString,
  218. filenameJString
  219. );
  220. }
  221. void Java_org_golang_app_GoNativeActivity_filePickerReturned(JNIEnv *env, jclass clazz, jstring str) {
  222. const char* cstr = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
  223. filePickerReturned((char*)cstr);
  224. }
  225. void Java_org_golang_app_GoNativeActivity_insetsChanged(JNIEnv *env, jclass clazz, int top, int bottom, int left, int right) {
  226. insetsChanged(top, bottom, left, right);
  227. }
  228. void Java_org_golang_app_GoNativeActivity_keyboardTyped(JNIEnv *env, jclass clazz, jstring str) {
  229. const char* cstr = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
  230. keyboardTyped((char*)cstr);
  231. }
  232. void Java_org_golang_app_GoNativeActivity_keyboardDelete(JNIEnv *env, jclass clazz) {
  233. keyboardDelete();
  234. }
  235. void Java_org_golang_app_GoNativeActivity_setDarkMode(JNIEnv *env, jclass clazz, jboolean dark) {
  236. setDarkMode((bool)dark);
  237. }