Make Android work again - adapt to recent changes and fix use of GL-nonES function
[freeglut] / src / android / fg_main_android.c
1 /*\r
2  * fg_main_android.c\r
3  *\r
4  * The Android-specific windows message processing methods.\r
5  *\r
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.\r
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>\r
8  * Copied for Platform code by Evan Felix <karcaw at gmail.com>\r
9  * Copyright (C) 2012  Sylvain Beucler\r
10  *\r
11  * Permission is hereby granted, free of charge, to any person obtaining a\r
12  * copy of this software and associated documentation files (the "Software"),\r
13  * to deal in the Software without restriction, including without limitation\r
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
15  * and/or sell copies of the Software, and to permit persons to whom the\r
16  * Software is furnished to do so, subject to the following conditions:\r
17  *\r
18  * The above copyright notice and this permission notice shall be included\r
19  * in all copies or substantial portions of the Software.\r
20  *\r
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL\r
24  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
25  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
27  */\r
28 \r
29 #include <GL/freeglut.h>\r
30 #include "fg_internal.h"\r
31 #include "egl/fg_window_egl.h"\r
32 \r
33 #include <android/log.h>\r
34 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__))\r
35 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "FreeGLUT", __VA_ARGS__))\r
36 #include <android/native_app_glue/android_native_app_glue.h>\r
37 #include <android/keycodes.h>\r
38 \r
39 extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);\r
40 extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);\r
41 extern void fgPlatformFullScreenToggle( SFG_Window *win );\r
42 extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );\r
43 extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );\r
44 extern void fgPlatformPushWindow( SFG_Window *window );\r
45 extern void fgPlatformPopWindow( SFG_Window *window );\r
46 extern void fgPlatformHideWindow( SFG_Window *window );\r
47 extern void fgPlatformIconifyWindow( SFG_Window *window );\r
48 extern void fgPlatformShowWindow( SFG_Window *window );\r
49 \r
50 static struct touchscreen touchscreen;\r
51 static unsigned char key_a2fg[256];\r
52 \r
53 /* Cf. http://developer.android.com/reference/android/view/KeyEvent.html */\r
54 /* These codes are missing in <android/keycodes.h> */\r
55 /* Don't convert to enum, since it may conflict with future version of\r
56    that <android/keycodes.h> */\r
57 #define AKEYCODE_FORWARD_DEL 112\r
58 #define AKEYCODE_CTRL_LEFT 113\r
59 #define AKEYCODE_CTRL_RIGHT 114\r
60 #define AKEYCODE_MOVE_HOME 122\r
61 #define AKEYCODE_MOVE_END 123\r
62 #define AKEYCODE_INSERT 124\r
63 #define AKEYCODE_ESCAPE 127\r
64 #define AKEYCODE_F1 131\r
65 #define AKEYCODE_F2 132\r
66 #define AKEYCODE_F3 133\r
67 #define AKEYCODE_F4 134\r
68 #define AKEYCODE_F5 135\r
69 #define AKEYCODE_F6 136\r
70 #define AKEYCODE_F7 137\r
71 #define AKEYCODE_F8 138\r
72 #define AKEYCODE_F9 139\r
73 #define AKEYCODE_F10 140\r
74 #define AKEYCODE_F11 141\r
75 #define AKEYCODE_F12 142\r
76 \r
77 #define EVENT_HANDLED 1\r
78 #define EVENT_NOT_HANDLED 0\r
79 \r
80 /**\r
81  * Initialize Android keycode to GLUT keycode mapping\r
82  */\r
83 static void key_init() {\r
84   memset(key_a2fg, 0, sizeof(key_a2fg));\r
85 \r
86   key_a2fg[AKEYCODE_F1]  = GLUT_KEY_F1;\r
87   key_a2fg[AKEYCODE_F2]  = GLUT_KEY_F2;\r
88   key_a2fg[AKEYCODE_F3]  = GLUT_KEY_F3;\r
89   key_a2fg[AKEYCODE_F4]  = GLUT_KEY_F4;\r
90   key_a2fg[AKEYCODE_F5]  = GLUT_KEY_F5;\r
91   key_a2fg[AKEYCODE_F6]  = GLUT_KEY_F6;\r
92   key_a2fg[AKEYCODE_F7]  = GLUT_KEY_F7;\r
93   key_a2fg[AKEYCODE_F8]  = GLUT_KEY_F8;\r
94   key_a2fg[AKEYCODE_F9]  = GLUT_KEY_F9;\r
95   key_a2fg[AKEYCODE_F10] = GLUT_KEY_F10;\r
96   key_a2fg[AKEYCODE_F11] = GLUT_KEY_F11;\r
97   key_a2fg[AKEYCODE_F12] = GLUT_KEY_F12;\r
98 \r
99   key_a2fg[AKEYCODE_PAGE_UP]   = GLUT_KEY_PAGE_UP;\r
100   key_a2fg[AKEYCODE_PAGE_DOWN] = GLUT_KEY_PAGE_DOWN;\r
101   key_a2fg[AKEYCODE_MOVE_HOME] = GLUT_KEY_HOME;\r
102   key_a2fg[AKEYCODE_MOVE_END]  = GLUT_KEY_END;\r
103   key_a2fg[AKEYCODE_INSERT]    = GLUT_KEY_INSERT;\r
104 \r
105   key_a2fg[AKEYCODE_DPAD_UP]    = GLUT_KEY_UP;\r
106   key_a2fg[AKEYCODE_DPAD_DOWN]  = GLUT_KEY_DOWN;\r
107   key_a2fg[AKEYCODE_DPAD_LEFT]  = GLUT_KEY_LEFT;\r
108   key_a2fg[AKEYCODE_DPAD_RIGHT] = GLUT_KEY_RIGHT;\r
109 \r
110   key_a2fg[AKEYCODE_ALT_LEFT]    = GLUT_KEY_ALT_L;\r
111   key_a2fg[AKEYCODE_ALT_RIGHT]   = GLUT_KEY_ALT_R;\r
112   key_a2fg[AKEYCODE_SHIFT_LEFT]  = GLUT_KEY_SHIFT_L;\r
113   key_a2fg[AKEYCODE_SHIFT_RIGHT] = GLUT_KEY_SHIFT_R;\r
114   key_a2fg[AKEYCODE_CTRL_LEFT]   = GLUT_KEY_CTRL_L;\r
115   key_a2fg[AKEYCODE_CTRL_RIGHT]  = GLUT_KEY_CTRL_R;\r
116 }\r
117 \r
118 /**\r
119  * Convert an Android key event to ASCII.\r
120  */\r
121 static unsigned char key_ascii(struct android_app* app, AInputEvent* event) {\r
122   int32_t code = AKeyEvent_getKeyCode(event);\r
123 \r
124   /* Handle a few special cases: */\r
125   switch (code) {\r
126   case AKEYCODE_DEL:\r
127     return 8;\r
128   case AKEYCODE_FORWARD_DEL:\r
129     return 127;\r
130   case AKEYCODE_ESCAPE:\r
131     return 27;\r
132   }\r
133 \r
134   /* Get usable JNI context */\r
135   JNIEnv* env = app->activity->env;\r
136   JavaVM* vm = app->activity->vm;\r
137   (*vm)->AttachCurrentThread(vm, &env, NULL);\r
138 \r
139   jclass KeyEventClass = (*env)->FindClass(env, "android/view/KeyEvent");\r
140   jmethodID KeyEventConstructor = (*env)->GetMethodID(env, KeyEventClass, "<init>", "(II)V");\r
141   jobject keyEvent = (*env)->NewObject(env, KeyEventClass, KeyEventConstructor,\r
142                                        AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event));\r
143   jmethodID KeyEvent_getUnicodeChar = (*env)->GetMethodID(env, KeyEventClass, "getUnicodeChar", "(I)I");\r
144   int ascii = (*env)->CallIntMethod(env, keyEvent, KeyEvent_getUnicodeChar, AKeyEvent_getMetaState(event));\r
145 \r
146   /* LOGI("getUnicodeChar(%d) = %d ('%c')", AKeyEvent_getKeyCode(event), ascii, ascii); */\r
147   (*vm)->DetachCurrentThread(vm);\r
148 \r
149   return ascii;\r
150 }\r
151 \r
152 unsigned long fgPlatformSystemTime ( void )\r
153 {\r
154   struct timeval now;\r
155   gettimeofday( &now, NULL );\r
156   return now.tv_usec/1000 + now.tv_sec*1000;\r
157 }\r
158 \r
159 /*\r
160  * Does the magic required to relinquish the CPU until something interesting\r
161  * happens.\r
162  */\r
163 void fgPlatformSleepForEvents( long msec )\r
164 {\r
165     /* Android's NativeActivity relies on a Looper/ALooper object to\r
166        notify about events.  The Looper object is plugged on two\r
167        internal pipe(2)s to detect system and input events.  Sadly you\r
168        can only ask the Looper for an event, not just ask whether\r
169        there is a pending event (and process it later).  Consequently,\r
170        short of redesigning NativeActivity, we cannot\r
171        SleepForEvents. */\r
172 }\r
173 \r
174 /**\r
175  * Process the next input event.\r
176  */\r
177 int32_t handle_input(struct android_app* app, AInputEvent* event) {\r
178   SFG_Window* window = fgWindowByHandle(app->window);\r
179   if (window == NULL)\r
180     return EVENT_NOT_HANDLED;\r
181 \r
182   /* FIXME: in Android, when a key is repeated, down\r
183      and up events happen most often at the exact same time.  This\r
184      makes it impossible to animate based on key press time. */\r
185   /* e.g. down/up/wait/down/up rather than down/wait/down/wait/up */ \r
186   /* This looks like a bug in the Android virtual keyboard system :/\r
187      Real buttons such as the Back button appear to work correctly\r
188      (series of down events with proper getRepeatCount value). */\r
189   \r
190   if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {\r
191     /* LOGI("action: %d", AKeyEvent_getAction(event)); */\r
192     /* LOGI("keycode: %d", code); */\r
193     int32_t code = AKeyEvent_getKeyCode(event);\r
194 \r
195     if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) {\r
196       int32_t keypress = 0;\r
197       unsigned char ascii = 0;\r
198       if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) {\r
199         INVOKE_WCB(*window, Special, (keypress, window->State.MouseX, window->State.MouseY));\r
200         return EVENT_HANDLED;\r
201       } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) {\r
202         INVOKE_WCB(*window, Keyboard, (ascii, window->State.MouseX, window->State.MouseY));\r
203         return EVENT_HANDLED;\r
204       }\r
205     }\r
206     else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) {\r
207       int32_t keypress = 0;\r
208       unsigned char ascii = 0;\r
209       if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) {\r
210         INVOKE_WCB(*window, SpecialUp, (keypress, window->State.MouseX, window->State.MouseY));\r
211         return EVENT_HANDLED;\r
212       } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) {\r
213         INVOKE_WCB(*window, KeyboardUp, (ascii, window->State.MouseX, window->State.MouseY));\r
214         return EVENT_HANDLED;\r
215       }\r
216     }\r
217   }\r
218 \r
219   int32_t source = AInputEvent_getSource(event);\r
220   if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION\r
221       && source == AINPUT_SOURCE_TOUCHSCREEN) {\r
222     int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;\r
223     /* Pointer ID for clicks */\r
224     int32_t pidx = AMotionEvent_getAction(event) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;\r
225     /* TODO: Handle multi-touch; also handle multiple sources/devices */\r
226     /* cf. http://sourceforge.net/mailarchive/forum.php?thread_name=20120518071314.GA28061%40perso.beuc.net&forum_name=freeglut-developer */\r
227     if (0) {\r
228       LOGI("motion action=%d index=%d source=%d", action, pidx, source);\r
229       int count = AMotionEvent_getPointerCount(event);\r
230       int i;\r
231       for (i = 0; i < count; i++) {\r
232         LOGI("multi(%d): %.01f,%.01f",\r
233              AMotionEvent_getPointerId(event, i),\r
234              AMotionEvent_getX(event, i), AMotionEvent_getY(event, i));\r
235       }\r
236     }\r
237     float x = AMotionEvent_getX(event, 0);\r
238     float y = AMotionEvent_getY(event, 0);\r
239 \r
240     /* Virtual arrows PAD */\r
241     /* Don't interfere with existing mouse move event */\r
242     if (!touchscreen.in_mmotion) {\r
243       struct vpad_state prev_vpad = touchscreen.vpad;\r
244       touchscreen.vpad.left = touchscreen.vpad.right\r
245         = touchscreen.vpad.up = touchscreen.vpad.down = false;\r
246 \r
247       /* int32_t width = ANativeWindow_getWidth(window->Window.Handle); */\r
248       int32_t height = ANativeWindow_getHeight(window->Window.Handle);\r
249       if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_MOVE) {\r
250         if ((x > 0 && x < 100) && (y > (height - 100) && y < height))\r
251           touchscreen.vpad.left = true;\r
252         if ((x > 200 && x < 300) && (y > (height - 100) && y < height))\r
253           touchscreen.vpad.right = true;\r
254         if ((x > 100 && x < 200) && (y > (height - 100) && y < height))\r
255           touchscreen.vpad.down = true;\r
256         if ((x > 100 && x < 200) && (y > (height - 200) && y < (height - 100)))\r
257           touchscreen.vpad.up = true;\r
258       }\r
259       if (action == AMOTION_EVENT_ACTION_DOWN && \r
260           (touchscreen.vpad.left || touchscreen.vpad.right || touchscreen.vpad.down || touchscreen.vpad.up))\r
261         touchscreen.vpad.on = true;\r
262       if (action == AMOTION_EVENT_ACTION_UP)\r
263         touchscreen.vpad.on = false;\r
264       if (prev_vpad.left != touchscreen.vpad.left\r
265           || prev_vpad.right != touchscreen.vpad.right\r
266           || prev_vpad.up != touchscreen.vpad.up\r
267           || prev_vpad.down != touchscreen.vpad.down\r
268           || prev_vpad.on != touchscreen.vpad.on) {\r
269         if (FETCH_WCB(*window, Special)) {\r
270           if (prev_vpad.left == false && touchscreen.vpad.left == true)\r
271             INVOKE_WCB(*window, Special, (GLUT_KEY_LEFT, x, y));\r
272           else if (prev_vpad.right == false && touchscreen.vpad.right == true)\r
273             INVOKE_WCB(*window, Special, (GLUT_KEY_RIGHT, x, y));\r
274           else if (prev_vpad.up == false && touchscreen.vpad.up == true)\r
275             INVOKE_WCB(*window, Special, (GLUT_KEY_UP, x, y));\r
276           else if (prev_vpad.down == false && touchscreen.vpad.down == true)\r
277             INVOKE_WCB(*window, Special, (GLUT_KEY_DOWN, x, y));\r
278         }\r
279         if (FETCH_WCB(*window, SpecialUp)) {\r
280           if (prev_vpad.left == true && touchscreen.vpad.left == false)\r
281             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_LEFT, x, y));\r
282           if (prev_vpad.right == true && touchscreen.vpad.right == false)\r
283             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_RIGHT, x, y));\r
284           if (prev_vpad.up == true && touchscreen.vpad.up == false)\r
285             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_UP, x, y));\r
286           if (prev_vpad.down == true && touchscreen.vpad.down == false)\r
287             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_DOWN, x, y));\r
288         }\r
289         return EVENT_HANDLED;\r
290       }\r
291     }\r
292     \r
293     /* Normal mouse events */\r
294     if (!touchscreen.vpad.on) {\r
295       window->State.MouseX = x;\r
296       window->State.MouseY = y;\r
297       if (action == AMOTION_EVENT_ACTION_DOWN && FETCH_WCB(*window, Mouse)) {\r
298         touchscreen.in_mmotion = true;\r
299         INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_DOWN, x, y));\r
300       } else if (action == AMOTION_EVENT_ACTION_UP && FETCH_WCB(*window, Mouse)) {\r
301         touchscreen.in_mmotion = false;\r
302         INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_UP, x, y));\r
303       } else if (action == AMOTION_EVENT_ACTION_MOVE && FETCH_WCB(*window, Motion)) {\r
304         INVOKE_WCB(*window, Motion, (x, y));\r
305       }\r
306     }\r
307     \r
308     return EVENT_HANDLED;\r
309   }\r
310 \r
311   /* Let Android handle other events (e.g. Back and Menu buttons) */\r
312   return EVENT_NOT_HANDLED;\r
313 }\r
314 \r
315 /**\r
316  * Process the next main command.\r
317  */\r
318 void handle_cmd(struct android_app* app, int32_t cmd) {\r
319   SFG_Window* window = fgWindowByHandle(app->window);  /* may be NULL */\r
320   switch (cmd) {\r
321   /* App life cycle, in that order: */\r
322   case APP_CMD_START:\r
323     LOGI("handle_cmd: APP_CMD_START");\r
324     break;\r
325   case APP_CMD_RESUME:\r
326     LOGI("handle_cmd: APP_CMD_RESUME");\r
327     /* Cf. fgPlatformProcessSingleEvent */\r
328     break;\r
329   case APP_CMD_INIT_WINDOW: /* surfaceCreated */\r
330     /* The window is being shown, get it ready. */\r
331     LOGI("handle_cmd: APP_CMD_INIT_WINDOW %p", app->window);\r
332     fgDisplay.pDisplay.single_native_window = app->window;\r
333     /* start|resume: glPlatformOpenWindow was waiting for Handle to be\r
334        defined and will now continue processing */\r
335     break;\r
336   case APP_CMD_GAINED_FOCUS:\r
337     LOGI("handle_cmd: APP_CMD_GAINED_FOCUS");\r
338     break;\r
339   case APP_CMD_WINDOW_RESIZED:\r
340     LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED");\r
341     if (window->Window.pContext.egl.Surface != EGL_NO_SURFACE)\r
342       /* Make ProcessSingleEvent detect the new size, only available\r
343          after the next SwapBuffer */\r
344       glutPostRedisplay();\r
345     break;\r
346 \r
347   case APP_CMD_SAVE_STATE: /* onSaveInstanceState */\r
348     /* The system has asked us to save our current state, when it\r
349        pauses the application without destroying it right after. */\r
350     app->savedState = strdup("Detect me as non-NULL on next android_main");\r
351     app->savedStateSize = strlen(app->savedState) + 1;\r
352     LOGI("handle_cmd: APP_CMD_SAVE_STATE");\r
353     break;\r
354   case APP_CMD_PAUSE:\r
355     LOGI("handle_cmd: APP_CMD_PAUSE");\r
356     /* Cf. fgPlatformProcessSingleEvent */\r
357     break;\r
358   case APP_CMD_LOST_FOCUS:\r
359     LOGI("handle_cmd: APP_CMD_LOST_FOCUS");\r
360     break;\r
361   case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */\r
362     /* The application is being hidden, but may be restored */\r
363     LOGI("handle_cmd: APP_CMD_TERM_WINDOW");\r
364     fghPlatformCloseWindowEGL(window);\r
365     fgDisplay.pDisplay.single_native_window = NULL;\r
366     break;\r
367   case APP_CMD_STOP:\r
368     LOGI("handle_cmd: APP_CMD_STOP");\r
369     break;\r
370   case APP_CMD_DESTROY: /* Activity.onDestroy */\r
371     LOGI("handle_cmd: APP_CMD_DESTROY");\r
372     /* User closed the application for good, let's kill the window */\r
373     {\r
374       /* Can't use fgWindowByHandle as app->window is NULL */\r
375       SFG_Window* window = fgStructure.CurrentWindow;\r
376       if (window != NULL) {\r
377         fgDestroyWindow(window);\r
378       } else {\r
379         LOGI("APP_CMD_DESTROY: No current window");\r
380       }\r
381     }\r
382     /* glue has already set android_app->destroyRequested=1 */\r
383     break;\r
384 \r
385   case APP_CMD_CONFIG_CHANGED:\r
386     /* Handle rotation / orientation change */\r
387     LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED");\r
388     break;\r
389   case APP_CMD_LOW_MEMORY:\r
390     LOGI("handle_cmd: APP_CMD_LOW_MEMORY");\r
391     break;\r
392   default:\r
393     LOGI("handle_cmd: unhandled cmd=%d", cmd);\r
394   }\r
395 }\r
396 \r
397 void fgPlatformOpenWindow( SFG_Window* window, const char* title,\r
398                            GLboolean positionUse, int x, int y,\r
399                            GLboolean sizeUse, int w, int h,\r
400                            GLboolean gameMode, GLboolean isSubWindow );\r
401 \r
402 void fgPlatformProcessSingleEvent ( void )\r
403 {\r
404   /* When the screen is resized, the window handle still points to the\r
405      old window until the next SwapBuffer, while it's crucial to set\r
406      the size (onShape) correctly before the next onDisplay callback.\r
407      Plus we don't know if the next SwapBuffer already occurred at the\r
408      time we process the event (e.g. during onDisplay). */\r
409   /* So we do the check each time rather than on event. */\r
410   /* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android\r
411      2.3, that next SwapBuffer is fake (but still necessary to get the\r
412      new size). */\r
413   SFG_Window* window = fgStructure.CurrentWindow;\r
414   if (window != NULL && window->Window.Handle != NULL) {\r
415     int32_t width = ANativeWindow_getWidth(window->Window.Handle);\r
416     int32_t height = ANativeWindow_getHeight(window->Window.Handle);\r
417     fghOnReshapeNotify(window,width,height,GL_FALSE);\r
418   }\r
419 \r
420   /* Read pending event. */\r
421   int ident;\r
422   int events;\r
423   struct android_poll_source* source;\r
424   /* This is called "ProcessSingleEvent" but this means we'd only\r
425      process ~60 (screen Hz) mouse events per second, plus other ports\r
426      are processing all events already.  So let's process all pending\r
427      events. */\r
428   /* if ((ident=ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0) { */\r
429   while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {\r
430     /* Process this event. */\r
431     if (source != NULL) {\r
432       source->process(source->app, source);\r
433     }\r
434   }\r
435 \r
436   /* If we're not in RESUME state, Android paused us, so wait */\r
437   struct android_app* app = fgDisplay.pDisplay.app;\r
438   if (app->destroyRequested != 1 && app->activityState != APP_CMD_RESUME) {\r
439       INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_PAUSE));\r
440 \r
441     int FOREVER = -1;\r
442     while (app->destroyRequested != 1 && (app->activityState != APP_CMD_RESUME)) {\r
443       if ((ident=ALooper_pollOnce(FOREVER, NULL, &events, (void**)&source)) >= 0) {\r
444         /* Process this event. */\r
445         if (source != NULL) {\r
446           source->process(source->app, source);\r
447         }\r
448       }\r
449     }\r
450     /* Coming back from a pause: */\r
451     /* - Recreate window context and surface */\r
452     /* - Call user-defined hook to restore resources (textures...) */\r
453     /* - Exit pause loop */\r
454     if (app->destroyRequested != 1) {\r
455       /* Android is full-screen only, simplified call.. */\r
456       /* Ideally we'd have a fgPlatformReopenWindow() */\r
457       /* If we're hidden by a non-fullscreen or translucent activity,\r
458          we'll be paused but not stopped, and keep the current\r
459          surface; in which case fgPlatformOpenWindow will no-op. */\r
460       fgPlatformOpenWindow(window, "", GL_FALSE, 0, 0, GL_FALSE, 0, 0, GL_FALSE, GL_FALSE);\r
461 \r
462       /* TODO: should there be a whole GLUT_INIT_WORK forced? probably...\r
463        * Could queue that up in APP_CMD_TERM_WINDOW handler, but it'll\r
464        * be not possible to ensure InitContext CB gets called before\r
465        * Resume CB like that.. so maybe just force calling initContext CB\r
466        * here is best. Or we could force work on the window in question..\r
467        * 1) save old work mask, 2) set mask to init only, 3) call fgProcessWork directly\r
468        * 4) set work mask back to the one saved in step 1.\r
469        */\r
470       if (!FETCH_WCB(*window, InitContext))\r
471           fgWarning("Resuming application, but no callback to reload context resources (glutInitContextFunc)");\r
472     }\r
473 \r
474     INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_RESUME));\r
475   }\r
476 }\r
477 \r
478 void fgPlatformMainLoopPreliminaryWork ( void )\r
479 {\r
480   LOGI("fgPlatformMainLoopPreliminaryWork\n");\r
481 \r
482   key_init();\r
483 \r
484   /* Make sure glue isn't stripped. */\r
485   /* JNI entry points need to be bundled even when linking statically */\r
486   app_dummy();\r
487 }\r
488 \r
489 \r
490 /* deal with work list items */\r
491 void fgPlatformInitWork(SFG_Window* window)\r
492 {\r
493     /* notify windowStatus/visibility */\r
494     INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );\r
495 \r
496     /* Position callback, always at 0,0 */\r
497     fghOnPositionNotify(window, 0, 0, GL_TRUE);\r
498 \r
499     /* Size gets notified on window creation with size detection in mainloop above\r
500      * XXX CHECK: does this messages happen too early like on windows,\r
501      * so client code cannot have registered a callback yet and the message\r
502      * is thus never received by client?\r
503      */\r
504 }\r
505 \r
506 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)\r
507 {\r
508     if (workMask & GLUT_FULL_SCREEN_WORK)\r
509         fgPlatformFullScreenToggle( window );\r
510     if (workMask & GLUT_POSITION_WORK)\r
511         fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );\r
512     if (workMask & GLUT_SIZE_WORK)\r
513         fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );\r
514     if (workMask & GLUT_ZORDER_WORK)\r
515     {\r
516         if (window->State.DesiredZOrder < 0)\r
517             fgPlatformPushWindow( window );\r
518         else\r
519             fgPlatformPopWindow( window );\r
520     }\r
521 }\r
522 \r
523 void fgPlatformVisibilityWork(SFG_Window* window)\r
524 {\r
525     /* Visibility status of window should get updated in the window message handlers\r
526      * For now, none of these functions called below do anything, so don't worry\r
527      * about it\r
528      */\r
529     SFG_Window *win = window;\r
530     switch (window->State.DesiredVisibility)\r
531     {\r
532     case DesireHiddenState:\r
533         fgPlatformHideWindow( window );\r
534         break;\r
535     case DesireIconicState:\r
536         /* Call on top-level window */\r
537         while (win->Parent)\r
538             win = win->Parent;\r
539         fgPlatformIconifyWindow( win );\r
540         break;\r
541     case DesireNormalState:\r
542         fgPlatformShowWindow( window );\r
543         break;\r
544     }\r
545 }\r
546 \r