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