on windows too: call resize callback only in response to WM message
[freeglut] / src / mswin / fg_main_mswin.c
1 /*
2  * freeglut_main_mswin.c
3  *
4  * The Windows-specific mouse cursor related stuff.
5  *
6  * Copyright (c) 2012 Stephen J. Baker. All Rights Reserved.
7  * Written by John F. Fay, <fayjf@sourceforge.net>
8  * Creation date: Sat Jan 21, 2012
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #include <GL/freeglut.h>
29 #include "../fg_internal.h"
30
31
32 extern void fghRedrawWindow ( SFG_Window *window );
33
34 extern void fgNewWGLCreateContext( SFG_Window* window );
35 extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly,
36                                      unsigned char layer_type );
37
38 extern void fgPlatformCheckMenuDeactivate();
39
40 #ifdef WM_TOUCH
41 typedef BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT,UINT,PTOUCHINPUT,int);
42 typedef BOOL (WINAPI *pCloseTouchInputHandle)(HTOUCHINPUT);
43 static pGetTouchInputInfo fghGetTouchInputInfo = (pGetTouchInputInfo)0xDEADBEEF;
44 static pCloseTouchInputHandle fghCloseTouchInputHandle = (pCloseTouchInputHandle)0xDEADBEEF;
45 #endif
46
47 #ifdef _WIN32_WCE
48 typedef struct GXDisplayProperties GXDisplayProperties;
49 typedef struct GXKeyList GXKeyList;
50 #include <gx.h>
51
52 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
53 typedef int (*GXOPENINPUT)();
54
55 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
56 GXOPENINPUT GXOpenInput_ = NULL;
57
58 struct GXKeyList gxKeyList;
59 #endif /* _WIN32_WCE */
60
61
62 /* Get system time, taking special precautions against 32bit timer wrap.
63    We use timeGetTime and not GetTickCount because of its better stability,
64    and because we can increase its granularity (to 1 ms in
65    fgPlatformInitialize). For that reason we can't use GetTickCount64 which
66    wouldn't have the wrap issue.
67    Credit: this is based on code in glibc (https://mail.gnome.org/archives/commits-list/2011-November/msg04588.html)
68    */
69 static fg_time_t lastTime32 = 0;
70 static fg_time_t timeEpoch = 0;
71 void fgPlatformInitSystemTime()
72 {
73 #if defined(_WIN32_WCE)
74     lastTime32 = GetTickCount();
75 #else
76     lastTime32 = timeGetTime();
77 #endif
78 }
79 fg_time_t fgPlatformSystemTime ( void )
80 {
81     fg_time_t currTime32;
82 #if defined(_WIN32_WCE)
83     currTime32 = GetTickCount();
84 #else
85     currTime32 = timeGetTime();
86 #endif
87     /* Check if we just wrapped */
88     if (currTime32 < lastTime32)
89         timeEpoch++;
90     
91     lastTime32 = currTime32;
92
93     return currTime32 | timeEpoch << 32;
94 }
95
96
97 void fgPlatformSleepForEvents( fg_time_t msec )
98 {
99     MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
100 }
101
102
103 void fgPlatformProcessSingleEvent ( void )
104 {
105     MSG stMsg;
106
107     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
108
109     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
110     {
111         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
112         {
113             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
114             {
115                 fgDeinitialize( );
116                 exit( 0 );
117             }
118             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
119                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
120
121             return;
122         }
123
124         TranslateMessage( &stMsg );
125         DispatchMessage( &stMsg );
126     }
127 }
128
129
130
131 static void fghUpdateWindowStatus(SFG_Window *window, GLboolean visState)
132 {
133     SFG_Window* child;
134
135     if (window->State.Visible != visState)
136     {
137         window->State.Visible = visState;
138         /* On win32 we only have two states, window displayed and window not displayed (iconified) 
139          * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively.
140          */
141         INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
142
143         /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */
144         if (!window->Parent && window->State.pWState.IconTitle)
145         {
146             if (visState)
147                 /* visible, set window title */
148                 SetWindowText( window->Window.Handle, window->State.pWState.WindowTitle );
149             else
150                 /* not visible, set icon title */
151                 SetWindowText( window->Window.Handle, window->State.pWState.IconTitle );
152         }
153     }
154
155     /* Also set visibility state for children */
156     for( child = ( SFG_Window * )window->Children.First;
157          child;
158          child = ( SFG_Window * )child->Node.Next )
159     {
160         fghUpdateWindowStatus(child, visState);
161     }
162 }
163
164 void fghNotifyWindowStatus(SFG_Window *window)
165 {
166     INVOKE_WCB( *window, WindowStatus, ( window->State.Visible?GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
167
168     /* Don't notify children, they get their own just before first time they're drawn */
169 }
170
171 void fgPlatformMainLoopPreliminaryWork ( void )
172 {
173     /* no-op */
174 }
175
176
177 /*
178  * Determine a GLUT modifier mask based on MS-WINDOWS system info.
179  */
180 static int fgPlatformGetModifiers (void)
181 {
182     return
183         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
184             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
185         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
186             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
187         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
188             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
189 }
190
191 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
192 {
193     static unsigned char lControl = 0, lShift = 0, lAlt = 0,
194                          rControl = 0, rShift = 0, rAlt = 0;
195
196     int keypress = -1;
197     
198     /* if keydown, check for repeat */
199     /* If repeat is globally switched off, it cannot be switched back on per window.
200      * But if it is globally switched on, it can be switched off per window. This matches
201      * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
202      * global state switch.
203      */
204     if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
205         return 1;
206     
207     /* Remember the current modifiers state so user can query it from their callback */
208     fgState.Modifiers = fgPlatformGetModifiers( );
209
210     /* Convert the Win32 keystroke codes to GLUTtish way */
211 #   define KEY(a,b) case a: keypress = b; break;
212
213     switch( wParam )
214     {
215         KEY( VK_F1,     GLUT_KEY_F1        );
216         KEY( VK_F2,     GLUT_KEY_F2        );
217         KEY( VK_F3,     GLUT_KEY_F3        );
218         KEY( VK_F4,     GLUT_KEY_F4        );
219         KEY( VK_F5,     GLUT_KEY_F5        );
220         KEY( VK_F6,     GLUT_KEY_F6        );
221         KEY( VK_F7,     GLUT_KEY_F7        );
222         KEY( VK_F8,     GLUT_KEY_F8        );
223         KEY( VK_F9,     GLUT_KEY_F9        );
224         KEY( VK_F10,    GLUT_KEY_F10       );
225         KEY( VK_F11,    GLUT_KEY_F11       );
226         KEY( VK_F12,    GLUT_KEY_F12       );
227         KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
228         KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
229         KEY( VK_HOME,   GLUT_KEY_HOME      );
230         KEY( VK_END,    GLUT_KEY_END       );
231         KEY( VK_LEFT,   GLUT_KEY_LEFT      );
232         KEY( VK_UP,     GLUT_KEY_UP        );
233         KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
234         KEY( VK_DOWN,   GLUT_KEY_DOWN      );
235         KEY( VK_INSERT, GLUT_KEY_INSERT    );
236
237     /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
238      * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
239      * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
240      */
241 #define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\
242     if (!keyStateVar && GetAsyncKeyState ( winKey ))\
243     {\
244         keypress   = glutKey;\
245         keyStateVar = 1;\
246     }\
247     else if (keyStateVar && !GetAsyncKeyState ( winKey ))\
248     {\
249         keypress   = glutKey;\
250         keyStateVar = 0;\
251     }
252     case VK_CONTROL:
253         ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
254         ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
255         break;
256     case VK_SHIFT:
257         ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
258         ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
259         break;
260     case VK_MENU:
261         ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
262         ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
263         break;
264 #undef ASYNC_KEY_EVENT
265
266     case VK_DELETE:
267         /* The delete key should be treated as an ASCII keypress: */
268         if (keydown)
269             INVOKE_WCB( *window, Keyboard,
270                         ( 127, window->State.MouseX, window->State.MouseY )
271             );
272         else
273             INVOKE_WCB( *window, KeyboardUp,
274                         ( 127, window->State.MouseX, window->State.MouseY )
275             );
276         break;
277
278 #if !defined(_WIN32_WCE)
279     default:
280         /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
281         if (!keydown)
282         {
283             BYTE state[ 256 ];
284             WORD code[ 2 ];
285
286             GetKeyboardState( state );
287
288             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
289                 wParam=code[ 0 ];
290
291             INVOKE_WCB( *window, KeyboardUp,
292                    ( (char)wParam,
293                         window->State.MouseX, window->State.MouseY )
294             );
295         }
296 #endif
297     }
298
299 #if defined(_WIN32_WCE)
300     if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
301     {
302         if(wParam==(unsigned)gxKeyList.vkRight)
303             keypress = GLUT_KEY_RIGHT;
304         else if(wParam==(unsigned)gxKeyList.vkLeft)
305             keypress = GLUT_KEY_LEFT;
306         else if(wParam==(unsigned)gxKeyList.vkUp)
307             keypress = GLUT_KEY_UP;
308         else if(wParam==(unsigned)gxKeyList.vkDown)
309             keypress = GLUT_KEY_DOWN;
310         else if(wParam==(unsigned)gxKeyList.vkA)
311             keypress = GLUT_KEY_F1;
312         else if(wParam==(unsigned)gxKeyList.vkB)
313             keypress = GLUT_KEY_F2;
314         else if(wParam==(unsigned)gxKeyList.vkC)
315             keypress = GLUT_KEY_F3;
316         else if(wParam==(unsigned)gxKeyList.vkStart)
317             keypress = GLUT_KEY_F4;
318     }
319 #endif
320     
321     if( keypress != -1 )
322         if (keydown)
323             INVOKE_WCB( *window, Special,
324                         ( keypress,
325                             window->State.MouseX, window->State.MouseY )
326             );
327         else
328             INVOKE_WCB( *window, SpecialUp,
329                         ( keypress,
330                             window->State.MouseX, window->State.MouseY )
331             );
332
333     fgState.Modifiers = INVALID_MODIFIERS;
334
335     /* SYSKEY events should be sent to default window proc for system to handle them */
336     if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
337         return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
338     else
339         return 1;
340 }
341
342 SFG_Window* fghWindowUnderCursor(SFG_Window *window)
343 {
344     /* Check if the current window that the mouse is over is a child window
345      * of the window the message was sent to. Some events only sent to main window,
346      * and when handling some messages, we need to make sure that we process
347      * callbacks on the child window instead. This mirrors how GLUT does things.
348      * returns either the original window or the found child.
349      */
350     if (window && window->Children.First)   /* This window has childs */
351     {
352         HWND hwnd;
353         SFG_Window* child_window;
354
355         /* Get mouse position at time of message */
356         DWORD mouse_pos_dw = GetMessagePos();
357         POINT mouse_pos = {GET_X_LPARAM(mouse_pos_dw), GET_Y_LPARAM(mouse_pos_dw)};
358         ScreenToClient( window->Window.Handle, &mouse_pos );
359         
360         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
361         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 */
362         {
363             child_window = fgWindowByHandle(hwnd);
364             if (child_window)    /* Verify we got a FreeGLUT window */
365             {
366                 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
367                 window = fghWindowUnderCursor(child_window);
368             }
369         }
370     }
371
372     return window;
373 }
374
375 /*
376  * The window procedure for handling Win32 events
377  */
378 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
379 {
380     SFG_Window *window;
381     LRESULT lRet = 1;
382     static int setCaptureActive = 0;
383
384     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
385
386     window = fgWindowByHandle( hWnd );
387
388     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
389       return DefWindowProc( hWnd, uMsg, wParam, lParam );
390
391     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
392              uMsg, wParam, lParam ); */
393
394     switch( uMsg )
395     {
396     case WM_CREATE:
397         /* The window structure is passed as the creation structure parameter... */
398         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
399         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
400                                        "fgPlatformWindowProc" );
401
402         window->Window.Handle = hWnd;
403         window->Window.pContext.Device = GetDC( hWnd );
404         if( window->IsMenu )
405         {
406             unsigned int current_DisplayMode = fgState.DisplayMode;
407             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
408 #if !defined(_WIN32_WCE)
409             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
410 #endif
411             fgState.DisplayMode = current_DisplayMode;
412
413             if( fgStructure.MenuContext )
414                 wglMakeCurrent( window->Window.pContext.Device,
415                                 fgStructure.MenuContext->MContext
416                 );
417             else
418             {
419                 fgStructure.MenuContext =
420                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
421                 fgStructure.MenuContext->MContext =
422                     wglCreateContext( window->Window.pContext.Device );
423             }
424
425             /* window->Window.Context = wglGetCurrentContext ();   */
426             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
427         }
428         else
429         {
430 #if !defined(_WIN32_WCE)
431             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
432 #endif
433
434             if( ! fgState.UseCurrentContext )
435                 window->Window.Context =
436                     wglCreateContext( window->Window.pContext.Device );
437             else
438             {
439                 window->Window.Context = wglGetCurrentContext( );
440                 if( ! window->Window.Context )
441                     window->Window.Context =
442                         wglCreateContext( window->Window.pContext.Device );
443             }
444
445 #if !defined(_WIN32_WCE)
446             fgNewWGLCreateContext( window );
447 #endif
448         }
449
450         window->State.NeedToResize = GL_TRUE;
451         /* if we used CW_USEDEFAULT (thats a negative value) for the size
452          * of the window, query the window now for the size at which it
453          * was created.
454          */
455         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
456         {
457             SFG_Window *current_window = fgStructure.CurrentWindow;
458
459             fgSetWindow( window );
460             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
461             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
462             fgSetWindow( current_window );
463         }
464
465         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
466
467 #if defined(_WIN32_WCE)
468         /* Take over button handling */
469         {
470             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
471             if (dxDllLib)
472             {
473                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
474                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
475             }
476
477             if(GXOpenInput_)
478                 (*GXOpenInput_)();
479             if(GXGetDefaultKeys_)
480                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
481         }
482
483 #endif /* defined(_WIN32_WCE) */
484         break;
485
486     case WM_SIZE:
487         /* printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam)); */
488
489         /* Update visibility state of the window */
490         if (wParam==SIZE_MINIMIZED)
491             fghUpdateWindowStatus(window,GL_FALSE);
492         else if (wParam==SIZE_RESTORED && !window->State.Visible)
493             fghUpdateWindowStatus(window,GL_TRUE);
494
495         /* Check window visible, we don't want to resize when the user or glutIconifyWindow minimized the window */
496         if( window->State.Visible )
497         {
498             /* get old values first to compare to below */
499             int width = window->State.Width, height=window->State.Height;
500 #if defined(_WIN32_WCE)
501             window->State.Width  = HIWORD(lParam);
502             window->State.Height = LOWORD(lParam);
503 #else
504             window->State.Width  = LOWORD(lParam);
505             window->State.Height = HIWORD(lParam);
506 #endif /* defined(_WIN32_WCE) */
507             
508             if (width!=window->State.Width || height!=window->State.Height)
509             {
510                 SFG_Window* saved_window = fgStructure.CurrentWindow;
511                 
512                 /* size changed, call reshape callback */
513                 INVOKE_WCB( *window, Reshape, ( width, height ) );
514                 glutPostRedisplay( );
515                 if( window->IsMenu )
516                     fgSetWindow( saved_window );
517             }
518         }
519
520         /* according to docs, should return 0 */
521         lRet = 0;
522         break;
523
524     case WM_MOVE:
525         {
526             SFG_Window* saved_window = fgStructure.CurrentWindow;
527             RECT windowRect;
528
529             /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */
530             if (!IsIconic(window->Window.Handle))
531             {
532                 /* Get top-left of non-client area of window, matching coordinates of
533                  * glutInitPosition and glutPositionWindow, but not those of 
534                  * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return
535                  * top-left of client area.
536                  */
537                 GetWindowRect( window->Window.Handle, &windowRect );
538             
539                 if (window->Parent)
540                 {
541                     /* For child window, we should return relative to upper-left
542                      * of parent's client area.
543                      */
544                     POINT topleft = {windowRect.left,windowRect.top};
545
546                     ScreenToClient(window->Parent->Window.Handle,&topleft);
547                     windowRect.left = topleft.x;
548                     windowRect.top  = topleft.y;
549                 }
550
551                 INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) );
552                 fgSetWindow(saved_window);
553             }
554         }
555
556         /* according to docs, should return 0 */
557         lRet = 0;
558         break;
559
560     case WM_SETFOCUS:
561         /*printf("WM_SETFOCUS: %p\n", window );*/
562         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
563
564         SetActiveWindow( window->Window.Handle );
565         UpdateWindow ( hWnd );
566
567         break;
568
569     case WM_KILLFOCUS:
570         /*printf("WM_KILLFOCUS: %p\n", window ); */
571         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
572
573         /* Check if there are any open menus that need to be closed */
574         fgPlatformCheckMenuDeactivate();
575         break;
576
577 #if 0
578     case WM_ACTIVATE:
579         //printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam));
580         if (LOWORD(wParam) != WA_INACTIVE)
581         {
582 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
583                    window->State.Cursor ); */
584             fgSetCursor( window, window->State.Cursor );
585         }
586
587         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
588         break;
589 #endif
590
591     case WM_SETCURSOR:
592 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
593         if( LOWORD( lParam ) == HTCLIENT )
594         {
595             if (!window->State.pWState.MouseTracking)
596             {
597                 TRACKMOUSEEVENT tme;
598
599                 /* Cursor just entered window, set cursor look */ 
600                 fgSetCursor ( window, window->State.Cursor ) ;
601
602                 /* If an EntryFunc callback is specified by the user, also
603                  * invoke that callback and start mouse tracking so that
604                  * we get a WM_MOUSELEAVE message
605                  */
606                 if (FETCH_WCB( *window, Entry ))
607                 {
608                     SFG_Window* saved_window = fgStructure.CurrentWindow;
609                     INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
610                     fgSetWindow(saved_window);
611
612                     tme.cbSize = sizeof(TRACKMOUSEEVENT);
613                     tme.dwFlags = TME_LEAVE;
614                     tme.hwndTrack = window->Window.Handle;
615                     TrackMouseEvent(&tme);
616
617                     window->State.pWState.MouseTracking = GL_TRUE;
618                 }
619             }
620         }
621         else
622             /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
623             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
624         break;
625
626     case WM_MOUSELEAVE:
627         {
628             /* NB: This message is only received when a EntryFunc callback
629              * is specified by the user, as that is the only condition under
630              * which mouse tracking is setup in WM_SETCURSOR handler above
631              */
632             SFG_Window* saved_window = fgStructure.CurrentWindow;
633             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
634             fgSetWindow(saved_window);
635
636             window->State.pWState.MouseTracking = GL_FALSE;
637             lRet = 0;   /* As per docs, must return zero */
638         }
639         break;
640
641     case WM_SHOWWINDOW:
642         /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
643         if (wParam)
644         {
645             fghUpdateWindowStatus(window, GL_TRUE);
646             window->State.Redisplay = GL_TRUE;
647         }
648         else
649         {
650             fghUpdateWindowStatus(window, GL_FALSE);
651             window->State.Redisplay = GL_FALSE;
652         }
653         break;
654
655     case WM_PAINT:
656     {
657         RECT rect;
658
659         if (GetUpdateRect(hWnd,&rect,FALSE))
660         {
661             /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
662             PAINTSTRUCT ps;
663
664             /* Turn on the visibility in case it was turned off somehow */
665             window->State.Visible = GL_TRUE;
666
667             InvalidateRect( hWnd, NULL, GL_FALSE );
668             BeginPaint( hWnd, &ps );
669             fghRedrawWindow( window );
670             EndPaint( hWnd, &ps );
671         }
672         lRet = 0;   /* As per docs, should return 0 */
673     }
674     break;
675
676     case WM_CLOSE:
677         fgDestroyWindow ( window );
678         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
679             PostQuitMessage(0);
680         break;
681
682     case WM_DESTROY:
683         /*
684          * The window already got destroyed, so don't bother with it.
685          */
686         return 0;
687
688     case WM_MOUSEMOVE:
689     {
690         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
691 #if defined(_WIN32_WCE)
692         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
693         window->State.MouseY = LOWORD( lParam );
694 #else
695         window->State.MouseX = GET_X_LPARAM( lParam );
696         window->State.MouseY = GET_Y_LPARAM( lParam );
697 #endif /* defined(_WIN32_WCE) */
698         /* Restrict to [-32768, 32767] to match X11 behaviour       */
699         /* See comment in "freeglut_developer" mailing list 10/4/04 */
700         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
701         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
702
703         if ( window->ActiveMenu )
704         {
705             fgUpdateMenuHighlight( window->ActiveMenu );
706             break;
707         }
708
709         fgState.Modifiers = fgPlatformGetModifiers( );
710
711         if( ( wParam & MK_LBUTTON ) ||
712             ( wParam & MK_MBUTTON ) ||
713             ( wParam & MK_RBUTTON ) )
714             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
715                                            window->State.MouseY ) );
716         else
717             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
718                                             window->State.MouseY ) );
719
720         fgState.Modifiers = INVALID_MODIFIERS;
721     }
722     break;
723
724     case WM_LBUTTONDOWN:
725     case WM_MBUTTONDOWN:
726     case WM_RBUTTONDOWN:
727     case WM_LBUTTONUP:
728     case WM_MBUTTONUP:
729     case WM_RBUTTONUP:
730     {
731         GLboolean pressed = GL_TRUE;
732         int button;
733
734         /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
735 #if defined(_WIN32_WCE)
736         window->State.MouseX = 320-HIWORD( lParam );    /* XXX: Docs say x should be loword and y hiword? */
737         window->State.MouseY = LOWORD( lParam );
738 #else
739         window->State.MouseX = GET_X_LPARAM( lParam );
740         window->State.MouseY = GET_Y_LPARAM( lParam );
741 #endif /* defined(_WIN32_WCE) */
742
743         /* Restrict to [-32768, 32767] to match X11 behaviour       */
744         /* See comment in "freeglut_developer" mailing list 10/4/04 */
745         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
746         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
747
748         switch( uMsg )
749         {
750         case WM_LBUTTONDOWN:
751             pressed = GL_TRUE;
752             button = GLUT_LEFT_BUTTON;
753             break;
754         case WM_MBUTTONDOWN:
755             pressed = GL_TRUE;
756             button = GLUT_MIDDLE_BUTTON;
757             break;
758         case WM_RBUTTONDOWN:
759             pressed = GL_TRUE;
760             button = GLUT_RIGHT_BUTTON;
761             break;
762         case WM_LBUTTONUP:
763             pressed = GL_FALSE;
764             button = GLUT_LEFT_BUTTON;
765             break;
766         case WM_MBUTTONUP:
767             pressed = GL_FALSE;
768             button = GLUT_MIDDLE_BUTTON;
769             break;
770         case WM_RBUTTONUP:
771             pressed = GL_FALSE;
772             button = GLUT_RIGHT_BUTTON;
773             break;
774         default:
775             pressed = GL_FALSE;
776             button = -1;
777             break;
778         }
779
780 #if !defined(_WIN32_WCE)
781         if( GetSystemMetrics( SM_SWAPBUTTON ) )
782         {
783             if( button == GLUT_LEFT_BUTTON )
784                 button = GLUT_RIGHT_BUTTON;
785             else
786                 if( button == GLUT_RIGHT_BUTTON )
787                     button = GLUT_LEFT_BUTTON;
788         }
789 #endif /* !defined(_WIN32_WCE) */
790
791         if( button == -1 )
792             return DefWindowProc( hWnd, uMsg, lParam, wParam );
793
794         /*
795          * Do not execute the application's mouse callback if a menu
796          * is hooked to this button.  In that case an appropriate
797          * private call should be generated.
798          */
799         if( fgCheckActiveMenu( window, button, pressed,
800                                window->State.MouseX, window->State.MouseY ) )
801             break;
802
803         /* Set capture so that the window captures all the mouse messages
804          *
805          * The mouse is not released from the window until all buttons have
806          * been released, even if the user presses a button in another window.
807          * This is consistent with the behavior on X11.
808          */
809         if ( pressed == GL_TRUE )
810         {
811             if (!setCaptureActive)
812                 SetCapture ( window->Window.Handle ) ;
813             setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
814         }
815         else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
816           /* Make sure all mouse buttons are released before releasing capture */
817           ReleaseCapture () ;
818
819         if( ! FETCH_WCB( *window, Mouse ) )
820             break;
821
822         fgSetWindow( window );
823         fgState.Modifiers = fgPlatformGetModifiers( );
824
825         INVOKE_WCB(
826             *window, Mouse,
827             ( button,
828               pressed ? GLUT_DOWN : GLUT_UP,
829               window->State.MouseX,
830               window->State.MouseY
831             )
832         );
833
834         fgState.Modifiers = INVALID_MODIFIERS;
835
836         /* As per docs, should return zero */
837         lRet = 0;
838     }
839     break;
840
841     case WM_MOUSEWHEEL:
842     {
843         int wheel_number = 0;   /* Only one scroll wheel on windows */
844 #if defined(_WIN32_WCE)
845         int modkeys = LOWORD(wParam); 
846         short ticks = (short)HIWORD(wParam);
847         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
848         xPos = LOWORD(lParam);  -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
849         yPos = HIWORD(lParam);
850         */
851 #else
852         /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
853         short ticks = GET_WHEEL_DELTA_WPARAM( wParam );
854         /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
855         window->State.MouseX = GET_X_LPARAM( lParam );
856         window->State.MouseY = GET_Y_LPARAM( lParam );
857         */
858 #endif /* defined(_WIN32_WCE) */
859
860         window = fghWindowUnderCursor(window);
861
862                 fgState.MouseWheelTicks += ticks;
863         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
864                 {
865                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
866
867             if( ! FETCH_WCB( *window, MouseWheel ) &&
868                 ! FETCH_WCB( *window, Mouse ) )
869                 break;
870
871             fgSetWindow( window );
872             fgState.Modifiers = fgPlatformGetModifiers( );
873
874             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
875                         {
876                 if( FETCH_WCB( *window, MouseWheel ) )
877                     INVOKE_WCB( *window, MouseWheel,
878                                 ( wheel_number,
879                                   direction,
880                                   window->State.MouseX,
881                                   window->State.MouseY
882                                 )
883                     );
884                 else  /* No mouse wheel, call the mouse button callback twice */
885                                 {
886                     /*
887                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
888                      *  "    "   one                     +1 to 5, -1 to 6, ...
889                      *
890                      * XXX The below assumes that you have no more than 3 mouse
891                      * XXX buttons.  Sorry.
892                      */
893                     int button = wheel_number * 2 + 3;
894                     if( direction < 0 )
895                         ++button;
896                     INVOKE_WCB( *window, Mouse,
897                                 ( button, GLUT_DOWN,
898                                   window->State.MouseX, window->State.MouseY )
899                     );
900                     INVOKE_WCB( *window, Mouse,
901                                 ( button, GLUT_UP,
902                                   window->State.MouseX, window->State.MouseY )
903                     );
904                                 }
905
906                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
907                         }
908
909             fgState.Modifiers = INVALID_MODIFIERS;
910                 }
911         /* Per docs, should return zero */
912         lRet = 0;
913     }
914     break ;
915
916     case WM_SYSKEYDOWN:
917     case WM_KEYDOWN:
918     {
919         window = fghWindowUnderCursor(window);
920         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
921     }
922     break;
923
924     case WM_SYSKEYUP:
925     case WM_KEYUP:
926     {
927         window = fghWindowUnderCursor(window);
928         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
929     }
930     break;
931
932     case WM_SYSCHAR:
933     case WM_CHAR:
934     {
935       window = fghWindowUnderCursor(window);
936
937       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
938             break;
939
940         fgState.Modifiers = fgPlatformGetModifiers( );
941         INVOKE_WCB( *window, Keyboard,
942                     ( (char)wParam,
943                       window->State.MouseX, window->State.MouseY )
944         );
945         fgState.Modifiers = INVALID_MODIFIERS;
946     }
947     break;
948
949     case WM_CAPTURECHANGED:
950         if (!lParam || !fgWindowByHandle((HWND)lParam))
951             /* Capture released or capture taken by non-FreeGLUT window */
952             setCaptureActive = 0;
953         /* Docs advise a redraw */
954         InvalidateRect( hWnd, NULL, GL_FALSE );
955         UpdateWindow(hWnd);
956         lRet = 0;   /* Per docs, should return zero */
957         break;
958
959 #if !defined(_WIN32_WCE)
960     case WM_SYNCPAINT:  /* 0x0088 */
961         /* Another window has moved, need to update this one */
962         window->State.Redisplay = GL_TRUE;
963         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
964         /* Help screen says this message must be passed to "DefWindowProc" */
965         break;
966
967     case WM_SYSCOMMAND :  /* 0x0112 */
968         {
969           /*
970            * We have received a system command message.  Try to act on it.
971            * The commands are passed in through the "wParam" parameter:
972            * The least significant digit seems to be which edge of the window
973            * is being used for a resize event:
974            *     4  3  5
975            *     1     2
976            *     7  6  8
977            * Congratulations and thanks to Richard Rauch for figuring this out..
978            */
979             switch ( wParam & 0xfff0 )
980             {
981             case SC_SIZE       :
982                 break ;
983
984             case SC_MOVE       :
985                 break ;
986
987             case SC_MINIMIZE   :
988                 /* User has clicked on the "-" to minimize the window */
989                 /* Turning off the visibility is handled in WM_SIZE handler */
990
991                 break ;
992
993             case SC_MAXIMIZE   :
994                 break ;
995
996             case SC_NEXTWINDOW :
997                 break ;
998
999             case SC_PREVWINDOW :
1000                 break ;
1001
1002             case SC_CLOSE      :
1003                 /* Followed very closely by a WM_CLOSE message */
1004                 break ;
1005
1006             case SC_VSCROLL    :
1007                 break ;
1008
1009             case SC_HSCROLL    :
1010                 break ;
1011
1012             case SC_MOUSEMENU  :
1013                 break ;
1014
1015             case SC_KEYMENU    :
1016                 break ;
1017
1018             case SC_ARRANGE    :
1019                 break ;
1020
1021             case SC_RESTORE    :
1022                 break ;
1023
1024             case SC_TASKLIST   :
1025                 break ;
1026
1027             case SC_SCREENSAVE :
1028                 break ;
1029
1030             case SC_HOTKEY     :
1031                 break ;
1032
1033 #if(WINVER >= 0x0400)
1034             case SC_DEFAULT    :
1035                 break ;
1036
1037             case SC_MONITORPOWER    :
1038                 break ;
1039
1040             case SC_CONTEXTHELP    :
1041                 break ;
1042 #endif /* WINVER >= 0x0400 */
1043
1044             default:
1045 #if _DEBUG
1046                 fgWarning( "Unknown wParam type 0x%x", wParam );
1047 #endif
1048                 break;
1049             }
1050         }
1051 #endif /* !defined(_WIN32_WCE) */
1052
1053         /* We need to pass the message on to the operating system as well */
1054         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1055         break;
1056
1057 #ifdef WM_TOUCH
1058         /* handle multi-touch messages */
1059         case WM_TOUCH:
1060         {
1061                 unsigned int numInputs = (unsigned int)wParam;
1062                 unsigned int i = 0;
1063                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1064
1065                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1066                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1067                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1068                 }
1069
1070                 if (!fghGetTouchInputInfo) { 
1071                         free( (void*)ti );
1072                         break;
1073                 }
1074
1075                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1076                         /* Handle each contact point */
1077                         for (i = 0; i < numInputs; ++i ) {
1078
1079                                 POINT tp;
1080                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1081                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1082                                 ScreenToClient( hWnd, &tp );
1083
1084                                 ti[i].dwID = ti[i].dwID * 2;
1085
1086                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1087                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1088                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1089                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1090                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1091                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1092                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1093                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1094                                 }
1095                         }
1096                 }
1097                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1098                 free( (void*)ti );
1099                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1100                 break;
1101         }
1102 #endif
1103     default:
1104         /* Handle unhandled messages */
1105         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1106         break;
1107     }
1108
1109     return lRet;
1110 }