2 * freeglut_main_mswin.c
4 * The Windows-specific mouse cursor related stuff.
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
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:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
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.
28 #include <GL/freeglut.h>
29 #include "../fg_internal.h"
32 extern void fghRedrawWindow ( SFG_Window *window );
34 extern void fgNewWGLCreateContext( SFG_Window* window );
35 extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly,
36 unsigned char layer_type );
38 extern void fgPlatformCheckMenuDeactivate();
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;
48 typedef struct GXDisplayProperties GXDisplayProperties;
49 typedef struct GXKeyList GXKeyList;
52 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
53 typedef int (*GXOPENINPUT)();
55 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
56 GXOPENINPUT GXOpenInput_ = NULL;
58 struct GXKeyList gxKeyList;
59 #endif /* _WIN32_WCE */
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)
69 static fg_time_t lastTime32 = 0;
70 static fg_time_t timeEpoch = 0;
71 void fgPlatformInitSystemTime()
73 #if defined(_WIN32_WCE)
74 lastTime32 = GetTickCount();
76 lastTime32 = timeGetTime();
79 fg_time_t fgPlatformSystemTime ( void )
82 #if defined(_WIN32_WCE)
83 currTime32 = GetTickCount();
85 currTime32 = timeGetTime();
87 /* Check if we just wrapped */
88 if (currTime32 < lastTime32)
91 lastTime32 = currTime32;
93 return currTime32 | timeEpoch << 32;
97 void fgPlatformSleepForEvents( fg_time_t msec )
99 MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
103 void fgPlatformProcessSingleEvent ( void )
107 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
109 while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
111 if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
113 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
118 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
119 fgState.ExecState = GLUT_EXEC_STATE_STOP;
124 TranslateMessage( &stMsg );
125 DispatchMessage( &stMsg );
131 static void fghUpdateWindowStatus(SFG_Window *window, GLboolean visState)
135 if (window->State.Visible != visState)
137 window->State.Visible = visState;
138 INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
141 /* Also notify children */
142 for( child = ( SFG_Window * )window->Children.First;
144 child = ( SFG_Window * )child->Node.Next )
146 fghUpdateWindowStatus(child, visState);
150 static void fghNotifyWindowStatus(SFG_Window *window)
154 INVOKE_WCB( *window, WindowStatus, ( window->State.Visible?GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
156 /* Also notify children */
157 for( child = ( SFG_Window * )window->Children.First;
159 child = ( SFG_Window * )child->Node.Next )
161 fghNotifyWindowStatus(child);
165 void fgPlatformMainLoopPreliminaryWork ( void )
167 SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
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
180 if ( FETCH_WCB( *window, WindowStatus ) )
182 SFG_Window *current_window = fgStructure.CurrentWindow ;
184 fghNotifyWindowStatus(window);
185 fgSetWindow( current_window );
188 window = (SFG_Window *)window->Node.Next ;
194 * Determine a GLUT modifier mask based on MS-WINDOWS system info.
196 static int fgPlatformGetModifiers (void)
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 );
207 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
209 static unsigned char lControl = 0, lShift = 0, lAlt = 0,
210 rControl = 0, rShift = 0, rAlt = 0;
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.
220 if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
223 /* Remember the current modifiers state so user can query it from their callback */
224 fgState.Modifiers = fgPlatformGetModifiers( );
226 /* Convert the Win32 keystroke codes to GLUTtish way */
227 # define KEY(a,b) case a: keypress = b; break;
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 );
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
257 #define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\
258 if (!keyStateVar && GetAsyncKeyState ( winKey ))\
263 else if (keyStateVar && !GetAsyncKeyState ( winKey ))\
269 ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
270 ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
273 ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
274 ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
277 ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
278 ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
280 #undef ASYNC_KEY_EVENT
283 /* The delete key should be treated as an ASCII keypress: */
285 INVOKE_WCB( *window, Keyboard,
286 ( 127, window->State.MouseX, window->State.MouseY )
289 INVOKE_WCB( *window, KeyboardUp,
290 ( 127, window->State.MouseX, window->State.MouseY )
294 #if !defined(_WIN32_WCE)
296 /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
302 GetKeyboardState( state );
304 if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
307 INVOKE_WCB( *window, KeyboardUp,
309 window->State.MouseX, window->State.MouseY )
315 #if defined(_WIN32_WCE)
316 if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
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;
339 INVOKE_WCB( *window, Special,
341 window->State.MouseX, window->State.MouseY )
344 INVOKE_WCB( *window, SpecialUp,
346 window->State.MouseX, window->State.MouseY )
349 fgState.Modifiers = INVALID_MODIFIERS;
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 );
358 static SFG_Window* fghWindowUnderCursor(SFG_Window *window)
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.
366 if (window && window->Children.First) /* This window has childs */
368 SFG_WindowHandleType hwnd;
369 SFG_Window* child_window;
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 );
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 */
379 child_window = fgWindowByHandle(hwnd);
380 if (child_window) /* Verify we got a FreeGLUT window */
382 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
383 window = fghWindowUnderCursor(child_window);
392 * The window procedure for handling Win32 events
394 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
398 static int setCaptureActive = 0;
400 FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
402 window = fgWindowByHandle( hWnd );
404 if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
405 return DefWindowProc( hWnd, uMsg, wParam, lParam );
407 /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
408 uMsg, wParam, lParam ); */
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" );
418 window->Window.Handle = hWnd;
419 window->Window.pContext.Device = GetDC( hWnd );
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 );
427 fgState.DisplayMode = current_DisplayMode;
429 if( fgStructure.MenuContext )
430 wglMakeCurrent( window->Window.pContext.Device,
431 fgStructure.MenuContext->MContext
435 fgStructure.MenuContext =
436 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
437 fgStructure.MenuContext->MContext =
438 wglCreateContext( window->Window.pContext.Device );
441 /* window->Window.Context = wglGetCurrentContext (); */
442 window->Window.Context = wglCreateContext( window->Window.pContext.Device );
446 #if !defined(_WIN32_WCE)
447 fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
450 if( ! fgState.UseCurrentContext )
451 window->Window.Context =
452 wglCreateContext( window->Window.pContext.Device );
455 window->Window.Context = wglGetCurrentContext( );
456 if( ! window->Window.Context )
457 window->Window.Context =
458 wglCreateContext( window->Window.pContext.Device );
461 #if !defined(_WIN32_WCE)
462 fgNewWGLCreateContext( window );
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
471 if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
473 SFG_Window *current_window = fgStructure.CurrentWindow;
475 fgSetWindow( window );
476 window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
477 window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
478 fgSetWindow( current_window );
481 ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
483 #if defined(_WIN32_WCE)
484 /* Take over button handling */
486 HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
489 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
490 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
495 if(GXGetDefaultKeys_)
496 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
499 #endif /* defined(_WIN32_WCE) */
503 //printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam));
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.
509 if( window->State.Visible )
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);
517 window->State.Width = LOWORD(lParam);
518 window->State.Height = HIWORD(lParam);
519 #endif /* defined(_WIN32_WCE) */
521 if (width!=window->State.Width || height!=window->State.Height)
522 /* Something changed, need to resize */
523 window->State.NeedToResize = GL_TRUE;
526 /* according to docs, should return 0 */
532 SFG_Window* saved_window = fgStructure.CurrentWindow;
535 /* Check window visible, we don't want to call the position callback when the user minimized the window */
536 if (window->State.Visible)
538 GetWindowRect( window->Window.Handle, &windowRect );
542 /* For child window, we should return relative to upper-left
543 * of parent's client area.
545 POINT topleft = {windowRect.left,windowRect.top};
547 ScreenToClient(window->Parent->Window.Handle,&topleft);
548 windowRect.left = topleft.x;
549 windowRect.top = topleft.y;
552 INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) );
553 fgSetWindow(saved_window);
559 /* printf("WM_SETFOCUS: %p\n", window ); */
561 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
563 SetActiveWindow( window->Window.Handle );
564 UpdateWindow ( hWnd );
570 /* printf("WM_KILLFOCUS: %p\n", window ); */
571 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
573 /* Check if there are any open menus that need to be closed */
574 fgPlatformCheckMenuDeactivate();
580 //printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam));
581 if (LOWORD(wParam) != WA_INACTIVE)
583 /* printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
584 window->State.Cursor ); */
585 fgSetCursor( window, window->State.Cursor );
588 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
593 /* printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
594 if( LOWORD( lParam ) == HTCLIENT )
596 if (!window->State.pWState.MouseTracking)
600 /* Cursor just entered window, set cursor look */
601 fgSetCursor ( window, window->State.Cursor ) ;
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
607 if (FETCH_WCB( *window, Entry ))
609 INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
611 tme.cbSize = sizeof(TRACKMOUSEEVENT);
612 tme.dwFlags = TME_LEAVE;
613 tme.hwndTrack = window->Window.Handle;
614 TrackMouseEvent(&tme);
616 window->State.pWState.MouseTracking = GL_TRUE;
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 );
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
631 SFG_Window* saved_window = fgStructure.CurrentWindow;
632 INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
633 fgSetWindow(saved_window);
635 window->State.pWState.MouseTracking = GL_FALSE;
636 lRet = 0; /* As per docs, must return zero */
641 //printf("WM_SHOWWINDOW\n");
642 fghUpdateWindowStatus(window, GL_TRUE);
643 window->State.Redisplay = GL_TRUE;
649 /* Turn on the visibility in case it was turned off somehow */
650 window->State.Visible = GL_TRUE;
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 );
660 fgDestroyWindow ( window );
661 if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
667 * The window already got destroyed, so don't bother with it.
673 #if defined(_WIN32_WCE)
674 window->State.MouseX = 320-HIWORD( lParam );
675 window->State.MouseY = LOWORD( lParam );
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;
685 if ( window->ActiveMenu )
687 fgUpdateMenuHighlight( window->ActiveMenu );
691 fgState.Modifiers = fgPlatformGetModifiers( );
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 ) );
699 INVOKE_WCB( *window, Passive, ( window->State.MouseX,
700 window->State.MouseY ) );
702 fgState.Modifiers = INVALID_MODIFIERS;
713 GLboolean pressed = GL_TRUE;
716 #if defined(_WIN32_WCE)
717 window->State.MouseX = 320-HIWORD( lParam );
718 window->State.MouseY = LOWORD( lParam );
720 window->State.MouseX = LOWORD( lParam );
721 window->State.MouseY = HIWORD( lParam );
722 #endif /* defined(_WIN32_WCE) */
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;
733 button = GLUT_LEFT_BUTTON;
737 button = GLUT_MIDDLE_BUTTON;
741 button = GLUT_RIGHT_BUTTON;
745 button = GLUT_LEFT_BUTTON;
749 button = GLUT_MIDDLE_BUTTON;
753 button = GLUT_RIGHT_BUTTON;
761 #if !defined(_WIN32_WCE)
762 if( GetSystemMetrics( SM_SWAPBUTTON ) )
764 if( button == GLUT_LEFT_BUTTON )
765 button = GLUT_RIGHT_BUTTON;
767 if( button == GLUT_RIGHT_BUTTON )
768 button = GLUT_LEFT_BUTTON;
770 #endif /* !defined(_WIN32_WCE) */
773 return DefWindowProc( hWnd, uMsg, lParam, wParam );
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.
780 if( fgCheckActiveMenu( window, button, pressed,
781 window->State.MouseX, window->State.MouseY ) )
784 /* Set capture so that the window captures all the mouse messages
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.
790 if ( pressed == GL_TRUE )
792 if (!setCaptureActive)
793 SetCapture ( window->Window.Handle ) ;
794 setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
796 else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
797 /* Make sure all mouse buttons are released before releasing capture */
800 if( ! FETCH_WCB( *window, Mouse ) )
803 fgSetWindow( window );
804 fgState.Modifiers = fgPlatformGetModifiers( );
809 pressed ? GLUT_DOWN : GLUT_UP,
810 window->State.MouseX,
815 fgState.Modifiers = INVALID_MODIFIERS;
817 /* As per docs, should return zero */
824 SFG_Window *child_window = NULL;
825 int wheel_number = LOWORD( wParam );
826 short ticks = ( short )HIWORD( wParam );
828 window = fghWindowUnderCursor(window);
830 fgState.MouseWheelTicks += ticks;
831 if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
833 int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
835 if( ! FETCH_WCB( *window, MouseWheel ) &&
836 ! FETCH_WCB( *window, Mouse ) )
839 fgSetWindow( window );
840 fgState.Modifiers = fgPlatformGetModifiers( );
842 while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
844 if( FETCH_WCB( *window, MouseWheel ) )
845 INVOKE_WCB( *window, MouseWheel,
848 window->State.MouseX,
852 else /* No mouse wheel, call the mouse button callback twice */
855 * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
856 * " " one +1 to 5, -1 to 6, ...
858 * XXX The below assumes that you have no more than 3 mouse
859 * XXX buttons. Sorry.
861 int button = wheel_number * 2 + 3;
864 INVOKE_WCB( *window, Mouse,
866 window->State.MouseX, window->State.MouseY )
868 INVOKE_WCB( *window, Mouse,
870 window->State.MouseX, window->State.MouseY )
874 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
877 fgState.Modifiers = INVALID_MODIFIERS;
885 window = fghWindowUnderCursor(window);
886 lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
893 window = fghWindowUnderCursor(window);
894 lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
901 window = fghWindowUnderCursor(window);
903 if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
906 fgState.Modifiers = fgPlatformGetModifiers( );
907 INVOKE_WCB( *window, Keyboard,
909 window->State.MouseX, window->State.MouseY )
911 fgState.Modifiers = INVALID_MODIFIERS;
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 );
922 lRet = 0; /* Per docs, should return zero */
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 */
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 );
939 case WM_GETTEXTLENGTH: /* 0x000e */
940 /* Ideally we would get the length of the title of the window */
942 /* the number of characters in "Window Title\0" (see above) */
945 case WM_ERASEBKGND: /* 0x0014 */
946 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
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" */
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 */
963 case WM_SYSCOMMAND : /* 0x0112 */
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:
973 * Congratulations and thanks to Richard Rauch for figuring this out..
975 switch ( wParam & 0xfff0 )
984 /* User has clicked on the "-" to minimize the window */
985 /* Turn off the visibility */
986 fghUpdateWindowStatus(window, GL_FALSE);
1000 /* Followed very closely by a WM_CLOSE message */
1019 fghUpdateWindowStatus(window, GL_TRUE);
1025 case SC_SCREENSAVE :
1031 #if(WINVER >= 0x0400)
1035 case SC_MONITORPOWER :
1038 case SC_CONTEXTHELP :
1040 #endif /* WINVER >= 0x0400 */
1044 fgWarning( "Unknown wParam type 0x%x", wParam );
1049 #endif /* !defined(_WIN32_WCE) */
1051 /* We need to pass the message on to the operating system as well */
1052 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1056 /* handle multi-touch messages */
1059 unsigned int numInputs = (unsigned int)wParam;
1061 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1063 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1064 fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1065 fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1068 if (!fghGetTouchInputInfo) {
1073 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1074 /* Handle each contact point */
1075 for (i = 0; i < numInputs; ++i ) {
1078 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1079 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1080 ScreenToClient( hWnd, &tp );
1082 ti[i].dwID = ti[i].dwID * 2;
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 ) );
1095 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1097 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1102 /* Handle unhandled messages */
1103 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );