updated docs for fgPlatformMainLoopPreliminaryWork
[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         INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
139     }
140
141     /* Also set visibility state for children */
142     for( child = ( SFG_Window * )window->Children.First;
143          child;
144          child = ( SFG_Window * )child->Node.Next )
145     {
146         fghUpdateWindowStatus(child, visState);
147     }
148 }
149
150 static void fghNotifyWindowStatus(SFG_Window *window)
151 {
152     SFG_Window* child;
153
154     INVOKE_WCB( *window, WindowStatus, ( window->State.Visible?GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
155
156     /* Also notify children */
157     for( child = ( SFG_Window * )window->Children.First;
158          child;
159          child = ( SFG_Window * )child->Node.Next )
160     {
161         fghNotifyWindowStatus(child);
162     }
163 }
164
165 void fgPlatformMainLoopPreliminaryWork ( void )
166 {
167     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
168
169     /*
170      * Processing before the main loop:  If there is a window which is open and
171      * which has a visibility/windowStatus callback, call it to inform the client
172      * code that the window is visible.  I know this is an ugly hack,
173      * but I'm not sure what else to do about it.  Depending on WM_ACTIVATE would
174      * not work as not all windows get this when you are opening multiple before
175      * the mainloop starts. WM_SHOWWINDOW looked like an interesting candidate, but
176      * it is generated and processed before glutCreate(Sub)Window returns, so no
177      * callback can yet be set on the window.
178      */
179     while( window )
180     {
181         if ( FETCH_WCB( *window, WindowStatus ) )
182         {
183             SFG_Window *current_window = fgStructure.CurrentWindow ;
184
185             fghNotifyWindowStatus(window);
186             fgSetWindow( current_window );
187         }
188
189         window = (SFG_Window *)window->Node.Next ;
190     }
191 }
192
193
194 /*
195  * Determine a GLUT modifier mask based on MS-WINDOWS system info.
196  */
197 static int fgPlatformGetModifiers (void)
198 {
199     return
200         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
201             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
202         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
203             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
204         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
205             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
206 }
207
208 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
209 {
210     static unsigned char lControl = 0, lShift = 0, lAlt = 0,
211                          rControl = 0, rShift = 0, rAlt = 0;
212
213     int keypress = -1;
214     
215     /* if keydown, check for repeat */
216     /* If repeat is globally switched off, it cannot be switched back on per window.
217      * But if it is globally switched on, it can be switched off per window. This matches
218      * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
219      * global state switch.
220      */
221     if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
222         return 1;
223     
224     /* Remember the current modifiers state so user can query it from their callback */
225     fgState.Modifiers = fgPlatformGetModifiers( );
226
227     /* Convert the Win32 keystroke codes to GLUTtish way */
228 #   define KEY(a,b) case a: keypress = b; break;
229
230     switch( wParam )
231     {
232         KEY( VK_F1,     GLUT_KEY_F1        );
233         KEY( VK_F2,     GLUT_KEY_F2        );
234         KEY( VK_F3,     GLUT_KEY_F3        );
235         KEY( VK_F4,     GLUT_KEY_F4        );
236         KEY( VK_F5,     GLUT_KEY_F5        );
237         KEY( VK_F6,     GLUT_KEY_F6        );
238         KEY( VK_F7,     GLUT_KEY_F7        );
239         KEY( VK_F8,     GLUT_KEY_F8        );
240         KEY( VK_F9,     GLUT_KEY_F9        );
241         KEY( VK_F10,    GLUT_KEY_F10       );
242         KEY( VK_F11,    GLUT_KEY_F11       );
243         KEY( VK_F12,    GLUT_KEY_F12       );
244         KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
245         KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
246         KEY( VK_HOME,   GLUT_KEY_HOME      );
247         KEY( VK_END,    GLUT_KEY_END       );
248         KEY( VK_LEFT,   GLUT_KEY_LEFT      );
249         KEY( VK_UP,     GLUT_KEY_UP        );
250         KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
251         KEY( VK_DOWN,   GLUT_KEY_DOWN      );
252         KEY( VK_INSERT, GLUT_KEY_INSERT    );
253
254     /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
255      * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
256      * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
257      */
258 #define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\
259     if (!keyStateVar && GetAsyncKeyState ( winKey ))\
260     {\
261         keypress   = glutKey;\
262         keyStateVar = 1;\
263     }\
264     else if (keyStateVar && !GetAsyncKeyState ( winKey ))\
265     {\
266         keypress   = glutKey;\
267         keyStateVar = 0;\
268     }
269     case VK_CONTROL:
270         ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
271         ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
272         break;
273     case VK_SHIFT:
274         ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
275         ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
276         break;
277     case VK_MENU:
278         ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
279         ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
280         break;
281 #undef ASYNC_KEY_EVENT
282
283     case VK_DELETE:
284         /* The delete key should be treated as an ASCII keypress: */
285         if (keydown)
286             INVOKE_WCB( *window, Keyboard,
287                         ( 127, window->State.MouseX, window->State.MouseY )
288             );
289         else
290             INVOKE_WCB( *window, KeyboardUp,
291                         ( 127, window->State.MouseX, window->State.MouseY )
292             );
293         break;
294
295 #if !defined(_WIN32_WCE)
296     default:
297         /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
298         if (!keydown)
299         {
300             BYTE state[ 256 ];
301             WORD code[ 2 ];
302
303             GetKeyboardState( state );
304
305             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
306                 wParam=code[ 0 ];
307
308             INVOKE_WCB( *window, KeyboardUp,
309                    ( (char)wParam,
310                         window->State.MouseX, window->State.MouseY )
311             );
312         }
313 #endif
314     }
315
316 #if defined(_WIN32_WCE)
317     if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
318     {
319         if(wParam==(unsigned)gxKeyList.vkRight)
320             keypress = GLUT_KEY_RIGHT;
321         else if(wParam==(unsigned)gxKeyList.vkLeft)
322             keypress = GLUT_KEY_LEFT;
323         else if(wParam==(unsigned)gxKeyList.vkUp)
324             keypress = GLUT_KEY_UP;
325         else if(wParam==(unsigned)gxKeyList.vkDown)
326             keypress = GLUT_KEY_DOWN;
327         else if(wParam==(unsigned)gxKeyList.vkA)
328             keypress = GLUT_KEY_F1;
329         else if(wParam==(unsigned)gxKeyList.vkB)
330             keypress = GLUT_KEY_F2;
331         else if(wParam==(unsigned)gxKeyList.vkC)
332             keypress = GLUT_KEY_F3;
333         else if(wParam==(unsigned)gxKeyList.vkStart)
334             keypress = GLUT_KEY_F4;
335     }
336 #endif
337     
338     if( keypress != -1 )
339         if (keydown)
340             INVOKE_WCB( *window, Special,
341                         ( keypress,
342                             window->State.MouseX, window->State.MouseY )
343             );
344         else
345             INVOKE_WCB( *window, SpecialUp,
346                         ( keypress,
347                             window->State.MouseX, window->State.MouseY )
348             );
349
350     fgState.Modifiers = INVALID_MODIFIERS;
351
352     /* SYSKEY events should be sent to default window proc for system to handle them */
353     if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
354         return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
355     else
356         return 1;
357 }
358
359 static SFG_Window* fghWindowUnderCursor(SFG_Window *window)
360 {
361     /* Check if the current window that the mouse is over is a child window
362      * of the window the message was sent to. Some events only sent to main window,
363      * and when handling some messages, we need to make sure that we process
364      * callbacks on the child window instead. This mirrors how GLUT does things.
365      * returns either the original window or the found child.
366      */
367     if (window && window->Children.First)   /* This window has childs */
368     {
369         SFG_WindowHandleType hwnd;
370         SFG_Window* child_window;
371
372         /* Get mouse position at time of message */
373         DWORD mouse_pos_dw = GetMessagePos();
374         POINT mouse_pos = {GET_X_LPARAM(mouse_pos_dw), GET_Y_LPARAM(mouse_pos_dw)};
375         ScreenToClient( window->Window.Handle, &mouse_pos );
376         
377         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
378         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 */
379         {
380             child_window = fgWindowByHandle(hwnd);
381             if (child_window)    /* Verify we got a FreeGLUT window */
382             {
383                 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
384                 window = fghWindowUnderCursor(child_window);
385             }
386         }
387     }
388
389     return window;
390 }
391
392 /*
393  * The window procedure for handling Win32 events
394  */
395 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
396 {
397     SFG_Window *window;
398     LRESULT lRet = 1;
399     static int setCaptureActive = 0;
400
401     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
402
403     window = fgWindowByHandle( hWnd );
404
405     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
406       return DefWindowProc( hWnd, uMsg, wParam, lParam );
407
408     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
409              uMsg, wParam, lParam ); */
410
411     switch( uMsg )
412     {
413     case WM_CREATE:
414         /* The window structure is passed as the creation structure parameter... */
415         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
416         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
417                                        "fgPlatformWindowProc" );
418
419         window->Window.Handle = hWnd;
420         window->Window.pContext.Device = GetDC( hWnd );
421         if( window->IsMenu )
422         {
423             unsigned int current_DisplayMode = fgState.DisplayMode;
424             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
425 #if !defined(_WIN32_WCE)
426             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
427 #endif
428             fgState.DisplayMode = current_DisplayMode;
429
430             if( fgStructure.MenuContext )
431                 wglMakeCurrent( window->Window.pContext.Device,
432                                 fgStructure.MenuContext->MContext
433                 );
434             else
435             {
436                 fgStructure.MenuContext =
437                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
438                 fgStructure.MenuContext->MContext =
439                     wglCreateContext( window->Window.pContext.Device );
440             }
441
442             /* window->Window.Context = wglGetCurrentContext ();   */
443             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
444         }
445         else
446         {
447 #if !defined(_WIN32_WCE)
448             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
449 #endif
450
451             if( ! fgState.UseCurrentContext )
452                 window->Window.Context =
453                     wglCreateContext( window->Window.pContext.Device );
454             else
455             {
456                 window->Window.Context = wglGetCurrentContext( );
457                 if( ! window->Window.Context )
458                     window->Window.Context =
459                         wglCreateContext( window->Window.pContext.Device );
460             }
461
462 #if !defined(_WIN32_WCE)
463             fgNewWGLCreateContext( window );
464 #endif
465         }
466
467         window->State.NeedToResize = GL_TRUE;
468         /* if we used CW_USEDEFAULT (thats a negative value) for the size
469          * of the window, query the window now for the size at which it
470          * was created.
471          */
472         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
473         {
474             SFG_Window *current_window = fgStructure.CurrentWindow;
475
476             fgSetWindow( window );
477             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
478             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
479             fgSetWindow( current_window );
480         }
481
482         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
483
484 #if defined(_WIN32_WCE)
485         /* Take over button handling */
486         {
487             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
488             if (dxDllLib)
489             {
490                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
491                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
492             }
493
494             if(GXOpenInput_)
495                 (*GXOpenInput_)();
496             if(GXGetDefaultKeys_)
497                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
498         }
499
500 #endif /* defined(_WIN32_WCE) */
501         break;
502
503     case WM_SIZE:
504         //printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam));
505         /*
506          * If the window is visible, then it is the user manually resizing it.
507          * If it is not, then it is the system sending us a dummy resize with
508          * zero dimensions on a "glutIconifyWindow" call.
509          */
510         if( window->State.Visible )
511         {
512             /* get old values first to compare to below */
513             int width = window->State.Width, height=window->State.Height;
514 #if defined(_WIN32_WCE)
515             window->State.Width  = HIWORD(lParam);
516             window->State.Height = LOWORD(lParam);
517 #else
518             window->State.Width  = LOWORD(lParam);
519             window->State.Height = HIWORD(lParam);
520 #endif /* defined(_WIN32_WCE) */
521             
522             if (width!=window->State.Width || height!=window->State.Height)
523                 /* Something changed, need to resize */
524                 window->State.NeedToResize = GL_TRUE;
525         }
526
527         /* according to docs, should return 0 */
528         lRet = 0;
529         break;
530
531     case WM_MOVE:
532         {
533             SFG_Window* saved_window = fgStructure.CurrentWindow;
534             RECT windowRect;
535
536             /* Check window visible, we don't want to call the position callback when the user minimized the window */
537             if (window->State.Visible)
538             {
539                 GetWindowRect( window->Window.Handle, &windowRect );
540             
541                 if (window->Parent)
542                 {
543                     /* For child window, we should return relative to upper-left
544                      * of parent's client area.
545                      */
546                     POINT topleft = {windowRect.left,windowRect.top};
547
548                     ScreenToClient(window->Parent->Window.Handle,&topleft);
549                     windowRect.left = topleft.x;
550                     windowRect.top  = topleft.y;
551                 }
552
553                 INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) );
554                 fgSetWindow(saved_window);
555             }
556         }
557         break;
558
559     case WM_SETFOCUS:
560 /*        printf("WM_SETFOCUS: %p\n", window ); */
561
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         {
571 /*            printf("WM_KILLFOCUS: %p\n", window ); */
572             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
573
574             /* Check if there are any open menus that need to be closed */
575             fgPlatformCheckMenuDeactivate();
576         }
577         break;
578
579 #if 0
580     case WM_ACTIVATE:
581         //printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam));
582         if (LOWORD(wParam) != WA_INACTIVE)
583         {
584 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
585                    window->State.Cursor ); */
586             fgSetCursor( window, window->State.Cursor );
587         }
588
589         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
590         break;
591 #endif
592
593     case WM_SETCURSOR:
594 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
595         if( LOWORD( lParam ) == HTCLIENT )
596         {
597             if (!window->State.pWState.MouseTracking)
598             {
599                 TRACKMOUSEEVENT tme;
600
601                 /* Cursor just entered window, set cursor look */ 
602                 fgSetCursor ( window, window->State.Cursor ) ;
603
604                 /* If an EntryFunc callback is specified by the user, also
605                  * invoke that callback and start mouse tracking so that
606                  * we get a WM_MOUSELEAVE message
607                  */
608                 if (FETCH_WCB( *window, Entry ))
609                 {
610                     INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
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\n");
643         fghUpdateWindowStatus(window, GL_TRUE);
644         window->State.Redisplay = GL_TRUE;
645         break;
646
647     case WM_PAINT:
648     {
649         PAINTSTRUCT ps;
650         /* Turn on the visibility in case it was turned off somehow */
651         window->State.Visible = GL_TRUE;
652
653         InvalidateRect( hWnd, NULL, GL_FALSE ); /* Make sure whole window is repainted. Bit of a hack, but a safe one from what google turns up... */
654         BeginPaint( hWnd, &ps );
655         fghRedrawWindow( window );
656         EndPaint( hWnd, &ps );
657     }
658     break;
659
660     case WM_CLOSE:
661         fgDestroyWindow ( window );
662         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
663             PostQuitMessage(0);
664         break;
665
666     case WM_DESTROY:
667         /*
668          * The window already got destroyed, so don't bother with it.
669          */
670         return 0;
671
672     case WM_MOUSEMOVE:
673     {
674 #if defined(_WIN32_WCE)
675         window->State.MouseX = 320-HIWORD( lParam );
676         window->State.MouseY = LOWORD( lParam );
677 #else
678         window->State.MouseX = LOWORD( lParam );
679         window->State.MouseY = HIWORD( lParam );
680 #endif /* defined(_WIN32_WCE) */
681         /* Restrict to [-32768, 32767] to match X11 behaviour       */
682         /* See comment in "freeglut_developer" mailing list 10/4/04 */
683         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
684         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
685
686         if ( window->ActiveMenu )
687         {
688             fgUpdateMenuHighlight( window->ActiveMenu );
689             break;
690         }
691
692         fgState.Modifiers = fgPlatformGetModifiers( );
693
694         if( ( wParam & MK_LBUTTON ) ||
695             ( wParam & MK_MBUTTON ) ||
696             ( wParam & MK_RBUTTON ) )
697             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
698                                            window->State.MouseY ) );
699         else
700             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
701                                             window->State.MouseY ) );
702
703         fgState.Modifiers = INVALID_MODIFIERS;
704     }
705     break;
706
707     case WM_LBUTTONDOWN:
708     case WM_MBUTTONDOWN:
709     case WM_RBUTTONDOWN:
710     case WM_LBUTTONUP:
711     case WM_MBUTTONUP:
712     case WM_RBUTTONUP:
713     {
714         GLboolean pressed = GL_TRUE;
715         int button;
716
717 #if defined(_WIN32_WCE)
718         window->State.MouseX = 320-HIWORD( lParam );
719         window->State.MouseY = LOWORD( lParam );
720 #else
721         window->State.MouseX = LOWORD( lParam );
722         window->State.MouseY = HIWORD( lParam );
723 #endif /* defined(_WIN32_WCE) */
724
725         /* Restrict to [-32768, 32767] to match X11 behaviour       */
726         /* See comment in "freeglut_developer" mailing list 10/4/04 */
727         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
728         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
729
730         switch( uMsg )
731         {
732         case WM_LBUTTONDOWN:
733             pressed = GL_TRUE;
734             button = GLUT_LEFT_BUTTON;
735             break;
736         case WM_MBUTTONDOWN:
737             pressed = GL_TRUE;
738             button = GLUT_MIDDLE_BUTTON;
739             break;
740         case WM_RBUTTONDOWN:
741             pressed = GL_TRUE;
742             button = GLUT_RIGHT_BUTTON;
743             break;
744         case WM_LBUTTONUP:
745             pressed = GL_FALSE;
746             button = GLUT_LEFT_BUTTON;
747             break;
748         case WM_MBUTTONUP:
749             pressed = GL_FALSE;
750             button = GLUT_MIDDLE_BUTTON;
751             break;
752         case WM_RBUTTONUP:
753             pressed = GL_FALSE;
754             button = GLUT_RIGHT_BUTTON;
755             break;
756         default:
757             pressed = GL_FALSE;
758             button = -1;
759             break;
760         }
761
762 #if !defined(_WIN32_WCE)
763         if( GetSystemMetrics( SM_SWAPBUTTON ) )
764         {
765             if( button == GLUT_LEFT_BUTTON )
766                 button = GLUT_RIGHT_BUTTON;
767             else
768                 if( button == GLUT_RIGHT_BUTTON )
769                     button = GLUT_LEFT_BUTTON;
770         }
771 #endif /* !defined(_WIN32_WCE) */
772
773         if( button == -1 )
774             return DefWindowProc( hWnd, uMsg, lParam, wParam );
775
776         /*
777          * Do not execute the application's mouse callback if a menu
778          * is hooked to this button.  In that case an appropriate
779          * private call should be generated.
780          */
781         if( fgCheckActiveMenu( window, button, pressed,
782                                window->State.MouseX, window->State.MouseY ) )
783             break;
784
785         /* Set capture so that the window captures all the mouse messages
786          *
787          * The mouse is not released from the window until all buttons have
788          * been released, even if the user presses a button in another window.
789          * This is consistent with the behavior on X11.
790          */
791         if ( pressed == GL_TRUE )
792         {
793             if (!setCaptureActive)
794                 SetCapture ( window->Window.Handle ) ;
795             setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
796         }
797         else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
798           /* Make sure all mouse buttons are released before releasing capture */
799           ReleaseCapture () ;
800
801         if( ! FETCH_WCB( *window, Mouse ) )
802             break;
803
804         fgSetWindow( window );
805         fgState.Modifiers = fgPlatformGetModifiers( );
806
807         INVOKE_WCB(
808             *window, Mouse,
809             ( button,
810               pressed ? GLUT_DOWN : GLUT_UP,
811               window->State.MouseX,
812               window->State.MouseY
813             )
814         );
815
816         fgState.Modifiers = INVALID_MODIFIERS;
817
818         /* As per docs, should return zero */
819         lRet = 0;
820     }
821     break;
822
823     case WM_MOUSEWHEEL:
824     {
825         SFG_Window *child_window = NULL;
826         int wheel_number = LOWORD( wParam );
827         short ticks = ( short )HIWORD( wParam );
828
829         window = fghWindowUnderCursor(window);
830
831                 fgState.MouseWheelTicks += ticks;
832         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
833                 {
834                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
835
836             if( ! FETCH_WCB( *window, MouseWheel ) &&
837                 ! FETCH_WCB( *window, Mouse ) )
838                 break;
839
840             fgSetWindow( window );
841             fgState.Modifiers = fgPlatformGetModifiers( );
842
843             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
844                         {
845                 if( FETCH_WCB( *window, MouseWheel ) )
846                     INVOKE_WCB( *window, MouseWheel,
847                                 ( wheel_number,
848                                   direction,
849                                   window->State.MouseX,
850                                   window->State.MouseY
851                                 )
852                     );
853                 else  /* No mouse wheel, call the mouse button callback twice */
854                                 {
855                     /*
856                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
857                      *  "    "   one                     +1 to 5, -1 to 6, ...
858                      *
859                      * XXX The below assumes that you have no more than 3 mouse
860                      * XXX buttons.  Sorry.
861                      */
862                     int button = wheel_number * 2 + 3;
863                     if( direction < 0 )
864                         ++button;
865                     INVOKE_WCB( *window, Mouse,
866                                 ( button, GLUT_DOWN,
867                                   window->State.MouseX, window->State.MouseY )
868                     );
869                     INVOKE_WCB( *window, Mouse,
870                                 ( button, GLUT_UP,
871                                   window->State.MouseX, window->State.MouseY )
872                     );
873                                 }
874
875                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
876                         }
877
878             fgState.Modifiers = INVALID_MODIFIERS;
879                 }
880     }
881     break ;
882
883     case WM_SYSKEYDOWN:
884     case WM_KEYDOWN:
885     {
886         window = fghWindowUnderCursor(window);
887         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
888     }
889     break;
890
891     case WM_SYSKEYUP:
892     case WM_KEYUP:
893     {
894         window = fghWindowUnderCursor(window);
895         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
896     }
897     break;
898
899     case WM_SYSCHAR:
900     case WM_CHAR:
901     {
902       window = fghWindowUnderCursor(window);
903
904       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
905             break;
906
907         fgState.Modifiers = fgPlatformGetModifiers( );
908         INVOKE_WCB( *window, Keyboard,
909                     ( (char)wParam,
910                       window->State.MouseX, window->State.MouseY )
911         );
912         fgState.Modifiers = INVALID_MODIFIERS;
913     }
914     break;
915
916     case WM_CAPTURECHANGED:
917         if (!lParam || !fgWindowByHandle((HWND)lParam))
918             /* Capture released or capture taken by non-FreeGLUT window */
919             setCaptureActive = 0;
920         /* Docs advise a redraw */
921         InvalidateRect( hWnd, NULL, GL_FALSE );
922         UpdateWindow(hWnd);
923         lRet = 0;   /* Per docs, should return zero */
924         break;
925
926         /* Other messages that I have seen and which are not handled already */
927     case WM_SETTEXT:  /* 0x000c */
928         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
929         /* Pass it on to "DefWindowProc" to set the window text */
930         break;
931
932     case WM_GETTEXT:  /* 0x000d */
933         /* Ideally we would copy the title of the window into "lParam" */
934         /* strncpy ( (char *)lParam, "Window Title", wParam );
935            lRet = ( wParam > 12 ) ? 12 : wParam;  */
936         /* the number of characters copied */
937         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
938         break;
939
940     case WM_GETTEXTLENGTH:  /* 0x000e */
941         /* Ideally we would get the length of the title of the window */
942         lRet = 12;
943         /* the number of characters in "Window Title\0" (see above) */
944         break;
945
946     case WM_ERASEBKGND:  /* 0x0014 */
947         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
948         break;
949
950 #if !defined(_WIN32_WCE)
951     case WM_SYNCPAINT:  /* 0x0088 */
952         /* Another window has moved, need to update this one */
953         window->State.Redisplay = GL_TRUE;
954         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
955         /* Help screen says this message must be passed to "DefWindowProc" */
956         break;
957
958     case WM_NCPAINT:  /* 0x0085 */
959       /* Need to update the border of this window */
960         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
961         /* Pass it on to "DefWindowProc" to repaint a standard border */
962         break;
963
964     case WM_SYSCOMMAND :  /* 0x0112 */
965         {
966           /*
967            * We have received a system command message.  Try to act on it.
968            * The commands are passed in through the "wParam" parameter:
969            * The least significant digit seems to be which edge of the window
970            * is being used for a resize event:
971            *     4  3  5
972            *     1     2
973            *     7  6  8
974            * Congratulations and thanks to Richard Rauch for figuring this out..
975            */
976             switch ( wParam & 0xfff0 )
977             {
978             case SC_SIZE       :
979                 break ;
980
981             case SC_MOVE       :
982                 break ;
983
984             case SC_MINIMIZE   :
985                 /* User has clicked on the "-" to minimize the window */
986                 /* Turn off the visibility */
987                 fghUpdateWindowStatus(window, GL_FALSE);
988
989                 break ;
990
991             case SC_MAXIMIZE   :
992                 break ;
993
994             case SC_NEXTWINDOW :
995                 break ;
996
997             case SC_PREVWINDOW :
998                 break ;
999
1000             case SC_CLOSE      :
1001                 /* Followed very closely by a WM_CLOSE message */
1002                 break ;
1003
1004             case SC_VSCROLL    :
1005                 break ;
1006
1007             case SC_HSCROLL    :
1008                 break ;
1009
1010             case SC_MOUSEMENU  :
1011                 break ;
1012
1013             case SC_KEYMENU    :
1014                 break ;
1015
1016             case SC_ARRANGE    :
1017                 break ;
1018
1019             case SC_RESTORE    :
1020                 fghUpdateWindowStatus(window, GL_TRUE);
1021                 break ;
1022
1023             case SC_TASKLIST   :
1024                 break ;
1025
1026             case SC_SCREENSAVE :
1027                 break ;
1028
1029             case SC_HOTKEY     :
1030                 break ;
1031
1032 #if(WINVER >= 0x0400)
1033             case SC_DEFAULT    :
1034                 break ;
1035
1036             case SC_MONITORPOWER    :
1037                 break ;
1038
1039             case SC_CONTEXTHELP    :
1040                 break ;
1041 #endif /* WINVER >= 0x0400 */
1042
1043             default:
1044 #if _DEBUG
1045                 fgWarning( "Unknown wParam type 0x%x", wParam );
1046 #endif
1047                 break;
1048             }
1049         }
1050 #endif /* !defined(_WIN32_WCE) */
1051
1052         /* We need to pass the message on to the operating system as well */
1053         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1054         break;
1055
1056 #ifdef WM_TOUCH
1057         /* handle multi-touch messages */
1058         case WM_TOUCH:
1059         {
1060                 unsigned int numInputs = (unsigned int)wParam;
1061                 unsigned int i = 0;
1062                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1063
1064                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1065                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1066                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1067                 }
1068
1069                 if (!fghGetTouchInputInfo) { 
1070                         free( (void*)ti );
1071                         break;
1072                 }
1073
1074                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1075                         /* Handle each contact point */
1076                         for (i = 0; i < numInputs; ++i ) {
1077
1078                                 POINT tp;
1079                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1080                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1081                                 ScreenToClient( hWnd, &tp );
1082
1083                                 ti[i].dwID = ti[i].dwID * 2;
1084
1085                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1086                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1087                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1088                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1089                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1090                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1091                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1092                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1093                                 }
1094                         }
1095                 }
1096                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1097                 free( (void*)ti );
1098                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1099                 break;
1100         }
1101 #endif
1102     default:
1103         /* Handle unhandled messages */
1104         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1105         break;
1106     }
1107
1108     return lRet;
1109 }