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 current_class;
19
find_class(JNIEnv * env,const char * class_name)20 static jclass find_class(JNIEnv *env, const char *class_name) {
21 jclass clazz = (*env)->FindClass(env, class_name);
22 if (clazz == NULL) {
23 (*env)->ExceptionClear(env);
24 LOG_FATAL("cannot find %s", class_name);
25 return NULL;
26 }
27 return clazz;
28 }
29
find_method(JNIEnv * env,jclass clazz,const char * name,const char * sig)30 static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
31 jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
32 if (m == 0) {
33 (*env)->ExceptionClear(env);
34 LOG_FATAL("cannot find method %s %s", name, sig);
35 return 0;
36 }
37 return m;
38 }
39
find_static_method(JNIEnv * env,jclass clazz,const char * name,const char * sig)40 static jmethodID find_static_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
41 jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
42 if (m == 0) {
43 (*env)->ExceptionClear(env);
44 LOG_FATAL("cannot find method %s %s", name, sig);
45 return 0;
46 }
47 return m;
48 }
49
50 static jmethodID key_rune_method;
51
JNI_OnLoad(JavaVM * vm,void * reserved)52 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
53 JNIEnv* env;
54 if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
55 return -1;
56 }
57
58 return JNI_VERSION_1_6;
59 }
60
61 static int main_running = 0;
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.
ANativeActivity_onCreate(ANativeActivity * activity,void * savedState,size_t savedStateSize)71 void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
72 if (!main_running) {
73 JNIEnv* env = activity->env;
74
75 // Note that activity->clazz is mis-named.
76 current_class = (*env)->GetObjectClass(env, activity->clazz);
77 current_class = (*env)->NewGlobalRef(env, current_class);
78 key_rune_method = find_static_method(env, current_class, "getRune", "(III)I");
79
80 setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
81
82 // Set TMPDIR.
83 jmethodID gettmpdir = find_method(env, current_class, "getTmpdir", "()Ljava/lang/String;");
84 jstring jpath = (jstring)(*env)->CallObjectMethod(env, activity->clazz, gettmpdir, NULL);
85 const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
86 if (setenv("TMPDIR", tmpdir, 1) != 0) {
87 LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
88 }
89 (*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
90
91 // Call the Go main.main.
92 uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
93 if (!mainPC) {
94 LOG_FATAL("missing main.main");
95 }
96 callMain(mainPC);
97 main_running = 1;
98 }
99
100 // These functions match the methods on Activity, described at
101 // http://developer.android.com/reference/android/app/Activity.html
102 //
103 // Note that onNativeWindowResized is not called on resize. Avoid it.
104 // https://code.google.com/p/android/issues/detail?id=180645
105 activity->callbacks->onStart = onStart;
106 activity->callbacks->onResume = onResume;
107 activity->callbacks->onSaveInstanceState = onSaveInstanceState;
108 activity->callbacks->onPause = onPause;
109 activity->callbacks->onStop = onStop;
110 activity->callbacks->onDestroy = onDestroy;
111 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
112 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
113 activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
114 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
115 activity->callbacks->onInputQueueCreated = onInputQueueCreated;
116 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
117 activity->callbacks->onConfigurationChanged = onConfigurationChanged;
118 activity->callbacks->onLowMemory = onLowMemory;
119
120 onCreate(activity);
121 }
122
123 // TODO(crawshaw): Test configuration on more devices.
124 static const EGLint RGB_888[] = {
125 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
126 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
127 EGL_BLUE_SIZE, 8,
128 EGL_GREEN_SIZE, 8,
129 EGL_RED_SIZE, 8,
130 EGL_DEPTH_SIZE, 16,
131 EGL_CONFIG_CAVEAT, EGL_NONE,
132 EGL_NONE
133 };
134
135 EGLDisplay display = NULL;
136 EGLSurface surface = NULL;
137
initEGLDisplay()138 static char* initEGLDisplay() {
139 display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
140 if (!eglInitialize(display, 0, 0)) {
141 return "EGL initialize failed";
142 }
143 return NULL;
144 }
145
createEGLSurface(ANativeWindow * window)146 char* createEGLSurface(ANativeWindow* window) {
147 char* err;
148 EGLint numConfigs, format;
149 EGLConfig config;
150 EGLContext context;
151
152 if (display == 0) {
153 if ((err = initEGLDisplay()) != NULL) {
154 return err;
155 }
156 }
157
158 if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
159 return "EGL choose RGB_888 config failed";
160 }
161 if (numConfigs <= 0) {
162 return "EGL no config found";
163 }
164
165 eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
166 if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
167 return "EGL set buffers geometry failed";
168 }
169
170 surface = eglCreateWindowSurface(display, config, window, NULL);
171 if (surface == EGL_NO_SURFACE) {
172 return "EGL create surface failed";
173 }
174
175 const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
176 context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
177
178 if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
179 return "eglMakeCurrent failed";
180 }
181 return NULL;
182 }
183
destroyEGLSurface()184 char* destroyEGLSurface() {
185 if (!eglDestroySurface(display, surface)) {
186 return "EGL destroy surface failed";
187 }
188 return NULL;
189 }
190
getKeyRune(JNIEnv * env,AInputEvent * e)191 int32_t getKeyRune(JNIEnv* env, AInputEvent* e) {
192 return (int32_t)(*env)->CallStaticIntMethod(
193 env,
194 current_class,
195 key_rune_method,
196 AInputEvent_getDeviceId(e),
197 AKeyEvent_getKeyCode(e),
198 AKeyEvent_getMetaState(e)
199 );
200 }
201