WM_SETFOCUS should not change focus to child instead, then all input goes to child...
[freeglut] / src / mswin / fg_main_mswin.c
1 /*
2  * freeglut_main_mswin.c
3  *
4  * The Windows-specific mouse cursor related stuff.
5  *
6  * Copyright (c) 2012 Stephen J. Baker. All Rights Reserved.
7  * Written by John F. Fay, <fayjf@sourceforge.net>
8  * Creation date: Sat Jan 21, 2012
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #include <GL/freeglut.h>
29 #include "../fg_internal.h"
30
31
32 extern void fghRedrawWindow ( SFG_Window *window );
33
34 extern void fgNewWGLCreateContext( SFG_Window* window );
35 extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly,
36                                      unsigned char layer_type );
37
38 extern void fgPlatformCheckMenuDeactivate();
39
40 #ifdef WM_TOUCH
41 typedef BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT,UINT,PTOUCHINPUT,int);
42 typedef BOOL (WINAPI *pCloseTouchInputHandle)(HTOUCHINPUT);
43 static pGetTouchInputInfo fghGetTouchInputInfo = (pGetTouchInputInfo)0xDEADBEEF;
44 static pCloseTouchInputHandle fghCloseTouchInputHandle = (pCloseTouchInputHandle)0xDEADBEEF;
45 #endif
46
47 #ifdef _WIN32_WCE
48 typedef struct GXDisplayProperties GXDisplayProperties;
49 typedef struct GXKeyList GXKeyList;
50 #include <gx.h>
51
52 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
53 typedef int (*GXOPENINPUT)();
54
55 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
56 GXOPENINPUT GXOpenInput_ = NULL;
57
58 struct GXKeyList gxKeyList;
59 #endif /* _WIN32_WCE */
60
61
62 /* Get system time, taking special precautions against 32bit timer wrap.
63    We use timeGetTime and not GetTickCount because of its better stability,
64    and because we can increase its granularity (to 1 ms in
65    fgPlatformInitialize). For that reason we can't use GetTickCount64 which
66    wouldn't have the wrap issue.
67    Credit: this is based on code in glibc (https://mail.gnome.org/archives/commits-list/2011-November/msg04588.html)
68    */
69 static fg_time_t lastTime32 = 0;
70 static fg_time_t timeEpoch = 0;
71 void fgPlatformInitSystemTime()
72 {
73 #if defined(_WIN32_WCE)
74     lastTime32 = GetTickCount();
75 #else
76     lastTime32 = timeGetTime();
77 #endif
78 }
79 fg_time_t fgPlatformSystemTime ( void )
80 {
81     fg_time_t currTime32;
82 #if defined(_WIN32_WCE)
83     currTime32 = GetTickCount();
84 #else
85     currTime32 = timeGetTime();
86 #endif
87     /* Check if we just wrapped */
88     if (currTime32 < lastTime32)
89         timeEpoch++;
90     
91     lastTime32 = currTime32;
92
93     return currTime32 | timeEpoch << 32;
94 }
95
96
97 void fgPlatformSleepForEvents( fg_time_t msec )
98 {
99     MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
100 }
101
102
103 void fgPlatformProcessSingleEvent ( void )
104 {
105     MSG stMsg;
106
107     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
108
109     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
110     {
111         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
112         {
113             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
114             {
115                 fgDeinitialize( );
116                 exit( 0 );
117             }
118             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
119                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
120
121             return;
122         }
123
124         TranslateMessage( &stMsg );
125         DispatchMessage( &stMsg );
126     }
127 }
128
129
130
131 void fgPlatformMainLoopPreliminaryWork ( void )
132 {
133     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
134
135     /*
136      * Processing before the main loop:  If there is a window which is open and
137      * which has a visibility callback, call it.  I know this is an ugly hack,
138      * but I'm not sure what else to do about it.  Ideally we should leave
139      * something uninitialized in the create window code and initialize it in
140      * the main loop, and have that initialization create a "WM_ACTIVATE"
141      * message.  Then we would put the visibility callback code in the
142      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
143      */
144     while( window )
145     {
146         if ( FETCH_WCB( *window, Visibility ) )
147         {
148             SFG_Window *current_window = fgStructure.CurrentWindow ;
149
150             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
151             fgSetWindow( current_window );
152         }
153
154         window = (SFG_Window *)window->Node.Next ;
155     }
156 }
157
158
159 /*
160  * Determine a GLUT modifier mask based on MS-WINDOWS system info.
161  */
162 static int fgPlatformGetModifiers (void)
163 {
164     return
165         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
166             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
167         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
168             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
169         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
170             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
171 }
172
173 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
174 {
175     static unsigned char lControl = 0, lShift = 0, lAlt = 0,
176                          rControl = 0, rShift = 0, rAlt = 0;
177
178     int keypress = -1;
179     
180     /* if keydown, check for repeat */
181     /* If repeat is globally switched off, it cannot be switched back on per window.
182      * But if it is globally switched on, it can be switched off per window. This matches
183      * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
184      * global state switch.
185      */
186     if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
187         return 1;
188     
189     /* Remember the current modifiers state so user can query it from their callback */
190     fgState.Modifiers = fgPlatformGetModifiers( );
191
192     /* Convert the Win32 keystroke codes to GLUTtish way */
193 #   define KEY(a,b) case a: keypress = b; break;
194
195     switch( wParam )
196     {
197         KEY( VK_F1,     GLUT_KEY_F1        );
198         KEY( VK_F2,     GLUT_KEY_F2        );
199         KEY( VK_F3,     GLUT_KEY_F3        );
200         KEY( VK_F4,     GLUT_KEY_F4        );
201         KEY( VK_F5,     GLUT_KEY_F5        );
202         KEY( VK_F6,     GLUT_KEY_F6        );
203         KEY( VK_F7,     GLUT_KEY_F7        );
204         KEY( VK_F8,     GLUT_KEY_F8        );
205         KEY( VK_F9,     GLUT_KEY_F9        );
206         KEY( VK_F10,    GLUT_KEY_F10       );
207         KEY( VK_F11,    GLUT_KEY_F11       );
208         KEY( VK_F12,    GLUT_KEY_F12       );
209         KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
210         KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
211         KEY( VK_HOME,   GLUT_KEY_HOME      );
212         KEY( VK_END,    GLUT_KEY_END       );
213         KEY( VK_LEFT,   GLUT_KEY_LEFT      );
214         KEY( VK_UP,     GLUT_KEY_UP        );
215         KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
216         KEY( VK_DOWN,   GLUT_KEY_DOWN      );
217         KEY( VK_INSERT, GLUT_KEY_INSERT    );
218
219     /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
220      * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
221      * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
222      */
223 #define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\
224     if (!keyStateVar && GetAsyncKeyState ( winKey ))\
225     {\
226         keypress   = glutKey;\
227         keyStateVar = 1;\
228     }\
229     else if (keyStateVar && !GetAsyncKeyState ( winKey ))\
230     {\
231         keypress   = glutKey;\
232         keyStateVar = 0;\
233     }
234     case VK_CONTROL:
235         ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
236         ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
237         break;
238     case VK_SHIFT:
239         ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
240         ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
241         break;
242     case VK_MENU:
243         ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
244         ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
245         break;
246 #undef ASYNC_KEY_EVENT
247
248     case VK_DELETE:
249         /* The delete key should be treated as an ASCII keypress: */
250         if (keydown)
251             INVOKE_WCB( *window, Keyboard,
252                         ( 127, window->State.MouseX, window->State.MouseY )
253             );
254         else
255             INVOKE_WCB( *window, KeyboardUp,
256                         ( 127, window->State.MouseX, window->State.MouseY )
257             );
258         break;
259
260 #if !defined(_WIN32_WCE)
261     default:
262         /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
263         if (!keydown)
264         {
265             BYTE state[ 256 ];
266             WORD code[ 2 ];
267
268             GetKeyboardState( state );
269
270             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
271                 wParam=code[ 0 ];
272
273             INVOKE_WCB( *window, KeyboardUp,
274                    ( (char)wParam,
275                         window->State.MouseX, window->State.MouseY )
276             );
277         }
278 #endif
279     }
280
281 #if defined(_WIN32_WCE)
282     if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
283     {
284         if(wParam==(unsigned)gxKeyList.vkRight)
285             keypress = GLUT_KEY_RIGHT;
286         else if(wParam==(unsigned)gxKeyList.vkLeft)
287             keypress = GLUT_KEY_LEFT;
288         else if(wParam==(unsigned)gxKeyList.vkUp)
289             keypress = GLUT_KEY_UP;
290         else if(wParam==(unsigned)gxKeyList.vkDown)
291             keypress = GLUT_KEY_DOWN;
292         else if(wParam==(unsigned)gxKeyList.vkA)
293             keypress = GLUT_KEY_F1;
294         else if(wParam==(unsigned)gxKeyList.vkB)
295             keypress = GLUT_KEY_F2;
296         else if(wParam==(unsigned)gxKeyList.vkC)
297             keypress = GLUT_KEY_F3;
298         else if(wParam==(unsigned)gxKeyList.vkStart)
299             keypress = GLUT_KEY_F4;
300     }
301 #endif
302     
303     if( keypress != -1 )
304         if (keydown)
305             INVOKE_WCB( *window, Special,
306                         ( keypress,
307                             window->State.MouseX, window->State.MouseY )
308             );
309         else
310             INVOKE_WCB( *window, SpecialUp,
311                         ( keypress,
312                             window->State.MouseX, window->State.MouseY )
313             );
314
315     fgState.Modifiers = INVALID_MODIFIERS;
316
317     /* SYSKEY events should be sent to default window proc for system to handle them */
318     if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
319         return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
320     else
321         return 1;
322 }
323
324 void fghWindowUnderCursor(SFG_Window *window, SFG_Window **child_window)
325 {
326     /* Check if the current window that the mouse is over is a child window
327      * of the window the message was sent to.
328      */
329     if (window && window->Children.First)
330     {
331         POINT mouse_pos;
332         SFG_WindowHandleType hwnd;
333         SFG_Window* temp_window;
334
335         /* Get mouse position at time of message */
336         DWORD mouse_pos_Dword = GetMessagePos();
337         mouse_pos.x = GET_X_LPARAM(mouse_pos_Dword);
338         mouse_pos.y = GET_Y_LPARAM(mouse_pos_Dword);
339         ScreenToClient( window->Window.Handle, &mouse_pos );
340         
341         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
342         if (hwnd && hwnd!=window->Window.Handle)   /* can be NULL if mouse outside parent by the time we get here, or can be same as parent if we didn't find a child */
343         {
344             temp_window = fgWindowByHandle(hwnd);
345             if (temp_window)    /* Verify we got a FreeGLUT window */
346             {
347                 *child_window = temp_window;
348                 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
349                 fghWindowUnderCursor(temp_window,child_window);
350             }
351         }
352     }
353 }
354
355 /*
356  * The window procedure for handling Win32 events
357  */
358 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
359 {
360     SFG_Window *window, *child_window = NULL;
361     PAINTSTRUCT ps;
362     LRESULT lRet = 1;
363
364     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
365
366     window = fgWindowByHandle( hWnd );
367
368     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
369       return DefWindowProc( hWnd, uMsg, wParam, lParam );
370
371     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
372              uMsg, wParam, lParam ); */
373
374     /* Some events only sent to main window. Check if the current window that
375      * the mouse is over is a child window. Below when handling some messages,
376      * we make sure that we process callbacks on the child window instead.
377      * This mirrors how GLUT does things.
378      */
379     fghWindowUnderCursor(window, &child_window);
380     
381     switch( uMsg )
382     {
383     case WM_CREATE:
384         /* The window structure is passed as the creation structure parameter... */
385         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
386         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
387                                        "fgPlatformWindowProc" );
388
389         window->Window.Handle = hWnd;
390         window->Window.pContext.Device = GetDC( hWnd );
391         if( window->IsMenu )
392         {
393             unsigned int current_DisplayMode = fgState.DisplayMode;
394             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
395 #if !defined(_WIN32_WCE)
396             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
397 #endif
398             fgState.DisplayMode = current_DisplayMode;
399
400             if( fgStructure.MenuContext )
401                 wglMakeCurrent( window->Window.pContext.Device,
402                                 fgStructure.MenuContext->MContext
403                 );
404             else
405             {
406                 fgStructure.MenuContext =
407                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
408                 fgStructure.MenuContext->MContext =
409                     wglCreateContext( window->Window.pContext.Device );
410             }
411
412             /* window->Window.Context = wglGetCurrentContext ();   */
413             window->Window.Context = wglCreateContext( window->Window.pContext.Device );
414         }
415         else
416         {
417 #if !defined(_WIN32_WCE)
418             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
419 #endif
420
421             if( ! fgState.UseCurrentContext )
422                 window->Window.Context =
423                     wglCreateContext( window->Window.pContext.Device );
424             else
425             {
426                 window->Window.Context = wglGetCurrentContext( );
427                 if( ! window->Window.Context )
428                     window->Window.Context =
429                         wglCreateContext( window->Window.pContext.Device );
430             }
431
432 #if !defined(_WIN32_WCE)
433             fgNewWGLCreateContext( window );
434 #endif
435         }
436
437         window->State.NeedToResize = GL_TRUE;
438         /* if we used CW_USEDEFAULT (thats a negative value) for the size
439          * of the window, query the window now for the size at which it
440          * was created.
441          */
442         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
443         {
444             SFG_Window *current_window = fgStructure.CurrentWindow;
445
446             fgSetWindow( window );
447             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
448             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
449             fgSetWindow( current_window );
450         }
451
452         ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
453
454 #if defined(_WIN32_WCE)
455         /* Take over button handling */
456         {
457             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
458             if (dxDllLib)
459             {
460                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
461                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
462             }
463
464             if(GXOpenInput_)
465                 (*GXOpenInput_)();
466             if(GXGetDefaultKeys_)
467                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
468         }
469
470 #endif /* defined(_WIN32_WCE) */
471         break;
472
473     case WM_SIZE:
474         /*
475          * If the window is visible, then it is the user manually resizing it.
476          * If it is not, then it is the system sending us a dummy resize with
477          * zero dimensions on a "glutIconifyWindow" call.
478          */
479         if( window->State.Visible )
480         {
481             /* get old values first to compare to below */
482             int width = window->State.Width, height=window->State.Height;
483 #if defined(_WIN32_WCE)
484             window->State.Width  = HIWORD(lParam);
485             window->State.Height = LOWORD(lParam);
486 #else
487             window->State.Width  = LOWORD(lParam);
488             window->State.Height = HIWORD(lParam);
489 #endif /* defined(_WIN32_WCE) */
490             
491             if (width!=window->State.Width || height!=window->State.Height)
492                 /* Something changed, need to resize */
493                 window->State.NeedToResize = GL_TRUE;
494         }
495
496         break;
497
498     case WM_MOVE:
499         {
500             SFG_Window* saved_window = fgStructure.CurrentWindow;
501             RECT windowRect;
502             GetWindowRect( window->Window.Handle, &windowRect );
503             
504             if (window->Parent)
505             {
506                 /* For child window, we should return relative to upper-left
507                 * of parent's client area.
508                 */
509                 POINT topleft = {windowRect.left,windowRect.top};
510
511                 ScreenToClient(window->Parent->Window.Handle,&topleft);
512                 windowRect.left = topleft.x;
513                 windowRect.top  = topleft.y;
514             }
515
516             INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) );
517             fgSetWindow(saved_window);
518         }
519         break;
520
521     case WM_SETFOCUS:
522 /*        printf("WM_SETFOCUS: %p\n", window ); */
523
524         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
525
526         SetActiveWindow( window->Window.Handle );
527         INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
528         UpdateWindow ( hWnd );
529
530         break;
531
532     case WM_KILLFOCUS:
533         {
534             SFG_Window* saved_window = fgStructure.CurrentWindow;
535 /*            printf("WM_KILLFOCUS: %p\n", window ); */
536             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
537             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
538             fgSetWindow(saved_window);
539
540             /* Check if there are any open menus that need to be closed */
541             fgPlatformCheckMenuDeactivate();
542         }
543         break;
544
545 #if 0
546     case WM_ACTIVATE:
547         //printf("WM_ACTIVATE: %x %d %d\n",lParam, HIWORD(wParam), LOWORD(wParam));
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         InvalidateRect( hWnd, NULL, GL_FALSE ); /* Make sure whole window is repainted. Bit of a hack, but a safe one from what google turns up... */
576         BeginPaint( hWnd, &ps );
577         fghRedrawWindow( window );
578         EndPaint( hWnd, &ps );
579         break;
580
581     case WM_CLOSE:
582         fgDestroyWindow ( window );
583         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
584             PostQuitMessage(0);
585         break;
586
587     case WM_DESTROY:
588         /*
589          * The window already got destroyed, so don't bother with it.
590          */
591         return 0;
592
593     case WM_MOUSEMOVE:
594     {
595 #if defined(_WIN32_WCE)
596         window->State.MouseX = 320-HIWORD( lParam );
597         window->State.MouseY = LOWORD( lParam );
598 #else
599         window->State.MouseX = LOWORD( lParam );
600         window->State.MouseY = HIWORD( lParam );
601 #endif /* defined(_WIN32_WCE) */
602         /* Restrict to [-32768, 32767] to match X11 behaviour       */
603         /* See comment in "freeglut_developer" mailing list 10/4/04 */
604         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
605         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
606
607         if ( window->ActiveMenu )
608         {
609             fgUpdateMenuHighlight( window->ActiveMenu );
610             break;
611         }
612
613         fgState.Modifiers = fgPlatformGetModifiers( );
614
615         if( ( wParam & MK_LBUTTON ) ||
616             ( wParam & MK_MBUTTON ) ||
617             ( wParam & MK_RBUTTON ) )
618             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
619                                            window->State.MouseY ) );
620         else
621             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
622                                             window->State.MouseY ) );
623
624         fgState.Modifiers = INVALID_MODIFIERS;
625     }
626     break;
627
628     case WM_LBUTTONDOWN:
629     case WM_MBUTTONDOWN:
630     case WM_RBUTTONDOWN:
631     case WM_LBUTTONUP:
632     case WM_MBUTTONUP:
633     case WM_RBUTTONUP:
634     {
635         GLboolean pressed = GL_TRUE;
636         int button;
637
638 #if defined(_WIN32_WCE)
639         window->State.MouseX = 320-HIWORD( lParam );
640         window->State.MouseY = LOWORD( lParam );
641 #else
642         window->State.MouseX = LOWORD( lParam );
643         window->State.MouseY = HIWORD( lParam );
644 #endif /* defined(_WIN32_WCE) */
645
646         /* Restrict to [-32768, 32767] to match X11 behaviour       */
647         /* See comment in "freeglut_developer" mailing list 10/4/04 */
648         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
649         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
650
651         switch( uMsg )
652         {
653         case WM_LBUTTONDOWN:
654             pressed = GL_TRUE;
655             button = GLUT_LEFT_BUTTON;
656             break;
657         case WM_MBUTTONDOWN:
658             pressed = GL_TRUE;
659             button = GLUT_MIDDLE_BUTTON;
660             break;
661         case WM_RBUTTONDOWN:
662             pressed = GL_TRUE;
663             button = GLUT_RIGHT_BUTTON;
664             break;
665         case WM_LBUTTONUP:
666             pressed = GL_FALSE;
667             button = GLUT_LEFT_BUTTON;
668             break;
669         case WM_MBUTTONUP:
670             pressed = GL_FALSE;
671             button = GLUT_MIDDLE_BUTTON;
672             break;
673         case WM_RBUTTONUP:
674             pressed = GL_FALSE;
675             button = GLUT_RIGHT_BUTTON;
676             break;
677         default:
678             pressed = GL_FALSE;
679             button = -1;
680             break;
681         }
682
683 #if !defined(_WIN32_WCE)
684         if( GetSystemMetrics( SM_SWAPBUTTON ) )
685         {
686             if( button == GLUT_LEFT_BUTTON )
687                 button = GLUT_RIGHT_BUTTON;
688             else
689                 if( button == GLUT_RIGHT_BUTTON )
690                     button = GLUT_LEFT_BUTTON;
691         }
692 #endif /* !defined(_WIN32_WCE) */
693
694         if( button == -1 )
695             return DefWindowProc( hWnd, uMsg, lParam, wParam );
696
697         /*
698          * Do not execute the application's mouse callback if a menu
699          * is hooked to this button.  In that case an appropriate
700          * private call should be generated.
701          */
702         if( fgCheckActiveMenu( window, button, pressed,
703                                window->State.MouseX, window->State.MouseY ) )
704             break;
705
706         /* Set capture so that the window captures all the mouse messages */
707         /*
708          * XXX - Multiple button support:  Under X11, the mouse is not released
709          * XXX - from the window until all buttons have been released, even if the
710          * XXX - user presses a button in another window.  This will take more
711          * XXX - code changes than I am up to at the moment (10/5/04).  The present
712          * XXX - is a 90 percent solution.
713          */
714         if ( pressed == GL_TRUE )
715           SetCapture ( window->Window.Handle ) ;
716         else
717           ReleaseCapture () ;
718
719         if( ! FETCH_WCB( *window, Mouse ) )
720             break;
721
722         fgSetWindow( window );
723         fgState.Modifiers = fgPlatformGetModifiers( );
724
725         INVOKE_WCB(
726             *window, Mouse,
727             ( button,
728               pressed ? GLUT_DOWN : GLUT_UP,
729               window->State.MouseX,
730               window->State.MouseY
731             )
732         );
733
734         fgState.Modifiers = INVALID_MODIFIERS;
735     }
736     break;
737
738     case WM_MOUSEWHEEL:
739     {
740         int wheel_number = LOWORD( wParam );
741         short ticks = ( short )HIWORD( wParam );
742
743         if (child_window)
744             window = child_window;
745
746                 fgState.MouseWheelTicks += ticks;
747         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
748                 {
749                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
750
751             if( ! FETCH_WCB( *window, MouseWheel ) &&
752                 ! FETCH_WCB( *window, Mouse ) )
753                 break;
754
755             fgSetWindow( window );
756             fgState.Modifiers = fgPlatformGetModifiers( );
757
758             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
759                         {
760                 if( FETCH_WCB( *window, MouseWheel ) )
761                     INVOKE_WCB( *window, MouseWheel,
762                                 ( wheel_number,
763                                   direction,
764                                   window->State.MouseX,
765                                   window->State.MouseY
766                                 )
767                     );
768                 else  /* No mouse wheel, call the mouse button callback twice */
769                                 {
770                     /*
771                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
772                      *  "    "   one                     +1 to 5, -1 to 6, ...
773                      *
774                      * XXX The below assumes that you have no more than 3 mouse
775                      * XXX buttons.  Sorry.
776                      */
777                     int button = wheel_number * 2 + 3;
778                     if( direction < 0 )
779                         ++button;
780                     INVOKE_WCB( *window, Mouse,
781                                 ( button, GLUT_DOWN,
782                                   window->State.MouseX, window->State.MouseY )
783                     );
784                     INVOKE_WCB( *window, Mouse,
785                                 ( button, GLUT_UP,
786                                   window->State.MouseX, window->State.MouseY )
787                     );
788                                 }
789
790                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
791                         }
792
793             fgState.Modifiers = INVALID_MODIFIERS;
794                 }
795     }
796     break ;
797
798     case WM_SYSKEYDOWN:
799     case WM_KEYDOWN:
800         lRet = fghWindowProcKeyPress(child_window?child_window:window,uMsg,GL_TRUE,wParam,lParam);
801     break;
802
803     case WM_SYSKEYUP:
804     case WM_KEYUP:
805         lRet = fghWindowProcKeyPress(child_window?child_window:window,uMsg,GL_FALSE,wParam,lParam);
806     break;
807
808     case WM_SYSCHAR:
809     case WM_CHAR:
810     {
811       if (child_window)
812         window = child_window;
813
814       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
815             break;
816
817         fgState.Modifiers = fgPlatformGetModifiers( );
818         INVOKE_WCB( *window, Keyboard,
819                     ( (char)wParam,
820                       window->State.MouseX, window->State.MouseY )
821         );
822         fgState.Modifiers = INVALID_MODIFIERS;
823     }
824     break;
825
826     case WM_CAPTURECHANGED:
827         /* User has finished resizing the window, force a redraw */
828         INVOKE_WCB( *window, Display, ( ) );
829
830         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
831         break;
832
833         /* Other messages that I have seen and which are not handled already */
834     case WM_SETTEXT:  /* 0x000c */
835         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
836         /* Pass it on to "DefWindowProc" to set the window text */
837         break;
838
839     case WM_GETTEXT:  /* 0x000d */
840         /* Ideally we would copy the title of the window into "lParam" */
841         /* strncpy ( (char *)lParam, "Window Title", wParam );
842            lRet = ( wParam > 12 ) ? 12 : wParam;  */
843         /* the number of characters copied */
844         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
845         break;
846
847     case WM_GETTEXTLENGTH:  /* 0x000e */
848         /* Ideally we would get the length of the title of the window */
849         lRet = 12;
850         /* the number of characters in "Window Title\0" (see above) */
851         break;
852
853     case WM_ERASEBKGND:  /* 0x0014 */
854         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
855         break;
856
857 #if !defined(_WIN32_WCE)
858     case WM_SYNCPAINT:  /* 0x0088 */
859         /* Another window has moved, need to update this one */
860         window->State.Redisplay = GL_TRUE;
861         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
862         /* Help screen says this message must be passed to "DefWindowProc" */
863         break;
864
865     case WM_NCPAINT:  /* 0x0085 */
866       /* Need to update the border of this window */
867         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
868         /* Pass it on to "DefWindowProc" to repaint a standard border */
869         break;
870
871     case WM_SYSCOMMAND :  /* 0x0112 */
872         {
873           /*
874            * We have received a system command message.  Try to act on it.
875            * The commands are passed in through the "wParam" parameter:
876            * The least significant digit seems to be which edge of the window
877            * is being used for a resize event:
878            *     4  3  5
879            *     1     2
880            *     7  6  8
881            * Congratulations and thanks to Richard Rauch for figuring this out..
882            */
883             switch ( wParam & 0xfff0 )
884             {
885             case SC_SIZE       :
886                 break ;
887
888             case SC_MOVE       :
889                 break ;
890
891             case SC_MINIMIZE   :
892                 /* User has clicked on the "-" to minimize the window */
893                 /* Turn off the visibility */
894                 window->State.Visible = GL_FALSE ;
895
896                 break ;
897
898             case SC_MAXIMIZE   :
899                 break ;
900
901             case SC_NEXTWINDOW :
902                 break ;
903
904             case SC_PREVWINDOW :
905                 break ;
906
907             case SC_CLOSE      :
908                 /* Followed very closely by a WM_CLOSE message */
909                 break ;
910
911             case SC_VSCROLL    :
912                 break ;
913
914             case SC_HSCROLL    :
915                 break ;
916
917             case SC_MOUSEMENU  :
918                 break ;
919
920             case SC_KEYMENU    :
921                 break ;
922
923             case SC_ARRANGE    :
924                 break ;
925
926             case SC_RESTORE    :
927                 break ;
928
929             case SC_TASKLIST   :
930                 break ;
931
932             case SC_SCREENSAVE :
933                 break ;
934
935             case SC_HOTKEY     :
936                 break ;
937
938 #if(WINVER >= 0x0400)
939             case SC_DEFAULT    :
940                 break ;
941
942             case SC_MONITORPOWER    :
943                 break ;
944
945             case SC_CONTEXTHELP    :
946                 break ;
947 #endif /* WINVER >= 0x0400 */
948
949             default:
950 #if _DEBUG
951                 fgWarning( "Unknown wParam type 0x%x", wParam );
952 #endif
953                 break;
954             }
955         }
956 #endif /* !defined(_WIN32_WCE) */
957
958         /* We need to pass the message on to the operating system as well */
959         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
960         break;
961
962 #ifdef WM_TOUCH
963         /* handle multi-touch messages */
964         case WM_TOUCH:
965         {
966                 unsigned int numInputs = (unsigned int)wParam;
967                 unsigned int i = 0;
968                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
969
970                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
971                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
972                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
973                 }
974
975                 if (!fghGetTouchInputInfo) { 
976                         free( (void*)ti );
977                         break;
978                 }
979
980                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
981                         /* Handle each contact point */
982                         for (i = 0; i < numInputs; ++i ) {
983
984                                 POINT tp;
985                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
986                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
987                                 ScreenToClient( hWnd, &tp );
988
989                                 ti[i].dwID = ti[i].dwID * 2;
990
991                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
992                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
993                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
994                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
995                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
996                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
997                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
998                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
999                                 }
1000                         }
1001                 }
1002                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1003                 free( (void*)ti );
1004                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1005                 break;
1006         }
1007 #endif
1008     default:
1009         /* Handle unhandled messages */
1010         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1011         break;
1012     }
1013
1014     return lRet;
1015 }