examples: update imports of sensors in mnit_ballz
[nit.git] / lib / mnit_android / android_app.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Impements the services of `mnit:app` using the API from the Android ndk
18 module android_app
19
20 import mnit
21 import android
22
23 in "C header" `{
24 #include <jni.h>
25 #include <errno.h>
26 #include <android/log.h>
27 #include <android_native_app_glue.h>
28
29 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "mnit", __VA_ARGS__))
30 #ifdef DEBUG
31 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mnit", __VA_ARGS__))
32 #else
33 #define LOGI(...) (void)0
34 #endif
35 `}
36
37 in "C" `{
38 #include <EGL/egl.h>
39 #include <GLES/gl.h>
40 #define GL_GLEXT_PROTOTYPES 1
41 #include <GLES/glext.h>
42 #include <errno.h>
43
44 extern EGLDisplay mnit_display;
45 extern EGLSurface mnit_surface;
46 extern EGLContext mnit_context;
47 extern EGLConfig mnit_config;
48 extern int32_t mnit_width;
49 extern int32_t mnit_height;
50 extern float mnit_zoom;
51
52 //int mnit_orientation_changed;
53 float mnit_zoom;
54
55 /* Handle inputs from the Android platform and sort them before
56 sending them in the Nit App */
57 static int32_t mnit_handle_input(struct android_app* app, AInputEvent* event) {
58 App nit_app = app->userData;
59 LOGI("handle input %i", (int)pthread_self());
60 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
61 LOGI("key");
62 return App_extern_input_key(nit_app, event);
63 }
64 else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
65 LOGI("motion");
66 return App_extern_input_motion(nit_app, event);
67 }
68
69 return 0;
70 }
71 `}
72
73
74 extern class InnerAndroidMotionEvent in "C" `{AInputEvent *`}
75 super Pointer
76 private fun pointers_count: Int is extern `{
77 return AMotionEvent_getPointerCount(recv);
78 `}
79 private fun just_went_down: Bool is extern `{
80 return (AMotionEvent_getAction(recv) & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN;
81 `}
82 private fun edge: Int is extern `{
83 return AMotionEvent_getEdgeFlags(recv);
84 `}
85 private fun index_down_pointer: Int is extern `{
86 int a = AMotionEvent_getAction(recv);
87 if ((a & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN)
88 return (a & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
89 else return -1;
90 `}
91
92 private fun action: AMotionEventAction `{ return AMotionEvent_getAction(recv); `}
93 end
94
95 extern class AMotionEventAction `{ int32_t `}
96 protected fun action: Int `{ return recv & AMOTION_EVENT_ACTION_MASK; `}
97 fun is_down: Bool do return action == 0
98 fun is_up: Bool do return action == 1
99 fun is_move: Bool do return action == 2
100 fun is_cancel: Bool do return action == 3
101 fun is_outside: Bool do return action == 4
102 fun is_pointer_down: Bool do return action == 5
103 fun is_pointer_up: Bool do return action == 6
104 end
105
106 interface AndroidInputEvent
107 super InputEvent
108 end
109
110 class AndroidMotionEvent
111 super AndroidInputEvent
112 super MotionEvent
113
114 private init(ie: InnerAndroidMotionEvent) do inner_event = ie
115 private var inner_event: InnerAndroidMotionEvent
116
117 private var pointers_cache: nullable Array[AndroidPointerEvent] = null
118 fun pointers: Array[AndroidPointerEvent]
119 do
120 if pointers_cache != null then
121 return pointers_cache.as(not null)
122 else
123 var pointers = new Array[AndroidPointerEvent]
124 var pointers_count = inner_event.pointers_count
125 for i in [0 .. pointers_count [do
126 var pointer_event = new AndroidPointerEvent(self, i)
127 pointers.add(pointer_event)
128 end
129 pointers_cache = pointers
130 return pointers
131 end
132 end
133
134 redef fun just_went_down: Bool do return inner_event.just_went_down
135 fun edge: Int do return inner_event.edge
136
137 redef fun down_pointer: nullable AndroidPointerEvent
138 do
139 var i = inner_event.index_down_pointer
140 if i > 0 then
141 return pointers[i]
142 else
143 return null
144 end
145 end
146 end
147
148 class AndroidPointerEvent
149 super PointerEvent
150 super AndroidInputEvent
151
152 protected var motion_event: AndroidMotionEvent
153 protected var pointer_id: Int
154
155 redef fun x: Float do return extern_x(motion_event.inner_event, pointer_id)
156 private fun extern_x(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
157 return ((int) AMotionEvent_getX(motion_event, pointer_id) * mnit_zoom);
158 `}
159
160 redef fun y: Float do return extern_y(motion_event.inner_event, pointer_id)
161 private fun extern_y(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
162 return ((int) AMotionEvent_getY(motion_event, pointer_id) * mnit_zoom) + 32;
163 `}
164
165 fun pressure: Float do return extern_pressure(motion_event.inner_event, pointer_id)
166 private fun extern_pressure(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
167 return AMotionEvent_getPressure(motion_event, pointer_id);
168 `}
169
170 redef fun pressed
171 do
172 var action = motion_event.inner_event.action
173 return action.is_down or action.is_move
174 end
175
176 redef fun depressed do return not pressed
177 end
178
179 extern class AndroidKeyEvent in "C" `{AInputEvent *`}
180 super KeyEvent
181 super AndroidInputEvent
182
183 fun action: Int is extern `{
184 return AKeyEvent_getAction(recv);
185 `}
186 redef fun is_down: Bool do return action == 0
187 redef fun is_up: Bool do return action == 1
188
189 fun key_code: Int is extern `{
190 return AKeyEvent_getKeyCode(recv);
191 `}
192
193 fun key_char: Char is extern `{
194 int code = AKeyEvent_getKeyCode(recv);
195 if (code >= AKEYCODE_0 && code <= AKEYCODE_9)
196 return '0'+code-AKEYCODE_0;
197 if (code >= AKEYCODE_A && code <= AKEYCODE_Z)
198 return 'a'+code-AKEYCODE_A;
199 return 0;
200 `}
201
202 fun is_back_key: Bool do return key_code == 2
203 fun is_menu_key: Bool do return key_code == 82
204 fun is_search_key: Bool do return key_code == 84
205 end
206
207 redef class App
208 redef type IE: AndroidInputEvent
209 redef type D: Opengles1Display
210
211 redef fun init_window
212 do
213 set_as_input_handler native_app_glue
214 display = new Opengles1Display
215
216 super
217 end
218
219 private fun set_as_input_handler(app_glue: NativeAppGlue) import extern_input_key, extern_input_motion `{
220 app_glue->onInputEvent = mnit_handle_input;
221 `}
222
223 redef fun full_frame do if not paused then super
224
225 # these are used as a callback from native to type incoming events
226 private fun extern_input_key(event: AndroidKeyEvent): Bool
227 do
228 return input(event)
229 end
230 private fun extern_input_motion(event: InnerAndroidMotionEvent): Bool
231 do
232 var ie = new AndroidMotionEvent(event)
233 var handled = input(ie)
234
235 if not handled then
236 for pe in ie.pointers do
237 input(pe)
238 end
239 end
240
241 return handled
242 end
243
244 redef fun generate_input do poll_looper 0
245 end
246
247 extern class JavaClassLoader in "Java" `{java.lang.ClassLoader`}
248 super JavaObject
249 end
250
251 redef class Sys
252 # Get the running JVM
253 redef fun create_default_jvm
254 do
255 var jvm = app.native_app_glue.ndk_native_activity.vm
256 var jni_env = jvm.attach_current_thread
257 if jni_env.address_is_null then jni_env = jvm.env
258
259 self.jvm = jvm
260 self.jni_env = jni_env
261 end
262
263 #protected fun ndk_jvm: JavaVM do`{ return mnit_java_app->activity->vm; `}
264
265 private var class_loader: nullable JavaObject = null
266 private var class_loader_method: nullable JMethodID = null
267 redef fun load_jclass(name)
268 do
269 var class_loader = self.class_loader
270 if class_loader == null then
271 find_class_loader(app.native_app_glue.ndk_native_activity.java_native_activity)
272 class_loader = self.class_loader
273 assert class_loader != null
274 end
275
276 var class_loader_method = self.class_loader_method
277 assert class_loader_method != null
278
279 return load_jclass_intern(class_loader, class_loader_method, name)
280 end
281
282 private fun find_class_loader(native_activity: NativeActivity) import jni_env, class_loader=, JavaObject.as nullable, class_loader_method=, JMethodID.as nullable `{
283 JNIEnv *env = Sys_jni_env(recv);
284
285 // Retrieve main activity
286 jclass class_activity = (*env)->GetObjectClass(env, native_activity);
287 if (class_activity == NULL) {
288 __android_log_print(ANDROID_LOG_ERROR, "Nit", "retreiving activity class");
289 (*env)->ExceptionDescribe(env);
290 exit(1);
291 }
292
293 jmethodID class_activity_getClassLoader = (*env)->GetMethodID(env, class_activity, "getClassLoader", "()Ljava/lang/ClassLoader;");
294 if (class_activity_getClassLoader == NULL) {
295 __android_log_print(ANDROID_LOG_ERROR, "Nit", "retreiving 'getClassLoader' method");
296 (*env)->ExceptionDescribe(env);
297 exit(1);
298 }
299
300 // Call activity.getClassLoader
301 jobject instance_class_loader = (*env)->CallObjectMethod(env, native_activity, class_activity_getClassLoader);
302 if (instance_class_loader == NULL) {
303 __android_log_print(ANDROID_LOG_ERROR, "Nit", "retreiving class loader instance");
304 (*env)->ExceptionDescribe(env);
305 exit(1);
306 }
307
308 jclass class_class_loader = (*env)->GetObjectClass(env, instance_class_loader);
309 if (class_class_loader == NULL) {
310 __android_log_print(ANDROID_LOG_ERROR, "Nit", "retreiving class of class loader");
311 (*env)->ExceptionDescribe(env);
312 exit(1);
313 }
314
315 // Get the method ClassLoader.findClass
316 jmethodID class_class_loader_findClass = (*env)->GetMethodID(env, class_class_loader, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
317 if (class_class_loader_findClass == NULL) {
318 __android_log_print(ANDROID_LOG_ERROR, "Nit", "retreiving 'findClass' method");
319 (*env)->ExceptionDescribe(env);
320 exit(1);
321 }
322
323 // Return the values to Nit
324 Sys_class_loader__assign(recv, JavaObject_as_nullable((*env)->NewGlobalRef(env, instance_class_loader)));
325 Sys_class_loader_method__assign(recv, JMethodID_as_nullable(class_class_loader_findClass));
326
327 // Clean up
328 (*env)->DeleteLocalRef(env, class_activity);
329 (*env)->DeleteLocalRef(env, instance_class_loader);
330 (*env)->DeleteLocalRef(env, class_class_loader);
331 `}
332
333 private fun load_jclass_intern(instance_class_loader: JavaObject, class_loader_findClass: JMethodID, name: NativeString): JClass import jni_env `{
334 JNIEnv *env = Sys_jni_env(recv);
335 jobject class_name = (*env)->NewStringUTF(env, name);
336
337 jclass java_class = (*env)->CallObjectMethod(env, instance_class_loader, class_loader_findClass, class_name);
338 if (java_class == NULL) {
339 __android_log_print(ANDROID_LOG_ERROR, "Nit", "loading targetted class");
340 (*env)->ExceptionDescribe(env);
341 exit(1);
342 }
343
344 (*env)->DeleteLocalRef(env, class_name);
345
346 return java_class;
347 `}
348 end