can now also handle the case where menus is opened, client area of
[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 (ismenu: %i): %p\n", window->IsMenu, 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.CurrentMenu )
508                 menu = fgGetActiveMenu();
509             
510             if ( menu )
511             {
512                 SFG_Window* wnd = NULL;
513                 HWND hwnd = GetForegroundWindow();  /* Get window with current focus */
514                 if (hwnd)
515                     /* See if its one of our windows */
516                     wnd = fgWindowByHandle(hwnd);
517
518                 if (!hwnd || !wnd)
519                     /* User switched to another application*/
520                     fgDeactivateMenu(menu->ParentWindow);
521                 else if (
522                     ( wnd->IsMenu && wnd->ActiveMenu && wnd->ActiveMenu->ParentWindow!=menu->ParentWindow) ||    /* Make sure we don't kill the menu when trying to enter a submenu */
523                     (!wnd->IsMenu && wnd!=menu->ParentWindow)
524                     )
525                     /* User switched to another FreeGLUT window */
526                     fgDeactivateMenu(menu->ParentWindow);
527             }
528         }
529         break;
530
531 #if 0
532     case WM_ACTIVATE:
533         if (LOWORD(wParam) != WA_INACTIVE)
534         {
535 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
536                    window->State.Cursor ); */
537             fgSetCursor( window, window->State.Cursor );
538         }
539
540         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
541         break;
542 #endif
543
544     case WM_SETCURSOR:
545 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
546         if( LOWORD( lParam ) == HTCLIENT )
547             fgSetCursor ( window, window->State.Cursor ) ;
548         else
549             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
550         break;
551
552     case WM_SHOWWINDOW:
553         window->State.Visible = GL_TRUE;
554         window->State.Redisplay = GL_TRUE;
555         break;
556
557     case WM_PAINT:
558         /* Turn on the visibility in case it was turned off somehow */
559         window->State.Visible = GL_TRUE;
560         BeginPaint( hWnd, &ps );
561         fghRedrawWindow( window );
562         EndPaint( hWnd, &ps );
563         break;
564
565     case WM_CLOSE:
566         fgDestroyWindow ( window );
567         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
568             PostQuitMessage(0);
569         break;
570
571     case WM_DESTROY:
572         /*
573          * The window already got destroyed, so don't bother with it.
574          */
575         return 0;
576
577     case WM_MOUSEMOVE:
578     {
579 #if defined(_WIN32_WCE)
580         window->State.MouseX = 320-HIWORD( lParam );
581         window->State.MouseY = LOWORD( lParam );
582 #else
583         window->State.MouseX = LOWORD( lParam );
584         window->State.MouseY = HIWORD( lParam );
585 #endif /* defined(_WIN32_WCE) */
586         /* Restrict to [-32768, 32767] to match X11 behaviour       */
587         /* See comment in "freeglut_developer" mailing list 10/4/04 */
588         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
589         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
590
591         if ( window->ActiveMenu )
592         {
593             fgUpdateMenuHighlight( window->ActiveMenu );
594             break;
595         }
596
597         fgState.Modifiers = fgPlatformGetModifiers( );
598
599         if( ( wParam & MK_LBUTTON ) ||
600             ( wParam & MK_MBUTTON ) ||
601             ( wParam & MK_RBUTTON ) )
602             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
603                                            window->State.MouseY ) );
604         else
605             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
606                                             window->State.MouseY ) );
607
608         fgState.Modifiers = INVALID_MODIFIERS;
609     }
610     break;
611
612     case WM_LBUTTONDOWN:
613     case WM_MBUTTONDOWN:
614     case WM_RBUTTONDOWN:
615     case WM_LBUTTONUP:
616     case WM_MBUTTONUP:
617     case WM_RBUTTONUP:
618     {
619         GLboolean pressed = GL_TRUE;
620         int button;
621
622 #if defined(_WIN32_WCE)
623         window->State.MouseX = 320-HIWORD( lParam );
624         window->State.MouseY = LOWORD( lParam );
625 #else
626         window->State.MouseX = LOWORD( lParam );
627         window->State.MouseY = HIWORD( lParam );
628 #endif /* defined(_WIN32_WCE) */
629
630         /* Restrict to [-32768, 32767] to match X11 behaviour       */
631         /* See comment in "freeglut_developer" mailing list 10/4/04 */
632         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
633         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
634
635         switch( uMsg )
636         {
637         case WM_LBUTTONDOWN:
638             pressed = GL_TRUE;
639             button = GLUT_LEFT_BUTTON;
640             break;
641         case WM_MBUTTONDOWN:
642             pressed = GL_TRUE;
643             button = GLUT_MIDDLE_BUTTON;
644             break;
645         case WM_RBUTTONDOWN:
646             pressed = GL_TRUE;
647             button = GLUT_RIGHT_BUTTON;
648             break;
649         case WM_LBUTTONUP:
650             pressed = GL_FALSE;
651             button = GLUT_LEFT_BUTTON;
652             break;
653         case WM_MBUTTONUP:
654             pressed = GL_FALSE;
655             button = GLUT_MIDDLE_BUTTON;
656             break;
657         case WM_RBUTTONUP:
658             pressed = GL_FALSE;
659             button = GLUT_RIGHT_BUTTON;
660             break;
661         default:
662             pressed = GL_FALSE;
663             button = -1;
664             break;
665         }
666
667 #if !defined(_WIN32_WCE)
668         if( GetSystemMetrics( SM_SWAPBUTTON ) )
669         {
670             if( button == GLUT_LEFT_BUTTON )
671                 button = GLUT_RIGHT_BUTTON;
672             else
673                 if( button == GLUT_RIGHT_BUTTON )
674                     button = GLUT_LEFT_BUTTON;
675         }
676 #endif /* !defined(_WIN32_WCE) */
677
678         if( button == -1 )
679             return DefWindowProc( hWnd, uMsg, lParam, wParam );
680
681         /*
682          * Do not execute the application's mouse callback if a menu
683          * is hooked to this button.  In that case an appropriate
684          * private call should be generated.
685          */
686         if( fgCheckActiveMenu( window, button, pressed,
687                                window->State.MouseX, window->State.MouseY ) )
688             break;
689
690         /* Set capture so that the window captures all the mouse messages */
691         /*
692          * XXX - Multiple button support:  Under X11, the mouse is not released
693          * XXX - from the window until all buttons have been released, even if the
694          * XXX - user presses a button in another window.  This will take more
695          * XXX - code changes than I am up to at the moment (10/5/04).  The present
696          * XXX - is a 90 percent solution.
697          */
698         if ( pressed == GL_TRUE )
699           SetCapture ( window->Window.Handle ) ;
700         else
701           ReleaseCapture () ;
702
703         if( ! FETCH_WCB( *window, Mouse ) )
704             break;
705
706         fgSetWindow( window );
707         fgState.Modifiers = fgPlatformGetModifiers( );
708
709         INVOKE_WCB(
710             *window, Mouse,
711             ( button,
712               pressed ? GLUT_DOWN : GLUT_UP,
713               window->State.MouseX,
714               window->State.MouseY
715             )
716         );
717
718         fgState.Modifiers = INVALID_MODIFIERS;
719     }
720     break;
721
722     case WM_MOUSEWHEEL:
723     {
724         int wheel_number = LOWORD( wParam );
725         short ticks = ( short )HIWORD( wParam );
726                 fgState.MouseWheelTicks += ticks;
727
728         /*
729          * XXX Should use WHEEL_DELTA instead of 120
730          */
731                 if ( abs ( fgState.MouseWheelTicks ) >= 120 )
732                 {
733                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
734
735             if( ! FETCH_WCB( *window, MouseWheel ) &&
736                 ! FETCH_WCB( *window, Mouse ) )
737                 break;
738
739             fgSetWindow( window );
740             fgState.Modifiers = fgPlatformGetModifiers( );
741
742             /*
743              * XXX Should use WHEEL_DELTA instead of 120
744              */
745             while( abs ( fgState.MouseWheelTicks ) >= 120 )
746                         {
747                 if( FETCH_WCB( *window, MouseWheel ) )
748                     INVOKE_WCB( *window, MouseWheel,
749                                 ( wheel_number,
750                                   direction,
751                                   window->State.MouseX,
752                                   window->State.MouseY
753                                 )
754                     );
755                 else  /* No mouse wheel, call the mouse button callback twice */
756                                 {
757                     /*
758                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
759                      *  "    "   one                     +1 to 5, -1 to 6, ...
760                      *
761                      * XXX The below assumes that you have no more than 3 mouse
762                      * XXX buttons.  Sorry.
763                      */
764                     int button = wheel_number * 2 + 3;
765                     if( direction < 0 )
766                         ++button;
767                     INVOKE_WCB( *window, Mouse,
768                                 ( button, GLUT_DOWN,
769                                   window->State.MouseX, window->State.MouseY )
770                     );
771                     INVOKE_WCB( *window, Mouse,
772                                 ( button, GLUT_UP,
773                                   window->State.MouseX, window->State.MouseY )
774                     );
775                                 }
776
777                 /*
778                  * XXX Should use WHEEL_DELTA instead of 120
779                  */
780                                 fgState.MouseWheelTicks -= 120 * direction;
781                         }
782
783             fgState.Modifiers = INVALID_MODIFIERS;
784                 }
785     }
786     break ;
787
788     case WM_SYSKEYDOWN:
789     case WM_KEYDOWN:
790     {
791         int keypress = -1;
792         POINT mouse_pos ;
793
794
795         if( ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
796             break;
797
798         /*
799          * Remember the current modifiers state. This is done here in order
800          * to make sure the VK_DELETE keyboard callback is executed properly.
801          */
802         fgState.Modifiers = fgPlatformGetModifiers( );
803
804         GetCursorPos( &mouse_pos );
805         ScreenToClient( window->Window.Handle, &mouse_pos );
806
807         window->State.MouseX = mouse_pos.x;
808         window->State.MouseY = mouse_pos.y;
809
810         /* Convert the Win32 keystroke codes to GLUTtish way */
811 #       define KEY(a,b) case a: keypress = b; break;
812
813         switch( wParam )
814         {
815             KEY( VK_F1,     GLUT_KEY_F1        );
816             KEY( VK_F2,     GLUT_KEY_F2        );
817             KEY( VK_F3,     GLUT_KEY_F3        );
818             KEY( VK_F4,     GLUT_KEY_F4        );
819             KEY( VK_F5,     GLUT_KEY_F5        );
820             KEY( VK_F6,     GLUT_KEY_F6        );
821             KEY( VK_F7,     GLUT_KEY_F7        );
822             KEY( VK_F8,     GLUT_KEY_F8        );
823             KEY( VK_F9,     GLUT_KEY_F9        );
824             KEY( VK_F10,    GLUT_KEY_F10       );
825             KEY( VK_F11,    GLUT_KEY_F11       );
826             KEY( VK_F12,    GLUT_KEY_F12       );
827             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
828             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
829             KEY( VK_HOME,   GLUT_KEY_HOME      );
830             KEY( VK_END,    GLUT_KEY_END       );
831             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
832             KEY( VK_UP,     GLUT_KEY_UP        );
833             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
834             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
835             KEY( VK_INSERT, GLUT_KEY_INSERT    );
836
837         case VK_LCONTROL:  case VK_RCONTROL:  case VK_CONTROL:
838         case VK_LSHIFT:    case VK_RSHIFT:    case VK_SHIFT:
839         case VK_LMENU:     case VK_RMENU:     case VK_MENU:
840             /* These keypresses and releases are handled earlier in the function */
841             break;
842
843         case VK_DELETE:
844             /* The delete key should be treated as an ASCII keypress: */
845             INVOKE_WCB( *window, Keyboard,
846                         ( 127, window->State.MouseX, window->State.MouseY )
847             );
848         }
849
850 #if defined(_WIN32_WCE)
851         if(!(lParam & 0x40000000)) /* Prevent auto-repeat */
852         {
853             if(wParam==(unsigned)gxKeyList.vkRight)
854                 keypress = GLUT_KEY_RIGHT;
855             else if(wParam==(unsigned)gxKeyList.vkLeft)
856                 keypress = GLUT_KEY_LEFT;
857             else if(wParam==(unsigned)gxKeyList.vkUp)
858                 keypress = GLUT_KEY_UP;
859             else if(wParam==(unsigned)gxKeyList.vkDown)
860                 keypress = GLUT_KEY_DOWN;
861             else if(wParam==(unsigned)gxKeyList.vkA)
862                 keypress = GLUT_KEY_F1;
863             else if(wParam==(unsigned)gxKeyList.vkB)
864                 keypress = GLUT_KEY_F2;
865             else if(wParam==(unsigned)gxKeyList.vkC)
866                 keypress = GLUT_KEY_F3;
867             else if(wParam==(unsigned)gxKeyList.vkStart)
868                 keypress = GLUT_KEY_F4;
869         }
870 #endif
871
872         if( keypress != -1 )
873             INVOKE_WCB( *window, Special,
874                         ( keypress,
875                           window->State.MouseX, window->State.MouseY )
876             );
877
878         fgState.Modifiers = INVALID_MODIFIERS;
879     }
880     break;
881
882     case WM_SYSKEYUP:
883     case WM_KEYUP:
884     {
885         int keypress = -1;
886         POINT mouse_pos;
887
888         /*
889          * Remember the current modifiers state. This is done here in order
890          * to make sure the VK_DELETE keyboard callback is executed properly.
891          */
892         fgState.Modifiers = fgPlatformGetModifiers( );
893
894         GetCursorPos( &mouse_pos );
895         ScreenToClient( window->Window.Handle, &mouse_pos );
896
897         window->State.MouseX = mouse_pos.x;
898         window->State.MouseY = mouse_pos.y;
899
900         /*
901          * Convert the Win32 keystroke codes to GLUTtish way.
902          * "KEY(a,b)" was defined under "WM_KEYDOWN"
903          */
904
905         switch( wParam )
906         {
907             KEY( VK_F1,     GLUT_KEY_F1        );
908             KEY( VK_F2,     GLUT_KEY_F2        );
909             KEY( VK_F3,     GLUT_KEY_F3        );
910             KEY( VK_F4,     GLUT_KEY_F4        );
911             KEY( VK_F5,     GLUT_KEY_F5        );
912             KEY( VK_F6,     GLUT_KEY_F6        );
913             KEY( VK_F7,     GLUT_KEY_F7        );
914             KEY( VK_F8,     GLUT_KEY_F8        );
915             KEY( VK_F9,     GLUT_KEY_F9        );
916             KEY( VK_F10,    GLUT_KEY_F10       );
917             KEY( VK_F11,    GLUT_KEY_F11       );
918             KEY( VK_F12,    GLUT_KEY_F12       );
919             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
920             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
921             KEY( VK_HOME,   GLUT_KEY_HOME      );
922             KEY( VK_END,    GLUT_KEY_END       );
923             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
924             KEY( VK_UP,     GLUT_KEY_UP        );
925             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
926             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
927             KEY( VK_INSERT, GLUT_KEY_INSERT    );
928
929           case VK_LCONTROL:  case VK_RCONTROL:  case VK_CONTROL:
930           case VK_LSHIFT:    case VK_RSHIFT:    case VK_SHIFT:
931           case VK_LMENU:     case VK_RMENU:     case VK_MENU:
932               /* These keypresses and releases are handled earlier in the function */
933               break;
934
935           case VK_DELETE:
936               /* The delete key should be treated as an ASCII keypress: */
937               INVOKE_WCB( *window, KeyboardUp,
938                           ( 127, window->State.MouseX, window->State.MouseY )
939               );
940               break;
941
942         default:
943         {
944 #if !defined(_WIN32_WCE)
945             BYTE state[ 256 ];
946             WORD code[ 2 ];
947
948             GetKeyboardState( state );
949
950             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
951                 wParam=code[ 0 ];
952
953             INVOKE_WCB( *window, KeyboardUp,
954                         ( (char)wParam,
955                           window->State.MouseX, window->State.MouseY )
956             );
957 #endif /* !defined(_WIN32_WCE) */
958         }
959         }
960
961         if( keypress != -1 )
962             INVOKE_WCB( *window, SpecialUp,
963                         ( keypress,
964                           window->State.MouseX, window->State.MouseY )
965             );
966
967         fgState.Modifiers = INVALID_MODIFIERS;
968     }
969     break;
970
971     case WM_SYSCHAR:
972     case WM_CHAR:
973     {
974       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
975             break;
976
977         fgState.Modifiers = fgPlatformGetModifiers( );
978         INVOKE_WCB( *window, Keyboard,
979                     ( (char)wParam,
980                       window->State.MouseX, window->State.MouseY )
981         );
982         fgState.Modifiers = INVALID_MODIFIERS;
983     }
984     break;
985
986     case WM_CAPTURECHANGED:
987         /* User has finished resizing the window, force a redraw */
988         INVOKE_WCB( *window, Display, ( ) );
989
990         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
991         break;
992
993         /* Other messages that I have seen and which are not handled already */
994     case WM_SETTEXT:  /* 0x000c */
995         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
996         /* Pass it on to "DefWindowProc" to set the window text */
997         break;
998
999     case WM_GETTEXT:  /* 0x000d */
1000         /* Ideally we would copy the title of the window into "lParam" */
1001         /* strncpy ( (char *)lParam, "Window Title", wParam );
1002            lRet = ( wParam > 12 ) ? 12 : wParam;  */
1003         /* the number of characters copied */
1004         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1005         break;
1006
1007     case WM_GETTEXTLENGTH:  /* 0x000e */
1008         /* Ideally we would get the length of the title of the window */
1009         lRet = 12;
1010         /* the number of characters in "Window Title\0" (see above) */
1011         break;
1012
1013     case WM_ERASEBKGND:  /* 0x0014 */
1014         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1015         break;
1016
1017 #if !defined(_WIN32_WCE)
1018     case WM_SYNCPAINT:  /* 0x0088 */
1019         /* Another window has moved, need to update this one */
1020         window->State.Redisplay = GL_TRUE;
1021         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1022         /* Help screen says this message must be passed to "DefWindowProc" */
1023         break;
1024
1025     case WM_NCPAINT:  /* 0x0085 */
1026       /* Need to update the border of this window */
1027         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1028         /* Pass it on to "DefWindowProc" to repaint a standard border */
1029         break;
1030
1031     case WM_SYSCOMMAND :  /* 0x0112 */
1032         {
1033           /*
1034            * We have received a system command message.  Try to act on it.
1035            * The commands are passed in through the "wParam" parameter:
1036            * The least significant digit seems to be which edge of the window
1037            * is being used for a resize event:
1038            *     4  3  5
1039            *     1     2
1040            *     7  6  8
1041            * Congratulations and thanks to Richard Rauch for figuring this out..
1042            */
1043             switch ( wParam & 0xfff0 )
1044             {
1045             case SC_SIZE       :
1046                 break ;
1047
1048             case SC_MOVE       :
1049                 break ;
1050
1051             case SC_MINIMIZE   :
1052                 /* User has clicked on the "-" to minimize the window */
1053                 /* Turn off the visibility */
1054                 window->State.Visible = GL_FALSE ;
1055
1056                 break ;
1057
1058             case SC_MAXIMIZE   :
1059                 break ;
1060
1061             case SC_NEXTWINDOW :
1062                 break ;
1063
1064             case SC_PREVWINDOW :
1065                 break ;
1066
1067             case SC_CLOSE      :
1068                 /* Followed very closely by a WM_CLOSE message */
1069                 break ;
1070
1071             case SC_VSCROLL    :
1072                 break ;
1073
1074             case SC_HSCROLL    :
1075                 break ;
1076
1077             case SC_MOUSEMENU  :
1078                 break ;
1079
1080             case SC_KEYMENU    :
1081                 break ;
1082
1083             case SC_ARRANGE    :
1084                 break ;
1085
1086             case SC_RESTORE    :
1087                 break ;
1088
1089             case SC_TASKLIST   :
1090                 break ;
1091
1092             case SC_SCREENSAVE :
1093                 break ;
1094
1095             case SC_HOTKEY     :
1096                 break ;
1097
1098 #if(WINVER >= 0x0400)
1099             case SC_DEFAULT    :
1100                 break ;
1101
1102             case SC_MONITORPOWER    :
1103                 break ;
1104
1105             case SC_CONTEXTHELP    :
1106                 break ;
1107 #endif /* WINVER >= 0x0400 */
1108
1109             default:
1110 #if _DEBUG
1111                 fgWarning( "Unknown wParam type 0x%x", wParam );
1112 #endif
1113                 break;
1114             }
1115         }
1116 #endif /* !defined(_WIN32_WCE) */
1117
1118         /* We need to pass the message on to the operating system as well */
1119         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1120         break;
1121
1122 #ifdef WM_TOUCH
1123         /* handle multi-touch messages */
1124         case WM_TOUCH:
1125         {
1126                 unsigned int numInputs = (unsigned int)wParam;
1127                 unsigned int i = 0;
1128                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1129
1130                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1131                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1132                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1133                 }
1134
1135                 if (!fghGetTouchInputInfo) { 
1136                         free( (void*)ti );
1137                         break;
1138                 }
1139
1140                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1141                         /* Handle each contact point */
1142                         for (i = 0; i < numInputs; ++i ) {
1143
1144                                 POINT tp;
1145                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1146                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1147                                 ScreenToClient( hWnd, &tp );
1148
1149                                 ti[i].dwID = ti[i].dwID * 2;
1150
1151                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1152                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1153                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1154                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1155                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1156                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1157                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1158                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1159                                 }
1160                         }
1161                 }
1162                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1163                 free( (void*)ti );
1164                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1165                 break;
1166         }
1167 #endif
1168     default:
1169         /* Handle unhandled messages */
1170         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1171         break;
1172     }
1173
1174     return lRet;
1175 }