baf4b3b0dbb5acc85445c8e039d1fc5d0365b812
[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     static int setCaptureActive = 0;
365
366     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
367
368     window = fgWindowByHandle( hWnd );
369
370     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
371       return DefWindowProc( hWnd, uMsg, wParam, lParam );
372
373     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
374              uMsg, wParam, lParam ); */
375
376     switch( uMsg )
377     {
378     case WM_CREATE:
379         /* The window structure is passed as the creation structure parameter... */
380         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
381         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
382                                        "fgPlatformWindowProc" );
383
384         window->Window.Handle = hWnd;
385         window->Window.pContext.Device = GetDC( hWnd );
386         if( window->IsMenu )
387         {
388             unsigned int current_DisplayMode = fgState.DisplayMode;
389             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
390 #if !defined(_WIN32_WCE)
391             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
392 #endif
393             fgState.DisplayMode = current_DisplayMode;
394
395             if( fgStructure.MenuContext )
396                 wglMakeCurrent( window->Window.pContext.Device,
397                                 fgStructure.MenuContext->MContext
398                 );
399             else
400             {
401                 fgStructure.MenuContext =
402                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
403                 fgStructure.MenuContext->MContext =
404                     wglCreateContext( window->Window.pContext.Device );
405             }
406
407             /* window->Window.Context = wglGetCurrentContext ();   */
408             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
409         }
410         else
411         {
412 #if !defined(_WIN32_WCE)
413             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
414 #endif
415
416             if( ! fgState.UseCurrentContext )
417                 window->Window.Context =
418                     wglCreateContext( window->Window.pContext.Device );
419             else
420             {
421                 window->Window.Context = wglGetCurrentContext( );
422                 if( ! window->Window.Context )
423                     window->Window.Context =
424                         wglCreateContext( window->Window.pContext.Device );
425             }
426
427 #if !defined(_WIN32_WCE)
428             fgNewWGLCreateContext( window );
429 #endif
430         }
431
432         window->State.NeedToResize = GL_TRUE;
433         /* if we used CW_USEDEFAULT (thats a negative value) for the size
434          * of the window, query the window now for the size at which it
435          * was created.
436          */
437         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
438         {
439             SFG_Window *current_window = fgStructure.CurrentWindow;
440
441             fgSetWindow( window );
442             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
443             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
444             fgSetWindow( current_window );
445         }
446
447         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
448
449 #if defined(_WIN32_WCE)
450         /* Take over button handling */
451         {
452             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
453             if (dxDllLib)
454             {
455                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
456                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
457             }
458
459             if(GXOpenInput_)
460                 (*GXOpenInput_)();
461             if(GXGetDefaultKeys_)
462                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
463         }
464
465 #endif /* defined(_WIN32_WCE) */
466         break;
467
468     case WM_SIZE:
469         /*
470          * If the window is visible, then it is the user manually resizing it.
471          * If it is not, then it is the system sending us a dummy resize with
472          * zero dimensions on a "glutIconifyWindow" call.
473          */
474         if( window->State.Visible )
475         {
476             /* get old values first to compare to below */
477             int width = window->State.Width, height=window->State.Height;
478 #if defined(_WIN32_WCE)
479             window->State.Width  = HIWORD(lParam);
480             window->State.Height = LOWORD(lParam);
481 #else
482             window->State.Width  = LOWORD(lParam);
483             window->State.Height = HIWORD(lParam);
484 #endif /* defined(_WIN32_WCE) */
485             
486             if (width!=window->State.Width || height!=window->State.Height)
487                 /* Something changed, need to resize */
488                 window->State.NeedToResize = GL_TRUE;
489         }
490
491         break;
492
493     case WM_MOVE:
494         {
495             SFG_Window* saved_window = fgStructure.CurrentWindow;
496             RECT windowRect;
497             GetWindowRect( window->Window.Handle, &windowRect );
498             
499             if (window->Parent)
500             {
501                 /* For child window, we should return relative to upper-left
502                 * of parent's client area.
503                 */
504                 POINT topleft = {windowRect.left,windowRect.top};
505
506                 ScreenToClient(window->Parent->Window.Handle,&topleft);
507                 windowRect.left = topleft.x;
508                 windowRect.top  = topleft.y;
509             }
510
511             INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) );
512             fgSetWindow(saved_window);
513         }
514         break;
515
516     case WM_SETFOCUS:
517 /*        printf("WM_SETFOCUS: %p\n", window ); */
518
519         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
520
521         SetActiveWindow( window->Window.Handle );
522         UpdateWindow ( hWnd );
523
524         break;
525
526     case WM_KILLFOCUS:
527         {
528 /*            printf("WM_KILLFOCUS: %p\n", window ); */
529             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
530
531             /* Check if there are any open menus that need to be closed */
532             fgPlatformCheckMenuDeactivate();
533         }
534         break;
535
536 #if 0
537     case WM_ACTIVATE:
538         //printf("WM_ACTIVATE: %x %d %d\n",lParam, HIWORD(wParam), LOWORD(wParam));
539         if (LOWORD(wParam) != WA_INACTIVE)
540         {
541 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
542                    window->State.Cursor ); */
543             fgSetCursor( window, window->State.Cursor );
544         }
545
546         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
547         break;
548 #endif
549
550     case WM_SETCURSOR:
551 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
552         if( LOWORD( lParam ) == HTCLIENT )
553         {
554             if (!window->State.pWState.MouseTracking)
555             {
556                 TRACKMOUSEEVENT tme;
557
558                 /* Cursor just entered window, set cursor look */ 
559                 fgSetCursor ( window, window->State.Cursor ) ;
560
561                 /* If an EntryFunc callback is specified by the user, also
562                  * invoke that callback and start mouse tracking so that
563                  * we get a WM_MOUSELEAVE message
564                  */
565                 if (FETCH_WCB( *window, Entry ))
566                 {
567                     INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
568
569                     tme.cbSize = sizeof(TRACKMOUSEEVENT);
570                     tme.dwFlags = TME_LEAVE;
571                     tme.hwndTrack = window->Window.Handle;
572                     TrackMouseEvent(&tme);
573
574                     window->State.pWState.MouseTracking = GL_TRUE;
575                 }
576             }
577         }
578         else
579             /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
580             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
581         break;
582
583     case WM_MOUSELEAVE:
584         {
585             /* NB: This message is only received when a EntryFunc callback
586              * is specified by the user, as that is the only condition under
587              * which mouse tracking is setup in WM_SETCURSOR handler above
588              */
589             SFG_Window* saved_window = fgStructure.CurrentWindow;
590             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
591             fgSetWindow(saved_window);
592
593             window->State.pWState.MouseTracking = GL_FALSE;
594             lRet = 0;   /* As per docs, must return zero */
595         }
596         break;
597
598     case WM_SHOWWINDOW:
599         window->State.Visible = GL_TRUE;
600         window->State.Redisplay = GL_TRUE;
601         break;
602
603     case WM_PAINT:
604     {
605         PAINTSTRUCT ps;
606         /* Turn on the visibility in case it was turned off somehow */
607         window->State.Visible = GL_TRUE;
608         InvalidateRect( hWnd, NULL, GL_FALSE ); /* Make sure whole window is repainted. Bit of a hack, but a safe one from what google turns up... */
609         BeginPaint( hWnd, &ps );
610         fghRedrawWindow( window );
611         EndPaint( hWnd, &ps );
612     }
613     break;
614
615     case WM_CLOSE:
616         fgDestroyWindow ( window );
617         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
618             PostQuitMessage(0);
619         break;
620
621     case WM_DESTROY:
622         /*
623          * The window already got destroyed, so don't bother with it.
624          */
625         return 0;
626
627     case WM_CANCELMODE:
628         /*
629          * The window manager sends this message when it detects a change
630          * that requires that an application cancel any modal state it has
631          * entered. If we've called SetCapture in the mouse button handler,
632          * call ReleaseCapture.
633          */
634         if (setCaptureActive)
635             ReleaseCapture();
636         break;
637
638     case WM_MOUSEMOVE:
639     {
640 #if defined(_WIN32_WCE)
641         window->State.MouseX = 320-HIWORD( lParam );
642         window->State.MouseY = LOWORD( lParam );
643 #else
644         window->State.MouseX = LOWORD( lParam );
645         window->State.MouseY = HIWORD( lParam );
646 #endif /* defined(_WIN32_WCE) */
647         /* Restrict to [-32768, 32767] to match X11 behaviour       */
648         /* See comment in "freeglut_developer" mailing list 10/4/04 */
649         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
650         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
651
652         if ( window->ActiveMenu )
653         {
654             fgUpdateMenuHighlight( window->ActiveMenu );
655             break;
656         }
657
658         fgState.Modifiers = fgPlatformGetModifiers( );
659
660         if( ( wParam & MK_LBUTTON ) ||
661             ( wParam & MK_MBUTTON ) ||
662             ( wParam & MK_RBUTTON ) )
663             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
664                                            window->State.MouseY ) );
665         else
666             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
667                                             window->State.MouseY ) );
668
669         fgState.Modifiers = INVALID_MODIFIERS;
670     }
671     break;
672
673     case WM_LBUTTONDOWN:
674     case WM_MBUTTONDOWN:
675     case WM_RBUTTONDOWN:
676     case WM_LBUTTONUP:
677     case WM_MBUTTONUP:
678     case WM_RBUTTONUP:
679     {
680         GLboolean pressed = GL_TRUE;
681         int button;
682
683 #if defined(_WIN32_WCE)
684         window->State.MouseX = 320-HIWORD( lParam );
685         window->State.MouseY = LOWORD( lParam );
686 #else
687         window->State.MouseX = LOWORD( lParam );
688         window->State.MouseY = HIWORD( lParam );
689 #endif /* defined(_WIN32_WCE) */
690
691         /* Restrict to [-32768, 32767] to match X11 behaviour       */
692         /* See comment in "freeglut_developer" mailing list 10/4/04 */
693         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
694         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
695
696         switch( uMsg )
697         {
698         case WM_LBUTTONDOWN:
699             pressed = GL_TRUE;
700             button = GLUT_LEFT_BUTTON;
701             break;
702         case WM_MBUTTONDOWN:
703             pressed = GL_TRUE;
704             button = GLUT_MIDDLE_BUTTON;
705             break;
706         case WM_RBUTTONDOWN:
707             pressed = GL_TRUE;
708             button = GLUT_RIGHT_BUTTON;
709             break;
710         case WM_LBUTTONUP:
711             pressed = GL_FALSE;
712             button = GLUT_LEFT_BUTTON;
713             break;
714         case WM_MBUTTONUP:
715             pressed = GL_FALSE;
716             button = GLUT_MIDDLE_BUTTON;
717             break;
718         case WM_RBUTTONUP:
719             pressed = GL_FALSE;
720             button = GLUT_RIGHT_BUTTON;
721             break;
722         default:
723             pressed = GL_FALSE;
724             button = -1;
725             break;
726         }
727
728 #if !defined(_WIN32_WCE)
729         if( GetSystemMetrics( SM_SWAPBUTTON ) )
730         {
731             if( button == GLUT_LEFT_BUTTON )
732                 button = GLUT_RIGHT_BUTTON;
733             else
734                 if( button == GLUT_RIGHT_BUTTON )
735                     button = GLUT_LEFT_BUTTON;
736         }
737 #endif /* !defined(_WIN32_WCE) */
738
739         if( button == -1 )
740             return DefWindowProc( hWnd, uMsg, lParam, wParam );
741
742         /*
743          * Do not execute the application's mouse callback if a menu
744          * is hooked to this button.  In that case an appropriate
745          * private call should be generated.
746          */
747         if( fgCheckActiveMenu( window, button, pressed,
748                                window->State.MouseX, window->State.MouseY ) )
749             break;
750
751         /* Set capture so that the window captures all the mouse messages
752          *
753          * The mouse is not released from the window until all buttons have
754          * been released, even if the user presses a button in another window.
755          * This is consistent with the behavior on X11.
756          */
757         if ( pressed == GL_TRUE )
758         {
759             if (!setCaptureActive)
760                 SetCapture ( window->Window.Handle ) ;
761             setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
762         }
763         else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
764           /* Make sure all mouse buttons are released before releasing capture */
765           ReleaseCapture () ;
766
767         if( ! FETCH_WCB( *window, Mouse ) )
768             break;
769
770         fgSetWindow( window );
771         fgState.Modifiers = fgPlatformGetModifiers( );
772
773         INVOKE_WCB(
774             *window, Mouse,
775             ( button,
776               pressed ? GLUT_DOWN : GLUT_UP,
777               window->State.MouseX,
778               window->State.MouseY
779             )
780         );
781
782         fgState.Modifiers = INVALID_MODIFIERS;
783
784         /* As per docs, should return zero */
785         lRet = 0;
786     }
787     break;
788
789     case WM_MOUSEWHEEL:
790     {
791         SFG_Window *child_window = NULL;
792         int wheel_number = LOWORD( wParam );
793         short ticks = ( short )HIWORD( wParam );
794
795         window = fghWindowUnderCursor(window);
796
797                 fgState.MouseWheelTicks += ticks;
798         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
799                 {
800                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
801
802             if( ! FETCH_WCB( *window, MouseWheel ) &&
803                 ! FETCH_WCB( *window, Mouse ) )
804                 break;
805
806             fgSetWindow( window );
807             fgState.Modifiers = fgPlatformGetModifiers( );
808
809             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
810                         {
811                 if( FETCH_WCB( *window, MouseWheel ) )
812                     INVOKE_WCB( *window, MouseWheel,
813                                 ( wheel_number,
814                                   direction,
815                                   window->State.MouseX,
816                                   window->State.MouseY
817                                 )
818                     );
819                 else  /* No mouse wheel, call the mouse button callback twice */
820                                 {
821                     /*
822                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
823                      *  "    "   one                     +1 to 5, -1 to 6, ...
824                      *
825                      * XXX The below assumes that you have no more than 3 mouse
826                      * XXX buttons.  Sorry.
827                      */
828                     int button = wheel_number * 2 + 3;
829                     if( direction < 0 )
830                         ++button;
831                     INVOKE_WCB( *window, Mouse,
832                                 ( button, GLUT_DOWN,
833                                   window->State.MouseX, window->State.MouseY )
834                     );
835                     INVOKE_WCB( *window, Mouse,
836                                 ( button, GLUT_UP,
837                                   window->State.MouseX, window->State.MouseY )
838                     );
839                                 }
840
841                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
842                         }
843
844             fgState.Modifiers = INVALID_MODIFIERS;
845                 }
846     }
847     break ;
848
849     case WM_SYSKEYDOWN:
850     case WM_KEYDOWN:
851     {
852         window = fghWindowUnderCursor(window);
853         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
854     }
855     break;
856
857     case WM_SYSKEYUP:
858     case WM_KEYUP:
859     {
860         window = fghWindowUnderCursor(window);
861         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
862     }
863     break;
864
865     case WM_SYSCHAR:
866     case WM_CHAR:
867     {
868       window = fghWindowUnderCursor(window);
869
870       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
871             break;
872
873         fgState.Modifiers = fgPlatformGetModifiers( );
874         INVOKE_WCB( *window, Keyboard,
875                     ( (char)wParam,
876                       window->State.MouseX, window->State.MouseY )
877         );
878         fgState.Modifiers = INVALID_MODIFIERS;
879     }
880     break;
881
882     case WM_CAPTURECHANGED:
883         if (!lParam || !fgWindowByHandle((HWND)lParam))
884             /* Capture released or capture taken by non-FreeGLUT window */
885             setCaptureActive = 0;
886         /* User has finished resizing the window, force a redraw */
887         INVOKE_WCB( *window, Display, ( ) );
888         lRet = 0;   /* Per docs, should return zero */
889         break;
890
891         /* Other messages that I have seen and which are not handled already */
892     case WM_SETTEXT:  /* 0x000c */
893         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
894         /* Pass it on to "DefWindowProc" to set the window text */
895         break;
896
897     case WM_GETTEXT:  /* 0x000d */
898         /* Ideally we would copy the title of the window into "lParam" */
899         /* strncpy ( (char *)lParam, "Window Title", wParam );
900            lRet = ( wParam > 12 ) ? 12 : wParam;  */
901         /* the number of characters copied */
902         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
903         break;
904
905     case WM_GETTEXTLENGTH:  /* 0x000e */
906         /* Ideally we would get the length of the title of the window */
907         lRet = 12;
908         /* the number of characters in "Window Title\0" (see above) */
909         break;
910
911     case WM_ERASEBKGND:  /* 0x0014 */
912         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
913         break;
914
915 #if !defined(_WIN32_WCE)
916     case WM_SYNCPAINT:  /* 0x0088 */
917         /* Another window has moved, need to update this one */
918         window->State.Redisplay = GL_TRUE;
919         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
920         /* Help screen says this message must be passed to "DefWindowProc" */
921         break;
922
923     case WM_NCPAINT:  /* 0x0085 */
924       /* Need to update the border of this window */
925         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
926         /* Pass it on to "DefWindowProc" to repaint a standard border */
927         break;
928
929     case WM_SYSCOMMAND :  /* 0x0112 */
930         {
931           /*
932            * We have received a system command message.  Try to act on it.
933            * The commands are passed in through the "wParam" parameter:
934            * The least significant digit seems to be which edge of the window
935            * is being used for a resize event:
936            *     4  3  5
937            *     1     2
938            *     7  6  8
939            * Congratulations and thanks to Richard Rauch for figuring this out..
940            */
941             switch ( wParam & 0xfff0 )
942             {
943             case SC_SIZE       :
944                 break ;
945
946             case SC_MOVE       :
947                 break ;
948
949             case SC_MINIMIZE   :
950                 /* User has clicked on the "-" to minimize the window */
951                 /* Turn off the visibility */
952                 window->State.Visible = GL_FALSE ;
953
954                 break ;
955
956             case SC_MAXIMIZE   :
957                 break ;
958
959             case SC_NEXTWINDOW :
960                 break ;
961
962             case SC_PREVWINDOW :
963                 break ;
964
965             case SC_CLOSE      :
966                 /* Followed very closely by a WM_CLOSE message */
967                 break ;
968
969             case SC_VSCROLL    :
970                 break ;
971
972             case SC_HSCROLL    :
973                 break ;
974
975             case SC_MOUSEMENU  :
976                 break ;
977
978             case SC_KEYMENU    :
979                 break ;
980
981             case SC_ARRANGE    :
982                 break ;
983
984             case SC_RESTORE    :
985                 break ;
986
987             case SC_TASKLIST   :
988                 break ;
989
990             case SC_SCREENSAVE :
991                 break ;
992
993             case SC_HOTKEY     :
994                 break ;
995
996 #if(WINVER >= 0x0400)
997             case SC_DEFAULT    :
998                 break ;
999
1000             case SC_MONITORPOWER    :
1001                 break ;
1002
1003             case SC_CONTEXTHELP    :
1004                 break ;
1005 #endif /* WINVER >= 0x0400 */
1006
1007             default:
1008 #if _DEBUG
1009                 fgWarning( "Unknown wParam type 0x%x", wParam );
1010 #endif
1011                 break;
1012             }
1013         }
1014 #endif /* !defined(_WIN32_WCE) */
1015
1016         /* We need to pass the message on to the operating system as well */
1017         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1018         break;
1019
1020 #ifdef WM_TOUCH
1021         /* handle multi-touch messages */
1022         case WM_TOUCH:
1023         {
1024                 unsigned int numInputs = (unsigned int)wParam;
1025                 unsigned int i = 0;
1026                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1027
1028                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1029                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1030                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1031                 }
1032
1033                 if (!fghGetTouchInputInfo) { 
1034                         free( (void*)ti );
1035                         break;
1036                 }
1037
1038                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1039                         /* Handle each contact point */
1040                         for (i = 0; i < numInputs; ++i ) {
1041
1042                                 POINT tp;
1043                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1044                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1045                                 ScreenToClient( hWnd, &tp );
1046
1047                                 ti[i].dwID = ti[i].dwID * 2;
1048
1049                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1050                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1051                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1052                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1053                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1054                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1055                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1056                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1057                                 }
1058                         }
1059                 }
1060                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1061                 free( (void*)ti );
1062                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1063                 break;
1064         }
1065 #endif
1066     default:
1067         /* Handle unhandled messages */
1068         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1069         break;
1070     }
1071
1072     return lRet;
1073 }