6d286e75142f2225d7e5c0178647791f0f5cdd56
[freeglut] / src / mswin / fg_main_mswin.c
1 /*
2  * fg_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(HWND newFocusWnd);
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 #ifdef _DEBUG
66 /*
67  * WM_ message to string, for debugging
68  * This is taken from the 8.0 SDK, so Windows 8 API and everything earlier is included
69  */
70 struct WM_MESSAGE_MAP
71 {
72     UINT    nMsg;
73     LPCSTR  lpszMsg;
74 };
75 #define DEFINE_MESSAGE(wm){ wm, #wm }
76 static struct WM_MESSAGE_MAP allMessages[] =
77 {
78     DEFINE_MESSAGE(WM_NULL),
79     DEFINE_MESSAGE(WM_CREATE),
80     DEFINE_MESSAGE(WM_DESTROY),
81     DEFINE_MESSAGE(WM_MOVE),
82     DEFINE_MESSAGE(WM_SIZE),
83
84     DEFINE_MESSAGE(WM_ACTIVATE),
85     DEFINE_MESSAGE(WM_SETFOCUS),
86     DEFINE_MESSAGE(WM_KILLFOCUS),
87     DEFINE_MESSAGE(WM_ENABLE),
88     DEFINE_MESSAGE(WM_SETREDRAW),
89     DEFINE_MESSAGE(WM_SETTEXT),
90     DEFINE_MESSAGE(WM_GETTEXT),
91     DEFINE_MESSAGE(WM_GETTEXTLENGTH),
92     DEFINE_MESSAGE(WM_PAINT),
93     DEFINE_MESSAGE(WM_CLOSE),
94 #   ifndef _WIN32_WCE
95         DEFINE_MESSAGE(WM_QUERYENDSESSION),
96         DEFINE_MESSAGE(WM_QUERYOPEN),
97         DEFINE_MESSAGE(WM_ENDSESSION),
98 #   endif
99     DEFINE_MESSAGE(WM_QUIT),
100     DEFINE_MESSAGE(WM_ERASEBKGND),
101     DEFINE_MESSAGE(WM_SYSCOLORCHANGE),
102     DEFINE_MESSAGE(WM_SHOWWINDOW),
103     DEFINE_MESSAGE(WM_WININICHANGE),
104
105     DEFINE_MESSAGE(WM_DEVMODECHANGE),
106     DEFINE_MESSAGE(WM_ACTIVATEAPP),
107     DEFINE_MESSAGE(WM_FONTCHANGE),
108     DEFINE_MESSAGE(WM_TIMECHANGE),
109     DEFINE_MESSAGE(WM_CANCELMODE),
110     DEFINE_MESSAGE(WM_SETCURSOR),
111     DEFINE_MESSAGE(WM_MOUSEACTIVATE),
112     DEFINE_MESSAGE(WM_CHILDACTIVATE),
113     DEFINE_MESSAGE(WM_QUEUESYNC),
114
115     DEFINE_MESSAGE(WM_GETMINMAXINFO),
116
117     DEFINE_MESSAGE(WM_PAINTICON),
118     DEFINE_MESSAGE(WM_ICONERASEBKGND),
119     DEFINE_MESSAGE(WM_NEXTDLGCTL),
120     DEFINE_MESSAGE(WM_SPOOLERSTATUS),
121     DEFINE_MESSAGE(WM_DRAWITEM),
122     DEFINE_MESSAGE(WM_MEASUREITEM),
123     DEFINE_MESSAGE(WM_DELETEITEM),
124     DEFINE_MESSAGE(WM_VKEYTOITEM),
125     DEFINE_MESSAGE(WM_CHARTOITEM),
126     DEFINE_MESSAGE(WM_SETFONT),
127     DEFINE_MESSAGE(WM_GETFONT),
128     DEFINE_MESSAGE(WM_SETHOTKEY),
129     DEFINE_MESSAGE(WM_GETHOTKEY),
130     DEFINE_MESSAGE(WM_QUERYDRAGICON),
131     DEFINE_MESSAGE(WM_COMPAREITEM),
132 #   if(WINVER >= 0x0500)
133 #       ifndef _WIN32_WCE
134             DEFINE_MESSAGE(WM_GETOBJECT),
135     #   endif
136 #   endif /* WINVER >= 0x0500 */
137     DEFINE_MESSAGE(WM_COMPACTING),
138     DEFINE_MESSAGE(WM_COMMNOTIFY),
139     DEFINE_MESSAGE(WM_WINDOWPOSCHANGING),
140     DEFINE_MESSAGE(WM_WINDOWPOSCHANGED),
141
142     DEFINE_MESSAGE(WM_POWER),
143
144     DEFINE_MESSAGE(WM_COPYDATA),
145     DEFINE_MESSAGE(WM_CANCELJOURNAL),
146
147 #   if(WINVER >= 0x0400)
148         DEFINE_MESSAGE(WM_NOTIFY),
149         DEFINE_MESSAGE(WM_INPUTLANGCHANGEREQUEST),
150         DEFINE_MESSAGE(WM_INPUTLANGCHANGE),
151         DEFINE_MESSAGE(WM_TCARD),
152         DEFINE_MESSAGE(WM_HELP),
153         DEFINE_MESSAGE(WM_USERCHANGED),
154         DEFINE_MESSAGE(WM_NOTIFYFORMAT),
155
156         DEFINE_MESSAGE(WM_CONTEXTMENU),
157         DEFINE_MESSAGE(WM_STYLECHANGING),
158         DEFINE_MESSAGE(WM_STYLECHANGED),
159         DEFINE_MESSAGE(WM_DISPLAYCHANGE),
160         DEFINE_MESSAGE(WM_GETICON),
161         DEFINE_MESSAGE(WM_SETICON),
162 #   endif /* WINVER >= 0x0400 */
163
164     DEFINE_MESSAGE(WM_NCCREATE),
165     DEFINE_MESSAGE(WM_NCDESTROY),
166     DEFINE_MESSAGE(WM_NCCALCSIZE),
167     DEFINE_MESSAGE(WM_NCHITTEST),
168     DEFINE_MESSAGE(WM_NCPAINT),
169     DEFINE_MESSAGE(WM_NCACTIVATE),
170     DEFINE_MESSAGE(WM_GETDLGCODE),
171 #   ifndef _WIN32_WCE
172         DEFINE_MESSAGE(WM_SYNCPAINT),
173 #   endif
174     DEFINE_MESSAGE(WM_NCMOUSEMOVE),
175     DEFINE_MESSAGE(WM_NCLBUTTONDOWN),
176     DEFINE_MESSAGE(WM_NCLBUTTONUP),
177     DEFINE_MESSAGE(WM_NCLBUTTONDBLCLK),
178     DEFINE_MESSAGE(WM_NCRBUTTONDOWN),
179     DEFINE_MESSAGE(WM_NCRBUTTONUP),
180     DEFINE_MESSAGE(WM_NCRBUTTONDBLCLK),
181     DEFINE_MESSAGE(WM_NCMBUTTONDOWN),
182     DEFINE_MESSAGE(WM_NCMBUTTONUP),
183     DEFINE_MESSAGE(WM_NCMBUTTONDBLCLK),
184
185
186
187 #   if(_WIN32_WINNT >= 0x0500) && defined(WM_NCXBUTTONDOWN)
188         DEFINE_MESSAGE(WM_NCXBUTTONDOWN),
189         DEFINE_MESSAGE(WM_NCXBUTTONUP),
190         DEFINE_MESSAGE(WM_NCXBUTTONDBLCLK),
191 #   endif /* _WIN32_WINNT >= 0x0500 */
192
193
194 #   if(_WIN32_WINNT >= 0x0501)
195         DEFINE_MESSAGE(WM_INPUT_DEVICE_CHANGE),
196 #   endif /* _WIN32_WINNT >= 0x0501 */
197
198 #   if(_WIN32_WINNT >= 0x0501)
199         DEFINE_MESSAGE(WM_INPUT),
200 #   endif /* _WIN32_WINNT >= 0x0501 */
201
202     DEFINE_MESSAGE(WM_KEYDOWN),
203     DEFINE_MESSAGE(WM_KEYUP),
204     DEFINE_MESSAGE(WM_CHAR),
205     DEFINE_MESSAGE(WM_DEADCHAR),
206     DEFINE_MESSAGE(WM_SYSKEYDOWN),
207     DEFINE_MESSAGE(WM_SYSKEYUP),
208     DEFINE_MESSAGE(WM_SYSCHAR),
209     DEFINE_MESSAGE(WM_SYSDEADCHAR),
210 #   if(_WIN32_WINNT >= 0x0501)
211         DEFINE_MESSAGE(WM_UNICHAR),
212 #   endif /* _WIN32_WINNT >= 0x0501 */
213
214 #   if(WINVER >= 0x0400)
215         DEFINE_MESSAGE(WM_IME_STARTCOMPOSITION),
216         DEFINE_MESSAGE(WM_IME_ENDCOMPOSITION),
217         DEFINE_MESSAGE(WM_IME_COMPOSITION),
218         DEFINE_MESSAGE(WM_IME_KEYLAST),
219 #   endif /* WINVER >= 0x0400 */
220
221     DEFINE_MESSAGE(WM_INITDIALOG),
222     DEFINE_MESSAGE(WM_COMMAND),
223     DEFINE_MESSAGE(WM_SYSCOMMAND),
224     DEFINE_MESSAGE(WM_TIMER),
225     DEFINE_MESSAGE(WM_HSCROLL),
226     DEFINE_MESSAGE(WM_VSCROLL),
227     DEFINE_MESSAGE(WM_INITMENU),
228     DEFINE_MESSAGE(WM_INITMENUPOPUP),
229 #   if(WINVER >= 0x0601)
230         DEFINE_MESSAGE(WM_GESTURE),
231         DEFINE_MESSAGE(WM_GESTURENOTIFY),
232 #   endif /* WINVER >= 0x0601 */
233     DEFINE_MESSAGE(WM_MENUSELECT),
234     DEFINE_MESSAGE(WM_MENUCHAR),
235     DEFINE_MESSAGE(WM_ENTERIDLE),
236 #   if(WINVER >= 0x0500)
237 #       ifndef _WIN32_WCE
238             DEFINE_MESSAGE(WM_MENURBUTTONUP),
239             DEFINE_MESSAGE(WM_MENUDRAG),
240             DEFINE_MESSAGE(WM_MENUGETOBJECT),
241             DEFINE_MESSAGE(WM_UNINITMENUPOPUP),
242             DEFINE_MESSAGE(WM_MENUCOMMAND),
243
244 #           if(_WIN32_WINNT >= 0x0500) && defined(WM_CHANGEUISTATE)
245                 DEFINE_MESSAGE(WM_CHANGEUISTATE),
246                 DEFINE_MESSAGE(WM_UPDATEUISTATE),
247                 DEFINE_MESSAGE(WM_QUERYUISTATE),
248 #           endif /* _WIN32_WINNT >= 0x0500 */
249
250 #       endif
251 #   endif /* WINVER >= 0x0500 */
252
253     DEFINE_MESSAGE(WM_CTLCOLORMSGBOX),
254     DEFINE_MESSAGE(WM_CTLCOLOREDIT),
255     DEFINE_MESSAGE(WM_CTLCOLORLISTBOX),
256     DEFINE_MESSAGE(WM_CTLCOLORBTN),
257     DEFINE_MESSAGE(WM_CTLCOLORDLG),
258     DEFINE_MESSAGE(WM_CTLCOLORSCROLLBAR),
259     DEFINE_MESSAGE(WM_CTLCOLORSTATIC),
260 #   define MN_GETHMENU                     0x01E1
261
262     DEFINE_MESSAGE(WM_MOUSEMOVE),
263     DEFINE_MESSAGE(WM_LBUTTONDOWN),
264     DEFINE_MESSAGE(WM_LBUTTONUP),
265     DEFINE_MESSAGE(WM_LBUTTONDBLCLK),
266     DEFINE_MESSAGE(WM_RBUTTONDOWN),
267     DEFINE_MESSAGE(WM_RBUTTONUP),
268     DEFINE_MESSAGE(WM_RBUTTONDBLCLK),
269     DEFINE_MESSAGE(WM_MBUTTONDOWN),
270     DEFINE_MESSAGE(WM_MBUTTONUP),
271     DEFINE_MESSAGE(WM_MBUTTONDBLCLK),
272 #   if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
273         DEFINE_MESSAGE(WM_MOUSEWHEEL),
274 #   endif
275 #   if (_WIN32_WINNT >= 0x0500) && defined(WM_XBUTTONDOWN)
276         DEFINE_MESSAGE(WM_XBUTTONDOWN),
277         DEFINE_MESSAGE(WM_XBUTTONUP),
278         DEFINE_MESSAGE(WM_XBUTTONDBLCLK),
279 #   endif
280 #   if (_WIN32_WINNT >= 0x0600)
281         DEFINE_MESSAGE(WM_MOUSEHWHEEL),
282 #   endif
283
284
285
286     DEFINE_MESSAGE(WM_PARENTNOTIFY),
287     DEFINE_MESSAGE(WM_ENTERMENULOOP),
288     DEFINE_MESSAGE(WM_EXITMENULOOP),
289
290 #   if(WINVER >= 0x0400)
291         DEFINE_MESSAGE(WM_NEXTMENU),
292         DEFINE_MESSAGE(WM_SIZING),
293         DEFINE_MESSAGE(WM_CAPTURECHANGED),
294         DEFINE_MESSAGE(WM_MOVING),
295 #   endif /* WINVER >= 0x0400 */
296
297 #   if(WINVER >= 0x0400)
298         DEFINE_MESSAGE(WM_POWERBROADCAST),
299         DEFINE_MESSAGE(WM_DEVICECHANGE),
300 #   endif /* WINVER >= 0x0400 */
301
302     DEFINE_MESSAGE(WM_MDICREATE),
303     DEFINE_MESSAGE(WM_MDIDESTROY),
304     DEFINE_MESSAGE(WM_MDIACTIVATE),
305     DEFINE_MESSAGE(WM_MDIRESTORE),
306     DEFINE_MESSAGE(WM_MDINEXT),
307     DEFINE_MESSAGE(WM_MDIMAXIMIZE),
308     DEFINE_MESSAGE(WM_MDITILE),
309     DEFINE_MESSAGE(WM_MDICASCADE),
310     DEFINE_MESSAGE(WM_MDIICONARRANGE),
311     DEFINE_MESSAGE(WM_MDIGETACTIVE),
312
313
314     DEFINE_MESSAGE(WM_MDISETMENU),
315     DEFINE_MESSAGE(WM_ENTERSIZEMOVE),
316     DEFINE_MESSAGE(WM_EXITSIZEMOVE),
317     DEFINE_MESSAGE(WM_DROPFILES),
318     DEFINE_MESSAGE(WM_MDIREFRESHMENU),
319
320 #   if(WINVER >= 0x0602)
321         DEFINE_MESSAGE(WM_POINTERDEVICECHANGE),
322         DEFINE_MESSAGE(WM_POINTERDEVICEINRANGE),
323         DEFINE_MESSAGE(WM_POINTERDEVICEOUTOFRANGE),
324 #   endif /* WINVER >= 0x0602 */
325
326 #   if(WINVER >= 0x0601)
327         DEFINE_MESSAGE(WM_TOUCH),
328 #   endif /* WINVER >= 0x0601 */
329
330 #   if(WINVER >= 0x0602)
331         DEFINE_MESSAGE(WM_NCPOINTERUPDATE),
332         DEFINE_MESSAGE(WM_NCPOINTERDOWN),
333         DEFINE_MESSAGE(WM_NCPOINTERUP),
334         DEFINE_MESSAGE(WM_POINTERUPDATE),
335         DEFINE_MESSAGE(WM_POINTERDOWN),
336         DEFINE_MESSAGE(WM_POINTERUP),
337         DEFINE_MESSAGE(WM_POINTERENTER),
338         DEFINE_MESSAGE(WM_POINTERLEAVE),
339         DEFINE_MESSAGE(WM_POINTERACTIVATE),
340         DEFINE_MESSAGE(WM_POINTERCAPTURECHANGED),
341         DEFINE_MESSAGE(WM_TOUCHHITTESTING),
342         DEFINE_MESSAGE(WM_POINTERWHEEL),
343         DEFINE_MESSAGE(WM_POINTERHWHEEL),
344 #   endif /* WINVER >= 0x0602 */
345
346
347 #   if(WINVER >= 0x0400)
348         DEFINE_MESSAGE(WM_IME_SETCONTEXT),
349         DEFINE_MESSAGE(WM_IME_NOTIFY),
350         DEFINE_MESSAGE(WM_IME_CONTROL),
351         DEFINE_MESSAGE(WM_IME_COMPOSITIONFULL),
352         DEFINE_MESSAGE(WM_IME_SELECT),
353         DEFINE_MESSAGE(WM_IME_CHAR),
354 #   endif /* WINVER >= 0x0400 */
355 #   if(WINVER >= 0x0500)
356         DEFINE_MESSAGE(WM_IME_REQUEST),
357 #   endif /* WINVER >= 0x0500 */
358 #   if(WINVER >= 0x0400)
359         DEFINE_MESSAGE(WM_IME_KEYDOWN),
360         DEFINE_MESSAGE(WM_IME_KEYUP),
361 #   endif /* WINVER >= 0x0400 */
362
363 #   if((_WIN32_WINNT >= 0x0400) || (WINVER >= 0x0500))
364         DEFINE_MESSAGE(WM_MOUSEHOVER),
365         DEFINE_MESSAGE(WM_MOUSELEAVE),
366 #   endif
367 #   if(WINVER >= 0x0500) && defined(WM_NCMOUSEHOVER)
368         DEFINE_MESSAGE(WM_NCMOUSEHOVER),
369         DEFINE_MESSAGE(WM_NCMOUSELEAVE),
370 #   endif /* WINVER >= 0x0500 */
371
372 #   if(_WIN32_WINNT >= 0x0501)
373         DEFINE_MESSAGE(WM_WTSSESSION_CHANGE),
374 #   endif /* _WIN32_WINNT >= 0x0501 */
375
376     DEFINE_MESSAGE(WM_CUT),
377     DEFINE_MESSAGE(WM_COPY),
378     DEFINE_MESSAGE(WM_PASTE),
379     DEFINE_MESSAGE(WM_CLEAR),
380     DEFINE_MESSAGE(WM_UNDO),
381     DEFINE_MESSAGE(WM_RENDERFORMAT),
382     DEFINE_MESSAGE(WM_RENDERALLFORMATS),
383     DEFINE_MESSAGE(WM_DESTROYCLIPBOARD),
384     DEFINE_MESSAGE(WM_DRAWCLIPBOARD),
385     DEFINE_MESSAGE(WM_PAINTCLIPBOARD),
386     DEFINE_MESSAGE(WM_VSCROLLCLIPBOARD),
387     DEFINE_MESSAGE(WM_SIZECLIPBOARD),
388     DEFINE_MESSAGE(WM_ASKCBFORMATNAME),
389     DEFINE_MESSAGE(WM_CHANGECBCHAIN),
390     DEFINE_MESSAGE(WM_HSCROLLCLIPBOARD),
391     DEFINE_MESSAGE(WM_QUERYNEWPALETTE),
392     DEFINE_MESSAGE(WM_PALETTEISCHANGING),
393     DEFINE_MESSAGE(WM_PALETTECHANGED),
394     DEFINE_MESSAGE(WM_HOTKEY),
395
396 #   if(WINVER >= 0x0400)
397         DEFINE_MESSAGE(WM_PRINT),
398         DEFINE_MESSAGE(WM_PRINTCLIENT),
399 #   endif /* WINVER >= 0x0400 */
400
401 #   if(_WIN32_WINNT >= 0x0500) && defined(WM_APPCOMMAND)
402         DEFINE_MESSAGE(WM_APPCOMMAND),
403 #   endif /* _WIN32_WINNT >= 0x0500 */
404
405 #   if(_WIN32_WINNT >= 0x0501)
406         DEFINE_MESSAGE(WM_THEMECHANGED),
407 #   endif /* _WIN32_WINNT >= 0x0501 */
408
409
410 #   if(_WIN32_WINNT >= 0x0501)
411         DEFINE_MESSAGE(WM_CLIPBOARDUPDATE),
412 #   endif /* _WIN32_WINNT >= 0x0501 */
413
414 #   if(_WIN32_WINNT >= 0x0600)
415         DEFINE_MESSAGE(WM_DWMCOMPOSITIONCHANGED),
416         DEFINE_MESSAGE(WM_DWMNCRENDERINGCHANGED),
417         DEFINE_MESSAGE(WM_DWMCOLORIZATIONCOLORCHANGED),
418         DEFINE_MESSAGE(WM_DWMWINDOWMAXIMIZEDCHANGE),
419 #   endif /* _WIN32_WINNT >= 0x0600 */
420
421 #   if(_WIN32_WINNT >= 0x0601)
422         DEFINE_MESSAGE(WM_DWMSENDICONICTHUMBNAIL),
423         DEFINE_MESSAGE(WM_DWMSENDICONICLIVEPREVIEWBITMAP),
424 #   endif /* _WIN32_WINNT >= 0x0601 */
425
426
427 #   if(WINVER >= 0x0600)
428         DEFINE_MESSAGE(WM_GETTITLEBARINFOEX),
429 #   endif /* WINVER >= 0x0600 */
430     { 0, NULL, }    /* end of message list */
431 };
432 #undef DEFINE_MESSAGE
433
434 char* WMMsg2Str(DWORD dwMessage)
435 {
436     struct WM_MESSAGE_MAP* pMapMsg = allMessages;
437     for (/*null*/; pMapMsg->lpszMsg != NULL; pMapMsg++)
438     {
439         if (pMapMsg->nMsg == dwMessage )
440         {
441             return (char *)pMapMsg->lpszMsg;
442         }
443     }
444     return "";
445 }
446 #endif /* _DEBUG */
447
448
449 /* Get system time, taking special precautions against 32bit timer wrap.
450    We use timeGetTime and not GetTickCount because of its better stability,
451    and because we can increase its granularity (to 1 ms in
452    fgPlatformInitialize). For that reason we can't use GetTickCount64 which
453    wouldn't have the wrap issue.
454    Credit: this is based on code in glibc (https://mail.gnome.org/archives/commits-list/2011-November/msg04588.html)
455    */
456 static fg_time_t lastTime32 = 0;
457 static fg_time_t timeEpoch = 0;
458 void fgPlatformInitSystemTime()
459 {
460 #if defined(_WIN32_WCE)
461     lastTime32 = GetTickCount();
462 #else
463     lastTime32 = timeGetTime();
464 #endif
465 }
466 fg_time_t fgPlatformSystemTime ( void )
467 {
468     fg_time_t currTime32;
469 #if defined(_WIN32_WCE)
470     currTime32 = GetTickCount();
471 #else
472     currTime32 = timeGetTime();
473 #endif
474     /* Check if we just wrapped */
475     if (currTime32 < lastTime32)
476         timeEpoch++;
477
478     lastTime32 = currTime32;
479
480     return currTime32 | timeEpoch << 32;
481 }
482
483
484 void fgPlatformSleepForEvents( fg_time_t msec )
485 {
486     MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
487 }
488
489
490 void fgPlatformProcessSingleEvent ( void )
491 {
492     MSG stMsg;
493
494     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
495
496     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
497     {
498         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
499         {
500             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
501             {
502                 fgDeinitialize( );
503                 exit( 0 );
504             }
505             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
506                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
507
508             return;
509         }
510
511         TranslateMessage( &stMsg );
512         DispatchMessage( &stMsg );
513     }
514 }
515
516
517
518 static void fghPlatformOnWindowStatusNotify(SFG_Window *window, GLboolean visState, GLboolean forceNotify)
519 {
520     GLboolean notify = GL_FALSE;
521     SFG_Window* child;
522
523     if (window->State.Visible != visState)
524     {
525         window->State.Visible = visState;
526
527         /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */
528         if (!window->Parent && window->State.pWState.IconTitle)
529         {
530             if (visState)
531                 /* visible, set window title */
532                 SetWindowText( window->Window.Handle, window->State.pWState.WindowTitle );
533             else
534                 /* not visible, set icon title */
535                 SetWindowText( window->Window.Handle, window->State.pWState.IconTitle );
536         }
537
538         notify = GL_TRUE;
539     }
540
541     if (notify || forceNotify)
542     {
543         SFG_Window *saved_window = fgStructure.CurrentWindow;
544
545         /* On win32 we only have two states, window displayed and window not displayed (iconified)
546          * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively.
547          */
548         INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
549         fgSetWindow( saved_window );
550     }
551
552     /* Also set windowStatus/visibility state for children */
553     for( child = ( SFG_Window * )window->Children.First;
554          child;
555          child = ( SFG_Window * )child->Node.Next )
556     {
557         fghPlatformOnWindowStatusNotify(child, visState, GL_FALSE); /* No need to propagate forceNotify. Childs get this from their own INIT_WORK */
558     }
559 }
560
561 void fgPlatformMainLoopPreliminaryWork ( void )
562 {
563     /* no-op */
564 }
565
566
567 /*
568  * Determine a GLUT modifier mask based on MS-WINDOWS system info.
569  */
570 static int fgPlatformGetModifiers (void)
571 {
572     return
573         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
574             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
575         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
576             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
577         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
578             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
579 }
580
581 /* Check whether a button (VK_*BUTTON) is currently depressed. Returns
582  * non-zero (not necessarily 1) if yes. */
583 static SHORT fgGetKeyState(int vKey)
584 {
585     /* MSDN says: "If the high-order bit is 1, the key is down; otherwise, it is up". */
586     return GetKeyState(vKey) & 0xFF00;
587 }
588
589 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
590 {
591     static unsigned char lControl = 0, lShift = 0, lAlt = 0,
592                          rControl = 0, rShift = 0, rAlt = 0;
593
594     int keypress = -1;
595
596     /* if keydown, check for repeat */
597     /* If repeat is globally switched off, it cannot be switched back on per window.
598      * But if it is globally switched on, it can be switched off per window. This matches
599      * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
600      * global state switch.
601      */
602     if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
603         return 1;
604
605     /* Remember the current modifiers state so user can query it from their callback */
606     fgState.Modifiers = fgPlatformGetModifiers( );
607
608     /* Convert the Win32 keystroke codes to GLUTtish way */
609 #   define FG_KEY(a,b) case a: keypress = b; break;
610
611     switch( wParam )
612     {
613         FG_KEY( VK_F1,     GLUT_KEY_F1        );
614         FG_KEY( VK_F2,     GLUT_KEY_F2        );
615         FG_KEY( VK_F3,     GLUT_KEY_F3        );
616         FG_KEY( VK_F4,     GLUT_KEY_F4        );
617         FG_KEY( VK_F5,     GLUT_KEY_F5        );
618         FG_KEY( VK_F6,     GLUT_KEY_F6        );
619         FG_KEY( VK_F7,     GLUT_KEY_F7        );
620         FG_KEY( VK_F8,     GLUT_KEY_F8        );
621         FG_KEY( VK_F9,     GLUT_KEY_F9        );
622         FG_KEY( VK_F10,    GLUT_KEY_F10       );
623         FG_KEY( VK_F11,    GLUT_KEY_F11       );
624         FG_KEY( VK_F12,    GLUT_KEY_F12       );
625         FG_KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
626         FG_KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
627         FG_KEY( VK_HOME,   GLUT_KEY_HOME      );
628         FG_KEY( VK_END,    GLUT_KEY_END       );
629         FG_KEY( VK_LEFT,   GLUT_KEY_LEFT      );
630         FG_KEY( VK_UP,     GLUT_KEY_UP        );
631         FG_KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
632         FG_KEY( VK_DOWN,   GLUT_KEY_DOWN      );
633         FG_KEY( VK_INSERT, GLUT_KEY_INSERT    );
634
635     /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
636      * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
637      * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
638      */
639 #define FG_KEY_EVENT(winKey,glutKey,keyStateVar)\
640     if (!keyStateVar && fgGetKeyState ( winKey ))\
641     {\
642         keypress   = glutKey;\
643         keyStateVar = 1;\
644     }\
645     else if (keyStateVar && !fgGetKeyState ( winKey ))\
646     {\
647         keypress   = glutKey;\
648         keyStateVar = 0;\
649     }
650     case VK_CONTROL:
651         FG_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
652         FG_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
653         break;
654     case VK_SHIFT:
655         FG_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
656         FG_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
657         break;
658     case VK_MENU:
659         FG_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
660         FG_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
661         break;
662 #undef KEY_EVENT
663
664     case VK_DELETE:
665         /* The delete key should be treated as an ASCII keypress: */
666         if (keydown)
667             INVOKE_WCB( *window, Keyboard,
668                         ( 127, window->State.MouseX, window->State.MouseY )
669             );
670         else
671             INVOKE_WCB( *window, KeyboardUp,
672                         ( 127, window->State.MouseX, window->State.MouseY )
673             );
674         break;
675
676 #if !defined(_WIN32_WCE)
677     default:
678         /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
679         if (!keydown)
680         {
681             BYTE state[ 256 ];
682             WORD code[ 2 ];
683
684             GetKeyboardState( state );
685
686             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
687                 wParam=code[ 0 ];
688
689             INVOKE_WCB( *window, KeyboardUp,
690                    ( (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 */
691                         window->State.MouseX, window->State.MouseY )
692             );
693         }
694 #endif
695     }
696
697 #if defined(_WIN32_WCE)
698     if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
699     {
700         if(wParam==(unsigned)gxKeyList.vkRight)
701             keypress = GLUT_KEY_RIGHT;
702         else if(wParam==(unsigned)gxKeyList.vkLeft)
703             keypress = GLUT_KEY_LEFT;
704         else if(wParam==(unsigned)gxKeyList.vkUp)
705             keypress = GLUT_KEY_UP;
706         else if(wParam==(unsigned)gxKeyList.vkDown)
707             keypress = GLUT_KEY_DOWN;
708         else if(wParam==(unsigned)gxKeyList.vkA)
709             keypress = GLUT_KEY_F1;
710         else if(wParam==(unsigned)gxKeyList.vkB)
711             keypress = GLUT_KEY_F2;
712         else if(wParam==(unsigned)gxKeyList.vkC)
713             keypress = GLUT_KEY_F3;
714         else if(wParam==(unsigned)gxKeyList.vkStart)
715             keypress = GLUT_KEY_F4;
716     }
717 #endif
718
719     if( keypress != -1 )
720         if (keydown)
721             INVOKE_WCB( *window, Special,
722                         ( keypress,
723                             window->State.MouseX, window->State.MouseY )
724             );
725         else
726             INVOKE_WCB( *window, SpecialUp,
727                         ( keypress,
728                             window->State.MouseX, window->State.MouseY )
729             );
730
731     fgState.Modifiers = INVALID_MODIFIERS;
732
733     /* SYSKEY events should be sent to default window proc for system to handle them */
734     if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
735         return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
736     else
737         return 1;
738 }
739
740 SFG_Window* fghWindowUnderCursor(SFG_Window *window)
741 {
742     /* Check if the current window that the mouse is over is a child window
743      * of the window the message was sent to. Some events only sent to main window,
744      * and when handling some messages, we need to make sure that we process
745      * callbacks on the child window instead. This mirrors how GLUT does things.
746      * returns either the original window or the found child.
747      */
748     if (window && window->Children.First)   /* This window has childs */
749     {
750         HWND hwnd;
751         SFG_Window* child_window;
752
753         /* Get mouse position at time of message */
754         DWORD mouse_pos_dw = GetMessagePos();
755         POINT mouse_pos;
756         mouse_pos.x = GET_X_LPARAM(mouse_pos_dw);
757         mouse_pos.y = GET_Y_LPARAM(mouse_pos_dw);
758         ScreenToClient( window->Window.Handle, &mouse_pos );
759
760         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
761         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 */
762         {
763             child_window = fgWindowByHandle(hwnd);
764             if (child_window)    /* Verify we got a FreeGLUT window */
765             {
766                 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
767                 window = fghWindowUnderCursor(child_window);
768             }
769         }
770     }
771
772     return window;
773 }
774
775 /*
776  * The window procedure for handling Win32 events
777  */
778 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
779 {
780     SFG_Window *window;
781     LRESULT lRet = 1;
782     static int setCaptureActive = 0;
783
784     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
785
786     window = fgWindowByHandle( hWnd );
787
788     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
789       return DefWindowProc( hWnd, uMsg, wParam, lParam );
790
791     /* printf ( "Window %3d message %s (<%04x>) %12d %12d\n", window?window->ID:0,
792              WMMsg2Str(uMsg), uMsg, wParam, lParam ); */
793
794     switch( uMsg )
795     {
796     case WM_CREATE:
797         /* The window structure is passed as the creation structure parameter... */
798         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
799         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
800                                        "fgPlatformWindowProc" );
801
802         window->Window.Handle = hWnd;
803         window->Window.pContext.Device = GetDC( hWnd );
804         if( window->IsMenu )
805         {
806             unsigned int current_DisplayMode = fgState.DisplayMode;
807             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
808 #if !defined(_WIN32_WCE)
809             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
810 #endif
811             fgState.DisplayMode = current_DisplayMode;
812
813             if( fgStructure.MenuContext )
814                 wglMakeCurrent( window->Window.pContext.Device,
815                                 fgStructure.MenuContext->MContext
816                 );
817             else
818             {
819                 fgStructure.MenuContext =
820                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
821                 fgStructure.MenuContext->MContext =
822                     wglCreateContext( window->Window.pContext.Device );
823             }
824
825             /* window->Window.Context = wglGetCurrentContext ();   */
826             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
827         }
828         else
829         {
830 #if !defined(_WIN32_WCE)
831             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
832 #endif
833
834             if( ! fgState.UseCurrentContext )
835                 window->Window.Context =
836                     wglCreateContext( window->Window.pContext.Device );
837             else
838             {
839                 window->Window.Context = wglGetCurrentContext( );
840                 if( ! window->Window.Context )
841                     window->Window.Context =
842                         wglCreateContext( window->Window.pContext.Device );
843             }
844
845 #if !defined(_WIN32_WCE)
846             fgNewWGLCreateContext( window );
847 #endif
848         }
849
850         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
851
852 #if defined(_WIN32_WCE)
853         /* Take over button handling */
854         {
855             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
856             if (dxDllLib)
857             {
858                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
859                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
860             }
861
862             if(GXOpenInput_)
863                 (*GXOpenInput_)();
864             if(GXGetDefaultKeys_)
865                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
866         }
867
868 #endif /* defined(_WIN32_WCE) */
869         break;
870
871     case WM_SIZE:
872         /* printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam)); */
873
874         /* Update visibility state of the window */
875         if (wParam==SIZE_MINIMIZED)
876             fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE);
877         else if ((wParam==SIZE_RESTORED || wParam == SIZE_MAXIMIZED) && !window->State.Visible)
878             fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE);
879
880         /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */
881         if( window->State.Visible )
882         {
883             int width, height;
884 #if defined(_WIN32_WCE)
885             width  = HIWORD(lParam);
886             height = LOWORD(lParam);
887 #else
888             width  = LOWORD(lParam);
889             height = HIWORD(lParam);
890 #endif /* defined(_WIN32_WCE) */
891
892             /* Update state and call callback, if there was a change */
893             fghOnReshapeNotify(window, width, height, GL_FALSE);
894         }
895
896         /* according to docs, should return 0 */
897         lRet = 0;
898         break;
899
900     case WM_SIZING:
901         {
902             /* User resize-dragging the window, call reshape callback and
903              * force redisplay so display keeps running during dragging.
904              * Screen still wont update when not moving the cursor though...
905              */
906             RECT rect;
907             /* PRECT prect = (PRECT) lParam;
908                printf("WM_SIZING: nc-area: %i,%i\n",prect->right-prect->left,prect->bottom-prect->top); */
909             /* Get client area, the rect in lParam is including non-client area. */
910             fghGetClientArea(&rect,window,FALSE);
911
912             /* We'll get a WM_SIZE as well, but as state has
913              * already been updated here, the fghOnReshapeNotify
914              * in the handler for that message doesn't do anything.
915              */
916             fghOnReshapeNotify(window, rect.right-rect.left, rect.bottom-rect.top, GL_FALSE);
917
918             /* Now directly call the drawing function to update
919              * window and window's childs.
920              * This mimics the WM_PAINT messages that are received during
921              * resizing. Note that we don't have a WM_MOVING handler
922              * as move-dragging doesn't generate WM_MOVE or WM_PAINT
923              * messages until the mouse is released.
924              */
925             fghRedrawWindowAndChildren(window);
926         }
927
928         /* according to docs, should return TRUE */
929         lRet = TRUE;
930         break;
931
932     case WM_MOVE:
933         {
934             /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */
935             if (!IsIconic(window->Window.Handle))
936             {
937                 RECT windowRect;
938
939                 /* lParam contains coordinates of top-left of client area.
940                  * Get top-left of non-client area of window, matching coordinates of
941                  * glutInitPosition and glutPositionWindow, but not those of
942                  * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return
943                  * top-left of client area.
944                  */
945                 GetWindowRect( window->Window.Handle, &windowRect );
946
947                 if (window->Parent)
948                 {
949                     /* For child window, we should return relative to upper-left
950                      * of parent's client area.
951                      */
952                     POINT topleft;
953                     topleft.x = windowRect.left;
954                     topleft.y = windowRect.top;
955
956                     ScreenToClient(window->Parent->Window.Handle,&topleft);
957                     windowRect.left = topleft.x;
958                     windowRect.top  = topleft.y;
959                 }
960
961                 /* Update state and call callback, if there was a change */
962                 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_FALSE);
963             }
964         }
965
966         /* according to docs, should return 0 */
967         lRet = 0;
968         break;
969
970     case WM_SETFOCUS:
971         /*printf("WM_SETFOCUS: %p\n", window );*/
972         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
973
974         SetActiveWindow( window->Window.Handle );
975         UpdateWindow ( hWnd );
976
977         break;
978
979     case WM_KILLFOCUS:
980         /*printf("WM_KILLFOCUS: %p\n", window ); */
981         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
982
983         /* Check if there are any open menus that need to be closed */
984         fgPlatformCheckMenuDeactivate((HWND)wParam);
985         break;
986
987     case WM_MOUSEACTIVATE:
988         /* Clicks should not activate the menu.
989          * Especially important when clicking on a menu's submenu item which has no effect.
990          */
991         /*printf("WM_MOUSEACTIVATE\n");*/
992         if (window->IsMenu)
993             lRet = MA_NOACTIVATEANDEAT;
994         else
995             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
996         break;
997
998     case WM_NCLBUTTONDOWN:
999     case WM_NCMBUTTONDOWN:
1000     case WM_NCRBUTTONDOWN:
1001         {
1002             SFG_Menu *menu;
1003             if (fgState.ActiveMenus && (menu = fgGetActiveMenu()))
1004                 /* user clicked non-client area of window while a menu is open. Close menu */
1005                 fgDeactivateMenu(menu->ParentWindow);
1006
1007             /* and always pass to DefWindowProc */
1008             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1009         }
1010         break;
1011
1012 #if 0
1013     case WM_ACTIVATE:
1014         /* printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); */
1015         if (LOWORD(wParam) != WA_INACTIVE)
1016         {
1017 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
1018                    window->State.Cursor ); */
1019             fgSetCursor( window, window->State.Cursor );
1020         }
1021
1022         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1023         break;
1024 #endif
1025
1026     case WM_SETCURSOR:
1027 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
1028         if( LOWORD( lParam ) == HTCLIENT )
1029         {
1030             if (!window->State.pWState.MouseTracking)
1031             {
1032                 TRACKMOUSEEVENT tme;
1033
1034                 /* Cursor just entered window, set cursor look */
1035                 fgSetCursor ( window, window->State.Cursor ) ;
1036
1037                 /* If an EntryFunc callback is specified by the user, also
1038                  * invoke that callback and start mouse tracking so that
1039                  * we get a WM_MOUSELEAVE message
1040                  */
1041                 if (FETCH_WCB( *window, Entry ))
1042                 {
1043                     SFG_Window* saved_window = fgStructure.CurrentWindow;
1044                     INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
1045                     fgSetWindow(saved_window);
1046
1047                     tme.cbSize = sizeof(TRACKMOUSEEVENT);
1048                     tme.dwFlags = TME_LEAVE;
1049                     tme.hwndTrack = window->Window.Handle;
1050                     TrackMouseEvent(&tme);
1051
1052                     window->State.pWState.MouseTracking = TRUE;
1053                 }
1054             }
1055         }
1056         else
1057             /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
1058             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1059         break;
1060
1061     case WM_MOUSELEAVE:
1062         {
1063             /* NB: This message is only received when a EntryFunc callback
1064              * is specified by the user, as that is the only condition under
1065              * which mouse tracking is setup in WM_SETCURSOR handler above
1066              */
1067             SFG_Window* saved_window = fgStructure.CurrentWindow;
1068             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
1069             fgSetWindow(saved_window);
1070
1071             window->State.pWState.MouseTracking = FALSE;
1072             lRet = 0;   /* As per docs, must return zero */
1073         }
1074         break;
1075
1076     case WM_SHOWWINDOW:
1077         /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
1078         if (wParam)
1079         {
1080             fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE);
1081             window->State.WorkMask |= GLUT_DISPLAY_WORK;
1082         }
1083         else
1084         {
1085             fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE);
1086             window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
1087         }
1088         break;
1089
1090     case WM_PAINT:
1091     {
1092         RECT rect;
1093
1094         /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
1095         if (GetUpdateRect(hWnd,&rect,FALSE))
1096         {
1097             /* Dummy begin/end paint to validate rect that needs
1098              * redrawing, then signal that a redisplay is needed.
1099              * This allows us full control about when we do any
1100              * redrawing, and is the same as what original GLUT
1101              * does.
1102              */
1103             PAINTSTRUCT ps;
1104             BeginPaint( hWnd, &ps );
1105             EndPaint( hWnd, &ps );
1106
1107             window->State.WorkMask |= GLUT_DISPLAY_WORK;
1108         }
1109         lRet = 0;   /* As per docs, should return 0 */
1110     }
1111     break;
1112
1113     case WM_CLOSE:
1114         fgDestroyWindow ( window );
1115         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
1116             PostQuitMessage(0);
1117         break;
1118
1119     case WM_DESTROY:
1120         /*
1121          * The window already got destroyed, so don't bother with it.
1122          */
1123         return 0;
1124
1125     case WM_MOUSEMOVE:
1126     {
1127         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
1128 #if defined(_WIN32_WCE)
1129         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
1130         window->State.MouseY = LOWORD( lParam );
1131 #else
1132         window->State.MouseX = GET_X_LPARAM( lParam );
1133         window->State.MouseY = GET_Y_LPARAM( lParam );
1134 #endif /* defined(_WIN32_WCE) */
1135         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1136         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1137         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1138         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1139
1140         if ( window->ActiveMenu )
1141         {
1142             fgUpdateMenuHighlight( window->ActiveMenu );
1143             break;
1144         }
1145
1146         fgState.Modifiers = fgPlatformGetModifiers( );
1147
1148         if( ( wParam & MK_LBUTTON ) ||
1149             ( wParam & MK_MBUTTON ) ||
1150             ( wParam & MK_RBUTTON ) )
1151             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1152                                            window->State.MouseY ) );
1153         else
1154             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1155                                             window->State.MouseY ) );
1156
1157         fgState.Modifiers = INVALID_MODIFIERS;
1158     }
1159     break;
1160
1161     case WM_LBUTTONDOWN:
1162     case WM_MBUTTONDOWN:
1163     case WM_RBUTTONDOWN:
1164     case WM_LBUTTONUP:
1165     case WM_MBUTTONUP:
1166     case WM_RBUTTONUP:
1167     {
1168         GLboolean pressed = GL_TRUE;
1169         int button;
1170
1171         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
1172 #if defined(_WIN32_WCE)
1173         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
1174         window->State.MouseY = LOWORD( lParam );
1175 #else
1176         window->State.MouseX = GET_X_LPARAM( lParam );
1177         window->State.MouseY = GET_Y_LPARAM( lParam );
1178 #endif /* defined(_WIN32_WCE) */
1179
1180         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1181         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1182         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1183         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1184
1185         switch( uMsg )
1186         {
1187         case WM_LBUTTONDOWN:
1188             pressed = GL_TRUE;
1189             button = GLUT_LEFT_BUTTON;
1190             break;
1191         case WM_MBUTTONDOWN:
1192             pressed = GL_TRUE;
1193             button = GLUT_MIDDLE_BUTTON;
1194             break;
1195         case WM_RBUTTONDOWN:
1196             pressed = GL_TRUE;
1197             button = GLUT_RIGHT_BUTTON;
1198             break;
1199         case WM_LBUTTONUP:
1200             pressed = GL_FALSE;
1201             button = GLUT_LEFT_BUTTON;
1202             break;
1203         case WM_MBUTTONUP:
1204             pressed = GL_FALSE;
1205             button = GLUT_MIDDLE_BUTTON;
1206             break;
1207         case WM_RBUTTONUP:
1208             pressed = GL_FALSE;
1209             button = GLUT_RIGHT_BUTTON;
1210             break;
1211         default:
1212             pressed = GL_FALSE;
1213             button = -1;
1214             break;
1215         }
1216
1217 #if !defined(_WIN32_WCE)
1218         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1219         {
1220             if( button == GLUT_LEFT_BUTTON )
1221                 button = GLUT_RIGHT_BUTTON;
1222             else
1223                 if( button == GLUT_RIGHT_BUTTON )
1224                     button = GLUT_LEFT_BUTTON;
1225         }
1226 #endif /* !defined(_WIN32_WCE) */
1227
1228         if( button == -1 )
1229             return DefWindowProc( hWnd, uMsg, lParam, wParam );
1230
1231         /*
1232          * Do not execute the application's mouse callback if a menu
1233          * is hooked to this button.  In that case an appropriate
1234          * private call should be generated.
1235          */
1236         if( fgCheckActiveMenu( window, button, pressed,
1237                                window->State.MouseX, window->State.MouseY ) )
1238             break;
1239
1240         /* Set capture so that the window captures all the mouse messages
1241          *
1242          * The mouse is not released from the window until all buttons have
1243          * been released, even if the user presses a button in another window.
1244          * This is consistent with the behavior on X11.
1245          */
1246         if ( pressed == GL_TRUE )
1247         {
1248             if (!setCaptureActive)
1249                 SetCapture ( window->Window.Handle ) ;
1250             setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
1251         }
1252         else if (!fgGetKeyState(VK_LBUTTON) && !fgGetKeyState(VK_MBUTTON) && !fgGetKeyState(VK_RBUTTON))
1253           /* Make sure all mouse buttons are released before releasing capture */
1254           ReleaseCapture () ;
1255
1256         if( ! FETCH_WCB( *window, Mouse ) )
1257             break;
1258
1259         fgSetWindow( window );
1260         fgState.Modifiers = fgPlatformGetModifiers( );
1261
1262         INVOKE_WCB(
1263             *window, Mouse,
1264             ( button,
1265               pressed ? GLUT_DOWN : GLUT_UP,
1266               window->State.MouseX,
1267               window->State.MouseY
1268             )
1269         );
1270
1271         fgState.Modifiers = INVALID_MODIFIERS;
1272
1273         /* As per docs, should return zero */
1274         lRet = 0;
1275     }
1276     break;
1277
1278     case WM_MOUSEWHEEL:
1279     {
1280         int wheel_number = 0;   /* Only one scroll wheel on windows */
1281 #if defined(_WIN32_WCE)
1282         int modkeys = LOWORD(wParam);
1283         short ticks = (short)HIWORD(wParam);
1284         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
1285         xPos = LOWORD(lParam);  -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
1286         yPos = HIWORD(lParam);
1287         */
1288 #else
1289         /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
1290         short ticks = HIWORD( wParam );
1291         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
1292         window->State.MouseX = GET_X_LPARAM( lParam );
1293         window->State.MouseY = GET_Y_LPARAM( lParam );
1294         */
1295 #endif /* defined(_WIN32_WCE) */
1296
1297         window = fghWindowUnderCursor(window);
1298
1299         fgState.MouseWheelTicks += ticks;
1300         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
1301         {
1302             int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
1303
1304             if( ! FETCH_WCB( *window, MouseWheel ) &&
1305                 ! FETCH_WCB( *window, Mouse ) )
1306                 break;
1307
1308             fgSetWindow( window );
1309             fgState.Modifiers = fgPlatformGetModifiers( );
1310
1311             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
1312             {
1313                 if( FETCH_WCB( *window, MouseWheel ) )
1314                     INVOKE_WCB( *window, MouseWheel,
1315                                 ( wheel_number,
1316                                   direction,
1317                                   window->State.MouseX,
1318                                   window->State.MouseY
1319                                 )
1320                     );
1321                 else  /* No mouse wheel, call the mouse button callback twice */
1322                 {
1323                     /*
1324                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
1325                      *  "    "   one                     +1 to 5, -1 to 6, ...
1326                      *
1327                      * XXX The below assumes that you have no more than 3 mouse
1328                      * XXX buttons.  Sorry.
1329                      */
1330                     int button = wheel_number * 2 + 3;
1331                     if( direction < 0 )
1332                         ++button;
1333                     INVOKE_WCB( *window, Mouse,
1334                                 ( button, GLUT_DOWN,
1335                                   window->State.MouseX, window->State.MouseY )
1336                     );
1337                     INVOKE_WCB( *window, Mouse,
1338                                 ( button, GLUT_UP,
1339                                   window->State.MouseX, window->State.MouseY )
1340                     );
1341                 }
1342
1343                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
1344             }
1345
1346             fgState.Modifiers = INVALID_MODIFIERS;
1347         }
1348         /* Per docs, should return zero */
1349         lRet = 0;
1350     }
1351     break ;
1352
1353     case WM_SYSKEYDOWN:
1354     case WM_KEYDOWN:
1355     {
1356         window = fghWindowUnderCursor(window);
1357         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
1358     }
1359     break;
1360
1361     case WM_SYSKEYUP:
1362     case WM_KEYUP:
1363     {
1364         window = fghWindowUnderCursor(window);
1365         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
1366     }
1367     break;
1368
1369     case WM_SYSCHAR:
1370     case WM_CHAR:
1371     {
1372       window = fghWindowUnderCursor(window);
1373
1374       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
1375             break;
1376
1377         fgState.Modifiers = fgPlatformGetModifiers( );
1378         INVOKE_WCB( *window, Keyboard,
1379                     ( (char)wParam,
1380                       window->State.MouseX, window->State.MouseY )
1381         );
1382         fgState.Modifiers = INVALID_MODIFIERS;
1383     }
1384     break;
1385
1386     case WM_CAPTURECHANGED:
1387         if (!lParam || !fgWindowByHandle((HWND)lParam))
1388             /* Capture released or capture taken by non-FreeGLUT window */
1389             setCaptureActive = 0;
1390         /* Docs advise a redraw */
1391         InvalidateRect( hWnd, NULL, GL_FALSE );
1392         UpdateWindow(hWnd);
1393         lRet = 0;   /* Per docs, should return zero */
1394         break;
1395
1396 #if !defined(_WIN32_WCE)
1397     case WM_SYNCPAINT:  /* 0x0088 */
1398         /* Another window has moved, need to update this one */
1399         window->State.WorkMask |= GLUT_DISPLAY_WORK;
1400         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1401         /* Help screen says this message must be passed to "DefWindowProc" */
1402         break;
1403
1404     case WM_DISPLAYCHANGE: /* 0x007E */
1405         /* The system display resolution/depth has changed */
1406         fgDisplay.ScreenWidth = LOWORD(lParam);
1407         fgDisplay.ScreenHeight = HIWORD(lParam);
1408         break;
1409
1410     case WM_SYSCOMMAND :  /* 0x0112 */
1411         {
1412           /*
1413            * We have received a system command message.  Try to act on it.
1414            * The commands are passed in through the "wParam" parameter:
1415            * The least significant digit seems to be which edge of the window
1416            * is being used for a resize event:
1417            *     4  3  5
1418            *     1     2
1419            *     7  6  8
1420            * Congratulations and thanks to Richard Rauch for figuring this out..
1421            */
1422             switch ( wParam & 0xfff0 )
1423             {
1424             case SC_SIZE       :
1425                 break ;
1426
1427             case SC_MOVE       :
1428                 break ;
1429
1430             case SC_MINIMIZE   :
1431                 /* User has clicked on the "-" to minimize the window */
1432                 /* Turning off the visibility is handled in WM_SIZE handler */
1433
1434                 break ;
1435
1436             case SC_MAXIMIZE   :
1437                 break ;
1438
1439             case SC_NEXTWINDOW :
1440                 break ;
1441
1442             case SC_PREVWINDOW :
1443                 break ;
1444
1445             case SC_CLOSE      :
1446                 /* Followed very closely by a WM_CLOSE message */
1447                 break ;
1448
1449             case SC_VSCROLL    :
1450                 break ;
1451
1452             case SC_HSCROLL    :
1453                 break ;
1454
1455             case SC_MOUSEMENU  :
1456                 break ;
1457
1458             case SC_KEYMENU    :
1459                 break ;
1460
1461             case SC_ARRANGE    :
1462                 break ;
1463
1464             case SC_RESTORE    :
1465                 break ;
1466
1467             case SC_TASKLIST   :
1468                 break ;
1469
1470             case SC_SCREENSAVE :
1471                 break ;
1472
1473             case SC_HOTKEY     :
1474                 break ;
1475
1476 #if(WINVER >= 0x0400)
1477             case SC_DEFAULT    :
1478                 break ;
1479
1480             case SC_MONITORPOWER    :
1481                 break ;
1482
1483             case SC_CONTEXTHELP    :
1484                 break ;
1485 #endif /* WINVER >= 0x0400 */
1486
1487             default:
1488 #if _DEBUG
1489                 fgWarning( "Unknown wParam type 0x%x", wParam );
1490 #endif
1491                 break;
1492             }
1493         }
1494 #endif /* !defined(_WIN32_WCE) */
1495
1496         /* We need to pass the message on to the operating system as well */
1497         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1498         break;
1499
1500 #ifdef WM_TOUCH
1501     /* handle multi-touch messages */
1502     case WM_TOUCH:
1503     {
1504         unsigned int numInputs = (unsigned int)wParam;
1505         unsigned int i = 0;
1506         TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1507
1508         if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1509             fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1510             fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1511         }
1512
1513         if (!fghGetTouchInputInfo) {
1514             free( (void*)ti );
1515             break;
1516         }
1517
1518         if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1519             /* Handle each contact point */
1520             for (i = 0; i < numInputs; ++i ) {
1521
1522                 POINT tp;
1523                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1524                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1525                 ScreenToClient( hWnd, &tp );
1526
1527                 ti[i].dwID = ti[i].dwID * 2;
1528
1529                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1530                     INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1531                     INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1532                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1533                     INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1534                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   {
1535                     INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1536                     INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1537                 }
1538             }
1539         }
1540         fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1541         free( (void*)ti );
1542         lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1543         break;
1544     }
1545 #endif
1546
1547 #ifdef WM_INPUT
1548     case WM_INPUT:
1549         /* Added by Jinrong Xie <stonexjr at gmail.com> for SpaceNavigator support on Windows. Dec 2014 */
1550         if (fgHasSpaceball())
1551         {
1552             fgSpaceballHandleWinEvent(hWnd, wParam, lParam);
1553         }
1554         break;
1555 #endif
1556     default:
1557         /* Handle unhandled messages */
1558         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1559         break;
1560     }
1561
1562     return lRet;
1563 }
1564
1565
1566 /* deal with work list items */
1567 void fgPlatformInitWork(SFG_Window* window)
1568 {
1569     RECT windowRect;
1570
1571     /* Notify windowStatus/visibility */
1572     fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE);
1573
1574     /* get and notify window's position */
1575     GetWindowRect(window->Window.Handle,&windowRect);
1576     fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE);
1577
1578     /* get and notify window's size */
1579     GetClientRect(window->Window.Handle,&windowRect);
1580     fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE);
1581 }
1582
1583 /* On windows we can position, resize and change z order at the same time */
1584 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
1585 {
1586     UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1587     HWND insertAfter = HWND_TOP;
1588     RECT clientRect;
1589
1590 #if !defined(_WIN32_WCE) /* FIXME: what about WinCE */
1591     if (workMask & GLUT_FULL_SCREEN_WORK)
1592     {
1593         /* This asks us to toggle fullscreen mode */
1594         flags |= SWP_FRAMECHANGED;
1595
1596         if (window->State.IsFullscreen)
1597         {
1598             /* If we are fullscreen, resize the current window back to its original size */
1599             /* 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); */
1600
1601             /* restore style of window before making it fullscreen */
1602             SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle);
1603             SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx);
1604
1605             /* Then set up resize/reposition, unless user already queued up reshape/position work */
1606             if (!(workMask & GLUT_POSITION_WORK))
1607             {
1608                 workMask |= GLUT_POSITION_WORK;
1609                 window->State.DesiredXpos   = window->State.pWState.OldRect.left;
1610                 window->State.DesiredYpos   = window->State.pWState.OldRect.top;
1611             }
1612             if (!(workMask & GLUT_SIZE_WORK))
1613             {
1614                 workMask |= GLUT_SIZE_WORK;
1615                 window->State.DesiredWidth  = window->State.pWState.OldRect.right  - window->State.pWState.OldRect.left;
1616                 window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top;
1617             }
1618
1619             /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */
1620         }
1621         else
1622         {
1623             /* we are currently not fullscreen, go to fullscreen:
1624                 * remove window decoration and then maximize
1625                 */
1626             RECT rect;
1627             HMONITOR hMonitor;
1628             MONITORINFO mi;
1629             DWORD newStyle;
1630
1631             /* save current window rect, style, exstyle and maximized state */
1632             window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle);
1633             if (window->State.pWState.OldMaximized)
1634                 /* We force the window into restored mode before going
1635                     * fullscreen because Windows doesn't seem to hide the
1636                     * taskbar if the window is in the maximized state.
1637                     */
1638                 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
1639
1640             fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE );
1641             window->State.pWState.OldStyle   = GetWindowLong(window->Window.Handle, GWL_STYLE);
1642             window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
1643
1644             /* remove decorations from style */
1645             newStyle = window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME);
1646             if (fgState.DisplayMode & GLUT_STEREO)
1647             {
1648                 /* stereo mode does not engage on nVidia stereo buffers. This kills child
1649                    windows, but we make the guess that those are rare for stereo windows. */
1650                 newStyle |= WS_POPUP;
1651             }
1652             SetWindowLong(window->Window.Handle, GWL_STYLE, newStyle);
1653             SetWindowLong(window->Window.Handle, GWL_EXSTYLE,
1654                             window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME |
1655                             WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1656
1657             /* For fullscreen mode, find the monitor that is covered the most
1658                 * by the window and get its rect as the resize target.
1659                 */
1660             hMonitor= MonitorFromWindow(window->Window.Handle, MONITOR_DEFAULTTONEAREST);
1661             mi.cbSize = sizeof(mi);
1662             GetMonitorInfo(hMonitor, &mi);
1663             rect = mi.rcMonitor;
1664
1665             /* then setup window resize, overwriting other work queued on the window */
1666             window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK;
1667             window->State.WorkMask &= ~GLUT_ZORDER_WORK;
1668             window->State.DesiredXpos   = rect.left;
1669             window->State.DesiredYpos   = rect.top;
1670             window->State.DesiredWidth  = rect.right  - rect.left;
1671             window->State.DesiredHeight = rect.bottom - rect.top;
1672         }
1673     }
1674 #endif /*!defined(_WIN32_WCE) */
1675
1676     /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */
1677     {
1678         /* get rect describing window's current position and size,
1679             * in screen coordinates and in FreeGLUT format
1680             * (size (right-left, bottom-top) is client area size, top and left
1681             * are outside of window including decorations).
1682             */
1683         fghGetClientArea( &clientRect, window, TRUE );
1684
1685         if (workMask & GLUT_POSITION_WORK)
1686         {
1687             flags &= ~SWP_NOMOVE;
1688
1689             /* Move rect so that top-left is at requested position */
1690             /* This also automatically makes sure that child window requested coordinates are relative
1691                 * to top-left of parent's client area (needed input for SetWindowPos on child windows),
1692                 * so no need to further correct rect for child windows below (childs don't have decorations either).
1693                 */
1694             OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top);
1695         }
1696         if (workMask & GLUT_SIZE_WORK)
1697         {
1698             flags &= ~SWP_NOSIZE;
1699
1700             /* Note on maximizing behavior of Windows: the resize borders are off
1701                 * the screen such that the client area extends all the way from the
1702                 * leftmost corner to the rightmost corner to maximize screen real
1703                 * estate. A caption is still shown however to allow interaction with
1704                 * the window controls. This is default behavior of Windows that
1705                 * FreeGLUT sticks with. To alter, one would have to check if
1706                 * WS_MAXIMIZE style is set when a resize event is triggered, and
1707                 * then manually correct the windowRect to put the borders back on
1708                 * screen.
1709                 */
1710
1711             /* Set new size of window, WxH specify client area */
1712             clientRect.right    = clientRect.left + window->State.DesiredWidth;
1713             clientRect.bottom   = clientRect.top  + window->State.DesiredHeight;
1714         }
1715         if (workMask & GLUT_ZORDER_WORK)
1716         {
1717             flags &= ~SWP_NOZORDER;
1718
1719             /* Could change this to push it down or up one window at a time with some
1720                 * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT.
1721                 * What would be consistent with X11? Win32 GLUT does what we do here...
1722                 */
1723             if (window->State.DesiredZOrder < 0)
1724                 insertAfter = HWND_BOTTOM;
1725         }
1726     }
1727
1728     /* Adjust for window decorations
1729         * Child windows don't have decoration, so no need to correct
1730         */
1731     if (!window->Parent)
1732         /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
1733         fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE);
1734
1735     /* Do the requested positioning, moving, and z order push/pop. */
1736     SetWindowPos( window->Window.Handle,
1737                     insertAfter,
1738                     clientRect.left, clientRect.top,
1739                     clientRect.right - clientRect.left,
1740                     clientRect.bottom- clientRect.top,
1741                     flags
1742     );
1743
1744     /* Finish off the fullscreen operation we were doing, if any */
1745     if (workMask & GLUT_FULL_SCREEN_WORK)
1746     {
1747         if (window->State.IsFullscreen)
1748         {
1749             /* leaving fullscreen, restore maximized state, if any */
1750             if (window->State.pWState.OldMaximized)
1751                 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1752
1753             window->State.IsFullscreen = GL_FALSE;
1754         }
1755         else
1756             window->State.IsFullscreen = GL_TRUE;
1757     }
1758 }
1759
1760
1761 void fgPlatformVisibilityWork(SFG_Window* window)
1762 {
1763     /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */
1764     int cmdShow = 0;
1765     SFG_Window *win = window;
1766     switch (window->State.DesiredVisibility)
1767     {
1768     case DesireHiddenState:
1769         cmdShow = SW_HIDE;
1770         break;
1771     case DesireIconicState:
1772         cmdShow = SW_MINIMIZE;
1773         /* Call on top-level window */
1774         while (win->Parent)
1775             win = win->Parent;
1776         break;
1777     case DesireNormalState:
1778         if (win->IsMenu && !fgStructure.GameModeWindow)
1779             cmdShow = SW_SHOWNA;    /* Just show, don't activate window if its a menu. Only exception is when there is a gamemode window as the menu would pop under it when we do this... */
1780         else
1781             cmdShow = SW_SHOW;
1782         break;
1783     }
1784
1785     ShowWindow( win->Window.Handle, cmdShow );
1786 }