670105154e19f604e1c26036bfd165b6c3f9a6f7
[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         if (child_window)
527         {
528             /* If we're dealing with a child window, make sure it has input focus instead, set it here. */
529             SetFocus(child_window->Window.Handle);
530             SetActiveWindow( child_window->Window.Handle );
531             INVOKE_WCB( *child_window, Entry, ( GLUT_ENTERED ) );
532             UpdateWindow ( child_window->Window.Handle );
533         }
534         else
535         {
536             SetActiveWindow( window->Window.Handle );
537             INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
538         }
539         /* Always request update on main window to be safe */
540         UpdateWindow ( hWnd );
541
542         break;
543
544     case WM_KILLFOCUS:
545         {
546             SFG_Window* saved_window = fgStructure.CurrentWindow;
547 /*            printf("WM_KILLFOCUS: %p\n", window ); */
548             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
549             INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
550             fgSetWindow(saved_window);
551
552             /* Check if there are any open menus that need to be closed */
553             fgPlatformCheckMenuDeactivate();
554         }
555         break;
556
557 #if 0
558     case WM_ACTIVATE:
559         //printf("WM_ACTIVATE: %x %d %d\n",lParam, HIWORD(wParam), LOWORD(wParam));
560         if (LOWORD(wParam) != WA_INACTIVE)
561         {
562 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
563                    window->State.Cursor ); */
564             fgSetCursor( window, window->State.Cursor );
565         }
566
567         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
568         break;
569 #endif
570
571     case WM_SETCURSOR:
572 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
573         if( LOWORD( lParam ) == HTCLIENT )
574             fgSetCursor ( window, window->State.Cursor ) ;
575         else
576             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
577         break;
578
579     case WM_SHOWWINDOW:
580         window->State.Visible = GL_TRUE;
581         window->State.Redisplay = GL_TRUE;
582         break;
583
584     case WM_PAINT:
585         /* Turn on the visibility in case it was turned off somehow */
586         window->State.Visible = GL_TRUE;
587         InvalidateRect( hWnd, NULL, GL_FALSE ); /* Make sure whole window is repainted. Bit of a hack, but a safe one from what google turns up... */
588         BeginPaint( hWnd, &ps );
589         fghRedrawWindow( window );
590         EndPaint( hWnd, &ps );
591         break;
592
593     case WM_CLOSE:
594         fgDestroyWindow ( window );
595         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
596             PostQuitMessage(0);
597         break;
598
599     case WM_DESTROY:
600         /*
601          * The window already got destroyed, so don't bother with it.
602          */
603         return 0;
604
605     case WM_MOUSEMOVE:
606     {
607 #if defined(_WIN32_WCE)
608         window->State.MouseX = 320-HIWORD( lParam );
609         window->State.MouseY = LOWORD( lParam );
610 #else
611         window->State.MouseX = LOWORD( lParam );
612         window->State.MouseY = HIWORD( lParam );
613 #endif /* defined(_WIN32_WCE) */
614         /* Restrict to [-32768, 32767] to match X11 behaviour       */
615         /* See comment in "freeglut_developer" mailing list 10/4/04 */
616         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
617         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
618
619         if ( window->ActiveMenu )
620         {
621             fgUpdateMenuHighlight( window->ActiveMenu );
622             break;
623         }
624
625         fgState.Modifiers = fgPlatformGetModifiers( );
626
627         if( ( wParam & MK_LBUTTON ) ||
628             ( wParam & MK_MBUTTON ) ||
629             ( wParam & MK_RBUTTON ) )
630             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
631                                            window->State.MouseY ) );
632         else
633             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
634                                             window->State.MouseY ) );
635
636         fgState.Modifiers = INVALID_MODIFIERS;
637     }
638     break;
639
640     case WM_LBUTTONDOWN:
641     case WM_MBUTTONDOWN:
642     case WM_RBUTTONDOWN:
643     case WM_LBUTTONUP:
644     case WM_MBUTTONUP:
645     case WM_RBUTTONUP:
646     {
647         GLboolean pressed = GL_TRUE;
648         int button;
649
650 #if defined(_WIN32_WCE)
651         window->State.MouseX = 320-HIWORD( lParam );
652         window->State.MouseY = LOWORD( lParam );
653 #else
654         window->State.MouseX = LOWORD( lParam );
655         window->State.MouseY = HIWORD( lParam );
656 #endif /* defined(_WIN32_WCE) */
657
658         /* Restrict to [-32768, 32767] to match X11 behaviour       */
659         /* See comment in "freeglut_developer" mailing list 10/4/04 */
660         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
661         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
662
663         switch( uMsg )
664         {
665         case WM_LBUTTONDOWN:
666             pressed = GL_TRUE;
667             button = GLUT_LEFT_BUTTON;
668             break;
669         case WM_MBUTTONDOWN:
670             pressed = GL_TRUE;
671             button = GLUT_MIDDLE_BUTTON;
672             break;
673         case WM_RBUTTONDOWN:
674             pressed = GL_TRUE;
675             button = GLUT_RIGHT_BUTTON;
676             break;
677         case WM_LBUTTONUP:
678             pressed = GL_FALSE;
679             button = GLUT_LEFT_BUTTON;
680             break;
681         case WM_MBUTTONUP:
682             pressed = GL_FALSE;
683             button = GLUT_MIDDLE_BUTTON;
684             break;
685         case WM_RBUTTONUP:
686             pressed = GL_FALSE;
687             button = GLUT_RIGHT_BUTTON;
688             break;
689         default:
690             pressed = GL_FALSE;
691             button = -1;
692             break;
693         }
694
695 #if !defined(_WIN32_WCE)
696         if( GetSystemMetrics( SM_SWAPBUTTON ) )
697         {
698             if( button == GLUT_LEFT_BUTTON )
699                 button = GLUT_RIGHT_BUTTON;
700             else
701                 if( button == GLUT_RIGHT_BUTTON )
702                     button = GLUT_LEFT_BUTTON;
703         }
704 #endif /* !defined(_WIN32_WCE) */
705
706         if( button == -1 )
707             return DefWindowProc( hWnd, uMsg, lParam, wParam );
708
709         /*
710          * Do not execute the application's mouse callback if a menu
711          * is hooked to this button.  In that case an appropriate
712          * private call should be generated.
713          */
714         if( fgCheckActiveMenu( window, button, pressed,
715                                window->State.MouseX, window->State.MouseY ) )
716             break;
717
718         /* Set capture so that the window captures all the mouse messages */
719         /*
720          * XXX - Multiple button support:  Under X11, the mouse is not released
721          * XXX - from the window until all buttons have been released, even if the
722          * XXX - user presses a button in another window.  This will take more
723          * XXX - code changes than I am up to at the moment (10/5/04).  The present
724          * XXX - is a 90 percent solution.
725          */
726         if ( pressed == GL_TRUE )
727           SetCapture ( window->Window.Handle ) ;
728         else
729           ReleaseCapture () ;
730
731         if( ! FETCH_WCB( *window, Mouse ) )
732             break;
733
734         fgSetWindow( window );
735         fgState.Modifiers = fgPlatformGetModifiers( );
736
737         INVOKE_WCB(
738             *window, Mouse,
739             ( button,
740               pressed ? GLUT_DOWN : GLUT_UP,
741               window->State.MouseX,
742               window->State.MouseY
743             )
744         );
745
746         fgState.Modifiers = INVALID_MODIFIERS;
747     }
748     break;
749
750     case WM_MOUSEWHEEL:
751     {
752         int wheel_number = LOWORD( wParam );
753         short ticks = ( short )HIWORD( wParam );
754                 fgState.MouseWheelTicks += ticks;
755
756         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
757                 {
758                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
759
760             if( ! FETCH_WCB( *window, MouseWheel ) &&
761                 ! FETCH_WCB( *window, Mouse ) )
762                 break;
763
764             fgSetWindow( window );
765             fgState.Modifiers = fgPlatformGetModifiers( );
766
767             while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
768                         {
769                 if( FETCH_WCB( *window, MouseWheel ) )
770                     INVOKE_WCB( *window, MouseWheel,
771                                 ( wheel_number,
772                                   direction,
773                                   window->State.MouseX,
774                                   window->State.MouseY
775                                 )
776                     );
777                 else  /* No mouse wheel, call the mouse button callback twice */
778                                 {
779                     /*
780                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
781                      *  "    "   one                     +1 to 5, -1 to 6, ...
782                      *
783                      * XXX The below assumes that you have no more than 3 mouse
784                      * XXX buttons.  Sorry.
785                      */
786                     int button = wheel_number * 2 + 3;
787                     if( direction < 0 )
788                         ++button;
789                     INVOKE_WCB( *window, Mouse,
790                                 ( button, GLUT_DOWN,
791                                   window->State.MouseX, window->State.MouseY )
792                     );
793                     INVOKE_WCB( *window, Mouse,
794                                 ( button, GLUT_UP,
795                                   window->State.MouseX, window->State.MouseY )
796                     );
797                                 }
798
799                                 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
800                         }
801
802             fgState.Modifiers = INVALID_MODIFIERS;
803                 }
804     }
805     break ;
806
807     case WM_SYSKEYDOWN:
808     case WM_KEYDOWN:
809         if (child_window)
810             window = child_window;
811         lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
812     break;
813
814     case WM_SYSKEYUP:
815     case WM_KEYUP:
816         if (child_window)
817             window = child_window;
818         lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
819     break;
820
821     case WM_SYSCHAR:
822     case WM_CHAR:
823     {
824       if (child_window)
825         window = child_window;
826
827       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
828             break;
829
830         fgState.Modifiers = fgPlatformGetModifiers( );
831         INVOKE_WCB( *window, Keyboard,
832                     ( (char)wParam,
833                       window->State.MouseX, window->State.MouseY )
834         );
835         fgState.Modifiers = INVALID_MODIFIERS;
836     }
837     break;
838
839     case WM_CAPTURECHANGED:
840         /* User has finished resizing the window, force a redraw */
841         INVOKE_WCB( *window, Display, ( ) );
842
843         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
844         break;
845
846         /* Other messages that I have seen and which are not handled already */
847     case WM_SETTEXT:  /* 0x000c */
848         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
849         /* Pass it on to "DefWindowProc" to set the window text */
850         break;
851
852     case WM_GETTEXT:  /* 0x000d */
853         /* Ideally we would copy the title of the window into "lParam" */
854         /* strncpy ( (char *)lParam, "Window Title", wParam );
855            lRet = ( wParam > 12 ) ? 12 : wParam;  */
856         /* the number of characters copied */
857         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
858         break;
859
860     case WM_GETTEXTLENGTH:  /* 0x000e */
861         /* Ideally we would get the length of the title of the window */
862         lRet = 12;
863         /* the number of characters in "Window Title\0" (see above) */
864         break;
865
866     case WM_ERASEBKGND:  /* 0x0014 */
867         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
868         break;
869
870 #if !defined(_WIN32_WCE)
871     case WM_SYNCPAINT:  /* 0x0088 */
872         /* Another window has moved, need to update this one */
873         window->State.Redisplay = GL_TRUE;
874         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
875         /* Help screen says this message must be passed to "DefWindowProc" */
876         break;
877
878     case WM_NCPAINT:  /* 0x0085 */
879       /* Need to update the border of this window */
880         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
881         /* Pass it on to "DefWindowProc" to repaint a standard border */
882         break;
883
884     case WM_SYSCOMMAND :  /* 0x0112 */
885         {
886           /*
887            * We have received a system command message.  Try to act on it.
888            * The commands are passed in through the "wParam" parameter:
889            * The least significant digit seems to be which edge of the window
890            * is being used for a resize event:
891            *     4  3  5
892            *     1     2
893            *     7  6  8
894            * Congratulations and thanks to Richard Rauch for figuring this out..
895            */
896             switch ( wParam & 0xfff0 )
897             {
898             case SC_SIZE       :
899                 break ;
900
901             case SC_MOVE       :
902                 break ;
903
904             case SC_MINIMIZE   :
905                 /* User has clicked on the "-" to minimize the window */
906                 /* Turn off the visibility */
907                 window->State.Visible = GL_FALSE ;
908
909                 break ;
910
911             case SC_MAXIMIZE   :
912                 break ;
913
914             case SC_NEXTWINDOW :
915                 break ;
916
917             case SC_PREVWINDOW :
918                 break ;
919
920             case SC_CLOSE      :
921                 /* Followed very closely by a WM_CLOSE message */
922                 break ;
923
924             case SC_VSCROLL    :
925                 break ;
926
927             case SC_HSCROLL    :
928                 break ;
929
930             case SC_MOUSEMENU  :
931                 break ;
932
933             case SC_KEYMENU    :
934                 break ;
935
936             case SC_ARRANGE    :
937                 break ;
938
939             case SC_RESTORE    :
940                 break ;
941
942             case SC_TASKLIST   :
943                 break ;
944
945             case SC_SCREENSAVE :
946                 break ;
947
948             case SC_HOTKEY     :
949                 break ;
950
951 #if(WINVER >= 0x0400)
952             case SC_DEFAULT    :
953                 break ;
954
955             case SC_MONITORPOWER    :
956                 break ;
957
958             case SC_CONTEXTHELP    :
959                 break ;
960 #endif /* WINVER >= 0x0400 */
961
962             default:
963 #if _DEBUG
964                 fgWarning( "Unknown wParam type 0x%x", wParam );
965 #endif
966                 break;
967             }
968         }
969 #endif /* !defined(_WIN32_WCE) */
970
971         /* We need to pass the message on to the operating system as well */
972         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
973         break;
974
975 #ifdef WM_TOUCH
976         /* handle multi-touch messages */
977         case WM_TOUCH:
978         {
979                 unsigned int numInputs = (unsigned int)wParam;
980                 unsigned int i = 0;
981                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
982
983                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
984                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
985                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
986                 }
987
988                 if (!fghGetTouchInputInfo) { 
989                         free( (void*)ti );
990                         break;
991                 }
992
993                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
994                         /* Handle each contact point */
995                         for (i = 0; i < numInputs; ++i ) {
996
997                                 POINT tp;
998                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
999                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1000                                 ScreenToClient( hWnd, &tp );
1001
1002                                 ti[i].dwID = ti[i].dwID * 2;
1003
1004                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1005                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
1006                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1007                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1008                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1009                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
1010                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1011                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
1012                                 }
1013                         }
1014                 }
1015                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1016                 free( (void*)ti );
1017                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1018                 break;
1019         }
1020 #endif
1021     default:
1022         /* Handle unhandled messages */
1023         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1024         break;
1025     }
1026
1027     return lRet;
1028 }