- moving to a new way of handling window changes (position, size, visibility)
[freeglut] / src / mswin / fg_main_mswin.c
1 /*
2  * freeglut_main_mswin.c
3  *
4  * The Windows-specific mouse cursor related stuff.
5  *
6  * Copyright (c) 2012 Stephen J. Baker. All Rights Reserved.
7  * Written by John F. Fay, <fayjf@sourceforge.net>
8  * Creation date: Sat Jan 21, 2012
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #include <GL/freeglut.h>
29 #include "../fg_internal.h"
30
31 extern void fghRedrawWindow ( SFG_Window *window );
32 extern void fghRedrawWindowAndChildren ( SFG_Window *window );
33 extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
34 extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
35 extern void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside );
36 extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside );
37
38 extern void fgNewWGLCreateContext( SFG_Window* window );
39 extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly,
40                                      unsigned char layer_type );
41
42 extern void fgPlatformCheckMenuDeactivate();
43
44 #ifdef WM_TOUCH
45 typedef BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT,UINT,PTOUCHINPUT,int);
46 typedef BOOL (WINAPI *pCloseTouchInputHandle)(HTOUCHINPUT);
47 static pGetTouchInputInfo fghGetTouchInputInfo = (pGetTouchInputInfo)0xDEADBEEF;
48 static pCloseTouchInputHandle fghCloseTouchInputHandle = (pCloseTouchInputHandle)0xDEADBEEF;
49 #endif
50
51 #ifdef _WIN32_WCE
52 typedef struct GXDisplayProperties GXDisplayProperties;
53 typedef struct GXKeyList GXKeyList;
54 #include <gx.h>
55
56 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
57 typedef int (*GXOPENINPUT)();
58
59 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
60 GXOPENINPUT GXOpenInput_ = NULL;
61
62 struct GXKeyList gxKeyList;
63 #endif /* _WIN32_WCE */
64
65
66 /* Get system time, taking special precautions against 32bit timer wrap.
67    We use timeGetTime and not GetTickCount because of its better stability,
68    and because we can increase its granularity (to 1 ms in
69    fgPlatformInitialize). For that reason we can't use GetTickCount64 which
70    wouldn't have the wrap issue.
71    Credit: this is based on code in glibc (https://mail.gnome.org/archives/commits-list/2011-November/msg04588.html)
72    */
73 static fg_time_t lastTime32 = 0;
74 static fg_time_t timeEpoch = 0;
75 void fgPlatformInitSystemTime()
76 {
77 #if defined(_WIN32_WCE)
78     lastTime32 = GetTickCount();
79 #else
80     lastTime32 = timeGetTime();
81 #endif
82 }
83 fg_time_t fgPlatformSystemTime ( void )
84 {
85     fg_time_t currTime32;
86 #if defined(_WIN32_WCE)
87     currTime32 = GetTickCount();
88 #else
89     currTime32 = timeGetTime();
90 #endif
91     /* Check if we just wrapped */
92     if (currTime32 < lastTime32)
93         timeEpoch++;
94     
95     lastTime32 = currTime32;
96
97     return currTime32 | timeEpoch << 32;
98 }
99
100
101 void fgPlatformSleepForEvents( fg_time_t msec )
102 {
103     MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
104 }
105
106
107 void fgPlatformProcessSingleEvent ( void )
108 {
109     MSG stMsg;
110
111     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
112
113     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
114     {
115         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
116         {
117             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
118             {
119                 fgDeinitialize( );
120                 exit( 0 );
121             }
122             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
123                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
124
125             return;
126         }
127
128         TranslateMessage( &stMsg );
129         DispatchMessage( &stMsg );
130     }
131 }
132
133
134
135 static void fghPlatformOnWindowStatusNotify(SFG_Window *window, GLboolean visState, GLboolean forceNotify)
136 {
137     GLboolean notify = GL_FALSE;
138     SFG_Window* child;
139
140     if (window->State.Visible != visState)
141     {
142         window->State.Visible = visState;
143
144         /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */
145         if (!window->Parent && window->State.pWState.IconTitle)
146         {
147             if (visState)
148                 /* visible, set window title */
149                 SetWindowText( window->Window.Handle, window->State.pWState.WindowTitle );
150             else
151                 /* not visible, set icon title */
152                 SetWindowText( window->Window.Handle, window->State.pWState.IconTitle );
153         }
154
155         notify = GL_TRUE;
156     }
157
158     if (notify || forceNotify)
159     {
160         SFG_Window *saved_window = fgStructure.CurrentWindow;
161
162         /* On win32 we only have two states, window displayed and window not displayed (iconified) 
163          * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively.
164          */
165         INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
166         fgSetWindow( saved_window );
167     }
168
169     /* Also set windowStatus/visibility state for children */
170     for( child = ( SFG_Window * )window->Children.First;
171          child;
172          child = ( SFG_Window * )child->Node.Next )
173     {
174         fghPlatformOnWindowStatusNotify(child, visState, GL_FALSE); /* No need to propagate forceNotify. Childs get this from their own INIT_WORK */
175     }
176 }
177
178 void fgPlatformMainLoopPreliminaryWork ( void )
179 {
180     /* no-op */
181 }
182
183
184 /*
185  * Determine a GLUT modifier mask based on MS-WINDOWS system info.
186  */
187 static int fgPlatformGetModifiers (void)
188 {
189     return
190         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
191             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
192         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
193             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
194         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
195             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
196 }
197
198 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
199 {
200     static unsigned char lControl = 0, lShift = 0, lAlt = 0,
201                          rControl = 0, rShift = 0, rAlt = 0;
202
203     int keypress = -1;
204     
205     /* if keydown, check for repeat */
206     /* If repeat is globally switched off, it cannot be switched back on per window.
207      * But if it is globally switched on, it can be switched off per window. This matches
208      * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
209      * global state switch.
210      */
211     if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
212         return 1;
213     
214     /* Remember the current modifiers state so user can query it from their callback */
215     fgState.Modifiers = fgPlatformGetModifiers( );
216
217     /* Convert the Win32 keystroke codes to GLUTtish way */
218 #   define KEY(a,b) case a: keypress = b; break;
219
220     switch( wParam )
221     {
222         KEY( VK_F1,     GLUT_KEY_F1        );
223         KEY( VK_F2,     GLUT_KEY_F2        );
224         KEY( VK_F3,     GLUT_KEY_F3        );
225         KEY( VK_F4,     GLUT_KEY_F4        );
226         KEY( VK_F5,     GLUT_KEY_F5        );
227         KEY( VK_F6,     GLUT_KEY_F6        );
228         KEY( VK_F7,     GLUT_KEY_F7        );
229         KEY( VK_F8,     GLUT_KEY_F8        );
230         KEY( VK_F9,     GLUT_KEY_F9        );
231         KEY( VK_F10,    GLUT_KEY_F10       );
232         KEY( VK_F11,    GLUT_KEY_F11       );
233         KEY( VK_F12,    GLUT_KEY_F12       );
234         KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
235         KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
236         KEY( VK_HOME,   GLUT_KEY_HOME      );
237         KEY( VK_END,    GLUT_KEY_END       );
238         KEY( VK_LEFT,   GLUT_KEY_LEFT      );
239         KEY( VK_UP,     GLUT_KEY_UP        );
240         KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
241         KEY( VK_DOWN,   GLUT_KEY_DOWN      );
242         KEY( VK_INSERT, GLUT_KEY_INSERT    );
243
244     /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
245      * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
246      * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
247      */
248 #define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\
249     if (!keyStateVar && GetAsyncKeyState ( winKey ))\
250     {\
251         keypress   = glutKey;\
252         keyStateVar = 1;\
253     }\
254     else if (keyStateVar && !GetAsyncKeyState ( winKey ))\
255     {\
256         keypress   = glutKey;\
257         keyStateVar = 0;\
258     }
259     case VK_CONTROL:
260         ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
261         ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
262         break;
263     case VK_SHIFT:
264         ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
265         ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
266         break;
267     case VK_MENU:
268         ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
269         ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
270         break;
271 #undef ASYNC_KEY_EVENT
272
273     case VK_DELETE:
274         /* The delete key should be treated as an ASCII keypress: */
275         if (keydown)
276             INVOKE_WCB( *window, Keyboard,
277                         ( 127, window->State.MouseX, window->State.MouseY )
278             );
279         else
280             INVOKE_WCB( *window, KeyboardUp,
281                         ( 127, window->State.MouseX, window->State.MouseY )
282             );
283         break;
284
285 #if !defined(_WIN32_WCE)
286     default:
287         /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
288         if (!keydown)
289         {
290             BYTE state[ 256 ];
291             WORD code[ 2 ];
292
293             GetKeyboardState( state );
294
295             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
296                 wParam=code[ 0 ];
297
298             INVOKE_WCB( *window, KeyboardUp,
299                    ( (char)wParam,
300                         window->State.MouseX, window->State.MouseY )
301             );
302         }
303 #endif
304     }
305
306 #if defined(_WIN32_WCE)
307     if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
308     {
309         if(wParam==(unsigned)gxKeyList.vkRight)
310             keypress = GLUT_KEY_RIGHT;
311         else if(wParam==(unsigned)gxKeyList.vkLeft)
312             keypress = GLUT_KEY_LEFT;
313         else if(wParam==(unsigned)gxKeyList.vkUp)
314             keypress = GLUT_KEY_UP;
315         else if(wParam==(unsigned)gxKeyList.vkDown)
316             keypress = GLUT_KEY_DOWN;
317         else if(wParam==(unsigned)gxKeyList.vkA)
318             keypress = GLUT_KEY_F1;
319         else if(wParam==(unsigned)gxKeyList.vkB)
320             keypress = GLUT_KEY_F2;
321         else if(wParam==(unsigned)gxKeyList.vkC)
322             keypress = GLUT_KEY_F3;
323         else if(wParam==(unsigned)gxKeyList.vkStart)
324             keypress = GLUT_KEY_F4;
325     }
326 #endif
327     
328     if( keypress != -1 )
329         if (keydown)
330             INVOKE_WCB( *window, Special,
331                         ( keypress,
332                             window->State.MouseX, window->State.MouseY )
333             );
334         else
335             INVOKE_WCB( *window, SpecialUp,
336                         ( keypress,
337                             window->State.MouseX, window->State.MouseY )
338             );
339
340     fgState.Modifiers = INVALID_MODIFIERS;
341
342     /* SYSKEY events should be sent to default window proc for system to handle them */
343     if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
344         return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
345     else
346         return 1;
347 }
348
349 SFG_Window* fghWindowUnderCursor(SFG_Window *window)
350 {
351     /* Check if the current window that the mouse is over is a child window
352      * of the window the message was sent to. Some events only sent to main window,
353      * and when handling some messages, we need to make sure that we process
354      * callbacks on the child window instead. This mirrors how GLUT does things.
355      * returns either the original window or the found child.
356      */
357     if (window && window->Children.First)   /* This window has childs */
358     {
359         HWND hwnd;
360         SFG_Window* child_window;
361
362         /* Get mouse position at time of message */
363         DWORD mouse_pos_dw = GetMessagePos();
364         POINT mouse_pos = {GET_X_LPARAM(mouse_pos_dw), GET_Y_LPARAM(mouse_pos_dw)};
365         ScreenToClient( window->Window.Handle, &mouse_pos );
366         
367         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
368         if (hwnd && hwnd!=window->Window.Handle)   /* can be NULL if mouse outside parent by the time we get here, or can be same as parent if we didn't find a child */
369         {
370             child_window = fgWindowByHandle(hwnd);
371             if (child_window)    /* Verify we got a FreeGLUT window */
372             {
373                 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
374                 window = fghWindowUnderCursor(child_window);
375             }
376         }
377     }
378
379     return window;
380 }
381
382 /*
383  * The window procedure for handling Win32 events
384  */
385 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
386 {
387     SFG_Window *window;
388     LRESULT lRet = 1;
389     static int setCaptureActive = 0;
390
391     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
392
393     window = fgWindowByHandle( hWnd );
394
395     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
396       return DefWindowProc( hWnd, uMsg, wParam, lParam );
397
398     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
399              uMsg, wParam, lParam ); */
400
401     switch( uMsg )
402     {
403     case WM_CREATE:
404         /* The window structure is passed as the creation structure parameter... */
405         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
406         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
407                                        "fgPlatformWindowProc" );
408
409         window->Window.Handle = hWnd;
410         window->Window.pContext.Device = GetDC( hWnd );
411         if( window->IsMenu )
412         {
413             unsigned int current_DisplayMode = fgState.DisplayMode;
414             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
415 #if !defined(_WIN32_WCE)
416             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
417 #endif
418             fgState.DisplayMode = current_DisplayMode;
419
420             if( fgStructure.MenuContext )
421                 wglMakeCurrent( window->Window.pContext.Device,
422                                 fgStructure.MenuContext->MContext
423                 );
424             else
425             {
426                 fgStructure.MenuContext =
427                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
428                 fgStructure.MenuContext->MContext =
429                     wglCreateContext( window->Window.pContext.Device );
430             }
431
432             /* window->Window.Context = wglGetCurrentContext ();   */
433             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
434         }
435         else
436         {
437 #if !defined(_WIN32_WCE)
438             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
439 #endif
440
441             if( ! fgState.UseCurrentContext )
442                 window->Window.Context =
443                     wglCreateContext( window->Window.pContext.Device );
444             else
445             {
446                 window->Window.Context = wglGetCurrentContext( );
447                 if( ! window->Window.Context )
448                     window->Window.Context =
449                         wglCreateContext( window->Window.pContext.Device );
450             }
451
452 #if !defined(_WIN32_WCE)
453             fgNewWGLCreateContext( window );
454 #endif
455         }
456
457         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
458
459 #if defined(_WIN32_WCE)
460         /* Take over button handling */
461         {
462             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
463             if (dxDllLib)
464             {
465                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
466                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
467             }
468
469             if(GXOpenInput_)
470                 (*GXOpenInput_)();
471             if(GXGetDefaultKeys_)
472                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
473         }
474
475 #endif /* defined(_WIN32_WCE) */
476         break;
477
478     case WM_SIZE:
479         /* printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam)); */
480
481         /* Update visibility state of the window */
482         if (wParam==SIZE_MINIMIZED)
483             fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE);
484         else if (wParam==SIZE_RESTORED && !window->State.Visible)
485             fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE);
486
487         /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */
488         if( window->State.Visible )
489         {
490             int width, height;
491 #if defined(_WIN32_WCE)
492             width  = HIWORD(lParam);
493             height = LOWORD(lParam);
494 #else
495             width  = LOWORD(lParam);
496             height = HIWORD(lParam);
497 #endif /* defined(_WIN32_WCE) */
498             
499             /* Update state and call callback, if there was a change */
500             fghOnReshapeNotify(window, width, height, GL_FALSE);
501         }
502
503         /* according to docs, should return 0 */
504         lRet = 0;
505         break;
506
507     case WM_SIZING:
508         {
509             /* User resize-dragging the window, call reshape callback and
510              * force redisplay so display keeps running during dragging.
511              * Screen still wont update when not moving the cursor though...
512              */
513             /* PRECT prect = (PRECT) lParam; */
514             RECT rect;
515             /* printf("WM_SIZING: nc-area: %i,%i\n",prect->right-prect->left,prect->bottom-prect->top); */
516             /* Get client area, the rect in lParam is including non-client area. */
517             fghGetClientArea(&rect,window,FALSE);
518
519             /* We'll get a WM_SIZE as well, but as state has
520              * already been updated here, the fghOnReshapeNotify
521              * in the handler for that message doesn't do anything.
522              */
523             fghOnReshapeNotify(window, rect.right-rect.left, rect.bottom-rect.top, GL_FALSE);
524
525             /* Now directly call the drawing function to update
526              * window and window's childs.
527              * This mimics the WM_PAINT messages that are received during
528              * resizing. Note that we don't have a WM_MOVING handler
529              * as move-dragging doesn't generate WM_MOVE or WM_PAINT
530              * messages until the mouse is released.
531              */
532             fghRedrawWindowAndChildren(window);
533         }
534
535         /* according to docs, should return TRUE */
536         lRet = TRUE;
537         break;
538
539     case WM_MOVE:
540         {
541             /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */
542             if (!IsIconic(window->Window.Handle))
543             {
544                 RECT windowRect;
545                 
546                 /* lParam contains coordinates of top-left of client area.
547                  * Get top-left of non-client area of window, matching coordinates of
548                  * glutInitPosition and glutPositionWindow, but not those of 
549                  * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return
550                  * top-left of client area.
551                  */
552                 GetWindowRect( window->Window.Handle, &windowRect );
553             
554                 if (window->Parent)
555                 {
556                     /* For child window, we should return relative to upper-left
557                      * of parent's client area.
558                      */
559                     POINT topleft = {windowRect.left,windowRect.top};
560
561                     ScreenToClient(window->Parent->Window.Handle,&topleft);
562                     windowRect.left = topleft.x;
563                     windowRect.top  = topleft.y;
564                 }
565
566                 /* Update state and call callback, if there was a change */
567                 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_FALSE);
568             }
569         }
570
571         /* according to docs, should return 0 */
572         lRet = 0;
573         break;
574
575     case WM_SETFOCUS:
576         /*printf("WM_SETFOCUS: %p\n", window );*/
577         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
578
579         SetActiveWindow( window->Window.Handle );
580         UpdateWindow ( hWnd );
581
582         break;
583
584     case WM_KILLFOCUS:
585         /*printf("WM_KILLFOCUS: %p\n", window ); */
586         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
587
588         /* Check if there are any open menus that need to be closed */
589         fgPlatformCheckMenuDeactivate();
590         break;
591
592 #if 0
593     case WM_ACTIVATE:
594         //printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam));
595         if (LOWORD(wParam) != WA_INACTIVE)
596         {
597 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
598                    window->State.Cursor ); */
599             fgSetCursor( window, window->State.Cursor );
600         }
601
602         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
603         break;
604 #endif
605
606     case WM_SETCURSOR:
607 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
608         if( LOWORD( lParam ) == HTCLIENT )
609         {
610             if (!window->State.pWState.MouseTracking)
611             {
612                 TRACKMOUSEEVENT tme;
613
614                 /* Cursor just entered window, set cursor look */ 
615                 fgSetCursor ( window, window->State.Cursor ) ;
616
617                 /* If an EntryFunc callback is specified by the user, also
618                  * invoke that callback and start mouse tracking so that
619                  * we get a WM_MOUSELEAVE message
620                  */
621                 if (FETCH_WCB( *window, Entry ))
622                 {
623                     SFG_Window* saved_window = fgStructure.CurrentWindow;
624                     INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
625                     fgSetWindow(saved_window);
626
627                     tme.cbSize = sizeof(TRACKMOUSEEVENT);
628                     tme.dwFlags = TME_LEAVE;
629                     tme.hwndTrack = window->Window.Handle;
630                     TrackMouseEvent(&tme);
631
632                     window->State.pWState.MouseTracking = GL_TRUE;
633                 }
634             }
635         }
636         else
637             /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
638             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
639         break;
640
641     case WM_MOUSELEAVE:
642         {
643             /* NB: This message is only received when a EntryFunc callback
644              * is specified by the user, as that is the only condition under
645              * which mouse tracking is setup in WM_SETCURSOR handler above
646              */
647             SFG_Window* saved_window = fgStructure.CurrentWindow;
648             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
649             fgSetWindow(saved_window);
650
651             window->State.pWState.MouseTracking = GL_FALSE;
652             lRet = 0;   /* As per docs, must return zero */
653         }
654         break;
655
656     case WM_SHOWWINDOW:
657         /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
658         if (wParam)
659         {
660             fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE);
661             window->State.Redisplay = GL_TRUE;
662         }
663         else
664         {
665             fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE);
666             window->State.Redisplay = GL_FALSE;
667         }
668         break;
669
670     case WM_PAINT:
671     {
672         RECT rect;
673         
674         /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
675         if (GetUpdateRect(hWnd,&rect,FALSE))
676         {
677             /* Dummy begin/end paint to validate rect that needs
678              * redrawing, then signal that a redisplay is needed.
679              * This allows us full control about when we do any
680              * redrawing, and is the same as what original GLUT
681              * does.
682              */
683             PAINTSTRUCT ps;
684             BeginPaint( hWnd, &ps );
685             EndPaint( hWnd, &ps );
686
687             window->State.Redisplay = GL_TRUE;
688         }
689         lRet = 0;   /* As per docs, should return 0 */
690     }
691     break;
692
693     case WM_CLOSE:
694         fgDestroyWindow ( window );
695         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
696             PostQuitMessage(0);
697         break;
698
699     case WM_DESTROY:
700         /*
701          * The window already got destroyed, so don't bother with it.
702          */
703         return 0;
704
705     case WM_MOUSEMOVE:
706     {
707         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
708 #if defined(_WIN32_WCE)
709         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
710         window->State.MouseY = LOWORD( lParam );
711 #else
712         window->State.MouseX = GET_X_LPARAM( lParam );
713         window->State.MouseY = GET_Y_LPARAM( lParam );
714 #endif /* defined(_WIN32_WCE) */
715         /* Restrict to [-32768, 32767] to match X11 behaviour       */
716         /* See comment in "freeglut_developer" mailing list 10/4/04 */
717         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
718         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
719
720         if ( window->ActiveMenu )
721         {
722             fgUpdateMenuHighlight( window->ActiveMenu );
723             break;
724         }
725
726         fgState.Modifiers = fgPlatformGetModifiers( );
727
728         if( ( wParam & MK_LBUTTON ) ||
729             ( wParam & MK_MBUTTON ) ||
730             ( wParam & MK_RBUTTON ) )
731             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
732                                            window->State.MouseY ) );
733         else
734             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
735                                             window->State.MouseY ) );
736
737         fgState.Modifiers = INVALID_MODIFIERS;
738     }
739     break;
740
741     case WM_LBUTTONDOWN:
742     case WM_MBUTTONDOWN:
743     case WM_RBUTTONDOWN:
744     case WM_LBUTTONUP:
745     case WM_MBUTTONUP:
746     case WM_RBUTTONUP:
747     {
748         GLboolean pressed = GL_TRUE;
749         int button;
750
751         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
752 #if defined(_WIN32_WCE)
753         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
754         window->State.MouseY = LOWORD( lParam );
755 #else
756         window->State.MouseX = GET_X_LPARAM( lParam );
757         window->State.MouseY = GET_Y_LPARAM( lParam );
758 #endif /* defined(_WIN32_WCE) */
759
760         /* Restrict to [-32768, 32767] to match X11 behaviour       */
761         /* See comment in "freeglut_developer" mailing list 10/4/04 */
762         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
763         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
764
765         switch( uMsg )
766         {
767         case WM_LBUTTONDOWN:
768             pressed = GL_TRUE;
769             button = GLUT_LEFT_BUTTON;
770             break;
771         case WM_MBUTTONDOWN:
772             pressed = GL_TRUE;
773             button = GLUT_MIDDLE_BUTTON;
774             break;
775         case WM_RBUTTONDOWN:
776             pressed = GL_TRUE;
777             button = GLUT_RIGHT_BUTTON;
778             break;
779         case WM_LBUTTONUP:
780             pressed = GL_FALSE;
781             button = GLUT_LEFT_BUTTON;
782             break;
783         case WM_MBUTTONUP:
784             pressed = GL_FALSE;
785             button = GLUT_MIDDLE_BUTTON;
786             break;
787         case WM_RBUTTONUP:
788             pressed = GL_FALSE;
789             button = GLUT_RIGHT_BUTTON;
790             break;
791         default:
792             pressed = GL_FALSE;
793             button = -1;
794             break;
795         }
796
797 #if !defined(_WIN32_WCE)
798         if( GetSystemMetrics( SM_SWAPBUTTON ) )
799         {
800             if( button == GLUT_LEFT_BUTTON )
801                 button = GLUT_RIGHT_BUTTON;
802             else
803                 if( button == GLUT_RIGHT_BUTTON )
804                     button = GLUT_LEFT_BUTTON;
805         }
806 #endif /* !defined(_WIN32_WCE) */
807
808         if( button == -1 )
809             return DefWindowProc( hWnd, uMsg, lParam, wParam );
810
811         /*
812          * Do not execute the application's mouse callback if a menu
813          * is hooked to this button.  In that case an appropriate
814          * private call should be generated.
815          */
816         if( fgCheckActiveMenu( window, button, pressed,
817                                window->State.MouseX, window->State.MouseY ) )
818             break;
819
820         /* Set capture so that the window captures all the mouse messages
821          *
822          * The mouse is not released from the window until all buttons have
823          * been released, even if the user presses a button in another window.
824          * This is consistent with the behavior on X11.
825          */
826         if ( pressed == GL_TRUE )
827         {
828             if (!setCaptureActive)
829                 SetCapture ( window->Window.Handle ) ;
830             setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
831         }
832         else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
833           /* Make sure all mouse buttons are released before releasing capture */
834           ReleaseCapture () ;
835
836         if( ! FETCH_WCB( *window, Mouse ) )
837             break;
838
839         fgSetWindow( window );
840         fgState.Modifiers = fgPlatformGetModifiers( );
841
842         INVOKE_WCB(
843             *window, Mouse,
844             ( button,
845               pressed ? GLUT_DOWN : GLUT_UP,
846               window->State.MouseX,
847               window->State.MouseY
848             )
849         );
850
851         fgState.Modifiers = INVALID_MODIFIERS;
852
853         /* As per docs, should return zero */
854         lRet = 0;
855     }
856     break;
857
858     case WM_MOUSEWHEEL:
859     {
860         int wheel_number = 0;   /* Only one scroll wheel on windows */
861 #if defined(_WIN32_WCE)
862         int modkeys = LOWORD(wParam); 
863         short ticks = (short)HIWORD(wParam);
864         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
865         xPos = LOWORD(lParam);  -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
866         yPos = HIWORD(lParam);
867         */
868 #else
869         /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
870         short ticks = GET_WHEEL_DELTA_WPARAM( wParam );
871         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
872         window->State.MouseX = GET_X_LPARAM( lParam );
873         window->State.MouseY = GET_Y_LPARAM( lParam );
874         */
875 #endif /* defined(_WIN32_WCE) */
876
877         window = fghWindowUnderCursor(window);
878
879                 fgState.MouseWheelTicks += ticks;
880         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
881                 {
882                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
883
884             if( ! FETCH_WCB( *window, MouseWheel ) &&
885                 ! FETCH_WCB( *window, Mouse ) )
886                 break;
887
888             fgSetWindow( window );
889             fgState.Modifiers = fgPlatformGetModifiers( );
890
891             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
892                         {
893                 if( FETCH_WCB( *window, MouseWheel ) )
894                     INVOKE_WCB( *window, MouseWheel,
895                                 ( wheel_number,
896                                   direction,
897                                   window->State.MouseX,
898                                   window->State.MouseY
899                                 )
900                     );
901                 else  /* No mouse wheel, call the mouse button callback twice */
902                                 {
903                     /*
904                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
905                      *  "    "   one                     +1 to 5, -1 to 6, ...
906                      *
907                      * XXX The below assumes that you have no more than 3 mouse
908                      * XXX buttons.  Sorry.
909                      */
910                     int button = wheel_number * 2 + 3;
911                     if( direction < 0 )
912                         ++button;
913                     INVOKE_WCB( *window, Mouse,
914                                 ( button, GLUT_DOWN,
915                                   window->State.MouseX, window->State.MouseY )
916                     );
917                     INVOKE_WCB( *window, Mouse,
918                                 ( button, GLUT_UP,
919                                   window->State.MouseX, window->State.MouseY )
920                     );
921                                 }
922
923                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
924                         }
925
926             fgState.Modifiers = INVALID_MODIFIERS;
927                 }
928         /* Per docs, should return zero */
929         lRet = 0;
930     }
931     break ;
932
933     case WM_SYSKEYDOWN:
934     case WM_KEYDOWN:
935     {
936         window = fghWindowUnderCursor(window);
937         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
938     }
939     break;
940
941     case WM_SYSKEYUP:
942     case WM_KEYUP:
943     {
944         window = fghWindowUnderCursor(window);
945         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
946     }
947     break;
948
949     case WM_SYSCHAR:
950     case WM_CHAR:
951     {
952       window = fghWindowUnderCursor(window);
953
954       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
955             break;
956
957         fgState.Modifiers = fgPlatformGetModifiers( );
958         INVOKE_WCB( *window, Keyboard,
959                     ( (char)wParam,
960                       window->State.MouseX, window->State.MouseY )
961         );
962         fgState.Modifiers = INVALID_MODIFIERS;
963     }
964     break;
965
966     case WM_CAPTURECHANGED:
967         if (!lParam || !fgWindowByHandle((HWND)lParam))
968             /* Capture released or capture taken by non-FreeGLUT window */
969             setCaptureActive = 0;
970         /* Docs advise a redraw */
971         InvalidateRect( hWnd, NULL, GL_FALSE );
972         UpdateWindow(hWnd);
973         lRet = 0;   /* Per docs, should return zero */
974         break;
975
976 #if !defined(_WIN32_WCE)
977     case WM_SYNCPAINT:  /* 0x0088 */
978         /* Another window has moved, need to update this one */
979         window->State.Redisplay = GL_TRUE;
980         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
981         /* Help screen says this message must be passed to "DefWindowProc" */
982         break;
983
984     case WM_SYSCOMMAND :  /* 0x0112 */
985         {
986           /*
987            * We have received a system command message.  Try to act on it.
988            * The commands are passed in through the "wParam" parameter:
989            * The least significant digit seems to be which edge of the window
990            * is being used for a resize event:
991            *     4  3  5
992            *     1     2
993            *     7  6  8
994            * Congratulations and thanks to Richard Rauch for figuring this out..
995            */
996             switch ( wParam & 0xfff0 )
997             {
998             case SC_SIZE       :
999                 break ;
1000
1001             case SC_MOVE       :
1002                 break ;
1003
1004             case SC_MINIMIZE   :
1005                 /* User has clicked on the "-" to minimize the window */
1006                 /* Turning off the visibility is handled in WM_SIZE handler */
1007
1008                 break ;
1009
1010             case SC_MAXIMIZE   :
1011                 break ;
1012
1013             case SC_NEXTWINDOW :
1014                 break ;
1015
1016             case SC_PREVWINDOW :
1017                 break ;
1018
1019             case SC_CLOSE      :
1020                 /* Followed very closely by a WM_CLOSE message */
1021                 break ;
1022
1023             case SC_VSCROLL    :
1024                 break ;
1025
1026             case SC_HSCROLL    :
1027                 break ;
1028
1029             case SC_MOUSEMENU  :
1030                 break ;
1031
1032             case SC_KEYMENU    :
1033                 break ;
1034
1035             case SC_ARRANGE    :
1036                 break ;
1037
1038             case SC_RESTORE    :
1039                 break ;
1040
1041             case SC_TASKLIST   :
1042                 break ;
1043
1044             case SC_SCREENSAVE :
1045                 break ;
1046
1047             case SC_HOTKEY     :
1048                 break ;
1049
1050 #if(WINVER >= 0x0400)
1051             case SC_DEFAULT    :
1052                 break ;
1053
1054             case SC_MONITORPOWER    :
1055                 break ;
1056
1057             case SC_CONTEXTHELP    :
1058                 break ;
1059 #endif /* WINVER >= 0x0400 */
1060
1061             default:
1062 #if _DEBUG
1063                 fgWarning( "Unknown wParam type 0x%x", wParam );
1064 #endif
1065                 break;
1066             }
1067         }
1068 #endif /* !defined(_WIN32_WCE) */
1069
1070         /* We need to pass the message on to the operating system as well */
1071         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1072         break;
1073
1074 #ifdef WM_TOUCH
1075         /* handle multi-touch messages */
1076         case WM_TOUCH:
1077         {
1078                 unsigned int numInputs = (unsigned int)wParam;
1079                 unsigned int i = 0;
1080                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1081
1082                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1083                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1084                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1085                 }
1086
1087                 if (!fghGetTouchInputInfo) { 
1088                         free( (void*)ti );
1089                         break;
1090                 }
1091
1092                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1093                         /* Handle each contact point */
1094                         for (i = 0; i < numInputs; ++i ) {
1095
1096                                 POINT tp;
1097                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1098                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1099                                 ScreenToClient( hWnd, &tp );
1100
1101                                 ti[i].dwID = ti[i].dwID * 2;
1102
1103                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1104                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1105                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1106                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1107                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1108                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1109                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1110                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1111                                 }
1112                         }
1113                 }
1114                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1115                 free( (void*)ti );
1116                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1117                 break;
1118         }
1119 #endif
1120     default:
1121         /* Handle unhandled messages */
1122         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1123         break;
1124     }
1125
1126     return lRet;
1127 }
1128
1129
1130 /* Step through the work list */
1131 void fgPlatformProcessWork(SFG_Window *window)
1132 {
1133     unsigned int workMask = window->State.WorkMask;
1134     /* Now clear it so that any callback generated by the actions below can set work again */
1135     window->State.WorkMask = 0;
1136
1137     /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc
1138      * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when
1139      * they are opened, and work is done before displaying in the mainloop.
1140      */
1141     if (workMask & GLUT_INIT_WORK)
1142     {
1143         RECT windowRect;
1144
1145         /* Notify windowStatus/visibility */
1146         fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE);
1147
1148         /* get and notify window's position */
1149         GetWindowRect(window->Window.Handle,&windowRect);
1150         fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE);
1151
1152         /* get and notify window's size */
1153         GetClientRect(window->Window.Handle,&windowRect);
1154         fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE);
1155
1156         /* Call init context callback */
1157         INVOKE_WCB( *window, InitContext, ());
1158
1159         /* Lastly, check if we have a display callback, error out if not
1160          * This is the right place to do it, as the redisplay will be
1161          * next right after we exit this function, so there is no more
1162          * opportunity for the user to register a callback for this window.
1163          */
1164         if (!FETCH_WCB(*window, Display))
1165             fgError ( "ERROR:  No display callback registered for window %d\n", window->ID );
1166     }
1167
1168     /* On windows we can position, resize and change z order at the same time */
1169     if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
1170     {
1171         UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1172         HWND insertAfter = HWND_TOP;
1173         RECT clientRect;
1174
1175 #if !defined(_WIN32_WCE) /* FIXME: what about WinCE */
1176         if (workMask & GLUT_FULL_SCREEN_WORK)
1177         {
1178             /* This asks us to toggle fullscreen mode */
1179             flags |= SWP_FRAMECHANGED;
1180
1181             if (window->State.IsFullscreen)
1182             {
1183                 /* If we are fullscreen, resize the current window back to its original size */
1184                 /* printf("OldRect %i,%i to %i,%i\n",window->State.pWState.OldRect.left,window->State.pWState.OldRect.top,window->State.pWState.OldRect.right,window->State.pWState.OldRect.bottom); */
1185
1186                 /* restore style of window before making it fullscreen */
1187                 SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle);
1188                 SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx);
1189
1190                 /* Then set up resize/reposition, unless user already queued up reshape/position work */
1191                 if (!(workMask & GLUT_POSITION_WORK))
1192                 {
1193                     workMask |= GLUT_POSITION_WORK;
1194                     window->State.DesiredXpos   = window->State.pWState.OldRect.left;
1195                     window->State.DesiredYpos   = window->State.pWState.OldRect.top;
1196                 }
1197                 if (!(workMask & GLUT_SIZE_WORK))
1198                 {
1199                     workMask |= GLUT_SIZE_WORK;
1200                     window->State.DesiredWidth  = window->State.pWState.OldRect.right  - window->State.pWState.OldRect.left;
1201                     window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top;
1202                 }
1203                 
1204                 /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */
1205             }
1206             else
1207             {
1208                 /* we are currently not fullscreen, go to fullscreen:
1209                  * remove window decoration and then maximize
1210                  */
1211                 RECT rect;
1212                 HMONITOR hMonitor;
1213                 MONITORINFO mi;
1214         
1215                 /* save current window rect, style, exstyle and maximized state */
1216                 window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle);
1217                 if (window->State.pWState.OldMaximized)
1218                     /* We force the window into restored mode before going
1219                      * fullscreen because Windows doesn't seem to hide the
1220                      * taskbar if the window is in the maximized state.
1221                      */
1222                     SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
1223
1224                 fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE );
1225                 window->State.pWState.OldStyle   = GetWindowLong(window->Window.Handle, GWL_STYLE);
1226                 window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
1227
1228                 /* remove decorations from style */
1229                 SetWindowLong(window->Window.Handle, GWL_STYLE,
1230                               window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME));
1231                 SetWindowLong(window->Window.Handle, GWL_EXSTYLE,
1232                               window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME |
1233                               WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1234
1235                 /* For fullscreen mode, find the monitor that is covered the most
1236                  * by the window and get its rect as the resize target.
1237                      */
1238                 GetWindowRect(window->Window.Handle, &rect);
1239                 hMonitor= MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
1240                 mi.cbSize = sizeof(mi);
1241                 GetMonitorInfo(hMonitor, &mi);
1242                 rect = mi.rcMonitor;
1243
1244                 /* then setup window resize, overwriting other work queued on the window */
1245                 window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK;
1246                 window->State.WorkMask &= ~GLUT_ZORDER_WORK;
1247                 window->State.DesiredXpos   = rect.left;
1248                 window->State.DesiredYpos   = rect.top;
1249                 window->State.DesiredWidth  = rect.right  - rect.left;
1250                 window->State.DesiredHeight = rect.bottom - rect.top;
1251             }
1252         }
1253 #endif /*!defined(_WIN32_WCE) */
1254
1255         /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */
1256         {
1257             /* get rect describing window's current position and size, 
1258              * in screen coordinates and in FreeGLUT format
1259              * (size (right-left, bottom-top) is client area size, top and left
1260              * are outside of window including decorations).
1261              */
1262             fghGetClientArea( &clientRect, window, TRUE );
1263
1264             if (workMask & GLUT_POSITION_WORK)
1265             {
1266                 flags &= ~SWP_NOMOVE;
1267                 
1268                 /* Move rect so that top-left is at requested position */
1269                 /* This also automatically makes sure that child window requested coordinates are relative
1270                  * to top-left of parent's client area (needed input for SetWindowPos on child windows),
1271                  * so no need to further correct rect for child windows below (childs don't have decorations either).
1272                  */
1273                 OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top);
1274             }
1275             if (workMask & GLUT_SIZE_WORK)
1276             {
1277                 flags &= ~SWP_NOSIZE;
1278                 
1279                 /* Note on maximizing behavior of Windows: the resize borders are off
1280                  * the screen such that the client area extends all the way from the
1281                  * leftmost corner to the rightmost corner to maximize screen real
1282                  * estate. A caption is still shown however to allow interaction with
1283                  * the window controls. This is default behavior of Windows that
1284                  * FreeGLUT sticks with. To alter, one would have to check if
1285                  * WS_MAXIMIZE style is set when a resize event is triggered, and
1286                  * then manually correct the windowRect to put the borders back on
1287                  * screen.
1288                  */
1289
1290                 /* Set new size of window, WxH specify client area */
1291                 clientRect.right    = clientRect.left + window->State.DesiredWidth;
1292                 clientRect.bottom   = clientRect.top  + window->State.DesiredHeight;
1293             }
1294             if (workMask & GLUT_ZORDER_WORK)
1295             {
1296                 flags &= ~SWP_NOZORDER;
1297
1298                 /* Could change this to push it down or up one window at a time with some
1299                  * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT.
1300                  * What would be consistent with X11? Win32 GLUT does what we do here...
1301                  */
1302                 if (window->State.DesiredZOrder < 0)
1303                     insertAfter = HWND_BOTTOM;
1304             }
1305         }
1306
1307         /* Adjust for window decorations
1308          * Child windows don't have decoration, so no need to correct
1309          */
1310         if (!window->Parent)
1311             /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
1312             fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE);
1313     
1314         /* Do the requested positioning, moving, and z order push/pop. */
1315         SetWindowPos( window->Window.Handle,
1316                       insertAfter,
1317                       clientRect.left, clientRect.top,
1318                       clientRect.right - clientRect.left,
1319                       clientRect.bottom- clientRect.top,
1320                       flags
1321         );
1322
1323         /* Finish off the fullscreen operation we were doing, if any */
1324         if (workMask & GLUT_FULL_SCREEN_WORK)
1325         {
1326             if (window->State.IsFullscreen)
1327             {
1328                 /* leaving fullscreen, restore maximized state, if any */
1329                 if (window->State.pWState.OldMaximized)
1330                     SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1331
1332                 window->State.IsFullscreen = GL_FALSE;
1333             }
1334             else
1335                 window->State.IsFullscreen = GL_TRUE;
1336         }
1337     }
1338
1339     if (workMask & GLUT_VISIBILITY_WORK)
1340     {
1341         /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */
1342         int cmdShow = 0;
1343         SFG_Window *win = window;
1344         switch (window->State.DesiredVisibility)
1345         {
1346         case DesireHiddenState:
1347             cmdShow = SW_HIDE;
1348                 break;
1349         case DesireIconicState:
1350             cmdShow = SW_MINIMIZE;
1351             /* Call on top-level window */
1352             while (win->Parent)
1353                 win = win->Parent;
1354             break;
1355         case DesireNormalState:
1356             cmdShow = SW_SHOW;
1357             break;
1358         }
1359
1360         ShowWindow( win->Window.Handle, cmdShow );
1361     }
1362 }