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