android.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. static jmethodID finish_method;
  49. jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  50. JNIEnv* env;
  51. if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
  52. return -1;
  53. }
  54. return JNI_VERSION_1_6;
  55. }
  56. static int main_running = 0;
  57. // ensure we refresh context on resume in case something has changed...
  58. void processOnResume(ANativeActivity *activity) {
  59. JNIEnv* env = activity->env;
  60. setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
  61. onResume(activity);
  62. }
  63. // Entry point from our subclassed NativeActivity.
  64. //
  65. // By here, the Go runtime has been initialized (as we are running in
  66. // -buildmode=c-shared) but the first time it is called, Go's main.main
  67. // hasn't been called yet.
  68. //
  69. // The Activity may be created and destroyed multiple times throughout
  70. // the life of a single process. Each time, onCreate is called.
  71. void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
  72. if (!main_running) {
  73. JNIEnv* env = activity->env;
  74. // Note that activity->clazz is mis-named.
  75. current_class = (*env)->GetObjectClass(env, activity->clazz);
  76. current_class = (*env)->NewGlobalRef(env, current_class);
  77. key_rune_method = find_static_method(env, current_class, "getRune", "(III)I");
  78. show_keyboard_method = find_static_method(env, current_class, "showKeyboard", "(I)V");
  79. hide_keyboard_method = find_static_method(env, current_class, "hideKeyboard", "()V");
  80. show_file_open_method = find_static_method(env, current_class, "showFileOpen", "(Ljava/lang/String;)V");
  81. show_file_save_method = find_static_method(env, current_class, "showFileSave", "(Ljava/lang/String;Ljava/lang/String;)V");
  82. finish_method = find_method(env, current_class, "finishActivity", "()V");
  83. setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
  84. // Set FILESDIR
  85. if (setenv("FILESDIR", activity->internalDataPath, 1) != 0) {
  86. LOG_INFO("setenv(\"FILESDIR\", \"%s\", 1) failed: %d", activity->internalDataPath, errno);
  87. }
  88. // Set TMPDIR.
  89. jmethodID gettmpdir = find_method(env, current_class, "getTmpdir", "()Ljava/lang/String;");
  90. jstring jpath = (jstring)(*env)->CallObjectMethod(env, activity->clazz, gettmpdir, NULL);
  91. const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
  92. if (setenv("TMPDIR", tmpdir, 1) != 0) {
  93. LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
  94. }
  95. (*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
  96. // Call the Go main.main.
  97. uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
  98. if (!mainPC) {
  99. LOG_FATAL("missing main.main");
  100. }
  101. callMain(mainPC);
  102. main_running = 1;
  103. }
  104. // These functions match the methods on Activity, described at
  105. // http://developer.android.com/reference/android/app/Activity.html
  106. //
  107. // Note that onNativeWindowResized is not called on resize. Avoid it.
  108. // https://code.google.com/p/android/issues/detail?id=180645
  109. activity->callbacks->onStart = onStart;
  110. activity->callbacks->onResume = processOnResume;
  111. activity->callbacks->onSaveInstanceState = onSaveInstanceState;
  112. activity->callbacks->onPause = onPause;
  113. activity->callbacks->onStop = onStop;
  114. activity->callbacks->onDestroy = onDestroy;
  115. activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
  116. activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
  117. activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
  118. activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
  119. activity->callbacks->onInputQueueCreated = onInputQueueCreated;
  120. activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
  121. activity->callbacks->onConfigurationChanged = onConfigurationChanged;
  122. activity->callbacks->onLowMemory = onLowMemory;
  123. onCreate(activity);
  124. }
  125. // TODO(crawshaw): Test configuration on more devices.
  126. static const EGLint RGB_888[] = {
  127. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  128. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  129. EGL_BLUE_SIZE, 8,
  130. EGL_GREEN_SIZE, 8,
  131. EGL_RED_SIZE, 8,
  132. EGL_DEPTH_SIZE, 16,
  133. EGL_CONFIG_CAVEAT, EGL_NONE,
  134. EGL_NONE
  135. };
  136. EGLDisplay display = NULL;
  137. EGLSurface surface = NULL;
  138. EGLContext context = NULL;
  139. static char* initEGLDisplay() {
  140. display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  141. if (!eglInitialize(display, 0, 0)) {
  142. return "EGL initialize failed";
  143. }
  144. return NULL;
  145. }
  146. char* createEGLSurface(ANativeWindow* window) {
  147. char* err;
  148. EGLint numConfigs, format;
  149. EGLConfig config;
  150. if (display == 0) {
  151. if ((err = initEGLDisplay()) != NULL) {
  152. return err;
  153. }
  154. }
  155. if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
  156. return "EGL choose RGB_888 config failed";
  157. }
  158. if (numConfigs <= 0) {
  159. return "EGL no config found";
  160. }
  161. eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
  162. if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
  163. return "EGL set buffers geometry failed";
  164. }
  165. surface = eglCreateWindowSurface(display, config, window, NULL);
  166. if (surface == EGL_NO_SURFACE) {
  167. return "EGL create surface failed";
  168. }
  169. if (context == NULL) {
  170. const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
  171. context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
  172. }
  173. if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
  174. return "eglMakeCurrent failed";
  175. }
  176. return NULL;
  177. }
  178. char* destroyEGLSurface() {
  179. if (!eglDestroySurface(display, surface)) {
  180. return "EGL destroy surface failed";
  181. }
  182. return NULL;
  183. }
  184. void finish(JNIEnv* env, jobject ctx) {
  185. (*env)->CallVoidMethod(
  186. env,
  187. ctx,
  188. finish_method);
  189. }
  190. int32_t getKeyRune(JNIEnv* env, AInputEvent* e) {
  191. return (int32_t)(*env)->CallStaticIntMethod(
  192. env,
  193. current_class,
  194. key_rune_method,
  195. AInputEvent_getDeviceId(e),
  196. AKeyEvent_getKeyCode(e),
  197. AKeyEvent_getMetaState(e)
  198. );
  199. }
  200. void showKeyboard(JNIEnv* env, int keyboardType) {
  201. (*env)->CallStaticVoidMethod(
  202. env,
  203. current_class,
  204. show_keyboard_method,
  205. keyboardType
  206. );
  207. }
  208. void hideKeyboard(JNIEnv* env) {
  209. (*env)->CallStaticVoidMethod(
  210. env,
  211. current_class,
  212. hide_keyboard_method
  213. );
  214. }
  215. void showFileOpen(JNIEnv* env, char* mimes) {
  216. jstring mimesJString = (*env)->NewStringUTF(env, mimes);
  217. (*env)->CallStaticVoidMethod(
  218. env,
  219. current_class,
  220. show_file_open_method,
  221. mimesJString
  222. );
  223. }
  224. void showFileSave(JNIEnv* env, char* mimes, char* filename) {
  225. jstring mimesJString = (*env)->NewStringUTF(env, mimes);
  226. jstring filenameJString = (*env)->NewStringUTF(env, filename);
  227. (*env)->CallStaticVoidMethod(
  228. env,
  229. current_class,
  230. show_file_save_method,
  231. mimesJString,
  232. filenameJString
  233. );
  234. }
  235. void Java_org_golang_app_GoNativeActivity_filePickerReturned(JNIEnv *env, jclass clazz, jstring str) {
  236. const char* cstr = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
  237. filePickerReturned((char*)cstr);
  238. }
  239. void Java_org_golang_app_GoNativeActivity_insetsChanged(JNIEnv *env, jclass clazz, int top, int bottom, int left, int right) {
  240. insetsChanged(top, bottom, left, right);
  241. }
  242. void Java_org_golang_app_GoNativeActivity_keyboardTyped(JNIEnv *env, jclass clazz, jstring str) {
  243. const char* cstr = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
  244. keyboardTyped((char*)cstr);
  245. }
  246. void Java_org_golang_app_GoNativeActivity_keyboardDelete(JNIEnv *env, jclass clazz) {
  247. keyboardDelete();
  248. }
  249. void Java_org_golang_app_GoNativeActivity_backPressed(JNIEnv *env, jclass clazz) {
  250. onBackPressed();
  251. }
  252. void Java_org_golang_app_GoNativeActivity_setDarkMode(JNIEnv *env, jclass clazz, jboolean dark) {
  253. setDarkMode((bool)dark);
  254. }