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;
552
/* These two lines are disabled to fix incorrect handling of
553
* window maximization on Vista. See bug 23182.
555
minmax->ptMaxSize.x = __glutScreenWidth;
556
minmax->ptMaxSize.y = __glutScreenHeight;
558
minmax->ptMinTrackSize.x = 0;
559
minmax->ptMinTrackSize.y = 0;
560
minmax->ptMaxTrackSize.x = __glutScreenWidth +
561
GetSystemMetrics(SM_CXSIZE) * 2;
562
minmax->ptMaxTrackSize.y = __glutScreenHeight +
563
GetSystemMetrics(SM_CXSIZE) * 2 + GetSystemMetrics(SM_CYCAPTION);
567
window = __glutGetWindow(hwnd);
569
width = LOWORD(lParam);
570
height = HIWORD(lParam);
571
if (width != window->width || height != window->height) {
572
#if 0 /* Win32 GLUT does not support overlays for now. */
573
if (window->overlay) {
574
XResizeWindow(__glutDisplay, window->overlay->win, width, height);
577
window->width = width;
578
window->height = height;
579
__glutSetWindow(window);
580
/* Do not execute OpenGL out of sequence with respect
581
to the SetWindowPos request! */
583
window->reshape(width, height);
584
window->forceReshape = FALSE;
585
/* A reshape should be considered like posting a
587
__glutPostRedisplay(window, GLUT_REPAIR_WORK);
593
/* If the cursor is not in the client area, then we want to send
594
this message to the default window procedure ('cause its
595
probably in the border or title, and we don't handle that
596
cursor. otherwise, set our cursor. Win32 makes us set the
597
cursor every time the mouse moves (DUMB!). */
598
if(LOWORD(lParam) != HTCLIENT) {
601
window = __glutGetWindow(hwnd);
603
__glutSetCursor(window);
605
/* TODO: check out the info in DevStudio on WM_SETCURSOR in the
606
DefaultAction section. */
610
window = __glutGetWindow(hwnd);
612
window->entryState = WM_SETFOCUS;
614
__glutSetWindow(window);
615
window->entry(GLUT_ENTERED);
616
/* XXX Generation of fake passive notify? See how much
617
work the X11 code does to support fake passive notify
620
if (window->joystick && __glutCurrentWindow) {
621
if (__glutCurrentWindow->joyPollInterval > 0) {
624
/* Because Win32 will only let one window capture the
625
joystick at a time, we must capture it when we get the
626
focus and release it when we lose the focus. */
627
result = joySetCapture(__glutCurrentWindow->win,
628
JOYSTICKID1, 0, TRUE);
629
if (result != JOYERR_NOERROR) {
632
(void) joySetThreshold(JOYSTICKID1,
633
__glutCurrentWindow->joyPollInterval);
640
window = __glutGetWindow(hwnd);
642
window->entryState = WM_KILLFOCUS;
644
__glutSetWindow(window);
645
window->entry(GLUT_LEFT);
647
if (window->joystick && __glutCurrentWindow) {
648
if (__glutCurrentWindow->joyPollInterval > 0) {
649
/* Because Win32 will only let one window capture the
650
joystick at a time, we must capture it when we get the
651
focus and release it when we lose the focus. */
652
(void) joyReleaseCapture(JOYSTICKID1);
658
window = __glutGetWindow(hwnd);
659
/* Make sure we re-select the correct palette if needed. */
660
if (LOWORD(wParam)) {
661
PostMessage(hwnd, WM_PALETTECHANGED, 0, 0);
666
/* HIWORD(wParam) is the minimized flag. */
667
visState = !HIWORD(wParam);
668
updateWindowState(window, visState);
672
/* Colour Palette Management */
673
case WM_PALETTECHANGED:
674
if (hwnd == (HWND)wParam) {
675
/* Don't respond to the message that we sent! */
678
/* fall through to WM_QUERYNEWPALETTE */
680
case WM_QUERYNEWPALETTE:
681
window = __glutGetWindow(hwnd);
682
if (window && window->colormap) {
683
UnrealizeObject(window->colormap->cmap);
684
SelectPalette(window->hdc, window->colormap->cmap, FALSE);
685
RealizePalette(window->hdc);
692
window = __glutGetWindow(hwnd);
693
if (window->joystick) {
697
/* Because WIN32 only supports messages for X, Y, and Z
698
translations, we must poll for the rest */
699
jix.dwSize = sizeof(jix);
700
jix.dwFlags = JOY_RETURNALL;
701
joyGetPosEx(JOYSTICKID1,&jix);
703
#define SCALE(v) ((int) ((v - 32767)/32.768))
705
/* Convert to integer for scaling. */
709
window->joystick(jix.dwButtons, SCALE(x), SCALE(y), SCALE(z));
714
case MM_JOY1BUTTONDOWN:
715
case MM_JOY1BUTTONUP:
716
window = __glutGetWindow(hwnd);
717
if (window->joystick) {
720
/* Because WIN32 only supports messages for X, Y, and Z
721
translations, we must poll for the rest */
722
jix.dwSize = sizeof(jix);
723
jix.dwFlags = JOY_RETURNALL;
724
joyGetPosEx(JOYSTICKID1,&jix);
731
/* Miscellaneous messages (don't really need to enumerate them,
732
but it's good to know what you're not getting sometimes). */
733
case WM_DISPLAYCHANGE:
736
/* This event is generated by every mouse move event. */
750
case WM_NCLBUTTONDOWN:
762
case WM_WINDOWPOSCHANGING:
764
case WM_WINDOWPOSCHANGED:
766
case WM_MOUSEACTIVATE:
776
case WM_CAPTURECHANGED:
780
case WM_ENTERSIZEMOVE:
791
return DefWindowProc(hwnd, msg, wParam, lParam);