comparison mupdf-source/thirdparty/freeglut/src/android/fg_main_android.c @ 2:b50eed0cc0ef upstream

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