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