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 /* start|resume: glPlatformOpenWindow was waiting for Handle to be
\r
334 defined and will now continue processing */
\r
336 case APP_CMD_GAINED_FOCUS:
\r
337 LOGI("handle_cmd: APP_CMD_GAINED_FOCUS");
\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
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
354 case APP_CMD_PAUSE:
\r
355 LOGI("handle_cmd: APP_CMD_PAUSE");
\r
356 /* Cf. fgPlatformProcessSingleEvent */
\r
358 case APP_CMD_LOST_FOCUS:
\r
359 LOGI("handle_cmd: APP_CMD_LOST_FOCUS");
\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
368 LOGI("handle_cmd: APP_CMD_STOP");
\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
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
379 LOGI("APP_CMD_DESTROY: No current window");
\r
382 /* glue has already set android_app->destroyRequested=1 */
\r
385 case APP_CMD_CONFIG_CHANGED:
\r
386 /* Handle rotation / orientation change */
\r
387 LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED");
\r
389 case APP_CMD_LOW_MEMORY:
\r
390 LOGI("handle_cmd: APP_CMD_LOW_MEMORY");
\r
393 LOGI("handle_cmd: unhandled cmd=%d", cmd);
\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
402 void fgPlatformProcessSingleEvent ( void )
\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
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
420 /* Read pending event. */
\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
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
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
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
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
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
470 if (!FETCH_WCB(*window, InitContext))
\r
471 fgWarning("Resuming application, but no callback to reload context resources (glutInitContextFunc)");
\r
474 INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_RESUME));
\r
478 void fgPlatformMainLoopPreliminaryWork ( void )
\r
480 LOGI("fgPlatformMainLoopPreliminaryWork\n");
\r
484 /* Make sure glue isn't stripped. */
\r
485 /* JNI entry points need to be bundled even when linking statically */
\r
490 /* deal with work list items */
\r
491 void fgPlatformInitWork(SFG_Window* window)
\r
493 /* notify windowStatus/visibility */
\r
494 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
\r
496 /* Position callback, always at 0,0 */
\r
497 fghOnPositionNotify(window, 0, 0, GL_TRUE);
\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
506 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
\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
516 if (window->State.DesiredZOrder < 0)
\r
517 fgPlatformPushWindow( window );
\r
519 fgPlatformPopWindow( window );
\r
523 void fgPlatformVisibilityWork(SFG_Window* window)
\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
529 SFG_Window *win = window;
\r
530 switch (window->State.DesiredVisibility)
\r
532 case DesireHiddenState:
\r
533 fgPlatformHideWindow( window );
\r
535 case DesireIconicState:
\r
536 /* Call on top-level window */
\r
537 while (win->Parent)
\r
539 fgPlatformIconifyWindow( win );
\r
541 case DesireNormalState:
\r
542 fgPlatformShowWindow( window );
\r