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"
31 extern void fghRedrawWindow ( SFG_Window *window );
32 extern void fghRedrawWindowAndChildren ( SFG_Window *window );
33 extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
34 extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
35 extern void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside );
36 extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside );
38 extern void fgNewWGLCreateContext( SFG_Window* window );
39 extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly,
40 unsigned char layer_type );
42 extern void fgPlatformCheckMenuDeactivate();
45 typedef BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT,UINT,PTOUCHINPUT,int);
46 typedef BOOL (WINAPI *pCloseTouchInputHandle)(HTOUCHINPUT);
47 static pGetTouchInputInfo fghGetTouchInputInfo = (pGetTouchInputInfo)0xDEADBEEF;
48 static pCloseTouchInputHandle fghCloseTouchInputHandle = (pCloseTouchInputHandle)0xDEADBEEF;
52 typedef struct GXDisplayProperties GXDisplayProperties;
53 typedef struct GXKeyList GXKeyList;
56 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
57 typedef int (*GXOPENINPUT)();
59 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
60 GXOPENINPUT GXOpenInput_ = NULL;
62 struct GXKeyList gxKeyList;
63 #endif /* _WIN32_WCE */
66 /* Get system time, taking special precautions against 32bit timer wrap.
67 We use timeGetTime and not GetTickCount because of its better stability,
68 and because we can increase its granularity (to 1 ms in
69 fgPlatformInitialize). For that reason we can't use GetTickCount64 which
70 wouldn't have the wrap issue.
71 Credit: this is based on code in glibc (https://mail.gnome.org/archives/commits-list/2011-November/msg04588.html)
73 static fg_time_t lastTime32 = 0;
74 static fg_time_t timeEpoch = 0;
75 void fgPlatformInitSystemTime()
77 #if defined(_WIN32_WCE)
78 lastTime32 = GetTickCount();
80 lastTime32 = timeGetTime();
83 fg_time_t fgPlatformSystemTime ( void )
86 #if defined(_WIN32_WCE)
87 currTime32 = GetTickCount();
89 currTime32 = timeGetTime();
91 /* Check if we just wrapped */
92 if (currTime32 < lastTime32)
95 lastTime32 = currTime32;
97 return currTime32 | timeEpoch << 32;
101 void fgPlatformSleepForEvents( fg_time_t msec )
103 MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
107 void fgPlatformProcessSingleEvent ( void )
111 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
113 while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
115 if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
117 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
122 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
123 fgState.ExecState = GLUT_EXEC_STATE_STOP;
128 TranslateMessage( &stMsg );
129 DispatchMessage( &stMsg );
135 static void fghPlatformOnWindowStatusNotify(SFG_Window *window, GLboolean visState, GLboolean forceNotify)
137 GLboolean notify = GL_FALSE;
140 if (window->State.Visible != visState)
142 window->State.Visible = visState;
144 /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */
145 if (!window->Parent && window->State.pWState.IconTitle)
148 /* visible, set window title */
149 SetWindowText( window->Window.Handle, window->State.pWState.WindowTitle );
151 /* not visible, set icon title */
152 SetWindowText( window->Window.Handle, window->State.pWState.IconTitle );
158 if (notify || forceNotify)
160 SFG_Window *saved_window = fgStructure.CurrentWindow;
162 /* On win32 we only have two states, window displayed and window not displayed (iconified)
163 * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively.
165 INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
166 fgSetWindow( saved_window );
169 /* Also set windowStatus/visibility state for children */
170 for( child = ( SFG_Window * )window->Children.First;
172 child = ( SFG_Window * )child->Node.Next )
174 fghPlatformOnWindowStatusNotify(child, visState, GL_FALSE); /* No need to propagate forceNotify. Childs get this from their own INIT_WORK */
178 void fgPlatformMainLoopPreliminaryWork ( void )
185 * Determine a GLUT modifier mask based on MS-WINDOWS system info.
187 static int fgPlatformGetModifiers (void)
190 ( ( ( GetKeyState( VK_LSHIFT ) < 0 ) ||
191 ( GetKeyState( VK_RSHIFT ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
192 ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
193 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL : 0 ) |
194 ( ( ( GetKeyState( VK_LMENU ) < 0 ) ||
195 ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 );
198 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
200 static unsigned char lControl = 0, lShift = 0, lAlt = 0,
201 rControl = 0, rShift = 0, rAlt = 0;
205 /* if keydown, check for repeat */
206 /* If repeat is globally switched off, it cannot be switched back on per window.
207 * But if it is globally switched on, it can be switched off per window. This matches
208 * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
209 * global state switch.
211 if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
214 /* Remember the current modifiers state so user can query it from their callback */
215 fgState.Modifiers = fgPlatformGetModifiers( );
217 /* Convert the Win32 keystroke codes to GLUTtish way */
218 # define KEY(a,b) case a: keypress = b; break;
222 KEY( VK_F1, GLUT_KEY_F1 );
223 KEY( VK_F2, GLUT_KEY_F2 );
224 KEY( VK_F3, GLUT_KEY_F3 );
225 KEY( VK_F4, GLUT_KEY_F4 );
226 KEY( VK_F5, GLUT_KEY_F5 );
227 KEY( VK_F6, GLUT_KEY_F6 );
228 KEY( VK_F7, GLUT_KEY_F7 );
229 KEY( VK_F8, GLUT_KEY_F8 );
230 KEY( VK_F9, GLUT_KEY_F9 );
231 KEY( VK_F10, GLUT_KEY_F10 );
232 KEY( VK_F11, GLUT_KEY_F11 );
233 KEY( VK_F12, GLUT_KEY_F12 );
234 KEY( VK_PRIOR, GLUT_KEY_PAGE_UP );
235 KEY( VK_NEXT, GLUT_KEY_PAGE_DOWN );
236 KEY( VK_HOME, GLUT_KEY_HOME );
237 KEY( VK_END, GLUT_KEY_END );
238 KEY( VK_LEFT, GLUT_KEY_LEFT );
239 KEY( VK_UP, GLUT_KEY_UP );
240 KEY( VK_RIGHT, GLUT_KEY_RIGHT );
241 KEY( VK_DOWN, GLUT_KEY_DOWN );
242 KEY( VK_INSERT, GLUT_KEY_INSERT );
244 /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
245 * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
246 * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
248 #define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\
249 if (!keyStateVar && GetAsyncKeyState ( winKey ))\
254 else if (keyStateVar && !GetAsyncKeyState ( winKey ))\
260 ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
261 ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
264 ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
265 ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
268 ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
269 ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
271 #undef ASYNC_KEY_EVENT
274 /* The delete key should be treated as an ASCII keypress: */
276 INVOKE_WCB( *window, Keyboard,
277 ( 127, window->State.MouseX, window->State.MouseY )
280 INVOKE_WCB( *window, KeyboardUp,
281 ( 127, window->State.MouseX, window->State.MouseY )
285 #if !defined(_WIN32_WCE)
287 /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
293 GetKeyboardState( state );
295 if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
298 INVOKE_WCB( *window, KeyboardUp,
299 ( (char)(wParam & 0xFF), /* and with 0xFF to indicate to runtime that we want to strip out higher bits - otherwise we get a runtime error when "Smaller Type Checks" is enabled */
300 window->State.MouseX, window->State.MouseY )
306 #if defined(_WIN32_WCE)
307 if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
309 if(wParam==(unsigned)gxKeyList.vkRight)
310 keypress = GLUT_KEY_RIGHT;
311 else if(wParam==(unsigned)gxKeyList.vkLeft)
312 keypress = GLUT_KEY_LEFT;
313 else if(wParam==(unsigned)gxKeyList.vkUp)
314 keypress = GLUT_KEY_UP;
315 else if(wParam==(unsigned)gxKeyList.vkDown)
316 keypress = GLUT_KEY_DOWN;
317 else if(wParam==(unsigned)gxKeyList.vkA)
318 keypress = GLUT_KEY_F1;
319 else if(wParam==(unsigned)gxKeyList.vkB)
320 keypress = GLUT_KEY_F2;
321 else if(wParam==(unsigned)gxKeyList.vkC)
322 keypress = GLUT_KEY_F3;
323 else if(wParam==(unsigned)gxKeyList.vkStart)
324 keypress = GLUT_KEY_F4;
330 INVOKE_WCB( *window, Special,
332 window->State.MouseX, window->State.MouseY )
335 INVOKE_WCB( *window, SpecialUp,
337 window->State.MouseX, window->State.MouseY )
340 fgState.Modifiers = INVALID_MODIFIERS;
342 /* SYSKEY events should be sent to default window proc for system to handle them */
343 if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
344 return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
349 SFG_Window* fghWindowUnderCursor(SFG_Window *window)
351 /* Check if the current window that the mouse is over is a child window
352 * of the window the message was sent to. Some events only sent to main window,
353 * and when handling some messages, we need to make sure that we process
354 * callbacks on the child window instead. This mirrors how GLUT does things.
355 * returns either the original window or the found child.
357 if (window && window->Children.First) /* This window has childs */
360 SFG_Window* child_window;
362 /* Get mouse position at time of message */
363 DWORD mouse_pos_dw = GetMessagePos();
364 POINT mouse_pos = {GET_X_LPARAM(mouse_pos_dw), GET_Y_LPARAM(mouse_pos_dw)};
365 ScreenToClient( window->Window.Handle, &mouse_pos );
367 hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
368 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 */
370 child_window = fgWindowByHandle(hwnd);
371 if (child_window) /* Verify we got a FreeGLUT window */
373 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
374 window = fghWindowUnderCursor(child_window);
383 * The window procedure for handling Win32 events
385 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
389 static int setCaptureActive = 0;
391 FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
393 window = fgWindowByHandle( hWnd );
395 if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
396 return DefWindowProc( hWnd, uMsg, wParam, lParam );
398 /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
399 uMsg, wParam, lParam ); */
404 /* The window structure is passed as the creation structure parameter... */
405 window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
406 FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
407 "fgPlatformWindowProc" );
409 window->Window.Handle = hWnd;
410 window->Window.pContext.Device = GetDC( hWnd );
413 unsigned int current_DisplayMode = fgState.DisplayMode;
414 fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
415 #if !defined(_WIN32_WCE)
416 fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
418 fgState.DisplayMode = current_DisplayMode;
420 if( fgStructure.MenuContext )
421 wglMakeCurrent( window->Window.pContext.Device,
422 fgStructure.MenuContext->MContext
426 fgStructure.MenuContext =
427 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
428 fgStructure.MenuContext->MContext =
429 wglCreateContext( window->Window.pContext.Device );
432 /* window->Window.Context = wglGetCurrentContext (); */
433 window->Window.Context = wglCreateContext( window->Window.pContext.Device );
437 #if !defined(_WIN32_WCE)
438 fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
441 if( ! fgState.UseCurrentContext )
442 window->Window.Context =
443 wglCreateContext( window->Window.pContext.Device );
446 window->Window.Context = wglGetCurrentContext( );
447 if( ! window->Window.Context )
448 window->Window.Context =
449 wglCreateContext( window->Window.pContext.Device );
452 #if !defined(_WIN32_WCE)
453 fgNewWGLCreateContext( window );
457 ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
459 #if defined(_WIN32_WCE)
460 /* Take over button handling */
462 HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
465 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
466 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
471 if(GXGetDefaultKeys_)
472 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
475 #endif /* defined(_WIN32_WCE) */
479 /* printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam)); */
481 /* Update visibility state of the window */
482 if (wParam==SIZE_MINIMIZED)
483 fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE);
484 else if (wParam==SIZE_RESTORED && !window->State.Visible)
485 fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE);
487 /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */
488 if( window->State.Visible )
491 #if defined(_WIN32_WCE)
492 width = HIWORD(lParam);
493 height = LOWORD(lParam);
495 width = LOWORD(lParam);
496 height = HIWORD(lParam);
497 #endif /* defined(_WIN32_WCE) */
499 /* Update state and call callback, if there was a change */
500 fghOnReshapeNotify(window, width, height, GL_FALSE);
503 /* according to docs, should return 0 */
509 /* User resize-dragging the window, call reshape callback and
510 * force redisplay so display keeps running during dragging.
511 * Screen still wont update when not moving the cursor though...
513 /* PRECT prect = (PRECT) lParam; */
515 /* printf("WM_SIZING: nc-area: %i,%i\n",prect->right-prect->left,prect->bottom-prect->top); */
516 /* Get client area, the rect in lParam is including non-client area. */
517 fghGetClientArea(&rect,window,FALSE);
519 /* We'll get a WM_SIZE as well, but as state has
520 * already been updated here, the fghOnReshapeNotify
521 * in the handler for that message doesn't do anything.
523 fghOnReshapeNotify(window, rect.right-rect.left, rect.bottom-rect.top, GL_FALSE);
525 /* Now directly call the drawing function to update
526 * window and window's childs.
527 * This mimics the WM_PAINT messages that are received during
528 * resizing. Note that we don't have a WM_MOVING handler
529 * as move-dragging doesn't generate WM_MOVE or WM_PAINT
530 * messages until the mouse is released.
532 fghRedrawWindowAndChildren(window);
535 /* according to docs, should return TRUE */
541 /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */
542 if (!IsIconic(window->Window.Handle))
546 /* lParam contains coordinates of top-left of client area.
547 * Get top-left of non-client area of window, matching coordinates of
548 * glutInitPosition and glutPositionWindow, but not those of
549 * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return
550 * top-left of client area.
552 GetWindowRect( window->Window.Handle, &windowRect );
556 /* For child window, we should return relative to upper-left
557 * of parent's client area.
559 POINT topleft = {windowRect.left,windowRect.top};
561 ScreenToClient(window->Parent->Window.Handle,&topleft);
562 windowRect.left = topleft.x;
563 windowRect.top = topleft.y;
566 /* Update state and call callback, if there was a change */
567 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_FALSE);
571 /* according to docs, should return 0 */
576 /*printf("WM_SETFOCUS: %p\n", window );*/
577 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
579 SetActiveWindow( window->Window.Handle );
580 UpdateWindow ( hWnd );
585 /*printf("WM_KILLFOCUS: %p\n", window ); */
586 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
588 /* Check if there are any open menus that need to be closed */
589 fgPlatformCheckMenuDeactivate();
592 case WM_MOUSEACTIVATE:
593 /* Clicks should not activate the menu.
594 * Especially important when clicking on a menu's submenu item which has no effect.
596 printf("WM_MOUSEACTIVATE\n");
598 lRet = MA_NOACTIVATEANDEAT;
600 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
605 /* printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); */
606 if (LOWORD(wParam) != WA_INACTIVE)
608 /* printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
609 window->State.Cursor ); */
610 fgSetCursor( window, window->State.Cursor );
613 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
618 /* printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
619 if( LOWORD( lParam ) == HTCLIENT )
621 if (!window->State.pWState.MouseTracking)
625 /* Cursor just entered window, set cursor look */
626 fgSetCursor ( window, window->State.Cursor ) ;
628 /* If an EntryFunc callback is specified by the user, also
629 * invoke that callback and start mouse tracking so that
630 * we get a WM_MOUSELEAVE message
632 if (FETCH_WCB( *window, Entry ))
634 SFG_Window* saved_window = fgStructure.CurrentWindow;
635 INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
636 fgSetWindow(saved_window);
638 tme.cbSize = sizeof(TRACKMOUSEEVENT);
639 tme.dwFlags = TME_LEAVE;
640 tme.hwndTrack = window->Window.Handle;
641 TrackMouseEvent(&tme);
643 window->State.pWState.MouseTracking = TRUE;
648 /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
649 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
654 /* NB: This message is only received when a EntryFunc callback
655 * is specified by the user, as that is the only condition under
656 * which mouse tracking is setup in WM_SETCURSOR handler above
658 SFG_Window* saved_window = fgStructure.CurrentWindow;
659 INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
660 fgSetWindow(saved_window);
662 window->State.pWState.MouseTracking = FALSE;
663 lRet = 0; /* As per docs, must return zero */
668 /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
671 fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE);
672 window->State.Redisplay = GL_TRUE;
676 fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE);
677 window->State.Redisplay = GL_FALSE;
685 /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
686 if (GetUpdateRect(hWnd,&rect,FALSE))
688 /* Dummy begin/end paint to validate rect that needs
689 * redrawing, then signal that a redisplay is needed.
690 * This allows us full control about when we do any
691 * redrawing, and is the same as what original GLUT
695 BeginPaint( hWnd, &ps );
696 EndPaint( hWnd, &ps );
698 window->State.Redisplay = GL_TRUE;
700 lRet = 0; /* As per docs, should return 0 */
705 fgDestroyWindow ( window );
706 if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
712 * The window already got destroyed, so don't bother with it.
718 /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
719 #if defined(_WIN32_WCE)
720 window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */
721 window->State.MouseY = LOWORD( lParam );
723 window->State.MouseX = GET_X_LPARAM( lParam );
724 window->State.MouseY = GET_Y_LPARAM( lParam );
725 #endif /* defined(_WIN32_WCE) */
726 /* Restrict to [-32768, 32767] to match X11 behaviour */
727 /* See comment in "freeglut_developer" mailing list 10/4/04 */
728 if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
729 if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
731 if ( window->ActiveMenu )
733 fgUpdateMenuHighlight( window->ActiveMenu );
737 fgState.Modifiers = fgPlatformGetModifiers( );
739 if( ( wParam & MK_LBUTTON ) ||
740 ( wParam & MK_MBUTTON ) ||
741 ( wParam & MK_RBUTTON ) )
742 INVOKE_WCB( *window, Motion, ( window->State.MouseX,
743 window->State.MouseY ) );
745 INVOKE_WCB( *window, Passive, ( window->State.MouseX,
746 window->State.MouseY ) );
748 fgState.Modifiers = INVALID_MODIFIERS;
759 GLboolean pressed = GL_TRUE;
762 /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
763 #if defined(_WIN32_WCE)
764 window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */
765 window->State.MouseY = LOWORD( lParam );
767 window->State.MouseX = GET_X_LPARAM( lParam );
768 window->State.MouseY = GET_Y_LPARAM( lParam );
769 #endif /* defined(_WIN32_WCE) */
771 /* Restrict to [-32768, 32767] to match X11 behaviour */
772 /* See comment in "freeglut_developer" mailing list 10/4/04 */
773 if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
774 if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
780 button = GLUT_LEFT_BUTTON;
784 button = GLUT_MIDDLE_BUTTON;
788 button = GLUT_RIGHT_BUTTON;
792 button = GLUT_LEFT_BUTTON;
796 button = GLUT_MIDDLE_BUTTON;
800 button = GLUT_RIGHT_BUTTON;
808 #if !defined(_WIN32_WCE)
809 if( GetSystemMetrics( SM_SWAPBUTTON ) )
811 if( button == GLUT_LEFT_BUTTON )
812 button = GLUT_RIGHT_BUTTON;
814 if( button == GLUT_RIGHT_BUTTON )
815 button = GLUT_LEFT_BUTTON;
817 #endif /* !defined(_WIN32_WCE) */
820 return DefWindowProc( hWnd, uMsg, lParam, wParam );
823 * Do not execute the application's mouse callback if a menu
824 * is hooked to this button. In that case an appropriate
825 * private call should be generated.
827 if( fgCheckActiveMenu( window, button, pressed,
828 window->State.MouseX, window->State.MouseY ) )
831 /* Set capture so that the window captures all the mouse messages
833 * The mouse is not released from the window until all buttons have
834 * been released, even if the user presses a button in another window.
835 * This is consistent with the behavior on X11.
837 if ( pressed == GL_TRUE )
839 if (!setCaptureActive)
840 SetCapture ( window->Window.Handle ) ;
841 setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
843 else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
844 /* Make sure all mouse buttons are released before releasing capture */
847 if( ! FETCH_WCB( *window, Mouse ) )
850 fgSetWindow( window );
851 fgState.Modifiers = fgPlatformGetModifiers( );
856 pressed ? GLUT_DOWN : GLUT_UP,
857 window->State.MouseX,
862 fgState.Modifiers = INVALID_MODIFIERS;
864 /* As per docs, should return zero */
871 int wheel_number = 0; /* Only one scroll wheel on windows */
872 #if defined(_WIN32_WCE)
873 int modkeys = LOWORD(wParam);
874 short ticks = (short)HIWORD(wParam);
875 /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
876 xPos = LOWORD(lParam); -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
877 yPos = HIWORD(lParam);
880 /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
881 short ticks = GET_WHEEL_DELTA_WPARAM( wParam );
882 /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
883 window->State.MouseX = GET_X_LPARAM( lParam );
884 window->State.MouseY = GET_Y_LPARAM( lParam );
886 #endif /* defined(_WIN32_WCE) */
888 window = fghWindowUnderCursor(window);
890 fgState.MouseWheelTicks += ticks;
891 if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
893 int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
895 if( ! FETCH_WCB( *window, MouseWheel ) &&
896 ! FETCH_WCB( *window, Mouse ) )
899 fgSetWindow( window );
900 fgState.Modifiers = fgPlatformGetModifiers( );
902 while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
904 if( FETCH_WCB( *window, MouseWheel ) )
905 INVOKE_WCB( *window, MouseWheel,
908 window->State.MouseX,
912 else /* No mouse wheel, call the mouse button callback twice */
915 * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
916 * " " one +1 to 5, -1 to 6, ...
918 * XXX The below assumes that you have no more than 3 mouse
919 * XXX buttons. Sorry.
921 int button = wheel_number * 2 + 3;
924 INVOKE_WCB( *window, Mouse,
926 window->State.MouseX, window->State.MouseY )
928 INVOKE_WCB( *window, Mouse,
930 window->State.MouseX, window->State.MouseY )
934 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
937 fgState.Modifiers = INVALID_MODIFIERS;
939 /* Per docs, should return zero */
947 window = fghWindowUnderCursor(window);
948 lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
955 window = fghWindowUnderCursor(window);
956 lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
963 window = fghWindowUnderCursor(window);
965 if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
968 fgState.Modifiers = fgPlatformGetModifiers( );
969 INVOKE_WCB( *window, Keyboard,
971 window->State.MouseX, window->State.MouseY )
973 fgState.Modifiers = INVALID_MODIFIERS;
977 case WM_CAPTURECHANGED:
978 if (!lParam || !fgWindowByHandle((HWND)lParam))
979 /* Capture released or capture taken by non-FreeGLUT window */
980 setCaptureActive = 0;
981 /* Docs advise a redraw */
982 InvalidateRect( hWnd, NULL, GL_FALSE );
984 lRet = 0; /* Per docs, should return zero */
987 #if !defined(_WIN32_WCE)
988 case WM_SYNCPAINT: /* 0x0088 */
989 /* Another window has moved, need to update this one */
990 window->State.Redisplay = GL_TRUE;
991 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
992 /* Help screen says this message must be passed to "DefWindowProc" */
995 case WM_DISPLAYCHANGE: /* 0x007E */
996 /* The system display resolution/depth has changed */
997 fgDisplay.ScreenWidth = LOWORD(lParam);
998 fgDisplay.ScreenHeight = HIWORD(lParam);
1001 case WM_SYSCOMMAND : /* 0x0112 */
1004 * We have received a system command message. Try to act on it.
1005 * The commands are passed in through the "wParam" parameter:
1006 * The least significant digit seems to be which edge of the window
1007 * is being used for a resize event:
1011 * Congratulations and thanks to Richard Rauch for figuring this out..
1013 switch ( wParam & 0xfff0 )
1022 /* User has clicked on the "-" to minimize the window */
1023 /* Turning off the visibility is handled in WM_SIZE handler */
1030 case SC_NEXTWINDOW :
1033 case SC_PREVWINDOW :
1037 /* Followed very closely by a WM_CLOSE message */
1061 case SC_SCREENSAVE :
1067 #if(WINVER >= 0x0400)
1071 case SC_MONITORPOWER :
1074 case SC_CONTEXTHELP :
1076 #endif /* WINVER >= 0x0400 */
1080 fgWarning( "Unknown wParam type 0x%x", wParam );
1085 #endif /* !defined(_WIN32_WCE) */
1087 /* We need to pass the message on to the operating system as well */
1088 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1092 /* handle multi-touch messages */
1095 unsigned int numInputs = (unsigned int)wParam;
1097 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1099 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1100 fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1101 fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1104 if (!fghGetTouchInputInfo) {
1109 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1110 /* Handle each contact point */
1111 for (i = 0; i < numInputs; ++i ) {
1114 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1115 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1116 ScreenToClient( hWnd, &tp );
1118 ti[i].dwID = ti[i].dwID * 2;
1120 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1121 INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_ENTERED ) );
1122 INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1123 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1124 INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1125 } else if (ti[i].dwFlags & TOUCHEVENTF_UP) {
1126 INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1127 INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_LEFT ) );
1131 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1133 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1138 /* Handle unhandled messages */
1139 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1147 /* Step through the work list */
1148 void fgPlatformProcessWork(SFG_Window *window)
1150 unsigned int workMask = window->State.WorkMask;
1151 /* Now clear it so that any callback generated by the actions below can set work again */
1152 window->State.WorkMask = 0;
1154 /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc
1155 * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when
1156 * they are opened, and work is done before displaying in the mainloop.
1158 if (workMask & GLUT_INIT_WORK)
1162 /* Notify windowStatus/visibility */
1163 fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE);
1165 /* get and notify window's position */
1166 GetWindowRect(window->Window.Handle,&windowRect);
1167 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE);
1169 /* get and notify window's size */
1170 GetClientRect(window->Window.Handle,&windowRect);
1171 fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE);
1173 /* Call init context callback */
1174 INVOKE_WCB( *window, InitContext, ());
1176 /* Lastly, check if we have a display callback, error out if not
1177 * This is the right place to do it, as the redisplay will be
1178 * next right after we exit this function, so there is no more
1179 * opportunity for the user to register a callback for this window.
1181 if (!FETCH_WCB(*window, Display))
1182 fgError ( "ERROR: No display callback registered for window %d\n", window->ID );
1185 /* On windows we can position, resize and change z order at the same time */
1186 if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
1188 UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1189 HWND insertAfter = HWND_TOP;
1192 #if !defined(_WIN32_WCE) /* FIXME: what about WinCE */
1193 if (workMask & GLUT_FULL_SCREEN_WORK)
1195 /* This asks us to toggle fullscreen mode */
1196 flags |= SWP_FRAMECHANGED;
1198 if (window->State.IsFullscreen)
1200 /* If we are fullscreen, resize the current window back to its original size */
1201 /* printf("OldRect %i,%i to %i,%i\n",window->State.pWState.OldRect.left,window->State.pWState.OldRect.top,window->State.pWState.OldRect.right,window->State.pWState.OldRect.bottom); */
1203 /* restore style of window before making it fullscreen */
1204 SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle);
1205 SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx);
1207 /* Then set up resize/reposition, unless user already queued up reshape/position work */
1208 if (!(workMask & GLUT_POSITION_WORK))
1210 workMask |= GLUT_POSITION_WORK;
1211 window->State.DesiredXpos = window->State.pWState.OldRect.left;
1212 window->State.DesiredYpos = window->State.pWState.OldRect.top;
1214 if (!(workMask & GLUT_SIZE_WORK))
1216 workMask |= GLUT_SIZE_WORK;
1217 window->State.DesiredWidth = window->State.pWState.OldRect.right - window->State.pWState.OldRect.left;
1218 window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top;
1221 /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */
1225 /* we are currently not fullscreen, go to fullscreen:
1226 * remove window decoration and then maximize
1232 /* save current window rect, style, exstyle and maximized state */
1233 window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle);
1234 if (window->State.pWState.OldMaximized)
1235 /* We force the window into restored mode before going
1236 * fullscreen because Windows doesn't seem to hide the
1237 * taskbar if the window is in the maximized state.
1239 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
1241 fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE );
1242 window->State.pWState.OldStyle = GetWindowLong(window->Window.Handle, GWL_STYLE);
1243 window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
1245 /* remove decorations from style */
1246 SetWindowLong(window->Window.Handle, GWL_STYLE,
1247 window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME));
1248 SetWindowLong(window->Window.Handle, GWL_EXSTYLE,
1249 window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME |
1250 WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1252 /* For fullscreen mode, find the monitor that is covered the most
1253 * by the window and get its rect as the resize target.
1255 GetWindowRect(window->Window.Handle, &rect);
1256 hMonitor= MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
1257 mi.cbSize = sizeof(mi);
1258 GetMonitorInfo(hMonitor, &mi);
1259 rect = mi.rcMonitor;
1261 /* then setup window resize, overwriting other work queued on the window */
1262 window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK;
1263 window->State.WorkMask &= ~GLUT_ZORDER_WORK;
1264 window->State.DesiredXpos = rect.left;
1265 window->State.DesiredYpos = rect.top;
1266 window->State.DesiredWidth = rect.right - rect.left;
1267 window->State.DesiredHeight = rect.bottom - rect.top;
1270 #endif /*!defined(_WIN32_WCE) */
1272 /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */
1274 /* get rect describing window's current position and size,
1275 * in screen coordinates and in FreeGLUT format
1276 * (size (right-left, bottom-top) is client area size, top and left
1277 * are outside of window including decorations).
1279 fghGetClientArea( &clientRect, window, TRUE );
1281 if (workMask & GLUT_POSITION_WORK)
1283 flags &= ~SWP_NOMOVE;
1285 /* Move rect so that top-left is at requested position */
1286 /* This also automatically makes sure that child window requested coordinates are relative
1287 * to top-left of parent's client area (needed input for SetWindowPos on child windows),
1288 * so no need to further correct rect for child windows below (childs don't have decorations either).
1290 OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top);
1292 if (workMask & GLUT_SIZE_WORK)
1294 flags &= ~SWP_NOSIZE;
1296 /* Note on maximizing behavior of Windows: the resize borders are off
1297 * the screen such that the client area extends all the way from the
1298 * leftmost corner to the rightmost corner to maximize screen real
1299 * estate. A caption is still shown however to allow interaction with
1300 * the window controls. This is default behavior of Windows that
1301 * FreeGLUT sticks with. To alter, one would have to check if
1302 * WS_MAXIMIZE style is set when a resize event is triggered, and
1303 * then manually correct the windowRect to put the borders back on
1307 /* Set new size of window, WxH specify client area */
1308 clientRect.right = clientRect.left + window->State.DesiredWidth;
1309 clientRect.bottom = clientRect.top + window->State.DesiredHeight;
1311 if (workMask & GLUT_ZORDER_WORK)
1313 flags &= ~SWP_NOZORDER;
1315 /* Could change this to push it down or up one window at a time with some
1316 * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT.
1317 * What would be consistent with X11? Win32 GLUT does what we do here...
1319 if (window->State.DesiredZOrder < 0)
1320 insertAfter = HWND_BOTTOM;
1324 /* Adjust for window decorations
1325 * Child windows don't have decoration, so no need to correct
1327 if (!window->Parent)
1328 /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
1329 fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE);
1331 /* Do the requested positioning, moving, and z order push/pop. */
1332 SetWindowPos( window->Window.Handle,
1334 clientRect.left, clientRect.top,
1335 clientRect.right - clientRect.left,
1336 clientRect.bottom- clientRect.top,
1340 /* Finish off the fullscreen operation we were doing, if any */
1341 if (workMask & GLUT_FULL_SCREEN_WORK)
1343 if (window->State.IsFullscreen)
1345 /* leaving fullscreen, restore maximized state, if any */
1346 if (window->State.pWState.OldMaximized)
1347 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1349 window->State.IsFullscreen = GL_FALSE;
1352 window->State.IsFullscreen = GL_TRUE;
1356 if (workMask & GLUT_VISIBILITY_WORK)
1358 /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */
1360 SFG_Window *win = window;
1361 switch (window->State.DesiredVisibility)
1363 case DesireHiddenState:
1366 case DesireIconicState:
1367 cmdShow = SW_MINIMIZE;
1368 /* Call on top-level window */
1372 case DesireNormalState:
1374 cmdShow = SW_SHOWNOACTIVATE; /* Just show, don't activate if its a menu */
1380 ShowWindow( win->Window.Handle, cmdShow );