2 /* Copyright (c) Mark J. Kilgard, 1994, 1997. */
4 /* This program is freely distributable without licensing fees
5 and is provided without guarantee or warrantee expressed or
6 implied. This program is -not- in the public domain. */
8 /* The Win32 GLUT file win32_menu.c completely re-implements all
9 the menuing functionality implemented. This file is used only by
10 the X Window System version of GLUT. */
13 #include <GL/vms_x_fix.h>
24 #include <X11/cursorfont.h> /* for XC_arrow */
27 #include "layerutil.h"
29 void (CDECL
*__glutMenuStatusFunc
) (int, int, int);
30 GLUTmenuItem
*__glutItemSelected
;
31 GLUTmenu
**__glutMenuList
= NULL
;
33 static int menuListSize
= 0;
34 static XFontStruct
*menuFont
= NULL
;
35 static Cursor menuCursor
;
36 static Colormap menuColormap
;
37 static Visual
*menuVisual
;
39 static int fontHeight
;
40 static GC blackGC
, grayGC
, whiteGC
;
41 static unsigned long menuBlack
, menuWhite
, menuGray
;
42 static unsigned long useSaveUnders
;
44 /* A replacement for XAllocColor (originally by Brian Paul).
45 This function should never fail to allocate a color. When
46 XAllocColor fails, we return the nearest matching color. If
47 we have to allocate many colors this function isn't a great
48 solution; the XQueryColors() could be done just once. */
50 noFaultXAllocColor(Display
* dpy
, Colormap cmap
, int cmapSize
,
53 XColor
*ctable
, subColor
;
55 double mindist
; /* 3*2^16^2 exceeds 32-bit long int
59 /* First try just using XAllocColor. */
60 if (XAllocColor(dpy
, cmap
, color
)) {
64 /* Retrieve color table entries. */
65 /* XXX alloca canidate. */
66 ctable
= (XColor
*) malloc(cmapSize
* sizeof(XColor
));
67 for (i
= 0; i
< cmapSize
; i
++)
69 XQueryColors(dpy
, cmap
, ctable
, cmapSize
);
71 /* Find best match. */
74 for (i
= 0; i
< cmapSize
; i
++) {
75 double dr
= (double) color
->red
- (double) ctable
[i
].red
;
76 double dg
= (double) color
->green
- (double) ctable
[i
].green
;
77 double db
= (double) color
->blue
- (double) ctable
[i
].blue
;
78 double dist
= dr
* dr
+ dg
* dg
+ db
* db
;
79 if (bestmatch
< 0 || dist
< mindist
) {
86 subColor
.red
= ctable
[bestmatch
].red
;
87 subColor
.green
= ctable
[bestmatch
].green
;
88 subColor
.blue
= ctable
[bestmatch
].blue
;
90 if (XAllocColor(dpy
, cmap
, &subColor
)) {
94 /* Extremely unlikely, but possibly color was deallocated
95 and reallocated by someone else before we could
96 XAllocColor the color cell we located. If so, loop
104 char *xvendor
, *glvendor
, *renderer
;
105 int isSunCreator
= 0; /* Until proven that it is. */
106 int savedDisplayMode
= 0;
107 char *savedDisplayString
= 0;
110 #define VENDOR_SUN "Sun Microsystems"
111 #define RENDERER_CREATOR "Creator"
113 /* Check the X vendor string first. It is easier to check
114 than the OpenGL vendor and renderer strings since it
115 doesn't require a valid OpenGL rendering context. Bail
116 early if not connected to a Sun. */
117 xvendor
= ServerVendor(__glutDisplay
);
118 if (!strncmp(xvendor
, VENDOR_SUN
, sizeof(VENDOR_SUN
) - 1)) {
120 /* We need a valid current OpenGL rendering context to be
121 able to call glGetString successfully. If there is not
122 a current window, set up a temporary one just to call
123 glGetString with (gag, expensive). */
124 if (__glutCurrentWindow
) {
127 savedDisplayMode
= __glutDisplayMode
;
128 savedDisplayString
= __glutDisplayString
;
129 __glutDisplayMode
= GLUT_RGB
| GLUT_SINGLE
;
130 __glutDisplayString
= NULL
;
131 window
= __glutCreateWindow(NULL
, 0, 0, 1, 1, 0);
134 glvendor
= (char *) glGetString(GL_VENDOR
);
135 if (!strncmp(glvendor
, VENDOR_SUN
, sizeof(VENDOR_SUN
) - 1)) {
136 renderer
= (char *) glGetString(GL_RENDERER
);
137 if (!strncmp(renderer
, RENDERER_CREATOR
, sizeof(RENDERER_CREATOR
) - 1)) {
141 /* Destroy the temporary window for glGetString if one
142 needed to be created. */
144 __glutDestroyWindow(window
, window
);
145 __glutDisplayMode
= savedDisplayMode
;
146 __glutDisplayString
= savedDisplayString
;
153 menuVisualSetup(void)
155 XLayerVisualInfo
template, *visual
, *overlayVisuals
;
159 int layer
, nVisuals
, i
, dummy
;
160 unsigned long *placeHolders
= NULL
;
161 int numPlaceHolders
= 0;
164 allocateHigh
= ifSunCreator();
166 /* Start with the highest overlay layer and work down. I
167 don't think any hardware has more than 3 overlay layers. */
168 for (layer
= 3; layer
> 0; layer
--) {
169 template.layer
= layer
;
170 template.vinfo
.screen
= __glutScreen
;
171 overlayVisuals
= __glutXGetLayerVisualInfo(__glutDisplay
,
172 VisualScreenMask
| VisualLayerMask
, &template, &nVisuals
);
173 if (overlayVisuals
) {
174 /* First, check if the default visual is in this layer.
175 If the default visual is in this layer, we try to use
176 it since it has pre-defined black and white pixels and
178 using the default visual will probably minimize
179 colormap flashing problems. Suggested by Thomas Roell
181 for (i
= 0; i
< nVisuals
; i
++) {
182 visual
= &overlayVisuals
[i
];
183 if (visual
->vinfo
.colormap_size
>= 3) {
184 /* Compare visual IDs just to be safe. */
185 if (visual
->vinfo
.visual
->visualid
== DefaultVisual(__glutDisplay
, __glutScreen
)->visualid
) {
186 /* Settle for default visual. */
187 menuVisual
= DefaultVisual(__glutDisplay
, __glutScreen
);
188 menuDepth
= DefaultDepth(__glutDisplay
, __glutScreen
);
189 menuColormap
= DefaultColormap(__glutDisplay
, __glutScreen
);
190 menuBlack
= BlackPixel(__glutDisplay
, __glutScreen
);
191 menuWhite
= WhitePixel(__glutDisplay
, __glutScreen
);
192 color
.red
= color
.green
= color
.blue
= 0xaa00;
193 noFaultXAllocColor(__glutDisplay
, menuColormap
,
194 menuVisual
->map_entries
, &color
);
195 menuGray
= color
.pixel
;
197 XFree(overlayVisuals
);
202 for (i
= 0; i
< nVisuals
; i
++) {
203 visual
= &overlayVisuals
[i
];
204 if (visual
->vinfo
.colormap_size
>= 3) {
206 /* For Sun's Creator graphics, try to force the
207 read-only colors to the high end of the colormap
208 by first allocating read-write place-holder cells
209 for all but the last three cells. This helps
210 avoid colormap flashing problems. */
211 numPlaceHolders
= visual
->vinfo
.colormap_size
- 3;
212 if (numPlaceHolders
> 0) {
213 placeHolders
= (unsigned long *)
214 malloc(numPlaceHolders
* sizeof(unsigned long));
215 /* A malloc failure would be harmless. */
218 menuColormap
= XCreateColormap(__glutDisplay
, __glutRoot
,
219 visual
->vinfo
.visual
, AllocNone
);
221 /* Again for Sun's Creator graphics, do the actual
222 read-write place-holder cell allocation. */
223 status
= XAllocColorCells(__glutDisplay
, menuColormap
, False
, 0, 0,
224 placeHolders
, numPlaceHolders
);
226 XFreeColormap(__glutDisplay
, menuColormap
);
232 /* Allocate overlay colormap cells in defined order:
233 gray, black, white to match the IRIS GL allocation
234 scheme. Increases likelihood of less overlay
235 colormap flashing. */
236 /* XXX Nice if these 3 AllocColor's could be done in
237 one protocol round-trip. */
238 color
.red
= color
.green
= color
.blue
= 0xaa00;
239 status
= XAllocColor(__glutDisplay
,
240 menuColormap
, &color
);
242 XFreeColormap(__glutDisplay
, menuColormap
);
249 menuGray
= color
.pixel
;
250 color
.red
= color
.green
= color
.blue
= 0x0000;
251 status
= XAllocColor(__glutDisplay
,
252 menuColormap
, &color
);
254 XFreeColormap(__glutDisplay
, menuColormap
);
261 menuBlack
= color
.pixel
;
262 color
.red
= color
.green
= color
.blue
= 0xffff;
263 status
= XAllocColor(__glutDisplay
,
264 menuColormap
, &color
);
266 XFreeColormap(__glutDisplay
, menuColormap
);
274 /* Now free the placeholder cells. */
275 XFreeColors(__glutDisplay
, menuColormap
,
276 placeHolders
, numPlaceHolders
, 0);
280 menuWhite
= color
.pixel
;
281 menuVisual
= visual
->vinfo
.visual
;
282 menuDepth
= visual
->vinfo
.depth
;
283 /* If using overlays, do not request "save unders". */
285 XFree(overlayVisuals
);
289 XFree(overlayVisuals
);
292 /* Settle for default visual. */
293 menuVisual
= DefaultVisual(__glutDisplay
, __glutScreen
);
294 menuDepth
= DefaultDepth(__glutDisplay
, __glutScreen
);
295 menuColormap
= DefaultColormap(__glutDisplay
, __glutScreen
);
296 menuBlack
= BlackPixel(__glutDisplay
, __glutScreen
);
297 menuWhite
= WhitePixel(__glutDisplay
, __glutScreen
);
298 color
.red
= color
.green
= color
.blue
= 0xaa00;
299 noFaultXAllocColor(__glutDisplay
, menuColormap
,
300 menuVisual
->map_entries
, &color
);
301 menuGray
= color
.pixel
;
303 /* When no overlays are supported, we would like to use X
304 "save unders" to avoid exposes to windows obscured by
305 pop-up menus. However, OpenGL's direct rendering support
306 means OpenGL interacts poorly with X backing store and
307 save unders. X servers do not (in implementation
308 practice) redirect OpenGL rendering destined to obscured
309 window regions into backing store.
311 Implementation solutions exist for this problem, but they
312 are expensive and high-end OpenGL implementations
313 typically provide fast rendering and/or overlays to
314 obviate the problem associated of user interfaces (pop-up
315 menus) forcing redraws of complex normal plane scenes.
316 (See support for overlays pop-up menus above.)
318 Mesa 3D, however, does not support direct rendering.
319 Overlays are often unavailable to Mesa, and Mesa is also
320 relatively slow. For these reasons, Mesa-rendering GLUT
321 programs can and should use X save unders.
323 Look for the GLX extension. If _not_ supported, we are
324 presumably using Mesa so enable save unders. */
326 presumablyMesa
= !XQueryExtension(__glutDisplay
, "GLX",
327 &dummy
, &dummy
, &dummy
);
329 if (presumablyMesa
) {
330 useSaveUnders
= CWSaveUnder
;
340 /* MenuFont overload to indicate menu initalization. */
343 menuFont
= XLoadQueryFont(__glutDisplay
,
344 "-*-helvetica-bold-o-normal--14-*-*-*-p-*-iso8859-1");
346 /* Try back up font. */
347 menuFont
= XLoadQueryFont(__glutDisplay
, "fixed");
350 __glutFatalError("could not load font.");
353 fontHeight
= menuFont
->ascent
+ menuFont
->descent
;
354 menuCursor
= XCreateFontCursor(__glutDisplay
, XC_arrow
);
358 menuGraphicsContextSetup(Window win
)
362 if (blackGC
!= None
) {
365 gcvals
.font
= menuFont
->fid
;
366 gcvals
.foreground
= menuBlack
;
367 blackGC
= XCreateGC(__glutDisplay
, win
,
368 GCFont
| GCForeground
, &gcvals
);
369 gcvals
.foreground
= menuGray
;
370 grayGC
= XCreateGC(__glutDisplay
, win
, GCForeground
, &gcvals
);
371 gcvals
.foreground
= menuWhite
;
372 whiteGC
= XCreateGC(__glutDisplay
, win
, GCForeground
, &gcvals
);
376 __glutSetMenu(GLUTmenu
* menu
)
378 __glutCurrentMenu
= menu
;
382 unmapMenu(GLUTmenu
* menu
)
385 unmapMenu(menu
->cascade
);
386 menu
->cascade
= NULL
;
389 menu
->highlighted
= NULL
;
390 XUnmapWindow(__glutDisplay
, menu
->win
);
394 finishMenu(Window win
, int x
, int y
)
399 unmapMenu(__glutMappedMenu
);
400 XUngrabPointer(__glutDisplay
, CurrentTime
);
402 /* Popping up an overlay popup menu will install its own
403 colormap. If the window associated with the menu has an
404 overlay, install that window's overlay colormap so the
405 overlay isn't left using the popup menu's colormap. */
406 if (__glutMenuWindow
->overlay
) {
407 XInstallColormap(__glutDisplay
,
408 __glutMenuWindow
->overlay
->colormap
->cmap
);
411 /* This XFlush is needed to to make sure the pointer is
412 really ungrabbed when the application's menu callback is
413 called. Otherwise, a deadlock might happen because the
414 application may try to read from an terminal window, but
415 yet the ungrab hasn't really happened since it hasn't been
417 XFlush(__glutDisplay
);
419 if (__glutMenuStatusFunc
) {
420 if (win
!= __glutMenuWindow
->win
) {
421 /* The button release may have occurred in a window other
422 than the window requesting the pop-up menu (for
423 example, one of the submenu windows). In this case, we
424 need to translate the coordinates into the coordinate
425 system of the window associated with the window. */
426 rc
= XTranslateCoordinates(__glutDisplay
, win
, __glutMenuWindow
->win
,
427 x
, y
, &x
, &y
, &dummy
);
428 assert(rc
!= False
); /* Will always be on same screen. */
430 __glutSetWindow(__glutMenuWindow
);
431 __glutSetMenu(__glutMappedMenu
);
433 /* Setting __glutMappedMenu to NULL permits operations that
434 change menus or destroy the menu window again. */
435 __glutMappedMenu
= NULL
;
437 __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE
, x
, y
);
439 /* Setting __glutMappedMenu to NULL permits operations that
440 change menus or destroy the menu window again. */
441 __glutMappedMenu
= NULL
;
443 /* If an item is selected and it is not a submenu trigger,
444 generate menu callback. */
445 if (__glutItemSelected
&& !__glutItemSelected
->isTrigger
) {
446 __glutSetWindow(__glutMenuWindow
);
447 /* When menu callback is triggered, current menu should be
448 set to the callback menu. */
449 __glutSetMenu(__glutItemSelected
->menu
);
450 __glutItemSelected
->menu
->select(
451 __glutItemSelected
->value
);
453 __glutMenuWindow
= NULL
;
456 #define MENU_BORDER 1
458 #define MENU_ARROW_GAP 6
459 #define MENU_ARROW_WIDTH 8
462 mapMenu(GLUTmenu
* menu
, int x
, int y
)
464 XWindowChanges changes
;
466 int subMenuExtension
, num
;
468 /* If there are submenus, we need to provide extra space for
469 the submenu pull arrow. */
470 if (menu
->submenus
> 0) {
471 subMenuExtension
= MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
;
473 subMenuExtension
= 0;
476 changes
.stack_mode
= Above
;
477 mask
= CWStackMode
| CWX
| CWY
;
478 /* If the menu isn't managed (ie, validated so all the
479 InputOnly subwindows are the right size), do so. */
480 if (!menu
->managed
) {
486 XWindowChanges itemupdate
;
488 itemupdate
.y
= (num
- 1) * fontHeight
+ MENU_GAP
;
489 itemupdate
.width
= menu
->pixwidth
;
490 itemupdate
.width
+= subMenuExtension
;
491 XConfigureWindow(__glutDisplay
, item
->win
,
492 CWWidth
| CWY
, &itemupdate
);
496 menu
->pixheight
= MENU_GAP
+
497 fontHeight
* menu
->num
+ MENU_GAP
;
498 changes
.height
= menu
->pixheight
;
499 changes
.width
= MENU_GAP
+
500 menu
->pixwidth
+ subMenuExtension
+ MENU_GAP
;
501 mask
|= CWWidth
| CWHeight
;
502 menu
->managed
= True
;
504 /* Make sure menu appears fully on screen. */
505 if (y
+ menu
->pixheight
>= __glutScreenHeight
) {
506 changes
.y
= __glutScreenHeight
- menu
->pixheight
;
510 if (x
+ menu
->pixwidth
+ subMenuExtension
>=
512 changes
.x
= __glutScreenWidth
-
513 menu
->pixwidth
+ subMenuExtension
;
518 /* Rember where the menu is placed so submenus can be
519 properly placed relative to it. */
523 XConfigureWindow(__glutDisplay
, menu
->win
, mask
, &changes
);
524 XInstallColormap(__glutDisplay
, menuColormap
);
525 /* XXX The XRaiseWindow below should not be necessary because
526 the XConfigureWindow requests an Above stack mode (same as
527 XRaiseWindow), but some Sun users complained this was still
528 necessary. Probably some window manager or X server bug on
530 XRaiseWindow(__glutDisplay
, menu
->win
);
531 XMapWindow(__glutDisplay
, menu
->win
);
535 startMenu(GLUTmenu
* menu
, GLUTwindow
* window
,
536 int x
, int y
, int x_win
, int y_win
)
540 assert(__glutMappedMenu
== NULL
);
541 grab
= XGrabPointer(__glutDisplay
, __glutRoot
, True
,
542 ButtonPressMask
| ButtonReleaseMask
,
543 GrabModeAsync
, GrabModeAsync
,
544 __glutRoot
, menuCursor
, CurrentTime
);
545 if (grab
!= GrabSuccess
) {
546 /* Somebody else has pointer grabbed, ignore menu
550 __glutMappedMenu
= menu
;
551 __glutMenuWindow
= window
;
552 __glutItemSelected
= NULL
;
553 if (__glutMenuStatusFunc
) {
555 __glutSetWindow(window
);
556 __glutMenuStatusFunc(GLUT_MENU_IN_USE
, x_win
, y_win
);
562 paintSubMenuArrow(Window win
, int x
, int y
)
567 p
[0].y
= p
[4].y
= y
- menuFont
->ascent
+ 1;
568 p
[1].x
= p
[0].x
+ MENU_ARROW_WIDTH
- 1;
569 p
[1].y
= p
[0].y
+ (menuFont
->ascent
/ 2) - 1;
573 p
[3].y
= p
[0].y
+ menuFont
->ascent
- 2;
574 XFillPolygon(__glutDisplay
, win
,
575 whiteGC
, p
, 4, Convex
, CoordModeOrigin
);
576 XDrawLines(__glutDisplay
, win
, blackGC
, p
, 5, CoordModeOrigin
);
580 paintMenuItem(GLUTmenuItem
* item
, int num
)
582 Window win
= item
->menu
->win
;
585 int subMenuExtension
;
587 if (item
->menu
->submenus
> 0) {
588 subMenuExtension
= MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
;
590 subMenuExtension
= 0;
592 if (item
->menu
->highlighted
== item
) {
597 y
= MENU_GAP
+ fontHeight
* num
- menuFont
->descent
;
598 XFillRectangle(__glutDisplay
, win
, gc
,
599 MENU_GAP
, y
- fontHeight
+ menuFont
->descent
,
600 item
->menu
->pixwidth
+ subMenuExtension
, fontHeight
);
601 XDrawString(__glutDisplay
, win
, blackGC
,
602 MENU_GAP
, y
, item
->label
, item
->len
);
603 if (item
->isTrigger
) {
604 paintSubMenuArrow(win
,
605 item
->menu
->pixwidth
+ MENU_ARROW_GAP
+ 1, y
);
610 paintMenu(GLUTmenu
* menu
)
614 int y
= MENU_GAP
+ fontHeight
* i
- menuFont
->descent
;
618 if (item
->menu
->highlighted
== item
) {
619 paintMenuItem(item
, i
);
621 /* Quick render of the menu item; assume background
622 already cleared to gray. */
623 XDrawString(__glutDisplay
, menu
->win
, blackGC
,
624 2, y
, item
->label
, item
->len
);
625 if (item
->isTrigger
) {
626 paintSubMenuArrow(menu
->win
,
627 menu
->pixwidth
+ MENU_ARROW_GAP
+ 1, y
);
636 static GLUTmenuItem
*
637 getMenuItem(GLUTmenu
* menu
, Window win
, int *which
)
642 if (menu
->searched
) {
643 __glutFatalError("submenu infinite loop detected");
645 menu
->searched
= True
;
649 if (item
->win
== win
) {
651 menu
->searched
= False
;
654 if (item
->isTrigger
) {
655 GLUTmenuItem
*subitem
;
657 subitem
= __glutGetMenuItem(__glutMenuList
[item
->value
],
660 menu
->searched
= False
;
667 menu
->searched
= False
;
672 getMenuItemIndex(GLUTmenuItem
* item
)
688 menu
= __glutMappedMenu
;
690 if (win
== menu
->win
) {
693 menu
= menu
->cascade
;
699 getMenuByNum(int menunum
)
701 if (menunum
< 1 || menunum
> menuListSize
) {
704 return __glutMenuList
[menunum
- 1];
708 getUnusedMenuSlot(void)
712 /* Look for allocated, unused slot. */
713 for (i
= 0; i
< menuListSize
; i
++) {
714 if (!__glutMenuList
[i
]) {
718 /* Allocate a new slot. */
720 if (__glutMenuList
) {
721 __glutMenuList
= (GLUTmenu
**)
722 realloc(__glutMenuList
, menuListSize
* sizeof(GLUTmenu
*));
724 /* XXX Some realloc's do not correctly perform a malloc
725 when asked to perform a realloc on a NULL pointer,
726 though the ANSI C library spec requires this. */
727 __glutMenuList
= (GLUTmenu
**) malloc(sizeof(GLUTmenu
*));
729 if (!__glutMenuList
) {
730 __glutFatalError("out of memory.");
732 __glutMenuList
[menuListSize
- 1] = NULL
;
733 return menuListSize
- 1;
737 __glutMenuModificationError(void)
739 /* XXX Remove the warning after GLUT 3.0. */
740 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
741 __glutFatalError("menu manipulation not allowed while menus in use.");
746 menuItemEnterOrLeave(GLUTmenuItem
* item
,
751 if (type
== EnterNotify
) {
752 GLUTmenuItem
*prevItem
= item
->menu
->highlighted
;
754 if (prevItem
&& prevItem
!= item
) {
755 /* If there's an already higlighted item in this menu
756 that is different from this one (we could be
757 re-entering an item with an already cascaded
758 submenu!), unhighlight the previous item. */
759 item
->menu
->highlighted
= NULL
;
760 paintMenuItem(prevItem
, getMenuItemIndex(prevItem
));
762 item
->menu
->highlighted
= item
;
763 __glutItemSelected
= item
;
764 if (item
->menu
->cascade
) {
765 if (!item
->isTrigger
) {
766 /* Entered a menu item that is not a submenu trigger,
767 so pop down the current submenu cascade of this
769 unmapMenu(item
->menu
->cascade
);
770 item
->menu
->cascade
= NULL
;
772 GLUTmenu
*submenu
= __glutMenuList
[item
->value
];
774 if (submenu
->anchor
== item
) {
775 /* We entered the submenu trigger for the submenu
776 that is already up, so don't take down the
780 /* Submenu already popped up for some other submenu
781 item of this menu; need to pop down that other
783 unmapMenu(item
->menu
->cascade
);
784 item
->menu
->cascade
= NULL
;
789 /* Make sure the menu item gets painted with
791 paintMenuItem(item
, num
);
793 /* If already up, should already be highlighted. */
796 /* LeaveNotify: Handle leaving a menu item... */
797 if (item
->menu
->cascade
&&
798 item
->menu
->cascade
->anchor
== item
) {
799 /* If there is a submenu casacaded from this item, do not
800 change the highlighting on this item upon leaving. */
802 /* Unhighlight this menu item. */
803 item
->menu
->highlighted
= NULL
;
804 paintMenuItem(item
, num
);
806 __glutItemSelected
= NULL
;
808 if (item
->isTrigger
) {
809 if (type
== EnterNotify
&& !alreadyUp
) {
810 GLUTmenu
*submenu
= __glutMenuList
[item
->value
];
813 item
->menu
->x
+ item
->menu
->pixwidth
+
814 MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
+
815 MENU_GAP
+ MENU_BORDER
,
816 item
->menu
->y
+ fontHeight
* (num
- 1) + MENU_GAP
);
817 item
->menu
->cascade
= submenu
;
818 submenu
->anchor
= item
;
823 /* Installs callback functions for use by glut_event.c The point
824 of this is so that GLUT's menu code only gets linked into
825 GLUT binaries (assuming a static library) if the GLUT menu
828 installMenuCallbacks(void)
830 __glutMenuItemEnterOrLeave
= menuItemEnterOrLeave
;
831 __glutFinishMenu
= finishMenu
;
832 __glutPaintMenu
= paintMenu
;
833 __glutStartMenu
= startMenu
;
834 __glutGetMenuByNum
= getMenuByNum
;
835 __glutGetMenu
= getMenu
;
836 __glutGetMenuItem
= getMenuItem
;
840 glutCreateMenu(GLUTselectCB selectFunc
)
842 XSetWindowAttributes wa
;
846 if (__glutMappedMenu
) {
847 __glutMenuModificationError();
849 if (!__glutDisplay
) {
850 __glutOpenXConnection(NULL
);
853 installMenuCallbacks();
855 menuid
= getUnusedMenuSlot();
856 menu
= (GLUTmenu
*) malloc(sizeof(GLUTmenu
));
858 __glutFatalError("out of memory.");
863 menu
->managed
= False
;
864 menu
->searched
= False
;
866 menu
->select
= selectFunc
;
868 menu
->cascade
= NULL
;
869 menu
->highlighted
= NULL
;
872 wa
.override_redirect
= True
;
873 wa
.background_pixel
= menuGray
;
874 wa
.border_pixel
= menuBlack
;
875 wa
.colormap
= menuColormap
;
876 wa
.event_mask
= StructureNotifyMask
| ExposureMask
|
877 ButtonPressMask
| ButtonReleaseMask
|
878 EnterWindowMask
| LeaveWindowMask
;
879 /* Save unders really only enabled if useSaveUnders is set to
880 CWSaveUnder, ie. using Mesa 3D. See earlier comments. */
881 wa
.save_under
= True
;
882 menu
->win
= XCreateWindow(__glutDisplay
, __glutRoot
,
883 /* Real position determined when mapped. */
885 /* Real size will be determined when menu is manged. */
887 MENU_BORDER
, menuDepth
, InputOutput
, menuVisual
,
888 CWOverrideRedirect
| CWBackPixel
| CWBorderPixel
|
889 CWEventMask
| CWColormap
| useSaveUnders
,
891 menuGraphicsContextSetup(menu
->win
);
892 __glutMenuList
[menuid
] = menu
;
901 if (__glutCurrentMenu
) {
902 return __glutCurrentMenu
->id
+ 1;
909 glutSetMenu(int menuid
)
913 if (menuid
< 1 || menuid
> menuListSize
) {
914 __glutWarning("glutSetMenu attempted on bogus menu.");
917 menu
= __glutMenuList
[menuid
- 1];
919 __glutWarning("glutSetMenu attempted on bogus menu.");
927 __glutSetMenuItem(GLUTmenuItem
* item
, const char *label
,
928 int value
, Bool isTrigger
)
933 item
->label
= __glutStrdup(label
);
935 __glutFatalError("out of memory.");
937 item
->isTrigger
= isTrigger
;
938 item
->len
= (int) strlen(label
);
940 item
->pixwidth
= XTextWidth(menuFont
, label
, item
->len
) + 4;
941 if (item
->pixwidth
> menu
->pixwidth
) {
942 menu
->pixwidth
= item
->pixwidth
;
944 menu
->managed
= False
;
949 glutAddMenuEntry(const char *label
, int value
)
951 XSetWindowAttributes wa
;
954 if (__glutMappedMenu
) {
955 __glutMenuModificationError();
957 entry
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
959 __glutFatalError("out of memory.");
961 entry
->menu
= __glutCurrentMenu
;
962 __glutSetMenuItem(entry
, label
, value
, False
);
963 wa
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
964 entry
->win
= XCreateWindow(__glutDisplay
,
965 __glutCurrentMenu
->win
, MENU_GAP
,
966 __glutCurrentMenu
->num
* fontHeight
+ MENU_GAP
, /* x & y */
967 entry
->pixwidth
, fontHeight
, /* width & height */
968 0, CopyFromParent
, InputOnly
, CopyFromParent
,
970 XMapWindow(__glutDisplay
, entry
->win
);
971 __glutCurrentMenu
->num
++;
972 entry
->next
= __glutCurrentMenu
->list
;
973 __glutCurrentMenu
->list
= entry
;
977 glutAddSubMenu(const char *label
, int menu
)
979 XSetWindowAttributes wa
;
980 GLUTmenuItem
*submenu
;
982 if (__glutMappedMenu
) {
983 __glutMenuModificationError();
985 submenu
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
987 __glutFatalError("out of memory.");
989 __glutCurrentMenu
->submenus
++;
990 submenu
->menu
= __glutCurrentMenu
;
991 __glutSetMenuItem(submenu
, label
, /* base 0 */ menu
- 1, True
);
992 wa
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
993 submenu
->win
= XCreateWindow(__glutDisplay
,
994 __glutCurrentMenu
->win
, MENU_GAP
,
995 __glutCurrentMenu
->num
* fontHeight
+ MENU_GAP
, /* x & y */
996 submenu
->pixwidth
, fontHeight
, /* width & height */
997 0, CopyFromParent
, InputOnly
, CopyFromParent
,
999 XMapWindow(__glutDisplay
, submenu
->win
);
1000 __glutCurrentMenu
->num
++;
1001 submenu
->next
= __glutCurrentMenu
->list
;
1002 __glutCurrentMenu
->list
= submenu
;
1006 glutAttachMenu(int button
)
1008 /* if button >= GLUT_MAX_MENUS, we'll go out of array bounds below */
1009 if (button
>= GLUT_MAX_MENUS
) {
1012 if (__glutMappedMenu
) {
1013 __glutMenuModificationError();
1015 installMenuCallbacks();
1016 if (__glutCurrentWindow
->menu
[button
] < 1) {
1017 __glutCurrentWindow
->buttonUses
++;
1019 __glutChangeWindowEventMask(
1020 ButtonPressMask
| ButtonReleaseMask
, True
);
1021 __glutCurrentWindow
->menu
[button
] = __glutCurrentMenu
->id
+ 1;