2 /* Copyright (c) Nate Robins, 1997. */
3 /* portions Copyright (c) Mark Kilgard, 1997, 1998. */
5 /* This program is freely distributable without licensing fees
6 and is provided without guarantee or warrantee expressed or
7 implied. This program is -not- in the public domain. */
11 #include <sys/timeb.h>
16 #if defined(_WIN32) && !defined(__CYGWIN32__)
17 #include <mmsystem.h> /* Win32 Multimedia API header. */
20 extern unsigned __glutMenuButton
;
21 extern GLUTidleCB __glutIdleFunc
;
22 extern GLUTtimer
*__glutTimerList
;
23 extern GLUTmenuItem
*__glutGetUniqueMenuItem(GLUTmenu
* menu
, UINT unique
);
24 static HMENU __glutHMenu
;
27 updateWindowState(GLUTwindow
*window
, int visState
)
31 /* XXX shownState and visState are the same in Win32. */
32 window
->shownState
= visState
;
33 if (visState
!= window
->visState
) {
34 if (window
->windowStatus
) {
35 window
->visState
= visState
;
36 __glutSetWindow(window
);
37 window
->windowStatus(visState
);
40 /* Since Win32 only sends an activate for the toplevel window,
41 update the visibility for all the child windows. */
42 child
= window
->children
;
44 updateWindowState(child
, visState
);
45 child
= child
->siblings
;
50 __glutWindowProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
52 POINT point
; /* Point structure. */
53 PAINTSTRUCT ps
; /* Paint structure. */
54 LPMINMAXINFO minmax
; /* Minimum/maximum info structure. */
55 GLUTwindow
* window
; /* GLUT window associated with message. */
56 GLUTmenu
* menu
; /* GLUT menu associated with message. */
57 int x
, y
, width
, height
, key
;
71 /* XXX NVidia's NT OpenGL can have problems closing down
72 its OpenGL internal data structures if we just allow
73 the process to terminate without unbinding and deleting
74 the windows context. Apparently, DirectDraw unloads
75 before OPENGL32.DLL in the close down sequence, but
76 NVidia's NT OpenGL needs DirectDraw to close down its
78 window
= __glutGetWindow(hwnd
);
81 wglMakeCurrent(NULL
, NULL
);
82 wglDeleteContext(window
->ctx
);
88 window
= __glutGetWindow(hwnd
);
90 BeginPaint(hwnd
, &ps
); /* Must have this for some Win32 reason. */
92 if (window
->win
== hwnd
) {
93 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
94 } else if (window
->overlay
&& window
->overlay
->win
== hwnd
) {
95 __glutPostRedisplay(window
, GLUT_OVERLAY_REPAIR_WORK
);
102 window
= __glutGetWindow(hwnd
);
106 /* Win32 is dumb and sends these messages only to the parent
107 window. Therefore, find out if we're in a child window and
108 call the child windows keyboard callback if we are. */
109 if (window
->parent
) {
110 GetCursorPos(&point
);
111 ScreenToClient(hwnd
, &point
);
112 hwnd
= ChildWindowFromPoint(hwnd
, point
);
113 window
= __glutGetWindow(hwnd
);
115 if (window
->specialUp
|| window
->keyboardUp
) {
116 GetCursorPos(&point
);
117 ScreenToClient(window
->win
, &point
);
118 __glutSetWindow(window
);
119 __glutModifierMask
= 0;
120 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
121 __glutModifierMask
|= ShiftMask
;
122 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
123 __glutModifierMask
|= ControlMask
;
124 if (GetKeyState(VK_MENU
) < 0)
125 __glutModifierMask
|= Mod1Mask
;
128 case VK_F1
: key
= GLUT_KEY_F1
; break;
129 case VK_F2
: key
= GLUT_KEY_F2
; break;
130 case VK_F3
: key
= GLUT_KEY_F3
; break;
131 case VK_F4
: key
= GLUT_KEY_F4
; break;
132 case VK_F5
: key
= GLUT_KEY_F5
; break;
133 case VK_F6
: key
= GLUT_KEY_F6
; break;
134 case VK_F7
: key
= GLUT_KEY_F7
; break;
135 case VK_F8
: key
= GLUT_KEY_F8
; break;
136 case VK_F9
: key
= GLUT_KEY_F9
; break;
137 case VK_F10
: key
= GLUT_KEY_F10
; break;
138 case VK_F11
: key
= GLUT_KEY_F11
; break;
139 case VK_F12
: key
= GLUT_KEY_F12
; break;
140 case VK_LEFT
: key
= GLUT_KEY_LEFT
; break;
141 case VK_UP
: key
= GLUT_KEY_UP
; break;
142 case VK_RIGHT
: key
= GLUT_KEY_RIGHT
; break;
143 case VK_DOWN
: key
= GLUT_KEY_DOWN
; break;
144 case VK_PRIOR
: key
= GLUT_KEY_PAGE_UP
; break;
145 case VK_NEXT
: key
= GLUT_KEY_PAGE_DOWN
; break;
146 case VK_HOME
: key
= GLUT_KEY_HOME
; break;
147 case VK_END
: key
= GLUT_KEY_END
; break;
148 case VK_INSERT
: key
= GLUT_KEY_INSERT
; break;
150 /* Delete is an ASCII character. */
151 if (window
->keyboardUp
) {
152 window
->keyboardUp((unsigned char) 127, point
.x
, point
.y
);
157 if (window
->keyboardUp
) {
158 key
= MapVirtualKey(wParam
, 2); /* Map to ASCII. */
159 if (isascii(key
) && (key
!= 0)) {
161 /* XXX Attempt to determine modified ASCII character
162 is quite incomplete. Digits, symbols, CapsLock,
163 Ctrl, and numeric keypad are all ignored. Fix this. */
165 if (!(__glutModifierMask
& ShiftMask
))
167 window
->keyboardUp((unsigned char) key
, point
.x
, point
.y
);
170 __glutModifierMask
= (unsigned int) ~0;
173 if (window
->specialUp
) {
174 window
->specialUp(key
, point
.x
, point
.y
);
176 __glutModifierMask
= (unsigned int) ~0;
182 window
= __glutGetWindow(hwnd
);
187 /* Bit 30 of lParam is set if key already held down. If
188 we are ignoring auto repeated key strokes for the window, bail. */
189 if (window
->ignoreKeyRepeat
&& (lParam
& (1 << 30)) ) {
193 /* Win32 is dumb and sends these messages only to the parent
194 window. Therefore, find out if we're in a child window and
195 call the child windows keyboard callback if we are. */
196 if (window
->parent
) {
197 GetCursorPos(&point
);
198 ScreenToClient(hwnd
, &point
);
199 hwnd
= ChildWindowFromPoint(hwnd
, point
);
200 window
= __glutGetWindow(hwnd
);
202 if (window
->keyboard
) {
203 GetCursorPos(&point
);
204 ScreenToClient(window
->win
, &point
);
205 __glutSetWindow(window
);
206 __glutModifierMask
= 0;
207 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
208 __glutModifierMask
|= ShiftMask
;
209 if (GetKeyState(VK_CONTROL
) < 0)
210 __glutModifierMask
|= ControlMask
;
211 if (GetKeyState(VK_MENU
) < 0)
212 __glutModifierMask
|= Mod1Mask
;
213 window
->keyboard((unsigned char)wParam
, point
.x
, point
.y
);
214 __glutModifierMask
= (unsigned int) ~0;
220 window
= __glutGetWindow(hwnd
);
225 /* Bit 30 of lParam is set if key already held down. If
226 we are ignoring auto repeated key strokes for the window, bail. */
227 if (window
->ignoreKeyRepeat
&& (lParam
& (1 << 30)) ) {
231 /* Win32 is dumb and sends these messages only to the parent
232 window. Therefore, find out if we're in a child window and
233 call the child windows keyboard callback if we are. */
234 if (window
->parent
) {
235 GetCursorPos(&point
);
236 ScreenToClient(hwnd
, &point
);
237 hwnd
= ChildWindowFromPoint(hwnd
, point
);
238 window
= __glutGetWindow(hwnd
);
240 if (window
->special
) {
244 case VK_F1
: key
= GLUT_KEY_F1
; break;
245 case VK_F2
: key
= GLUT_KEY_F2
; break;
246 case VK_F3
: key
= GLUT_KEY_F3
; break;
247 case VK_F4
: key
= GLUT_KEY_F4
; break;
248 case VK_F5
: key
= GLUT_KEY_F5
; break;
249 case VK_F6
: key
= GLUT_KEY_F6
; break;
250 case VK_F7
: key
= GLUT_KEY_F7
; break;
251 case VK_F8
: key
= GLUT_KEY_F8
; break;
252 case VK_F9
: key
= GLUT_KEY_F9
; break;
253 case VK_F10
: key
= GLUT_KEY_F10
; break;
254 case VK_F11
: key
= GLUT_KEY_F11
; break;
255 case VK_F12
: key
= GLUT_KEY_F12
; break;
256 /* directional keys */
257 case VK_LEFT
: key
= GLUT_KEY_LEFT
; break;
258 case VK_UP
: key
= GLUT_KEY_UP
; break;
259 case VK_RIGHT
: key
= GLUT_KEY_RIGHT
; break;
260 case VK_DOWN
: key
= GLUT_KEY_DOWN
; break;
264 /* VK_PRIOR is Win32's Page Up */
265 key
= GLUT_KEY_PAGE_UP
;
268 /* VK_NEXT is Win32's Page Down */
269 key
= GLUT_KEY_PAGE_DOWN
;
278 key
= GLUT_KEY_INSERT
;
285 GetCursorPos(&point
);
286 ScreenToClient(window
->win
, &point
);
287 __glutSetWindow(window
);
288 __glutModifierMask
= 0;
289 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
290 __glutModifierMask
|= ShiftMask
;
291 if (GetKeyState(VK_CONTROL
) < 0)
292 __glutModifierMask
|= ControlMask
;
293 if (GetKeyState(VK_MENU
) < 0)
294 __glutModifierMask
|= Mod1Mask
;
295 window
->special(key
, point
.x
, point
.y
);
296 __glutModifierMask
= (unsigned int) ~0;
297 } else if (window
->keyboard
) {
298 /* Specially handle any keys that match ASCII values but
299 do not generate Windows WM_SYSCHAR or WM_CHAR messages. */
303 /* Delete is an ASCII character. */
304 GetCursorPos(&point
);
305 ScreenToClient(window
->win
, &point
);
306 __glutSetWindow(window
);
307 __glutModifierMask
= 0;
308 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
309 __glutModifierMask
|= ShiftMask
;
310 if (GetKeyState(VK_CONTROL
) < 0)
311 __glutModifierMask
|= ControlMask
;
312 if (GetKeyState(VK_MENU
) < 0)
313 __glutModifierMask
|= Mod1Mask
;
314 window
->keyboard((unsigned char) 127, point
.x
, point
.y
);
315 __glutModifierMask
= (unsigned int) ~0;
318 /* Let the following WM_SYSCHAR or WM_CHAR message generate
319 the keyboard callback. */
326 button
= GLUT_LEFT_BUTTON
;
329 button
= GLUT_MIDDLE_BUTTON
;
332 button
= GLUT_RIGHT_BUTTON
;
334 /* finish the menu if we get a button down message (user must have
335 cancelled the menu). */
336 if (__glutMappedMenu
) {
337 /* TODO: take this out once the menu on middle mouse stuff works
339 if (button
== GLUT_MIDDLE_BUTTON
)
341 GetCursorPos(&point
);
342 ScreenToClient(hwnd
, &point
);
343 __glutItemSelected
= NULL
;
344 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
348 /* set the capture so we can get mouse events outside the window */
351 /* Win32 doesn't return the same numbers as X does when the mouse
352 goes beyond the upper or left side of the window. roll the
353 Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
356 if(x
& 1 << 15) x
-= (1 << 16);
357 if(y
& 1 << 15) y
-= (1 << 16);
359 window
= __glutGetWindow(hwnd
);
361 menu
= __glutGetMenuByNum(window
->menu
[button
]);
363 point
.x
= LOWORD(lParam
); point
.y
= HIWORD(lParam
);
364 ClientToScreen(window
->win
, &point
);
365 __glutMenuButton
= button
== GLUT_RIGHT_BUTTON
? TPM_RIGHTBUTTON
:
366 button
== GLUT_LEFT_BUTTON
? TPM_LEFTBUTTON
:
368 __glutStartMenu(menu
, window
, point
.x
, point
.y
, x
, y
);
369 } else if (window
->mouse
) {
371 __glutSetWindow(window
);
372 __glutModifierMask
= 0;
373 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on. */
374 __glutModifierMask
|= ShiftMask
;
375 if (GetKeyState(VK_CONTROL
) < 0)
376 __glutModifierMask
|= ControlMask
;
377 if (GetKeyState(VK_MENU
) < 0)
378 __glutModifierMask
|= Mod1Mask
;
379 window
->mouse(button
, GLUT_DOWN
, x
, y
);
380 __glutModifierMask
= (unsigned int)~0;
382 /* Stray mouse events. Ignore. */
388 button
= GLUT_LEFT_BUTTON
;
391 button
= GLUT_MIDDLE_BUTTON
;
394 button
= GLUT_RIGHT_BUTTON
;
396 /* Bail out if we're processing a menu. */
397 if (__glutMappedMenu
) {
398 GetCursorPos(&point
);
399 ScreenToClient(hwnd
, &point
);
400 /* if we're getting the middle button up signal, then something
401 on the menu was selected. */
402 if (button
== GLUT_MIDDLE_BUTTON
) {
404 /* For some reason, the code below always returns -1 even
405 though the point IS IN THE ITEM! Therefore, just bail out if
406 we get a middle mouse up. The user must select using the
407 left mouse button. Stupid Win32. */
409 int item
= MenuItemFromPoint(hwnd
, __glutHMenu
, point
);
411 __glutItemSelected
= (GLUTmenuItem
*)GetMenuItemID(__glutHMenu
, item
);
413 __glutItemSelected
= NULL
;
414 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
417 __glutItemSelected
= NULL
;
418 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
423 /* Release the mouse capture. */
426 window
= __glutGetWindow(hwnd
);
427 if (window
&& window
->mouse
) {
428 /* Win32 doesn't return the same numbers as X does when the
429 mouse goes beyond the upper or left side of the window. roll
430 the Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
433 if(x
& 1 << 15) x
-= (1 << 16);
434 if(y
& 1 << 15) y
-= (1 << 16);
436 __glutSetWindow(window
);
437 __glutModifierMask
= 0;
438 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
439 __glutModifierMask
|= ShiftMask
;
440 if (GetKeyState(VK_CONTROL
) < 0)
441 __glutModifierMask
|= ControlMask
;
442 if (GetKeyState(VK_MENU
) < 0)
443 __glutModifierMask
|= Mod1Mask
;
444 window
->mouse(button
, GLUT_UP
, x
, y
);
445 __glutModifierMask
= (unsigned int)~0;
447 /* Window might have been destroyed and all the
448 events for the window may not yet be received. */
452 case WM_ENTERMENULOOP
:
453 /* KLUDGE: create a timer that fires every 100 ms when we start a
454 menu so that we can still process the idle & timer events (that
455 way, the timers will fire during a menu pick and so will the
457 SetTimer(hwnd
, 1, 1, NULL
);
462 /* If the timer id is 2, then this is the timer that is set up in
463 the main glut message processing loop, and we don't want to do
464 anything but acknowledge that we got it. It is used to prevent
465 CPU spiking when an idle function is installed. */
470 /* only worry about the idle function and the timeouts, since
471 these are the only events we expect to process during
472 processing of a menu. */
473 /* we no longer process the idle functions (as outlined in the
474 README), since drawing can't be done until the menu has
475 finished...it's pretty lame when the animation goes on, but
476 doesn't update, so you get this weird jerkiness. */
485 case WM_EXITMENULOOP
:
486 /* nuke the above created timer...we don't need it anymore, since
487 the menu is gone now. */
493 __glutHMenu
= (HMENU
)lParam
;
497 if (__glutMappedMenu
) {
498 if (GetSubMenu(__glutHMenu
, LOWORD(wParam
)))
499 __glutItemSelected
= NULL
;
502 __glutGetUniqueMenuItem(__glutMappedMenu
, LOWORD(wParam
));
503 GetCursorPos(&point
);
504 ScreenToClient(hwnd
, &point
);
505 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
510 if (!__glutMappedMenu
) {
511 window
= __glutGetWindow(hwnd
);
513 /* If motion function registered _and_ buttons held *
514 down, call motion function... */
518 /* Win32 doesn't return the same numbers as X does when the
519 mouse goes beyond the upper or left side of the window.
520 roll the Win32's 0..2^16 pointer co-ord range to 0..+/-2^15. */
521 if(x
& 1 << 15) x
-= (1 << 16);
522 if(y
& 1 << 15) y
-= (1 << 16);
524 if (window
->motion
&& wParam
&
525 (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
526 __glutSetWindow(window
);
527 window
->motion(x
, y
);
529 /* If passive motion function registered _and_
530 buttons not held down, call passive motion
532 else if (window
->passive
&&
534 (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) ==
536 __glutSetWindow(window
);
537 window
->passive(x
, y
);
541 /* Motion events are thrown away when a pop up menu is
546 case WM_GETMINMAXINFO
:
547 /* this voodoo is brought to you by Win32 (again). It allows the
548 window to be bigger than the screen, and smaller than 100x100
549 (although it doesn't seem to help the y minimum). */
550 minmax
= (LPMINMAXINFO
)lParam
;
551 minmax
->ptMaxSize
.x
= __glutScreenWidth
;
552 minmax
->ptMaxSize
.y
= __glutScreenHeight
;
553 minmax
->ptMinTrackSize
.x
= 0;
554 minmax
->ptMinTrackSize
.y
= 0;
555 minmax
->ptMaxTrackSize
.x
= __glutScreenWidth
+
556 GetSystemMetrics(SM_CXSIZE
) * 2;
557 minmax
->ptMaxTrackSize
.y
= __glutScreenHeight
+
558 GetSystemMetrics(SM_CXSIZE
) * 2 + GetSystemMetrics(SM_CYCAPTION
);
562 window
= __glutGetWindow(hwnd
);
564 width
= LOWORD(lParam
);
565 height
= HIWORD(lParam
);
566 if (width
!= window
->width
|| height
!= window
->height
) {
567 #if 0 /* Win32 GLUT does not support overlays for now. */
568 if (window
->overlay
) {
569 XResizeWindow(__glutDisplay
, window
->overlay
->win
, width
, height
);
572 window
->width
= width
;
573 window
->height
= height
;
574 __glutSetWindow(window
);
575 /* Do not execute OpenGL out of sequence with respect
576 to the SetWindowPos request! */
578 window
->reshape(width
, height
);
579 window
->forceReshape
= FALSE
;
580 /* A reshape should be considered like posting a
582 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
588 /* If the cursor is not in the client area, then we want to send
589 this message to the default window procedure ('cause its
590 probably in the border or title, and we don't handle that
591 cursor. otherwise, set our cursor. Win32 makes us set the
592 cursor every time the mouse moves (DUMB!). */
593 if(LOWORD(lParam
) != HTCLIENT
) {
596 window
= __glutGetWindow(hwnd
);
598 __glutSetCursor(window
);
600 /* TODO: check out the info in DevStudio on WM_SETCURSOR in the
601 DefaultAction section. */
605 window
= __glutGetWindow(hwnd
);
607 window
->entryState
= WM_SETFOCUS
;
609 __glutSetWindow(window
);
610 window
->entry(GLUT_ENTERED
);
611 /* XXX Generation of fake passive notify? See how much
612 work the X11 code does to support fake passive notify
615 if (window
->joystick
&& __glutCurrentWindow
) {
616 if (__glutCurrentWindow
->joyPollInterval
> 0) {
619 /* Because Win32 will only let one window capture the
620 joystick at a time, we must capture it when we get the
621 focus and release it when we lose the focus. */
622 result
= joySetCapture(__glutCurrentWindow
->win
,
623 JOYSTICKID1
, 0, TRUE
);
624 if (result
!= JOYERR_NOERROR
) {
627 (void) joySetThreshold(JOYSTICKID1
,
628 __glutCurrentWindow
->joyPollInterval
);
635 window
= __glutGetWindow(hwnd
);
637 window
->entryState
= WM_KILLFOCUS
;
639 __glutSetWindow(window
);
640 window
->entry(GLUT_LEFT
);
642 if (window
->joystick
&& __glutCurrentWindow
) {
643 if (__glutCurrentWindow
->joyPollInterval
> 0) {
644 /* Because Win32 will only let one window capture the
645 joystick at a time, we must capture it when we get the
646 focus and release it when we lose the focus. */
647 (void) joyReleaseCapture(JOYSTICKID1
);
653 window
= __glutGetWindow(hwnd
);
654 /* Make sure we re-select the correct palette if needed. */
655 if (LOWORD(wParam
)) {
656 PostMessage(hwnd
, WM_PALETTECHANGED
, 0, 0);
661 /* HIWORD(wParam) is the minimized flag. */
662 visState
= !HIWORD(wParam
);
663 updateWindowState(window
, visState
);
667 /* Colour Palette Management */
668 case WM_PALETTECHANGED
:
669 if (hwnd
== (HWND
)wParam
) {
670 /* Don't respond to the message that we sent! */
673 /* fall through to WM_QUERYNEWPALETTE */
675 case WM_QUERYNEWPALETTE
:
676 window
= __glutGetWindow(hwnd
);
677 if (window
&& window
->colormap
) {
678 UnrealizeObject(window
->colormap
->cmap
);
679 SelectPalette(window
->hdc
, window
->colormap
->cmap
, FALSE
);
680 RealizePalette(window
->hdc
);
687 window
= __glutGetWindow(hwnd
);
688 if (window
->joystick
) {
692 /* Because WIN32 only supports messages for X, Y, and Z
693 translations, we must poll for the rest */
694 jix
.dwSize
= sizeof(jix
);
695 jix
.dwFlags
= JOY_RETURNALL
;
696 joyGetPosEx(JOYSTICKID1
,&jix
);
698 #define SCALE(v) ((int) ((v - 32767)/32.768))
700 /* Convert to integer for scaling. */
704 window
->joystick(jix
.dwButtons
, SCALE(x
), SCALE(y
), SCALE(z
));
709 case MM_JOY1BUTTONDOWN
:
710 case MM_JOY1BUTTONUP
:
711 window
= __glutGetWindow(hwnd
);
712 if (window
->joystick
) {
715 /* Because WIN32 only supports messages for X, Y, and Z
716 translations, we must poll for the rest */
717 jix
.dwSize
= sizeof(jix
);
718 jix
.dwFlags
= JOY_RETURNALL
;
719 joyGetPosEx(JOYSTICKID1
,&jix
);
726 /* Miscellaneous messages (don't really need to enumerate them,
727 but it's good to know what you're not getting sometimes). */
728 case WM_DISPLAYCHANGE
:
731 /* This event is generated by every mouse move event. */
745 case WM_NCLBUTTONDOWN
:
757 case WM_WINDOWPOSCHANGING
:
759 case WM_WINDOWPOSCHANGED
:
761 case WM_MOUSEACTIVATE
:
771 case WM_CAPTURECHANGED
:
775 case WM_ENTERSIZEMOVE
:
786 return DefWindowProc(hwnd
, msg
, wParam
, lParam
);