now handling WM_MOUSEACTIVATE so that menus don't get activated upon mouseclick
[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 & 0xFF), /* and with 0xFF to indicate to runtime that we want to strip out higher bits - otherwise we get a runtime error when "Smaller Type Checks" is enabled */
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     case WM_MOUSEACTIVATE:
593         /* Clicks should not activate the menu.
594          * Especially important when clicking on a menu's submenu item which has no effect.
595          */
596         printf("WM_MOUSEACTIVATE\n");
597         if (window->IsMenu)
598             lRet = MA_NOACTIVATEANDEAT;
599         else
600             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
601         break;
602
603 #if 0
604     case WM_ACTIVATE:
605         /* printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); */
606         if (LOWORD(wParam) != WA_INACTIVE)
607         {
608 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
609                    window->State.Cursor ); */
610             fgSetCursor( window, window->State.Cursor );
611         }
612
613         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
614         break;
615 #endif
616
617     case WM_SETCURSOR:
618 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
619         if( LOWORD( lParam ) == HTCLIENT )
620         {
621             if (!window->State.pWState.MouseTracking)
622             {
623                 TRACKMOUSEEVENT tme;
624
625                 /* Cursor just entered window, set cursor look */ 
626                 fgSetCursor ( window, window->State.Cursor ) ;
627
628                 /* If an EntryFunc callback is specified by the user, also
629                  * invoke that callback and start mouse tracking so that
630                  * we get a WM_MOUSELEAVE message
631                  */
632                 if (FETCH_WCB( *window, Entry ))
633                 {
634                     SFG_Window* saved_window = fgStructure.CurrentWindow;
635                     INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
636                     fgSetWindow(saved_window);
637
638                     tme.cbSize = sizeof(TRACKMOUSEEVENT);
639                     tme.dwFlags = TME_LEAVE;
640                     tme.hwndTrack = window->Window.Handle;
641                     TrackMouseEvent(&tme);
642
643                     window->State.pWState.MouseTracking = TRUE;
644                 }
645             }
646         }
647         else
648             /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
649             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
650         break;
651
652     case WM_MOUSELEAVE:
653         {
654             /* NB: This message is only received when a EntryFunc callback
655              * is specified by the user, as that is the only condition under
656              * which mouse tracking is setup in WM_SETCURSOR handler above
657              */
658             SFG_Window* saved_window = fgStructure.CurrentWindow;
659             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
660             fgSetWindow(saved_window);
661
662             window->State.pWState.MouseTracking = FALSE;
663             lRet = 0;   /* As per docs, must return zero */
664         }
665         break;
666
667     case WM_SHOWWINDOW:
668         /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
669         if (wParam)
670         {
671             fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE);
672             window->State.Redisplay = GL_TRUE;
673         }
674         else
675         {
676             fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE);
677             window->State.Redisplay = GL_FALSE;
678         }
679         break;
680
681     case WM_PAINT:
682     {
683         RECT rect;
684         
685         /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
686         if (GetUpdateRect(hWnd,&rect,FALSE))
687         {
688             /* Dummy begin/end paint to validate rect that needs
689              * redrawing, then signal that a redisplay is needed.
690              * This allows us full control about when we do any
691              * redrawing, and is the same as what original GLUT
692              * does.
693              */
694             PAINTSTRUCT ps;
695             BeginPaint( hWnd, &ps );
696             EndPaint( hWnd, &ps );
697
698             window->State.Redisplay = GL_TRUE;
699         }
700         lRet = 0;   /* As per docs, should return 0 */
701     }
702     break;
703
704     case WM_CLOSE:
705         fgDestroyWindow ( window );
706         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
707             PostQuitMessage(0);
708         break;
709
710     case WM_DESTROY:
711         /*
712          * The window already got destroyed, so don't bother with it.
713          */
714         return 0;
715
716     case WM_MOUSEMOVE:
717     {
718         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
719 #if defined(_WIN32_WCE)
720         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
721         window->State.MouseY = LOWORD( lParam );
722 #else
723         window->State.MouseX = GET_X_LPARAM( lParam );
724         window->State.MouseY = GET_Y_LPARAM( lParam );
725 #endif /* defined(_WIN32_WCE) */
726         /* Restrict to [-32768, 32767] to match X11 behaviour       */
727         /* See comment in "freeglut_developer" mailing list 10/4/04 */
728         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
729         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
730
731         if ( window->ActiveMenu )
732         {
733             fgUpdateMenuHighlight( window->ActiveMenu );
734             break;
735         }
736
737         fgState.Modifiers = fgPlatformGetModifiers( );
738
739         if( ( wParam & MK_LBUTTON ) ||
740             ( wParam & MK_MBUTTON ) ||
741             ( wParam & MK_RBUTTON ) )
742             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
743                                            window->State.MouseY ) );
744         else
745             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
746                                             window->State.MouseY ) );
747
748         fgState.Modifiers = INVALID_MODIFIERS;
749     }
750     break;
751
752     case WM_LBUTTONDOWN:
753     case WM_MBUTTONDOWN:
754     case WM_RBUTTONDOWN:
755     case WM_LBUTTONUP:
756     case WM_MBUTTONUP:
757     case WM_RBUTTONUP:
758     {
759         GLboolean pressed = GL_TRUE;
760         int button;
761
762         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
763 #if defined(_WIN32_WCE)
764         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
765         window->State.MouseY = LOWORD( lParam );
766 #else
767         window->State.MouseX = GET_X_LPARAM( lParam );
768         window->State.MouseY = GET_Y_LPARAM( lParam );
769 #endif /* defined(_WIN32_WCE) */
770
771         /* Restrict to [-32768, 32767] to match X11 behaviour       */
772         /* See comment in "freeglut_developer" mailing list 10/4/04 */
773         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
774         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
775
776         switch( uMsg )
777         {
778         case WM_LBUTTONDOWN:
779             pressed = GL_TRUE;
780             button = GLUT_LEFT_BUTTON;
781             break;
782         case WM_MBUTTONDOWN:
783             pressed = GL_TRUE;
784             button = GLUT_MIDDLE_BUTTON;
785             break;
786         case WM_RBUTTONDOWN:
787             pressed = GL_TRUE;
788             button = GLUT_RIGHT_BUTTON;
789             break;
790         case WM_LBUTTONUP:
791             pressed = GL_FALSE;
792             button = GLUT_LEFT_BUTTON;
793             break;
794         case WM_MBUTTONUP:
795             pressed = GL_FALSE;
796             button = GLUT_MIDDLE_BUTTON;
797             break;
798         case WM_RBUTTONUP:
799             pressed = GL_FALSE;
800             button = GLUT_RIGHT_BUTTON;
801             break;
802         default:
803             pressed = GL_FALSE;
804             button = -1;
805             break;
806         }
807
808 #if !defined(_WIN32_WCE)
809         if( GetSystemMetrics( SM_SWAPBUTTON ) )
810         {
811             if( button == GLUT_LEFT_BUTTON )
812                 button = GLUT_RIGHT_BUTTON;
813             else
814                 if( button == GLUT_RIGHT_BUTTON )
815                     button = GLUT_LEFT_BUTTON;
816         }
817 #endif /* !defined(_WIN32_WCE) */
818
819         if( button == -1 )
820             return DefWindowProc( hWnd, uMsg, lParam, wParam );
821
822         /*
823          * Do not execute the application's mouse callback if a menu
824          * is hooked to this button.  In that case an appropriate
825          * private call should be generated.
826          */
827         if( fgCheckActiveMenu( window, button, pressed,
828                                window->State.MouseX, window->State.MouseY ) )
829             break;
830
831         /* Set capture so that the window captures all the mouse messages
832          *
833          * The mouse is not released from the window until all buttons have
834          * been released, even if the user presses a button in another window.
835          * This is consistent with the behavior on X11.
836          */
837         if ( pressed == GL_TRUE )
838         {
839             if (!setCaptureActive)
840                 SetCapture ( window->Window.Handle ) ;
841             setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
842         }
843         else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
844           /* Make sure all mouse buttons are released before releasing capture */
845           ReleaseCapture () ;
846
847         if( ! FETCH_WCB( *window, Mouse ) )
848             break;
849
850         fgSetWindow( window );
851         fgState.Modifiers = fgPlatformGetModifiers( );
852
853         INVOKE_WCB(
854             *window, Mouse,
855             ( button,
856               pressed ? GLUT_DOWN : GLUT_UP,
857               window->State.MouseX,
858               window->State.MouseY
859             )
860         );
861
862         fgState.Modifiers = INVALID_MODIFIERS;
863
864         /* As per docs, should return zero */
865         lRet = 0;
866     }
867     break;
868
869     case WM_MOUSEWHEEL:
870     {
871         int wheel_number = 0;   /* Only one scroll wheel on windows */
872 #if defined(_WIN32_WCE)
873         int modkeys = LOWORD(wParam); 
874         short ticks = (short)HIWORD(wParam);
875         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
876         xPos = LOWORD(lParam);  -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
877         yPos = HIWORD(lParam);
878         */
879 #else
880         /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
881         short ticks = GET_WHEEL_DELTA_WPARAM( wParam );
882         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
883         window->State.MouseX = GET_X_LPARAM( lParam );
884         window->State.MouseY = GET_Y_LPARAM( lParam );
885         */
886 #endif /* defined(_WIN32_WCE) */
887
888         window = fghWindowUnderCursor(window);
889
890                 fgState.MouseWheelTicks += ticks;
891         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
892                 {
893                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
894
895             if( ! FETCH_WCB( *window, MouseWheel ) &&
896                 ! FETCH_WCB( *window, Mouse ) )
897                 break;
898
899             fgSetWindow( window );
900             fgState.Modifiers = fgPlatformGetModifiers( );
901
902             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
903                         {
904                 if( FETCH_WCB( *window, MouseWheel ) )
905                     INVOKE_WCB( *window, MouseWheel,
906                                 ( wheel_number,
907                                   direction,
908                                   window->State.MouseX,
909                                   window->State.MouseY
910                                 )
911                     );
912                 else  /* No mouse wheel, call the mouse button callback twice */
913                                 {
914                     /*
915                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
916                      *  "    "   one                     +1 to 5, -1 to 6, ...
917                      *
918                      * XXX The below assumes that you have no more than 3 mouse
919                      * XXX buttons.  Sorry.
920                      */
921                     int button = wheel_number * 2 + 3;
922                     if( direction < 0 )
923                         ++button;
924                     INVOKE_WCB( *window, Mouse,
925                                 ( button, GLUT_DOWN,
926                                   window->State.MouseX, window->State.MouseY )
927                     );
928                     INVOKE_WCB( *window, Mouse,
929                                 ( button, GLUT_UP,
930                                   window->State.MouseX, window->State.MouseY )
931                     );
932                                 }
933
934                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
935                         }
936
937             fgState.Modifiers = INVALID_MODIFIERS;
938                 }
939         /* Per docs, should return zero */
940         lRet = 0;
941     }
942     break ;
943
944     case WM_SYSKEYDOWN:
945     case WM_KEYDOWN:
946     {
947         window = fghWindowUnderCursor(window);
948         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
949     }
950     break;
951
952     case WM_SYSKEYUP:
953     case WM_KEYUP:
954     {
955         window = fghWindowUnderCursor(window);
956         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
957     }
958     break;
959
960     case WM_SYSCHAR:
961     case WM_CHAR:
962     {
963       window = fghWindowUnderCursor(window);
964
965       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
966             break;
967
968         fgState.Modifiers = fgPlatformGetModifiers( );
969         INVOKE_WCB( *window, Keyboard,
970                     ( (char)wParam,
971                       window->State.MouseX, window->State.MouseY )
972         );
973         fgState.Modifiers = INVALID_MODIFIERS;
974     }
975     break;
976
977     case WM_CAPTURECHANGED:
978         if (!lParam || !fgWindowByHandle((HWND)lParam))
979             /* Capture released or capture taken by non-FreeGLUT window */
980             setCaptureActive = 0;
981         /* Docs advise a redraw */
982         InvalidateRect( hWnd, NULL, GL_FALSE );
983         UpdateWindow(hWnd);
984         lRet = 0;   /* Per docs, should return zero */
985         break;
986
987 #if !defined(_WIN32_WCE)
988     case WM_SYNCPAINT:  /* 0x0088 */
989         /* Another window has moved, need to update this one */
990         window->State.Redisplay = GL_TRUE;
991         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
992         /* Help screen says this message must be passed to "DefWindowProc" */
993         break;
994
995     case WM_DISPLAYCHANGE: /* 0x007E */
996         /* The system display resolution/depth has changed */
997         fgDisplay.ScreenWidth = LOWORD(lParam);
998         fgDisplay.ScreenHeight = HIWORD(lParam);
999         break;
1000
1001     case WM_SYSCOMMAND :  /* 0x0112 */
1002         {
1003           /*
1004            * We have received a system command message.  Try to act on it.
1005            * The commands are passed in through the "wParam" parameter:
1006            * The least significant digit seems to be which edge of the window
1007            * is being used for a resize event:
1008            *     4  3  5
1009            *     1     2
1010            *     7  6  8
1011            * Congratulations and thanks to Richard Rauch for figuring this out..
1012            */
1013             switch ( wParam & 0xfff0 )
1014             {
1015             case SC_SIZE       :
1016                 break ;
1017
1018             case SC_MOVE       :
1019                 break ;
1020
1021             case SC_MINIMIZE   :
1022                 /* User has clicked on the "-" to minimize the window */
1023                 /* Turning off the visibility is handled in WM_SIZE handler */
1024
1025                 break ;
1026
1027             case SC_MAXIMIZE   :
1028                 break ;
1029
1030             case SC_NEXTWINDOW :
1031                 break ;
1032
1033             case SC_PREVWINDOW :
1034                 break ;
1035
1036             case SC_CLOSE      :
1037                 /* Followed very closely by a WM_CLOSE message */
1038                 break ;
1039
1040             case SC_VSCROLL    :
1041                 break ;
1042
1043             case SC_HSCROLL    :
1044                 break ;
1045
1046             case SC_MOUSEMENU  :
1047                 break ;
1048
1049             case SC_KEYMENU    :
1050                 break ;
1051
1052             case SC_ARRANGE    :
1053                 break ;
1054
1055             case SC_RESTORE    :
1056                 break ;
1057
1058             case SC_TASKLIST   :
1059                 break ;
1060
1061             case SC_SCREENSAVE :
1062                 break ;
1063
1064             case SC_HOTKEY     :
1065                 break ;
1066
1067 #if(WINVER >= 0x0400)
1068             case SC_DEFAULT    :
1069                 break ;
1070
1071             case SC_MONITORPOWER    :
1072                 break ;
1073
1074             case SC_CONTEXTHELP    :
1075                 break ;
1076 #endif /* WINVER >= 0x0400 */
1077
1078             default:
1079 #if _DEBUG
1080                 fgWarning( "Unknown wParam type 0x%x", wParam );
1081 #endif
1082                 break;
1083             }
1084         }
1085 #endif /* !defined(_WIN32_WCE) */
1086
1087         /* We need to pass the message on to the operating system as well */
1088         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1089         break;
1090
1091 #ifdef WM_TOUCH
1092         /* handle multi-touch messages */
1093         case WM_TOUCH:
1094         {
1095                 unsigned int numInputs = (unsigned int)wParam;
1096                 unsigned int i = 0;
1097                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1098
1099                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1100                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1101                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1102                 }
1103
1104                 if (!fghGetTouchInputInfo) { 
1105                         free( (void*)ti );
1106                         break;
1107                 }
1108
1109                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1110                         /* Handle each contact point */
1111                         for (i = 0; i < numInputs; ++i ) {
1112
1113                                 POINT tp;
1114                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1115                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1116                                 ScreenToClient( hWnd, &tp );
1117
1118                                 ti[i].dwID = ti[i].dwID * 2;
1119
1120                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1121                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1122                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1123                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1124                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1125                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1126                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1127                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1128                                 }
1129                         }
1130                 }
1131                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1132                 free( (void*)ti );
1133                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1134                 break;
1135         }
1136 #endif
1137     default:
1138         /* Handle unhandled messages */
1139         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1140         break;
1141     }
1142
1143     return lRet;
1144 }
1145
1146
1147 /* Step through the work list */
1148 void fgPlatformProcessWork(SFG_Window *window)
1149 {
1150     unsigned int workMask = window->State.WorkMask;
1151     /* Now clear it so that any callback generated by the actions below can set work again */
1152     window->State.WorkMask = 0;
1153
1154     /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc
1155      * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when
1156      * they are opened, and work is done before displaying in the mainloop.
1157      */
1158     if (workMask & GLUT_INIT_WORK)
1159     {
1160         RECT windowRect;
1161
1162         /* Notify windowStatus/visibility */
1163         fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE);
1164
1165         /* get and notify window's position */
1166         GetWindowRect(window->Window.Handle,&windowRect);
1167         fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE);
1168
1169         /* get and notify window's size */
1170         GetClientRect(window->Window.Handle,&windowRect);
1171         fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE);
1172
1173         /* Call init context callback */
1174         INVOKE_WCB( *window, InitContext, ());
1175
1176         /* Lastly, check if we have a display callback, error out if not
1177          * This is the right place to do it, as the redisplay will be
1178          * next right after we exit this function, so there is no more
1179          * opportunity for the user to register a callback for this window.
1180          */
1181         if (!FETCH_WCB(*window, Display))
1182             fgError ( "ERROR:  No display callback registered for window %d\n", window->ID );
1183     }
1184
1185     /* On windows we can position, resize and change z order at the same time */
1186     if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
1187     {
1188         UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1189         HWND insertAfter = HWND_TOP;
1190         RECT clientRect;
1191
1192 #if !defined(_WIN32_WCE) /* FIXME: what about WinCE */
1193         if (workMask & GLUT_FULL_SCREEN_WORK)
1194         {
1195             /* This asks us to toggle fullscreen mode */
1196             flags |= SWP_FRAMECHANGED;
1197
1198             if (window->State.IsFullscreen)
1199             {
1200                 /* If we are fullscreen, resize the current window back to its original size */
1201                 /* 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); */
1202
1203                 /* restore style of window before making it fullscreen */
1204                 SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle);
1205                 SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx);
1206
1207                 /* Then set up resize/reposition, unless user already queued up reshape/position work */
1208                 if (!(workMask & GLUT_POSITION_WORK))
1209                 {
1210                     workMask |= GLUT_POSITION_WORK;
1211                     window->State.DesiredXpos   = window->State.pWState.OldRect.left;
1212                     window->State.DesiredYpos   = window->State.pWState.OldRect.top;
1213                 }
1214                 if (!(workMask & GLUT_SIZE_WORK))
1215                 {
1216                     workMask |= GLUT_SIZE_WORK;
1217                     window->State.DesiredWidth  = window->State.pWState.OldRect.right  - window->State.pWState.OldRect.left;
1218                     window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top;
1219                 }
1220                 
1221                 /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */
1222             }
1223             else
1224             {
1225                 /* we are currently not fullscreen, go to fullscreen:
1226                  * remove window decoration and then maximize
1227                  */
1228                 RECT rect;
1229                 HMONITOR hMonitor;
1230                 MONITORINFO mi;
1231         
1232                 /* save current window rect, style, exstyle and maximized state */
1233                 window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle);
1234                 if (window->State.pWState.OldMaximized)
1235                     /* We force the window into restored mode before going
1236                      * fullscreen because Windows doesn't seem to hide the
1237                      * taskbar if the window is in the maximized state.
1238                      */
1239                     SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
1240
1241                 fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE );
1242                 window->State.pWState.OldStyle   = GetWindowLong(window->Window.Handle, GWL_STYLE);
1243                 window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
1244
1245                 /* remove decorations from style */
1246                 SetWindowLong(window->Window.Handle, GWL_STYLE,
1247                               window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME));
1248                 SetWindowLong(window->Window.Handle, GWL_EXSTYLE,
1249                               window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME |
1250                               WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1251
1252                 /* For fullscreen mode, find the monitor that is covered the most
1253                  * by the window and get its rect as the resize target.
1254                      */
1255                 GetWindowRect(window->Window.Handle, &rect);
1256                 hMonitor= MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
1257                 mi.cbSize = sizeof(mi);
1258                 GetMonitorInfo(hMonitor, &mi);
1259                 rect = mi.rcMonitor;
1260
1261                 /* then setup window resize, overwriting other work queued on the window */
1262                 window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK;
1263                 window->State.WorkMask &= ~GLUT_ZORDER_WORK;
1264                 window->State.DesiredXpos   = rect.left;
1265                 window->State.DesiredYpos   = rect.top;
1266                 window->State.DesiredWidth  = rect.right  - rect.left;
1267                 window->State.DesiredHeight = rect.bottom - rect.top;
1268             }
1269         }
1270 #endif /*!defined(_WIN32_WCE) */
1271
1272         /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */
1273         {
1274             /* get rect describing window's current position and size, 
1275              * in screen coordinates and in FreeGLUT format
1276              * (size (right-left, bottom-top) is client area size, top and left
1277              * are outside of window including decorations).
1278              */
1279             fghGetClientArea( &clientRect, window, TRUE );
1280
1281             if (workMask & GLUT_POSITION_WORK)
1282             {
1283                 flags &= ~SWP_NOMOVE;
1284                 
1285                 /* Move rect so that top-left is at requested position */
1286                 /* This also automatically makes sure that child window requested coordinates are relative
1287                  * to top-left of parent's client area (needed input for SetWindowPos on child windows),
1288                  * so no need to further correct rect for child windows below (childs don't have decorations either).
1289                  */
1290                 OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top);
1291             }
1292             if (workMask & GLUT_SIZE_WORK)
1293             {
1294                 flags &= ~SWP_NOSIZE;
1295                 
1296                 /* Note on maximizing behavior of Windows: the resize borders are off
1297                  * the screen such that the client area extends all the way from the
1298                  * leftmost corner to the rightmost corner to maximize screen real
1299                  * estate. A caption is still shown however to allow interaction with
1300                  * the window controls. This is default behavior of Windows that
1301                  * FreeGLUT sticks with. To alter, one would have to check if
1302                  * WS_MAXIMIZE style is set when a resize event is triggered, and
1303                  * then manually correct the windowRect to put the borders back on
1304                  * screen.
1305                  */
1306
1307                 /* Set new size of window, WxH specify client area */
1308                 clientRect.right    = clientRect.left + window->State.DesiredWidth;
1309                 clientRect.bottom   = clientRect.top  + window->State.DesiredHeight;
1310             }
1311             if (workMask & GLUT_ZORDER_WORK)
1312             {
1313                 flags &= ~SWP_NOZORDER;
1314
1315                 /* Could change this to push it down or up one window at a time with some
1316                  * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT.
1317                  * What would be consistent with X11? Win32 GLUT does what we do here...
1318                  */
1319                 if (window->State.DesiredZOrder < 0)
1320                     insertAfter = HWND_BOTTOM;
1321             }
1322         }
1323
1324         /* Adjust for window decorations
1325          * Child windows don't have decoration, so no need to correct
1326          */
1327         if (!window->Parent)
1328             /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
1329             fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE);
1330     
1331         /* Do the requested positioning, moving, and z order push/pop. */
1332         SetWindowPos( window->Window.Handle,
1333                       insertAfter,
1334                       clientRect.left, clientRect.top,
1335                       clientRect.right - clientRect.left,
1336                       clientRect.bottom- clientRect.top,
1337                       flags
1338         );
1339
1340         /* Finish off the fullscreen operation we were doing, if any */
1341         if (workMask & GLUT_FULL_SCREEN_WORK)
1342         {
1343             if (window->State.IsFullscreen)
1344             {
1345                 /* leaving fullscreen, restore maximized state, if any */
1346                 if (window->State.pWState.OldMaximized)
1347                     SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1348
1349                 window->State.IsFullscreen = GL_FALSE;
1350             }
1351             else
1352                 window->State.IsFullscreen = GL_TRUE;
1353         }
1354     }
1355
1356     if (workMask & GLUT_VISIBILITY_WORK)
1357     {
1358         /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */
1359         int cmdShow = 0;
1360         SFG_Window *win = window;
1361         switch (window->State.DesiredVisibility)
1362         {
1363         case DesireHiddenState:
1364             cmdShow = SW_HIDE;
1365             break;
1366         case DesireIconicState:
1367             cmdShow = SW_MINIMIZE;
1368             /* Call on top-level window */
1369             while (win->Parent)
1370                 win = win->Parent;
1371             break;
1372         case DesireNormalState:
1373             if (win->IsMenu)
1374                 cmdShow = SW_SHOWNOACTIVATE;    /* Just show, don't activate if its a menu */
1375             else
1376                 cmdShow = SW_SHOW;
1377             break;
1378         }
1379
1380         ShowWindow( win->Window.Handle, cmdShow );
1381     }
1382 }