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, UINT unique);
24static HMENU __glutHMenu;
25
26static void
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#if 0
552    /* These two lines are disabled to fix incorrect handling of
553     * window maximization on Vista.  See bug 23182.
554     */
555    minmax->ptMaxSize.x = __glutScreenWidth;
556    minmax->ptMaxSize.y = __glutScreenHeight;
557#endif
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);
564    return 0;
565
566  case WM_SIZE:
567    window = __glutGetWindow(hwnd);
568    if (window) {
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);
575 	}
576#endif
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! */
582	GdiFlush();
583	window->reshape(width, height);
584	window->forceReshape = FALSE;
585	/* A reshape should be considered like posting a
586	   repair request. */
587	__glutPostRedisplay(window, GLUT_REPAIR_WORK);
588      }
589    }
590    return 0;
591
592  case WM_SETCURSOR:
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) {
599      goto defproc;
600    }
601    window = __glutGetWindow(hwnd);
602    if (window) {
603      __glutSetCursor(window);
604    }
605    /* TODO: check out the info in DevStudio on WM_SETCURSOR in the
606       DefaultAction section. */
607    return 1;
608
609  case WM_SETFOCUS:
610    window = __glutGetWindow(hwnd);
611    if (window) {
612      window->entryState = WM_SETFOCUS;
613      if (window->entry) {
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
618	   callbacks. */
619      }
620      if (window->joystick && __glutCurrentWindow) {
621        if (__glutCurrentWindow->joyPollInterval > 0) {
622	  MMRESULT result;
623
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) {
630	    return 0;
631          }
632	  (void) joySetThreshold(JOYSTICKID1,
633            __glutCurrentWindow->joyPollInterval);
634        }
635      }
636    }
637    return 0;
638
639  case WM_KILLFOCUS:
640    window = __glutGetWindow(hwnd);
641    if (window) {
642      window->entryState = WM_KILLFOCUS;
643      if (window->entry) {
644	__glutSetWindow(window);
645	window->entry(GLUT_LEFT);
646      }
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);
653        }
654      }
655    }
656    return 0;
657  case WM_ACTIVATE:
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);
662    }
663    if (window) {
664      int visState;
665
666      /* HIWORD(wParam) is the minimized flag. */
667      visState = !HIWORD(wParam);
668      updateWindowState(window, visState);
669    }
670    return 0;
671
672  /* Colour Palette Management */
673  case WM_PALETTECHANGED:
674    if (hwnd == (HWND)wParam) {
675      /* Don't respond to the message that we sent! */
676      break;
677    }
678    /* fall through to WM_QUERYNEWPALETTE */
679
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);
686      return TRUE;
687    }
688    return FALSE;
689
690  case MM_JOY1MOVE:
691  case MM_JOY1ZMOVE:
692    window = __glutGetWindow(hwnd);
693    if (window->joystick) {
694      JOYINFOEX jix;
695      int x, y, z;
696
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);
702
703#define SCALE(v)  ((int) ((v - 32767)/32.768))
704
705      /* Convert to integer for scaling. */
706      x = jix.dwXpos;
707      y = jix.dwYpos;
708      z = jix.dwZpos;
709      window->joystick(jix.dwButtons, SCALE(x), SCALE(y), SCALE(z));
710
711      return TRUE;
712    }
713    return FALSE;
714  case MM_JOY1BUTTONDOWN:
715  case MM_JOY1BUTTONUP:
716    window = __glutGetWindow(hwnd);
717    if (window->joystick) {
718      JOYINFOEX jix;
719
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);
725
726      return TRUE;
727    }
728    return FALSE;
729
730#if 0
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:
734    break;
735  case WM_NCHITTEST:
736    /* This event is generated by every mouse move event. */
737    goto defproc;
738  case WM_NCMOUSEMOVE:
739    goto defproc;
740  case WM_NCACTIVATE:
741    goto defproc;
742  case WM_NCPAINT:
743    goto defproc;
744  case WM_NCCALCSIZE:
745    goto defproc;
746  case WM_NCCREATE:
747    goto defproc;
748  case WM_NCDESTROY:
749    goto defproc;
750  case WM_NCLBUTTONDOWN:
751    goto defproc;
752  case WM_SETTEXT:
753    goto defproc;
754  case WM_GETTEXT:
755    goto defproc;
756  case WM_ACTIVATEAPP:
757    goto defproc;
758  case WM_GETICON:
759    goto defproc;
760  case WM_ERASEBKGND:
761    goto defproc;
762  case WM_WINDOWPOSCHANGING:
763    goto defproc;
764  case WM_WINDOWPOSCHANGED:
765    goto defproc;
766  case WM_MOUSEACTIVATE:
767    goto defproc;
768  case WM_SHOWWINDOW:
769    goto defproc;
770  case WM_MOVING:
771    goto defproc;
772  case WM_MOVE:
773    goto defproc;
774  case WM_KEYUP:
775    goto defproc;
776  case WM_CAPTURECHANGED:
777    goto defproc;
778  case WM_SYSCOMMAND:
779    goto defproc;
780  case WM_ENTERSIZEMOVE:
781    goto defproc;
782  case WM_ENTERIDLE:
783    goto defproc;
784#endif
785
786  default:
787    goto defproc;
788  }
789
790defproc:
791  return DefWindowProc(hwnd, msg, wParam, lParam);
792}
793