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 void handleTimeouts(void);
21 extern GLUTmenuItem
*__glutGetUniqueMenuItem(GLUTmenu
* menu
, int unique
);
22 static HMENU __glutHMenu
;
25 updateWindowState(GLUTwindow
*window
, int visState
)
29 /* XXX shownState and visState are the same in Win32. */
30 window
->shownState
= visState
;
31 if (visState
!= window
->visState
) {
32 if (window
->windowStatus
) {
33 window
->visState
= visState
;
34 __glutSetWindow(window
);
35 window
->windowStatus(visState
);
38 /* Since Win32 only sends an activate for the toplevel window,
39 update the visibility for all the child windows. */
40 child
= window
->children
;
42 updateWindowState(child
, visState
);
43 child
= child
->siblings
;
48 __glutWindowProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
50 POINT point
; /* Point structure. */
51 PAINTSTRUCT ps
; /* Paint structure. */
52 LPMINMAXINFO minmax
; /* Minimum/maximum info structure. */
53 GLUTwindow
* window
; /* GLUT window associated with message. */
54 GLUTmenu
* menu
; /* GLUT menu associated with message. */
55 int x
, y
, width
, height
, key
;
69 /* XXX NVidia's NT OpenGL can have problems closing down
70 its OpenGL internal data structures if we just allow
71 the process to terminate without unbinding and deleting
72 the windows context. Apparently, DirectDraw unloads
73 before OPENGL32.DLL in the close down sequence, but
74 NVidia's NT OpenGL needs DirectDraw to close down its
76 window
= __glutGetWindow(hwnd
);
79 wglMakeCurrent(NULL
, NULL
);
80 wglDeleteContext(window
->ctx
);
86 window
= __glutGetWindow(hwnd
);
88 BeginPaint(hwnd
, &ps
); /* Must have this for some Win32 reason. */
90 if (window
->win
== hwnd
) {
91 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
92 } else if (window
->overlay
&& window
->overlay
->win
== hwnd
) {
93 __glutPostRedisplay(window
, GLUT_OVERLAY_REPAIR_WORK
);
100 window
= __glutGetWindow(hwnd
);
104 /* Win32 is dumb and sends these messages only to the parent
105 window. Therefore, find out if we're in a child window and
106 call the child windows keyboard callback if we are. */
107 if (window
->parent
) {
108 GetCursorPos(&point
);
109 ScreenToClient(hwnd
, &point
);
110 hwnd
= ChildWindowFromPoint(hwnd
, point
);
111 window
= __glutGetWindow(hwnd
);
113 if (window
->specialUp
|| window
->keyboardUp
) {
114 GetCursorPos(&point
);
115 ScreenToClient(window
->win
, &point
);
116 __glutSetWindow(window
);
117 __glutModifierMask
= 0;
118 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
119 __glutModifierMask
|= ShiftMask
;
120 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
121 __glutModifierMask
|= ControlMask
;
122 if (GetKeyState(VK_MENU
) < 0)
123 __glutModifierMask
|= Mod1Mask
;
126 case VK_F1
: key
= GLUT_KEY_F1
; break;
127 case VK_F2
: key
= GLUT_KEY_F2
; break;
128 case VK_F3
: key
= GLUT_KEY_F3
; break;
129 case VK_F4
: key
= GLUT_KEY_F4
; break;
130 case VK_F5
: key
= GLUT_KEY_F5
; break;
131 case VK_F6
: key
= GLUT_KEY_F6
; break;
132 case VK_F7
: key
= GLUT_KEY_F7
; break;
133 case VK_F8
: key
= GLUT_KEY_F8
; break;
134 case VK_F9
: key
= GLUT_KEY_F9
; break;
135 case VK_F10
: key
= GLUT_KEY_F10
; break;
136 case VK_F11
: key
= GLUT_KEY_F11
; break;
137 case VK_F12
: key
= GLUT_KEY_F12
; break;
138 case VK_LEFT
: key
= GLUT_KEY_LEFT
; break;
139 case VK_UP
: key
= GLUT_KEY_UP
; break;
140 case VK_RIGHT
: key
= GLUT_KEY_RIGHT
; break;
141 case VK_DOWN
: key
= GLUT_KEY_DOWN
; break;
142 case VK_PRIOR
: key
= GLUT_KEY_PAGE_UP
; break;
143 case VK_NEXT
: key
= GLUT_KEY_PAGE_DOWN
; break;
144 case VK_HOME
: key
= GLUT_KEY_HOME
; break;
145 case VK_END
: key
= GLUT_KEY_END
; break;
146 case VK_INSERT
: key
= GLUT_KEY_INSERT
; break;
148 /* Delete is an ASCII character. */
149 if (window
->keyboardUp
) {
150 window
->keyboardUp((unsigned char) 127, point
.x
, point
.y
);
155 if (window
->keyboardUp
) {
156 key
= MapVirtualKey(wParam
, 2); /* Map to ASCII. */
157 if (isascii(key
) && (key
!= 0)) {
159 /* XXX Attempt to determine modified ASCII character
160 is quite incomplete. Digits, symbols, CapsLock,
161 Ctrl, and numeric keypad are all ignored. Fix this. */
163 if (!(__glutModifierMask
& ShiftMask
))
165 window
->keyboardUp((unsigned char) key
, point
.x
, point
.y
);
168 __glutModifierMask
= (unsigned int) ~0;
171 if (window
->specialUp
) {
172 window
->specialUp(key
, point
.x
, point
.y
);
174 __glutModifierMask
= (unsigned int) ~0;
180 window
= __glutGetWindow(hwnd
);
185 /* Bit 30 of lParam is set if key already held down. If
186 we are ignoring auto repeated key strokes for the window, bail. */
187 if (window
->ignoreKeyRepeat
&& (lParam
& (1 << 30)) ) {
191 /* Win32 is dumb and sends these messages only to the parent
192 window. Therefore, find out if we're in a child window and
193 call the child windows keyboard callback if we are. */
194 if (window
->parent
) {
195 GetCursorPos(&point
);
196 ScreenToClient(hwnd
, &point
);
197 hwnd
= ChildWindowFromPoint(hwnd
, point
);
198 window
= __glutGetWindow(hwnd
);
200 if (window
->keyboard
) {
201 GetCursorPos(&point
);
202 ScreenToClient(window
->win
, &point
);
203 __glutSetWindow(window
);
204 __glutModifierMask
= 0;
205 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
206 __glutModifierMask
|= ShiftMask
;
207 if (GetKeyState(VK_CONTROL
) < 0)
208 __glutModifierMask
|= ControlMask
;
209 if (GetKeyState(VK_MENU
) < 0)
210 __glutModifierMask
|= Mod1Mask
;
211 window
->keyboard((unsigned char)wParam
, point
.x
, point
.y
);
212 __glutModifierMask
= (unsigned int) ~0;
218 window
= __glutGetWindow(hwnd
);
223 /* Bit 30 of lParam is set if key already held down. If
224 we are ignoring auto repeated key strokes for the window, bail. */
225 if (window
->ignoreKeyRepeat
&& (lParam
& (1 << 30)) ) {
229 /* Win32 is dumb and sends these messages only to the parent
230 window. Therefore, find out if we're in a child window and
231 call the child windows keyboard callback if we are. */
232 if (window
->parent
) {
233 GetCursorPos(&point
);
234 ScreenToClient(hwnd
, &point
);
235 hwnd
= ChildWindowFromPoint(hwnd
, point
);
236 window
= __glutGetWindow(hwnd
);
238 if (window
->special
) {
242 case VK_F1
: key
= GLUT_KEY_F1
; break;
243 case VK_F2
: key
= GLUT_KEY_F2
; break;
244 case VK_F3
: key
= GLUT_KEY_F3
; break;
245 case VK_F4
: key
= GLUT_KEY_F4
; break;
246 case VK_F5
: key
= GLUT_KEY_F5
; break;
247 case VK_F6
: key
= GLUT_KEY_F6
; break;
248 case VK_F7
: key
= GLUT_KEY_F7
; break;
249 case VK_F8
: key
= GLUT_KEY_F8
; break;
250 case VK_F9
: key
= GLUT_KEY_F9
; break;
251 case VK_F10
: key
= GLUT_KEY_F10
; break;
252 case VK_F11
: key
= GLUT_KEY_F11
; break;
253 case VK_F12
: key
= GLUT_KEY_F12
; break;
254 /* directional keys */
255 case VK_LEFT
: key
= GLUT_KEY_LEFT
; break;
256 case VK_UP
: key
= GLUT_KEY_UP
; break;
257 case VK_RIGHT
: key
= GLUT_KEY_RIGHT
; break;
258 case VK_DOWN
: key
= GLUT_KEY_DOWN
; break;
262 /* VK_PRIOR is Win32's Page Up */
263 key
= GLUT_KEY_PAGE_UP
;
266 /* VK_NEXT is Win32's Page Down */
267 key
= GLUT_KEY_PAGE_DOWN
;
276 key
= GLUT_KEY_INSERT
;
283 GetCursorPos(&point
);
284 ScreenToClient(window
->win
, &point
);
285 __glutSetWindow(window
);
286 __glutModifierMask
= 0;
287 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
288 __glutModifierMask
|= ShiftMask
;
289 if (GetKeyState(VK_CONTROL
) < 0)
290 __glutModifierMask
|= ControlMask
;
291 if (GetKeyState(VK_MENU
) < 0)
292 __glutModifierMask
|= Mod1Mask
;
293 window
->special(key
, point
.x
, point
.y
);
294 __glutModifierMask
= (unsigned int) ~0;
295 } else if (window
->keyboard
) {
296 /* Specially handle any keys that match ASCII values but
297 do not generate Windows WM_SYSCHAR or WM_CHAR messages. */
301 /* Delete is an ASCII character. */
302 GetCursorPos(&point
);
303 ScreenToClient(window
->win
, &point
);
304 __glutSetWindow(window
);
305 __glutModifierMask
= 0;
306 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
307 __glutModifierMask
|= ShiftMask
;
308 if (GetKeyState(VK_CONTROL
) < 0)
309 __glutModifierMask
|= ControlMask
;
310 if (GetKeyState(VK_MENU
) < 0)
311 __glutModifierMask
|= Mod1Mask
;
312 window
->keyboard((unsigned char) 127, point
.x
, point
.y
);
313 __glutModifierMask
= (unsigned int) ~0;
316 /* Let the following WM_SYSCHAR or WM_CHAR message generate
317 the keyboard callback. */
324 button
= GLUT_LEFT_BUTTON
;
327 button
= GLUT_MIDDLE_BUTTON
;
330 button
= GLUT_RIGHT_BUTTON
;
332 /* finish the menu if we get a button down message (user must have
333 cancelled the menu). */
334 if (__glutMappedMenu
) {
335 /* TODO: take this out once the menu on middle mouse stuff works
337 if (button
== GLUT_MIDDLE_BUTTON
)
339 GetCursorPos(&point
);
340 ScreenToClient(hwnd
, &point
);
341 __glutItemSelected
= NULL
;
342 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
346 /* set the capture so we can get mouse events outside the window */
349 /* Win32 doesn't return the same numbers as X does when the mouse
350 goes beyond the upper or left side of the window. roll the
351 Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
354 if(x
& 1 << 15) x
-= (1 << 16);
355 if(y
& 1 << 15) y
-= (1 << 16);
357 window
= __glutGetWindow(hwnd
);
359 menu
= __glutGetMenuByNum(window
->menu
[button
]);
361 point
.x
= LOWORD(lParam
); point
.y
= HIWORD(lParam
);
362 ClientToScreen(window
->win
, &point
);
363 __glutMenuButton
= button
== GLUT_RIGHT_BUTTON
? TPM_RIGHTBUTTON
:
364 button
== GLUT_LEFT_BUTTON
? TPM_LEFTBUTTON
:
366 __glutStartMenu(menu
, window
, point
.x
, point
.y
, x
, y
);
367 } else if (window
->mouse
) {
369 __glutSetWindow(window
);
370 __glutModifierMask
= 0;
371 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on. */
372 __glutModifierMask
|= ShiftMask
;
373 if (GetKeyState(VK_CONTROL
) < 0)
374 __glutModifierMask
|= ControlMask
;
375 if (GetKeyState(VK_MENU
) < 0)
376 __glutModifierMask
|= Mod1Mask
;
377 window
->mouse(button
, GLUT_DOWN
, x
, y
);
378 __glutModifierMask
= (unsigned int)~0;
380 /* Stray mouse events. Ignore. */
386 button
= GLUT_LEFT_BUTTON
;
389 button
= GLUT_MIDDLE_BUTTON
;
392 button
= GLUT_RIGHT_BUTTON
;
394 /* Bail out if we're processing a menu. */
395 if (__glutMappedMenu
) {
396 GetCursorPos(&point
);
397 ScreenToClient(hwnd
, &point
);
398 /* if we're getting the middle button up signal, then something
399 on the menu was selected. */
400 if (button
== GLUT_MIDDLE_BUTTON
) {
402 /* For some reason, the code below always returns -1 even
403 though the point IS IN THE ITEM! Therefore, just bail out if
404 we get a middle mouse up. The user must select using the
405 left mouse button. Stupid Win32. */
407 int item
= MenuItemFromPoint(hwnd
, __glutHMenu
, point
);
409 __glutItemSelected
= (GLUTmenuItem
*)GetMenuItemID(__glutHMenu
, item
);
411 __glutItemSelected
= NULL
;
412 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
415 __glutItemSelected
= NULL
;
416 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
421 /* Release the mouse capture. */
424 window
= __glutGetWindow(hwnd
);
425 if (window
&& window
->mouse
) {
426 /* Win32 doesn't return the same numbers as X does when the
427 mouse goes beyond the upper or left side of the window. roll
428 the Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
431 if(x
& 1 << 15) x
-= (1 << 16);
432 if(y
& 1 << 15) y
-= (1 << 16);
434 __glutSetWindow(window
);
435 __glutModifierMask
= 0;
436 if (GetKeyState(VK_SHIFT
) < 0) /* < 0 = high order bit is on */
437 __glutModifierMask
|= ShiftMask
;
438 if (GetKeyState(VK_CONTROL
) < 0)
439 __glutModifierMask
|= ControlMask
;
440 if (GetKeyState(VK_MENU
) < 0)
441 __glutModifierMask
|= Mod1Mask
;
442 window
->mouse(button
, GLUT_UP
, x
, y
);
443 __glutModifierMask
= (unsigned int)~0;
445 /* Window might have been destroyed and all the
446 events for the window may not yet be received. */
450 case WM_ENTERMENULOOP
:
451 /* KLUDGE: create a timer that fires every 100 ms when we start a
452 menu so that we can still process the idle & timer events (that
453 way, the timers will fire during a menu pick and so will the
455 SetTimer(hwnd
, 1, 1, NULL
);
460 /* If the timer id is 2, then this is the timer that is set up in
461 the main glut message processing loop, and we don't want to do
462 anything but acknowledge that we got it. It is used to prevent
463 CPU spiking when an idle function is installed. */
468 /* only worry about the idle function and the timeouts, since
469 these are the only events we expect to process during
470 processing of a menu. */
471 /* we no longer process the idle functions (as outlined in the
472 README), since drawing can't be done until the menu has
473 finished...it's pretty lame when the animation goes on, but
474 doesn't update, so you get this weird jerkiness. */
483 case WM_EXITMENULOOP
:
484 /* nuke the above created timer...we don't need it anymore, since
485 the menu is gone now. */
491 __glutHMenu
= (HMENU
)lParam
;
495 if (__glutMappedMenu
) {
496 if (GetSubMenu(__glutHMenu
, LOWORD(wParam
)))
497 __glutItemSelected
= NULL
;
500 __glutGetUniqueMenuItem(__glutMappedMenu
, LOWORD(wParam
));
501 GetCursorPos(&point
);
502 ScreenToClient(hwnd
, &point
);
503 __glutFinishMenu(hwnd
, point
.x
, point
.y
);
508 if (!__glutMappedMenu
) {
509 window
= __glutGetWindow(hwnd
);
511 /* If motion function registered _and_ buttons held *
512 down, call motion function... */
516 /* Win32 doesn't return the same numbers as X does when the
517 mouse goes beyond the upper or left side of the window.
518 roll the Win32's 0..2^16 pointer co-ord range to 0..+/-2^15. */
519 if(x
& 1 << 15) x
-= (1 << 16);
520 if(y
& 1 << 15) y
-= (1 << 16);
522 if (window
->motion
&& wParam
&
523 (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) {
524 __glutSetWindow(window
);
525 window
->motion(x
, y
);
527 /* If passive motion function registered _and_
528 buttons not held down, call passive motion
530 else if (window
->passive
&&
532 (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) ==
534 __glutSetWindow(window
);
535 window
->passive(x
, y
);
539 /* Motion events are thrown away when a pop up menu is
544 case WM_GETMINMAXINFO
:
545 /* this voodoo is brought to you by Win32 (again). It allows the
546 window to be bigger than the screen, and smaller than 100x100
547 (although it doesn't seem to help the y minimum). */
548 minmax
= (LPMINMAXINFO
)lParam
;
549 minmax
->ptMaxSize
.x
= __glutScreenWidth
;
550 minmax
->ptMaxSize
.y
= __glutScreenHeight
;
551 minmax
->ptMinTrackSize
.x
= 0;
552 minmax
->ptMinTrackSize
.y
= 0;
553 minmax
->ptMaxTrackSize
.x
= __glutScreenWidth
+
554 GetSystemMetrics(SM_CXSIZE
) * 2;
555 minmax
->ptMaxTrackSize
.y
= __glutScreenHeight
+
556 GetSystemMetrics(SM_CXSIZE
) * 2 + GetSystemMetrics(SM_CYCAPTION
);
560 window
= __glutGetWindow(hwnd
);
562 width
= LOWORD(lParam
);
563 height
= HIWORD(lParam
);
564 if (width
!= window
->width
|| height
!= window
->height
) {
565 #if 0 /* Win32 GLUT does not support overlays for now. */
566 if (window
->overlay
) {
567 XResizeWindow(__glutDisplay
, window
->overlay
->win
, width
, height
);
570 window
->width
= width
;
571 window
->height
= height
;
572 __glutSetWindow(window
);
573 /* Do not execute OpenGL out of sequence with respect
574 to the SetWindowPos request! */
576 window
->reshape(width
, height
);
577 window
->forceReshape
= FALSE
;
578 /* A reshape should be considered like posting a
580 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
586 /* If the cursor is not in the client area, then we want to send
587 this message to the default window procedure ('cause its
588 probably in the border or title, and we don't handle that
589 cursor. otherwise, set our cursor. Win32 makes us set the
590 cursor every time the mouse moves (DUMB!). */
591 if(LOWORD(lParam
) != HTCLIENT
) {
594 window
= __glutGetWindow(hwnd
);
596 __glutSetCursor(window
);
598 /* TODO: check out the info in DevStudio on WM_SETCURSOR in the
599 DefaultAction section. */
603 window
= __glutGetWindow(hwnd
);
605 window
->entryState
= WM_SETFOCUS
;
607 __glutSetWindow(window
);
608 window
->entry(GLUT_ENTERED
);
609 /* XXX Generation of fake passive notify? See how much
610 work the X11 code does to support fake passive notify
613 if (window
->joystick
&& __glutCurrentWindow
) {
614 if (__glutCurrentWindow
->joyPollInterval
> 0) {
617 /* Because Win32 will only let one window capture the
618 joystick at a time, we must capture it when we get the
619 focus and release it when we lose the focus. */
620 result
= joySetCapture(__glutCurrentWindow
->win
,
621 JOYSTICKID1
, 0, TRUE
);
622 if (result
!= JOYERR_NOERROR
) {
625 (void) joySetThreshold(JOYSTICKID1
,
626 __glutCurrentWindow
->joyPollInterval
);
633 window
= __glutGetWindow(hwnd
);
635 window
->entryState
= WM_KILLFOCUS
;
637 __glutSetWindow(window
);
638 window
->entry(GLUT_LEFT
);
640 if (window
->joystick
&& __glutCurrentWindow
) {
641 if (__glutCurrentWindow
->joyPollInterval
> 0) {
642 /* Because Win32 will only let one window capture the
643 joystick at a time, we must capture it when we get the
644 focus and release it when we lose the focus. */
645 (void) joyReleaseCapture(JOYSTICKID1
);
651 window
= __glutGetWindow(hwnd
);
652 /* Make sure we re-select the correct palette if needed. */
653 if (LOWORD(wParam
)) {
654 PostMessage(hwnd
, WM_PALETTECHANGED
, 0, 0);
659 /* HIWORD(wParam) is the minimized flag. */
660 visState
= !HIWORD(wParam
);
661 updateWindowState(window
, visState
);
665 /* Colour Palette Management */
666 case WM_PALETTECHANGED
:
667 if (hwnd
== (HWND
)wParam
) {
668 /* Don't respond to the message that we sent! */
671 /* fall through to WM_QUERYNEWPALETTE */
673 case WM_QUERYNEWPALETTE
:
674 window
= __glutGetWindow(hwnd
);
675 if (window
&& window
->colormap
) {
676 UnrealizeObject(window
->colormap
->cmap
);
677 SelectPalette(window
->hdc
, window
->colormap
->cmap
, FALSE
);
678 RealizePalette(window
->hdc
);
685 window
= __glutGetWindow(hwnd
);
686 if (window
->joystick
) {
690 /* Because WIN32 only supports messages for X, Y, and Z
691 translations, we must poll for the rest */
692 jix
.dwSize
= sizeof(jix
);
693 jix
.dwFlags
= JOY_RETURNALL
;
694 joyGetPosEx(JOYSTICKID1
,&jix
);
696 #define SCALE(v) ((int) ((v - 32767)/32.768))
698 /* Convert to integer for scaling. */
702 window
->joystick(jix
.dwButtons
, SCALE(x
), SCALE(y
), SCALE(z
));
707 case MM_JOY1BUTTONDOWN
:
708 case MM_JOY1BUTTONUP
:
709 window
= __glutGetWindow(hwnd
);
710 if (window
->joystick
) {
713 /* Because WIN32 only supports messages for X, Y, and Z
714 translations, we must poll for the rest */
715 jix
.dwSize
= sizeof(jix
);
716 jix
.dwFlags
= JOY_RETURNALL
;
717 joyGetPosEx(JOYSTICKID1
,&jix
);
724 /* Miscellaneous messages (don't really need to enumerate them,
725 but it's good to know what you're not getting sometimes). */
726 case WM_DISPLAYCHANGE
:
729 /* This event is generated by every mouse move event. */
743 case WM_NCLBUTTONDOWN
:
755 case WM_WINDOWPOSCHANGING
:
757 case WM_WINDOWPOSCHANGED
:
759 case WM_MOUSEACTIVATE
:
769 case WM_CAPTURECHANGED
:
773 case WM_ENTERSIZEMOVE
:
784 return DefWindowProc(hwnd
, msg
, wParam
, lParam
);