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