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
20 import android_opengles1
26 #include <android/log.h>
27 #include <android_native_app_glue.h>
33 #define GL_GLEXT_PROTOTYPES 1
34 #include <GLES/glext.h>
37 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "mnit", __VA_ARGS__))
39 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mnit", __VA_ARGS__))
41 #define LOGI(...) (void)0
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;
260 interface AndroidInputEvent
264 class AndroidMotionEvent
265 super AndroidInputEvent
268 private init(ie: InnerAndroidMotionEvent) do inner_event = ie
269 private var inner_event: InnerAndroidMotionEvent
271 private var pointers_cache: nullable Array[AndroidPointerEvent] = null
272 fun pointers: Array[AndroidPointerEvent]
274 if pointers_cache != null then
275 return pointers_cache.as(not null)
277 var pointers = new Array[AndroidPointerEvent]
278 var pointers_count = inner_event.pointers_count
279 for i in [0 .. pointers_count [do
280 var pointer_event = new AndroidPointerEvent(self, i)
281 pointers.add(pointer_event)
283 pointers_cache = pointers
288 redef fun just_went_down: Bool do return inner_event.just_went_down
289 fun edge: Int do return inner_event.edge
291 redef fun down_pointer: nullable AndroidPointerEvent
293 var i = inner_event.index_down_pointer
302 class AndroidPointerEvent
304 super AndroidInputEvent
306 protected var motion_event: AndroidMotionEvent
307 protected var pointer_id: Int
309 redef fun x: Float do return extern_x(motion_event.inner_event, pointer_id)
310 private fun extern_x(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
311 return ((int
) AMotionEvent_getX(motion_event
, pointer_id
) * mnit_zoom
);
314 redef fun y: Float do return extern_y(motion_event.inner_event, pointer_id)
315 private fun extern_y(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
316 return ((int
) AMotionEvent_getY(motion_event
, pointer_id
) * mnit_zoom
) + 32;
319 fun pressure: Float do return extern_pressure(motion_event.inner_event, pointer_id)
320 private fun extern_pressure(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
321 return AMotionEvent_getPressure(motion_event
, pointer_id
);
324 redef fun pressed do return true
325 redef fun depressed do return false
328 extern AndroidKeyEvent in "C" `{AInputEvent *`}
330 super AndroidInputEvent
332 fun action
: Int is extern `{
333 return AKeyEvent_getAction(recv);
335 redef fun is_down
: Bool do return action
== 0
336 redef fun is_up
: Bool do return action
== 1
338 fun key_code
: Int is extern `{
339 return AKeyEvent_getKeyCode(recv);
342 fun key_char
: Char is extern `{
343 int code = AKeyEvent_getKeyCode(recv);
344 if (code >= AKEYCODE_0 && code <= AKEYCODE_9)
345 return '0'+code-AKEYCODE_0;
346 if (code >= AKEYCODE_A && code <= AKEYCODE_Z)
347 return 'a'+code-AKEYCODE_A;
351 fun is_back_key
: Bool do return key_code
== 2
352 fun is_menu_key
: Bool do return key_code
== 82
353 fun is_search_key
: Bool do return key_code
== 84
357 # Uses Android logs for every print
358 redef fun print
(text
: Object) is extern import Object.to_s
, String.to_cstring
`{
359 __android_log_print(ANDROID_LOG_INFO, "mnit print", "%s", String_to_cstring(Object_to_s(object)));
364 redef type IE: AndroidInputEvent
365 redef type D
: Opengles1Display
367 redef fun log_warning
(msg
) is extern import String.to_cstring
`{
368 LOGW("%s", String_to_cstring(msg));
370 redef fun log_info
(msg
) is extern import String.to_cstring
`{
371 LOGI("%s", String_to_cstring(msg));
374 redef fun init_window
378 display
= new Opengles1Display
381 # these two are used as a callback from native to type incoming events
382 private fun extern_input_key
(event
: AndroidKeyEvent): Bool
386 private fun extern_input_motion
(event
: InnerAndroidMotionEvent): Bool
388 var ie
= new AndroidMotionEvent(event
)
389 var handled
= input
(ie
)
392 for pe
in ie
.pointers
do
400 redef fun main_loop
is extern import full_frame
, save
, pause
, resume
, gained_focus
, lost_focus
, init_window
, term_window
, extern_input_key
, extern_input_motion
`{
405 mnit_java_app->userData = &nit_app;
406 mnit_java_app->onAppCmd = mnit_handle_cmd;
407 mnit_java_app->onInputEvent = mnit_handle_input;
412 static int block = 0;
413 struct android_poll_source* source;
415 while ((ident=ALooper_pollAll(0, NULL, &events,
416 (void**)&source)) >= 0) { /* first 0 is for non-blocking */
418 // Process this event.
420 source->process(mnit_java_app, source);
422 // Check if we are exiting.
423 if (mnit_java_app->destroyRequested != 0) {
429 if (mnit_animating == 1) {
431 LOGI("frame at loop end 1");
435 /* App_exit(); // this is unreachable anyway*/