4 * The Android-specific windows message processing methods.
\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
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
18 * The above copyright notice and this permission notice shall be included
\r
19 * in all copies or substantial portions of the Software.
\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
29 #include <GL/freeglut.h>
\r
30 #include "fg_internal.h"
\r
31 #include "egl/fg_window_egl.h"
\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
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
50 static struct touchscreen touchscreen;
\r
51 static unsigned char key_a2fg[256];
\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
77 #define EVENT_HANDLED 1
\r
78 #define EVENT_NOT_HANDLED 0
\r
81 * Initialize Android keycode to GLUT keycode mapping
\r
83 static void key_init() {
\r
84 memset(key_a2fg, 0, sizeof(key_a2fg));
\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
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
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
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
119 * Convert an Android key event to ASCII.
\r
121 static unsigned char key_ascii(struct android_app* app, AInputEvent* event) {
\r
122 int32_t code = AKeyEvent_getKeyCode(event);
\r
124 /* Handle a few special cases: */
\r
128 case AKEYCODE_FORWARD_DEL:
\r
130 case AKEYCODE_ESCAPE:
\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
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
146 /* LOGI("getUnicodeChar(%d) = %d ('%c')", AKeyEvent_getKeyCode(event), ascii, ascii); */
\r
147 (*vm)->DetachCurrentThread(vm);
\r
152 unsigned long fgPlatformSystemTime ( void )
\r
154 struct timeval now;
\r
155 gettimeofday( &now, NULL );
\r
156 return now.tv_usec/1000 + now.tv_sec*1000;
\r
160 * Does the magic required to relinquish the CPU until something interesting
\r
163 void fgPlatformSleepForEvents( long msec )
\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
175 * Process the next input event.
\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
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
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
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
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
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
228 LOGI("motion action=%d index=%d source=%d", action, pidx, source);
\r
229 int count = AMotionEvent_getPointerCount(event);
\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
237 float x = AMotionEvent_getX(event, 0);
\r
238 float y = AMotionEvent_getY(event, 0);
\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
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
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
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
289 return EVENT_HANDLED;
\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
308 return EVENT_HANDLED;
\r
311 /* Let Android handle other events (e.g. Back and Menu buttons) */
\r
312 return EVENT_NOT_HANDLED;
\r
316 * Process the next main command.
\r
318 void handle_cmd(struct android_app* app, int32_t cmd) {
\r
319 SFG_Window* window = fgWindowByHandle(app->window); /* may be NULL */
\r
321 /* App life cycle, in that order: */
\r
322 case APP_CMD_START:
\r
323 LOGI("handle_cmd: APP_CMD_START");
\r
325 case APP_CMD_RESUME:
\r
326 LOGI("handle_cmd: APP_CMD_RESUME");
\r
327 /* Cf. fgPlatformProcessSingleEvent */
\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 window->State.WorkMask |= GLUT_INIT_WORK;
\r
334 /* start|resume: glPlatformOpenWindow was waiting for Handle to be
\r
335 defined and will now continue processing */
\r
337 case APP_CMD_GAINED_FOCUS:
\r
338 LOGI("handle_cmd: APP_CMD_GAINED_FOCUS");
\r
340 case APP_CMD_WINDOW_RESIZED:
\r
341 LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED");
\r
342 if (window->Window.pContext.egl.Surface != EGL_NO_SURFACE)
\r
343 /* Make ProcessSingleEvent detect the new size, only available
\r
344 after the next SwapBuffer */
\r
345 glutPostRedisplay();
\r
348 case APP_CMD_SAVE_STATE: /* onSaveInstanceState */
\r
349 /* The system has asked us to save our current state, when it
\r
350 pauses the application without destroying it right after. */
\r
351 app->savedState = strdup("Detect me as non-NULL on next android_main");
\r
352 app->savedStateSize = strlen(app->savedState) + 1;
\r
353 LOGI("handle_cmd: APP_CMD_SAVE_STATE");
\r
355 case APP_CMD_PAUSE:
\r
356 LOGI("handle_cmd: APP_CMD_PAUSE");
\r
357 /* Cf. fgPlatformProcessSingleEvent */
\r
359 case APP_CMD_LOST_FOCUS:
\r
360 LOGI("handle_cmd: APP_CMD_LOST_FOCUS");
\r
362 case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */
\r
363 /* The application is being hidden, but may be restored */
\r
364 LOGI("handle_cmd: APP_CMD_TERM_WINDOW");
\r
365 fghPlatformCloseWindowEGL(window);
\r
366 fgDisplay.pDisplay.single_native_window = NULL;
\r
369 LOGI("handle_cmd: APP_CMD_STOP");
\r
371 case APP_CMD_DESTROY: /* Activity.onDestroy */
\r
372 LOGI("handle_cmd: APP_CMD_DESTROY");
\r
373 /* User closed the application for good, let's kill the window */
\r
375 /* Can't use fgWindowByHandle as app->window is NULL */
\r
376 SFG_Window* window = fgStructure.CurrentWindow;
\r
377 if (window != NULL) {
\r
378 fgDestroyWindow(window);
\r
380 LOGI("APP_CMD_DESTROY: No current window");
\r
383 /* glue has already set android_app->destroyRequested=1 */
\r
386 case APP_CMD_CONFIG_CHANGED:
\r
387 /* Handle rotation / orientation change */
\r
388 LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED");
\r
390 case APP_CMD_LOW_MEMORY:
\r
391 LOGI("handle_cmd: APP_CMD_LOW_MEMORY");
\r
394 LOGI("handle_cmd: unhandled cmd=%d", cmd);
\r
398 void fgPlatformOpenWindow( SFG_Window* window, const char* title,
\r
399 GLboolean positionUse, int x, int y,
\r
400 GLboolean sizeUse, int w, int h,
\r
401 GLboolean gameMode, GLboolean isSubWindow );
\r
403 void fgPlatformProcessSingleEvent ( void )
\r
405 /* When the screen is resized, the window handle still points to the
\r
406 old window until the next SwapBuffer, while it's crucial to set
\r
407 the size (onShape) correctly before the next onDisplay callback.
\r
408 Plus we don't know if the next SwapBuffer already occurred at the
\r
409 time we process the event (e.g. during onDisplay). */
\r
410 /* So we do the check each time rather than on event. */
\r
411 /* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android
\r
412 2.3, that next SwapBuffer is fake (but still necessary to get the
\r
414 SFG_Window* window = fgStructure.CurrentWindow;
\r
415 if (window != NULL && window->Window.Handle != NULL) {
\r
416 int32_t width = ANativeWindow_getWidth(window->Window.Handle);
\r
417 int32_t height = ANativeWindow_getHeight(window->Window.Handle);
\r
418 fghOnReshapeNotify(width,height);
\r
421 /* Read pending event. */
\r
424 struct android_poll_source* source;
\r
425 /* This is called "ProcessSingleEvent" but this means we'd only
\r
426 process ~60 (screen Hz) mouse events per second, plus other ports
\r
427 are processing all events already. So let's process all pending
\r
429 /* if ((ident=ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0) { */
\r
430 while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {
\r
431 /* Process this event. */
\r
432 if (source != NULL) {
\r
433 source->process(source->app, source);
\r
437 /* If we're not in RESUME state, Android paused us, so wait */
\r
438 struct android_app* app = fgDisplay.pDisplay.app;
\r
439 if (app->destroyRequested != 1 && app->activityState != APP_CMD_RESUME) {
\r
440 INVOKE_WCB(*window, Pause, ());
\r
443 while (app->destroyRequested != 1 && (app->activityState != APP_CMD_RESUME)) {
\r
444 if ((ident=ALooper_pollOnce(FOREVER, NULL, &events, (void**)&source)) >= 0) {
\r
445 /* Process this event. */
\r
446 if (source != NULL) {
\r
447 source->process(source->app, source);
\r
451 /* Coming back from a pause: */
\r
452 /* - Recreate window context and surface */
\r
453 /* - Call user-defined hook to restore resources (textures...) */
\r
454 /* - Exit pause loop */
\r
455 if (app->destroyRequested != 1) {
\r
456 /* Android is full-screen only, simplified call.. */
\r
457 /* Ideally we'd have a fgPlatformReopenWindow() */
\r
458 /* If we're hidden by a non-fullscreen or translucent activity,
\r
459 we'll be paused but not stopped, and keep the current
\r
460 surface; in which case fgPlatformOpenWindow will no-op. */
\r
461 fgPlatformOpenWindow(window, "", GL_FALSE, 0, 0, GL_FALSE, 0, 0, GL_FALSE, GL_FALSE);
\r
463 /* TODO: should there be a whole GLUT_INIT_WORK forced? probably...
\r
464 * Could queue that up in APP_CMD_TERM_WINDOW handler, but it'll
\r
465 * be not possible to ensure InitContext CB gets called before
\r
466 * Resume CB like that.. so maybe just force calling initContext CB
\r
467 * here is best. Or we could force work on the window in question..
\r
468 * 1) save old work mask, 2) set mask to init only, 3) call fgPlatformProcessWork directly
\r
469 * 4) set work mask back to the one saved in step 1.
\r
471 if (!FETCH_WCB(*window, InitContext))
\r
472 fgWarning("Resuming application, but no callback to reload context resources (glutInitContextFunc)");
\r
475 INVOKE_WCB(*window, Resume, ());
\r
479 void fgPlatformMainLoopPreliminaryWork ( void )
\r
481 LOGI("fgPlatformMainLoopPreliminaryWork\n");
\r
485 /* Make sure glue isn't stripped. */
\r
486 /* JNI entry points need to be bundled even when linking statically */
\r
491 /* Step through the work list */
\r
492 void fgPlatformProcessWork(SFG_Window *window)
\r
494 unsigned int workMask = window->State.WorkMask;
\r
495 /* Now clear it so that any callback generated by the actions below can set work again */
\r
496 window->State.WorkMask = 0;
\r
498 /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc
\r
499 * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when
\r
500 * they are opened, and work is done before displaying in the mainloop.
\r
502 if (workMask & GLUT_INIT_WORK)
\r
504 /* notify windowStatus/visibility */
\r
505 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
\r
507 /* Position callback, always at 0,0 */
\r
508 fghOnPositionNotify(window, 0, 0, GL_TRUE);
\r
510 /* Size gets notified on window creation with size detection in mainloop above
\r
511 * XXX CHECK: does this messages happen too early like on windows,
\r
512 * so client code cannot have registered a callback yet and the message
\r
513 * is thus never received by client?
\r
516 /* Call init context callback */
\r
517 INVOKE_WCB( *window, InitContext, ());
\r
519 /* Lastly, check if we have a display callback, error out if not
\r
520 * This is the right place to do it, as the redisplay will be
\r
521 * next right after we exit this function, so there is no more
\r
522 * opportunity for the user to register a callback for this window.
\r
524 if (!FETCH_WCB(*window, Display))
\r
525 fgError ( "ERROR: No display callback registered for window %d\n", window->ID );
\r
528 if (workMask & GLUT_FULL_SCREEN_WORK)
\r
529 fgPlatformFullScreenToggle( window );
\r
530 if (workMask & GLUT_POSITION_WORK)
\r
531 fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );
\r
532 if (workMask & GLUT_SIZE_WORK)
\r
533 fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );
\r
534 if (workMask & GLUT_ZORDER_WORK)
\r
536 if (window->State.DesiredZOrder < 0)
\r
537 fgPlatformPushWindow( window );
\r
539 fgPlatformPopWindow( window );
\r
542 if (workMask & GLUT_VISIBILITY_WORK)
\r
544 /* Visibility status of window should get updated in the window message handlers
\r
545 * For now, none of these functions called below do anything, so don't worry
\r
548 SFG_Window *win = window;
\r
549 switch (window->State.DesiredVisibility)
\r
551 case DesireHiddenState:
\r
552 fgPlatformHideWindow( window );
\r
554 case DesireIconicState:
\r
555 /* Call on top-level window */
\r
556 while (win->Parent)
\r
558 fgPlatformIconifyWindow( win );
\r
560 case DesireNormalState:
\r
561 fgPlatformShowWindow( window );
\r