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(HWND newFocusWnd);
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 */
67 * WM_ message to string, for debugging
68 * This is taken from the 8.0 SDK, so Windows 8 API and everything earlier is included
75 #define DEFINE_MESSAGE(wm){ wm, #wm }
76 static struct WM_MESSAGE_MAP allMessages[] =
78 DEFINE_MESSAGE(WM_NULL),
79 DEFINE_MESSAGE(WM_CREATE),
80 DEFINE_MESSAGE(WM_DESTROY),
81 DEFINE_MESSAGE(WM_MOVE),
82 DEFINE_MESSAGE(WM_SIZE),
84 DEFINE_MESSAGE(WM_ACTIVATE),
85 DEFINE_MESSAGE(WM_SETFOCUS),
86 DEFINE_MESSAGE(WM_KILLFOCUS),
87 DEFINE_MESSAGE(WM_ENABLE),
88 DEFINE_MESSAGE(WM_SETREDRAW),
89 DEFINE_MESSAGE(WM_SETTEXT),
90 DEFINE_MESSAGE(WM_GETTEXT),
91 DEFINE_MESSAGE(WM_GETTEXTLENGTH),
92 DEFINE_MESSAGE(WM_PAINT),
93 DEFINE_MESSAGE(WM_CLOSE),
95 DEFINE_MESSAGE(WM_QUERYENDSESSION),
96 DEFINE_MESSAGE(WM_QUERYOPEN),
97 DEFINE_MESSAGE(WM_ENDSESSION),
99 DEFINE_MESSAGE(WM_QUIT),
100 DEFINE_MESSAGE(WM_ERASEBKGND),
101 DEFINE_MESSAGE(WM_SYSCOLORCHANGE),
102 DEFINE_MESSAGE(WM_SHOWWINDOW),
103 DEFINE_MESSAGE(WM_WININICHANGE),
105 DEFINE_MESSAGE(WM_DEVMODECHANGE),
106 DEFINE_MESSAGE(WM_ACTIVATEAPP),
107 DEFINE_MESSAGE(WM_FONTCHANGE),
108 DEFINE_MESSAGE(WM_TIMECHANGE),
109 DEFINE_MESSAGE(WM_CANCELMODE),
110 DEFINE_MESSAGE(WM_SETCURSOR),
111 DEFINE_MESSAGE(WM_MOUSEACTIVATE),
112 DEFINE_MESSAGE(WM_CHILDACTIVATE),
113 DEFINE_MESSAGE(WM_QUEUESYNC),
115 DEFINE_MESSAGE(WM_GETMINMAXINFO),
117 DEFINE_MESSAGE(WM_PAINTICON),
118 DEFINE_MESSAGE(WM_ICONERASEBKGND),
119 DEFINE_MESSAGE(WM_NEXTDLGCTL),
120 DEFINE_MESSAGE(WM_SPOOLERSTATUS),
121 DEFINE_MESSAGE(WM_DRAWITEM),
122 DEFINE_MESSAGE(WM_MEASUREITEM),
123 DEFINE_MESSAGE(WM_DELETEITEM),
124 DEFINE_MESSAGE(WM_VKEYTOITEM),
125 DEFINE_MESSAGE(WM_CHARTOITEM),
126 DEFINE_MESSAGE(WM_SETFONT),
127 DEFINE_MESSAGE(WM_GETFONT),
128 DEFINE_MESSAGE(WM_SETHOTKEY),
129 DEFINE_MESSAGE(WM_GETHOTKEY),
130 DEFINE_MESSAGE(WM_QUERYDRAGICON),
131 DEFINE_MESSAGE(WM_COMPAREITEM),
132 # if(WINVER >= 0x0500)
134 DEFINE_MESSAGE(WM_GETOBJECT),
136 # endif /* WINVER >= 0x0500 */
137 DEFINE_MESSAGE(WM_COMPACTING),
138 DEFINE_MESSAGE(WM_COMMNOTIFY),
139 DEFINE_MESSAGE(WM_WINDOWPOSCHANGING),
140 DEFINE_MESSAGE(WM_WINDOWPOSCHANGED),
142 DEFINE_MESSAGE(WM_POWER),
144 DEFINE_MESSAGE(WM_COPYDATA),
145 DEFINE_MESSAGE(WM_CANCELJOURNAL),
147 # if(WINVER >= 0x0400)
148 DEFINE_MESSAGE(WM_NOTIFY),
149 DEFINE_MESSAGE(WM_INPUTLANGCHANGEREQUEST),
150 DEFINE_MESSAGE(WM_INPUTLANGCHANGE),
151 DEFINE_MESSAGE(WM_TCARD),
152 DEFINE_MESSAGE(WM_HELP),
153 DEFINE_MESSAGE(WM_USERCHANGED),
154 DEFINE_MESSAGE(WM_NOTIFYFORMAT),
156 DEFINE_MESSAGE(WM_CONTEXTMENU),
157 DEFINE_MESSAGE(WM_STYLECHANGING),
158 DEFINE_MESSAGE(WM_STYLECHANGED),
159 DEFINE_MESSAGE(WM_DISPLAYCHANGE),
160 DEFINE_MESSAGE(WM_GETICON),
161 DEFINE_MESSAGE(WM_SETICON),
162 # endif /* WINVER >= 0x0400 */
164 DEFINE_MESSAGE(WM_NCCREATE),
165 DEFINE_MESSAGE(WM_NCDESTROY),
166 DEFINE_MESSAGE(WM_NCCALCSIZE),
167 DEFINE_MESSAGE(WM_NCHITTEST),
168 DEFINE_MESSAGE(WM_NCPAINT),
169 DEFINE_MESSAGE(WM_NCACTIVATE),
170 DEFINE_MESSAGE(WM_GETDLGCODE),
172 DEFINE_MESSAGE(WM_SYNCPAINT),
174 DEFINE_MESSAGE(WM_NCMOUSEMOVE),
175 DEFINE_MESSAGE(WM_NCLBUTTONDOWN),
176 DEFINE_MESSAGE(WM_NCLBUTTONUP),
177 DEFINE_MESSAGE(WM_NCLBUTTONDBLCLK),
178 DEFINE_MESSAGE(WM_NCRBUTTONDOWN),
179 DEFINE_MESSAGE(WM_NCRBUTTONUP),
180 DEFINE_MESSAGE(WM_NCRBUTTONDBLCLK),
181 DEFINE_MESSAGE(WM_NCMBUTTONDOWN),
182 DEFINE_MESSAGE(WM_NCMBUTTONUP),
183 DEFINE_MESSAGE(WM_NCMBUTTONDBLCLK),
187 # if(_WIN32_WINNT >= 0x0500) && defined(WM_NCXBUTTONDOWN)
188 DEFINE_MESSAGE(WM_NCXBUTTONDOWN),
189 DEFINE_MESSAGE(WM_NCXBUTTONUP),
190 DEFINE_MESSAGE(WM_NCXBUTTONDBLCLK),
191 # endif /* _WIN32_WINNT >= 0x0500 */
194 # if(_WIN32_WINNT >= 0x0501)
195 DEFINE_MESSAGE(WM_INPUT_DEVICE_CHANGE),
196 # endif /* _WIN32_WINNT >= 0x0501 */
198 # if(_WIN32_WINNT >= 0x0501)
199 DEFINE_MESSAGE(WM_INPUT),
200 # endif /* _WIN32_WINNT >= 0x0501 */
202 DEFINE_MESSAGE(WM_KEYDOWN),
203 DEFINE_MESSAGE(WM_KEYUP),
204 DEFINE_MESSAGE(WM_CHAR),
205 DEFINE_MESSAGE(WM_DEADCHAR),
206 DEFINE_MESSAGE(WM_SYSKEYDOWN),
207 DEFINE_MESSAGE(WM_SYSKEYUP),
208 DEFINE_MESSAGE(WM_SYSCHAR),
209 DEFINE_MESSAGE(WM_SYSDEADCHAR),
210 # if(_WIN32_WINNT >= 0x0501)
211 DEFINE_MESSAGE(WM_UNICHAR),
212 # endif /* _WIN32_WINNT >= 0x0501 */
214 # if(WINVER >= 0x0400)
215 DEFINE_MESSAGE(WM_IME_STARTCOMPOSITION),
216 DEFINE_MESSAGE(WM_IME_ENDCOMPOSITION),
217 DEFINE_MESSAGE(WM_IME_COMPOSITION),
218 DEFINE_MESSAGE(WM_IME_KEYLAST),
219 # endif /* WINVER >= 0x0400 */
221 DEFINE_MESSAGE(WM_INITDIALOG),
222 DEFINE_MESSAGE(WM_COMMAND),
223 DEFINE_MESSAGE(WM_SYSCOMMAND),
224 DEFINE_MESSAGE(WM_TIMER),
225 DEFINE_MESSAGE(WM_HSCROLL),
226 DEFINE_MESSAGE(WM_VSCROLL),
227 DEFINE_MESSAGE(WM_INITMENU),
228 DEFINE_MESSAGE(WM_INITMENUPOPUP),
229 # if(WINVER >= 0x0601)
230 DEFINE_MESSAGE(WM_GESTURE),
231 DEFINE_MESSAGE(WM_GESTURENOTIFY),
232 # endif /* WINVER >= 0x0601 */
233 DEFINE_MESSAGE(WM_MENUSELECT),
234 DEFINE_MESSAGE(WM_MENUCHAR),
235 DEFINE_MESSAGE(WM_ENTERIDLE),
236 # if(WINVER >= 0x0500)
238 DEFINE_MESSAGE(WM_MENURBUTTONUP),
239 DEFINE_MESSAGE(WM_MENUDRAG),
240 DEFINE_MESSAGE(WM_MENUGETOBJECT),
241 DEFINE_MESSAGE(WM_UNINITMENUPOPUP),
242 DEFINE_MESSAGE(WM_MENUCOMMAND),
244 # if(_WIN32_WINNT >= 0x0500) && defined(WM_CHANGEUISTATE)
245 DEFINE_MESSAGE(WM_CHANGEUISTATE),
246 DEFINE_MESSAGE(WM_UPDATEUISTATE),
247 DEFINE_MESSAGE(WM_QUERYUISTATE),
248 # endif /* _WIN32_WINNT >= 0x0500 */
251 # endif /* WINVER >= 0x0500 */
253 DEFINE_MESSAGE(WM_CTLCOLORMSGBOX),
254 DEFINE_MESSAGE(WM_CTLCOLOREDIT),
255 DEFINE_MESSAGE(WM_CTLCOLORLISTBOX),
256 DEFINE_MESSAGE(WM_CTLCOLORBTN),
257 DEFINE_MESSAGE(WM_CTLCOLORDLG),
258 DEFINE_MESSAGE(WM_CTLCOLORSCROLLBAR),
259 DEFINE_MESSAGE(WM_CTLCOLORSTATIC),
260 # define MN_GETHMENU 0x01E1
262 DEFINE_MESSAGE(WM_MOUSEMOVE),
263 DEFINE_MESSAGE(WM_LBUTTONDOWN),
264 DEFINE_MESSAGE(WM_LBUTTONUP),
265 DEFINE_MESSAGE(WM_LBUTTONDBLCLK),
266 DEFINE_MESSAGE(WM_RBUTTONDOWN),
267 DEFINE_MESSAGE(WM_RBUTTONUP),
268 DEFINE_MESSAGE(WM_RBUTTONDBLCLK),
269 DEFINE_MESSAGE(WM_MBUTTONDOWN),
270 DEFINE_MESSAGE(WM_MBUTTONUP),
271 DEFINE_MESSAGE(WM_MBUTTONDBLCLK),
272 # if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
273 DEFINE_MESSAGE(WM_MOUSEWHEEL),
275 # if (_WIN32_WINNT >= 0x0500) && defined(WM_XBUTTONDOWN)
276 DEFINE_MESSAGE(WM_XBUTTONDOWN),
277 DEFINE_MESSAGE(WM_XBUTTONUP),
278 DEFINE_MESSAGE(WM_XBUTTONDBLCLK),
280 # if (_WIN32_WINNT >= 0x0600)
281 DEFINE_MESSAGE(WM_MOUSEHWHEEL),
286 DEFINE_MESSAGE(WM_PARENTNOTIFY),
287 DEFINE_MESSAGE(WM_ENTERMENULOOP),
288 DEFINE_MESSAGE(WM_EXITMENULOOP),
290 # if(WINVER >= 0x0400)
291 DEFINE_MESSAGE(WM_NEXTMENU),
292 DEFINE_MESSAGE(WM_SIZING),
293 DEFINE_MESSAGE(WM_CAPTURECHANGED),
294 DEFINE_MESSAGE(WM_MOVING),
295 # endif /* WINVER >= 0x0400 */
297 # if(WINVER >= 0x0400)
298 DEFINE_MESSAGE(WM_POWERBROADCAST),
299 DEFINE_MESSAGE(WM_DEVICECHANGE),
300 # endif /* WINVER >= 0x0400 */
302 DEFINE_MESSAGE(WM_MDICREATE),
303 DEFINE_MESSAGE(WM_MDIDESTROY),
304 DEFINE_MESSAGE(WM_MDIACTIVATE),
305 DEFINE_MESSAGE(WM_MDIRESTORE),
306 DEFINE_MESSAGE(WM_MDINEXT),
307 DEFINE_MESSAGE(WM_MDIMAXIMIZE),
308 DEFINE_MESSAGE(WM_MDITILE),
309 DEFINE_MESSAGE(WM_MDICASCADE),
310 DEFINE_MESSAGE(WM_MDIICONARRANGE),
311 DEFINE_MESSAGE(WM_MDIGETACTIVE),
314 DEFINE_MESSAGE(WM_MDISETMENU),
315 DEFINE_MESSAGE(WM_ENTERSIZEMOVE),
316 DEFINE_MESSAGE(WM_EXITSIZEMOVE),
317 DEFINE_MESSAGE(WM_DROPFILES),
318 DEFINE_MESSAGE(WM_MDIREFRESHMENU),
320 # if(WINVER >= 0x0602)
321 DEFINE_MESSAGE(WM_POINTERDEVICECHANGE),
322 DEFINE_MESSAGE(WM_POINTERDEVICEINRANGE),
323 DEFINE_MESSAGE(WM_POINTERDEVICEOUTOFRANGE),
324 # endif /* WINVER >= 0x0602 */
326 # if(WINVER >= 0x0601)
327 DEFINE_MESSAGE(WM_TOUCH),
328 # endif /* WINVER >= 0x0601 */
330 # if(WINVER >= 0x0602)
331 DEFINE_MESSAGE(WM_NCPOINTERUPDATE),
332 DEFINE_MESSAGE(WM_NCPOINTERDOWN),
333 DEFINE_MESSAGE(WM_NCPOINTERUP),
334 DEFINE_MESSAGE(WM_POINTERUPDATE),
335 DEFINE_MESSAGE(WM_POINTERDOWN),
336 DEFINE_MESSAGE(WM_POINTERUP),
337 DEFINE_MESSAGE(WM_POINTERENTER),
338 DEFINE_MESSAGE(WM_POINTERLEAVE),
339 DEFINE_MESSAGE(WM_POINTERACTIVATE),
340 DEFINE_MESSAGE(WM_POINTERCAPTURECHANGED),
341 DEFINE_MESSAGE(WM_TOUCHHITTESTING),
342 DEFINE_MESSAGE(WM_POINTERWHEEL),
343 DEFINE_MESSAGE(WM_POINTERHWHEEL),
344 # endif /* WINVER >= 0x0602 */
347 # if(WINVER >= 0x0400)
348 DEFINE_MESSAGE(WM_IME_SETCONTEXT),
349 DEFINE_MESSAGE(WM_IME_NOTIFY),
350 DEFINE_MESSAGE(WM_IME_CONTROL),
351 DEFINE_MESSAGE(WM_IME_COMPOSITIONFULL),
352 DEFINE_MESSAGE(WM_IME_SELECT),
353 DEFINE_MESSAGE(WM_IME_CHAR),
354 # endif /* WINVER >= 0x0400 */
355 # if(WINVER >= 0x0500)
356 DEFINE_MESSAGE(WM_IME_REQUEST),
357 # endif /* WINVER >= 0x0500 */
358 # if(WINVER >= 0x0400)
359 DEFINE_MESSAGE(WM_IME_KEYDOWN),
360 DEFINE_MESSAGE(WM_IME_KEYUP),
361 # endif /* WINVER >= 0x0400 */
363 # if((_WIN32_WINNT >= 0x0400) || (WINVER >= 0x0500))
364 DEFINE_MESSAGE(WM_MOUSEHOVER),
365 DEFINE_MESSAGE(WM_MOUSELEAVE),
367 # if(WINVER >= 0x0500) && defined(WM_NCMOUSEHOVER)
368 DEFINE_MESSAGE(WM_NCMOUSEHOVER),
369 DEFINE_MESSAGE(WM_NCMOUSELEAVE),
370 # endif /* WINVER >= 0x0500 */
372 # if(_WIN32_WINNT >= 0x0501)
373 DEFINE_MESSAGE(WM_WTSSESSION_CHANGE),
374 # endif /* _WIN32_WINNT >= 0x0501 */
376 DEFINE_MESSAGE(WM_CUT),
377 DEFINE_MESSAGE(WM_COPY),
378 DEFINE_MESSAGE(WM_PASTE),
379 DEFINE_MESSAGE(WM_CLEAR),
380 DEFINE_MESSAGE(WM_UNDO),
381 DEFINE_MESSAGE(WM_RENDERFORMAT),
382 DEFINE_MESSAGE(WM_RENDERALLFORMATS),
383 DEFINE_MESSAGE(WM_DESTROYCLIPBOARD),
384 DEFINE_MESSAGE(WM_DRAWCLIPBOARD),
385 DEFINE_MESSAGE(WM_PAINTCLIPBOARD),
386 DEFINE_MESSAGE(WM_VSCROLLCLIPBOARD),
387 DEFINE_MESSAGE(WM_SIZECLIPBOARD),
388 DEFINE_MESSAGE(WM_ASKCBFORMATNAME),
389 DEFINE_MESSAGE(WM_CHANGECBCHAIN),
390 DEFINE_MESSAGE(WM_HSCROLLCLIPBOARD),
391 DEFINE_MESSAGE(WM_QUERYNEWPALETTE),
392 DEFINE_MESSAGE(WM_PALETTEISCHANGING),
393 DEFINE_MESSAGE(WM_PALETTECHANGED),
394 DEFINE_MESSAGE(WM_HOTKEY),
396 # if(WINVER >= 0x0400)
397 DEFINE_MESSAGE(WM_PRINT),
398 DEFINE_MESSAGE(WM_PRINTCLIENT),
399 # endif /* WINVER >= 0x0400 */
401 # if(_WIN32_WINNT >= 0x0500) && defined(WM_APPCOMMAND)
402 DEFINE_MESSAGE(WM_APPCOMMAND),
403 # endif /* _WIN32_WINNT >= 0x0500 */
405 # if(_WIN32_WINNT >= 0x0501)
406 DEFINE_MESSAGE(WM_THEMECHANGED),
407 # endif /* _WIN32_WINNT >= 0x0501 */
410 # if(_WIN32_WINNT >= 0x0501)
411 DEFINE_MESSAGE(WM_CLIPBOARDUPDATE),
412 # endif /* _WIN32_WINNT >= 0x0501 */
414 # if(_WIN32_WINNT >= 0x0600)
415 DEFINE_MESSAGE(WM_DWMCOMPOSITIONCHANGED),
416 DEFINE_MESSAGE(WM_DWMNCRENDERINGCHANGED),
417 DEFINE_MESSAGE(WM_DWMCOLORIZATIONCOLORCHANGED),
418 DEFINE_MESSAGE(WM_DWMWINDOWMAXIMIZEDCHANGE),
419 # endif /* _WIN32_WINNT >= 0x0600 */
421 # if(_WIN32_WINNT >= 0x0601)
422 DEFINE_MESSAGE(WM_DWMSENDICONICTHUMBNAIL),
423 DEFINE_MESSAGE(WM_DWMSENDICONICLIVEPREVIEWBITMAP),
424 # endif /* _WIN32_WINNT >= 0x0601 */
427 # if(WINVER >= 0x0600)
428 DEFINE_MESSAGE(WM_GETTITLEBARINFOEX),
429 # endif /* WINVER >= 0x0600 */
430 { 0, NULL, } /* end of message list */
432 #undef DEFINE_MESSAGE
434 char* WMMsg2Str(DWORD dwMessage)
436 struct WM_MESSAGE_MAP* pMapMsg = allMessages;
437 for (/*null*/; pMapMsg->lpszMsg != NULL; pMapMsg++)
439 if (pMapMsg->nMsg == dwMessage )
441 return (char *)pMapMsg->lpszMsg;
449 /* Get system time, taking special precautions against 32bit timer wrap.
450 We use timeGetTime and not GetTickCount because of its better stability,
451 and because we can increase its granularity (to 1 ms in
452 fgPlatformInitialize). For that reason we can't use GetTickCount64 which
453 wouldn't have the wrap issue.
454 Credit: this is based on code in glibc (https://mail.gnome.org/archives/commits-list/2011-November/msg04588.html)
456 static fg_time_t lastTime32 = 0;
457 static fg_time_t timeEpoch = 0;
458 void fgPlatformInitSystemTime()
460 #if defined(_WIN32_WCE)
461 lastTime32 = GetTickCount();
463 lastTime32 = timeGetTime();
466 fg_time_t fgPlatformSystemTime ( void )
468 fg_time_t currTime32;
469 #if defined(_WIN32_WCE)
470 currTime32 = GetTickCount();
472 currTime32 = timeGetTime();
474 /* Check if we just wrapped */
475 if (currTime32 < lastTime32)
478 lastTime32 = currTime32;
480 return currTime32 | timeEpoch << 32;
484 void fgPlatformSleepForEvents( fg_time_t msec )
486 MsgWaitForMultipleObjects( 0, NULL, FALSE, (DWORD) msec, QS_ALLINPUT );
490 void fgPlatformProcessSingleEvent ( void )
494 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
496 while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
498 if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
500 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
505 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
506 fgState.ExecState = GLUT_EXEC_STATE_STOP;
511 TranslateMessage( &stMsg );
512 DispatchMessage( &stMsg );
518 static void fghPlatformOnWindowStatusNotify(SFG_Window *window, GLboolean visState, GLboolean forceNotify)
520 GLboolean notify = GL_FALSE;
523 if (window->State.Visible != visState)
525 window->State.Visible = visState;
527 /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */
528 if (!window->Parent && window->State.pWState.IconTitle)
531 /* visible, set window title */
532 SetWindowText( window->Window.Handle, window->State.pWState.WindowTitle );
534 /* not visible, set icon title */
535 SetWindowText( window->Window.Handle, window->State.pWState.IconTitle );
541 if (notify || forceNotify)
543 SFG_Window *saved_window = fgStructure.CurrentWindow;
545 /* On win32 we only have two states, window displayed and window not displayed (iconified)
546 * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively.
548 INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) );
549 fgSetWindow( saved_window );
552 /* Also set windowStatus/visibility state for children */
553 for( child = ( SFG_Window * )window->Children.First;
555 child = ( SFG_Window * )child->Node.Next )
557 fghPlatformOnWindowStatusNotify(child, visState, GL_FALSE); /* No need to propagate forceNotify. Childs get this from their own INIT_WORK */
561 void fgPlatformMainLoopPreliminaryWork ( void )
568 * Determine a GLUT modifier mask based on MS-WINDOWS system info.
570 static int fgPlatformGetModifiers (void)
573 ( ( ( GetKeyState( VK_LSHIFT ) < 0 ) ||
574 ( GetKeyState( VK_RSHIFT ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
575 ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
576 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL : 0 ) |
577 ( ( ( GetKeyState( VK_LMENU ) < 0 ) ||
578 ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 ) |
579 ( ( ( GetKeyState( VK_LWIN ) < 0 ) ||
580 ( GetKeyState( VK_RWIN ) < 0 )) ? GLUT_ACTIVE_SUPER : 0 );
583 /* Check whether a button (VK_*BUTTON) is currently depressed. Returns
584 * non-zero (not necessarily 1) if yes. */
585 static SHORT fgGetKeyState(int vKey)
587 /* MSDN says: "If the high-order bit is 1, the key is down; otherwise, it is up". */
588 return GetKeyState(vKey) & 0xFF00;
591 static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
593 static unsigned char lControl = 0, lShift = 0, lAlt = 0,
594 rControl = 0, rShift = 0, rAlt = 0;
598 /* if keydown, check for repeat */
599 /* If repeat is globally switched off, it cannot be switched back on per window.
600 * But if it is globally switched on, it can be switched off per window. This matches
601 * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
602 * global state switch.
604 if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
607 /* Remember the current modifiers state so user can query it from their callback */
608 fgState.Modifiers = fgPlatformGetModifiers( );
610 /* Convert the Win32 keystroke codes to GLUTtish way */
611 # define FG_KEY(a,b) case a: keypress = b; break;
615 FG_KEY( VK_F1, GLUT_KEY_F1 );
616 FG_KEY( VK_F2, GLUT_KEY_F2 );
617 FG_KEY( VK_F3, GLUT_KEY_F3 );
618 FG_KEY( VK_F4, GLUT_KEY_F4 );
619 FG_KEY( VK_F5, GLUT_KEY_F5 );
620 FG_KEY( VK_F6, GLUT_KEY_F6 );
621 FG_KEY( VK_F7, GLUT_KEY_F7 );
622 FG_KEY( VK_F8, GLUT_KEY_F8 );
623 FG_KEY( VK_F9, GLUT_KEY_F9 );
624 FG_KEY( VK_F10, GLUT_KEY_F10 );
625 FG_KEY( VK_F11, GLUT_KEY_F11 );
626 FG_KEY( VK_F12, GLUT_KEY_F12 );
627 FG_KEY( VK_PRIOR, GLUT_KEY_PAGE_UP );
628 FG_KEY( VK_NEXT, GLUT_KEY_PAGE_DOWN );
629 FG_KEY( VK_HOME, GLUT_KEY_HOME );
630 FG_KEY( VK_END, GLUT_KEY_END );
631 FG_KEY( VK_LEFT, GLUT_KEY_LEFT );
632 FG_KEY( VK_UP, GLUT_KEY_UP );
633 FG_KEY( VK_RIGHT, GLUT_KEY_RIGHT );
634 FG_KEY( VK_DOWN, GLUT_KEY_DOWN );
635 FG_KEY( VK_INSERT, GLUT_KEY_INSERT );
636 FG_KEY( VK_LWIN, GLUT_KEY_SUPER_L );
637 FG_KEY( VK_RWIN, GLUT_KEY_SUPER_R );
639 /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
640 * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
641 * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
643 #define FG_KEY_EVENT(winKey,glutKey,keyStateVar)\
644 if (!keyStateVar && fgGetKeyState ( winKey ))\
649 else if (keyStateVar && !fgGetKeyState ( winKey ))\
655 FG_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
656 FG_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
659 FG_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
660 FG_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
663 FG_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
664 FG_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
669 /* The delete key should be treated as an ASCII keypress: */
671 INVOKE_WCB( *window, Keyboard,
672 ( 127, window->State.MouseX, window->State.MouseY )
675 INVOKE_WCB( *window, KeyboardUp,
676 ( 127, window->State.MouseX, window->State.MouseY )
680 #if !defined(_WIN32_WCE)
682 /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
688 GetKeyboardState( state );
690 if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
693 INVOKE_WCB( *window, KeyboardUp,
694 ( (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 */
695 window->State.MouseX, window->State.MouseY )
701 #if defined(_WIN32_WCE)
702 if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
704 if(wParam==(unsigned)gxKeyList.vkRight)
705 keypress = GLUT_KEY_RIGHT;
706 else if(wParam==(unsigned)gxKeyList.vkLeft)
707 keypress = GLUT_KEY_LEFT;
708 else if(wParam==(unsigned)gxKeyList.vkUp)
709 keypress = GLUT_KEY_UP;
710 else if(wParam==(unsigned)gxKeyList.vkDown)
711 keypress = GLUT_KEY_DOWN;
712 else if(wParam==(unsigned)gxKeyList.vkA)
713 keypress = GLUT_KEY_F1;
714 else if(wParam==(unsigned)gxKeyList.vkB)
715 keypress = GLUT_KEY_F2;
716 else if(wParam==(unsigned)gxKeyList.vkC)
717 keypress = GLUT_KEY_F3;
718 else if(wParam==(unsigned)gxKeyList.vkStart)
719 keypress = GLUT_KEY_F4;
725 INVOKE_WCB( *window, Special,
727 window->State.MouseX, window->State.MouseY )
730 INVOKE_WCB( *window, SpecialUp,
732 window->State.MouseX, window->State.MouseY )
735 fgState.Modifiers = INVALID_MODIFIERS;
737 /* SYSKEY events should be sent to default window proc for system to handle them */
738 if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
739 return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
744 SFG_Window* fghWindowUnderCursor(SFG_Window *window)
746 /* Check if the current window that the mouse is over is a child window
747 * of the window the message was sent to. Some events only sent to main window,
748 * and when handling some messages, we need to make sure that we process
749 * callbacks on the child window instead. This mirrors how GLUT does things.
750 * returns either the original window or the found child.
752 if (window && window->Children.First) /* This window has childs */
755 SFG_Window* child_window;
757 /* Get mouse position at time of message */
758 DWORD mouse_pos_dw = GetMessagePos();
760 mouse_pos.x = GET_X_LPARAM(mouse_pos_dw);
761 mouse_pos.y = GET_Y_LPARAM(mouse_pos_dw);
762 ScreenToClient( window->Window.Handle, &mouse_pos );
764 hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
765 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 */
767 child_window = fgWindowByHandle(hwnd);
768 if (child_window) /* Verify we got a FreeGLUT window */
770 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
771 window = fghWindowUnderCursor(child_window);
780 * The window procedure for handling Win32 events
782 LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
786 static int setCaptureActive = 0;
788 FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
790 window = fgWindowByHandle( hWnd );
792 if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
793 return DefWindowProc( hWnd, uMsg, wParam, lParam );
795 /* printf ( "Window %3d message %s (<%04x>) %12d %12d\n", window?window->ID:0,
796 WMMsg2Str(uMsg), uMsg, wParam, lParam ); */
801 /* The window structure is passed as the creation structure parameter... */
802 window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
803 FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
804 "fgPlatformWindowProc" );
806 window->Window.Handle = hWnd;
807 window->Window.pContext.Device = GetDC( hWnd );
810 unsigned int current_DisplayMode = fgState.DisplayMode;
811 fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
812 #if !defined(_WIN32_WCE)
813 fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
815 fgState.DisplayMode = current_DisplayMode;
817 if( fgStructure.MenuContext )
818 wglMakeCurrent( window->Window.pContext.Device,
819 fgStructure.MenuContext->MContext
823 fgStructure.MenuContext =
824 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
825 fgStructure.MenuContext->MContext =
826 wglCreateContext( window->Window.pContext.Device );
829 /* window->Window.Context = wglGetCurrentContext (); */
830 window->Window.Context = wglCreateContext( window->Window.pContext.Device );
834 #if !defined(_WIN32_WCE)
835 fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
838 if( ! fgState.UseCurrentContext )
839 window->Window.Context =
840 wglCreateContext( window->Window.pContext.Device );
843 window->Window.Context = wglGetCurrentContext( );
844 if( ! window->Window.Context )
845 window->Window.Context =
846 wglCreateContext( window->Window.pContext.Device );
849 #if !defined(_WIN32_WCE)
850 fgNewWGLCreateContext( window );
854 ReleaseDC( window->Window.Handle, window->Window.pContext.Device );
856 #if defined(_WIN32_WCE)
857 /* Take over button handling */
859 HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
862 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
863 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
868 if(GXGetDefaultKeys_)
869 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
872 #endif /* defined(_WIN32_WCE) */
876 /* printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam)); */
878 /* Update visibility state of the window */
879 if (wParam==SIZE_MINIMIZED)
880 fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE);
881 else if ((wParam==SIZE_RESTORED || wParam == SIZE_MAXIMIZED) && !window->State.Visible)
882 fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE);
884 /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */
885 if( window->State.Visible )
888 #if defined(_WIN32_WCE)
889 width = HIWORD(lParam);
890 height = LOWORD(lParam);
892 width = LOWORD(lParam);
893 height = HIWORD(lParam);
894 #endif /* defined(_WIN32_WCE) */
896 /* Update state and call callback, if there was a change */
897 fghOnReshapeNotify(window, width, height, GL_FALSE);
900 /* according to docs, should return 0 */
906 /* User resize-dragging the window, call reshape callback and
907 * force redisplay so display keeps running during dragging.
908 * Screen still wont update when not moving the cursor though...
911 /* PRECT prect = (PRECT) lParam;
912 printf("WM_SIZING: nc-area: %i,%i\n",prect->right-prect->left,prect->bottom-prect->top); */
913 /* Get client area, the rect in lParam is including non-client area. */
914 fghGetClientArea(&rect,window,FALSE);
916 /* We'll get a WM_SIZE as well, but as state has
917 * already been updated here, the fghOnReshapeNotify
918 * in the handler for that message doesn't do anything.
920 fghOnReshapeNotify(window, rect.right-rect.left, rect.bottom-rect.top, GL_FALSE);
922 /* Now directly call the drawing function to update
923 * window and window's childs.
924 * This mimics the WM_PAINT messages that are received during
925 * resizing. Note that we don't have a WM_MOVING handler
926 * as move-dragging doesn't generate WM_MOVE or WM_PAINT
927 * messages until the mouse is released.
929 fghRedrawWindowAndChildren(window);
932 /* according to docs, should return TRUE */
938 /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */
939 if (!IsIconic(window->Window.Handle))
943 /* lParam contains coordinates of top-left of client area.
944 * Get top-left of non-client area of window, matching coordinates of
945 * glutInitPosition and glutPositionWindow, but not those of
946 * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return
947 * top-left of client area.
949 GetWindowRect( window->Window.Handle, &windowRect );
953 /* For child window, we should return relative to upper-left
954 * of parent's client area.
957 topleft.x = windowRect.left;
958 topleft.y = windowRect.top;
960 ScreenToClient(window->Parent->Window.Handle,&topleft);
961 windowRect.left = topleft.x;
962 windowRect.top = topleft.y;
965 /* Update state and call callback, if there was a change */
966 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_FALSE);
970 /* according to docs, should return 0 */
975 /*printf("WM_SETFOCUS: %p\n", window );*/
976 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
978 SetActiveWindow( window->Window.Handle );
979 UpdateWindow ( hWnd );
984 /*printf("WM_KILLFOCUS: %p\n", window ); */
985 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
987 /* Check if there are any open menus that need to be closed */
988 fgPlatformCheckMenuDeactivate((HWND)wParam);
991 case WM_MOUSEACTIVATE:
992 /* Clicks should not activate the menu.
993 * Especially important when clicking on a menu's submenu item which has no effect.
995 /*printf("WM_MOUSEACTIVATE\n");*/
997 lRet = MA_NOACTIVATEANDEAT;
999 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1002 case WM_NCLBUTTONDOWN:
1003 case WM_NCMBUTTONDOWN:
1004 case WM_NCRBUTTONDOWN:
1007 if (fgState.ActiveMenus && (menu = fgGetActiveMenu()))
1008 /* user clicked non-client area of window while a menu is open. Close menu */
1009 fgDeactivateMenu(menu->ParentWindow);
1011 /* and always pass to DefWindowProc */
1012 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1018 /* printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); */
1019 if (LOWORD(wParam) != WA_INACTIVE)
1021 /* printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
1022 window->State.Cursor ); */
1023 fgSetCursor( window, window->State.Cursor );
1026 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1031 /* printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
1032 if( LOWORD( lParam ) == HTCLIENT )
1034 if (!window->State.pWState.MouseTracking)
1036 TRACKMOUSEEVENT tme;
1038 /* Cursor just entered window, set cursor look */
1039 fgSetCursor ( window, window->State.Cursor ) ;
1041 /* If an EntryFunc callback is specified by the user, also
1042 * invoke that callback and start mouse tracking so that
1043 * we get a WM_MOUSELEAVE message
1045 if (FETCH_WCB( *window, Entry ))
1047 SFG_Window* saved_window = fgStructure.CurrentWindow;
1048 INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
1049 fgSetWindow(saved_window);
1051 tme.cbSize = sizeof(TRACKMOUSEEVENT);
1052 tme.dwFlags = TME_LEAVE;
1053 tme.hwndTrack = window->Window.Handle;
1054 TrackMouseEvent(&tme);
1056 window->State.pWState.MouseTracking = TRUE;
1061 /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
1062 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1067 /* NB: This message is only received when a EntryFunc callback
1068 * is specified by the user, as that is the only condition under
1069 * which mouse tracking is setup in WM_SETCURSOR handler above
1071 SFG_Window* saved_window = fgStructure.CurrentWindow;
1072 INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
1073 fgSetWindow(saved_window);
1075 window->State.pWState.MouseTracking = FALSE;
1076 lRet = 0; /* As per docs, must return zero */
1081 /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */
1084 fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE);
1085 window->State.WorkMask |= GLUT_DISPLAY_WORK;
1089 fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE);
1090 window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
1098 /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */
1099 if (GetUpdateRect(hWnd,&rect,FALSE))
1101 /* Dummy begin/end paint to validate rect that needs
1102 * redrawing, then signal that a redisplay is needed.
1103 * This allows us full control about when we do any
1104 * redrawing, and is the same as what original GLUT
1108 BeginPaint( hWnd, &ps );
1109 EndPaint( hWnd, &ps );
1111 window->State.WorkMask |= GLUT_DISPLAY_WORK;
1113 lRet = 0; /* As per docs, should return 0 */
1118 fgDestroyWindow ( window );
1119 if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
1125 * The window already got destroyed, so don't bother with it.
1131 /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
1132 #if defined(_WIN32_WCE)
1133 window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */
1134 window->State.MouseY = LOWORD( lParam );
1136 window->State.MouseX = GET_X_LPARAM( lParam );
1137 window->State.MouseY = GET_Y_LPARAM( lParam );
1138 #endif /* defined(_WIN32_WCE) */
1139 /* Restrict to [-32768, 32767] to match X11 behaviour */
1140 /* See comment in "freeglut_developer" mailing list 10/4/04 */
1141 if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1142 if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1144 if ( window->ActiveMenu )
1146 fgUpdateMenuHighlight( window->ActiveMenu );
1150 fgState.Modifiers = fgPlatformGetModifiers( );
1152 if( ( wParam & MK_LBUTTON ) ||
1153 ( wParam & MK_MBUTTON ) ||
1154 ( wParam & MK_RBUTTON ) )
1155 INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1156 window->State.MouseY ) );
1158 INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1159 window->State.MouseY ) );
1161 fgState.Modifiers = INVALID_MODIFIERS;
1165 case WM_LBUTTONDOWN:
1166 case WM_MBUTTONDOWN:
1167 case WM_RBUTTONDOWN:
1172 GLboolean pressed = GL_TRUE;
1175 /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */
1176 #if defined(_WIN32_WCE)
1177 window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */
1178 window->State.MouseY = LOWORD( lParam );
1180 window->State.MouseX = GET_X_LPARAM( lParam );
1181 window->State.MouseY = GET_Y_LPARAM( lParam );
1182 #endif /* defined(_WIN32_WCE) */
1184 /* Restrict to [-32768, 32767] to match X11 behaviour */
1185 /* See comment in "freeglut_developer" mailing list 10/4/04 */
1186 if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1187 if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1191 case WM_LBUTTONDOWN:
1193 button = GLUT_LEFT_BUTTON;
1195 case WM_MBUTTONDOWN:
1197 button = GLUT_MIDDLE_BUTTON;
1199 case WM_RBUTTONDOWN:
1201 button = GLUT_RIGHT_BUTTON;
1205 button = GLUT_LEFT_BUTTON;
1209 button = GLUT_MIDDLE_BUTTON;
1213 button = GLUT_RIGHT_BUTTON;
1221 #if !defined(_WIN32_WCE)
1222 if( GetSystemMetrics( SM_SWAPBUTTON ) )
1224 if( button == GLUT_LEFT_BUTTON )
1225 button = GLUT_RIGHT_BUTTON;
1227 if( button == GLUT_RIGHT_BUTTON )
1228 button = GLUT_LEFT_BUTTON;
1230 #endif /* !defined(_WIN32_WCE) */
1233 return DefWindowProc( hWnd, uMsg, lParam, wParam );
1236 * Do not execute the application's mouse callback if a menu
1237 * is hooked to this button. In that case an appropriate
1238 * private call should be generated.
1240 if( fgCheckActiveMenu( window, button, pressed,
1241 window->State.MouseX, window->State.MouseY ) )
1244 /* Set capture so that the window captures all the mouse messages
1246 * The mouse is not released from the window until all buttons have
1247 * been released, even if the user presses a button in another window.
1248 * This is consistent with the behavior on X11.
1250 if ( pressed == GL_TRUE )
1252 if (!setCaptureActive)
1253 SetCapture ( window->Window.Handle ) ;
1254 setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
1256 else if (!fgGetKeyState(VK_LBUTTON) && !fgGetKeyState(VK_MBUTTON) && !fgGetKeyState(VK_RBUTTON))
1257 /* Make sure all mouse buttons are released before releasing capture */
1260 if( ! FETCH_WCB( *window, Mouse ) )
1263 fgSetWindow( window );
1264 fgState.Modifiers = fgPlatformGetModifiers( );
1269 pressed ? GLUT_DOWN : GLUT_UP,
1270 window->State.MouseX,
1271 window->State.MouseY
1275 fgState.Modifiers = INVALID_MODIFIERS;
1277 /* As per docs, should return zero */
1284 int wheel_number = 0; /* Only one scroll wheel on windows */
1285 #if defined(_WIN32_WCE)
1286 int modkeys = LOWORD(wParam);
1287 short ticks = (short)HIWORD(wParam);
1288 /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
1289 xPos = LOWORD(lParam); -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong)
1290 yPos = HIWORD(lParam);
1293 /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */
1294 short ticks = HIWORD( wParam );
1295 /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first:
1296 window->State.MouseX = GET_X_LPARAM( lParam );
1297 window->State.MouseY = GET_Y_LPARAM( lParam );
1299 #endif /* defined(_WIN32_WCE) */
1301 window = fghWindowUnderCursor(window);
1303 fgState.MouseWheelTicks += ticks;
1304 if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
1306 int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
1308 if( ! FETCH_WCB( *window, MouseWheel ) &&
1309 ! FETCH_WCB( *window, Mouse ) )
1312 fgSetWindow( window );
1313 fgState.Modifiers = fgPlatformGetModifiers( );
1315 while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
1317 if( FETCH_WCB( *window, MouseWheel ) )
1318 INVOKE_WCB( *window, MouseWheel,
1321 window->State.MouseX,
1322 window->State.MouseY
1325 else /* No mouse wheel, call the mouse button callback twice */
1328 * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
1329 * " " one +1 to 5, -1 to 6, ...
1331 * XXX The below assumes that you have no more than 3 mouse
1332 * XXX buttons. Sorry.
1334 int button = wheel_number * 2 + 3;
1337 INVOKE_WCB( *window, Mouse,
1338 ( button, GLUT_DOWN,
1339 window->State.MouseX, window->State.MouseY )
1341 INVOKE_WCB( *window, Mouse,
1343 window->State.MouseX, window->State.MouseY )
1347 fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
1350 fgState.Modifiers = INVALID_MODIFIERS;
1352 /* Per docs, should return zero */
1360 window = fghWindowUnderCursor(window);
1361 lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
1368 window = fghWindowUnderCursor(window);
1369 lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
1376 window = fghWindowUnderCursor(window);
1378 if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
1381 fgState.Modifiers = fgPlatformGetModifiers( );
1382 INVOKE_WCB( *window, Keyboard,
1384 window->State.MouseX, window->State.MouseY )
1386 fgState.Modifiers = INVALID_MODIFIERS;
1390 case WM_CAPTURECHANGED:
1391 if (!lParam || !fgWindowByHandle((HWND)lParam))
1392 /* Capture released or capture taken by non-FreeGLUT window */
1393 setCaptureActive = 0;
1394 /* Docs advise a redraw */
1395 InvalidateRect( hWnd, NULL, GL_FALSE );
1397 lRet = 0; /* Per docs, should return zero */
1400 #if !defined(_WIN32_WCE)
1401 case WM_SYNCPAINT: /* 0x0088 */
1402 /* Another window has moved, need to update this one */
1403 window->State.WorkMask |= GLUT_DISPLAY_WORK;
1404 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1405 /* Help screen says this message must be passed to "DefWindowProc" */
1408 case WM_DISPLAYCHANGE: /* 0x007E */
1409 /* The system display resolution/depth has changed */
1410 fgDisplay.ScreenWidth = LOWORD(lParam);
1411 fgDisplay.ScreenHeight = HIWORD(lParam);
1414 case WM_SYSCOMMAND : /* 0x0112 */
1417 * We have received a system command message. Try to act on it.
1418 * The commands are passed in through the "wParam" parameter:
1419 * The least significant digit seems to be which edge of the window
1420 * is being used for a resize event:
1424 * Congratulations and thanks to Richard Rauch for figuring this out..
1426 switch ( wParam & 0xfff0 )
1435 /* User has clicked on the "-" to minimize the window */
1436 /* Turning off the visibility is handled in WM_SIZE handler */
1443 case SC_NEXTWINDOW :
1446 case SC_PREVWINDOW :
1450 /* Followed very closely by a WM_CLOSE message */
1474 case SC_SCREENSAVE :
1480 #if(WINVER >= 0x0400)
1484 case SC_MONITORPOWER :
1487 case SC_CONTEXTHELP :
1489 #endif /* WINVER >= 0x0400 */
1493 fgWarning( "Unknown wParam type 0x%x", wParam );
1498 #endif /* !defined(_WIN32_WCE) */
1500 /* We need to pass the message on to the operating system as well */
1501 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1505 /* handle multi-touch messages */
1508 unsigned int numInputs = (unsigned int)wParam;
1510 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
1512 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
1513 fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
1514 fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
1517 if (!fghGetTouchInputInfo) {
1522 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
1523 /* Handle each contact point */
1524 for (i = 0; i < numInputs; ++i ) {
1527 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
1528 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
1529 ScreenToClient( hWnd, &tp );
1531 ti[i].dwID = ti[i].dwID * 2;
1533 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
1534 INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_ENTERED ) );
1535 INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
1536 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
1537 INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
1538 } else if (ti[i].dwFlags & TOUCHEVENTF_UP) {
1539 INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
1540 INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_LEFT ) );
1544 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
1546 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
1553 /* Added by Jinrong Xie <stonexjr at gmail.com> for SpaceNavigator support on Windows. Dec 2014 */
1554 if (fgHasSpaceball())
1556 fgSpaceballHandleWinEvent(hWnd, wParam, lParam);
1561 /* Handle unhandled messages */
1562 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1570 /* deal with work list items */
1571 void fgPlatformInitWork(SFG_Window* window)
1575 /* Notify windowStatus/visibility */
1576 fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE);
1578 /* get and notify window's position */
1579 GetWindowRect(window->Window.Handle,&windowRect);
1580 fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE);
1582 /* get and notify window's size */
1583 GetClientRect(window->Window.Handle,&windowRect);
1584 fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE);
1587 /* On windows we can position, resize and change z order at the same time */
1588 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
1590 UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1591 HWND insertAfter = HWND_TOP;
1594 #if !defined(_WIN32_WCE) /* FIXME: what about WinCE */
1595 if (workMask & GLUT_FULL_SCREEN_WORK)
1597 /* This asks us to toggle fullscreen mode */
1598 flags |= SWP_FRAMECHANGED;
1600 if (window->State.IsFullscreen)
1602 /* If we are fullscreen, resize the current window back to its original size */
1603 /* 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); */
1605 /* restore style of window before making it fullscreen */
1606 SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle);
1607 SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx);
1609 /* Then set up resize/reposition, unless user already queued up reshape/position work */
1610 if (!(workMask & GLUT_POSITION_WORK))
1612 workMask |= GLUT_POSITION_WORK;
1613 window->State.DesiredXpos = window->State.pWState.OldRect.left;
1614 window->State.DesiredYpos = window->State.pWState.OldRect.top;
1616 if (!(workMask & GLUT_SIZE_WORK))
1618 workMask |= GLUT_SIZE_WORK;
1619 window->State.DesiredWidth = window->State.pWState.OldRect.right - window->State.pWState.OldRect.left;
1620 window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top;
1623 /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */
1627 /* we are currently not fullscreen, go to fullscreen:
1628 * remove window decoration and then maximize
1635 /* save current window rect, style, exstyle and maximized state */
1636 window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle);
1637 if (window->State.pWState.OldMaximized)
1638 /* We force the window into restored mode before going
1639 * fullscreen because Windows doesn't seem to hide the
1640 * taskbar if the window is in the maximized state.
1642 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
1644 fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE );
1645 window->State.pWState.OldStyle = GetWindowLong(window->Window.Handle, GWL_STYLE);
1646 window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
1648 /* remove decorations from style */
1649 newStyle = window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME);
1650 if (fgState.DisplayMode & GLUT_STEREO)
1652 /* stereo mode does not engage on nVidia stereo buffers. This kills child
1653 windows, but we make the guess that those are rare for stereo windows. */
1654 newStyle |= WS_POPUP;
1656 SetWindowLong(window->Window.Handle, GWL_STYLE, newStyle);
1657 SetWindowLong(window->Window.Handle, GWL_EXSTYLE,
1658 window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME |
1659 WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1661 /* For fullscreen mode, find the monitor that is covered the most
1662 * by the window and get its rect as the resize target.
1664 hMonitor= MonitorFromWindow(window->Window.Handle, MONITOR_DEFAULTTONEAREST);
1665 mi.cbSize = sizeof(mi);
1666 GetMonitorInfo(hMonitor, &mi);
1667 rect = mi.rcMonitor;
1669 /* then setup window resize, overwriting other work queued on the window */
1670 window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK;
1671 window->State.WorkMask &= ~GLUT_ZORDER_WORK;
1672 window->State.DesiredXpos = rect.left;
1673 window->State.DesiredYpos = rect.top;
1674 window->State.DesiredWidth = rect.right - rect.left;
1675 window->State.DesiredHeight = rect.bottom - rect.top;
1678 #endif /*!defined(_WIN32_WCE) */
1680 /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */
1682 /* get rect describing window's current position and size,
1683 * in screen coordinates and in FreeGLUT format
1684 * (size (right-left, bottom-top) is client area size, top and left
1685 * are outside of window including decorations).
1687 fghGetClientArea( &clientRect, window, TRUE );
1689 if (workMask & GLUT_POSITION_WORK)
1691 flags &= ~SWP_NOMOVE;
1693 /* Move rect so that top-left is at requested position */
1694 /* This also automatically makes sure that child window requested coordinates are relative
1695 * to top-left of parent's client area (needed input for SetWindowPos on child windows),
1696 * so no need to further correct rect for child windows below (childs don't have decorations either).
1698 OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top);
1700 if (workMask & GLUT_SIZE_WORK)
1702 flags &= ~SWP_NOSIZE;
1704 /* Note on maximizing behavior of Windows: the resize borders are off
1705 * the screen such that the client area extends all the way from the
1706 * leftmost corner to the rightmost corner to maximize screen real
1707 * estate. A caption is still shown however to allow interaction with
1708 * the window controls. This is default behavior of Windows that
1709 * FreeGLUT sticks with. To alter, one would have to check if
1710 * WS_MAXIMIZE style is set when a resize event is triggered, and
1711 * then manually correct the windowRect to put the borders back on
1715 /* Set new size of window, WxH specify client area */
1716 clientRect.right = clientRect.left + window->State.DesiredWidth;
1717 clientRect.bottom = clientRect.top + window->State.DesiredHeight;
1719 if (workMask & GLUT_ZORDER_WORK)
1721 flags &= ~SWP_NOZORDER;
1723 /* Could change this to push it down or up one window at a time with some
1724 * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT.
1725 * What would be consistent with X11? Win32 GLUT does what we do here...
1727 if (window->State.DesiredZOrder < 0)
1728 insertAfter = HWND_BOTTOM;
1732 /* Adjust for window decorations
1733 * Child windows don't have decoration, so no need to correct
1735 if (!window->Parent)
1736 /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
1737 fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE);
1739 /* Do the requested positioning, moving, and z order push/pop. */
1740 SetWindowPos( window->Window.Handle,
1742 clientRect.left, clientRect.top,
1743 clientRect.right - clientRect.left,
1744 clientRect.bottom- clientRect.top,
1748 /* Finish off the fullscreen operation we were doing, if any */
1749 if (workMask & GLUT_FULL_SCREEN_WORK)
1751 if (window->State.IsFullscreen)
1753 /* leaving fullscreen, restore maximized state, if any */
1754 if (window->State.pWState.OldMaximized)
1755 SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1757 window->State.IsFullscreen = GL_FALSE;
1760 window->State.IsFullscreen = GL_TRUE;
1765 void fgPlatformVisibilityWork(SFG_Window* window)
1767 /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */
1769 SFG_Window *win = window;
1770 switch (window->State.DesiredVisibility)
1772 case DesireHiddenState:
1775 case DesireIconicState:
1776 cmdShow = SW_MINIMIZE;
1777 /* Call on top-level window */
1781 case DesireNormalState:
1782 if (win->IsMenu && !fgStructure.GameModeWindow)
1783 cmdShow = SW_SHOWNA; /* Just show, don't activate window if its a menu. Only exception is when there is a gamemode window as the menu would pop under it when we do this... */
1789 ShowWindow( win->Window.Handle, cmdShow );