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