1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 // this is translation of android_native_app_glue.c
19 module android.android_native_app_glue_impl;
20
21 version(Android):
22 extern(C):
23 @system:
24
25 import core.sys.posix.pthread;
26 import android.input, android.native_window, android.rect, android.log;
27 import android.configuration, android.looper, android.native_activity;
28 import core.stdc.stdlib;
29 import core.stdc.string;
30 import core.stdc.stdio;
31 import core.stdc.errno;
32 import core.sys.posix.sys.resource;
33 import core.sys.posix.unistd;
34
35 import android.android_native_app_glue;
36 import android.log;
37
38 static void free_saved_state(android_app* android_app) {
39 pthread_mutex_lock(&android_app.mutex);
40 if (android_app.savedState != null) {
41 free(android_app.savedState);
42 android_app.savedState = null;
43 android_app.savedStateSize = 0;
44 }
45 pthread_mutex_unlock(&android_app.mutex);
46 }
47
48 byte android_app_read_cmd(android_app* android_app) {
49 byte cmd;
50 if (read(android_app.msgread, &cmd, cmd.sizeof) == cmd.sizeof) {
51 switch (cmd) {
52 case APP_CMD_SAVE_STATE:
53 free_saved_state(android_app);
54 break;
55 default:
56 break;
57 }
58 return cmd;
59 } else {
60 LOGE("No data on command pipe!");
61 }
62 return -1;
63 }
64
65 static void print_cur_config(android_app* android_app) {
66
67 char[2] lang;
68 char[2] country;
69 AConfiguration_getLanguage(android_app.config, lang.ptr);
70 AConfiguration_getCountry(android_app.config, country.ptr);
71
72 LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " ~
73 "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " ~
74 "modetype=%d modenight=%d",
75 AConfiguration_getMcc(android_app.config),
76 AConfiguration_getMnc(android_app.config),
77 lang[0], lang[1], country[0], country[1],
78 AConfiguration_getOrientation(android_app.config),
79 AConfiguration_getTouchscreen(android_app.config),
80 AConfiguration_getDensity(android_app.config),
81 AConfiguration_getKeyboard(android_app.config),
82 AConfiguration_getNavigation(android_app.config),
83 AConfiguration_getKeysHidden(android_app.config),
84 AConfiguration_getNavHidden(android_app.config),
85 AConfiguration_getSdkVersion(android_app.config),
86 AConfiguration_getScreenSize(android_app.config),
87 AConfiguration_getScreenLong(android_app.config),
88 AConfiguration_getUiModeType(android_app.config),
89 AConfiguration_getUiModeNight(android_app.config));
90 }
91
92 void android_app_pre_exec_cmd(android_app* android_app, byte cmd) {
93 switch (cmd) {
94 case APP_CMD_INPUT_CHANGED:
95 LOGV("APP_CMD_INPUT_CHANGED\n");
96 pthread_mutex_lock(&android_app.mutex);
97 if (android_app.inputQueue != null) {
98 AInputQueue_detachLooper(android_app.inputQueue);
99 }
100 android_app.inputQueue = android_app.pendingInputQueue;
101 if (android_app.inputQueue != null) {
102 LOGV("Attaching input queue to looper");
103 AInputQueue_attachLooper(android_app.inputQueue,
104 android_app.looper, LOOPER_ID_INPUT, null,
105 &android_app.inputPollSource);
106 }
107 pthread_cond_broadcast(&android_app.cond);
108 pthread_mutex_unlock(&android_app.mutex);
109 break;
110
111 case APP_CMD_INIT_WINDOW:
112 LOGV("APP_CMD_INIT_WINDOW\n");
113 pthread_mutex_lock(&android_app.mutex);
114 android_app.window = android_app.pendingWindow;
115 pthread_cond_broadcast(&android_app.cond);
116 pthread_mutex_unlock(&android_app.mutex);
117 break;
118
119 case APP_CMD_TERM_WINDOW:
120 LOGV("APP_CMD_TERM_WINDOW\n");
121 pthread_cond_broadcast(&android_app.cond);
122 break;
123
124 case APP_CMD_RESUME:
125 case APP_CMD_START:
126 case APP_CMD_PAUSE:
127 case APP_CMD_STOP:
128 LOGV("activityState=%d\n", cmd);
129 pthread_mutex_lock(&android_app.mutex);
130 android_app.activityState = cmd;
131 pthread_cond_broadcast(&android_app.cond);
132 pthread_mutex_unlock(&android_app.mutex);
133 break;
134
135 case APP_CMD_CONFIG_CHANGED:
136 LOGV("APP_CMD_CONFIG_CHANGED\n");
137 AConfiguration_fromAssetManager(android_app.config,
138 android_app.activity.assetManager);
139 print_cur_config(android_app);
140 break;
141
142 case APP_CMD_DESTROY:
143 LOGV("APP_CMD_DESTROY\n");
144 android_app.destroyRequested = 1;
145 break;
146 default:
147 break;
148 }
149 }
150
151 void android_app_post_exec_cmd(android_app* android_app, byte cmd) {
152 switch (cmd) {
153 case APP_CMD_TERM_WINDOW:
154 LOGV("APP_CMD_TERM_WINDOW\n");
155 pthread_mutex_lock(&android_app.mutex);
156 android_app.window = null;
157 pthread_cond_broadcast(&android_app.cond);
158 pthread_mutex_unlock(&android_app.mutex);
159 break;
160
161 case APP_CMD_SAVE_STATE:
162 LOGV("APP_CMD_SAVE_STATE\n");
163 pthread_mutex_lock(&android_app.mutex);
164 android_app.stateSaved = 1;
165 pthread_cond_broadcast(&android_app.cond);
166 pthread_mutex_unlock(&android_app.mutex);
167 break;
168
169 case APP_CMD_RESUME:
170 free_saved_state(android_app);
171 break;
172 default:
173 break;
174 }
175 }
176
177 void app_dummy() {
178
179 }
180
181 static void android_app_destroy(android_app* android_app) {
182 LOGV("android_app_destroy!");
183 free_saved_state(android_app);
184 pthread_mutex_lock(&android_app.mutex);
185 if (android_app.inputQueue != null) {
186 AInputQueue_detachLooper(android_app.inputQueue);
187 }
188 AConfiguration_delete(android_app.config);
189 android_app.destroyed = 1;
190 pthread_cond_broadcast(&android_app.cond);
191 pthread_mutex_unlock(&android_app.mutex);
192 // Can't touch android_app object after this.
193 }
194
195 static void process_input(android_app* app, android_poll_source* source) {
196 AInputEvent* event = null;
197 while (AInputQueue_getEvent(app.inputQueue, &event) >= 0) {
198 LOGV("New input event: type=%d\n", AInputEvent_getType(event));
199 if (AInputQueue_preDispatchEvent(app.inputQueue, event)) {
200 continue;
201 }
202 int handled = 0;
203 if (app.onInputEvent != null) handled = app.onInputEvent(app, event);
204 AInputQueue_finishEvent(app.inputQueue, event, handled);
205 }
206 }
207
208 static void process_cmd(android_app* app, android_poll_source* source) {
209 byte cmd = android_app_read_cmd(app);
210 android_app_pre_exec_cmd(app, cmd);
211 if (app.onAppCmd != null) app.onAppCmd(app, cmd);
212 android_app_post_exec_cmd(app, cmd);
213 }
214
215 void* android_app_entry(void* param) {
216 android_app* android_app = cast(android_app*)param;
217
218 android_app.config = AConfiguration_new();
219 AConfiguration_fromAssetManager(android_app.config, android_app.activity.assetManager);
220
221 print_cur_config(android_app);
222
223 android_app.cmdPollSource.id = LOOPER_ID_MAIN;
224 android_app.cmdPollSource.app = android_app;
225 android_app.cmdPollSource.process = &process_cmd;
226 android_app.inputPollSource.id = LOOPER_ID_INPUT;
227 android_app.inputPollSource.app = android_app;
228 android_app.inputPollSource.process = &process_input;
229
230 ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
231 ALooper_addFd(looper, android_app.msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, null,
232 &android_app.cmdPollSource);
233 android_app.looper = looper;
234
235 pthread_mutex_lock(&android_app.mutex);
236 android_app.running = 1;
237 pthread_cond_broadcast(&android_app.cond);
238 pthread_mutex_unlock(&android_app.mutex);
239
240
241 import core.runtime;
242 rt_init();
243 android_main(android_app);
244 rt_term();
245
246 android_app_destroy(android_app);
247 return null;
248 }
249
250 // --------------------------------------------------------------------
251 // Native activity interaction (called from main thread)
252 // --------------------------------------------------------------------
253
254 static android_app* android_app_create(ANativeActivity* activity,
255 void* savedState, size_t savedStateSize) {
256 size_t sz = android_app.sizeof;
257 android_app* android_app = cast(android_app*)malloc(sz);
258 memset(android_app, 0, sz);
259 android_app.activity = activity;
260
261 pthread_mutex_init(&android_app.mutex, null);
262 pthread_cond_init(&android_app.cond, null);
263
264 if (savedState != null) {
265 android_app.savedState = malloc(savedStateSize);
266 android_app.savedStateSize = savedStateSize;
267 memcpy(android_app.savedState, savedState, savedStateSize);
268 }
269
270 int[2] msgpipe;
271 if (pipe(msgpipe)) {
272 LOGE("could not create pipe: %s", strerror(errno));
273 return null;
274 }
275 android_app.msgread = msgpipe[0];
276 android_app.msgwrite = msgpipe[1];
277
278 pthread_attr_t attr;
279 pthread_attr_init(&attr);
280 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
281 pthread_create(&android_app.thread, &attr, &android_app_entry, android_app);
282
283 // Wait for thread to start.
284 pthread_mutex_lock(&android_app.mutex);
285 while (!android_app.running) {
286 pthread_cond_wait(&android_app.cond, &android_app.mutex);
287 }
288 pthread_mutex_unlock(&android_app.mutex);
289
290 return android_app;
291 }
292
293 static void android_app_write_cmd(android_app* android_app, byte cmd) {
294 if (write(android_app.msgwrite, &cmd, cmd.sizeof) != cmd.sizeof) {
295 LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
296 }
297 }
298
299 static void android_app_set_input(android_app* android_app, AInputQueue* inputQueue) {
300 pthread_mutex_lock(&android_app.mutex);
301 android_app.pendingInputQueue = inputQueue;
302 android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
303 while (android_app.inputQueue != android_app.pendingInputQueue) {
304 pthread_cond_wait(&android_app.cond, &android_app.mutex);
305 }
306 pthread_mutex_unlock(&android_app.mutex);
307 }
308
309 static void android_app_set_window(android_app* android_app, ANativeWindow* window) {
310 pthread_mutex_lock(&android_app.mutex);
311 if (android_app.pendingWindow != null) {
312 android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
313 }
314 android_app.pendingWindow = window;
315 if (window != null) {
316 android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
317 }
318 while (android_app.window != android_app.pendingWindow) {
319 pthread_cond_wait(&android_app.cond, &android_app.mutex);
320 }
321 pthread_mutex_unlock(&android_app.mutex);
322 }
323
324 static void android_app_set_activity_state(android_app* android_app, byte cmd) {
325 pthread_mutex_lock(&android_app.mutex);
326 android_app_write_cmd(android_app, cmd);
327 while (android_app.activityState != cmd) {
328 pthread_cond_wait(&android_app.cond, &android_app.mutex);
329 }
330 pthread_mutex_unlock(&android_app.mutex);
331 }
332
333 static void android_app_free(android_app* android_app) {
334 pthread_mutex_lock(&android_app.mutex);
335 android_app_write_cmd(android_app, APP_CMD_DESTROY);
336 while (!android_app.destroyed) {
337 pthread_cond_wait(&android_app.cond, &android_app.mutex);
338 }
339 pthread_mutex_unlock(&android_app.mutex);
340
341 close(android_app.msgread);
342 close(android_app.msgwrite);
343 pthread_cond_destroy(&android_app.cond);
344 pthread_mutex_destroy(&android_app.mutex);
345 free(android_app);
346 }
347
348 static void onDestroy(ANativeActivity* activity) {
349 LOGV("Destroy: %p\n", activity);
350 android_app_free(cast(android_app*)activity.instance);
351 }
352
353 static void onStart(ANativeActivity* activity) {
354 LOGV("Start: %p\n", activity);
355 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_START);
356 }
357
358 static void onResume(ANativeActivity* activity) {
359 LOGV("Resume: %p\n", activity);
360 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_RESUME);
361 }
362
363 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
364 android_app* android_app = cast(android_app*)activity.instance;
365 void* savedState = null;
366
367 LOGV("SaveInstanceState: %p\n", activity);
368 pthread_mutex_lock(&android_app.mutex);
369 android_app.stateSaved = 0;
370 android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
371 while (!android_app.stateSaved) {
372 pthread_cond_wait(&android_app.cond, &android_app.mutex);
373 }
374
375 if (android_app.savedState != null) {
376 savedState = android_app.savedState;
377 *outLen = android_app.savedStateSize;
378 android_app.savedState = null;
379 android_app.savedStateSize = 0;
380 }
381
382 pthread_mutex_unlock(&android_app.mutex);
383
384 return savedState;
385 }
386
387 static void onPause(ANativeActivity* activity) {
388 LOGV("Pause: %p\n", activity);
389 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_PAUSE);
390 }
391
392 static void onStop(ANativeActivity* activity) {
393 LOGV("Stop: %p\n", activity);
394 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_STOP);
395 }
396
397 static void onConfigurationChanged(ANativeActivity* activity) {
398 android_app* android_app = cast(android_app*)activity.instance;
399 LOGV("ConfigurationChanged: %p\n", activity);
400 android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
401 }
402
403 static void onLowMemory(ANativeActivity* activity) {
404 android_app* android_app = cast(android_app*)activity.instance;
405 LOGV("LowMemory: %p\n", activity);
406 android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
407 }
408
409 static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
410 LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
411 android_app_write_cmd(cast(android_app*)activity.instance,
412 focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
413 }
414
415 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
416 LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
417 android_app_set_window(cast(android_app*)activity.instance, window);
418 }
419
420 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
421 LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
422 android_app_set_window(cast(android_app*)activity.instance, null);
423 }
424
425 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
426 LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
427 android_app_set_input(cast(android_app*)activity.instance, queue);
428 }
429
430 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
431 LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
432 android_app_set_input(cast(android_app*)activity.instance, null);
433 }
434
435 void ANativeActivity_onCreate(ANativeActivity* activity,
436 void* savedState, size_t savedStateSize) {
437 LOGV("Creating: %p\n", activity);
438 activity.callbacks.onDestroy = &onDestroy;
439 activity.callbacks.onStart = &onStart;
440 activity.callbacks.onResume = &onResume;
441 activity.callbacks.onSaveInstanceState = &onSaveInstanceState;
442 activity.callbacks.onPause = &onPause;
443 activity.callbacks.onStop = &onStop;
444 activity.callbacks.onConfigurationChanged = &onConfigurationChanged;
445 activity.callbacks.onLowMemory = &onLowMemory;
446 activity.callbacks.onWindowFocusChanged = &onWindowFocusChanged;
447 activity.callbacks.onNativeWindowCreated = &onNativeWindowCreated;
448 activity.callbacks.onNativeWindowDestroyed = &onNativeWindowDestroyed;
449 activity.callbacks.onInputQueueCreated = &onInputQueueCreated;
450 activity.callbacks.onInputQueueDestroyed = &onInputQueueDestroyed;
451
452 activity.instance = android_app_create(activity, savedState, savedStateSize);
453 }