android.c 9.0 KB

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