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