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