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>
13 #if defined(_WIN32) && !defined(__CYGWIN32__)
14 #include <mmsystem.h> /* Win32 Multimedia API header. */
17 extern unsigned __glutMenuButton
;
18 extern GLUTidleCB __glutIdleFunc
;
19 extern GLUTtimer
*__glutTimerList
;
20 extern GLUTmenuItem
*__glutGetUniqueMenuItem(GLUTmenu
* menu
, int unique
);
21 static HMENU __glutHMenu
;
24 updateWindowState(GLUTwindow
*window
, int visState
)
28 /* XXX shownState and visState are the same in Win32. */
29 window
->shownState
= visState
;
30 if (visState
!= window
->visState
) {
31 if (window
->windowStatus
) {
32 window
->visState
= visState
;
33 __glutSetWindow(window
);
34 window
->windowStatus(visState
);
37 /* Since Win32 only sends an activate for the toplevel window,
38 update the visibility for all the child windows. */
39 child
= window
->children
;
41 updateWindowState(child
, visState
);
42 child
= child
->siblings
;
47 __glutWindowProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
49 POINT point
; /* Point structure. */
50 PAINTSTRUCT ps
; /* Paint structure. */
51 LPMINMAXINFO minmax
; /* Minimum/maximum info structure. */
52 GLUTwindow
* window
; /* GLUT window associated with message. */
53 GLUTmenu
* menu
; /* GLUT menu associated with message. */
54 int x
, y
, width
, height
, key
;
68 /* XXX NVidia's NT OpenGL can have problems closing down
69 its OpenGL internal data structures if we just allow
70 the process to terminate without unbinding and deleting
71 the windows context. Apparently, DirectDraw unloads
72 before OPENGL32.DLL in the close down sequence, but
73 NVidia's NT OpenGL needs DirectDraw to close down its
75 window
= __glutGetWindow(hwnd
);
78 wglMakeCurrent(NULL
, NULL
);
79 wglDeleteContext(window
->ctx
);
85 window
= __glutGetWindow(hwnd
);
87 BeginPaint(hwnd
, &ps
); /* Must have this for some Win32 reason. */
89 if (window
->win
== hwnd
) {
90 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
91 } else if (window
->overlay
&& window
->overlay
->win
== hwnd
) {
92 __glutPostRedisplay(window
, GLUT_OVERLAY_REPAIR_WORK
);
99 window
= __glutGetWindow(hwnd
);
103 /* Win32 is dumb and sends these messages only to the parent
104 window. Therefore, find out if we're in a child window and
105 call the child windows keyboard callback if we are. */
106 if (window
->parent
) {
107 GetCursorPos(&point
);
108 ScreenToClient(hwnd
, &point
);
109 hwnd
= ChildWindowFromPoint(hwnd
, point
);
110 window
= __glutGetWindow(hwnd
);
112 if (window
->specialUp
|| window
->keyboardUp
) {
113 GetCursorPos(&point
);
114 ScreenToClient(window
->win
, &point
);
115 __glutSetWindow(window
);
116 __glutModifierMask
= 0;
117 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
118 __glutModifierMask
|= ShiftMask
;
119 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
120 __glutModifierMask
|= ControlMask
;
121 if (GetKeyState(VK_MENU
) < 0)
122 __glutModifierMask
|= Mod1Mask
;
125 case VK_F1
: key
= GLUT_KEY_F1
; break;
126 case VK_F2
: key
= GLUT_KEY_F2
; break;
127 case VK_F3
: key
= GLUT_KEY_F3
; break;
128 case VK_F4
: key
= GLUT_KEY_F4
; break;
129 case VK_F5
: key
= GLUT_KEY_F5
; break;
130 case VK_F6
: key
= GLUT_KEY_F6
; break;
131 case VK_F7
: key
= GLUT_KEY_F7
; break;
132 case VK_F8
: key
= GLUT_KEY_F8
; break;
133 case VK_F9
: key
= GLUT_KEY_F9
; break;
134 case VK_F10
: key
= GLUT_KEY_F10
; break;
135 case VK_F11
: key
= GLUT_KEY_F11
; break;
136 case VK_F12
: key
= GLUT_KEY_F12
; break;
137 case VK_LEFT
: key
= GLUT_KEY_LEFT
; break;
138 case VK_UP
: key
= GLUT_KEY_UP
; break;
139 case VK_RIGHT
: key
= GLUT_KEY_RIGHT
; break;
140 case VK_DOWN
: key
= GLUT_KEY_DOWN
; break;
141 case VK_PRIOR
: key
= GLUT_KEY_PAGE_UP
; break;
142 case VK_NEXT
: key
= GLUT_KEY_PAGE_DOWN
; break;
143 case VK_HOME
: key
= GLUT_KEY_HOME
; break;
144 case VK_END
: key
= GLUT_KEY_END
; break;
145 case VK_INSERT
: key
= GLUT_KEY_INSERT
; break;
147 /* Delete is an ASCII character. */
148 if (window
->keyboardUp
) {
149 window
->keyboardUp((unsigned char) 127, point
.x
, point
.y
);
154 if (window
->keyboardUp
) {
155 key
= MapVirtualKey(wParam
, 2); /* Map to ASCII. */
156 if (isascii(key
) && (key
!= 0)) {
158 /* XXX Attempt to determine modified ASCII character
159 is quite incomplete. Digits, symbols, CapsLock,
160 Ctrl, and numeric keypad are all ignored. Fix this. */
162 if (!(__glutModifierMask
& ShiftMask
))
164 window
->keyboardUp((unsigned char) key
, point
.x
, point
.y
);
167 __glutModifierMask
= (unsigned int) ~0;
170 if (window
->specialUp
) {
171 window
->specialUp(key
, point
.x
, point
.y
);
173 __glutModifierMask
= (unsigned int) ~0;
179 window
= __glutGetWindow(hwnd
);
184 /* Bit 30 of lParam is set if key already held down. If
185 we are ignoring auto repeated key strokes for the window, bail. */
186 if (window
->ignoreKeyRepeat
&& (lParam
& (1 << 30)) ) {
190 /* Win32 is dumb and sends these messages only to the parent
191 window. Therefore, find out if we're in a child window and
192 call the child windows keyboard callback if we are. */
193 if (window
->parent
) {
194 GetCursorPos(&point
);
195 ScreenToClient(hwnd
, &point
);
196 hwnd
= ChildWindowFromPoint(hwnd
, point
);
197 window
= __glutGetWindow(hwnd
);
199 if (window
->keyboard
) {
200 GetCursorPos(&point
);
201 ScreenToClient(window
->win
, &point
);
202 __glutSetWindow(window
);
203 __glutModifierMask
= 0;
204 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
205 __glutModifierMask
|= ShiftMask
;
206 if (GetKeyState(VK_CONTROL
) < 0)
207 __glutModifierMask
|= ControlMask
;
208 if (GetKeyState(VK_MENU
) < 0)
209 __glutModifierMask
|= Mod1Mask
;
210 window
->keyboard((unsigned char)wParam
, point
.x
, point
.y
);
211 __glutModifierMask
= (unsigned int) ~0;
217 window
= __glutGetWindow(hwnd
);
222 /* Bit 30 of lParam is set if key already held down. If
223 we are ignoring auto repeated key strokes for the window, bail. */
224 if (window
->ignoreKeyRepeat
&& (lParam
& (1 << 30)) ) {
228 /* Win32 is dumb and sends these messages only to the parent
229 window. Therefore, find out if we're in a child window and
230 call the child windows keyboard callback if we are. */
231 if (window
->parent
) {
232 GetCursorPos(&point
);
233 ScreenToClient(hwnd
, &point
);
234 hwnd
= ChildWindowFromPoint(hwnd
, point
);
235 window
= __glutGetWindow(hwnd
);
237 if (window
->special
) {
241 case VK_F1
: key
= GLUT_KEY_F1
; break;
242 case VK_F2
: key
= GLUT_KEY_F2
; break;
243 case VK_F3
: key
= GLUT_KEY_F3
; break;
244 case VK_F4
: key
= GLUT_KEY_F4
; break;
245 case VK_F5
: key
= GLUT_KEY_F5
; break;
246 case VK_F6
: key
= GLUT_KEY_F6
; break;
247 case VK_F7
: key
= GLUT_KEY_F7
; break;
248 case VK_F8
: key
= GLUT_KEY_F8
; break;
249 case VK_F9
: key
= GLUT_KEY_F9
; break;
250 case VK_F10
: key
= GLUT_KEY_F10
; break;
251 case VK_F11
: key
= GLUT_KEY_F11
; break;
252 case VK_F12
: key
= GLUT_KEY_F12
; break;
253 /* directional keys */
254 case VK_LEFT
: key
= GLUT_KEY_LEFT
; break;
255 case VK_UP
: key
= GLUT_KEY_UP
; break;
256 case VK_RIGHT
: key
= GLUT_KEY_RIGHT
; break;
257 case VK_DOWN
: key
= GLUT_KEY_DOWN
; break;
261 /* VK_PRIOR is Win32's Page Up */
262 key
= GLUT_KEY_PAGE_UP
;
265 /* VK_NEXT is Win32's Page Down */
266 key
= GLUT_KEY_PAGE_DOWN
;
275 key
= GLUT_KEY_INSERT
;
282 GetCursorPos(&point
);
283 ScreenToClient(window
->win
, &point
);
284 __glutSetWindow(window
);
285 __glutModifierMask
= 0;
286 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
287 __glutModifierMask
|= ShiftMask
;
288 if (GetKeyState(VK_CONTROL
) < 0)
289 __glutModifierMask
|= ControlMask
;
290 if (GetKeyState(VK_MENU
) < 0)
291 __glutModifierMask
|= Mod1Mask
;
292 window
->special(key
, point
.x
, point
.y
);
293 __glutModifierMask
= (unsigned int) ~0;
294 } else if (window
->keyboard
) {
295 /* Specially handle any keys that match ASCII values but
296 do not generate Windows WM_SYSCHAR or WM_CHAR messages. */
300 /* Delete is an ASCII character. */
301 GetCursorPos(&point
);
302 ScreenToClient(window
->win
, &point
);
303 __glutSetWindow(window
);
304 __glutModifierMask
= 0;
305 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
306 __glutModifierMask
|= ShiftMask
;
307 if (GetKeyState(VK_CONTROL
) < 0)
308 __glutModifierMask
|= ControlMask
;
309 if (GetKeyState(VK_MENU
) < 0)
310 __glutModifierMask
|= Mod1Mask
;
311 window
->keyboard((unsigned char) 127, point
.x
, point
.y
);
312 __glutModifierMask
= (unsigned int) ~0;
315 /* Let the following WM_SYSCHAR or WM_CHAR message generate
316 the keyboard callback. */
323 button
= GLUT_LEFT_BUTTON
;
326 button
= GLUT_MIDDLE_BUTTON
;
329 button
= GLUT_RIGHT_BUTTON
;
331 /* finish the menu if we get a button down message (user must have
332 cancelled the menu). */
333 if (__glutMappedMenu
) {
334 /* TODO: take this out once the menu on middle mouse stuff works
336 if (button
== GLUT_MIDDLE_BUTTON
)
338 GetCursorPos(&point
);
339 ScreenToClient(hwnd
, &point
);
340 __glutItemSelected
= NULL
;
341 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
345 /* set the capture so we can get mouse events outside the window */
348 /* Win32 doesn't return the same numbers as X does when the mouse
349 goes beyond the upper or left side of the window. roll the
350 Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
353 if(x
& 1 << 15) x
-= (1 << 16);
354 if(y
& 1 << 15) y
-= (1 << 16);
356 window
= __glutGetWindow(hwnd
);
358 menu
= __glutGetMenuByNum(window
->menu
[button
]);
360 point
.x
= LOWORD(lParam
); point
.y
= HIWORD(lParam
);
361 ClientToScreen(window
->win
, &point
);
362 __glutMenuButton
= button
== GLUT_RIGHT_BUTTON
? TPM_RIGHTBUTTON
:
363 button
== GLUT_LEFT_BUTTON
? TPM_LEFTBUTTON
:
365 __glutStartMenu(menu
, window
, point
.x
, point
.y
, x
, y
);
366 } else if (window
->mouse
) {
368 __glutSetWindow(window
);
369 __glutModifierMask
= 0;
370 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on. */
371 __glutModifierMask
|= ShiftMask
;
372 if (GetKeyState(VK_CONTROL
) < 0)
373 __glutModifierMask
|= ControlMask
;
374 if (GetKeyState(VK_MENU
) < 0)
375 __glutModifierMask
|= Mod1Mask
;
376 window
->mouse(button
, GLUT_DOWN
, x
, y
);
377 __glutModifierMask
= (unsigned int)~0;
379 /* Stray mouse events. Ignore. */
385 button
= GLUT_LEFT_BUTTON
;
388 button
= GLUT_MIDDLE_BUTTON
;
391 button
= GLUT_RIGHT_BUTTON
;
393 /* Bail out if we're processing a menu. */
394 if (__glutMappedMenu
) {
395 GetCursorPos(&point
);
396 ScreenToClient(hwnd
, &point
);
397 /* if we're getting the middle button up signal, then something
398 on the menu was selected. */
399 if (button
== GLUT_MIDDLE_BUTTON
) {
401 /* For some reason, the code below always returns -1 even
402 though the point IS IN THE ITEM! Therefore, just bail out if
403 we get a middle mouse up. The user must select using the
404 left mouse button. Stupid Win32. */
406 int item
= MenuItemFromPoint(hwnd
, __glutHMenu
, point
);
408 __glutItemSelected
= (GLUTmenuItem
*)GetMenuItemID(__glutHMenu
, item
);
410 __glutItemSelected
= NULL
;
411 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
414 __glutItemSelected
= NULL
;
415 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
420 /* Release the mouse capture. */
423 window
= __glutGetWindow(hwnd
);
424 if (window
&& window
->mouse
) {
425 /* Win32 doesn't return the same numbers as X does when the
426 mouse goes beyond the upper or left side of the window. roll
427 the Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
430 if(x
& 1 << 15) x
-= (1 << 16);
431 if(y
& 1 << 15) y
-= (1 << 16);
433 __glutSetWindow(window
);
434 __glutModifierMask
= 0;
435 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
436 __glutModifierMask
|= ShiftMask
;
437 if (GetKeyState(VK_CONTROL
) < 0)
438 __glutModifierMask
|= ControlMask
;
439 if (GetKeyState(VK_MENU
) < 0)
440 __glutModifierMask
|= Mod1Mask
;
441 window
->mouse(button
, GLUT_UP
, x
, y
);
442 __glutModifierMask
= (unsigned int)~0;
444 /* Window might have been destroyed and all the
445 events for the window may not yet be received. */
449 case WM_ENTERMENULOOP
:
450 /* KLUDGE: create a timer that fires every 100 ms when we start a
451 menu so that we can still process the idle & timer events (that
452 way, the timers will fire during a menu pick and so will the
454 SetTimer(hwnd
, 1, 1, NULL
);
459 /* If the timer id is 2, then this is the timer that is set up in
460 the main glut message processing loop, and we don't want to do
461 anything but acknowledge that we got it. It is used to prevent
462 CPU spiking when an idle function is installed. */
467 /* only worry about the idle function and the timeouts, since
468 these are the only events we expect to process during
469 processing of a menu. */
470 /* we no longer process the idle functions (as outlined in the
471 README), since drawing can't be done until the menu has
472 finished...it's pretty lame when the animation goes on, but
473 doesn't update, so you get this weird jerkiness. */
482 case WM_EXITMENULOOP
:
483 /* nuke the above created timer...we don't need it anymore, since
484 the menu is gone now. */
490 __glutHMenu
= (HMENU
)lParam
;
494 if (__glutMappedMenu
) {
495 if (GetSubMenu(__glutHMenu
, LOWORD(wParam
)))
496 __glutItemSelected
= NULL
;
499 __glutGetUniqueMenuItem(__glutMappedMenu
, LOWORD(wParam
));
500 GetCursorPos(&point
);
501 ScreenToClient(hwnd
, &point
);
502 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
507 if (!__glutMappedMenu
) {
508 window
= __glutGetWindow(hwnd
);
510 /* If motion function registered _and_ buttons held *
511 down, call motion function... */
515 /* Win32 doesn't return the same numbers as X does when the
516 mouse goes beyond the upper or left side of the window.
517 roll the Win32's 0..2^16 pointer co-ord range to 0..+/-2^15. */
518 if(x
& 1 << 15) x
-= (1 << 16);
519 if(y
& 1 << 15) y
-= (1 << 16);
521 if (window
->motion
&& wParam
&
522 (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
523 __glutSetWindow(window
);
524 window
->motion(x
, y
);
526 /* If passive motion function registered _and_
527 buttons not held down, call passive motion
529 else if (window
->passive
&&
531 (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) ==
533 __glutSetWindow(window
);
534 window
->passive(x
, y
);
538 /* Motion events are thrown away when a pop up menu is
543 case WM_GETMINMAXINFO
:
544 /* this voodoo is brought to you by Win32 (again). It allows the
545 window to be bigger than the screen, and smaller than 100x100
546 (although it doesn't seem to help the y minimum). */
547 minmax
= (LPMINMAXINFO
)lParam
;
548 minmax
->ptMaxSize
.x
= __glutScreenWidth
;
549 minmax
->ptMaxSize
.y
= __glutScreenHeight
;
550 minmax
->ptMinTrackSize
.x
= 0;
551 minmax
->ptMinTrackSize
.y
= 0;
552 minmax
->ptMaxTrackSize
.x
= __glutScreenWidth
+
553 GetSystemMetrics(SM_CXSIZE
) * 2;
554 minmax
->ptMaxTrackSize
.y
= __glutScreenHeight
+
555 GetSystemMetrics(SM_CXSIZE
) * 2 + GetSystemMetrics(SM_CYCAPTION
);
559 window
= __glutGetWindow(hwnd
);
561 width
= LOWORD(lParam
);
562 height
= HIWORD(lParam
);
563 if (width
!= window
->width
|| height
!= window
->height
) {
564 #if 0 /* Win32 GLUT does not support overlays for now. */
565 if (window
->overlay
) {
566 XResizeWindow(__glutDisplay
, window
->overlay
->win
, width
, height
);
569 window
->width
= width
;
570 window
->height
= height
;
571 __glutSetWindow(window
);
572 /* Do not execute OpenGL out of sequence with respect
573 to the SetWindowPos request! */
575 window
->reshape(width
, height
);
576 window
->forceReshape
= FALSE
;
577 /* A reshape should be considered like posting a
579 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
585 /* If the cursor is not in the client area, then we want to send
586 this message to the default window procedure ('cause its
587 probably in the border or title, and we don't handle that
588 cursor. otherwise, set our cursor. Win32 makes us set the
589 cursor every time the mouse moves (DUMB!). */
590 if(LOWORD(lParam
) != HTCLIENT
) {
593 window
= __glutGetWindow(hwnd
);
595 __glutSetCursor(window
);
597 /* TODO: check out the info in DevStudio on WM_SETCURSOR in the
598 DefaultAction section. */
602 window
= __glutGetWindow(hwnd
);
604 window
->entryState
= WM_SETFOCUS
;
606 __glutSetWindow(window
);
607 window
->entry(GLUT_ENTERED
);
608 /* XXX Generation of fake passive notify? See how much
609 work the X11 code does to support fake passive notify
612 if (window
->joystick
&& __glutCurrentWindow
) {
613 if (__glutCurrentWindow
->joyPollInterval
> 0) {
616 /* Because Win32 will only let one window capture the
617 joystick at a time, we must capture it when we get the
618 focus and release it when we lose the focus. */
619 result
= joySetCapture(__glutCurrentWindow
->win
,
620 JOYSTICKID1
, 0, TRUE
);
621 if (result
!= JOYERR_NOERROR
) {
624 (void) joySetThreshold(JOYSTICKID1
,
625 __glutCurrentWindow
->joyPollInterval
);
632 window
= __glutGetWindow(hwnd
);
634 window
->entryState
= WM_KILLFOCUS
;
636 __glutSetWindow(window
);
637 window
->entry(GLUT_LEFT
);
639 if (window
->joystick
&& __glutCurrentWindow
) {
640 if (__glutCurrentWindow
->joyPollInterval
> 0) {
641 /* Because Win32 will only let one window capture the
642 joystick at a time, we must capture it when we get the
643 focus and release it when we lose the focus. */
644 (void) joyReleaseCapture(JOYSTICKID1
);
650 window
= __glutGetWindow(hwnd
);
651 /* Make sure we re-select the correct palette if needed. */
652 if (LOWORD(wParam
)) {
653 PostMessage(hwnd
, WM_PALETTECHANGED
, 0, 0);
658 /* HIWORD(wParam) is the minimized flag. */
659 visState
= !HIWORD(wParam
);
660 updateWindowState(window
, visState
);
664 /* Colour Palette Management */
665 case WM_PALETTECHANGED
:
666 if (hwnd
== (HWND
)wParam
) {
667 /* Don't respond to the message that we sent! */
670 /* fall through to WM_QUERYNEWPALETTE */
672 case WM_QUERYNEWPALETTE
:
673 window
= __glutGetWindow(hwnd
);
674 if (window
&& window
->colormap
) {
675 UnrealizeObject(window
->colormap
->cmap
);
676 SelectPalette(window
->hdc
, window
->colormap
->cmap
, FALSE
);
677 RealizePalette(window
->hdc
);
684 window
= __glutGetWindow(hwnd
);
685 if (window
->joystick
) {
689 /* Because WIN32 only supports messages for X, Y, and Z
690 translations, we must poll for the rest */
691 jix
.dwSize
= sizeof(jix
);
692 jix
.dwFlags
= JOY_RETURNALL
;
693 joyGetPosEx(JOYSTICKID1
,&jix
);
695 #define SCALE(v) ((int) ((v - 32767)/32.768))
697 /* Convert to integer for scaling. */
701 window
->joystick(jix
.dwButtons
, SCALE(x
), SCALE(y
), SCALE(z
));
706 case MM_JOY1BUTTONDOWN
:
707 case MM_JOY1BUTTONUP
:
708 window
= __glutGetWindow(hwnd
);
709 if (window
->joystick
) {
712 /* Because WIN32 only supports messages for X, Y, and Z
713 translations, we must poll for the rest */
714 jix
.dwSize
= sizeof(jix
);
715 jix
.dwFlags
= JOY_RETURNALL
;
716 joyGetPosEx(JOYSTICKID1
,&jix
);
723 /* Miscellaneous messages (don't really need to enumerate them,
724 but it's good to know what you're not getting sometimes). */
725 case WM_DISPLAYCHANGE
:
728 /* This event is generated by every mouse move event. */
742 case WM_NCLBUTTONDOWN
:
754 case WM_WINDOWPOSCHANGING
:
756 case WM_WINDOWPOSCHANGED
:
758 case WM_MOUSEACTIVATE
:
768 case WM_CAPTURECHANGED
:
772 case WM_ENTERSIZEMOVE
:
783 return DefWindowProc(hwnd
, msg
, wParam
, lParam
);