1c041511dScube
2c041511dScube/* Copyright (c) Mark J. Kilgard, 1994, 1997. */
3c041511dScube
4c041511dScube/* This program is freely distributable without licensing fees
5c041511dScube   and is provided without guarantee or warrantee expressed or
6c041511dScube   implied. This program is -not- in the public domain. */
7c041511dScube
8c041511dScube/* The Win32 GLUT file win32_menu.c completely re-implements all
9c041511dScube   the menuing functionality implemented.  This file is used only by
10c041511dScube   the X Window System version of GLUT. */
11c041511dScube
12c041511dScube#ifdef __VMS
13c041511dScube#include <GL/vms_x_fix.h>
14c041511dScube#endif
15c041511dScube
16c041511dScube#include <stdlib.h>
17c041511dScube#include <string.h>
18c041511dScube#include <stdio.h>
19c041511dScube#include <errno.h>
20c041511dScube#include <assert.h>
21c041511dScube
22c041511dScube#include <unistd.h>
23c041511dScube#include <X11/Xlib.h>
24c041511dScube#include <X11/cursorfont.h>  /* for XC_arrow */
25c041511dScube
26c041511dScube#include "glutint.h"
27c041511dScube#include "layerutil.h"
28c041511dScube
29c041511dScubevoid (CDECL *__glutMenuStatusFunc) (int, int, int);
30c041511dScubeGLUTmenuItem *__glutItemSelected;
31c041511dScubeGLUTmenu **__glutMenuList = NULL;
32c041511dScube
33c041511dScubestatic int menuListSize = 0;
34c041511dScubestatic XFontStruct *menuFont = NULL;
35c041511dScubestatic Cursor menuCursor;
36c041511dScubestatic Colormap menuColormap;
37c041511dScubestatic Visual *menuVisual;
38c041511dScubestatic int menuDepth;
39c041511dScubestatic int fontHeight;
40c041511dScubestatic GC blackGC, grayGC, whiteGC;
41c041511dScubestatic unsigned long menuBlack, menuWhite, menuGray;
42c041511dScubestatic unsigned long useSaveUnders;
43c041511dScube
44c041511dScube/* A replacement for XAllocColor (originally by Brian Paul).
45c041511dScube   This  function should never fail to allocate a color.  When
46c041511dScube   XAllocColor fails, we return the nearest matching color.  If
47c041511dScube   we have to allocate many colors this function isn't a great
48c041511dScube   solution; the XQueryColors() could be done just once.  */
49c041511dScubestatic void
50c041511dScubenoFaultXAllocColor(Display * dpy, Colormap cmap, int cmapSize,
51c041511dScube  XColor * color)
52c041511dScube{
53c041511dScube  XColor *ctable, subColor;
54c041511dScube  int i, bestmatch;
55c041511dScube  double mindist;       /* 3*2^16^2 exceeds 32-bit long int
56c041511dScube                           precision. */
57c041511dScube
58c041511dScube  for (;;) {
59c041511dScube    /* First try just using XAllocColor. */
60c041511dScube    if (XAllocColor(dpy, cmap, color)) {
61c041511dScube      return;
62c041511dScube    }
63c041511dScube
64c041511dScube    /* Retrieve color table entries. */
65c041511dScube    /* XXX alloca canidate. */
66c041511dScube    ctable = (XColor *) malloc(cmapSize * sizeof(XColor));
67c041511dScube    for (i = 0; i < cmapSize; i++)
68c041511dScube      ctable[i].pixel = i;
69c041511dScube    XQueryColors(dpy, cmap, ctable, cmapSize);
70c041511dScube
71c041511dScube    /* Find best match. */
72c041511dScube    bestmatch = -1;
73c041511dScube    mindist = 0.0;
74c041511dScube    for (i = 0; i < cmapSize; i++) {
75c041511dScube      double dr = (double) color->red - (double) ctable[i].red;
76c041511dScube      double dg = (double) color->green - (double) ctable[i].green;
77c041511dScube      double db = (double) color->blue - (double) ctable[i].blue;
78c041511dScube      double dist = dr * dr + dg * dg + db * db;
79c041511dScube      if (bestmatch < 0 || dist < mindist) {
80c041511dScube        bestmatch = i;
81c041511dScube        mindist = dist;
82c041511dScube      }
83c041511dScube    }
84c041511dScube
85c041511dScube    /* Return result. */
86c041511dScube    subColor.red = ctable[bestmatch].red;
87c041511dScube    subColor.green = ctable[bestmatch].green;
88c041511dScube    subColor.blue = ctable[bestmatch].blue;
89c041511dScube    free(ctable);
90c041511dScube    if (XAllocColor(dpy, cmap, &subColor)) {
91c041511dScube      *color = subColor;
92c041511dScube      return;
93c041511dScube    }
94c041511dScube    /* Extremely unlikely, but possibly color was deallocated
95c041511dScube       and reallocated by someone else before we could
96c041511dScube       XAllocColor the color cell we located.  If so, loop
97c041511dScube       again... */
98c041511dScube  }
99c041511dScube}
100c041511dScube
101c041511dScubestatic int
102c041511dScubeifSunCreator(void)
103c041511dScube{
104c041511dScube  char *xvendor, *glvendor, *renderer;
105c041511dScube  int isSunCreator = 0; /* Until proven that it is. */
106c041511dScube  int savedDisplayMode = 0;
107c041511dScube  char *savedDisplayString = 0;
108c041511dScube  GLUTwindow *window;
109c041511dScube
110c041511dScube#define VENDOR_SUN "Sun Microsystems"
111c041511dScube#define RENDERER_CREATOR "Creator"
112c041511dScube
113c041511dScube  /* Check the X vendor string first.  It is easier to check
114c041511dScube     than the OpenGL vendor and renderer strings since it
115c041511dScube     doesn't require a valid OpenGL rendering context.  Bail
116c041511dScube     early if not connected to a Sun. */
117c041511dScube  xvendor = ServerVendor(__glutDisplay);
118c041511dScube  if (!strncmp(xvendor, VENDOR_SUN, sizeof(VENDOR_SUN) - 1)) {
119c041511dScube
120c041511dScube    /* We need a valid current OpenGL rendering context to be
121c041511dScube       able to call glGetString successfully.  If there is not
122c041511dScube       a current window, set up a temporary one just to call
123c041511dScube       glGetString with (gag, expensive). */
124c041511dScube    if (__glutCurrentWindow) {
125c041511dScube      window = NULL;
126c041511dScube    } else {
127c041511dScube      savedDisplayMode = __glutDisplayMode;
128c041511dScube      savedDisplayString = __glutDisplayString;
129c041511dScube      __glutDisplayMode = GLUT_RGB | GLUT_SINGLE;
130c041511dScube      __glutDisplayString = NULL;
131c041511dScube      window = __glutCreateWindow(NULL, 0, 0, 1, 1, 0);
132c041511dScube    }
133c041511dScube
134c041511dScube    glvendor = (char *) glGetString(GL_VENDOR);
135c041511dScube    if (!strncmp(glvendor, VENDOR_SUN, sizeof(VENDOR_SUN) - 1)) {
136c041511dScube      renderer = (char *) glGetString(GL_RENDERER);
137c041511dScube      if (!strncmp(renderer, RENDERER_CREATOR, sizeof(RENDERER_CREATOR) - 1)) {
138c041511dScube        isSunCreator = 1;
139c041511dScube      }
140c041511dScube    }
141c041511dScube    /* Destroy the temporary window for glGetString if one
142c041511dScube       needed to be created. */
143c041511dScube    if (window) {
144c041511dScube      __glutDestroyWindow(window, window);
145c041511dScube      __glutDisplayMode = savedDisplayMode;
146c041511dScube      __glutDisplayString = savedDisplayString;
147c041511dScube    }
148c041511dScube  }
149c041511dScube  return isSunCreator;
150c041511dScube}
151c041511dScube
152c041511dScubestatic void
153c041511dScubemenuVisualSetup(void)
154c041511dScube{
155c041511dScube  XLayerVisualInfo template, *visual, *overlayVisuals;
156c041511dScube  XColor color;
157c041511dScube  Status status;
158c041511dScube  Bool presumablyMesa;
159c041511dScube  int layer, nVisuals, i, dummy;
160c041511dScube  unsigned long *placeHolders = NULL;
161c041511dScube  int numPlaceHolders = 0;
162c041511dScube  Bool allocateHigh;
163c041511dScube
164c041511dScube  allocateHigh = ifSunCreator();
165c041511dScube
166c041511dScube  /* Start with the highest overlay layer and work down.  I
167c041511dScube     don't think any hardware has more than 3 overlay layers. */
168c041511dScube  for (layer = 3; layer > 0; layer--) {
169c041511dScube    template.layer = layer;
170c041511dScube    template.vinfo.screen = __glutScreen;
171c041511dScube    overlayVisuals = __glutXGetLayerVisualInfo(__glutDisplay,
172c041511dScube      VisualScreenMask | VisualLayerMask, &template, &nVisuals);
173c041511dScube    if (overlayVisuals) {
174c041511dScube      /* First, check if the default visual is in this layer.
175c041511dScube         If the default visual is in this layer, we try to use
176c041511dScube         it since it has pre-defined black and white pixels and
177c041511dScube
178c041511dScube         using the default visual will probably minimize
179c041511dScube         colormap flashing problems. Suggested by Thomas Roell
180c041511dScube         (thomas@xig.com). */
181c041511dScube      for (i = 0; i < nVisuals; i++) {
182c041511dScube        visual = &overlayVisuals[i];
183c041511dScube        if (visual->vinfo.colormap_size >= 3) {
184c041511dScube          /* Compare visual IDs just to be safe. */
185c041511dScube          if (visual->vinfo.visual->visualid == DefaultVisual(__glutDisplay, __glutScreen)->visualid) {
186c041511dScube            /* Settle for default visual. */
187c041511dScube            menuVisual = DefaultVisual(__glutDisplay, __glutScreen);
188c041511dScube            menuDepth = DefaultDepth(__glutDisplay, __glutScreen);
189c041511dScube            menuColormap = DefaultColormap(__glutDisplay, __glutScreen);
190c041511dScube            menuBlack = BlackPixel(__glutDisplay, __glutScreen);
191c041511dScube            menuWhite = WhitePixel(__glutDisplay, __glutScreen);
192c041511dScube            color.red = color.green = color.blue = 0xaa00;
193c041511dScube            noFaultXAllocColor(__glutDisplay, menuColormap,
194c041511dScube              menuVisual->map_entries, &color);
195c041511dScube            menuGray = color.pixel;
196c041511dScube            useSaveUnders = 0;
197c041511dScube            XFree(overlayVisuals);
198c041511dScube            return;
199c041511dScube          }
200c041511dScube        }
201c041511dScube      }
202c041511dScube      for (i = 0; i < nVisuals; i++) {
203c041511dScube        visual = &overlayVisuals[i];
204c041511dScube        if (visual->vinfo.colormap_size >= 3) {
205c041511dScube          if (allocateHigh) {
206c041511dScube            /* For Sun's Creator graphics, try to force the
207c041511dScube               read-only colors to the high end of the colormap
208c041511dScube               by first allocating read-write place-holder cells
209c041511dScube               for all but the last three cells.  This helps
210c041511dScube               avoid colormap flashing problems. */
211c041511dScube            numPlaceHolders = visual->vinfo.colormap_size - 3;
212c041511dScube            if (numPlaceHolders > 0) {
213c041511dScube              placeHolders = (unsigned long *)
214c041511dScube                malloc(numPlaceHolders * sizeof(unsigned long));
215c041511dScube              /* A malloc failure would be harmless. */
216c041511dScube            }
217c041511dScube          }
218c041511dScube          menuColormap = XCreateColormap(__glutDisplay, __glutRoot,
219c041511dScube            visual->vinfo.visual, AllocNone);
220c041511dScube          if (placeHolders) {
221c041511dScube            /* Again for Sun's Creator graphics, do the actual
222c041511dScube               read-write place-holder cell allocation. */
223c041511dScube            status = XAllocColorCells(__glutDisplay, menuColormap, False, 0, 0,
224c041511dScube              placeHolders, numPlaceHolders);
225c041511dScube            if (!status) {
226c041511dScube              XFreeColormap(__glutDisplay, menuColormap);
227c041511dScube              free(placeHolders);
2282590f9beSmrg              placeHolders = NULL;
229c041511dScube              continue;
230c041511dScube            }
231c041511dScube          }
232c041511dScube          /* Allocate overlay colormap cells in defined order:
233c041511dScube             gray, black, white to match the IRIS GL allocation
234c041511dScube             scheme.  Increases likelihood of less overlay
235c041511dScube             colormap flashing. */
236c041511dScube          /* XXX Nice if these 3 AllocColor's could be done in
237c041511dScube             one protocol round-trip. */
238c041511dScube          color.red = color.green = color.blue = 0xaa00;
239c041511dScube          status = XAllocColor(__glutDisplay,
240c041511dScube            menuColormap, &color);
241c041511dScube          if (!status) {
242c041511dScube            XFreeColormap(__glutDisplay, menuColormap);
243c041511dScube            if (placeHolders) {
244c041511dScube              free(placeHolders);
2452590f9beSmrg              placeHolders = NULL;
246c041511dScube            }
247c041511dScube            continue;
248c041511dScube          }
249c041511dScube          menuGray = color.pixel;
250c041511dScube          color.red = color.green = color.blue = 0x0000;
251c041511dScube          status = XAllocColor(__glutDisplay,
252c041511dScube            menuColormap, &color);
253c041511dScube          if (!status) {
254c041511dScube            XFreeColormap(__glutDisplay, menuColormap);
255c041511dScube            if (placeHolders) {
256c041511dScube              free(placeHolders);
2572590f9beSmrg              placeHolders = NULL;
258c041511dScube            }
259c041511dScube            continue;
260c041511dScube          }
261c041511dScube          menuBlack = color.pixel;
262c041511dScube          color.red = color.green = color.blue = 0xffff;
263c041511dScube          status = XAllocColor(__glutDisplay,
264c041511dScube            menuColormap, &color);
265c041511dScube          if (!status) {
266c041511dScube            XFreeColormap(__glutDisplay, menuColormap);
267c041511dScube            if (placeHolders) {
268c041511dScube              free(placeHolders);
2692590f9beSmrg              placeHolders = NULL;
270c041511dScube            }
271c041511dScube            continue;
272c041511dScube          }
273c041511dScube          if (placeHolders) {
274c041511dScube            /* Now free the placeholder cells. */
275c041511dScube            XFreeColors(__glutDisplay, menuColormap,
276c041511dScube              placeHolders, numPlaceHolders, 0);
277c041511dScube            free(placeHolders);
2782590f9beSmrg            placeHolders = NULL;
279c041511dScube          }
280c041511dScube          menuWhite = color.pixel;
281c041511dScube          menuVisual = visual->vinfo.visual;
282c041511dScube          menuDepth = visual->vinfo.depth;
283c041511dScube          /* If using overlays, do not request "save unders". */
284c041511dScube          useSaveUnders = 0;
285c041511dScube          XFree(overlayVisuals);
286c041511dScube          return;
287c041511dScube        }
288c041511dScube      }
289c041511dScube      XFree(overlayVisuals);
290c041511dScube    }
291c041511dScube  }
292c041511dScube  /* Settle for default visual. */
293c041511dScube  menuVisual = DefaultVisual(__glutDisplay, __glutScreen);
294c041511dScube  menuDepth = DefaultDepth(__glutDisplay, __glutScreen);
295c041511dScube  menuColormap = DefaultColormap(__glutDisplay, __glutScreen);
296c041511dScube  menuBlack = BlackPixel(__glutDisplay, __glutScreen);
297c041511dScube  menuWhite = WhitePixel(__glutDisplay, __glutScreen);
298c041511dScube  color.red = color.green = color.blue = 0xaa00;
299c041511dScube  noFaultXAllocColor(__glutDisplay, menuColormap,
300c041511dScube    menuVisual->map_entries, &color);
301c041511dScube  menuGray = color.pixel;
302c041511dScube
303c041511dScube  /* When no overlays are supported, we would like to use X
304c041511dScube     "save unders" to avoid exposes to windows obscured by
305c041511dScube     pop-up menus.  However, OpenGL's direct rendering support
306c041511dScube     means OpenGL interacts poorly with X backing store and
307c041511dScube     save unders.  X servers do not (in implementation
308c041511dScube     practice) redirect OpenGL rendering destined to obscured
309c041511dScube     window regions into backing store.
310c041511dScube
311c041511dScube     Implementation solutions exist for this problem, but they
312c041511dScube     are expensive and high-end OpenGL implementations
313c041511dScube     typically provide fast rendering and/or overlays to
314c041511dScube     obviate the problem associated of user interfaces (pop-up
315c041511dScube     menus) forcing redraws of complex normal plane scenes.
316c041511dScube     (See support for overlays pop-up menus above.)
317c041511dScube
318c041511dScube     Mesa 3D, however, does not support direct rendering.
319c041511dScube     Overlays are often unavailable to Mesa, and Mesa is also
320c041511dScube     relatively slow.  For these reasons, Mesa-rendering GLUT
321c041511dScube     programs can and should use X save unders.
322c041511dScube
323c041511dScube     Look for the GLX extension.  If _not_ supported, we are
324c041511dScube     presumably using Mesa so enable save unders. */
325c041511dScube
326c041511dScube  presumablyMesa = !XQueryExtension(__glutDisplay, "GLX",
327c041511dScube    &dummy, &dummy, &dummy);
328c041511dScube
329c041511dScube  if (presumablyMesa) {
330c041511dScube    useSaveUnders = CWSaveUnder;
331c041511dScube  } else {
332c041511dScube    useSaveUnders = 0;
333c041511dScube  }
334c041511dScube}
335c041511dScube
336c041511dScubestatic void
337c041511dScubemenuSetup(void)
338c041511dScube{
339c041511dScube  if (menuFont) {
340c041511dScube    /* MenuFont overload to indicate menu initalization. */
341c041511dScube    return;
342c041511dScube  }
343c041511dScube  menuFont = XLoadQueryFont(__glutDisplay,
344c041511dScube    "-*-helvetica-bold-o-normal--14-*-*-*-p-*-iso8859-1");
345c041511dScube  if (!menuFont) {
346c041511dScube    /* Try back up font. */
347c041511dScube    menuFont = XLoadQueryFont(__glutDisplay, "fixed");
348c041511dScube  }
349c041511dScube  if (!menuFont) {
350c041511dScube    __glutFatalError("could not load font.");
351c041511dScube  }
352c041511dScube  menuVisualSetup();
353c041511dScube  fontHeight = menuFont->ascent + menuFont->descent;
354c041511dScube  menuCursor = XCreateFontCursor(__glutDisplay, XC_arrow);
355c041511dScube}
356c041511dScube
357c041511dScubestatic void
358c041511dScubemenuGraphicsContextSetup(Window win)
359c041511dScube{
360c041511dScube  XGCValues gcvals;
361c041511dScube
362c041511dScube  if (blackGC != None) {
363c041511dScube    return;
364c041511dScube  }
365c041511dScube  gcvals.font = menuFont->fid;
366c041511dScube  gcvals.foreground = menuBlack;
367c041511dScube  blackGC = XCreateGC(__glutDisplay, win,
368c041511dScube    GCFont | GCForeground, &gcvals);
369c041511dScube  gcvals.foreground = menuGray;
370c041511dScube  grayGC = XCreateGC(__glutDisplay, win, GCForeground, &gcvals);
371c041511dScube  gcvals.foreground = menuWhite;
372c041511dScube  whiteGC = XCreateGC(__glutDisplay, win, GCForeground, &gcvals);
373c041511dScube}
374c041511dScube
375c041511dScubevoid
376c041511dScube__glutSetMenu(GLUTmenu * menu)
377c041511dScube{
378c041511dScube  __glutCurrentMenu = menu;
379c041511dScube}
380c041511dScube
381c041511dScubestatic void
382c041511dScubeunmapMenu(GLUTmenu * menu)
383c041511dScube{
384c041511dScube  if (menu->cascade) {
385c041511dScube    unmapMenu(menu->cascade);
386c041511dScube    menu->cascade = NULL;
387c041511dScube  }
388c041511dScube  menu->anchor = NULL;
389c041511dScube  menu->highlighted = NULL;
390c041511dScube  XUnmapWindow(__glutDisplay, menu->win);
391c041511dScube}
392c041511dScube
393c041511dScubestatic void
394c041511dScubefinishMenu(Window win, int x, int y)
395c041511dScube{
396c041511dScube  Window dummy;
397c041511dScube  int rc;
398c041511dScube
399c041511dScube  unmapMenu(__glutMappedMenu);
400c041511dScube  XUngrabPointer(__glutDisplay, CurrentTime);
401c041511dScube
402c041511dScube  /* Popping up an overlay popup menu will install its own
403c041511dScube     colormap.  If the window associated with the menu has an
404c041511dScube     overlay, install that window's overlay colormap so the
405c041511dScube     overlay isn't left using the popup menu's colormap. */
406c041511dScube  if (__glutMenuWindow->overlay) {
407c041511dScube    XInstallColormap(__glutDisplay,
408c041511dScube      __glutMenuWindow->overlay->colormap->cmap);
409c041511dScube  }
410c041511dScube
411c041511dScube  /* This XFlush is needed to to make sure the pointer is
412c041511dScube     really ungrabbed when the application's menu callback is
413c041511dScube     called. Otherwise, a deadlock might happen because the
414c041511dScube     application may try to read from an terminal window, but
415c041511dScube     yet the ungrab hasn't really happened since it hasn't been
416c041511dScube     flushed out. */
417c041511dScube  XFlush(__glutDisplay);
418c041511dScube
419c041511dScube  if (__glutMenuStatusFunc) {
420c041511dScube    if (win != __glutMenuWindow->win) {
421c041511dScube      /* The button release may have occurred in a window other
422c041511dScube         than the window requesting the pop-up menu (for
423c041511dScube         example, one of the submenu windows).  In this case, we
424c041511dScube         need to translate the coordinates into the coordinate
425c041511dScube         system of the window associated with the window. */
426c041511dScube      rc = XTranslateCoordinates(__glutDisplay, win, __glutMenuWindow->win,
427c041511dScube        x, y, &x, &y, &dummy);
428c041511dScube      assert(rc != False);  /* Will always be on same screen. */
429c041511dScube    }
430c041511dScube    __glutSetWindow(__glutMenuWindow);
431c041511dScube    __glutSetMenu(__glutMappedMenu);
432c041511dScube
433c041511dScube    /* Setting __glutMappedMenu to NULL permits operations that
434c041511dScube       change menus or destroy the menu window again. */
435c041511dScube    __glutMappedMenu = NULL;
436c041511dScube
437c041511dScube    __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y);
438c041511dScube  }
439c041511dScube  /* Setting __glutMappedMenu to NULL permits operations that
440c041511dScube     change menus or destroy the menu window again. */
441c041511dScube  __glutMappedMenu = NULL;
442c041511dScube
443c041511dScube  /* If an item is selected and it is not a submenu trigger,
444c041511dScube     generate menu callback. */
445c041511dScube  if (__glutItemSelected && !__glutItemSelected->isTrigger) {
446c041511dScube    __glutSetWindow(__glutMenuWindow);
447c041511dScube    /* When menu callback is triggered, current menu should be
448c041511dScube       set to the callback menu. */
449c041511dScube    __glutSetMenu(__glutItemSelected->menu);
450c041511dScube    __glutItemSelected->menu->select(
451c041511dScube      __glutItemSelected->value);
452c041511dScube  }
453c041511dScube  __glutMenuWindow = NULL;
454c041511dScube}
455c041511dScube
456c041511dScube#define MENU_BORDER 1
457c041511dScube#define MENU_GAP 2
458c041511dScube#define MENU_ARROW_GAP 6
459c041511dScube#define MENU_ARROW_WIDTH 8
460c041511dScube
461c041511dScubestatic void
462c041511dScubemapMenu(GLUTmenu * menu, int x, int y)
463c041511dScube{
464c041511dScube  XWindowChanges changes;
465c041511dScube  unsigned int mask;
466c041511dScube  int subMenuExtension, num;
467c041511dScube
468c041511dScube  /* If there are submenus, we need to provide extra space for
469c041511dScube     the submenu pull arrow.  */
470c041511dScube  if (menu->submenus > 0) {
471c041511dScube    subMenuExtension = MENU_ARROW_GAP + MENU_ARROW_WIDTH;
472c041511dScube  } else {
473c041511dScube    subMenuExtension = 0;
474c041511dScube  }
475c041511dScube
476c041511dScube  changes.stack_mode = Above;
477c041511dScube  mask = CWStackMode | CWX | CWY;
478c041511dScube  /* If the menu isn't managed (ie, validated so all the
479c041511dScube     InputOnly subwindows are the right size), do so.  */
480c041511dScube  if (!menu->managed) {
481c041511dScube    GLUTmenuItem *item;
482c041511dScube
483c041511dScube    item = menu->list;
484c041511dScube    num = menu->num;
485c041511dScube    while (item) {
486c041511dScube      XWindowChanges itemupdate;
487c041511dScube
488c041511dScube      itemupdate.y = (num - 1) * fontHeight + MENU_GAP;
489c041511dScube      itemupdate.width = menu->pixwidth;
490c041511dScube      itemupdate.width += subMenuExtension;
491c041511dScube      XConfigureWindow(__glutDisplay, item->win,
492c041511dScube        CWWidth | CWY, &itemupdate);
493c041511dScube      item = item->next;
494c041511dScube      num--;
495c041511dScube    }
496c041511dScube    menu->pixheight = MENU_GAP +
497c041511dScube      fontHeight * menu->num + MENU_GAP;
498c041511dScube    changes.height = menu->pixheight;
499c041511dScube    changes.width = MENU_GAP +
500c041511dScube      menu->pixwidth + subMenuExtension + MENU_GAP;
501c041511dScube    mask |= CWWidth | CWHeight;
502c041511dScube    menu->managed = True;
503c041511dScube  }
504c041511dScube  /* Make sure menu appears fully on screen. */
505c041511dScube  if (y + menu->pixheight >= __glutScreenHeight) {
506c041511dScube    changes.y = __glutScreenHeight - menu->pixheight;
507c041511dScube  } else {
508c041511dScube    changes.y = y;
509c041511dScube  }
510c041511dScube  if (x + menu->pixwidth + subMenuExtension >=
511c041511dScube    __glutScreenWidth) {
512c041511dScube    changes.x = __glutScreenWidth -
513c041511dScube      menu->pixwidth + subMenuExtension;
514c041511dScube  } else {
515c041511dScube    changes.x = x;
516c041511dScube  }
517c041511dScube
518c041511dScube  /* Rember where the menu is placed so submenus can be
519c041511dScube     properly placed relative to it. */
520c041511dScube  menu->x = changes.x;
521c041511dScube  menu->y = changes.y;
522c041511dScube
523c041511dScube  XConfigureWindow(__glutDisplay, menu->win, mask, &changes);
524c041511dScube  XInstallColormap(__glutDisplay, menuColormap);
525c041511dScube  /* XXX The XRaiseWindow below should not be necessary because
526c041511dScube     the XConfigureWindow requests an Above stack mode (same as
527c041511dScube     XRaiseWindow), but some Sun users complained this was still
528c041511dScube     necessary.  Probably some window manager or X server bug on
529c041511dScube     these machines?? */
530c041511dScube  XRaiseWindow(__glutDisplay, menu->win);
531c041511dScube  XMapWindow(__glutDisplay, menu->win);
532c041511dScube}
533c041511dScube
534c041511dScubestatic void
535c041511dScubestartMenu(GLUTmenu * menu, GLUTwindow * window,
536c041511dScube  int x, int y, int x_win, int y_win)
537c041511dScube{
538c041511dScube  int grab;
539c041511dScube
540c041511dScube  assert(__glutMappedMenu == NULL);
541c041511dScube  grab = XGrabPointer(__glutDisplay, __glutRoot, True,
542c041511dScube    ButtonPressMask | ButtonReleaseMask,
543c041511dScube    GrabModeAsync, GrabModeAsync,
544c041511dScube    __glutRoot, menuCursor, CurrentTime);
545c041511dScube  if (grab != GrabSuccess) {
546c041511dScube    /* Somebody else has pointer grabbed, ignore menu
547c041511dScube       activation. */
548c041511dScube    return;
549c041511dScube  }
550c041511dScube  __glutMappedMenu = menu;
551c041511dScube  __glutMenuWindow = window;
552c041511dScube  __glutItemSelected = NULL;
553c041511dScube  if (__glutMenuStatusFunc) {
554c041511dScube    __glutSetMenu(menu);
555c041511dScube    __glutSetWindow(window);
556c041511dScube    __glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win);
557c041511dScube  }
558c041511dScube  mapMenu(menu, x, y);
559c041511dScube}
560c041511dScube
561c041511dScubestatic void
562c041511dScubepaintSubMenuArrow(Window win, int x, int y)
563c041511dScube{
564c041511dScube  XPoint p[5];
565c041511dScube
566c041511dScube  p[0].x = p[4].x = x;
567c041511dScube  p[0].y = p[4].y = y - menuFont->ascent + 1;
568c041511dScube  p[1].x = p[0].x + MENU_ARROW_WIDTH - 1;
569c041511dScube  p[1].y = p[0].y + (menuFont->ascent / 2) - 1;
570c041511dScube  p[2].x = p[1].x;
571c041511dScube  p[2].y = p[1].y + 1;
572c041511dScube  p[3].x = p[0].x;
573c041511dScube  p[3].y = p[0].y + menuFont->ascent - 2;
574c041511dScube  XFillPolygon(__glutDisplay, win,
575c041511dScube    whiteGC, p, 4, Convex, CoordModeOrigin);
576c041511dScube  XDrawLines(__glutDisplay, win, blackGC, p, 5, CoordModeOrigin);
577c041511dScube}
578c041511dScube
579c041511dScubestatic void
580c041511dScubepaintMenuItem(GLUTmenuItem * item, int num)
581c041511dScube{
582c041511dScube  Window win = item->menu->win;
583c041511dScube  GC gc;
584c041511dScube  int y;
585c041511dScube  int subMenuExtension;
586c041511dScube
587c041511dScube  if (item->menu->submenus > 0) {
588c041511dScube    subMenuExtension = MENU_ARROW_GAP + MENU_ARROW_WIDTH;
589c041511dScube  } else {
590c041511dScube    subMenuExtension = 0;
591c041511dScube  }
592c041511dScube  if (item->menu->highlighted == item) {
593c041511dScube    gc = whiteGC;
594c041511dScube  } else {
595c041511dScube    gc = grayGC;
596c041511dScube  }
597c041511dScube  y = MENU_GAP + fontHeight * num - menuFont->descent;
598c041511dScube  XFillRectangle(__glutDisplay, win, gc,
599c041511dScube    MENU_GAP, y - fontHeight + menuFont->descent,
600c041511dScube    item->menu->pixwidth + subMenuExtension, fontHeight);
601c041511dScube  XDrawString(__glutDisplay, win, blackGC,
602c041511dScube    MENU_GAP, y, item->label, item->len);
603c041511dScube  if (item->isTrigger) {
604c041511dScube    paintSubMenuArrow(win,
605c041511dScube      item->menu->pixwidth + MENU_ARROW_GAP + 1, y);
606c041511dScube  }
607c041511dScube}
608c041511dScube
609c041511dScubestatic void
610c041511dScubepaintMenu(GLUTmenu * menu)
611c041511dScube{
612c041511dScube  GLUTmenuItem *item;
613c041511dScube  int i = menu->num;
614c041511dScube  int y = MENU_GAP + fontHeight * i - menuFont->descent;
615c041511dScube
616c041511dScube  item = menu->list;
617c041511dScube  while (item) {
618c041511dScube    if (item->menu->highlighted == item) {
619c041511dScube      paintMenuItem(item, i);
620c041511dScube    } else {
621c041511dScube      /* Quick render of the menu item; assume background
622c041511dScube         already cleared to gray. */
623c041511dScube      XDrawString(__glutDisplay, menu->win, blackGC,
624c041511dScube        2, y, item->label, item->len);
625c041511dScube      if (item->isTrigger) {
626c041511dScube        paintSubMenuArrow(menu->win,
627c041511dScube          menu->pixwidth + MENU_ARROW_GAP + 1, y);
628c041511dScube      }
629c041511dScube    }
630c041511dScube    i--;
631c041511dScube    y -= fontHeight;
632c041511dScube    item = item->next;
633c041511dScube  }
634c041511dScube}
635c041511dScube
636c041511dScubestatic GLUTmenuItem *
637c041511dScubegetMenuItem(GLUTmenu * menu, Window win, int *which)
638c041511dScube{
639c041511dScube  GLUTmenuItem *item;
640c041511dScube  int i;
641c041511dScube
642c041511dScube  if (menu->searched) {
643c041511dScube    __glutFatalError("submenu infinite loop detected");
644c041511dScube  }
645c041511dScube  menu->searched = True;
646c041511dScube  i = menu->num;
647c041511dScube  item = menu->list;
648c041511dScube  while (item) {
649c041511dScube    if (item->win == win) {
650c041511dScube      *which = i;
651c041511dScube      menu->searched = False;
652c041511dScube      return item;
653c041511dScube    }
654c041511dScube    if (item->isTrigger) {
655c041511dScube      GLUTmenuItem *subitem;
656c041511dScube
657c041511dScube      subitem = __glutGetMenuItem(__glutMenuList[item->value],
658c041511dScube        win, which);
659c041511dScube      if (subitem) {
660c041511dScube        menu->searched = False;
661c041511dScube        return subitem;
662c041511dScube      }
663c041511dScube    }
664c041511dScube    i--;
665c041511dScube    item = item->next;
666c041511dScube  }
667c041511dScube  menu->searched = False;
668c041511dScube  return NULL;
669c041511dScube}
670c041511dScube
671c041511dScubestatic int
672c041511dScubegetMenuItemIndex(GLUTmenuItem * item)
673c041511dScube{
674c041511dScube  int count = 0;
675c041511dScube
676c041511dScube  while (item) {
677c041511dScube    count++;
678c041511dScube    item = item->next;
679c041511dScube  }
680c041511dScube  return count;
681c041511dScube}
682c041511dScube
683c041511dScubestatic GLUTmenu *
684c041511dScubegetMenu(Window win)
685c041511dScube{
686c041511dScube  GLUTmenu *menu;
687c041511dScube
688c041511dScube  menu = __glutMappedMenu;
689c041511dScube  while (menu) {
690c041511dScube    if (win == menu->win) {
691c041511dScube      return menu;
692c041511dScube    }
693c041511dScube    menu = menu->cascade;
694c041511dScube  }
695c041511dScube  return NULL;
696c041511dScube}
697c041511dScube
698c041511dScubestatic GLUTmenu *
699c041511dScubegetMenuByNum(int menunum)
700c041511dScube{
701c041511dScube  if (menunum < 1 || menunum > menuListSize) {
702c041511dScube    return NULL;
703c041511dScube  }
704c041511dScube  return __glutMenuList[menunum - 1];
705c041511dScube}
706c041511dScube
707c041511dScubestatic int
708c041511dScubegetUnusedMenuSlot(void)
709c041511dScube{
710c041511dScube  int i;
711c041511dScube
712c041511dScube  /* Look for allocated, unused slot. */
713c041511dScube  for (i = 0; i < menuListSize; i++) {
714c041511dScube    if (!__glutMenuList[i]) {
715c041511dScube      return i;
716c041511dScube    }
717c041511dScube  }
718c041511dScube  /* Allocate a new slot. */
719c041511dScube  menuListSize++;
720c041511dScube  if (__glutMenuList) {
721c041511dScube    __glutMenuList = (GLUTmenu **)
722c041511dScube      realloc(__glutMenuList, menuListSize * sizeof(GLUTmenu *));
723c041511dScube  } else {
724c041511dScube    /* XXX Some realloc's do not correctly perform a malloc
725c041511dScube       when asked to perform a realloc on a NULL pointer,
726c041511dScube       though the ANSI C library spec requires this. */
727c041511dScube    __glutMenuList = (GLUTmenu **) malloc(sizeof(GLUTmenu *));
728c041511dScube  }
729c041511dScube  if (!__glutMenuList) {
730c041511dScube    __glutFatalError("out of memory.");
731c041511dScube  }
732c041511dScube  __glutMenuList[menuListSize - 1] = NULL;
733c041511dScube  return menuListSize - 1;
734c041511dScube}
735c041511dScube
736c041511dScubevoid
737c041511dScube__glutMenuModificationError(void)
738c041511dScube{
739c041511dScube  /* XXX Remove the warning after GLUT 3.0. */
740c041511dScube  __glutWarning("The following is a new check for GLUT 3.0; update your code.");
741c041511dScube  __glutFatalError("menu manipulation not allowed while menus in use.");
742c041511dScube}
743c041511dScube
744c041511dScube
745c041511dScubestatic void
746c041511dScubemenuItemEnterOrLeave(GLUTmenuItem * item,
747c041511dScube  int num, int type)
748c041511dScube{
749c041511dScube  int alreadyUp = 0;
750c041511dScube
751c041511dScube  if (type == EnterNotify) {
752c041511dScube    GLUTmenuItem *prevItem = item->menu->highlighted;
753c041511dScube
754c041511dScube    if (prevItem && prevItem != item) {
755c041511dScube      /* If there's an already higlighted item in this menu
756c041511dScube         that is different from this one (we could be
757c041511dScube         re-entering an item with an already cascaded
758c041511dScube         submenu!), unhighlight the previous item. */
759c041511dScube      item->menu->highlighted = NULL;
760c041511dScube      paintMenuItem(prevItem, getMenuItemIndex(prevItem));
761c041511dScube    }
762c041511dScube    item->menu->highlighted = item;
763c041511dScube    __glutItemSelected = item;
764c041511dScube    if (item->menu->cascade) {
765c041511dScube      if (!item->isTrigger) {
766c041511dScube        /* Entered a menu item that is not a submenu trigger,
767c041511dScube           so pop down the current submenu cascade of this
768c041511dScube           menu.  */
769c041511dScube        unmapMenu(item->menu->cascade);
770c041511dScube        item->menu->cascade = NULL;
771c041511dScube      } else {
772c041511dScube        GLUTmenu *submenu = __glutMenuList[item->value];
773c041511dScube
774c041511dScube        if (submenu->anchor == item) {
775c041511dScube          /* We entered the submenu trigger for the submenu
776c041511dScube             that is already up, so don't take down the
777c041511dScube             submenu.  */
778c041511dScube          alreadyUp = 1;
779c041511dScube        } else {
780c041511dScube          /* Submenu already popped up for some other submenu
781c041511dScube             item of this menu; need to pop down that other
782c041511dScube             submenu cascade.  */
783c041511dScube          unmapMenu(item->menu->cascade);
784c041511dScube          item->menu->cascade = NULL;
785c041511dScube        }
786c041511dScube      }
787c041511dScube    }
788c041511dScube    if (!alreadyUp) {
789c041511dScube      /* Make sure the menu item gets painted with
790c041511dScube         highlighting. */
791c041511dScube      paintMenuItem(item, num);
792c041511dScube    } else {
793c041511dScube      /* If already up, should already be highlighted.  */
794c041511dScube    }
795c041511dScube  } else {
796c041511dScube    /* LeaveNotify: Handle leaving a menu item...  */
797c041511dScube    if (item->menu->cascade &&
798c041511dScube      item->menu->cascade->anchor == item) {
799c041511dScube      /* If there is a submenu casacaded from this item, do not
800c041511dScube         change the highlighting on this item upon leaving. */
801c041511dScube    } else {
802c041511dScube      /* Unhighlight this menu item.  */
803c041511dScube      item->menu->highlighted = NULL;
804c041511dScube      paintMenuItem(item, num);
805c041511dScube    }
806c041511dScube    __glutItemSelected = NULL;
807c041511dScube  }
808c041511dScube  if (item->isTrigger) {
809c041511dScube    if (type == EnterNotify && !alreadyUp) {
810c041511dScube      GLUTmenu *submenu = __glutMenuList[item->value];
811c041511dScube
812c041511dScube      mapMenu(submenu,
813c041511dScube        item->menu->x + item->menu->pixwidth +
814c041511dScube        MENU_ARROW_GAP + MENU_ARROW_WIDTH +
815c041511dScube        MENU_GAP + MENU_BORDER,
816c041511dScube        item->menu->y + fontHeight * (num - 1) + MENU_GAP);
817c041511dScube      item->menu->cascade = submenu;
818c041511dScube      submenu->anchor = item;
819c041511dScube    }
820c041511dScube  }
821c041511dScube}
822c041511dScube
823c041511dScube/* Installs callback functions for use by glut_event.c  The point
824c041511dScube   of this is so that GLUT's menu code only gets linked into
825c041511dScube   GLUT binaries (assuming a static library) if the GLUT menu
826c041511dScube   API is used. */
827c041511dScubestatic void
828c041511dScubeinstallMenuCallbacks(void)
829c041511dScube{
830c041511dScube  __glutMenuItemEnterOrLeave = menuItemEnterOrLeave;
831c041511dScube  __glutFinishMenu = finishMenu;
832c041511dScube  __glutPaintMenu = paintMenu;
833c041511dScube  __glutStartMenu = startMenu;
834c041511dScube  __glutGetMenuByNum = getMenuByNum;
835c041511dScube  __glutGetMenu = getMenu;
836c041511dScube  __glutGetMenuItem = getMenuItem;
837c041511dScube}
838c041511dScube
839c041511dScubeint GLUTAPIENTRY
840c041511dScubeglutCreateMenu(GLUTselectCB selectFunc)
841c041511dScube{
842c041511dScube  XSetWindowAttributes wa;
843c041511dScube  GLUTmenu *menu;
844c041511dScube  int menuid;
845c041511dScube
846c041511dScube  if (__glutMappedMenu) {
847c041511dScube    __glutMenuModificationError();
848c041511dScube  }
849c041511dScube  if (!__glutDisplay) {
850c041511dScube    __glutOpenXConnection(NULL);
851c041511dScube  }
852c041511dScube
853c041511dScube  installMenuCallbacks();
854c041511dScube
855c041511dScube  menuid = getUnusedMenuSlot();
856c041511dScube  menu = (GLUTmenu *) malloc(sizeof(GLUTmenu));
857c041511dScube  if (!menu) {
858c041511dScube    __glutFatalError("out of memory.");
859c041511dScube  }
860c041511dScube  menu->id = menuid;
861c041511dScube  menu->num = 0;
862c041511dScube  menu->submenus = 0;
863c041511dScube  menu->managed = False;
864c041511dScube  menu->searched = False;
865c041511dScube  menu->pixwidth = 0;
866c041511dScube  menu->select = selectFunc;
867c041511dScube  menu->list = NULL;
868c041511dScube  menu->cascade = NULL;
869c041511dScube  menu->highlighted = NULL;
870c041511dScube  menu->anchor = NULL;
871c041511dScube  menuSetup();
872c041511dScube  wa.override_redirect = True;
873c041511dScube  wa.background_pixel = menuGray;
874c041511dScube  wa.border_pixel = menuBlack;
875c041511dScube  wa.colormap = menuColormap;
876c041511dScube  wa.event_mask = StructureNotifyMask | ExposureMask |
877c041511dScube    ButtonPressMask | ButtonReleaseMask |
878c041511dScube    EnterWindowMask | LeaveWindowMask;
879c041511dScube  /* Save unders really only enabled if useSaveUnders is set to
880c041511dScube     CWSaveUnder, ie. using Mesa 3D.  See earlier comments. */
881c041511dScube  wa.save_under = True;
882c041511dScube  menu->win = XCreateWindow(__glutDisplay, __glutRoot,
883c041511dScube  /* Real position determined when mapped. */
884c041511dScube    0, 0,
885c041511dScube  /* Real size will be determined when menu is manged. */
886c041511dScube    1, 1,
887c041511dScube    MENU_BORDER, menuDepth, InputOutput, menuVisual,
888c041511dScube    CWOverrideRedirect | CWBackPixel | CWBorderPixel |
889c041511dScube    CWEventMask | CWColormap | useSaveUnders,
890c041511dScube    &wa);
891c041511dScube  menuGraphicsContextSetup(menu->win);
892c041511dScube  __glutMenuList[menuid] = menu;
893c041511dScube  __glutSetMenu(menu);
894c041511dScube  return menuid + 1;
895c041511dScube}
896c041511dScube
897c041511dScube/* CENTRY */
898c041511dScubeint GLUTAPIENTRY
899c041511dScubeglutGetMenu(void)
900c041511dScube{
901c041511dScube  if (__glutCurrentMenu) {
902c041511dScube    return __glutCurrentMenu->id + 1;
903c041511dScube  } else {
904c041511dScube    return 0;
905c041511dScube  }
906c041511dScube}
907c041511dScube
908c041511dScubevoid GLUTAPIENTRY
909c041511dScubeglutSetMenu(int menuid)
910c041511dScube{
911c041511dScube  GLUTmenu *menu;
912c041511dScube
913c041511dScube  if (menuid < 1 || menuid > menuListSize) {
914c041511dScube    __glutWarning("glutSetMenu attempted on bogus menu.");
915c041511dScube    return;
916c041511dScube  }
917c041511dScube  menu = __glutMenuList[menuid - 1];
918c041511dScube  if (!menu) {
919c041511dScube    __glutWarning("glutSetMenu attempted on bogus menu.");
920c041511dScube    return;
921c041511dScube  }
922c041511dScube  __glutSetMenu(menu);
923c041511dScube}
924c041511dScube/* ENDCENTRY */
925c041511dScube
926c041511dScubevoid
927c041511dScube__glutSetMenuItem(GLUTmenuItem * item, const char *label,
928c041511dScube  int value, Bool isTrigger)
929c041511dScube{
930c041511dScube  GLUTmenu *menu;
931c041511dScube
932c041511dScube  menu = item->menu;
933c041511dScube  item->label = __glutStrdup(label);
934c041511dScube  if (!item->label) {
935c041511dScube    __glutFatalError("out of memory.");
936c041511dScube  }
937c041511dScube  item->isTrigger = isTrigger;
938c041511dScube  item->len = (int) strlen(label);
939c041511dScube  item->value = value;
940c041511dScube  item->pixwidth = XTextWidth(menuFont, label, item->len) + 4;
941c041511dScube  if (item->pixwidth > menu->pixwidth) {
942c041511dScube    menu->pixwidth = item->pixwidth;
943c041511dScube  }
944c041511dScube  menu->managed = False;
945c041511dScube}
946c041511dScube
947c041511dScube/* CENTRY */
948c041511dScubevoid GLUTAPIENTRY
949c041511dScubeglutAddMenuEntry(const char *label, int value)
950c041511dScube{
951c041511dScube  XSetWindowAttributes wa;
952c041511dScube  GLUTmenuItem *entry;
953c041511dScube
954c041511dScube  if (__glutMappedMenu) {
955c041511dScube    __glutMenuModificationError();
956c041511dScube  }
957c041511dScube  entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
958c041511dScube  if (!entry) {
959c041511dScube    __glutFatalError("out of memory.");
960c041511dScube  }
961c041511dScube  entry->menu = __glutCurrentMenu;
962c041511dScube  __glutSetMenuItem(entry, label, value, False);
963c041511dScube  wa.event_mask = EnterWindowMask | LeaveWindowMask;
964c041511dScube  entry->win = XCreateWindow(__glutDisplay,
965c041511dScube    __glutCurrentMenu->win, MENU_GAP,
966c041511dScube    __glutCurrentMenu->num * fontHeight + MENU_GAP,  /* x & y */
967c041511dScube    entry->pixwidth, fontHeight,  /* width & height */
968c041511dScube    0, CopyFromParent, InputOnly, CopyFromParent,
969c041511dScube    CWEventMask, &wa);
970c041511dScube  XMapWindow(__glutDisplay, entry->win);
971c041511dScube  __glutCurrentMenu->num++;
972c041511dScube  entry->next = __glutCurrentMenu->list;
973c041511dScube  __glutCurrentMenu->list = entry;
974c041511dScube}
975c041511dScube
976c041511dScubevoid GLUTAPIENTRY
977c041511dScubeglutAddSubMenu(const char *label, int menu)
978c041511dScube{
979c041511dScube  XSetWindowAttributes wa;
980c041511dScube  GLUTmenuItem *submenu;
981c041511dScube
982c041511dScube  if (__glutMappedMenu) {
983c041511dScube    __glutMenuModificationError();
984c041511dScube  }
985c041511dScube  submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
986c041511dScube  if (!submenu) {
987c041511dScube    __glutFatalError("out of memory.");
988c041511dScube  }
989c041511dScube  __glutCurrentMenu->submenus++;
990c041511dScube  submenu->menu = __glutCurrentMenu;
991c041511dScube  __glutSetMenuItem(submenu, label, /* base 0 */ menu - 1, True);
992c041511dScube  wa.event_mask = EnterWindowMask | LeaveWindowMask;
993c041511dScube  submenu->win = XCreateWindow(__glutDisplay,
994c041511dScube    __glutCurrentMenu->win, MENU_GAP,
995c041511dScube    __glutCurrentMenu->num * fontHeight + MENU_GAP,  /* x & y */
996c041511dScube    submenu->pixwidth, fontHeight,  /* width & height */
997c041511dScube    0, CopyFromParent, InputOnly, CopyFromParent,
998c041511dScube    CWEventMask, &wa);
999c041511dScube  XMapWindow(__glutDisplay, submenu->win);
1000c041511dScube  __glutCurrentMenu->num++;
1001c041511dScube  submenu->next = __glutCurrentMenu->list;
1002c041511dScube  __glutCurrentMenu->list = submenu;
1003c041511dScube}
1004c041511dScube
1005c041511dScubevoid GLUTAPIENTRY
1006c041511dScubeglutAttachMenu(int button)
1007c041511dScube{
1008c041511dScube  /* if button >= GLUT_MAX_MENUS, we'll go out of array bounds below */
1009c041511dScube  if (button >= GLUT_MAX_MENUS) {
1010c041511dScube    return;
1011c041511dScube  }
1012c041511dScube  if (__glutMappedMenu) {
1013c041511dScube    __glutMenuModificationError();
1014c041511dScube  }
1015c041511dScube  installMenuCallbacks();
1016c041511dScube  if (__glutCurrentWindow->menu[button] < 1) {
1017c041511dScube    __glutCurrentWindow->buttonUses++;
1018c041511dScube  }
1019c041511dScube  __glutChangeWindowEventMask(
1020c041511dScube    ButtonPressMask | ButtonReleaseMask, True);
1021c041511dScube  __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
1022c041511dScube}
1023c041511dScube/* ENDCENTRY */
1024