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();
594 //printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam));
595 if (LOWORD(wParam) != WA_INACTIVE)
597 /* printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
598 window->State.Cursor ); */
599 fgSetCursor( window, window->State.Cursor );
602 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
607 /* printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
608 if( LOWORD( lParam ) == HTCLIENT )
610 if (!window->State.pWState.MouseTracking)
614 /* Cursor just entered window, set cursor look */
615 fgSetCursor ( window, window->State.Cursor ) ;
617 /* If an EntryFunc callback is specified by the user, also
618 * invoke that callback and start mouse tracking so that
619 * we get a WM_MOUSELEAVE message
621 if (FETCH_WCB( *window, Entry ))
623 SFG_Window* saved_window = fgStructure.CurrentWindow;
624 INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
625 fgSetWindow(saved_window);
627 tme.cbSize = sizeof(TRACKMOUSEEVENT);
628 tme.dwFlags = TME_LEAVE;
629 tme.hwndTrack = window->Window.Handle;
630 TrackMouseEvent(&tme);
632 window->State.pWState.MouseTracking = GL_TRUE;
637 /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
638 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
643 /* NB: This message is only received when a EntryFunc callback
644 * is specified by the user, as that is the only condition under
645 * which mouse tracking is setup in WM_SETCURSOR handler above
647 SFG_Window* saved_window = fgStructure.CurrentWindow;
648 INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
649 fgSetWindow(saved_window);
651 window->State.pWState.MouseTracking = GL_FALSE;
652 lRet = 0; /* As per docs, must return zero */
657 /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
660 fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE);
661 window->State.Redisplay = GL_TRUE;
665 fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE);
666 window->State.Redisplay = GL_FALSE;
674 /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
675 if (GetUpdateRect(hWnd,&rect,FALSE))
677 /* Dummy begin/end paint to validate rect that needs
678 * redrawing, then signal that a redisplay is needed.
679 * This allows us full control about when we do any
680 * redrawing, and is the same as what original GLUT
684 BeginPaint( hWnd, &ps );
685 EndPaint( hWnd, &ps );
687 window->State.Redisplay = GL_TRUE;
689 lRet = 0; /* As per docs, should return 0 */
694 fgDestroyWindow ( window );
695 if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
701 * The window already got destroyed, so don't bother with it.
707 /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
708 #if defined(_WIN32_WCE)
709 window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */
710 window->State.MouseY = LOWORD( lParam );
712 window->State.MouseX = GET_X_LPARAM( lParam );
713 window->State.MouseY = GET_Y_LPARAM( lParam );
714 #endif /* defined(_WIN32_WCE) */
715 /* Restrict to [-32768, 32767] to match X11 behaviour */
716 /* See comment in "freeglut_developer" mailing list 10/4/04 */
717 if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
718 if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
720 if ( window->ActiveMenu )
722 fgUpdateMenuHighlight( window->ActiveMenu );
726 fgState.Modifiers = fgPlatformGetModifiers( );
728 if( ( wParam & MK_LBUTTON ) ||
729 ( wParam & MK_MBUTTON ) ||
730 ( wParam & MK_RBUTTON ) )
731 INVOKE_WCB( *window, Motion, ( window->State.MouseX,
732 window->State.MouseY ) );
734 INVOKE_WCB( *window, Passive, ( window->State.MouseX,
735 window->State.MouseY ) );
737 fgState.Modifiers = INVALID_MODIFIERS;
748 GLboolean pressed = GL_TRUE;
751 /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
752 #if defined(_WIN32_WCE)
753 window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */
754 window->State.MouseY = LOWORD( lParam );
756 window->State.MouseX = GET_X_LPARAM( lParam );
757 window->State.MouseY = GET_Y_LPARAM( lParam );
758 #endif /* defined(_WIN32_WCE) */
760 /* Restrict to [-32768, 32767] to match X11 behaviour */
761 /* See comment in "freeglut_developer" mailing list 10/4/04 */
762 if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
763 if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
769 button = GLUT_LEFT_BUTTON;
773 button = GLUT_MIDDLE_BUTTON;
777 button = GLUT_RIGHT_BUTTON;
781 button = GLUT_LEFT_BUTTON;
785 button = GLUT_MIDDLE_BUTTON;
789 button = GLUT_RIGHT_BUTTON;
797 #if !defined(_WIN32_WCE)
798 if( GetSystemMetrics( SM_SWAPBUTTON ) )
800 if( button == GLUT_LEFT_BUTTON )
801 button = GLUT_RIGHT_BUTTON;
803 if( button == GLUT_RIGHT_BUTTON )
804 button = GLUT_LEFT_BUTTON;
806 #endif /* !defined(_WIN32_WCE) */
809 return DefWindowProc( hWnd, uMsg, lParam, wParam );
812 * Do not execute the application's mouse callback if a menu
813 * is hooked to this button. In that case an appropriate
814 * private call should be generated.
816 if( fgCheckActiveMenu( window, button, pressed,
817 window->State.MouseX, window->State.MouseY ) )
820 /* Set capture so that the window captures all the mouse messages
822 * The mouse is not released from the window until all buttons have
823 * been released, even if the user presses a button in another window.
824 * This is consistent with the behavior on X11.
826 if ( pressed == GL_TRUE )
828 if (!setCaptureActive)
829 SetCapture ( window->Window.Handle ) ;
830 setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
832 else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
833 /* Make sure all mouse buttons are released before releasing capture */
836 if( ! FETCH_WCB( *window, Mouse ) )
839 fgSetWindow( window );
840 fgState.Modifiers = fgPlatformGetModifiers( );
845 pressed ? GLUT_DOWN : GLUT_UP,
846 window->State.MouseX,
851 fgState.Modifiers = INVALID_MODIFIERS;
853 /* As per docs, should return zero */
860 int wheel_number = 0; /* Only one scroll wheel on windows */
861 #if defined(_WIN32_WCE)
862 int modkeys = LOWORD(wParam);
863 short ticks = (short)HIWORD(wParam);
864 /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
865 xPos = LOWORD(lParam); -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
866 yPos = HIWORD(lParam);
869 /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
870 short ticks = GET_WHEEL_DELTA_WPARAM( wParam );
871 /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
872 window->State.MouseX = GET_X_LPARAM( lParam );
873 window->State.MouseY = GET_Y_LPARAM( lParam );
875 #endif /* defined(_WIN32_WCE) */
877 window = fghWindowUnderCursor(window);
879 fgState.MouseWheelTicks += ticks;
880 if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
882 int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
884 if( ! FETCH_WCB( *window, MouseWheel ) &&
885 ! FETCH_WCB( *window, Mouse ) )
888 fgSetWindow( window );
889 fgState.Modifiers = fgPlatformGetModifiers( );
891 while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
893 if( FETCH_WCB( *window, MouseWheel ) )
894 INVOKE_WCB( *window, MouseWheel,
897 window->State.MouseX,
901 else /* No mouse wheel, call the mouse button callback twice */
904 * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
905 * " " one +1 to 5, -1 to 6, ...
907 * XXX The below assumes that you have no more than 3 mouse
908 * XXX buttons. Sorry.
910 int button = wheel_number * 2 + 3;
913 INVOKE_WCB( *window, Mouse,
915 window->State.MouseX, window->State.MouseY )
917 INVOKE_WCB( *window, Mouse,
919 window->State.MouseX, window->State.MouseY )
923 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
926 fgState.Modifiers = INVALID_MODIFIERS;
928 /* Per docs, should return zero */
936 window = fghWindowUnderCursor(window);
937 lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
944 window = fghWindowUnderCursor(window);
945 lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
952 window = fghWindowUnderCursor(window);
954 if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
957 fgState.Modifiers = fgPlatformGetModifiers( );
958 INVOKE_WCB( *window, Keyboard,
960 window->State.MouseX, window->State.MouseY )
962 fgState.Modifiers = INVALID_MODIFIERS;
966 case WM_CAPTURECHANGED:
967 if (!lParam || !fgWindowByHandle((HWND)lParam))
968 /* Capture released or capture taken by non-FreeGLUT window */
969 setCaptureActive = 0;
970 /* Docs advise a redraw */
971 InvalidateRect( hWnd, NULL, GL_FALSE );
973 lRet = 0; /* Per docs, should return zero */
976 #if !defined(_WIN32_WCE)
977 case WM_SYNCPAINT: /* 0x0088 */
978 /* Another window has moved, need to update this one */
979 window->State.Redisplay = GL_TRUE;
980 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
981 /* Help screen says this message must be passed to "DefWindowProc" */
984 case WM_SYSCOMMAND : /* 0x0112 */
987 * We have received a system command message. Try to act on it.
988 * The commands are passed in through the "wParam" parameter:
989 * The least significant digit seems to be which edge of the window
990 * is being used for a resize event:
994 * Congratulations and thanks to Richard Rauch for figuring this out..
996 switch ( wParam & 0xfff0 )
1005 /* User has clicked on the "-" to minimize the window */
1006 /* Turning off the visibility is handled in WM_SIZE handler */
1013 case SC_NEXTWINDOW :
1016 case SC_PREVWINDOW :
1020 /* Followed very closely by a WM_CLOSE message */
1044 case SC_SCREENSAVE :
1050 #if(WINVER >= 0x0400)
1054 case SC_MONITORPOWER :
1057 case SC_CONTEXTHELP :
1059 #endif /* WINVER >= 0x0400 */
1063 fgWarning( "Unknown wParam type 0x%x", wParam );
1068 #endif /* !defined(_WIN32_WCE) */
1070 /* We need to pass the message on to the operating system as well */
1071 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1075 /* handle multi-touch messages */
1078 unsigned int numInputs = (unsigned int)wParam;
1080 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1082 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1083 fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1084 fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1087 if (!fghGetTouchInputInfo) {
1092 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1093 /* Handle each contact point */
1094 for (i = 0; i < numInputs; ++i ) {
1097 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1098 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1099 ScreenToClient( hWnd, &tp );
1101 ti[i].dwID = ti[i].dwID * 2;
1103 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1104 INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_ENTERED ) );
1105 INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1106 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1107 INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1108 } else if (ti[i].dwFlags & TOUCHEVENTF_UP) {
1109 INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1110 INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_LEFT ) );
1114 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1116 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1121 /* Handle unhandled messages */
1122 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1130 /* Step through the work list */
1131 void fgPlatformProcessWork(SFG_Window *window)
1133 unsigned int workMask = window->State.WorkMask;
1134 /* Now clear it so that any callback generated by the actions below can set work again */
1135 window->State.WorkMask = 0;
1137 /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc
1138 * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when
1139 * they are opened, and work is done before displaying in the mainloop.
1141 if (workMask & GLUT_INIT_WORK)
1145 /* Notify windowStatus/visibility */
1146 fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE);
1148 /* get and notify window's position */
1149 GetWindowRect(window->Window.Handle,&windowRect);
1150 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE);
1152 /* get and notify window's size */
1153 GetClientRect(window->Window.Handle,&windowRect);
1154 fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE);
1156 /* Call init context callback */
1157 INVOKE_WCB( *window, InitContext, ());
1159 /* Lastly, check if we have a display callback, error out if not
1160 * This is the right place to do it, as the redisplay will be
1161 * next right after we exit this function, so there is no more
1162 * opportunity for the user to register a callback for this window.
1164 if (!FETCH_WCB(*window, Display))
1165 fgError ( "ERROR: No display callback registered for window %d\n", window->ID );
1168 /* On windows we can position, resize and change z order at the same time */
1169 if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
1171 UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1172 HWND insertAfter = HWND_TOP;
1175 #if !defined(_WIN32_WCE) /* FIXME: what about WinCE */
1176 if (workMask & GLUT_FULL_SCREEN_WORK)
1178 /* This asks us to toggle fullscreen mode */
1179 flags |= SWP_FRAMECHANGED;
1181 if (window->State.IsFullscreen)
1183 /* If we are fullscreen, resize the current window back to its original size */
1184 /* 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); */
1186 /* restore style of window before making it fullscreen */
1187 SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle);
1188 SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx);
1190 /* Then set up resize/reposition, unless user already queued up reshape/position work */
1191 if (!(workMask & GLUT_POSITION_WORK))
1193 workMask |= GLUT_POSITION_WORK;
1194 window->State.DesiredXpos = window->State.pWState.OldRect.left;
1195 window->State.DesiredYpos = window->State.pWState.OldRect.top;
1197 if (!(workMask & GLUT_SIZE_WORK))
1199 workMask |= GLUT_SIZE_WORK;
1200 window->State.DesiredWidth = window->State.pWState.OldRect.right - window->State.pWState.OldRect.left;
1201 window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top;
1204 /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */
1208 /* we are currently not fullscreen, go to fullscreen:
1209 * remove window decoration and then maximize
1215 /* save current window rect, style, exstyle and maximized state */
1216 window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle);
1217 if (window->State.pWState.OldMaximized)
1218 /* We force the window into restored mode before going
1219 * fullscreen because Windows doesn't seem to hide the
1220 * taskbar if the window is in the maximized state.
1222 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
1224 fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE );
1225 window->State.pWState.OldStyle = GetWindowLong(window->Window.Handle, GWL_STYLE);
1226 window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
1228 /* remove decorations from style */
1229 SetWindowLong(window->Window.Handle, GWL_STYLE,
1230 window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME));
1231 SetWindowLong(window->Window.Handle, GWL_EXSTYLE,
1232 window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME |
1233 WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1235 /* For fullscreen mode, find the monitor that is covered the most
1236 * by the window and get its rect as the resize target.
1238 GetWindowRect(window->Window.Handle, &rect);
1239 hMonitor= MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
1240 mi.cbSize = sizeof(mi);
1241 GetMonitorInfo(hMonitor, &mi);
1242 rect = mi.rcMonitor;
1244 /* then setup window resize, overwriting other work queued on the window */
1245 window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK;
1246 window->State.WorkMask &= ~GLUT_ZORDER_WORK;
1247 window->State.DesiredXpos = rect.left;
1248 window->State.DesiredYpos = rect.top;
1249 window->State.DesiredWidth = rect.right - rect.left;
1250 window->State.DesiredHeight = rect.bottom - rect.top;
1253 #endif /*!defined(_WIN32_WCE) */
1255 /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */
1257 /* get rect describing window's current position and size,
1258 * in screen coordinates and in FreeGLUT format
1259 * (size (right-left, bottom-top) is client area size, top and left
1260 * are outside of window including decorations).
1262 fghGetClientArea( &clientRect, window, TRUE );
1264 if (workMask & GLUT_POSITION_WORK)
1266 flags &= ~SWP_NOMOVE;
1268 /* Move rect so that top-left is at requested position */
1269 /* This also automatically makes sure that child window requested coordinates are relative
1270 * to top-left of parent's client area (needed input for SetWindowPos on child windows),
1271 * so no need to further correct rect for child windows below (childs don't have decorations either).
1273 OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top);
1275 if (workMask & GLUT_SIZE_WORK)
1277 flags &= ~SWP_NOSIZE;
1279 /* Note on maximizing behavior of Windows: the resize borders are off
1280 * the screen such that the client area extends all the way from the
1281 * leftmost corner to the rightmost corner to maximize screen real
1282 * estate. A caption is still shown however to allow interaction with
1283 * the window controls. This is default behavior of Windows that
1284 * FreeGLUT sticks with. To alter, one would have to check if
1285 * WS_MAXIMIZE style is set when a resize event is triggered, and
1286 * then manually correct the windowRect to put the borders back on
1290 /* Set new size of window, WxH specify client area */
1291 clientRect.right = clientRect.left + window->State.DesiredWidth;
1292 clientRect.bottom = clientRect.top + window->State.DesiredHeight;
1294 if (workMask & GLUT_ZORDER_WORK)
1296 flags &= ~SWP_NOZORDER;
1298 /* Could change this to push it down or up one window at a time with some
1299 * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT.
1300 * What would be consistent with X11? Win32 GLUT does what we do here...
1302 if (window->State.DesiredZOrder < 0)
1303 insertAfter = HWND_BOTTOM;
1307 /* Adjust for window decorations
1308 * Child windows don't have decoration, so no need to correct
1310 if (!window->Parent)
1311 /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
1312 fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE);
1314 /* Do the requested positioning, moving, and z order push/pop. */
1315 SetWindowPos( window->Window.Handle,
1317 clientRect.left, clientRect.top,
1318 clientRect.right - clientRect.left,
1319 clientRect.bottom- clientRect.top,
1323 /* Finish off the fullscreen operation we were doing, if any */
1324 if (workMask & GLUT_FULL_SCREEN_WORK)
1326 if (window->State.IsFullscreen)
1328 /* leaving fullscreen, restore maximized state, if any */
1329 if (window->State.pWState.OldMaximized)
1330 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1332 window->State.IsFullscreen = GL_FALSE;
1335 window->State.IsFullscreen = GL_TRUE;
1339 if (workMask & GLUT_VISIBILITY_WORK)
1341 /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */
1343 SFG_Window *win = window;
1344 switch (window->State.DesiredVisibility)
1346 case DesireHiddenState:
1349 case DesireIconicState:
1350 cmdShow = SW_MINIMIZE;
1351 /* Call on top-level window */
1355 case DesireNormalState:
1357 cmdShow = SW_SHOWNOACTIVATE; /* Just show, don't activate if its a menu */
1363 ShowWindow( win->Window.Handle, cmdShow );