win32_winproc.c revision c041511d
1
2/* Copyright (c) Nate Robins, 1997. */
3/* portions Copyright (c) Mark Kilgard, 1997, 1998. */
4
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. */
8
9
10#include "glutint.h"
11#include <sys/timeb.h>
12#ifdef __MINGW32__
13#include <ctype.h>
14#endif
15
16#if defined(_WIN32) && !defined(__CYGWIN32__)
17#include <mmsystem.h>  /* Win32 Multimedia API header. */
18#endif
19
20extern unsigned __glutMenuButton;
21extern GLUTidleCB __glutIdleFunc;
22extern GLUTtimer *__glutTimerList;
23extern GLUTmenuItem *__glutGetUniqueMenuItem(GLUTmenu * menu, int unique);
24static HMENU __glutHMenu;
25
26void
27updateWindowState(GLUTwindow *window, int visState)
28{
29  GLUTwindow* child;
30
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);
38    }
39  }
40  /* Since Win32 only sends an activate for the toplevel window,
41     update the visibility for all the child windows. */
42  child = window->children;
43  while (child) {
44    updateWindowState(child, visState);
45    child = child->siblings;
46  }
47}
48
49LONG WINAPI
50__glutWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
51{
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;
58  int button = -1;
59
60  switch(msg) {
61  case WM_CREATE:
62    return 0;
63  case WM_CLOSE:
64    if (__glutExitFunc) {
65      __glutExitFunc(0);
66    }
67    exit(0);
68    break;
69#if 0
70  case WM_DESTROY:
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
77       data structures. */
78    window = __glutGetWindow(hwnd);
79    if (window) {
80      if (window->ctx) {
81        wglMakeCurrent(NULL, NULL);
82        wglDeleteContext(window->ctx);
83      }
84    }
85    return 0;
86#endif
87  case WM_PAINT:
88    window = __glutGetWindow(hwnd);
89    if (window) {
90      BeginPaint(hwnd, &ps);		/* Must have this for some Win32 reason. */
91      EndPaint(hwnd, &ps);
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);
96      }
97    }
98    return 0;
99
100  case WM_SYSKEYUP:
101  case WM_KEYUP:
102    window = __glutGetWindow(hwnd);
103    if (!window) {
104      break;
105    }
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);
114    }
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;
126      switch (wParam) {
127      /* *INDENT-OFF* */
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;
149      case VK_DELETE:
150        /* Delete is an ASCII character. */
151	if (window->keyboardUp) {
152	  window->keyboardUp((unsigned char) 127, point.x, point.y);
153	}
154	return 0;
155      /* *INDENT-ON* */
156      default:
157	if (window->keyboardUp) {
158	  key = MapVirtualKey(wParam, 2);  /* Map to ASCII. */
159	  if (isascii(key) && (key != 0)) {
160
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. */
164
165	    if (!(__glutModifierMask & ShiftMask))
166	      key = tolower(key);
167	    window->keyboardUp((unsigned char) key, point.x, point.y);
168          }
169        }
170	__glutModifierMask = (unsigned int) ~0;
171	return 0;
172      }
173      if (window->specialUp) {
174        window->specialUp(key, point.x, point.y);
175      }
176      __glutModifierMask = (unsigned int) ~0;
177    }
178    return 0;
179
180  case WM_SYSCHAR:
181  case WM_CHAR:
182    window = __glutGetWindow(hwnd);
183    if (!window) {
184      break;
185    }
186
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)) ) {
190      break;
191    }
192
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);
201    }
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;
215    }
216    return 0;
217
218  case WM_SYSKEYDOWN:
219  case WM_KEYDOWN:
220    window = __glutGetWindow(hwnd);
221    if (!window) {
222      break;
223    }
224
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)) ) {
228      break;
229    }
230
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);
239    }
240    if (window->special) {
241      switch (wParam) {
242	/* *INDENT-OFF* */
243	/* function keys */
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;
261	/* *INDENT-ON* */
262
263	case VK_PRIOR:
264	  /* VK_PRIOR is Win32's Page Up */
265	  key = GLUT_KEY_PAGE_UP;
266	  break;
267	case VK_NEXT:
268	  /* VK_NEXT is Win32's Page Down */
269	  key = GLUT_KEY_PAGE_DOWN;
270	  break;
271	case VK_HOME:
272	  key = GLUT_KEY_HOME;
273	  break;
274	case VK_END:
275	  key = GLUT_KEY_END;
276	  break;
277	case VK_INSERT:
278	  key = GLUT_KEY_INSERT;
279	  break;
280        case VK_DELETE:
281	  goto handleDelete;
282	default:
283	  goto defproc;
284      }
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. */
300      switch (wParam) {
301      case VK_DELETE:
302      handleDelete:
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;
316	return 0;
317      default:
318        /* Let the following WM_SYSCHAR or WM_CHAR message generate
319	   the keyboard callback. */
320        break;
321      }
322    }
323    return 0;
324
325  case WM_LBUTTONDOWN:
326    button = GLUT_LEFT_BUTTON;
327  case WM_MBUTTONDOWN:
328    if (button < 0)
329      button = GLUT_MIDDLE_BUTTON;
330  case WM_RBUTTONDOWN:
331    if (button < 0)
332      button = GLUT_RIGHT_BUTTON;
333
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
338	 properly. */
339      if (button == GLUT_MIDDLE_BUTTON)
340	return 0;
341      GetCursorPos(&point);
342      ScreenToClient(hwnd, &point);
343      __glutItemSelected = NULL;
344      __glutFinishMenu(hwnd, point.x, point.y);
345      return 0;
346    }
347
348    /* set the capture so we can get mouse events outside the window */
349    SetCapture(hwnd);
350
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. */
354    x = LOWORD(lParam);
355    y = HIWORD(lParam);
356    if(x & 1 << 15) x -= (1 << 16);
357    if(y & 1 << 15) y -= (1 << 16);
358
359    window = __glutGetWindow(hwnd);
360    if (window) {
361      menu = __glutGetMenuByNum(window->menu[button]);
362      if (menu) {
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 :
367                           0x0001;
368	__glutStartMenu(menu, window, point.x, point.y, x, y);
369      } else if (window->mouse) {
370
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;
381      } else {
382	/* Stray mouse events.  Ignore. */
383      }
384    }
385    return 0;
386
387  case WM_LBUTTONUP:
388    button = GLUT_LEFT_BUTTON;
389  case WM_MBUTTONUP:
390    if (button < 0)
391      button = GLUT_MIDDLE_BUTTON;
392  case WM_RBUTTONUP:
393    if (button < 0)
394      button = GLUT_RIGHT_BUTTON;
395
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) {
403	return 0;
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. */
408#if 0
409 	int item = MenuItemFromPoint(hwnd, __glutHMenu, point);
410 	if (item != -1)
411 	  __glutItemSelected = (GLUTmenuItem*)GetMenuItemID(__glutHMenu, item);
412 	else
413 	  __glutItemSelected = NULL;
414 	__glutFinishMenu(hwnd, point.x, point.y);
415#endif
416      } else {
417	__glutItemSelected = NULL;
418	__glutFinishMenu(hwnd, point.x, point.y);
419      }
420      return 0;
421    }
422
423    /* Release the mouse capture. */
424    ReleaseCapture();
425
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. */
431      x = LOWORD(lParam);
432      y = HIWORD(lParam);
433      if(x & 1 << 15) x -= (1 << 16);
434      if(y & 1 << 15) y -= (1 << 16);
435
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;
446    } else {
447      /* Window might have been destroyed and all the
448	 events for the window may not yet be received. */
449    }
450    return 0;
451
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
456       idle func. */
457    SetTimer(hwnd, 1, 1, NULL);
458    return 0;
459
460  case WM_TIMER:
461#if 0
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. */
466    if (wParam == 2)
467      return 0;
468#endif
469
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. */
477#if 0
478     if (__glutIdleFunc)
479       __glutIdleFunc();
480#endif
481    if (__glutTimerList)
482      handleTimeouts();
483    return 0;
484
485  case WM_EXITMENULOOP:
486    /* nuke the above created timer...we don't need it anymore, since
487       the menu is gone now. */
488    KillTimer(hwnd, 1);
489    return 0;
490
491  case WM_MENUSELECT:
492    if (lParam != 0)
493      __glutHMenu = (HMENU)lParam;
494    return 0;
495
496  case WM_COMMAND:
497    if (__glutMappedMenu) {
498      if (GetSubMenu(__glutHMenu, LOWORD(wParam)))
499	__glutItemSelected = NULL;
500      else
501	__glutItemSelected =
502	  __glutGetUniqueMenuItem(__glutMappedMenu, LOWORD(wParam));
503      GetCursorPos(&point);
504      ScreenToClient(hwnd, &point);
505      __glutFinishMenu(hwnd, point.x, point.y);
506    }
507    return 0;
508
509  case WM_MOUSEMOVE:
510    if (!__glutMappedMenu) {
511      window = __glutGetWindow(hwnd);
512      if (window) {
513          /* If motion function registered _and_ buttons held *
514             down, call motion function...  */
515	x = LOWORD(lParam);
516	y = HIWORD(lParam);
517
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);
523
524	if (window->motion && wParam &
525            (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
526	  __glutSetWindow(window);
527	  window->motion(x, y);
528	}
529	/* If passive motion function registered _and_
530	   buttons not held down, call passive motion
531	   function...  */
532	else if (window->passive &&
533		 ((wParam &
534		   (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) ==
535		  0)) {
536	  __glutSetWindow(window);
537	  window->passive(x, y);
538	}
539      }
540    } else {
541      /* Motion events are thrown away when a pop up menu is
542	 active. */
543    }
544    return 0;
545
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);
559    return 0;
560
561  case WM_SIZE:
562    window = __glutGetWindow(hwnd);
563    if (window) {
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);
570 	}
571#endif
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! */
577	GdiFlush();
578	window->reshape(width, height);
579	window->forceReshape = FALSE;
580	/* A reshape should be considered like posting a
581	   repair request. */
582	__glutPostRedisplay(window, GLUT_REPAIR_WORK);
583      }
584    }
585    return 0;
586
587  case WM_SETCURSOR:
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) {
594      goto defproc;
595    }
596    window = __glutGetWindow(hwnd);
597    if (window) {
598      __glutSetCursor(window);
599    }
600    /* TODO: check out the info in DevStudio on WM_SETCURSOR in the
601       DefaultAction section. */
602    return 1;
603
604  case WM_SETFOCUS:
605    window = __glutGetWindow(hwnd);
606    if (window) {
607      window->entryState = WM_SETFOCUS;
608      if (window->entry) {
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
613	   callbacks. */
614      }
615      if (window->joystick && __glutCurrentWindow) {
616        if (__glutCurrentWindow->joyPollInterval > 0) {
617	  MMRESULT result;
618
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) {
625	    return 0;
626          }
627	  (void) joySetThreshold(JOYSTICKID1,
628            __glutCurrentWindow->joyPollInterval);
629        }
630      }
631    }
632    return 0;
633
634  case WM_KILLFOCUS:
635    window = __glutGetWindow(hwnd);
636    if (window) {
637      window->entryState = WM_KILLFOCUS;
638      if (window->entry) {
639	__glutSetWindow(window);
640	window->entry(GLUT_LEFT);
641      }
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);
648        }
649      }
650    }
651    return 0;
652  case WM_ACTIVATE:
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);
657    }
658    if (window) {
659      int visState;
660
661      /* HIWORD(wParam) is the minimized flag. */
662      visState = !HIWORD(wParam);
663      updateWindowState(window, visState);
664    }
665    return 0;
666
667  /* Colour Palette Management */
668  case WM_PALETTECHANGED:
669    if (hwnd == (HWND)wParam) {
670      /* Don't respond to the message that we sent! */
671      break;
672    }
673    /* fall through to WM_QUERYNEWPALETTE */
674
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);
681      return TRUE;
682    }
683    return FALSE;
684
685  case MM_JOY1MOVE:
686  case MM_JOY1ZMOVE:
687    window = __glutGetWindow(hwnd);
688    if (window->joystick) {
689      JOYINFOEX jix;
690      int x, y, z;
691
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);
697
698#define SCALE(v)  ((int) ((v - 32767)/32.768))
699
700      /* Convert to integer for scaling. */
701      x = jix.dwXpos;
702      y = jix.dwYpos;
703      z = jix.dwZpos;
704      window->joystick(jix.dwButtons, SCALE(x), SCALE(y), SCALE(z));
705
706      return TRUE;
707    }
708    return FALSE;
709  case MM_JOY1BUTTONDOWN:
710  case MM_JOY1BUTTONUP:
711    window = __glutGetWindow(hwnd);
712    if (window->joystick) {
713      JOYINFOEX jix;
714
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);
720
721      return TRUE;
722    }
723    return FALSE;
724
725#if 0
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:
729    break;
730  case WM_NCHITTEST:
731    /* This event is generated by every mouse move event. */
732    goto defproc;
733  case WM_NCMOUSEMOVE:
734    goto defproc;
735  case WM_NCACTIVATE:
736    goto defproc;
737  case WM_NCPAINT:
738    goto defproc;
739  case WM_NCCALCSIZE:
740    goto defproc;
741  case WM_NCCREATE:
742    goto defproc;
743  case WM_NCDESTROY:
744    goto defproc;
745  case WM_NCLBUTTONDOWN:
746    goto defproc;
747  case WM_SETTEXT:
748    goto defproc;
749  case WM_GETTEXT:
750    goto defproc;
751  case WM_ACTIVATEAPP:
752    goto defproc;
753  case WM_GETICON:
754    goto defproc;
755  case WM_ERASEBKGND:
756    goto defproc;
757  case WM_WINDOWPOSCHANGING:
758    goto defproc;
759  case WM_WINDOWPOSCHANGED:
760    goto defproc;
761  case WM_MOUSEACTIVATE:
762    goto defproc;
763  case WM_SHOWWINDOW:
764    goto defproc;
765  case WM_MOVING:
766    goto defproc;
767  case WM_MOVE:
768    goto defproc;
769  case WM_KEYUP:
770    goto defproc;
771  case WM_CAPTURECHANGED:
772    goto defproc;
773  case WM_SYSCOMMAND:
774    goto defproc;
775  case WM_ENTERSIZEMOVE:
776    goto defproc;
777  case WM_ENTERIDLE:
778    goto defproc;
779#endif
780
781  default:
782    goto defproc;
783  }
784
785defproc:
786  return DefWindowProc(hwnd, msg, wParam, lParam);
787}
788