now have 64bit internal time on Windows, as we deal with wrap of timeGetTime manually
[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  * Helper functions for getting client area from the window rect
63  * and the window rect from the client area given the style of the window
64  * (or a valid window pointer from which the style can be queried).
65  */
66 extern void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside );
67 extern void fghGetClientArea                              ( RECT *clientRect, const SFG_Window *window, BOOL wantPosOutside );
68
69
70 void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
71 {
72     RECT windowRect;
73
74     /*
75      * For windowed mode, get the current position of the
76      * window and resize taking the size of the frame
77      * decorations into account.
78      *
79      * Note on maximizing behavior of Windows: the resize borders are off
80      * the screen such that the client area extends all the way from the
81      * leftmost corner to the rightmost corner to maximize screen real
82      * estate. A caption is still shown however to allow interaction with
83      * the window controls. This is default behavior of Windows that
84      * FreeGLUT sticks with. To alter, one would have to check if
85      * WS_MAXIMIZE style is set when a resize event is triggered, and
86      * then manually correct the windowRect to put the borders back on
87      * screen.
88      */
89
90     /* "GetWindowRect" returns the pixel coordinates of the outside of the window */
91     GetWindowRect( window->Window.Handle, &windowRect );
92
93     /* Create rect in FreeGLUT format, (X,Y) topleft outside window, WxH of client area */
94     windowRect.right    = windowRect.left+width;
95     windowRect.bottom   = windowRect.top+height;
96
97     if (window->Parent == NULL)
98         /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
99         fghComputeWindowRectFromClientArea_QueryWindow(&windowRect,window,TRUE);
100     else
101     {
102         /* correct rect for position client area of parent window
103          * (SetWindowPos input for child windows is in coordinates
104          * relative to the parent's client area).
105          * Child windows don't have decoration, so no need to correct
106          * for them.
107          */
108         RECT parentRect;
109         fghGetClientArea( &parentRect, window->Parent, FALSE );
110         OffsetRect(&windowRect,-parentRect.left,-parentRect.top);
111     }
112     
113     /* Do the actual resizing */
114     SetWindowPos( window->Window.Handle,
115                   HWND_TOP,
116                   windowRect.left, windowRect.top,
117                   windowRect.right - windowRect.left,
118                   windowRect.bottom- windowRect.top,
119                   SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING |
120                   SWP_NOZORDER
121     );
122
123     /* Set new width and height so we can test for that in WM_SIZE message handler and don't do anything if not needed */
124     window->State.Width  = width;
125     window->State.Height = height;
126 }
127
128
129 void fgPlatformDisplayWindow ( SFG_Window *window )
130 {
131     RedrawWindow(
132         window->Window.Handle, NULL, NULL,
133         RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW
134     );
135 }
136
137
138 /* Get system time, taking special precautions against 32bit timer wrap.
139    We use timeGetTime and not GetTickCount because of its better stability,
140    and because we can increase its granularity (to 1 ms in
141    fgPlatformInitialize). For that reason we can't use GetTickCount64 which
142    wouldn't have the wrap issue.
143    Credit: this is based on code in glibc (https://mail.gnome.org/archives/commits-list/2011-November/msg04588.html)
144    */
145 static fg_time_t lastTime32 = 0;
146 static fg_time_t timeEpoch = 0;
147 void fgPlatformInitSystemTime()
148 {
149 #if defined(_WIN32_WCE)
150     lastTime32 = GetTickCount();
151 #else
152     lastTime32 = timeGetTime();
153 #endif
154 }
155 fg_time_t fgPlatformSystemTime ( void )
156 {
157     fg_time_t currTime32;
158 #if defined(_WIN32_WCE)
159     currTime32 = GetTickCount();
160 #else
161     currTime32 = timeGetTime();
162 #endif
163     /* Check if we just wrapped */
164     if (currTime32 < lastTime32)
165         timeEpoch++;
166     
167     lastTime32 = currTime32;
168
169     return currTime32 | timeEpoch << 32;
170 }
171
172
173 void fgPlatformSleepForEvents( fg_time_t msec )
174 {
175     MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
176 }
177
178
179 void fgPlatformProcessSingleEvent ( void )
180 {
181     MSG stMsg;
182
183     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
184
185     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
186     {
187         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
188         {
189             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
190             {
191                 fgDeinitialize( );
192                 exit( 0 );
193             }
194             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
195                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
196
197             return;
198         }
199
200         TranslateMessage( &stMsg );
201         DispatchMessage( &stMsg );
202     }
203 }
204
205
206
207 void fgPlatformMainLoopPreliminaryWork ( void )
208 {
209     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
210
211     /*
212      * Processing before the main loop:  If there is a window which is open and
213      * which has a visibility callback, call it.  I know this is an ugly hack,
214      * but I'm not sure what else to do about it.  Ideally we should leave
215      * something uninitialized in the create window code and initialize it in
216      * the main loop, and have that initialization create a "WM_ACTIVATE"
217      * message.  Then we would put the visibility callback code in the
218      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
219      */
220     while( window )
221     {
222         if ( FETCH_WCB( *window, Visibility ) )
223         {
224             SFG_Window *current_window = fgStructure.CurrentWindow ;
225
226             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
227             fgSetWindow( current_window );
228         }
229
230         window = (SFG_Window *)window->Node.Next ;
231     }
232 }
233
234
235 /*
236  * Determine a GLUT modifier mask based on MS-WINDOWS system info.
237  */
238 static int fgPlatformGetModifiers (void)
239 {
240     return
241         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
242             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
243         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
244             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
245         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
246             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
247 }
248
249 /*
250  * The window procedure for handling Win32 events
251  */
252 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
253                                        LPARAM lParam )
254 {
255     static unsigned char lControl = 0, rControl = 0, lShift = 0,
256                          rShift = 0, lAlt = 0, rAlt = 0;
257
258     SFG_Window *window, *child_window = NULL;
259     PAINTSTRUCT ps;
260     LRESULT lRet = 1;
261
262     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
263
264     window = fgWindowByHandle( hWnd );
265
266     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
267       return DefWindowProc( hWnd, uMsg, wParam, lParam );
268
269     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
270              uMsg, wParam, lParam ); */
271
272     /* Some events only sent to main window. Check if the current window that
273      * the mouse is over is a child window. Below when handling some messages,
274      * we make sure that we process callbacks on the child window instead.
275      * This mirrors how GLUT does things.
276      */
277     if (window && window->Children.First)
278     {
279         POINT mouse_pos;
280         SFG_WindowHandleType hwnd;
281         SFG_Window* temp_window;
282
283         GetCursorPos( &mouse_pos );
284         ScreenToClient( window->Window.Handle, &mouse_pos );
285         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
286         if (hwnd)   /* can be NULL if mouse outside parent by the time we get here */
287         {
288             temp_window = fgWindowByHandle(hwnd);
289             if (temp_window && temp_window->Parent)    /* Verify we got a child window */
290                 child_window = temp_window;
291         }
292     }
293
294     if ( window )
295     {
296       SFG_Window* temp_window = child_window?child_window:window;
297
298       fgState.Modifiers = fgPlatformGetModifiers( );
299
300       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Down! */
301 #define SPECIAL_KEY_DOWN(winKey,glutKey,winProcVar)\
302       if ( !winProcVar && GetAsyncKeyState ( winKey ) )\
303       {\
304           INVOKE_WCB  ( *temp_window, Special,\
305               ( glutKey, temp_window->State.MouseX, temp_window->State.MouseY )\
306               );\
307           winProcVar = 1;\
308       }
309
310       SPECIAL_KEY_DOWN(VK_LCONTROL,GLUT_KEY_CTRL_L ,lControl);
311       SPECIAL_KEY_DOWN(VK_RCONTROL,GLUT_KEY_CTRL_R ,rControl);
312       SPECIAL_KEY_DOWN(VK_LSHIFT  ,GLUT_KEY_SHIFT_L,lShift);
313       SPECIAL_KEY_DOWN(VK_RSHIFT  ,GLUT_KEY_SHIFT_R,rShift);
314       SPECIAL_KEY_DOWN(VK_LMENU   ,GLUT_KEY_ALT_L  ,lAlt);
315       SPECIAL_KEY_DOWN(VK_RMENU   ,GLUT_KEY_ALT_R  ,rAlt);
316 #undef SPECIAL_KEY_DOWN
317
318       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Up! */
319 #define SPECIAL_KEY_UP(winKey,glutKey,winProcVar)\
320       if ( winProcVar && !GetAsyncKeyState ( winKey ) )\
321       {\
322           INVOKE_WCB  ( *temp_window, SpecialUp,\
323               ( glutKey, temp_window->State.MouseX, temp_window->State.MouseY )\
324               );\
325           winProcVar = 0;\
326       }
327
328       SPECIAL_KEY_UP(VK_LCONTROL,GLUT_KEY_CTRL_L ,lControl);
329       SPECIAL_KEY_UP(VK_RCONTROL,GLUT_KEY_CTRL_R ,rControl);
330       SPECIAL_KEY_UP(VK_LSHIFT  ,GLUT_KEY_SHIFT_L,lShift);
331       SPECIAL_KEY_UP(VK_RSHIFT  ,GLUT_KEY_SHIFT_R,rShift);
332       SPECIAL_KEY_UP(VK_LMENU   ,GLUT_KEY_ALT_L  ,lAlt);
333       SPECIAL_KEY_UP(VK_RMENU   ,GLUT_KEY_ALT_R  ,rAlt);
334 #undef SPECIAL_KEY_UP
335
336       fgState.Modifiers = INVALID_MODIFIERS;
337     }
338
339     switch( uMsg )
340     {
341     case WM_CREATE:
342         /* The window structure is passed as the creation structure parameter... */
343         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
344         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
345                                        "fgPlatformWindowProc" );
346
347         window->Window.Handle = hWnd;
348         window->Window.pContext.Device = GetDC( hWnd );
349         if( window->IsMenu )
350         {
351             unsigned int current_DisplayMode = fgState.DisplayMode;
352             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
353 #if !defined(_WIN32_WCE)
354             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
355 #endif
356             fgState.DisplayMode = current_DisplayMode;
357
358             if( fgStructure.MenuContext )
359                 wglMakeCurrent( window->Window.pContext.Device,
360                                 fgStructure.MenuContext->MContext
361                 );
362             else
363             {
364                 fgStructure.MenuContext =
365                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
366                 fgStructure.MenuContext->MContext =
367                     wglCreateContext( window->Window.pContext.Device );
368             }
369
370             /* window->Window.Context = wglGetCurrentContext ();   */
371             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
372         }
373         else
374         {
375 #if !defined(_WIN32_WCE)
376             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
377 #endif
378
379             if( ! fgState.UseCurrentContext )
380                 window->Window.Context =
381                     wglCreateContext( window->Window.pContext.Device );
382             else
383             {
384                 window->Window.Context = wglGetCurrentContext( );
385                 if( ! window->Window.Context )
386                     window->Window.Context =
387                         wglCreateContext( window->Window.pContext.Device );
388             }
389
390 #if !defined(_WIN32_WCE)
391             fgNewWGLCreateContext( window );
392 #endif
393         }
394
395         window->State.NeedToResize = GL_TRUE;
396         /* if we used CW_USEDEFAULT (thats a negative value) for the size
397          * of the window, query the window now for the size at which it
398          * was created.
399          */
400         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
401         {
402             SFG_Window *current_window = fgStructure.CurrentWindow;
403
404             fgSetWindow( window );
405             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
406             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
407             fgSetWindow( current_window );
408         }
409
410         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
411
412 #if defined(_WIN32_WCE)
413         /* Take over button handling */
414         {
415             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
416             if (dxDllLib)
417             {
418                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
419                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
420             }
421
422             if(GXOpenInput_)
423                 (*GXOpenInput_)();
424             if(GXGetDefaultKeys_)
425                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
426         }
427
428 #endif /* defined(_WIN32_WCE) */
429         break;
430
431     case WM_SIZE:
432         /*
433          * If the window is visible, then it is the user manually resizing it.
434          * If it is not, then it is the system sending us a dummy resize with
435          * zero dimensions on a "glutIconifyWindow" call.
436          */
437         if( window->State.Visible )
438         {
439             /* get old values first to compare to below */
440             int width = window->State.Width, height=window->State.Height;
441 #if defined(_WIN32_WCE)
442             window->State.Width  = HIWORD(lParam);
443             window->State.Height = LOWORD(lParam);
444 #else
445             window->State.Width  = LOWORD(lParam);
446             window->State.Height = HIWORD(lParam);
447 #endif /* defined(_WIN32_WCE) */
448             
449             if (width!=window->State.Width || height!=window->State.Height)
450                 /* Something changed, need to resize */
451                 window->State.NeedToResize = GL_TRUE;
452         }
453
454         break;
455
456     case WM_MOVE:
457         {
458             SFG_Window* saved_window = fgStructure.CurrentWindow;
459             RECT windowRect;
460             GetWindowRect( window->Window.Handle, &windowRect );
461             
462             if (window->Parent)
463             {
464                 /* For child window, we should return relative to upper-left
465                 * of parent's client area.
466                 */
467                 POINT topleft = {windowRect.left,windowRect.top};
468
469                 ScreenToClient(window->Parent->Window.Handle,&topleft);
470                 windowRect.left = topleft.x;
471                 windowRect.top  = topleft.y;
472             }
473
474             INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) );
475             fgSetWindow(saved_window);
476         }
477         break;
478
479     case WM_SETFOCUS:
480 /*        printf("WM_SETFOCUS: %p\n", window ); */
481
482         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
483
484         if (child_window)
485         {
486             /* If we're dealing with a child window, make sure it has input focus instead, set it here. */
487             SetFocus(child_window->Window.Handle);
488             SetActiveWindow( child_window->Window.Handle );
489             INVOKE_WCB( *child_window, Entry, ( GLUT_ENTERED ) );
490             UpdateWindow ( child_window->Window.Handle );
491         }
492         else
493         {
494             SetActiveWindow( window->Window.Handle );
495             INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
496         }
497         /* Always request update on main window to be safe */
498         UpdateWindow ( hWnd );
499
500         break;
501
502     case WM_KILLFOCUS:
503         {
504             SFG_Window* saved_window = fgStructure.CurrentWindow;
505 /*            printf("WM_KILLFOCUS: %p\n", window ); */
506             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
507             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
508             fgSetWindow(saved_window);
509
510             /* Check if there are any open menus that need to be closed */
511             fgPlatformCheckMenuDeactivate();
512         }
513         break;
514
515 #if 0
516     case WM_ACTIVATE:
517         if (LOWORD(wParam) != WA_INACTIVE)
518         {
519 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
520                    window->State.Cursor ); */
521             fgSetCursor( window, window->State.Cursor );
522         }
523
524         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
525         break;
526 #endif
527
528     case WM_SETCURSOR:
529 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
530         if( LOWORD( lParam ) == HTCLIENT )
531             fgSetCursor ( window, window->State.Cursor ) ;
532         else
533             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
534         break;
535
536     case WM_SHOWWINDOW:
537         window->State.Visible = GL_TRUE;
538         window->State.Redisplay = GL_TRUE;
539         break;
540
541     case WM_PAINT:
542         /* Turn on the visibility in case it was turned off somehow */
543         window->State.Visible = GL_TRUE;
544         InvalidateRect( hWnd, NULL, GL_FALSE ); /* Make sure whole window is repainted. Bit of a hack, but a safe one from what google turns up... */
545         BeginPaint( hWnd, &ps );
546         fghRedrawWindow( window );
547         EndPaint( hWnd, &ps );
548         break;
549
550     case WM_CLOSE:
551         fgDestroyWindow ( window );
552         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
553             PostQuitMessage(0);
554         break;
555
556     case WM_DESTROY:
557         /*
558          * The window already got destroyed, so don't bother with it.
559          */
560         return 0;
561
562     case WM_MOUSEMOVE:
563     {
564 #if defined(_WIN32_WCE)
565         window->State.MouseX = 320-HIWORD( lParam );
566         window->State.MouseY = LOWORD( lParam );
567 #else
568         window->State.MouseX = LOWORD( lParam );
569         window->State.MouseY = HIWORD( lParam );
570 #endif /* defined(_WIN32_WCE) */
571         /* Restrict to [-32768, 32767] to match X11 behaviour       */
572         /* See comment in "freeglut_developer" mailing list 10/4/04 */
573         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
574         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
575
576         if ( window->ActiveMenu )
577         {
578             fgUpdateMenuHighlight( window->ActiveMenu );
579             break;
580         }
581
582         fgState.Modifiers = fgPlatformGetModifiers( );
583
584         if( ( wParam & MK_LBUTTON ) ||
585             ( wParam & MK_MBUTTON ) ||
586             ( wParam & MK_RBUTTON ) )
587             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
588                                            window->State.MouseY ) );
589         else
590             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
591                                             window->State.MouseY ) );
592
593         fgState.Modifiers = INVALID_MODIFIERS;
594     }
595     break;
596
597     case WM_LBUTTONDOWN:
598     case WM_MBUTTONDOWN:
599     case WM_RBUTTONDOWN:
600     case WM_LBUTTONUP:
601     case WM_MBUTTONUP:
602     case WM_RBUTTONUP:
603     {
604         GLboolean pressed = GL_TRUE;
605         int button;
606
607 #if defined(_WIN32_WCE)
608         window->State.MouseX = 320-HIWORD( lParam );
609         window->State.MouseY = LOWORD( lParam );
610 #else
611         window->State.MouseX = LOWORD( lParam );
612         window->State.MouseY = HIWORD( lParam );
613 #endif /* defined(_WIN32_WCE) */
614
615         /* Restrict to [-32768, 32767] to match X11 behaviour       */
616         /* See comment in "freeglut_developer" mailing list 10/4/04 */
617         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
618         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
619
620         switch( uMsg )
621         {
622         case WM_LBUTTONDOWN:
623             pressed = GL_TRUE;
624             button = GLUT_LEFT_BUTTON;
625             break;
626         case WM_MBUTTONDOWN:
627             pressed = GL_TRUE;
628             button = GLUT_MIDDLE_BUTTON;
629             break;
630         case WM_RBUTTONDOWN:
631             pressed = GL_TRUE;
632             button = GLUT_RIGHT_BUTTON;
633             break;
634         case WM_LBUTTONUP:
635             pressed = GL_FALSE;
636             button = GLUT_LEFT_BUTTON;
637             break;
638         case WM_MBUTTONUP:
639             pressed = GL_FALSE;
640             button = GLUT_MIDDLE_BUTTON;
641             break;
642         case WM_RBUTTONUP:
643             pressed = GL_FALSE;
644             button = GLUT_RIGHT_BUTTON;
645             break;
646         default:
647             pressed = GL_FALSE;
648             button = -1;
649             break;
650         }
651
652 #if !defined(_WIN32_WCE)
653         if( GetSystemMetrics( SM_SWAPBUTTON ) )
654         {
655             if( button == GLUT_LEFT_BUTTON )
656                 button = GLUT_RIGHT_BUTTON;
657             else
658                 if( button == GLUT_RIGHT_BUTTON )
659                     button = GLUT_LEFT_BUTTON;
660         }
661 #endif /* !defined(_WIN32_WCE) */
662
663         if( button == -1 )
664             return DefWindowProc( hWnd, uMsg, lParam, wParam );
665
666         /*
667          * Do not execute the application's mouse callback if a menu
668          * is hooked to this button.  In that case an appropriate
669          * private call should be generated.
670          */
671         if( fgCheckActiveMenu( window, button, pressed,
672                                window->State.MouseX, window->State.MouseY ) )
673             break;
674
675         /* Set capture so that the window captures all the mouse messages */
676         /*
677          * XXX - Multiple button support:  Under X11, the mouse is not released
678          * XXX - from the window until all buttons have been released, even if the
679          * XXX - user presses a button in another window.  This will take more
680          * XXX - code changes than I am up to at the moment (10/5/04).  The present
681          * XXX - is a 90 percent solution.
682          */
683         if ( pressed == GL_TRUE )
684           SetCapture ( window->Window.Handle ) ;
685         else
686           ReleaseCapture () ;
687
688         if( ! FETCH_WCB( *window, Mouse ) )
689             break;
690
691         fgSetWindow( window );
692         fgState.Modifiers = fgPlatformGetModifiers( );
693
694         INVOKE_WCB(
695             *window, Mouse,
696             ( button,
697               pressed ? GLUT_DOWN : GLUT_UP,
698               window->State.MouseX,
699               window->State.MouseY
700             )
701         );
702
703         fgState.Modifiers = INVALID_MODIFIERS;
704     }
705     break;
706
707     case WM_MOUSEWHEEL:
708     {
709         int wheel_number = LOWORD( wParam );
710         short ticks = ( short )HIWORD( wParam );
711                 fgState.MouseWheelTicks += ticks;
712
713         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
714                 {
715                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
716
717             if( ! FETCH_WCB( *window, MouseWheel ) &&
718                 ! FETCH_WCB( *window, Mouse ) )
719                 break;
720
721             fgSetWindow( window );
722             fgState.Modifiers = fgPlatformGetModifiers( );
723
724             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
725                         {
726                 if( FETCH_WCB( *window, MouseWheel ) )
727                     INVOKE_WCB( *window, MouseWheel,
728                                 ( wheel_number,
729                                   direction,
730                                   window->State.MouseX,
731                                   window->State.MouseY
732                                 )
733                     );
734                 else  /* No mouse wheel, call the mouse button callback twice */
735                                 {
736                     /*
737                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
738                      *  "    "   one                     +1 to 5, -1 to 6, ...
739                      *
740                      * XXX The below assumes that you have no more than 3 mouse
741                      * XXX buttons.  Sorry.
742                      */
743                     int button = wheel_number * 2 + 3;
744                     if( direction < 0 )
745                         ++button;
746                     INVOKE_WCB( *window, Mouse,
747                                 ( button, GLUT_DOWN,
748                                   window->State.MouseX, window->State.MouseY )
749                     );
750                     INVOKE_WCB( *window, Mouse,
751                                 ( button, GLUT_UP,
752                                   window->State.MouseX, window->State.MouseY )
753                     );
754                                 }
755
756                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
757                         }
758
759             fgState.Modifiers = INVALID_MODIFIERS;
760                 }
761     }
762     break ;
763
764     case WM_SYSKEYDOWN:
765     case WM_KEYDOWN:
766     {
767         int keypress = -1;
768         POINT mouse_pos ;
769
770         if (child_window)
771             window = child_window;
772
773         if( ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
774             break;
775
776         /*
777          * Remember the current modifiers state. This is done here in order
778          * to make sure the VK_DELETE keyboard callback is executed properly.
779          */
780         fgState.Modifiers = fgPlatformGetModifiers( );
781
782         GetCursorPos( &mouse_pos );
783         ScreenToClient( window->Window.Handle, &mouse_pos );
784
785         window->State.MouseX = mouse_pos.x;
786         window->State.MouseY = mouse_pos.y;
787
788         /* Convert the Win32 keystroke codes to GLUTtish way */
789 #       define KEY(a,b) case a: keypress = b; break;
790
791         switch( wParam )
792         {
793             KEY( VK_F1,     GLUT_KEY_F1        );
794             KEY( VK_F2,     GLUT_KEY_F2        );
795             KEY( VK_F3,     GLUT_KEY_F3        );
796             KEY( VK_F4,     GLUT_KEY_F4        );
797             KEY( VK_F5,     GLUT_KEY_F5        );
798             KEY( VK_F6,     GLUT_KEY_F6        );
799             KEY( VK_F7,     GLUT_KEY_F7        );
800             KEY( VK_F8,     GLUT_KEY_F8        );
801             KEY( VK_F9,     GLUT_KEY_F9        );
802             KEY( VK_F10,    GLUT_KEY_F10       );
803             KEY( VK_F11,    GLUT_KEY_F11       );
804             KEY( VK_F12,    GLUT_KEY_F12       );
805             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
806             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
807             KEY( VK_HOME,   GLUT_KEY_HOME      );
808             KEY( VK_END,    GLUT_KEY_END       );
809             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
810             KEY( VK_UP,     GLUT_KEY_UP        );
811             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
812             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
813             KEY( VK_INSERT, GLUT_KEY_INSERT    );
814
815         case VK_LCONTROL:  case VK_RCONTROL:  case VK_CONTROL:
816         case VK_LSHIFT:    case VK_RSHIFT:    case VK_SHIFT:
817         case VK_LMENU:     case VK_RMENU:     case VK_MENU:
818             /* These keypresses and releases are handled earlier in the function */
819             break;
820
821         case VK_DELETE:
822             /* The delete key should be treated as an ASCII keypress: */
823             INVOKE_WCB( *window, Keyboard,
824                         ( 127, window->State.MouseX, window->State.MouseY )
825             );
826         }
827
828 #if defined(_WIN32_WCE)
829         if(!(lParam & 0x40000000)) /* Prevent auto-repeat */
830         {
831             if(wParam==(unsigned)gxKeyList.vkRight)
832                 keypress = GLUT_KEY_RIGHT;
833             else if(wParam==(unsigned)gxKeyList.vkLeft)
834                 keypress = GLUT_KEY_LEFT;
835             else if(wParam==(unsigned)gxKeyList.vkUp)
836                 keypress = GLUT_KEY_UP;
837             else if(wParam==(unsigned)gxKeyList.vkDown)
838                 keypress = GLUT_KEY_DOWN;
839             else if(wParam==(unsigned)gxKeyList.vkA)
840                 keypress = GLUT_KEY_F1;
841             else if(wParam==(unsigned)gxKeyList.vkB)
842                 keypress = GLUT_KEY_F2;
843             else if(wParam==(unsigned)gxKeyList.vkC)
844                 keypress = GLUT_KEY_F3;
845             else if(wParam==(unsigned)gxKeyList.vkStart)
846                 keypress = GLUT_KEY_F4;
847         }
848 #endif
849
850         if( keypress != -1 )
851             INVOKE_WCB( *window, Special,
852                         ( keypress,
853                           window->State.MouseX, window->State.MouseY )
854             );
855
856         fgState.Modifiers = INVALID_MODIFIERS;
857     }
858     break;
859
860     case WM_SYSKEYUP:
861     case WM_KEYUP:
862     {
863         int keypress = -1;
864         POINT mouse_pos;
865
866         if (child_window)
867             window = child_window;
868
869         /*
870          * Remember the current modifiers state. This is done here in order
871          * to make sure the VK_DELETE keyboard callback is executed properly.
872          */
873         fgState.Modifiers = fgPlatformGetModifiers( );
874
875         GetCursorPos( &mouse_pos );
876         ScreenToClient( window->Window.Handle, &mouse_pos );
877
878         window->State.MouseX = mouse_pos.x;
879         window->State.MouseY = mouse_pos.y;
880
881         /*
882          * Convert the Win32 keystroke codes to GLUTtish way.
883          * "KEY(a,b)" was defined under "WM_KEYDOWN"
884          */
885
886         switch( wParam )
887         {
888             KEY( VK_F1,     GLUT_KEY_F1        );
889             KEY( VK_F2,     GLUT_KEY_F2        );
890             KEY( VK_F3,     GLUT_KEY_F3        );
891             KEY( VK_F4,     GLUT_KEY_F4        );
892             KEY( VK_F5,     GLUT_KEY_F5        );
893             KEY( VK_F6,     GLUT_KEY_F6        );
894             KEY( VK_F7,     GLUT_KEY_F7        );
895             KEY( VK_F8,     GLUT_KEY_F8        );
896             KEY( VK_F9,     GLUT_KEY_F9        );
897             KEY( VK_F10,    GLUT_KEY_F10       );
898             KEY( VK_F11,    GLUT_KEY_F11       );
899             KEY( VK_F12,    GLUT_KEY_F12       );
900             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
901             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
902             KEY( VK_HOME,   GLUT_KEY_HOME      );
903             KEY( VK_END,    GLUT_KEY_END       );
904             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
905             KEY( VK_UP,     GLUT_KEY_UP        );
906             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
907             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
908             KEY( VK_INSERT, GLUT_KEY_INSERT    );
909
910           case VK_LCONTROL:  case VK_RCONTROL:  case VK_CONTROL:
911           case VK_LSHIFT:    case VK_RSHIFT:    case VK_SHIFT:
912           case VK_LMENU:     case VK_RMENU:     case VK_MENU:
913               /* These keypresses and releases are handled earlier in the function */
914               break;
915
916           case VK_DELETE:
917               /* The delete key should be treated as an ASCII keypress: */
918               INVOKE_WCB( *window, KeyboardUp,
919                           ( 127, window->State.MouseX, window->State.MouseY )
920               );
921               break;
922
923         default:
924         {
925 #if !defined(_WIN32_WCE)
926             BYTE state[ 256 ];
927             WORD code[ 2 ];
928
929             GetKeyboardState( state );
930
931             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
932                 wParam=code[ 0 ];
933
934             INVOKE_WCB( *window, KeyboardUp,
935                         ( (char)wParam,
936                           window->State.MouseX, window->State.MouseY )
937             );
938 #endif /* !defined(_WIN32_WCE) */
939         }
940         }
941
942         if( keypress != -1 )
943             INVOKE_WCB( *window, SpecialUp,
944                         ( keypress,
945                           window->State.MouseX, window->State.MouseY )
946             );
947
948         fgState.Modifiers = INVALID_MODIFIERS;
949     }
950     break;
951
952     case WM_SYSCHAR:
953     case WM_CHAR:
954     {
955       if (child_window)
956         window = child_window;
957
958       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
959             break;
960
961         fgState.Modifiers = fgPlatformGetModifiers( );
962         INVOKE_WCB( *window, Keyboard,
963                     ( (char)wParam,
964                       window->State.MouseX, window->State.MouseY )
965         );
966         fgState.Modifiers = INVALID_MODIFIERS;
967     }
968     break;
969
970     case WM_CAPTURECHANGED:
971         /* User has finished resizing the window, force a redraw */
972         INVOKE_WCB( *window, Display, ( ) );
973
974         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
975         break;
976
977         /* Other messages that I have seen and which are not handled already */
978     case WM_SETTEXT:  /* 0x000c */
979         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
980         /* Pass it on to "DefWindowProc" to set the window text */
981         break;
982
983     case WM_GETTEXT:  /* 0x000d */
984         /* Ideally we would copy the title of the window into "lParam" */
985         /* strncpy ( (char *)lParam, "Window Title", wParam );
986            lRet = ( wParam > 12 ) ? 12 : wParam;  */
987         /* the number of characters copied */
988         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
989         break;
990
991     case WM_GETTEXTLENGTH:  /* 0x000e */
992         /* Ideally we would get the length of the title of the window */
993         lRet = 12;
994         /* the number of characters in "Window Title\0" (see above) */
995         break;
996
997     case WM_ERASEBKGND:  /* 0x0014 */
998         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
999         break;
1000
1001 #if !defined(_WIN32_WCE)
1002     case WM_SYNCPAINT:  /* 0x0088 */
1003         /* Another window has moved, need to update this one */
1004         window->State.Redisplay = GL_TRUE;
1005         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1006         /* Help screen says this message must be passed to "DefWindowProc" */
1007         break;
1008
1009     case WM_NCPAINT:  /* 0x0085 */
1010       /* Need to update the border of this window */
1011         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1012         /* Pass it on to "DefWindowProc" to repaint a standard border */
1013         break;
1014
1015     case WM_SYSCOMMAND :  /* 0x0112 */
1016         {
1017           /*
1018            * We have received a system command message.  Try to act on it.
1019            * The commands are passed in through the "wParam" parameter:
1020            * The least significant digit seems to be which edge of the window
1021            * is being used for a resize event:
1022            *     4  3  5
1023            *     1     2
1024            *     7  6  8
1025            * Congratulations and thanks to Richard Rauch for figuring this out..
1026            */
1027             switch ( wParam & 0xfff0 )
1028             {
1029             case SC_SIZE       :
1030                 break ;
1031
1032             case SC_MOVE       :
1033                 break ;
1034
1035             case SC_MINIMIZE   :
1036                 /* User has clicked on the "-" to minimize the window */
1037                 /* Turn off the visibility */
1038                 window->State.Visible = GL_FALSE ;
1039
1040                 break ;
1041
1042             case SC_MAXIMIZE   :
1043                 break ;
1044
1045             case SC_NEXTWINDOW :
1046                 break ;
1047
1048             case SC_PREVWINDOW :
1049                 break ;
1050
1051             case SC_CLOSE      :
1052                 /* Followed very closely by a WM_CLOSE message */
1053                 break ;
1054
1055             case SC_VSCROLL    :
1056                 break ;
1057
1058             case SC_HSCROLL    :
1059                 break ;
1060
1061             case SC_MOUSEMENU  :
1062                 break ;
1063
1064             case SC_KEYMENU    :
1065                 break ;
1066
1067             case SC_ARRANGE    :
1068                 break ;
1069
1070             case SC_RESTORE    :
1071                 break ;
1072
1073             case SC_TASKLIST   :
1074                 break ;
1075
1076             case SC_SCREENSAVE :
1077                 break ;
1078
1079             case SC_HOTKEY     :
1080                 break ;
1081
1082 #if(WINVER >= 0x0400)
1083             case SC_DEFAULT    :
1084                 break ;
1085
1086             case SC_MONITORPOWER    :
1087                 break ;
1088
1089             case SC_CONTEXTHELP    :
1090                 break ;
1091 #endif /* WINVER >= 0x0400 */
1092
1093             default:
1094 #if _DEBUG
1095                 fgWarning( "Unknown wParam type 0x%x", wParam );
1096 #endif
1097                 break;
1098             }
1099         }
1100 #endif /* !defined(_WIN32_WCE) */
1101
1102         /* We need to pass the message on to the operating system as well */
1103         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1104         break;
1105
1106 #ifdef WM_TOUCH
1107         /* handle multi-touch messages */
1108         case WM_TOUCH:
1109         {
1110                 unsigned int numInputs = (unsigned int)wParam;
1111                 unsigned int i = 0;
1112                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1113
1114                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1115                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1116                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1117                 }
1118
1119                 if (!fghGetTouchInputInfo) { 
1120                         free( (void*)ti );
1121                         break;
1122                 }
1123
1124                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1125                         /* Handle each contact point */
1126                         for (i = 0; i < numInputs; ++i ) {
1127
1128                                 POINT tp;
1129                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1130                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1131                                 ScreenToClient( hWnd, &tp );
1132
1133                                 ti[i].dwID = ti[i].dwID * 2;
1134
1135                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1136                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1137                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1138                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1139                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1140                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1141                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1142                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1143                                 }
1144                         }
1145                 }
1146                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1147                 free( (void*)ti );
1148                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1149                 break;
1150         }
1151 #endif
1152     default:
1153         /* Handle unhandled messages */
1154         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1155         break;
1156     }
1157
1158     return lRet;
1159 }