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