implemented SUPER key/modifier support on windows
[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                 ( ( ( GetKeyState( VK_LWIN     ) < 0 ) ||
580             ( GetKeyState( VK_RWIN     ) < 0 )) ? GLUT_ACTIVE_SUPER : 0 );
581 }
582
583 /* Check whether a button (VK_*BUTTON) is currently depressed. Returns
584  * non-zero (not necessarily 1) if yes. */
585 static SHORT fgGetKeyState(int vKey)
586 {
587     /* MSDN says: "If the high-order bit is 1, the key is down; otherwise, it is up". */
588     return GetKeyState(vKey) & 0xFF00;
589 }
590
591 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
592 {
593     static unsigned char lControl = 0, lShift = 0, lAlt = 0,
594                          rControl = 0, rShift = 0, rAlt = 0;
595
596     int keypress = -1;
597
598     /* if keydown, check for repeat */
599     /* If repeat is globally switched off, it cannot be switched back on per window.
600      * But if it is globally switched on, it can be switched off per window. This matches
601      * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
602      * global state switch.
603      */
604     if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
605         return 1;
606
607     /* Remember the current modifiers state so user can query it from their callback */
608     fgState.Modifiers = fgPlatformGetModifiers( );
609
610     /* Convert the Win32 keystroke codes to GLUTtish way */
611 #   define FG_KEY(a,b) case a: keypress = b; break;
612
613     switch( wParam )
614     {
615         FG_KEY( VK_F1,     GLUT_KEY_F1        );
616         FG_KEY( VK_F2,     GLUT_KEY_F2        );
617         FG_KEY( VK_F3,     GLUT_KEY_F3        );
618         FG_KEY( VK_F4,     GLUT_KEY_F4        );
619         FG_KEY( VK_F5,     GLUT_KEY_F5        );
620         FG_KEY( VK_F6,     GLUT_KEY_F6        );
621         FG_KEY( VK_F7,     GLUT_KEY_F7        );
622         FG_KEY( VK_F8,     GLUT_KEY_F8        );
623         FG_KEY( VK_F9,     GLUT_KEY_F9        );
624         FG_KEY( VK_F10,    GLUT_KEY_F10       );
625         FG_KEY( VK_F11,    GLUT_KEY_F11       );
626         FG_KEY( VK_F12,    GLUT_KEY_F12       );
627         FG_KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
628         FG_KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
629         FG_KEY( VK_HOME,   GLUT_KEY_HOME      );
630         FG_KEY( VK_END,    GLUT_KEY_END       );
631         FG_KEY( VK_LEFT,   GLUT_KEY_LEFT      );
632         FG_KEY( VK_UP,     GLUT_KEY_UP        );
633         FG_KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
634         FG_KEY( VK_DOWN,   GLUT_KEY_DOWN      );
635         FG_KEY( VK_INSERT, GLUT_KEY_INSERT    );
636                 FG_KEY( VK_LWIN,   GLUT_KEY_SUPER_L   );
637                 FG_KEY( VK_RWIN,   GLUT_KEY_SUPER_R   );
638
639     /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
640      * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
641      * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
642      */
643 #define FG_KEY_EVENT(winKey,glutKey,keyStateVar)\
644     if (!keyStateVar && fgGetKeyState ( winKey ))\
645     {\
646         keypress   = glutKey;\
647         keyStateVar = 1;\
648     }\
649     else if (keyStateVar && !fgGetKeyState ( winKey ))\
650     {\
651         keypress   = glutKey;\
652         keyStateVar = 0;\
653     }
654     case VK_CONTROL:
655         FG_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
656         FG_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
657         break;
658     case VK_SHIFT:
659         FG_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
660         FG_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
661         break;
662     case VK_MENU:
663         FG_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
664         FG_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
665         break;
666 #undef KEY_EVENT
667
668     case VK_DELETE:
669         /* The delete key should be treated as an ASCII keypress: */
670         if (keydown)
671             INVOKE_WCB( *window, Keyboard,
672                         ( 127, window->State.MouseX, window->State.MouseY )
673             );
674         else
675             INVOKE_WCB( *window, KeyboardUp,
676                         ( 127, window->State.MouseX, window->State.MouseY )
677             );
678         break;
679
680 #if !defined(_WIN32_WCE)
681     default:
682         /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
683         if (!keydown)
684         {
685             BYTE state[ 256 ];
686             WORD code[ 2 ];
687
688             GetKeyboardState( state );
689
690             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
691                 wParam=code[ 0 ];
692
693             INVOKE_WCB( *window, KeyboardUp,
694                    ( (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 */
695                         window->State.MouseX, window->State.MouseY )
696             );
697         }
698 #endif
699     }
700
701 #if defined(_WIN32_WCE)
702     if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
703     {
704         if(wParam==(unsigned)gxKeyList.vkRight)
705             keypress = GLUT_KEY_RIGHT;
706         else if(wParam==(unsigned)gxKeyList.vkLeft)
707             keypress = GLUT_KEY_LEFT;
708         else if(wParam==(unsigned)gxKeyList.vkUp)
709             keypress = GLUT_KEY_UP;
710         else if(wParam==(unsigned)gxKeyList.vkDown)
711             keypress = GLUT_KEY_DOWN;
712         else if(wParam==(unsigned)gxKeyList.vkA)
713             keypress = GLUT_KEY_F1;
714         else if(wParam==(unsigned)gxKeyList.vkB)
715             keypress = GLUT_KEY_F2;
716         else if(wParam==(unsigned)gxKeyList.vkC)
717             keypress = GLUT_KEY_F3;
718         else if(wParam==(unsigned)gxKeyList.vkStart)
719             keypress = GLUT_KEY_F4;
720     }
721 #endif
722
723     if( keypress != -1 )
724         if (keydown)
725             INVOKE_WCB( *window, Special,
726                         ( keypress,
727                             window->State.MouseX, window->State.MouseY )
728             );
729         else
730             INVOKE_WCB( *window, SpecialUp,
731                         ( keypress,
732                             window->State.MouseX, window->State.MouseY )
733             );
734
735     fgState.Modifiers = INVALID_MODIFIERS;
736
737     /* SYSKEY events should be sent to default window proc for system to handle them */
738     if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
739         return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
740     else
741         return 1;
742 }
743
744 SFG_Window* fghWindowUnderCursor(SFG_Window *window)
745 {
746     /* Check if the current window that the mouse is over is a child window
747      * of the window the message was sent to. Some events only sent to main window,
748      * and when handling some messages, we need to make sure that we process
749      * callbacks on the child window instead. This mirrors how GLUT does things.
750      * returns either the original window or the found child.
751      */
752     if (window && window->Children.First)   /* This window has childs */
753     {
754         HWND hwnd;
755         SFG_Window* child_window;
756
757         /* Get mouse position at time of message */
758         DWORD mouse_pos_dw = GetMessagePos();
759         POINT mouse_pos;
760         mouse_pos.x = GET_X_LPARAM(mouse_pos_dw);
761         mouse_pos.y = GET_Y_LPARAM(mouse_pos_dw);
762         ScreenToClient( window->Window.Handle, &mouse_pos );
763
764         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
765         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 */
766         {
767             child_window = fgWindowByHandle(hwnd);
768             if (child_window)    /* Verify we got a FreeGLUT window */
769             {
770                 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
771                 window = fghWindowUnderCursor(child_window);
772             }
773         }
774     }
775
776     return window;
777 }
778
779 /*
780  * The window procedure for handling Win32 events
781  */
782 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
783 {
784     SFG_Window *window;
785     LRESULT lRet = 1;
786     static int setCaptureActive = 0;
787
788     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
789
790     window = fgWindowByHandle( hWnd );
791
792     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
793       return DefWindowProc( hWnd, uMsg, wParam, lParam );
794
795     /* printf ( "Window %3d message %s (<%04x>) %12d %12d\n", window?window->ID:0,
796              WMMsg2Str(uMsg), uMsg, wParam, lParam ); */
797
798     switch( uMsg )
799     {
800     case WM_CREATE:
801         /* The window structure is passed as the creation structure parameter... */
802         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
803         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
804                                        "fgPlatformWindowProc" );
805
806         window->Window.Handle = hWnd;
807         window->Window.pContext.Device = GetDC( hWnd );
808         if( window->IsMenu )
809         {
810             unsigned int current_DisplayMode = fgState.DisplayMode;
811             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
812 #if !defined(_WIN32_WCE)
813             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
814 #endif
815             fgState.DisplayMode = current_DisplayMode;
816
817             if( fgStructure.MenuContext )
818                 wglMakeCurrent( window->Window.pContext.Device,
819                                 fgStructure.MenuContext->MContext
820                 );
821             else
822             {
823                 fgStructure.MenuContext =
824                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
825                 fgStructure.MenuContext->MContext =
826                     wglCreateContext( window->Window.pContext.Device );
827             }
828
829             /* window->Window.Context = wglGetCurrentContext ();   */
830             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
831         }
832         else
833         {
834 #if !defined(_WIN32_WCE)
835             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
836 #endif
837
838             if( ! fgState.UseCurrentContext )
839                 window->Window.Context =
840                     wglCreateContext( window->Window.pContext.Device );
841             else
842             {
843                 window->Window.Context = wglGetCurrentContext( );
844                 if( ! window->Window.Context )
845                     window->Window.Context =
846                         wglCreateContext( window->Window.pContext.Device );
847             }
848
849 #if !defined(_WIN32_WCE)
850             fgNewWGLCreateContext( window );
851 #endif
852         }
853
854         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
855
856 #if defined(_WIN32_WCE)
857         /* Take over button handling */
858         {
859             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
860             if (dxDllLib)
861             {
862                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
863                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
864             }
865
866             if(GXOpenInput_)
867                 (*GXOpenInput_)();
868             if(GXGetDefaultKeys_)
869                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
870         }
871
872 #endif /* defined(_WIN32_WCE) */
873         break;
874
875     case WM_SIZE:
876         /* printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam)); */
877
878         /* Update visibility state of the window */
879         if (wParam==SIZE_MINIMIZED)
880             fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE);
881         else if ((wParam==SIZE_RESTORED || wParam == SIZE_MAXIMIZED) && !window->State.Visible)
882             fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE);
883
884         /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */
885         if( window->State.Visible )
886         {
887             int width, height;
888 #if defined(_WIN32_WCE)
889             width  = HIWORD(lParam);
890             height = LOWORD(lParam);
891 #else
892             width  = LOWORD(lParam);
893             height = HIWORD(lParam);
894 #endif /* defined(_WIN32_WCE) */
895
896             /* Update state and call callback, if there was a change */
897             fghOnReshapeNotify(window, width, height, GL_FALSE);
898         }
899
900         /* according to docs, should return 0 */
901         lRet = 0;
902         break;
903
904     case WM_SIZING:
905         {
906             /* User resize-dragging the window, call reshape callback and
907              * force redisplay so display keeps running during dragging.
908              * Screen still wont update when not moving the cursor though...
909              */
910             RECT rect;
911             /* PRECT prect = (PRECT) lParam;
912                printf("WM_SIZING: nc-area: %i,%i\n",prect->right-prect->left,prect->bottom-prect->top); */
913             /* Get client area, the rect in lParam is including non-client area. */
914             fghGetClientArea(&rect,window,FALSE);
915
916             /* We'll get a WM_SIZE as well, but as state has
917              * already been updated here, the fghOnReshapeNotify
918              * in the handler for that message doesn't do anything.
919              */
920             fghOnReshapeNotify(window, rect.right-rect.left, rect.bottom-rect.top, GL_FALSE);
921
922             /* Now directly call the drawing function to update
923              * window and window's childs.
924              * This mimics the WM_PAINT messages that are received during
925              * resizing. Note that we don't have a WM_MOVING handler
926              * as move-dragging doesn't generate WM_MOVE or WM_PAINT
927              * messages until the mouse is released.
928              */
929             fghRedrawWindowAndChildren(window);
930         }
931
932         /* according to docs, should return TRUE */
933         lRet = TRUE;
934         break;
935
936     case WM_MOVE:
937         {
938             /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */
939             if (!IsIconic(window->Window.Handle))
940             {
941                 RECT windowRect;
942
943                 /* lParam contains coordinates of top-left of client area.
944                  * Get top-left of non-client area of window, matching coordinates of
945                  * glutInitPosition and glutPositionWindow, but not those of
946                  * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return
947                  * top-left of client area.
948                  */
949                 GetWindowRect( window->Window.Handle, &windowRect );
950
951                 if (window->Parent)
952                 {
953                     /* For child window, we should return relative to upper-left
954                      * of parent's client area.
955                      */
956                     POINT topleft;
957                     topleft.x = windowRect.left;
958                     topleft.y = windowRect.top;
959
960                     ScreenToClient(window->Parent->Window.Handle,&topleft);
961                     windowRect.left = topleft.x;
962                     windowRect.top  = topleft.y;
963                 }
964
965                 /* Update state and call callback, if there was a change */
966                 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_FALSE);
967             }
968         }
969
970         /* according to docs, should return 0 */
971         lRet = 0;
972         break;
973
974     case WM_SETFOCUS:
975         /*printf("WM_SETFOCUS: %p\n", window );*/
976         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
977
978         SetActiveWindow( window->Window.Handle );
979         UpdateWindow ( hWnd );
980
981         break;
982
983     case WM_KILLFOCUS:
984         /*printf("WM_KILLFOCUS: %p\n", window ); */
985         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
986
987         /* Check if there are any open menus that need to be closed */
988         fgPlatformCheckMenuDeactivate((HWND)wParam);
989         break;
990
991     case WM_MOUSEACTIVATE:
992         /* Clicks should not activate the menu.
993          * Especially important when clicking on a menu's submenu item which has no effect.
994          */
995         /*printf("WM_MOUSEACTIVATE\n");*/
996         if (window->IsMenu)
997             lRet = MA_NOACTIVATEANDEAT;
998         else
999             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1000         break;
1001
1002     case WM_NCLBUTTONDOWN:
1003     case WM_NCMBUTTONDOWN:
1004     case WM_NCRBUTTONDOWN:
1005         {
1006             SFG_Menu *menu;
1007             if (fgState.ActiveMenus && (menu = fgGetActiveMenu()))
1008                 /* user clicked non-client area of window while a menu is open. Close menu */
1009                 fgDeactivateMenu(menu->ParentWindow);
1010
1011             /* and always pass to DefWindowProc */
1012             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1013         }
1014         break;
1015
1016 #if 0
1017     case WM_ACTIVATE:
1018         /* printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); */
1019         if (LOWORD(wParam) != WA_INACTIVE)
1020         {
1021 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
1022                    window->State.Cursor ); */
1023             fgSetCursor( window, window->State.Cursor );
1024         }
1025
1026         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1027         break;
1028 #endif
1029
1030     case WM_SETCURSOR:
1031 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
1032         if( LOWORD( lParam ) == HTCLIENT )
1033         {
1034             if (!window->State.pWState.MouseTracking)
1035             {
1036                 TRACKMOUSEEVENT tme;
1037
1038                 /* Cursor just entered window, set cursor look */
1039                 fgSetCursor ( window, window->State.Cursor ) ;
1040
1041                 /* If an EntryFunc callback is specified by the user, also
1042                  * invoke that callback and start mouse tracking so that
1043                  * we get a WM_MOUSELEAVE message
1044                  */
1045                 if (FETCH_WCB( *window, Entry ))
1046                 {
1047                     SFG_Window* saved_window = fgStructure.CurrentWindow;
1048                     INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
1049                     fgSetWindow(saved_window);
1050
1051                     tme.cbSize = sizeof(TRACKMOUSEEVENT);
1052                     tme.dwFlags = TME_LEAVE;
1053                     tme.hwndTrack = window->Window.Handle;
1054                     TrackMouseEvent(&tme);
1055
1056                     window->State.pWState.MouseTracking = TRUE;
1057                 }
1058             }
1059         }
1060         else
1061             /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
1062             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1063         break;
1064
1065     case WM_MOUSELEAVE:
1066         {
1067             /* NB: This message is only received when a EntryFunc callback
1068              * is specified by the user, as that is the only condition under
1069              * which mouse tracking is setup in WM_SETCURSOR handler above
1070              */
1071             SFG_Window* saved_window = fgStructure.CurrentWindow;
1072             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
1073             fgSetWindow(saved_window);
1074
1075             window->State.pWState.MouseTracking = FALSE;
1076             lRet = 0;   /* As per docs, must return zero */
1077         }
1078         break;
1079
1080     case WM_SHOWWINDOW:
1081         /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
1082         if (wParam)
1083         {
1084             fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE);
1085             window->State.WorkMask |= GLUT_DISPLAY_WORK;
1086         }
1087         else
1088         {
1089             fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE);
1090             window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
1091         }
1092         break;
1093
1094     case WM_PAINT:
1095     {
1096         RECT rect;
1097
1098         /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
1099         if (GetUpdateRect(hWnd,&rect,FALSE))
1100         {
1101             /* Dummy begin/end paint to validate rect that needs
1102              * redrawing, then signal that a redisplay is needed.
1103              * This allows us full control about when we do any
1104              * redrawing, and is the same as what original GLUT
1105              * does.
1106              */
1107             PAINTSTRUCT ps;
1108             BeginPaint( hWnd, &ps );
1109             EndPaint( hWnd, &ps );
1110
1111             window->State.WorkMask |= GLUT_DISPLAY_WORK;
1112         }
1113         lRet = 0;   /* As per docs, should return 0 */
1114     }
1115     break;
1116
1117     case WM_CLOSE:
1118         fgDestroyWindow ( window );
1119         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
1120             PostQuitMessage(0);
1121         break;
1122
1123     case WM_DESTROY:
1124         /*
1125          * The window already got destroyed, so don't bother with it.
1126          */
1127         return 0;
1128
1129     case WM_MOUSEMOVE:
1130     {
1131         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
1132 #if defined(_WIN32_WCE)
1133         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
1134         window->State.MouseY = LOWORD( lParam );
1135 #else
1136         window->State.MouseX = GET_X_LPARAM( lParam );
1137         window->State.MouseY = GET_Y_LPARAM( lParam );
1138 #endif /* defined(_WIN32_WCE) */
1139         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1140         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1141         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1142         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1143
1144         if ( window->ActiveMenu )
1145         {
1146             fgUpdateMenuHighlight( window->ActiveMenu );
1147             break;
1148         }
1149
1150         fgState.Modifiers = fgPlatformGetModifiers( );
1151
1152         if( ( wParam & MK_LBUTTON ) ||
1153             ( wParam & MK_MBUTTON ) ||
1154             ( wParam & MK_RBUTTON ) )
1155             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1156                                            window->State.MouseY ) );
1157         else
1158             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1159                                             window->State.MouseY ) );
1160
1161         fgState.Modifiers = INVALID_MODIFIERS;
1162     }
1163     break;
1164
1165     case WM_LBUTTONDOWN:
1166     case WM_MBUTTONDOWN:
1167     case WM_RBUTTONDOWN:
1168     case WM_LBUTTONUP:
1169     case WM_MBUTTONUP:
1170     case WM_RBUTTONUP:
1171     {
1172         GLboolean pressed = GL_TRUE;
1173         int button;
1174
1175         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
1176 #if defined(_WIN32_WCE)
1177         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
1178         window->State.MouseY = LOWORD( lParam );
1179 #else
1180         window->State.MouseX = GET_X_LPARAM( lParam );
1181         window->State.MouseY = GET_Y_LPARAM( lParam );
1182 #endif /* defined(_WIN32_WCE) */
1183
1184         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1185         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1186         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1187         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1188
1189         switch( uMsg )
1190         {
1191         case WM_LBUTTONDOWN:
1192             pressed = GL_TRUE;
1193             button = GLUT_LEFT_BUTTON;
1194             break;
1195         case WM_MBUTTONDOWN:
1196             pressed = GL_TRUE;
1197             button = GLUT_MIDDLE_BUTTON;
1198             break;
1199         case WM_RBUTTONDOWN:
1200             pressed = GL_TRUE;
1201             button = GLUT_RIGHT_BUTTON;
1202             break;
1203         case WM_LBUTTONUP:
1204             pressed = GL_FALSE;
1205             button = GLUT_LEFT_BUTTON;
1206             break;
1207         case WM_MBUTTONUP:
1208             pressed = GL_FALSE;
1209             button = GLUT_MIDDLE_BUTTON;
1210             break;
1211         case WM_RBUTTONUP:
1212             pressed = GL_FALSE;
1213             button = GLUT_RIGHT_BUTTON;
1214             break;
1215         default:
1216             pressed = GL_FALSE;
1217             button = -1;
1218             break;
1219         }
1220
1221 #if !defined(_WIN32_WCE)
1222         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1223         {
1224             if( button == GLUT_LEFT_BUTTON )
1225                 button = GLUT_RIGHT_BUTTON;
1226             else
1227                 if( button == GLUT_RIGHT_BUTTON )
1228                     button = GLUT_LEFT_BUTTON;
1229         }
1230 #endif /* !defined(_WIN32_WCE) */
1231
1232         if( button == -1 )
1233             return DefWindowProc( hWnd, uMsg, lParam, wParam );
1234
1235         /*
1236          * Do not execute the application's mouse callback if a menu
1237          * is hooked to this button.  In that case an appropriate
1238          * private call should be generated.
1239          */
1240         if( fgCheckActiveMenu( window, button, pressed,
1241                                window->State.MouseX, window->State.MouseY ) )
1242             break;
1243
1244         /* Set capture so that the window captures all the mouse messages
1245          *
1246          * The mouse is not released from the window until all buttons have
1247          * been released, even if the user presses a button in another window.
1248          * This is consistent with the behavior on X11.
1249          */
1250         if ( pressed == GL_TRUE )
1251         {
1252             if (!setCaptureActive)
1253                 SetCapture ( window->Window.Handle ) ;
1254             setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
1255         }
1256         else if (!fgGetKeyState(VK_LBUTTON) && !fgGetKeyState(VK_MBUTTON) && !fgGetKeyState(VK_RBUTTON))
1257           /* Make sure all mouse buttons are released before releasing capture */
1258           ReleaseCapture () ;
1259
1260         if( ! FETCH_WCB( *window, Mouse ) )
1261             break;
1262
1263         fgSetWindow( window );
1264         fgState.Modifiers = fgPlatformGetModifiers( );
1265
1266         INVOKE_WCB(
1267             *window, Mouse,
1268             ( button,
1269               pressed ? GLUT_DOWN : GLUT_UP,
1270               window->State.MouseX,
1271               window->State.MouseY
1272             )
1273         );
1274
1275         fgState.Modifiers = INVALID_MODIFIERS;
1276
1277         /* As per docs, should return zero */
1278         lRet = 0;
1279     }
1280     break;
1281
1282     case WM_MOUSEWHEEL:
1283     {
1284         int wheel_number = 0;   /* Only one scroll wheel on windows */
1285 #if defined(_WIN32_WCE)
1286         int modkeys = LOWORD(wParam);
1287         short ticks = (short)HIWORD(wParam);
1288         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
1289         xPos = LOWORD(lParam);  -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
1290         yPos = HIWORD(lParam);
1291         */
1292 #else
1293         /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
1294         short ticks = HIWORD( wParam );
1295         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
1296         window->State.MouseX = GET_X_LPARAM( lParam );
1297         window->State.MouseY = GET_Y_LPARAM( lParam );
1298         */
1299 #endif /* defined(_WIN32_WCE) */
1300
1301         window = fghWindowUnderCursor(window);
1302
1303         fgState.MouseWheelTicks += ticks;
1304         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
1305         {
1306             int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
1307
1308             if( ! FETCH_WCB( *window, MouseWheel ) &&
1309                 ! FETCH_WCB( *window, Mouse ) )
1310                 break;
1311
1312             fgSetWindow( window );
1313             fgState.Modifiers = fgPlatformGetModifiers( );
1314
1315             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
1316             {
1317                 if( FETCH_WCB( *window, MouseWheel ) )
1318                     INVOKE_WCB( *window, MouseWheel,
1319                                 ( wheel_number,
1320                                   direction,
1321                                   window->State.MouseX,
1322                                   window->State.MouseY
1323                                 )
1324                     );
1325                 else  /* No mouse wheel, call the mouse button callback twice */
1326                 {
1327                     /*
1328                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
1329                      *  "    "   one                     +1 to 5, -1 to 6, ...
1330                      *
1331                      * XXX The below assumes that you have no more than 3 mouse
1332                      * XXX buttons.  Sorry.
1333                      */
1334                     int button = wheel_number * 2 + 3;
1335                     if( direction < 0 )
1336                         ++button;
1337                     INVOKE_WCB( *window, Mouse,
1338                                 ( button, GLUT_DOWN,
1339                                   window->State.MouseX, window->State.MouseY )
1340                     );
1341                     INVOKE_WCB( *window, Mouse,
1342                                 ( button, GLUT_UP,
1343                                   window->State.MouseX, window->State.MouseY )
1344                     );
1345                 }
1346
1347                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
1348             }
1349
1350             fgState.Modifiers = INVALID_MODIFIERS;
1351         }
1352         /* Per docs, should return zero */
1353         lRet = 0;
1354     }
1355     break ;
1356
1357     case WM_SYSKEYDOWN:
1358     case WM_KEYDOWN:
1359     {
1360         window = fghWindowUnderCursor(window);
1361         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
1362     }
1363     break;
1364
1365     case WM_SYSKEYUP:
1366     case WM_KEYUP:
1367     {
1368         window = fghWindowUnderCursor(window);
1369         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
1370     }
1371     break;
1372
1373     case WM_SYSCHAR:
1374     case WM_CHAR:
1375     {
1376       window = fghWindowUnderCursor(window);
1377
1378       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
1379             break;
1380
1381         fgState.Modifiers = fgPlatformGetModifiers( );
1382         INVOKE_WCB( *window, Keyboard,
1383                     ( (char)wParam,
1384                       window->State.MouseX, window->State.MouseY )
1385         );
1386         fgState.Modifiers = INVALID_MODIFIERS;
1387     }
1388     break;
1389
1390     case WM_CAPTURECHANGED:
1391         if (!lParam || !fgWindowByHandle((HWND)lParam))
1392             /* Capture released or capture taken by non-FreeGLUT window */
1393             setCaptureActive = 0;
1394         /* Docs advise a redraw */
1395         InvalidateRect( hWnd, NULL, GL_FALSE );
1396         UpdateWindow(hWnd);
1397         lRet = 0;   /* Per docs, should return zero */
1398         break;
1399
1400 #if !defined(_WIN32_WCE)
1401     case WM_SYNCPAINT:  /* 0x0088 */
1402         /* Another window has moved, need to update this one */
1403         window->State.WorkMask |= GLUT_DISPLAY_WORK;
1404         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1405         /* Help screen says this message must be passed to "DefWindowProc" */
1406         break;
1407
1408     case WM_DISPLAYCHANGE: /* 0x007E */
1409         /* The system display resolution/depth has changed */
1410         fgDisplay.ScreenWidth = LOWORD(lParam);
1411         fgDisplay.ScreenHeight = HIWORD(lParam);
1412         break;
1413
1414     case WM_SYSCOMMAND :  /* 0x0112 */
1415         {
1416           /*
1417            * We have received a system command message.  Try to act on it.
1418            * The commands are passed in through the "wParam" parameter:
1419            * The least significant digit seems to be which edge of the window
1420            * is being used for a resize event:
1421            *     4  3  5
1422            *     1     2
1423            *     7  6  8
1424            * Congratulations and thanks to Richard Rauch for figuring this out..
1425            */
1426             switch ( wParam & 0xfff0 )
1427             {
1428             case SC_SIZE       :
1429                 break ;
1430
1431             case SC_MOVE       :
1432                 break ;
1433
1434             case SC_MINIMIZE   :
1435                 /* User has clicked on the "-" to minimize the window */
1436                 /* Turning off the visibility is handled in WM_SIZE handler */
1437
1438                 break ;
1439
1440             case SC_MAXIMIZE   :
1441                 break ;
1442
1443             case SC_NEXTWINDOW :
1444                 break ;
1445
1446             case SC_PREVWINDOW :
1447                 break ;
1448
1449             case SC_CLOSE      :
1450                 /* Followed very closely by a WM_CLOSE message */
1451                 break ;
1452
1453             case SC_VSCROLL    :
1454                 break ;
1455
1456             case SC_HSCROLL    :
1457                 break ;
1458
1459             case SC_MOUSEMENU  :
1460                 break ;
1461
1462             case SC_KEYMENU    :
1463                 break ;
1464
1465             case SC_ARRANGE    :
1466                 break ;
1467
1468             case SC_RESTORE    :
1469                 break ;
1470
1471             case SC_TASKLIST   :
1472                 break ;
1473
1474             case SC_SCREENSAVE :
1475                 break ;
1476
1477             case SC_HOTKEY     :
1478                 break ;
1479
1480 #if(WINVER >= 0x0400)
1481             case SC_DEFAULT    :
1482                 break ;
1483
1484             case SC_MONITORPOWER    :
1485                 break ;
1486
1487             case SC_CONTEXTHELP    :
1488                 break ;
1489 #endif /* WINVER >= 0x0400 */
1490
1491             default:
1492 #if _DEBUG
1493                 fgWarning( "Unknown wParam type 0x%x", wParam );
1494 #endif
1495                 break;
1496             }
1497         }
1498 #endif /* !defined(_WIN32_WCE) */
1499
1500         /* We need to pass the message on to the operating system as well */
1501         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1502         break;
1503
1504 #ifdef WM_TOUCH
1505     /* handle multi-touch messages */
1506     case WM_TOUCH:
1507     {
1508         unsigned int numInputs = (unsigned int)wParam;
1509         unsigned int i = 0;
1510         TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1511
1512         if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1513             fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1514             fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1515         }
1516
1517         if (!fghGetTouchInputInfo) {
1518             free( (void*)ti );
1519             break;
1520         }
1521
1522         if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1523             /* Handle each contact point */
1524             for (i = 0; i < numInputs; ++i ) {
1525
1526                 POINT tp;
1527                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1528                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1529                 ScreenToClient( hWnd, &tp );
1530
1531                 ti[i].dwID = ti[i].dwID * 2;
1532
1533                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1534                     INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1535                     INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1536                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1537                     INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1538                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   {
1539                     INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1540                     INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1541                 }
1542             }
1543         }
1544         fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1545         free( (void*)ti );
1546         lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1547         break;
1548     }
1549 #endif
1550
1551 #ifdef WM_INPUT
1552     case WM_INPUT:
1553         /* Added by Jinrong Xie <stonexjr at gmail.com> for SpaceNavigator support on Windows. Dec 2014 */
1554         if (fgHasSpaceball())
1555         {
1556             fgSpaceballHandleWinEvent(hWnd, wParam, lParam);
1557         }
1558         break;
1559 #endif
1560     default:
1561         /* Handle unhandled messages */
1562         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1563         break;
1564     }
1565
1566     return lRet;
1567 }
1568
1569
1570 /* deal with work list items */
1571 void fgPlatformInitWork(SFG_Window* window)
1572 {
1573     RECT windowRect;
1574
1575     /* Notify windowStatus/visibility */
1576     fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE);
1577
1578     /* get and notify window's position */
1579     GetWindowRect(window->Window.Handle,&windowRect);
1580     fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE);
1581
1582     /* get and notify window's size */
1583     GetClientRect(window->Window.Handle,&windowRect);
1584     fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE);
1585 }
1586
1587 /* On windows we can position, resize and change z order at the same time */
1588 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
1589 {
1590     UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1591     HWND insertAfter = HWND_TOP;
1592     RECT clientRect;
1593
1594 #if !defined(_WIN32_WCE) /* FIXME: what about WinCE */
1595     if (workMask & GLUT_FULL_SCREEN_WORK)
1596     {
1597         /* This asks us to toggle fullscreen mode */
1598         flags |= SWP_FRAMECHANGED;
1599
1600         if (window->State.IsFullscreen)
1601         {
1602             /* If we are fullscreen, resize the current window back to its original size */
1603             /* 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); */
1604
1605             /* restore style of window before making it fullscreen */
1606             SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle);
1607             SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx);
1608
1609             /* Then set up resize/reposition, unless user already queued up reshape/position work */
1610             if (!(workMask & GLUT_POSITION_WORK))
1611             {
1612                 workMask |= GLUT_POSITION_WORK;
1613                 window->State.DesiredXpos   = window->State.pWState.OldRect.left;
1614                 window->State.DesiredYpos   = window->State.pWState.OldRect.top;
1615             }
1616             if (!(workMask & GLUT_SIZE_WORK))
1617             {
1618                 workMask |= GLUT_SIZE_WORK;
1619                 window->State.DesiredWidth  = window->State.pWState.OldRect.right  - window->State.pWState.OldRect.left;
1620                 window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top;
1621             }
1622
1623             /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */
1624         }
1625         else
1626         {
1627             /* we are currently not fullscreen, go to fullscreen:
1628                 * remove window decoration and then maximize
1629                 */
1630             RECT rect;
1631             HMONITOR hMonitor;
1632             MONITORINFO mi;
1633             DWORD newStyle;
1634
1635             /* save current window rect, style, exstyle and maximized state */
1636             window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle);
1637             if (window->State.pWState.OldMaximized)
1638                 /* We force the window into restored mode before going
1639                     * fullscreen because Windows doesn't seem to hide the
1640                     * taskbar if the window is in the maximized state.
1641                     */
1642                 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
1643
1644             fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE );
1645             window->State.pWState.OldStyle   = GetWindowLong(window->Window.Handle, GWL_STYLE);
1646             window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
1647
1648             /* remove decorations from style */
1649             newStyle = window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME);
1650             if (fgState.DisplayMode & GLUT_STEREO)
1651             {
1652                 /* stereo mode does not engage on nVidia stereo buffers. This kills child
1653                    windows, but we make the guess that those are rare for stereo windows. */
1654                 newStyle |= WS_POPUP;
1655             }
1656             SetWindowLong(window->Window.Handle, GWL_STYLE, newStyle);
1657             SetWindowLong(window->Window.Handle, GWL_EXSTYLE,
1658                             window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME |
1659                             WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1660
1661             /* For fullscreen mode, find the monitor that is covered the most
1662                 * by the window and get its rect as the resize target.
1663                 */
1664             hMonitor= MonitorFromWindow(window->Window.Handle, MONITOR_DEFAULTTONEAREST);
1665             mi.cbSize = sizeof(mi);
1666             GetMonitorInfo(hMonitor, &mi);
1667             rect = mi.rcMonitor;
1668
1669             /* then setup window resize, overwriting other work queued on the window */
1670             window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK;
1671             window->State.WorkMask &= ~GLUT_ZORDER_WORK;
1672             window->State.DesiredXpos   = rect.left;
1673             window->State.DesiredYpos   = rect.top;
1674             window->State.DesiredWidth  = rect.right  - rect.left;
1675             window->State.DesiredHeight = rect.bottom - rect.top;
1676         }
1677     }
1678 #endif /*!defined(_WIN32_WCE) */
1679
1680     /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */
1681     {
1682         /* get rect describing window's current position and size,
1683             * in screen coordinates and in FreeGLUT format
1684             * (size (right-left, bottom-top) is client area size, top and left
1685             * are outside of window including decorations).
1686             */
1687         fghGetClientArea( &clientRect, window, TRUE );
1688
1689         if (workMask & GLUT_POSITION_WORK)
1690         {
1691             flags &= ~SWP_NOMOVE;
1692
1693             /* Move rect so that top-left is at requested position */
1694             /* This also automatically makes sure that child window requested coordinates are relative
1695                 * to top-left of parent's client area (needed input for SetWindowPos on child windows),
1696                 * so no need to further correct rect for child windows below (childs don't have decorations either).
1697                 */
1698             OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top);
1699         }
1700         if (workMask & GLUT_SIZE_WORK)
1701         {
1702             flags &= ~SWP_NOSIZE;
1703
1704             /* Note on maximizing behavior of Windows: the resize borders are off
1705                 * the screen such that the client area extends all the way from the
1706                 * leftmost corner to the rightmost corner to maximize screen real
1707                 * estate. A caption is still shown however to allow interaction with
1708                 * the window controls. This is default behavior of Windows that
1709                 * FreeGLUT sticks with. To alter, one would have to check if
1710                 * WS_MAXIMIZE style is set when a resize event is triggered, and
1711                 * then manually correct the windowRect to put the borders back on
1712                 * screen.
1713                 */
1714
1715             /* Set new size of window, WxH specify client area */
1716             clientRect.right    = clientRect.left + window->State.DesiredWidth;
1717             clientRect.bottom   = clientRect.top  + window->State.DesiredHeight;
1718         }
1719         if (workMask & GLUT_ZORDER_WORK)
1720         {
1721             flags &= ~SWP_NOZORDER;
1722
1723             /* Could change this to push it down or up one window at a time with some
1724                 * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT.
1725                 * What would be consistent with X11? Win32 GLUT does what we do here...
1726                 */
1727             if (window->State.DesiredZOrder < 0)
1728                 insertAfter = HWND_BOTTOM;
1729         }
1730     }
1731
1732     /* Adjust for window decorations
1733         * Child windows don't have decoration, so no need to correct
1734         */
1735     if (!window->Parent)
1736         /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
1737         fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE);
1738
1739     /* Do the requested positioning, moving, and z order push/pop. */
1740     SetWindowPos( window->Window.Handle,
1741                     insertAfter,
1742                     clientRect.left, clientRect.top,
1743                     clientRect.right - clientRect.left,
1744                     clientRect.bottom- clientRect.top,
1745                     flags
1746     );
1747
1748     /* Finish off the fullscreen operation we were doing, if any */
1749     if (workMask & GLUT_FULL_SCREEN_WORK)
1750     {
1751         if (window->State.IsFullscreen)
1752         {
1753             /* leaving fullscreen, restore maximized state, if any */
1754             if (window->State.pWState.OldMaximized)
1755                 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1756
1757             window->State.IsFullscreen = GL_FALSE;
1758         }
1759         else
1760             window->State.IsFullscreen = GL_TRUE;
1761     }
1762 }
1763
1764
1765 void fgPlatformVisibilityWork(SFG_Window* window)
1766 {
1767     /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */
1768     int cmdShow = 0;
1769     SFG_Window *win = window;
1770     switch (window->State.DesiredVisibility)
1771     {
1772     case DesireHiddenState:
1773         cmdShow = SW_HIDE;
1774         break;
1775     case DesireIconicState:
1776         cmdShow = SW_MINIMIZE;
1777         /* Call on top-level window */
1778         while (win->Parent)
1779             win = win->Parent;
1780         break;
1781     case DesireNormalState:
1782         if (win->IsMenu && !fgStructure.GameModeWindow)
1783             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... */
1784         else
1785             cmdShow = SW_SHOW;
1786         break;
1787     }
1788
1789     ShowWindow( win->Window.Handle, cmdShow );
1790 }