1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Impements the services of `mnit:app` using the API from the Android ndk
26 #include <android/log.h>
27 #include <android_native_app_glue.h>
29 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "mnit", __VA_ARGS__))
31 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mnit", __VA_ARGS__))
33 #define LOGI(...) (void)0
40 #define GL_GLEXT_PROTOTYPES 1
41 #include <GLES/glext.h>
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;
52 int mnit_orientation_changed;
54 int mnit_animating = 0;
56 /* This is confusing; the type come from android_native_app_glue.h
57 and so identifies the java part of the app */
58 struct android_app *mnit_java_app;
60 /* This is the pure Nit App */
63 /* The main of the Nit application, compiled somewhere else */
64 extern int main(int, char**);
66 /* Wraps App_full_frame() and check for orientation. */
69 void mnit_term_display()
71 // At this point we have nothing to do
74 /* Handle inputs from the Android platform and sort them before
75 sending them in the Nit App */
76 static int32_t mnit_handle_input(struct android_app* app, AInputEvent* event) {
77 LOGI("handle input %i", (int)pthread_self());
78 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
80 return App_extern_input_key(nit_app, event);
82 else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
84 return App_extern_input_motion(nit_app, event);
90 static void mnit_handle_cmd(struct android_app* app, int32_t cmd) {
93 AConfiguration_setOrientation(mnit_java_app->config, ACONFIGURATION_ORIENTATION_LAND);
94 LOGI("cmd %i", (int)pthread_self());
97 case APP_CMD_SAVE_STATE:
99 mnit_java_app->savedStateSize = 1;
100 mnit_java_app->savedState = malloc(1);
104 case APP_CMD_INIT_WINDOW:
105 LOGI ("init window");
106 if (mnit_java_app->window != NULL) {
107 LOGI("init window in");
108 App_init_window(nit_app);
114 case APP_CMD_TERM_WINDOW:
115 LOGI ("term window");
117 App_term_window(nit_app);
120 case APP_CMD_GAINED_FOCUS:
123 App_gained_focus(nit_app);
127 case APP_CMD_LOST_FOCUS:
130 App_lost_focus(nit_app);
145 case APP_CMD_DESTROY:
146 LOGI ("app destrop");
147 App_destroy(nit_app);
161 case APP_CMD_LOW_MEMORY:
162 LOGI ("app low mem");
165 case APP_CMD_CONFIG_CHANGED:
166 LOGI ("app cmd conf ch");
169 case APP_CMD_INPUT_CHANGED:
170 LOGI ("app cmd in ch");
173 case APP_CMD_WINDOW_RESIZED:
174 mnit_orientation_changed = 1;
175 LOGI ("app win res");
178 case APP_CMD_WINDOW_REDRAW_NEEDED:
179 LOGI ("app win redraw needed");
182 case APP_CMD_CONTENT_RECT_CHANGED:
183 LOGI ("app content rect ch");
188 void android_main(struct android_app* app)
199 if (mnit_display == EGL_NO_DISPLAY) {
204 if (mnit_orientation_changed)
206 mnit_orientation_changed = 0;
208 if (mnit_surface != EGL_NO_SURFACE) {
209 eglDestroySurface(mnit_display, mnit_surface);
211 EGLSurface surface = eglCreateWindowSurface(mnit_display, mnit_config, mnit_java_app->window, NULL);
213 if (eglMakeCurrent(mnit_display, surface, surface, mnit_context) == EGL_FALSE) {
214 LOGW("Unable to eglMakeCurrent");
217 eglQuerySurface(mnit_display, surface, EGL_WIDTH, &mnit_width);
218 eglQuerySurface(mnit_display, surface, EGL_HEIGHT, &mnit_height);
220 mnit_surface = surface;
222 glViewport(0, 0, mnit_width, mnit_height);
223 glMatrixMode(GL_PROJECTION);
225 glOrthof(0.0f, mnit_width, mnit_height, 0.0f, 0.0f, 1.0f);
226 glMatrixMode(GL_MODELVIEW);
231 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
232 glClear(GL_COLOR_BUFFER_BIT); // | GL_DEPTH_BUFFER_BIT);
234 App_full_frame(nit_app);
241 extern InnerAndroidMotionEvent in "C" `{AInputEvent *`}
243 private fun pointers_count: Int is extern `{
244 return AMotionEvent_getPointerCount(recv
);
246 private fun just_went_down: Bool is extern `{
247 return (AMotionEvent_getAction(recv
) & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN;
249 private fun edge: Int is extern `{
250 return AMotionEvent_getEdgeFlags(recv
);
252 private fun index_down_pointer: Int is extern `{
253 int a
= AMotionEvent_getAction(recv
);
254 if ((a
& AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN)
255 return (a
& AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
259 private fun action: AMotionEventAction `{ return AMotionEvent_getAction(recv); `}
262 extern class AMotionEventAction `{ int32_t `}
263 protected fun action: Int `{ return recv & AMOTION_EVENT_ACTION_MASK; `}
264 fun is_down
: Bool do return action
== 0
265 fun is_up
: Bool do return action
== 1
266 fun is_move
: Bool do return action
== 2
267 fun is_cancel
: Bool do return action
== 3
268 fun is_outside
: Bool do return action
== 4
269 fun is_pointer_down
: Bool do return action
== 5
270 fun is_pointer_up
: Bool do return action
== 6
273 interface AndroidInputEvent
277 class AndroidMotionEvent
278 super AndroidInputEvent
281 private init(ie
: InnerAndroidMotionEvent) do inner_event
= ie
282 private var inner_event
: InnerAndroidMotionEvent
284 private var pointers_cache
: nullable Array[AndroidPointerEvent] = null
285 fun pointers
: Array[AndroidPointerEvent]
287 if pointers_cache
!= null then
288 return pointers_cache
.as(not null)
290 var pointers
= new Array[AndroidPointerEvent]
291 var pointers_count
= inner_event
.pointers_count
292 for i
in [0 .. pointers_count
[do
293 var pointer_event
= new AndroidPointerEvent(self, i
)
294 pointers
.add
(pointer_event
)
296 pointers_cache
= pointers
301 redef fun just_went_down
: Bool do return inner_event
.just_went_down
302 fun edge
: Int do return inner_event
.edge
304 redef fun down_pointer
: nullable AndroidPointerEvent
306 var i
= inner_event
.index_down_pointer
315 class AndroidPointerEvent
317 super AndroidInputEvent
319 protected var motion_event
: AndroidMotionEvent
320 protected var pointer_id
: Int
322 redef fun x
: Float do return extern_x
(motion_event
.inner_event
, pointer_id
)
323 private fun extern_x
(motion_event
: InnerAndroidMotionEvent, pointer_id
: Int): Float is extern `{
324 return ((int) AMotionEvent_getX(motion_event, pointer_id) * mnit_zoom);
327 redef fun y
: Float do return extern_y
(motion_event
.inner_event
, pointer_id
)
328 private fun extern_y
(motion_event
: InnerAndroidMotionEvent, pointer_id
: Int): Float is extern `{
329 return ((int) AMotionEvent_getY(motion_event, pointer_id) * mnit_zoom) + 32;
332 fun pressure
: Float do return extern_pressure
(motion_event
.inner_event
, pointer_id
)
333 private fun extern_pressure
(motion_event
: InnerAndroidMotionEvent, pointer_id
: Int): Float is extern `{
334 return AMotionEvent_getPressure(motion_event, pointer_id);
339 var action
= motion_event
.inner_event
.action
340 return action
.is_down
or action
.is_move
343 redef fun depressed
do return not pressed
346 extern AndroidKeyEvent in "C" `{AInputEvent *`}
348 super AndroidInputEvent
350 fun action: Int is extern `{
351 return AKeyEvent_getAction(recv
);
353 redef fun is_down: Bool do return action == 0
354 redef fun is_up: Bool do return action == 1
356 fun key_code: Int is extern `{
357 return AKeyEvent_getKeyCode(recv
);
360 fun key_char: Char is extern `{
361 int code
= AKeyEvent_getKeyCode(recv
);
362 if (code
>= AKEYCODE_0 && code
<= AKEYCODE_9)
363 return '0'+code-AKEYCODE_0
;
364 if (code
>= AKEYCODE_A && code
<= AKEYCODE_Z)
365 return 'a'+code-AKEYCODE_A
;
369 fun is_back_key: Bool do return key_code == 2
370 fun is_menu_key: Bool do return key_code == 82
371 fun is_search_key: Bool do return key_code == 84
375 # Uses Android logs for every print
376 redef fun print(text: Object) is extern import Object.to_s, String.to_cstring `{
377 __android_log_print
(ANDROID_LOG_INFO, "mnit print", "%s", String_to_cstring(Object_to_s(text
)));
382 redef type IE: AndroidInputEvent
383 redef type D: Opengles1Display
385 redef fun log_warning(msg) is extern import String.to_cstring `{
386 LOGW("%s", String_to_cstring(msg
));
388 redef fun log_info(msg) is extern import String.to_cstring `{
389 LOGI("%s", String_to_cstring(msg
));
392 redef fun init_window
396 display = new Opengles1Display
399 # these two are used as a callback from native to type incoming events
400 private fun extern_input_key(event: AndroidKeyEvent): Bool
404 private fun extern_input_motion(event: InnerAndroidMotionEvent): Bool
406 var ie = new AndroidMotionEvent(event)
407 var handled = input(ie)
410 for pe in ie.pointers do
418 redef fun main_loop is extern import full_frame, generate_input `{
423 mnit_java_app-
>userData
= &nit_app
;
424 mnit_java_app-
>onAppCmd
= mnit_handle_cmd
;
425 mnit_java_app-
>onInputEvent
= mnit_handle_input
;
428 App_generate_input(recv
);
430 if (mnit_java_app-
>destroyRequested
!= 0) return;
432 if (mnit_animating
== 1) {
434 LOGI("frame at loop end 1");
438 /* App_exit(); // this
is unreachable anyway
*/
441 redef fun generate_input import save, pause, resume, gained_focus, lost_focus, init_window, term_window, extern_input_key, extern_input_motion `{
444 static int block
= 0;
445 struct android_poll_source
* source
;
447 while ((ident
=ALooper_pollAll(0, NULL, &events
,
448 (void
**)&source
)) >= 0) { /* first
0 is for non-blocking
*/
450 // Process this event
.
452 source-
>process
(mnit_java_app
, source
);
454 // Check if we are exiting
.
455 if (mnit_java_app-
>destroyRequested
!= 0) {