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