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
);
231 /* Allocate overlay colormap cells in defined order:
232 gray, black, white to match the IRIS GL allocation
233 scheme. Increases likelihood of less overlay
234 colormap flashing. */
235 /* XXX Nice if these 3 AllocColor's could be done in
236 one protocol round-trip. */
237 color
.red
= color
.green
= color
.blue
= 0xaa00;
238 status
= XAllocColor(__glutDisplay
,
239 menuColormap
, &color
);
241 XFreeColormap(__glutDisplay
, menuColormap
);
247 menuGray
= color
.pixel
;
248 color
.red
= color
.green
= color
.blue
= 0x0000;
249 status
= XAllocColor(__glutDisplay
,
250 menuColormap
, &color
);
252 XFreeColormap(__glutDisplay
, menuColormap
);
258 menuBlack
= color
.pixel
;
259 color
.red
= color
.green
= color
.blue
= 0xffff;
260 status
= XAllocColor(__glutDisplay
,
261 menuColormap
, &color
);
263 XFreeColormap(__glutDisplay
, menuColormap
);
270 /* Now free the placeholder cells. */
271 XFreeColors(__glutDisplay
, menuColormap
,
272 placeHolders
, numPlaceHolders
, 0);
275 menuWhite
= color
.pixel
;
276 menuVisual
= visual
->vinfo
.visual
;
277 menuDepth
= visual
->vinfo
.depth
;
278 /* If using overlays, do not request "save unders". */
280 XFree(overlayVisuals
);
284 XFree(overlayVisuals
);
287 /* Settle for default visual. */
288 menuVisual
= DefaultVisual(__glutDisplay
, __glutScreen
);
289 menuDepth
= DefaultDepth(__glutDisplay
, __glutScreen
);
290 menuColormap
= DefaultColormap(__glutDisplay
, __glutScreen
);
291 menuBlack
= BlackPixel(__glutDisplay
, __glutScreen
);
292 menuWhite
= WhitePixel(__glutDisplay
, __glutScreen
);
293 color
.red
= color
.green
= color
.blue
= 0xaa00;
294 noFaultXAllocColor(__glutDisplay
, menuColormap
,
295 menuVisual
->map_entries
, &color
);
296 menuGray
= color
.pixel
;
298 /* When no overlays are supported, we would like to use X
299 "save unders" to avoid exposes to windows obscured by
300 pop-up menus. However, OpenGL's direct rendering support
301 means OpenGL interacts poorly with X backing store and
302 save unders. X servers do not (in implementation
303 practice) redirect OpenGL rendering destined to obscured
304 window regions into backing store.
306 Implementation solutions exist for this problem, but they
307 are expensive and high-end OpenGL implementations
308 typically provide fast rendering and/or overlays to
309 obviate the problem associated of user interfaces (pop-up
310 menus) forcing redraws of complex normal plane scenes.
311 (See support for overlays pop-up menus above.)
313 Mesa 3D, however, does not support direct rendering.
314 Overlays are often unavailable to Mesa, and Mesa is also
315 relatively slow. For these reasons, Mesa-rendering GLUT
316 programs can and should use X save unders.
318 Look for the GLX extension. If _not_ supported, we are
319 presumably using Mesa so enable save unders. */
321 presumablyMesa
= !XQueryExtension(__glutDisplay
, "GLX",
322 &dummy
, &dummy
, &dummy
);
324 if (presumablyMesa
) {
325 useSaveUnders
= CWSaveUnder
;
335 /* MenuFont overload to indicate menu initalization. */
338 menuFont
= XLoadQueryFont(__glutDisplay
,
339 "-*-helvetica-bold-o-normal--14-*-*-*-p-*-iso8859-1");
341 /* Try back up font. */
342 menuFont
= XLoadQueryFont(__glutDisplay
, "fixed");
345 __glutFatalError("could not load font.");
348 fontHeight
= menuFont
->ascent
+ menuFont
->descent
;
349 menuCursor
= XCreateFontCursor(__glutDisplay
, XC_arrow
);
353 menuGraphicsContextSetup(Window win
)
357 if (blackGC
!= None
) {
360 gcvals
.font
= menuFont
->fid
;
361 gcvals
.foreground
= menuBlack
;
362 blackGC
= XCreateGC(__glutDisplay
, win
,
363 GCFont
| GCForeground
, &gcvals
);
364 gcvals
.foreground
= menuGray
;
365 grayGC
= XCreateGC(__glutDisplay
, win
, GCForeground
, &gcvals
);
366 gcvals
.foreground
= menuWhite
;
367 whiteGC
= XCreateGC(__glutDisplay
, win
, GCForeground
, &gcvals
);
371 __glutSetMenu(GLUTmenu
* menu
)
373 __glutCurrentMenu
= menu
;
377 unmapMenu(GLUTmenu
* menu
)
380 unmapMenu(menu
->cascade
);
381 menu
->cascade
= NULL
;
384 menu
->highlighted
= NULL
;
385 XUnmapWindow(__glutDisplay
, menu
->win
);
389 finishMenu(Window win
, int x
, int y
)
394 unmapMenu(__glutMappedMenu
);
395 XUngrabPointer(__glutDisplay
, CurrentTime
);
397 /* Popping up an overlay popup menu will install its own
398 colormap. If the window associated with the menu has an
399 overlay, install that window's overlay colormap so the
400 overlay isn't left using the popup menu's colormap. */
401 if (__glutMenuWindow
->overlay
) {
402 XInstallColormap(__glutDisplay
,
403 __glutMenuWindow
->overlay
->colormap
->cmap
);
406 /* This XFlush is needed to to make sure the pointer is
407 really ungrabbed when the application's menu callback is
408 called. Otherwise, a deadlock might happen because the
409 application may try to read from an terminal window, but
410 yet the ungrab hasn't really happened since it hasn't been
412 XFlush(__glutDisplay
);
414 if (__glutMenuStatusFunc
) {
415 if (win
!= __glutMenuWindow
->win
) {
416 /* The button release may have occurred in a window other
417 than the window requesting the pop-up menu (for
418 example, one of the submenu windows). In this case, we
419 need to translate the coordinates into the coordinate
420 system of the window associated with the window. */
421 rc
= XTranslateCoordinates(__glutDisplay
, win
, __glutMenuWindow
->win
,
422 x
, y
, &x
, &y
, &dummy
);
423 assert(rc
!= False
); /* Will always be on same screen. */
425 __glutSetWindow(__glutMenuWindow
);
426 __glutSetMenu(__glutMappedMenu
);
428 /* Setting __glutMappedMenu to NULL permits operations that
429 change menus or destroy the menu window again. */
430 __glutMappedMenu
= NULL
;
432 __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE
, x
, y
);
434 /* Setting __glutMappedMenu to NULL permits operations that
435 change menus or destroy the menu window again. */
436 __glutMappedMenu
= NULL
;
438 /* If an item is selected and it is not a submenu trigger,
439 generate menu callback. */
440 if (__glutItemSelected
&& !__glutItemSelected
->isTrigger
) {
441 __glutSetWindow(__glutMenuWindow
);
442 /* When menu callback is triggered, current menu should be
443 set to the callback menu. */
444 __glutSetMenu(__glutItemSelected
->menu
);
445 __glutItemSelected
->menu
->select(
446 __glutItemSelected
->value
);
448 __glutMenuWindow
= NULL
;
451 #define MENU_BORDER 1
453 #define MENU_ARROW_GAP 6
454 #define MENU_ARROW_WIDTH 8
457 mapMenu(GLUTmenu
* menu
, int x
, int y
)
459 XWindowChanges changes
;
461 int subMenuExtension
, num
;
463 /* If there are submenus, we need to provide extra space for
464 the submenu pull arrow. */
465 if (menu
->submenus
> 0) {
466 subMenuExtension
= MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
;
468 subMenuExtension
= 0;
471 changes
.stack_mode
= Above
;
472 mask
= CWStackMode
| CWX
| CWY
;
473 /* If the menu isn't managed (ie, validated so all the
474 InputOnly subwindows are the right size), do so. */
475 if (!menu
->managed
) {
481 XWindowChanges itemupdate
;
483 itemupdate
.y
= (num
- 1) * fontHeight
+ MENU_GAP
;
484 itemupdate
.width
= menu
->pixwidth
;
485 itemupdate
.width
+= subMenuExtension
;
486 XConfigureWindow(__glutDisplay
, item
->win
,
487 CWWidth
| CWY
, &itemupdate
);
491 menu
->pixheight
= MENU_GAP
+
492 fontHeight
* menu
->num
+ MENU_GAP
;
493 changes
.height
= menu
->pixheight
;
494 changes
.width
= MENU_GAP
+
495 menu
->pixwidth
+ subMenuExtension
+ MENU_GAP
;
496 mask
|= CWWidth
| CWHeight
;
497 menu
->managed
= True
;
499 /* Make sure menu appears fully on screen. */
500 if (y
+ menu
->pixheight
>= __glutScreenHeight
) {
501 changes
.y
= __glutScreenHeight
- menu
->pixheight
;
505 if (x
+ menu
->pixwidth
+ subMenuExtension
>=
507 changes
.x
= __glutScreenWidth
-
508 menu
->pixwidth
+ subMenuExtension
;
513 /* Rember where the menu is placed so submenus can be
514 properly placed relative to it. */
518 XConfigureWindow(__glutDisplay
, menu
->win
, mask
, &changes
);
519 XInstallColormap(__glutDisplay
, menuColormap
);
520 /* XXX The XRaiseWindow below should not be necessary because
521 the XConfigureWindow requests an Above stack mode (same as
522 XRaiseWindow), but some Sun users complained this was still
523 necessary. Probably some window manager or X server bug on
525 XRaiseWindow(__glutDisplay
, menu
->win
);
526 XMapWindow(__glutDisplay
, menu
->win
);
530 startMenu(GLUTmenu
* menu
, GLUTwindow
* window
,
531 int x
, int y
, int x_win
, int y_win
)
535 assert(__glutMappedMenu
== NULL
);
536 grab
= XGrabPointer(__glutDisplay
, __glutRoot
, True
,
537 ButtonPressMask
| ButtonReleaseMask
,
538 GrabModeAsync
, GrabModeAsync
,
539 __glutRoot
, menuCursor
, CurrentTime
);
540 if (grab
!= GrabSuccess
) {
541 /* Somebody else has pointer grabbed, ignore menu
545 __glutMappedMenu
= menu
;
546 __glutMenuWindow
= window
;
547 __glutItemSelected
= NULL
;
548 if (__glutMenuStatusFunc
) {
550 __glutSetWindow(window
);
551 __glutMenuStatusFunc(GLUT_MENU_IN_USE
, x_win
, y_win
);
557 paintSubMenuArrow(Window win
, int x
, int y
)
562 p
[0].y
= p
[4].y
= y
- menuFont
->ascent
+ 1;
563 p
[1].x
= p
[0].x
+ MENU_ARROW_WIDTH
- 1;
564 p
[1].y
= p
[0].y
+ (menuFont
->ascent
/ 2) - 1;
568 p
[3].y
= p
[0].y
+ menuFont
->ascent
- 2;
569 XFillPolygon(__glutDisplay
, win
,
570 whiteGC
, p
, 4, Convex
, CoordModeOrigin
);
571 XDrawLines(__glutDisplay
, win
, blackGC
, p
, 5, CoordModeOrigin
);
575 paintMenuItem(GLUTmenuItem
* item
, int num
)
577 Window win
= item
->menu
->win
;
580 int subMenuExtension
;
582 if (item
->menu
->submenus
> 0) {
583 subMenuExtension
= MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
;
585 subMenuExtension
= 0;
587 if (item
->menu
->highlighted
== item
) {
592 y
= MENU_GAP
+ fontHeight
* num
- menuFont
->descent
;
593 XFillRectangle(__glutDisplay
, win
, gc
,
594 MENU_GAP
, y
- fontHeight
+ menuFont
->descent
,
595 item
->menu
->pixwidth
+ subMenuExtension
, fontHeight
);
596 XDrawString(__glutDisplay
, win
, blackGC
,
597 MENU_GAP
, y
, item
->label
, item
->len
);
598 if (item
->isTrigger
) {
599 paintSubMenuArrow(win
,
600 item
->menu
->pixwidth
+ MENU_ARROW_GAP
+ 1, y
);
605 paintMenu(GLUTmenu
* menu
)
609 int y
= MENU_GAP
+ fontHeight
* i
- menuFont
->descent
;
613 if (item
->menu
->highlighted
== item
) {
614 paintMenuItem(item
, i
);
616 /* Quick render of the menu item; assume background
617 already cleared to gray. */
618 XDrawString(__glutDisplay
, menu
->win
, blackGC
,
619 2, y
, item
->label
, item
->len
);
620 if (item
->isTrigger
) {
621 paintSubMenuArrow(menu
->win
,
622 menu
->pixwidth
+ MENU_ARROW_GAP
+ 1, y
);
631 static GLUTmenuItem
*
632 getMenuItem(GLUTmenu
* menu
, Window win
, int *which
)
637 if (menu
->searched
) {
638 __glutFatalError("submenu infinite loop detected");
640 menu
->searched
= True
;
644 if (item
->win
== win
) {
646 menu
->searched
= False
;
649 if (item
->isTrigger
) {
650 GLUTmenuItem
*subitem
;
652 subitem
= __glutGetMenuItem(__glutMenuList
[item
->value
],
655 menu
->searched
= False
;
662 menu
->searched
= False
;
667 getMenuItemIndex(GLUTmenuItem
* item
)
683 menu
= __glutMappedMenu
;
685 if (win
== menu
->win
) {
688 menu
= menu
->cascade
;
694 getMenuByNum(int menunum
)
696 if (menunum
< 1 || menunum
> menuListSize
) {
699 return __glutMenuList
[menunum
- 1];
703 getUnusedMenuSlot(void)
707 /* Look for allocated, unused slot. */
708 for (i
= 0; i
< menuListSize
; i
++) {
709 if (!__glutMenuList
[i
]) {
713 /* Allocate a new slot. */
715 if (__glutMenuList
) {
716 __glutMenuList
= (GLUTmenu
**)
717 realloc(__glutMenuList
, menuListSize
* sizeof(GLUTmenu
*));
719 /* XXX Some realloc's do not correctly perform a malloc
720 when asked to perform a realloc on a NULL pointer,
721 though the ANSI C library spec requires this. */
722 __glutMenuList
= (GLUTmenu
**) malloc(sizeof(GLUTmenu
*));
724 if (!__glutMenuList
) {
725 __glutFatalError("out of memory.");
727 __glutMenuList
[menuListSize
- 1] = NULL
;
728 return menuListSize
- 1;
732 __glutMenuModificationError(void)
734 /* XXX Remove the warning after GLUT 3.0. */
735 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
736 __glutFatalError("menu manipulation not allowed while menus in use.");
741 menuItemEnterOrLeave(GLUTmenuItem
* item
,
746 if (type
== EnterNotify
) {
747 GLUTmenuItem
*prevItem
= item
->menu
->highlighted
;
749 if (prevItem
&& prevItem
!= item
) {
750 /* If there's an already higlighted item in this menu
751 that is different from this one (we could be
752 re-entering an item with an already cascaded
753 submenu!), unhighlight the previous item. */
754 item
->menu
->highlighted
= NULL
;
755 paintMenuItem(prevItem
, getMenuItemIndex(prevItem
));
757 item
->menu
->highlighted
= item
;
758 __glutItemSelected
= item
;
759 if (item
->menu
->cascade
) {
760 if (!item
->isTrigger
) {
761 /* Entered a menu item that is not a submenu trigger,
762 so pop down the current submenu cascade of this
764 unmapMenu(item
->menu
->cascade
);
765 item
->menu
->cascade
= NULL
;
767 GLUTmenu
*submenu
= __glutMenuList
[item
->value
];
769 if (submenu
->anchor
== item
) {
770 /* We entered the submenu trigger for the submenu
771 that is already up, so don't take down the
775 /* Submenu already popped up for some other submenu
776 item of this menu; need to pop down that other
778 unmapMenu(item
->menu
->cascade
);
779 item
->menu
->cascade
= NULL
;
784 /* Make sure the menu item gets painted with
786 paintMenuItem(item
, num
);
788 /* If already up, should already be highlighted. */
791 /* LeaveNotify: Handle leaving a menu item... */
792 if (item
->menu
->cascade
&&
793 item
->menu
->cascade
->anchor
== item
) {
794 /* If there is a submenu casacaded from this item, do not
795 change the highlighting on this item upon leaving. */
797 /* Unhighlight this menu item. */
798 item
->menu
->highlighted
= NULL
;
799 paintMenuItem(item
, num
);
801 __glutItemSelected
= NULL
;
803 if (item
->isTrigger
) {
804 if (type
== EnterNotify
&& !alreadyUp
) {
805 GLUTmenu
*submenu
= __glutMenuList
[item
->value
];
808 item
->menu
->x
+ item
->menu
->pixwidth
+
809 MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
+
810 MENU_GAP
+ MENU_BORDER
,
811 item
->menu
->y
+ fontHeight
* (num
- 1) + MENU_GAP
);
812 item
->menu
->cascade
= submenu
;
813 submenu
->anchor
= item
;
818 /* Installs callback functions for use by glut_event.c The point
819 of this is so that GLUT's menu code only gets linked into
820 GLUT binaries (assuming a static library) if the GLUT menu
823 installMenuCallbacks(void)
825 __glutMenuItemEnterOrLeave
= menuItemEnterOrLeave
;
826 __glutFinishMenu
= finishMenu
;
827 __glutPaintMenu
= paintMenu
;
828 __glutStartMenu
= startMenu
;
829 __glutGetMenuByNum
= getMenuByNum
;
830 __glutGetMenu
= getMenu
;
831 __glutGetMenuItem
= getMenuItem
;
835 glutCreateMenu(GLUTselectCB selectFunc
)
837 XSetWindowAttributes wa
;
841 if (__glutMappedMenu
) {
842 __glutMenuModificationError();
844 if (!__glutDisplay
) {
845 __glutOpenXConnection(NULL
);
848 installMenuCallbacks();
850 menuid
= getUnusedMenuSlot();
851 menu
= (GLUTmenu
*) malloc(sizeof(GLUTmenu
));
853 __glutFatalError("out of memory.");
858 menu
->managed
= False
;
859 menu
->searched
= False
;
861 menu
->select
= selectFunc
;
863 menu
->cascade
= NULL
;
864 menu
->highlighted
= NULL
;
867 wa
.override_redirect
= True
;
868 wa
.background_pixel
= menuGray
;
869 wa
.border_pixel
= menuBlack
;
870 wa
.colormap
= menuColormap
;
871 wa
.event_mask
= StructureNotifyMask
| ExposureMask
|
872 ButtonPressMask
| ButtonReleaseMask
|
873 EnterWindowMask
| LeaveWindowMask
;
874 /* Save unders really only enabled if useSaveUnders is set to
875 CWSaveUnder, ie. using Mesa 3D. See earlier comments. */
876 wa
.save_under
= True
;
877 menu
->win
= XCreateWindow(__glutDisplay
, __glutRoot
,
878 /* Real position determined when mapped. */
880 /* Real size will be determined when menu is manged. */
882 MENU_BORDER
, menuDepth
, InputOutput
, menuVisual
,
883 CWOverrideRedirect
| CWBackPixel
| CWBorderPixel
|
884 CWEventMask
| CWColormap
| useSaveUnders
,
886 menuGraphicsContextSetup(menu
->win
);
887 __glutMenuList
[menuid
] = menu
;
896 if (__glutCurrentMenu
) {
897 return __glutCurrentMenu
->id
+ 1;
904 glutSetMenu(int menuid
)
908 if (menuid
< 1 || menuid
> menuListSize
) {
909 __glutWarning("glutSetMenu attempted on bogus menu.");
912 menu
= __glutMenuList
[menuid
- 1];
914 __glutWarning("glutSetMenu attempted on bogus menu.");
922 __glutSetMenuItem(GLUTmenuItem
* item
, const char *label
,
923 int value
, Bool isTrigger
)
928 item
->label
= __glutStrdup(label
);
930 __glutFatalError("out of memory.");
932 item
->isTrigger
= isTrigger
;
933 item
->len
= (int) strlen(label
);
935 item
->pixwidth
= XTextWidth(menuFont
, label
, item
->len
) + 4;
936 if (item
->pixwidth
> menu
->pixwidth
) {
937 menu
->pixwidth
= item
->pixwidth
;
939 menu
->managed
= False
;
944 glutAddMenuEntry(const char *label
, int value
)
946 XSetWindowAttributes wa
;
949 if (__glutMappedMenu
) {
950 __glutMenuModificationError();
952 entry
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
954 __glutFatalError("out of memory.");
956 entry
->menu
= __glutCurrentMenu
;
957 __glutSetMenuItem(entry
, label
, value
, False
);
958 wa
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
959 entry
->win
= XCreateWindow(__glutDisplay
,
960 __glutCurrentMenu
->win
, MENU_GAP
,
961 __glutCurrentMenu
->num
* fontHeight
+ MENU_GAP
, /* x & y */
962 entry
->pixwidth
, fontHeight
, /* width & height */
963 0, CopyFromParent
, InputOnly
, CopyFromParent
,
965 XMapWindow(__glutDisplay
, entry
->win
);
966 __glutCurrentMenu
->num
++;
967 entry
->next
= __glutCurrentMenu
->list
;
968 __glutCurrentMenu
->list
= entry
;
972 glutAddSubMenu(const char *label
, int menu
)
974 XSetWindowAttributes wa
;
975 GLUTmenuItem
*submenu
;
977 if (__glutMappedMenu
) {
978 __glutMenuModificationError();
980 submenu
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
982 __glutFatalError("out of memory.");
984 __glutCurrentMenu
->submenus
++;
985 submenu
->menu
= __glutCurrentMenu
;
986 __glutSetMenuItem(submenu
, label
, /* base 0 */ menu
- 1, True
);
987 wa
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
988 submenu
->win
= XCreateWindow(__glutDisplay
,
989 __glutCurrentMenu
->win
, MENU_GAP
,
990 __glutCurrentMenu
->num
* fontHeight
+ MENU_GAP
, /* x & y */
991 submenu
->pixwidth
, fontHeight
, /* width & height */
992 0, CopyFromParent
, InputOnly
, CopyFromParent
,
994 XMapWindow(__glutDisplay
, submenu
->win
);
995 __glutCurrentMenu
->num
++;
996 submenu
->next
= __glutCurrentMenu
->list
;
997 __glutCurrentMenu
->list
= submenu
;
1001 glutAttachMenu(int button
)
1003 if (__glutMappedMenu
) {
1004 __glutMenuModificationError();
1006 installMenuCallbacks();
1007 if (__glutCurrentWindow
->menu
[button
] < 1) {
1008 __glutCurrentWindow
->buttonUses
++;
1010 __glutChangeWindowEventMask(
1011 ButtonPressMask
| ButtonReleaseMask
, True
);
1012 __glutCurrentWindow
->menu
[button
] = __glutCurrentMenu
->id
+ 1;