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
27 #include <android/log.h>
28 #include <android_native_app_glue.h>
29 #include <android/sensor.h>
31 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "mnit", __VA_ARGS__))
33 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mnit", __VA_ARGS__))
35 #define LOGI(...) (void)0
42 #define GL_GLEXT_PROTOTYPES 1
43 #include <GLES/glext.h>
46 extern EGLDisplay mnit_display;
47 extern EGLSurface mnit_surface;
48 extern EGLContext mnit_context;
49 extern EGLConfig mnit_config;
50 extern int32_t mnit_width;
51 extern int32_t mnit_height;
52 extern float mnit_zoom;
54 int mnit_orientation_changed;
56 int mnit_animating = 0;
58 /* This is confusing; the type come from android_native_app_glue.h
59 and so identifies the java part of the app */
60 struct android_app *mnit_java_app;
62 /* This is the pure Nit App */
65 /* The main of the Nit application, compiled somewhere else */
66 extern int main(int, char**);
68 /* Wraps App_full_frame() and check for orientation. */
71 void mnit_term_display()
73 // At this point we have nothing to do
76 /* Handle inputs from the Android platform and sort them before
77 sending them in the Nit App */
78 static int32_t mnit_handle_input(struct android_app* app, AInputEvent* event) {
79 LOGI("handle input %i", (int)pthread_self());
80 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
82 return App_extern_input_key(nit_app, event);
84 else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
86 return App_extern_input_motion(nit_app, event);
92 static void mnit_handle_cmd(struct android_app* app, int32_t cmd) {
95 AConfiguration_setOrientation(mnit_java_app->config, ACONFIGURATION_ORIENTATION_LAND);
96 LOGI("cmd %i", (int)pthread_self());
99 case APP_CMD_SAVE_STATE:
101 mnit_java_app->savedStateSize = 1;
102 mnit_java_app->savedState = malloc(1);
106 case APP_CMD_INIT_WINDOW:
107 LOGI ("init window");
108 if (mnit_java_app->window != NULL) {
109 LOGI("init window in");
110 App_init_window(nit_app);
116 case APP_CMD_TERM_WINDOW:
117 LOGI ("term window");
119 App_term_window(nit_app);
122 case APP_CMD_GAINED_FOCUS:
125 App_gained_focus(nit_app);
129 case APP_CMD_LOST_FOCUS:
132 App_lost_focus(nit_app);
147 case APP_CMD_DESTROY:
148 LOGI ("app destrop");
149 App_destroy(nit_app);
163 case APP_CMD_LOW_MEMORY:
164 LOGI ("app low mem");
167 case APP_CMD_CONFIG_CHANGED:
168 LOGI ("app cmd conf ch");
171 case APP_CMD_INPUT_CHANGED:
172 LOGI ("app cmd in ch");
175 case APP_CMD_WINDOW_RESIZED:
176 mnit_orientation_changed = 1;
177 LOGI ("app win res");
180 case APP_CMD_WINDOW_REDRAW_NEEDED:
181 LOGI ("app win redraw needed");
184 case APP_CMD_CONTENT_RECT_CHANGED:
185 LOGI ("app content rect ch");
190 void android_main(struct android_app* app)
201 if (mnit_display == EGL_NO_DISPLAY) {
206 if (mnit_orientation_changed)
208 mnit_orientation_changed = 0;
210 if (mnit_surface != EGL_NO_SURFACE) {
211 eglDestroySurface(mnit_display, mnit_surface);
213 EGLSurface surface = eglCreateWindowSurface(mnit_display, mnit_config, mnit_java_app->window, NULL);
215 if (eglMakeCurrent(mnit_display, surface, surface, mnit_context) == EGL_FALSE) {
216 LOGW("Unable to eglMakeCurrent");
219 eglQuerySurface(mnit_display, surface, EGL_WIDTH, &mnit_width);
220 eglQuerySurface(mnit_display, surface, EGL_HEIGHT, &mnit_height);
222 mnit_surface = surface;
224 glViewport(0, 0, mnit_width, mnit_height);
225 glMatrixMode(GL_PROJECTION);
227 glOrthof(0.0f, mnit_width, mnit_height, 0.0f, 0.0f, 1.0f);
228 glMatrixMode(GL_MODELVIEW);
233 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
234 glClear(GL_COLOR_BUFFER_BIT); // | GL_DEPTH_BUFFER_BIT);
236 App_full_frame(nit_app);
243 extern InnerAndroidMotionEvent in "C" `{AInputEvent *`}
245 private fun pointers_count: Int is extern `{
246 return AMotionEvent_getPointerCount(recv
);
248 private fun just_went_down: Bool is extern `{
249 return (AMotionEvent_getAction(recv
) & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN;
251 private fun edge: Int is extern `{
252 return AMotionEvent_getEdgeFlags(recv
);
254 private fun index_down_pointer: Int is extern `{
255 int a
= AMotionEvent_getAction(recv
);
256 if ((a
& AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN)
257 return (a
& AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
261 private fun action: AMotionEventAction `{ return AMotionEvent_getAction(recv); `}
264 extern class AMotionEventAction `{ int32_t `}
265 protected fun action: Int `{ return recv & AMOTION_EVENT_ACTION_MASK; `}
266 fun is_down
: Bool do return action
== 0
267 fun is_up
: Bool do return action
== 1
268 fun is_move
: Bool do return action
== 2
269 fun is_cancel
: Bool do return action
== 3
270 fun is_outside
: Bool do return action
== 4
271 fun is_pointer_down
: Bool do return action
== 5
272 fun is_pointer_up
: Bool do return action
== 6
275 interface AndroidInputEvent
279 class AndroidMotionEvent
280 super AndroidInputEvent
283 private init(ie
: InnerAndroidMotionEvent) do inner_event
= ie
284 private var inner_event
: InnerAndroidMotionEvent
286 private var pointers_cache
: nullable Array[AndroidPointerEvent] = null
287 fun pointers
: Array[AndroidPointerEvent]
289 if pointers_cache
!= null then
290 return pointers_cache
.as(not null)
292 var pointers
= new Array[AndroidPointerEvent]
293 var pointers_count
= inner_event
.pointers_count
294 for i
in [0 .. pointers_count
[do
295 var pointer_event
= new AndroidPointerEvent(self, i
)
296 pointers
.add
(pointer_event
)
298 pointers_cache
= pointers
303 redef fun just_went_down
: Bool do return inner_event
.just_went_down
304 fun edge
: Int do return inner_event
.edge
306 redef fun down_pointer
: nullable AndroidPointerEvent
308 var i
= inner_event
.index_down_pointer
317 class AndroidPointerEvent
319 super AndroidInputEvent
321 protected var motion_event
: AndroidMotionEvent
322 protected var pointer_id
: Int
324 redef fun x
: Float do return extern_x
(motion_event
.inner_event
, pointer_id
)
325 private fun extern_x
(motion_event
: InnerAndroidMotionEvent, pointer_id
: Int): Float is extern `{
326 return ((int) AMotionEvent_getX(motion_event, pointer_id) * mnit_zoom);
329 redef fun y
: Float do return extern_y
(motion_event
.inner_event
, pointer_id
)
330 private fun extern_y
(motion_event
: InnerAndroidMotionEvent, pointer_id
: Int): Float is extern `{
331 return ((int) AMotionEvent_getY(motion_event, pointer_id) * mnit_zoom) + 32;
334 fun pressure
: Float do return extern_pressure
(motion_event
.inner_event
, pointer_id
)
335 private fun extern_pressure
(motion_event
: InnerAndroidMotionEvent, pointer_id
: Int): Float is extern `{
336 return AMotionEvent_getPressure(motion_event, pointer_id);
341 var action
= motion_event
.inner_event
.action
342 return action
.is_down
or action
.is_move
345 redef fun depressed
do return not pressed
348 extern AndroidKeyEvent in "C" `{AInputEvent *`}
350 super AndroidInputEvent
352 fun action: Int is extern `{
353 return AKeyEvent_getAction(recv
);
355 redef fun is_down: Bool do return action == 0
356 redef fun is_up: Bool do return action == 1
358 fun key_code: Int is extern `{
359 return AKeyEvent_getKeyCode(recv
);
362 fun key_char: Char is extern `{
363 int code
= AKeyEvent_getKeyCode(recv
);
364 if (code
>= AKEYCODE_0 && code
<= AKEYCODE_9)
365 return '0'+code-AKEYCODE_0
;
366 if (code
>= AKEYCODE_A && code
<= AKEYCODE_Z)
367 return 'a'+code-AKEYCODE_A
;
371 fun is_back_key: Bool do return key_code == 2
372 fun is_menu_key: Bool do return key_code == 82
373 fun is_search_key: Bool do return key_code == 84
377 # Uses Android logs for every print
378 redef fun print(text: Object) is extern import Object.to_s, String.to_cstring `{
379 __android_log_print
(ANDROID_LOG_INFO, "mnit print", "%s", String_to_cstring(Object_to_s(text
)));
384 redef type IE: AndroidInputEvent
385 redef type D: Opengles1Display
387 var accelerometer = new AndroidSensor
388 var magnetic_field = new AndroidSensor
389 var gyroscope = new AndroidSensor
390 var light = new AndroidSensor
391 var proximity = new AndroidSensor
392 var sensormanager: ASensorManager
393 var eventqueue: ASensorEventQueue
394 var sensors_support_enabled writable = false
396 redef fun log_warning(msg) is extern import String.to_cstring `{
397 LOGW("%s", String_to_cstring(msg
));
399 redef fun log_info(msg) is extern import String.to_cstring `{
400 LOGI("%s", String_to_cstring(msg
));
403 redef fun init_window
407 display = new Opengles1Display
410 # these are used as a callback from native to type incoming events
411 private fun extern_input_key(event: AndroidKeyEvent): Bool
415 private fun extern_input_motion(event: InnerAndroidMotionEvent): Bool
417 var ie = new AndroidMotionEvent(event)
418 var handled = input(ie)
421 for pe in ie.pointers do
429 private fun extern_input_sensor_accelerometer(event: ASensorAccelerometer) do input(event)
430 private fun extern_input_sensor_magnetic_field(event: ASensorMagneticField) do input(event)
431 private fun extern_input_sensor_gyroscope(event: ASensorGyroscope) do input(event)
432 private fun extern_input_sensor_light(event: ASensorLight) do input(event)
433 private fun extern_input_sensor_proximity(event: ASensorProximity) do input(event)
436 # The user decides which sensors he wants to use by setting them enabled
437 private fun enable_sensors
439 if sensors_support_enabled then enable_sensors_management else return
440 if accelerometer.enabled then enable_accelerometer
441 if magnetic_field.enabled then enable_magnetic_field
442 if gyroscope.enabled then enable_gyroscope
443 if light.enabled then enable_light
444 if proximity.enabled then enable_proximity
447 private fun enable_sensors_management
449 sensormanager = new ASensorManager.get_instance
450 #eventqueue = sensormanager.create_event_queue(new NdkAndroidApp)
451 eventqueue = initialize_event_queue(sensormanager)
454 # HACK: need a nit method to get mnit_java_app, then we can use the appropriate sensormanager.create_event_queue method to initialize the event queue
455 private fun initialize_event_queue(sensormanager: ASensorManager): ASensorEventQueue `{
456 return ASensorManager_createEventQueue(sensormanager
, mnit_java_app-
>looper
, LOOPER_ID_USER, NULL, NULL);
459 private fun enable_accelerometer
461 accelerometer.asensor = sensormanager.get_default_sensor(new ASensorType.accelerometer)
462 if accelerometer.asensor.address_is_null then
463 print "Accelerometer sensor unavailable"
465 if eventqueue.enable_sensor(accelerometer.asensor) < 0 then print "Accelerometer enabling failed"
466 eventqueue.set_event_rate(accelerometer.asensor, accelerometer.event_rate)
470 private fun enable_magnetic_field
472 magnetic_field.asensor = sensormanager.get_default_sensor(new ASensorType.magnetic_field)
473 if magnetic_field.asensor.address_is_null then
474 print "Magnetic Field unavailable"
476 if eventqueue.enable_sensor(magnetic_field.asensor) < 0 then print "Magnetic Field enabling failed"
477 eventqueue.set_event_rate(magnetic_field.asensor, magnetic_field.event_rate)
481 private fun enable_gyroscope
483 gyroscope.asensor = sensormanager.get_default_sensor(new ASensorType.gyroscope)
484 if gyroscope.asensor.address_is_null then
485 print "Gyroscope sensor unavailable"
487 if eventqueue.enable_sensor(gyroscope.asensor) < 0 then print "Gyroscope enabling failed"
488 eventqueue.set_event_rate(gyroscope.asensor, gyroscope.event_rate)
492 private fun enable_light
494 light.asensor = sensormanager.get_default_sensor(new ASensorType.light)
495 if light.asensor.address_is_null then
496 print "Light sensor unavailable"
498 if eventqueue.enable_sensor(light.asensor) < 0 then print "Light enabling failed"
499 eventqueue.set_event_rate(light.asensor, light.event_rate)
503 private fun enable_proximity
505 proximity.asensor = sensormanager.get_default_sensor(new ASensorType.proximity)
506 if proximity.asensor.address_is_null then
507 print "Proximity sensor unavailable"
509 if eventqueue.enable_sensor(proximity.asensor) < 0 then print "Proximity enabling failed"
510 eventqueue.set_event_rate(light.asensor, light.event_rate)
514 redef fun main_loop is extern import full_frame, generate_input, enable_sensors `{
519 mnit_java_app-
>userData
= &nit_app
;
520 mnit_java_app-
>onAppCmd
= mnit_handle_cmd
;
521 mnit_java_app-
>onInputEvent
= mnit_handle_input
;
523 //Enbales sensors
if needed
524 App_enable_sensors(nit_app
);
527 App_generate_input(recv
);
529 if (mnit_java_app-
>destroyRequested
!= 0) return;
531 if (mnit_animating
== 1) {
533 LOGI("frame at loop end 1");
536 /* App_exit(); // this
is unreachable anyway
*/
539 redef fun generate_input import save, pause, resume, gained_focus, lost_focus, init_window, term_window, extern_input_key, extern_input_motion, extern_input_sensor_accelerometer, extern_input_sensor_magnetic_field, extern_input_sensor_gyroscope, extern_input_sensor_light, extern_input_sensor_proximity, eventqueue `{
542 static int block
= 0;
543 struct android_poll_source
* source
;
545 while ((ident
=ALooper_pollAll(0, NULL, &events
,
546 (void
**)&source
)) >= 0) { /* first
0 is for non-blocking
*/
548 // Process this event
.
550 source-
>process
(mnit_java_app
, source
);
552 //If a sensor has data
, process it
553 if(ident
== LOOPER_ID_USER) {
554 //maybe add a boolean to the app to know
if we want to use
Sensor API or ASensorEvent directly
...
555 ASensorEvent* events
= malloc
(sizeof
(ASensorEvent)*10);
557 ASensorEventQueue* queue
= App_eventqueue(nit_app
);
558 while((nbevents
= ASensorEventQueue_getEvents(queue
, events
, 10)) > 0) {
560 for(i
= 0; i
< nbevents
; i
++){
561 ASensorEvent event
= events
[i
];
562 switch
(event
.type) {
563 case
ASENSOR_TYPE_ACCELEROMETER:
564 App_extern_input_sensor_accelerometer(nit_app
, &event
);
566 case
ASENSOR_TYPE_MAGNETIC_FIELD:
567 App_extern_input_sensor_magnetic_field(nit_app
, &event
);
569 case
ASENSOR_TYPE_GYROSCOPE:
570 App_extern_input_sensor_gyroscope(nit_app
, &event
);
572 case
ASENSOR_TYPE_LIGHT:
573 App_extern_input_sensor_light(nit_app
, &event
);
575 case
ASENSOR_TYPE_PROXIMITY:
576 App_extern_input_sensor_proximity(nit_app
, &event
);
583 // Check if we are exiting
.
584 if (mnit_java_app-
>destroyRequested
!= 0) {