1c041511dScube
2c041511dScube/* Copyright (c) Nate Robins, 1997. */
3c041511dScube/* portions Copyright (c) Mark Kilgard, 1997, 1998. */
4c041511dScube
5c041511dScube/* This program is freely distributable without licensing fees
6c041511dScube   and is provided without guarantee or warrantee expressed or
7c041511dScube   implied. This program is -not- in the public domain. */
8c041511dScube
9c041511dScube
10c041511dScube#include "glutint.h"
11c041511dScube#include <sys/timeb.h>
12c041511dScube#ifdef __MINGW32__
13c041511dScube#include <ctype.h>
14c041511dScube#endif
15c041511dScube
16c041511dScube#if defined(_WIN32) && !defined(__CYGWIN32__)
17c041511dScube#include <mmsystem.h>  /* Win32 Multimedia API header. */
18c041511dScube#endif
19c041511dScube
20c041511dScubeextern unsigned __glutMenuButton;
21c041511dScubeextern GLUTidleCB __glutIdleFunc;
22c041511dScubeextern GLUTtimer *__glutTimerList;
232590f9beSmrgextern GLUTmenuItem *__glutGetUniqueMenuItem(GLUTmenu * menu, UINT unique);
24c041511dScubestatic HMENU __glutHMenu;
25c041511dScube
262590f9beSmrgstatic void
27c041511dScubeupdateWindowState(GLUTwindow *window, int visState)
28c041511dScube{
29c041511dScube  GLUTwindow* child;
30c041511dScube
31c041511dScube  /* XXX shownState and visState are the same in Win32. */
32c041511dScube  window->shownState = visState;
33c041511dScube  if (visState != window->visState) {
34c041511dScube    if (window->windowStatus) {
35c041511dScube      window->visState = visState;
36c041511dScube      __glutSetWindow(window);
37c041511dScube      window->windowStatus(visState);
38c041511dScube    }
39c041511dScube  }
40c041511dScube  /* Since Win32 only sends an activate for the toplevel window,
41c041511dScube     update the visibility for all the child windows. */
42c041511dScube  child = window->children;
43c041511dScube  while (child) {
44c041511dScube    updateWindowState(child, visState);
45c041511dScube    child = child->siblings;
46c041511dScube  }
47c041511dScube}
48c041511dScube
49c041511dScubeLONG WINAPI
50c041511dScube__glutWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
51c041511dScube{
52c041511dScube  POINT         point;			/* Point structure. */
53c041511dScube  PAINTSTRUCT   ps;			/* Paint structure. */
54c041511dScube  LPMINMAXINFO  minmax;			/* Minimum/maximum info structure. */
55c041511dScube  GLUTwindow*   window;			/* GLUT window associated with message. */
56c041511dScube  GLUTmenu*     menu;			/* GLUT menu associated with message. */
57c041511dScube  int x, y, width, height, key;
58c041511dScube  int button = -1;
59c041511dScube
60c041511dScube  switch(msg) {
61c041511dScube  case WM_CREATE:
62c041511dScube    return 0;
63c041511dScube  case WM_CLOSE:
64c041511dScube    if (__glutExitFunc) {
65c041511dScube      __glutExitFunc(0);
66c041511dScube    }
67c041511dScube    exit(0);
68c041511dScube    break;
69c041511dScube#if 0
70c041511dScube  case WM_DESTROY:
71c041511dScube    /* XXX NVidia's NT OpenGL can have problems closing down
72c041511dScube       its OpenGL internal data structures if we just allow
73c041511dScube       the process to terminate without unbinding and deleting
74c041511dScube       the windows context.  Apparently, DirectDraw unloads
75c041511dScube       before OPENGL32.DLL in the close down sequence, but
76c041511dScube       NVidia's NT OpenGL needs DirectDraw to close down its
77c041511dScube       data structures. */
78c041511dScube    window = __glutGetWindow(hwnd);
79c041511dScube    if (window) {
80c041511dScube      if (window->ctx) {
81c041511dScube        wglMakeCurrent(NULL, NULL);
82c041511dScube        wglDeleteContext(window->ctx);
83c041511dScube      }
84c041511dScube    }
85c041511dScube    return 0;
86c041511dScube#endif
87c041511dScube  case WM_PAINT:
88c041511dScube    window = __glutGetWindow(hwnd);
89c041511dScube    if (window) {
90c041511dScube      BeginPaint(hwnd, &ps);		/* Must have this for some Win32 reason. */
91c041511dScube      EndPaint(hwnd, &ps);
92c041511dScube      if (window->win == hwnd) {
93c041511dScube	__glutPostRedisplay(window, GLUT_REPAIR_WORK);
94c041511dScube      } else if (window->overlay && window->overlay->win == hwnd) {
95c041511dScube	__glutPostRedisplay(window, GLUT_OVERLAY_REPAIR_WORK);
96c041511dScube      }
97c041511dScube    }
98c041511dScube    return 0;
99c041511dScube
100c041511dScube  case WM_SYSKEYUP:
101c041511dScube  case WM_KEYUP:
102c041511dScube    window = __glutGetWindow(hwnd);
103c041511dScube    if (!window) {
104c041511dScube      break;
105c041511dScube    }
106c041511dScube    /* Win32 is dumb and sends these messages only to the parent
107c041511dScube       window.  Therefore, find out if we're in a child window and
108c041511dScube       call the child windows keyboard callback if we are. */
109c041511dScube    if (window->parent) {
110c041511dScube      GetCursorPos(&point);
111c041511dScube      ScreenToClient(hwnd, &point);
112c041511dScube      hwnd = ChildWindowFromPoint(hwnd, point);
113c041511dScube      window = __glutGetWindow(hwnd);
114c041511dScube    }
115c041511dScube    if (window->specialUp || window->keyboardUp) {
116c041511dScube      GetCursorPos(&point);
117c041511dScube      ScreenToClient(window->win, &point);
118c041511dScube      __glutSetWindow(window);
119c041511dScube      __glutModifierMask = 0;
120c041511dScube      if (GetKeyState(VK_SHIFT) < 0)  /* < 0 = high order bit is on */
121c041511dScube	__glutModifierMask |= ShiftMask;
122c041511dScube      if (GetKeyState(VK_SHIFT) < 0)  /* < 0 = high order bit is on */
123c041511dScube	__glutModifierMask |= ControlMask;
124c041511dScube      if (GetKeyState(VK_MENU) < 0)
125c041511dScube	__glutModifierMask |= Mod1Mask;
126c041511dScube      switch (wParam) {
127c041511dScube      /* *INDENT-OFF* */
128c041511dScube      case VK_F1:     key = GLUT_KEY_F1; break;
129c041511dScube      case VK_F2:     key = GLUT_KEY_F2; break;
130c041511dScube      case VK_F3:     key = GLUT_KEY_F3; break;
131c041511dScube      case VK_F4:     key = GLUT_KEY_F4; break;
132c041511dScube      case VK_F5:     key = GLUT_KEY_F5; break;
133c041511dScube      case VK_F6:     key = GLUT_KEY_F6; break;
134c041511dScube      case VK_F7:     key = GLUT_KEY_F7; break;
135c041511dScube      case VK_F8:     key = GLUT_KEY_F8; break;
136c041511dScube      case VK_F9:     key = GLUT_KEY_F9; break;
137c041511dScube      case VK_F10:    key = GLUT_KEY_F10; break;
138c041511dScube      case VK_F11:    key = GLUT_KEY_F11; break;
139c041511dScube      case VK_F12:    key = GLUT_KEY_F12; break;
140c041511dScube      case VK_LEFT:   key = GLUT_KEY_LEFT; break;
141c041511dScube      case VK_UP:     key = GLUT_KEY_UP; break;
142c041511dScube      case VK_RIGHT:  key = GLUT_KEY_RIGHT; break;
143c041511dScube      case VK_DOWN:   key = GLUT_KEY_DOWN; break;
144c041511dScube      case VK_PRIOR:  key = GLUT_KEY_PAGE_UP; break;
145c041511dScube      case VK_NEXT:   key = GLUT_KEY_PAGE_DOWN; break;
146c041511dScube      case VK_HOME:   key = GLUT_KEY_HOME; break;
147c041511dScube      case VK_END:    key = GLUT_KEY_END; break;
148c041511dScube      case VK_INSERT: key = GLUT_KEY_INSERT; break;
149c041511dScube      case VK_DELETE:
150c041511dScube        /* Delete is an ASCII character. */
151c041511dScube	if (window->keyboardUp) {
152c041511dScube	  window->keyboardUp((unsigned char) 127, point.x, point.y);
153c041511dScube	}
154c041511dScube	return 0;
155c041511dScube      /* *INDENT-ON* */
156c041511dScube      default:
157c041511dScube	if (window->keyboardUp) {
158c041511dScube	  key = MapVirtualKey(wParam, 2);  /* Map to ASCII. */
159c041511dScube	  if (isascii(key) && (key != 0)) {
160c041511dScube
161c041511dScube	    /* XXX Attempt to determine modified ASCII character
162c041511dScube	       is quite incomplete.  Digits, symbols, CapsLock,
163c041511dScube	       Ctrl, and numeric keypad are all ignored.  Fix this. */
164c041511dScube
165c041511dScube	    if (!(__glutModifierMask & ShiftMask))
166c041511dScube	      key = tolower(key);
167c041511dScube	    window->keyboardUp((unsigned char) key, point.x, point.y);
168c041511dScube          }
169c041511dScube        }
170c041511dScube	__glutModifierMask = (unsigned int) ~0;
171c041511dScube	return 0;
172c041511dScube      }
173c041511dScube      if (window->specialUp) {
174c041511dScube        window->specialUp(key, point.x, point.y);
175c041511dScube      }
176c041511dScube      __glutModifierMask = (unsigned int) ~0;
177c041511dScube    }
178c041511dScube    return 0;
179c041511dScube
180c041511dScube  case WM_SYSCHAR:
181c041511dScube  case WM_CHAR:
182c041511dScube    window = __glutGetWindow(hwnd);
183c041511dScube    if (!window) {
184c041511dScube      break;
185c041511dScube    }
186c041511dScube
187c041511dScube    /* Bit 30 of lParam is set if key already held down.  If
188c041511dScube       we are ignoring auto repeated key strokes for the window, bail. */
189c041511dScube    if (window->ignoreKeyRepeat && (lParam & (1 << 30)) ) {
190c041511dScube      break;
191c041511dScube    }
192c041511dScube
193c041511dScube    /* Win32 is dumb and sends these messages only to the parent
194c041511dScube       window.  Therefore, find out if we're in a child window and
195c041511dScube       call the child windows keyboard callback if we are. */
196c041511dScube    if (window->parent) {
197c041511dScube	GetCursorPos(&point);
198c041511dScube	ScreenToClient(hwnd, &point);
199c041511dScube	hwnd = ChildWindowFromPoint(hwnd, point);
200c041511dScube	window = __glutGetWindow(hwnd);
201c041511dScube    }
202c041511dScube    if (window->keyboard) {
203c041511dScube      GetCursorPos(&point);
204c041511dScube      ScreenToClient(window->win, &point);
205c041511dScube      __glutSetWindow(window);
206c041511dScube      __glutModifierMask = 0;
207c041511dScube      if (GetKeyState(VK_SHIFT) < 0)	/* < 0 = high order bit is on */
208c041511dScube	__glutModifierMask |= ShiftMask;
209c041511dScube      if (GetKeyState(VK_CONTROL) < 0)
210c041511dScube	__glutModifierMask |= ControlMask;
211c041511dScube      if (GetKeyState(VK_MENU) < 0)
212c041511dScube	__glutModifierMask |= Mod1Mask;
213c041511dScube      window->keyboard((unsigned char)wParam, point.x, point.y);
214c041511dScube      __glutModifierMask = (unsigned int) ~0;
215c041511dScube    }
216c041511dScube    return 0;
217c041511dScube
218c041511dScube  case WM_SYSKEYDOWN:
219c041511dScube  case WM_KEYDOWN:
220c041511dScube    window = __glutGetWindow(hwnd);
221c041511dScube    if (!window) {
222c041511dScube      break;
223c041511dScube    }
224c041511dScube
225c041511dScube    /* Bit 30 of lParam is set if key already held down.  If
226c041511dScube       we are ignoring auto repeated key strokes for the window, bail. */
227c041511dScube    if (window->ignoreKeyRepeat && (lParam & (1 << 30)) ) {
228c041511dScube      break;
229c041511dScube    }
230c041511dScube
231c041511dScube    /* Win32 is dumb and sends these messages only to the parent
232c041511dScube       window.  Therefore, find out if we're in a child window and
233c041511dScube       call the child windows keyboard callback if we are. */
234c041511dScube    if (window->parent) {
235c041511dScube	GetCursorPos(&point);
236c041511dScube	ScreenToClient(hwnd, &point);
237c041511dScube	hwnd = ChildWindowFromPoint(hwnd, point);
238c041511dScube	window = __glutGetWindow(hwnd);
239c041511dScube    }
240c041511dScube    if (window->special) {
241c041511dScube      switch (wParam) {
242c041511dScube	/* *INDENT-OFF* */
243c041511dScube	/* function keys */
244c041511dScube	case VK_F1:     key = GLUT_KEY_F1; break;
245c041511dScube	case VK_F2:     key = GLUT_KEY_F2; break;
246c041511dScube	case VK_F3:     key = GLUT_KEY_F3; break;
247c041511dScube	case VK_F4:     key = GLUT_KEY_F4; break;
248c041511dScube	case VK_F5:     key = GLUT_KEY_F5; break;
249c041511dScube	case VK_F6:     key = GLUT_KEY_F6; break;
250c041511dScube	case VK_F7:     key = GLUT_KEY_F7; break;
251c041511dScube	case VK_F8:     key = GLUT_KEY_F8; break;
252c041511dScube	case VK_F9:     key = GLUT_KEY_F9; break;
253c041511dScube	case VK_F10:    key = GLUT_KEY_F10; break;
254c041511dScube	case VK_F11:    key = GLUT_KEY_F11; break;
255c041511dScube	case VK_F12:    key = GLUT_KEY_F12; break;
256c041511dScube	/* directional keys */
257c041511dScube	case VK_LEFT:   key = GLUT_KEY_LEFT; break;
258c041511dScube	case VK_UP:     key = GLUT_KEY_UP; break;
259c041511dScube	case VK_RIGHT:  key = GLUT_KEY_RIGHT; break;
260c041511dScube	case VK_DOWN:   key = GLUT_KEY_DOWN; break;
261c041511dScube	/* *INDENT-ON* */
262c041511dScube
263c041511dScube	case VK_PRIOR:
264c041511dScube	  /* VK_PRIOR is Win32's Page Up */
265c041511dScube	  key = GLUT_KEY_PAGE_UP;
266c041511dScube	  break;
267c041511dScube	case VK_NEXT:
268c041511dScube	  /* VK_NEXT is Win32's Page Down */
269c041511dScube	  key = GLUT_KEY_PAGE_DOWN;
270c041511dScube	  break;
271c041511dScube	case VK_HOME:
272c041511dScube	  key = GLUT_KEY_HOME;
273c041511dScube	  break;
274c041511dScube	case VK_END:
275c041511dScube	  key = GLUT_KEY_END;
276c041511dScube	  break;
277c041511dScube	case VK_INSERT:
278c041511dScube	  key = GLUT_KEY_INSERT;
279c041511dScube	  break;
280c041511dScube        case VK_DELETE:
281c041511dScube	  goto handleDelete;
282c041511dScube	default:
283c041511dScube	  goto defproc;
284c041511dScube      }
285c041511dScube      GetCursorPos(&point);
286c041511dScube      ScreenToClient(window->win, &point);
287c041511dScube      __glutSetWindow(window);
288c041511dScube      __glutModifierMask = 0;
289c041511dScube      if (GetKeyState(VK_SHIFT) < 0)	/* < 0 = high order bit is on */
290c041511dScube	__glutModifierMask |= ShiftMask;
291c041511dScube      if (GetKeyState(VK_CONTROL) < 0)
292c041511dScube	__glutModifierMask |= ControlMask;
293c041511dScube      if (GetKeyState(VK_MENU) < 0)
294c041511dScube	__glutModifierMask |= Mod1Mask;
295c041511dScube      window->special(key, point.x, point.y);
296c041511dScube      __glutModifierMask = (unsigned int) ~0;
297c041511dScube    } else if (window->keyboard) {
298c041511dScube      /* Specially handle any keys that match ASCII values but
299c041511dScube         do not generate Windows WM_SYSCHAR or WM_CHAR messages. */
300c041511dScube      switch (wParam) {
301c041511dScube      case VK_DELETE:
302c041511dScube      handleDelete:
303c041511dScube        /* Delete is an ASCII character. */
304c041511dScube        GetCursorPos(&point);
305c041511dScube        ScreenToClient(window->win, &point);
306c041511dScube        __glutSetWindow(window);
307c041511dScube        __glutModifierMask = 0;
308c041511dScube        if (GetKeyState(VK_SHIFT) < 0)	/* < 0 = high order bit is on */
309c041511dScube          __glutModifierMask |= ShiftMask;
310c041511dScube        if (GetKeyState(VK_CONTROL) < 0)
311c041511dScube          __glutModifierMask |= ControlMask;
312c041511dScube        if (GetKeyState(VK_MENU) < 0)
313c041511dScube          __glutModifierMask |= Mod1Mask;
314c041511dScube	window->keyboard((unsigned char) 127, point.x, point.y);
315c041511dScube        __glutModifierMask = (unsigned int) ~0;
316c041511dScube	return 0;
317c041511dScube      default:
318c041511dScube        /* Let the following WM_SYSCHAR or WM_CHAR message generate
319c041511dScube	   the keyboard callback. */
320c041511dScube        break;
321c041511dScube      }
322c041511dScube    }
323c041511dScube    return 0;
324c041511dScube
325c041511dScube  case WM_LBUTTONDOWN:
326c041511dScube    button = GLUT_LEFT_BUTTON;
327c041511dScube  case WM_MBUTTONDOWN:
328c041511dScube    if (button < 0)
329c041511dScube      button = GLUT_MIDDLE_BUTTON;
330c041511dScube  case WM_RBUTTONDOWN:
331c041511dScube    if (button < 0)
332c041511dScube      button = GLUT_RIGHT_BUTTON;
333c041511dScube
334c041511dScube    /* finish the menu if we get a button down message (user must have
335c041511dScube       cancelled the menu). */
336c041511dScube    if (__glutMappedMenu) {
337c041511dScube      /* TODO: take this out once the menu on middle mouse stuff works
338c041511dScube	 properly. */
339c041511dScube      if (button == GLUT_MIDDLE_BUTTON)
340c041511dScube	return 0;
341c041511dScube      GetCursorPos(&point);
342c041511dScube      ScreenToClient(hwnd, &point);
343c041511dScube      __glutItemSelected = NULL;
344c041511dScube      __glutFinishMenu(hwnd, point.x, point.y);
345c041511dScube      return 0;
346c041511dScube    }
347c041511dScube
348c041511dScube    /* set the capture so we can get mouse events outside the window */
349c041511dScube    SetCapture(hwnd);
350c041511dScube
351c041511dScube    /* Win32 doesn't return the same numbers as X does when the mouse
352c041511dScube       goes beyond the upper or left side of the window.  roll the
353c041511dScube       Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
354c041511dScube    x = LOWORD(lParam);
355c041511dScube    y = HIWORD(lParam);
356c041511dScube    if(x & 1 << 15) x -= (1 << 16);
357c041511dScube    if(y & 1 << 15) y -= (1 << 16);
358c041511dScube
359c041511dScube    window = __glutGetWindow(hwnd);
360c041511dScube    if (window) {
361c041511dScube      menu = __glutGetMenuByNum(window->menu[button]);
362c041511dScube      if (menu) {
363c041511dScube	point.x = LOWORD(lParam); point.y = HIWORD(lParam);
364c041511dScube	ClientToScreen(window->win, &point);
365c041511dScube	__glutMenuButton = button == GLUT_RIGHT_BUTTON ? TPM_RIGHTBUTTON :
366c041511dScube                           button == GLUT_LEFT_BUTTON  ? TPM_LEFTBUTTON :
367c041511dScube                           0x0001;
368c041511dScube	__glutStartMenu(menu, window, point.x, point.y, x, y);
369c041511dScube      } else if (window->mouse) {
370c041511dScube
371c041511dScube        __glutSetWindow(window);
372c041511dScube	__glutModifierMask = 0;
373c041511dScube	if (GetKeyState(VK_SHIFT) < 0)	/* < 0 = high order bit is on. */
374c041511dScube	  __glutModifierMask |= ShiftMask;
375c041511dScube	if (GetKeyState(VK_CONTROL) < 0)
376c041511dScube	  __glutModifierMask |= ControlMask;
377c041511dScube	if (GetKeyState(VK_MENU) < 0)
378c041511dScube	  __glutModifierMask |= Mod1Mask;
379c041511dScube	window->mouse(button, GLUT_DOWN, x, y);
380c041511dScube	__glutModifierMask = (unsigned int)~0;
381c041511dScube      } else {
382c041511dScube	/* Stray mouse events.  Ignore. */
383c041511dScube      }
384c041511dScube    }
385c041511dScube    return 0;
386c041511dScube
387c041511dScube  case WM_LBUTTONUP:
388c041511dScube    button = GLUT_LEFT_BUTTON;
389c041511dScube  case WM_MBUTTONUP:
390c041511dScube    if (button < 0)
391c041511dScube      button = GLUT_MIDDLE_BUTTON;
392c041511dScube  case WM_RBUTTONUP:
393c041511dScube    if (button < 0)
394c041511dScube      button = GLUT_RIGHT_BUTTON;
395c041511dScube
396c041511dScube    /* Bail out if we're processing a menu. */
397c041511dScube    if (__glutMappedMenu) {
398c041511dScube      GetCursorPos(&point);
399c041511dScube      ScreenToClient(hwnd, &point);
400c041511dScube      /* if we're getting the middle button up signal, then something
401c041511dScube	 on the menu was selected. */
402c041511dScube      if (button == GLUT_MIDDLE_BUTTON) {
403c041511dScube	return 0;
404c041511dScube	/* For some reason, the code below always returns -1 even
405c041511dScube	   though the point IS IN THE ITEM!  Therefore, just bail out if
406c041511dScube	   we get a middle mouse up.  The user must select using the
407c041511dScube	   left mouse button.  Stupid Win32. */
408c041511dScube#if 0
409c041511dScube 	int item = MenuItemFromPoint(hwnd, __glutHMenu, point);
410c041511dScube 	if (item != -1)
411c041511dScube 	  __glutItemSelected = (GLUTmenuItem*)GetMenuItemID(__glutHMenu, item);
412c041511dScube 	else
413c041511dScube 	  __glutItemSelected = NULL;
414c041511dScube 	__glutFinishMenu(hwnd, point.x, point.y);
415c041511dScube#endif
416c041511dScube      } else {
417c041511dScube	__glutItemSelected = NULL;
418c041511dScube	__glutFinishMenu(hwnd, point.x, point.y);
419c041511dScube      }
420c041511dScube      return 0;
421c041511dScube    }
422c041511dScube
423c041511dScube    /* Release the mouse capture. */
424c041511dScube    ReleaseCapture();
425c041511dScube
426c041511dScube    window = __glutGetWindow(hwnd);
427c041511dScube    if (window && window->mouse) {
428c041511dScube      /* Win32 doesn't return the same numbers as X does when the
429c041511dScube	 mouse goes beyond the upper or left side of the window.  roll
430c041511dScube	 the Win32's 0..2^16 pointer co-ord range to 0 +/- 2^15. */
431c041511dScube      x = LOWORD(lParam);
432c041511dScube      y = HIWORD(lParam);
433c041511dScube      if(x & 1 << 15) x -= (1 << 16);
434c041511dScube      if(y & 1 << 15) y -= (1 << 16);
435c041511dScube
436c041511dScube      __glutSetWindow(window);
437c041511dScube      __glutModifierMask = 0;
438c041511dScube      if (GetKeyState(VK_SHIFT) < 0)	/* < 0 = high order bit is on */
439c041511dScube	__glutModifierMask |= ShiftMask;
440c041511dScube      if (GetKeyState(VK_CONTROL) < 0)
441c041511dScube	__glutModifierMask |= ControlMask;
442c041511dScube      if (GetKeyState(VK_MENU) < 0)
443c041511dScube	__glutModifierMask |= Mod1Mask;
444c041511dScube      window->mouse(button, GLUT_UP, x, y);
445c041511dScube      __glutModifierMask = (unsigned int)~0;
446c041511dScube    } else {
447c041511dScube      /* Window might have been destroyed and all the
448c041511dScube	 events for the window may not yet be received. */
449c041511dScube    }
450c041511dScube    return 0;
451c041511dScube
452c041511dScube  case WM_ENTERMENULOOP:
453c041511dScube    /* KLUDGE: create a timer that fires every 100 ms when we start a
454c041511dScube       menu so that we can still process the idle & timer events (that
455c041511dScube       way, the timers will fire during a menu pick and so will the
456c041511dScube       idle func. */
457c041511dScube    SetTimer(hwnd, 1, 1, NULL);
458c041511dScube    return 0;
459c041511dScube
460c041511dScube  case WM_TIMER:
461c041511dScube#if 0
462c041511dScube    /* If the timer id is 2, then this is the timer that is set up in
463c041511dScube       the main glut message processing loop, and we don't want to do
464c041511dScube       anything but acknowledge that we got it.  It is used to prevent
465c041511dScube       CPU spiking when an idle function is installed. */
466c041511dScube    if (wParam == 2)
467c041511dScube      return 0;
468c041511dScube#endif
469c041511dScube
470c041511dScube    /* only worry about the idle function and the timeouts, since
471c041511dScube       these are the only events we expect to process during
472c041511dScube       processing of a menu. */
473c041511dScube    /* we no longer process the idle functions (as outlined in the
474c041511dScube       README), since drawing can't be done until the menu has
475c041511dScube       finished...it's pretty lame when the animation goes on, but
476c041511dScube       doesn't update, so you get this weird jerkiness. */
477c041511dScube#if 0
478c041511dScube     if (__glutIdleFunc)
479c041511dScube       __glutIdleFunc();
480c041511dScube#endif
481c041511dScube    if (__glutTimerList)
482c041511dScube      handleTimeouts();
483c041511dScube    return 0;
484c041511dScube
485c041511dScube  case WM_EXITMENULOOP:
486c041511dScube    /* nuke the above created timer...we don't need it anymore, since
487c041511dScube       the menu is gone now. */
488c041511dScube    KillTimer(hwnd, 1);
489c041511dScube    return 0;
490c041511dScube
491c041511dScube  case WM_MENUSELECT:
492c041511dScube    if (lParam != 0)
493c041511dScube      __glutHMenu = (HMENU)lParam;
494c041511dScube    return 0;
495c041511dScube
496c041511dScube  case WM_COMMAND:
497c041511dScube    if (__glutMappedMenu) {
498c041511dScube      if (GetSubMenu(__glutHMenu, LOWORD(wParam)))
499c041511dScube	__glutItemSelected = NULL;
500c041511dScube      else
501c041511dScube	__glutItemSelected =
502c041511dScube	  __glutGetUniqueMenuItem(__glutMappedMenu, LOWORD(wParam));
503c041511dScube      GetCursorPos(&point);
504c041511dScube      ScreenToClient(hwnd, &point);
505c041511dScube      __glutFinishMenu(hwnd, point.x, point.y);
506c041511dScube    }
507c041511dScube    return 0;
508c041511dScube
509c041511dScube  case WM_MOUSEMOVE:
510c041511dScube    if (!__glutMappedMenu) {
511c041511dScube      window = __glutGetWindow(hwnd);
512c041511dScube      if (window) {
513c041511dScube          /* If motion function registered _and_ buttons held *
514c041511dScube             down, call motion function...  */
515c041511dScube	x = LOWORD(lParam);
516c041511dScube	y = HIWORD(lParam);
517c041511dScube
518c041511dScube	/* Win32 doesn't return the same numbers as X does when the
519c041511dScube	   mouse goes beyond the upper or left side of the window.
520c041511dScube	   roll the Win32's 0..2^16 pointer co-ord range to 0..+/-2^15. */
521c041511dScube	if(x & 1 << 15) x -= (1 << 16);
522c041511dScube	if(y & 1 << 15) y -= (1 << 16);
523c041511dScube
524c041511dScube	if (window->motion && wParam &
525c041511dScube            (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
526c041511dScube	  __glutSetWindow(window);
527c041511dScube	  window->motion(x, y);
528c041511dScube	}
529c041511dScube	/* If passive motion function registered _and_
530c041511dScube	   buttons not held down, call passive motion
531c041511dScube	   function...  */
532c041511dScube	else if (window->passive &&
533c041511dScube		 ((wParam &
534c041511dScube		   (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) ==
535c041511dScube		  0)) {
536c041511dScube	  __glutSetWindow(window);
537c041511dScube	  window->passive(x, y);
538c041511dScube	}
539c041511dScube      }
540c041511dScube    } else {
541c041511dScube      /* Motion events are thrown away when a pop up menu is
542c041511dScube	 active. */
543c041511dScube    }
544c041511dScube    return 0;
545c041511dScube
546c041511dScube  case WM_GETMINMAXINFO:
547c041511dScube    /* this voodoo is brought to you by Win32 (again).  It allows the
548c041511dScube       window to be bigger than the screen, and smaller than 100x100
549c041511dScube       (although it doesn't seem to help the y minimum). */
550c041511dScube    minmax = (LPMINMAXINFO)lParam;
5512590f9beSmrg#if 0
5522590f9beSmrg    /* These two lines are disabled to fix incorrect handling of
5532590f9beSmrg     * window maximization on Vista.  See bug 23182.
5542590f9beSmrg     */
555c041511dScube    minmax->ptMaxSize.x = __glutScreenWidth;
556c041511dScube    minmax->ptMaxSize.y = __glutScreenHeight;
5572590f9beSmrg#endif
558c041511dScube    minmax->ptMinTrackSize.x = 0;
559c041511dScube    minmax->ptMinTrackSize.y = 0;
560c041511dScube    minmax->ptMaxTrackSize.x = __glutScreenWidth +
561c041511dScube      GetSystemMetrics(SM_CXSIZE) * 2;
562c041511dScube    minmax->ptMaxTrackSize.y = __glutScreenHeight +
563c041511dScube      GetSystemMetrics(SM_CXSIZE) * 2 + GetSystemMetrics(SM_CYCAPTION);
564c041511dScube    return 0;
565c041511dScube
566c041511dScube  case WM_SIZE:
567c041511dScube    window = __glutGetWindow(hwnd);
568c041511dScube    if (window) {
569c041511dScube      width = LOWORD(lParam);
570c041511dScube      height = HIWORD(lParam);
571c041511dScube      if (width != window->width || height != window->height) {
572c041511dScube#if 0  /* Win32 GLUT does not support overlays for now. */
573c041511dScube 	if (window->overlay) {
574c041511dScube 	  XResizeWindow(__glutDisplay, window->overlay->win, width, height);
575c041511dScube 	}
576c041511dScube#endif
577c041511dScube	window->width = width;
578c041511dScube	window->height = height;
579c041511dScube	__glutSetWindow(window);
580c041511dScube	/* Do not execute OpenGL out of sequence with respect
581c041511dScube	   to the SetWindowPos request! */
582c041511dScube	GdiFlush();
583c041511dScube	window->reshape(width, height);
584c041511dScube	window->forceReshape = FALSE;
585c041511dScube	/* A reshape should be considered like posting a
586c041511dScube	   repair request. */
587c041511dScube	__glutPostRedisplay(window, GLUT_REPAIR_WORK);
588c041511dScube      }
589c041511dScube    }
590c041511dScube    return 0;
591c041511dScube
592c041511dScube  case WM_SETCURSOR:
593c041511dScube    /* If the cursor is not in the client area, then we want to send
594c041511dScube       this message to the default window procedure ('cause its
595c041511dScube       probably in the border or title, and we don't handle that
596c041511dScube       cursor.  otherwise, set our cursor.  Win32 makes us set the
597c041511dScube       cursor every time the mouse moves (DUMB!). */
598c041511dScube    if(LOWORD(lParam) != HTCLIENT) {
599c041511dScube      goto defproc;
600c041511dScube    }
601c041511dScube    window = __glutGetWindow(hwnd);
602c041511dScube    if (window) {
603c041511dScube      __glutSetCursor(window);
604c041511dScube    }
605c041511dScube    /* TODO: check out the info in DevStudio on WM_SETCURSOR in the
606c041511dScube       DefaultAction section. */
607c041511dScube    return 1;
608c041511dScube
609c041511dScube  case WM_SETFOCUS:
610c041511dScube    window = __glutGetWindow(hwnd);
611c041511dScube    if (window) {
612c041511dScube      window->entryState = WM_SETFOCUS;
613c041511dScube      if (window->entry) {
614c041511dScube	__glutSetWindow(window);
615c041511dScube	window->entry(GLUT_ENTERED);
616c041511dScube	/* XXX Generation of fake passive notify?  See how much
617c041511dScube	   work the X11 code does to support fake passive notify
618c041511dScube	   callbacks. */
619c041511dScube      }
620c041511dScube      if (window->joystick && __glutCurrentWindow) {
621c041511dScube        if (__glutCurrentWindow->joyPollInterval > 0) {
622c041511dScube	  MMRESULT result;
623c041511dScube
624c041511dScube	  /* Because Win32 will only let one window capture the
625c041511dScube	     joystick at a time, we must capture it when we get the
626c041511dScube	     focus and release it when we lose the focus. */
627c041511dScube	  result = joySetCapture(__glutCurrentWindow->win,
628c041511dScube	    JOYSTICKID1, 0, TRUE);
629c041511dScube	  if (result != JOYERR_NOERROR) {
630c041511dScube	    return 0;
631c041511dScube          }
632c041511dScube	  (void) joySetThreshold(JOYSTICKID1,
633c041511dScube            __glutCurrentWindow->joyPollInterval);
634c041511dScube        }
635c041511dScube      }
636c041511dScube    }
637c041511dScube    return 0;
638c041511dScube
639c041511dScube  case WM_KILLFOCUS:
640c041511dScube    window = __glutGetWindow(hwnd);
641c041511dScube    if (window) {
642c041511dScube      window->entryState = WM_KILLFOCUS;
643c041511dScube      if (window->entry) {
644c041511dScube	__glutSetWindow(window);
645c041511dScube	window->entry(GLUT_LEFT);
646c041511dScube      }
647c041511dScube      if (window->joystick && __glutCurrentWindow) {
648c041511dScube	if (__glutCurrentWindow->joyPollInterval > 0) {
649c041511dScube	  /* Because Win32 will only let one window capture the
650c041511dScube	     joystick at a time, we must capture it when we get the
651c041511dScube	     focus and release it when we lose the focus. */
652c041511dScube	    (void) joyReleaseCapture(JOYSTICKID1);
653c041511dScube        }
654c041511dScube      }
655c041511dScube    }
656c041511dScube    return 0;
657c041511dScube  case WM_ACTIVATE:
658c041511dScube    window = __glutGetWindow(hwnd);
659c041511dScube    /* Make sure we re-select the correct palette if needed. */
660c041511dScube    if (LOWORD(wParam)) {
661c041511dScube      PostMessage(hwnd, WM_PALETTECHANGED, 0, 0);
662c041511dScube    }
663c041511dScube    if (window) {
664c041511dScube      int visState;
665c041511dScube
666c041511dScube      /* HIWORD(wParam) is the minimized flag. */
667c041511dScube      visState = !HIWORD(wParam);
668c041511dScube      updateWindowState(window, visState);
669c041511dScube    }
670c041511dScube    return 0;
671c041511dScube
672c041511dScube  /* Colour Palette Management */
673c041511dScube  case WM_PALETTECHANGED:
674c041511dScube    if (hwnd == (HWND)wParam) {
675c041511dScube      /* Don't respond to the message that we sent! */
676c041511dScube      break;
677c041511dScube    }
678c041511dScube    /* fall through to WM_QUERYNEWPALETTE */
679c041511dScube
680c041511dScube  case WM_QUERYNEWPALETTE:
681c041511dScube    window = __glutGetWindow(hwnd);
682c041511dScube    if (window && window->colormap) {
683c041511dScube      UnrealizeObject(window->colormap->cmap);
684c041511dScube      SelectPalette(window->hdc, window->colormap->cmap, FALSE);
685c041511dScube      RealizePalette(window->hdc);
686c041511dScube      return TRUE;
687c041511dScube    }
688c041511dScube    return FALSE;
689c041511dScube
690c041511dScube  case MM_JOY1MOVE:
691c041511dScube  case MM_JOY1ZMOVE:
692c041511dScube    window = __glutGetWindow(hwnd);
693c041511dScube    if (window->joystick) {
694c041511dScube      JOYINFOEX jix;
695c041511dScube      int x, y, z;
696c041511dScube
697c041511dScube      /* Because WIN32 only supports messages for X, Y, and Z
698c041511dScube         translations, we must poll for the rest */
699c041511dScube      jix.dwSize = sizeof(jix);
700c041511dScube      jix.dwFlags = JOY_RETURNALL;
701c041511dScube      joyGetPosEx(JOYSTICKID1,&jix);
702c041511dScube
703c041511dScube#define SCALE(v)  ((int) ((v - 32767)/32.768))
704c041511dScube
705c041511dScube      /* Convert to integer for scaling. */
706c041511dScube      x = jix.dwXpos;
707c041511dScube      y = jix.dwYpos;
708c041511dScube      z = jix.dwZpos;
709c041511dScube      window->joystick(jix.dwButtons, SCALE(x), SCALE(y), SCALE(z));
710c041511dScube
711c041511dScube      return TRUE;
712c041511dScube    }
713c041511dScube    return FALSE;
714c041511dScube  case MM_JOY1BUTTONDOWN:
715c041511dScube  case MM_JOY1BUTTONUP:
716c041511dScube    window = __glutGetWindow(hwnd);
717c041511dScube    if (window->joystick) {
718c041511dScube      JOYINFOEX jix;
719c041511dScube
720c041511dScube      /* Because WIN32 only supports messages for X, Y, and Z
721c041511dScube         translations, we must poll for the rest */
722c041511dScube      jix.dwSize = sizeof(jix);
723c041511dScube      jix.dwFlags = JOY_RETURNALL;
724c041511dScube      joyGetPosEx(JOYSTICKID1,&jix);
725c041511dScube
726c041511dScube      return TRUE;
727c041511dScube    }
728c041511dScube    return FALSE;
729c041511dScube
730c041511dScube#if 0
731c041511dScube  /* Miscellaneous messages (don't really need to enumerate them,
732c041511dScube     but it's good to know what you're not getting sometimes). */
733c041511dScube  case WM_DISPLAYCHANGE:
734c041511dScube    break;
735c041511dScube  case WM_NCHITTEST:
736c041511dScube    /* This event is generated by every mouse move event. */
737c041511dScube    goto defproc;
738c041511dScube  case WM_NCMOUSEMOVE:
739c041511dScube    goto defproc;
740c041511dScube  case WM_NCACTIVATE:
741c041511dScube    goto defproc;
742c041511dScube  case WM_NCPAINT:
743c041511dScube    goto defproc;
744c041511dScube  case WM_NCCALCSIZE:
745c041511dScube    goto defproc;
746c041511dScube  case WM_NCCREATE:
747c041511dScube    goto defproc;
748c041511dScube  case WM_NCDESTROY:
749c041511dScube    goto defproc;
750c041511dScube  case WM_NCLBUTTONDOWN:
751c041511dScube    goto defproc;
752c041511dScube  case WM_SETTEXT:
753c041511dScube    goto defproc;
754c041511dScube  case WM_GETTEXT:
755c041511dScube    goto defproc;
756c041511dScube  case WM_ACTIVATEAPP:
757c041511dScube    goto defproc;
758c041511dScube  case WM_GETICON:
759c041511dScube    goto defproc;
760c041511dScube  case WM_ERASEBKGND:
761c041511dScube    goto defproc;
762c041511dScube  case WM_WINDOWPOSCHANGING:
763c041511dScube    goto defproc;
764c041511dScube  case WM_WINDOWPOSCHANGED:
765c041511dScube    goto defproc;
766c041511dScube  case WM_MOUSEACTIVATE:
767c041511dScube    goto defproc;
768c041511dScube  case WM_SHOWWINDOW:
769c041511dScube    goto defproc;
770c041511dScube  case WM_MOVING:
771c041511dScube    goto defproc;
772c041511dScube  case WM_MOVE:
773c041511dScube    goto defproc;
774c041511dScube  case WM_KEYUP:
775c041511dScube    goto defproc;
776c041511dScube  case WM_CAPTURECHANGED:
777c041511dScube    goto defproc;
778c041511dScube  case WM_SYSCOMMAND:
779c041511dScube    goto defproc;
780c041511dScube  case WM_ENTERSIZEMOVE:
781c041511dScube    goto defproc;
782c041511dScube  case WM_ENTERIDLE:
783c041511dScube    goto defproc;
784c041511dScube#endif
785c041511dScube
786c041511dScube  default:
787c041511dScube    goto defproc;
788c041511dScube  }
789c041511dScube
790c041511dScubedefproc:
791c041511dScube  return DefWindowProc(hwnd, msg, wParam, lParam);
792c041511dScube}
793