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