mnit: does not depends on opengles1
[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 import mnit::opengles1
23
24 in "C header" `{
25 #include <jni.h>
26 #include <errno.h>
27 #include <android/log.h>
28 #include <android_native_app_glue.h>
29
30 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "mnit", __VA_ARGS__))
31 #ifdef DEBUG
32 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mnit", __VA_ARGS__))
33 #else
34 #define LOGI(...) (void)0
35 #endif
36 `}
37
38 in "C" `{
39 #include <EGL/egl.h>
40 #include <GLES/gl.h>
41 #define GL_GLEXT_PROTOTYPES 1
42 #include <GLES/glext.h>
43 #include <errno.h>
44
45 extern EGLDisplay mnit_display;
46 extern EGLSurface mnit_surface;
47 extern EGLContext mnit_context;
48 extern EGLConfig mnit_config;
49 extern int32_t mnit_width;
50 extern int32_t mnit_height;
51 extern float mnit_zoom;
52
53 //int mnit_orientation_changed;
54 float mnit_zoom;
55
56 /* Handle inputs from the Android platform and sort them before
57 sending them in the Nit App */
58 static int32_t mnit_handle_input(struct android_app* app, AInputEvent* event) {
59 App nit_app = app->userData;
60 LOGI("handle input %i", (int)pthread_self());
61 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
62 LOGI("key");
63 return App_extern_input_key(nit_app, event);
64 }
65 else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
66 LOGI("motion");
67 return App_extern_input_motion(nit_app, event);
68 }
69
70 return 0;
71 }
72 `}
73
74
75 extern class InnerAndroidMotionEvent in "C" `{AInputEvent *`}
76 super Pointer
77 private fun pointers_count: Int is extern `{
78 return AMotionEvent_getPointerCount(recv);
79 `}
80 private fun just_went_down: Bool is extern `{
81 return (AMotionEvent_getAction(recv) & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN;
82 `}
83 private fun edge: Int is extern `{
84 return AMotionEvent_getEdgeFlags(recv);
85 `}
86 private fun index_down_pointer: Int is extern `{
87 int a = AMotionEvent_getAction(recv);
88 if ((a & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN)
89 return (a & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
90 else return -1;
91 `}
92
93 private fun action: AMotionEventAction `{ return AMotionEvent_getAction(recv); `}
94 end
95
96 extern class AMotionEventAction `{ int32_t `}
97 protected fun action: Int `{ return recv & AMOTION_EVENT_ACTION_MASK; `}
98 fun is_down: Bool do return action == 0
99 fun is_up: Bool do return action == 1
100 fun is_move: Bool do return action == 2
101 fun is_cancel: Bool do return action == 3
102 fun is_outside: Bool do return action == 4
103 fun is_pointer_down: Bool do return action == 5
104 fun is_pointer_up: Bool do return action == 6
105 end
106
107 interface AndroidInputEvent
108 super InputEvent
109 end
110
111 class AndroidMotionEvent
112 super AndroidInputEvent
113 super MotionEvent
114
115 private init(ie: InnerAndroidMotionEvent) do inner_event = ie
116 private var inner_event: InnerAndroidMotionEvent
117
118 private var pointers_cache: nullable Array[AndroidPointerEvent] = null
119 fun pointers: Array[AndroidPointerEvent]
120 do
121 if pointers_cache != null then
122 return pointers_cache.as(not null)
123 else
124 var pointers = new Array[AndroidPointerEvent]
125 var pointers_count = inner_event.pointers_count
126 for i in [0 .. pointers_count [do
127 var pointer_event = new AndroidPointerEvent(self, i)
128 pointers.add(pointer_event)
129 end
130 pointers_cache = pointers
131 return pointers
132 end
133 end
134
135 redef fun just_went_down: Bool do return inner_event.just_went_down
136 fun edge: Int do return inner_event.edge
137
138 redef fun down_pointer: nullable AndroidPointerEvent
139 do
140 var i = inner_event.index_down_pointer
141 if i > 0 then
142 return pointers[i]
143 else
144 return null
145 end
146 end
147 end
148
149 class AndroidPointerEvent
150 super PointerEvent
151 super AndroidInputEvent
152
153 protected var motion_event: AndroidMotionEvent
154 protected var pointer_id: Int
155
156 redef fun x: Float do return extern_x(motion_event.inner_event, pointer_id)
157 private fun extern_x(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
158 return ((int) AMotionEvent_getX(motion_event, pointer_id) * mnit_zoom);
159 `}
160
161 redef fun y: Float do return extern_y(motion_event.inner_event, pointer_id)
162 private fun extern_y(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
163 return ((int) AMotionEvent_getY(motion_event, pointer_id) * mnit_zoom);
164 `}
165
166 fun pressure: Float do return extern_pressure(motion_event.inner_event, pointer_id)
167 private fun extern_pressure(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{
168 return AMotionEvent_getPressure(motion_event, pointer_id);
169 `}
170
171 redef fun pressed
172 do
173 var action = motion_event.inner_event.action
174 return action.is_down or action.is_move
175 end
176
177 redef fun depressed do return not pressed
178 end
179
180 extern class AndroidKeyEvent in "C" `{AInputEvent *`}
181 super KeyEvent
182 super AndroidInputEvent
183
184 fun action: Int is extern `{
185 return AKeyEvent_getAction(recv);
186 `}
187 redef fun is_down: Bool do return action == 0
188 redef fun is_up: Bool do return action == 1
189
190 fun key_code: Int is extern `{
191 return AKeyEvent_getKeyCode(recv);
192 `}
193
194 fun key_char: Char is extern `{
195 int code = AKeyEvent_getKeyCode(recv);
196 if (code >= AKEYCODE_0 && code <= AKEYCODE_9)
197 return '0'+code-AKEYCODE_0;
198 if (code >= AKEYCODE_A && code <= AKEYCODE_Z)
199 return 'a'+code-AKEYCODE_A;
200 return 0;
201 `}
202
203 fun is_back_key: Bool do return key_code == 4
204 fun is_menu_key: Bool do return key_code == 82
205 fun is_search_key: Bool do return key_code == 84
206 end
207
208 redef class App
209 redef type IE: AndroidInputEvent
210 redef type D: Opengles1Display
211
212 redef fun init_window
213 do
214 set_as_input_handler native_app_glue
215 display = new Opengles1Display
216
217 super
218 end
219
220 private fun set_as_input_handler(app_glue: NativeAppGlue) import extern_input_key, extern_input_motion `{
221 app_glue->onInputEvent = mnit_handle_input;
222 `}
223
224 redef fun full_frame do if not paused then super
225
226 # these are used as a callback from native to type incoming events
227 private fun extern_input_key(event: AndroidKeyEvent): Bool
228 do
229 return input(event)
230 end
231 private fun extern_input_motion(event: InnerAndroidMotionEvent): Bool
232 do
233 var ie = new AndroidMotionEvent(event)
234 var handled = input(ie)
235
236 if not handled then
237 for pe in ie.pointers do
238 input(pe)
239 end
240 end
241
242 return handled
243 end
244
245 redef fun generate_input do poll_looper 0
246 end