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. */
20 #include <X11/cursorfont.h> /* for XC_arrow */
23 #include "layerutil.h"
25 void (CDECL
*__glutMenuStatusFunc
) (int, int, int);
26 GLUTmenuItem
*__glutItemSelected
;
27 GLUTmenu
**__glutMenuList
= NULL
;
29 static int menuListSize
= 0;
30 static XFontStruct
*menuFont
= NULL
;
31 static Cursor menuCursor
;
32 static Colormap menuColormap
;
33 static Visual
*menuVisual
;
35 static int fontHeight
;
36 static GC blackGC
, grayGC
, whiteGC
;
37 static unsigned long menuBlack
, menuWhite
, menuGray
;
38 static unsigned long useSaveUnders
;
40 /* A replacement for XAllocColor (originally by Brian Paul).
41 This function should never fail to allocate a color. When
42 XAllocColor fails, we return the nearest matching color. If
43 we have to allocate many colors this function isn't a great
44 solution; the XQueryColors() could be done just once. */
46 noFaultXAllocColor(Display
* dpy
, Colormap cmap
, int cmapSize
,
49 XColor
*ctable
, subColor
;
51 double mindist
; /* 3*2^16^2 exceeds 32-bit long int
55 /* First try just using XAllocColor. */
56 if (XAllocColor(dpy
, cmap
, color
)) {
60 /* Retrieve color table entries. */
61 /* XXX alloca canidate. */
62 ctable
= (XColor
*) malloc(cmapSize
* sizeof(XColor
));
63 for (i
= 0; i
< cmapSize
; i
++)
65 XQueryColors(dpy
, cmap
, ctable
, cmapSize
);
67 /* Find best match. */
70 for (i
= 0; i
< cmapSize
; i
++) {
71 double dr
= (double) color
->red
- (double) ctable
[i
].red
;
72 double dg
= (double) color
->green
- (double) ctable
[i
].green
;
73 double db
= (double) color
->blue
- (double) ctable
[i
].blue
;
74 double dist
= dr
* dr
+ dg
* dg
+ db
* db
;
75 if (bestmatch
< 0 || dist
< mindist
) {
82 subColor
.red
= ctable
[bestmatch
].red
;
83 subColor
.green
= ctable
[bestmatch
].green
;
84 subColor
.blue
= ctable
[bestmatch
].blue
;
86 if (XAllocColor(dpy
, cmap
, &subColor
)) {
90 /* Extremely unlikely, but possibly color was deallocated
91 and reallocated by someone else before we could
92 XAllocColor the color cell we located. If so, loop
100 char *xvendor
, *glvendor
, *renderer
;
101 int isSunCreator
= 0; /* Until proven that it is. */
102 int savedDisplayMode
;
103 char *savedDisplayString
;
106 #define VENDOR_SUN "Sun Microsystems"
107 #define RENDERER_CREATOR "Creator"
109 /* Check the X vendor string first. It is easier to check
110 than the OpenGL vendor and renderer strings since it
111 doesn't require a valid OpenGL rendering context. Bail
112 early if not connected to a Sun. */
113 xvendor
= ServerVendor(__glutDisplay
);
114 if (!strncmp(xvendor
, VENDOR_SUN
, sizeof(VENDOR_SUN
) - 1)) {
116 /* We need a valid current OpenGL rendering context to be
117 able to call glGetString successfully. If there is not
118 a current window, set up a temporary one just to call
119 glGetString with (gag, expensive). */
120 if (__glutCurrentWindow
) {
123 savedDisplayMode
= __glutDisplayMode
;
124 savedDisplayString
= __glutDisplayString
;
125 __glutDisplayMode
= GLUT_RGB
| GLUT_SINGLE
;
126 __glutDisplayString
= NULL
;
127 window
= __glutCreateWindow(NULL
, 0, 0, 1, 1, 0);
130 glvendor
= (char *) glGetString(GL_VENDOR
);
131 if (!strncmp(glvendor
, VENDOR_SUN
, sizeof(VENDOR_SUN
) - 1)) {
132 renderer
= (char *) glGetString(GL_RENDERER
);
133 if (!strncmp(renderer
, RENDERER_CREATOR
, sizeof(RENDERER_CREATOR
) - 1)) {
137 /* Destroy the temporary window for glGetString if one
138 needed to be created. */
140 __glutDestroyWindow(window
, window
);
141 __glutDisplayMode
= savedDisplayMode
;
142 __glutDisplayString
= savedDisplayString
;
149 menuVisualSetup(void)
151 XLayerVisualInfo
template, *visual
, *overlayVisuals
;
155 int layer
, nVisuals
, i
, dummy
;
156 unsigned long *placeHolders
= NULL
;
160 allocateHigh
= ifSunCreator();
162 /* Start with the highest overlay layer and work down. I
163 don't think any hardware has more than 3 overlay layers. */
164 for (layer
= 3; layer
> 0; layer
--) {
165 template.layer
= layer
;
166 template.vinfo
.screen
= __glutScreen
;
167 overlayVisuals
= __glutXGetLayerVisualInfo(__glutDisplay
,
168 VisualScreenMask
| VisualLayerMask
, &template, &nVisuals
);
169 if (overlayVisuals
) {
170 /* First, check if the default visual is in this layer.
171 If the default visual is in this layer, we try to use
172 it since it has pre-defined black and white pixels and
174 using the default visual will probably minimize
175 colormap flashing problems. Suggested by Thomas Roell
177 for (i
= 0; i
< nVisuals
; i
++) {
178 visual
= &overlayVisuals
[i
];
179 if (visual
->vinfo
.colormap_size
>= 3) {
180 /* Compare visual IDs just to be safe. */
181 if (visual
->vinfo
.visual
->visualid
== DefaultVisual(__glutDisplay
, __glutScreen
)->visualid
) {
182 /* Settle for default visual. */
183 menuVisual
= DefaultVisual(__glutDisplay
, __glutScreen
);
184 menuDepth
= DefaultDepth(__glutDisplay
, __glutScreen
);
185 menuColormap
= DefaultColormap(__glutDisplay
, __glutScreen
);
186 menuBlack
= BlackPixel(__glutDisplay
, __glutScreen
);
187 menuWhite
= WhitePixel(__glutDisplay
, __glutScreen
);
188 color
.red
= color
.green
= color
.blue
= 0xaa00;
189 noFaultXAllocColor(__glutDisplay
, menuColormap
,
190 menuVisual
->map_entries
, &color
);
191 menuGray
= color
.pixel
;
193 XFree(overlayVisuals
);
198 for (i
= 0; i
< nVisuals
; i
++) {
199 visual
= &overlayVisuals
[i
];
200 if (visual
->vinfo
.colormap_size
>= 3) {
202 /* For Sun's Creator graphics, try to force the
203 read-only colors to the high end of the colormap
204 by first allocating read-write place-holder cells
205 for all but the last three cells. This helps
206 avoid colormap flashing problems. */
207 numPlaceHolders
= visual
->vinfo
.colormap_size
- 3;
208 if (numPlaceHolders
> 0) {
209 placeHolders
= (unsigned long *)
210 malloc(numPlaceHolders
* sizeof(unsigned long));
211 /* A malloc failure would be harmless. */
214 menuColormap
= XCreateColormap(__glutDisplay
, __glutRoot
,
215 visual
->vinfo
.visual
, AllocNone
);
217 /* Again for Sun's Creator graphics, do the actual
218 read-write place-holder cell allocation. */
219 status
= XAllocColorCells(__glutDisplay
, menuColormap
, False
, 0, 0,
220 placeHolders
, numPlaceHolders
);
222 XFreeColormap(__glutDisplay
, menuColormap
);
227 /* Allocate overlay colormap cells in defined order:
228 gray, black, white to match the IRIS GL allocation
229 scheme. Increases likelihood of less overlay
230 colormap flashing. */
231 /* XXX Nice if these 3 AllocColor's could be done in
232 one protocol round-trip. */
233 color
.red
= color
.green
= color
.blue
= 0xaa00;
234 status
= XAllocColor(__glutDisplay
,
235 menuColormap
, &color
);
237 XFreeColormap(__glutDisplay
, menuColormap
);
243 menuGray
= color
.pixel
;
244 color
.red
= color
.green
= color
.blue
= 0x0000;
245 status
= XAllocColor(__glutDisplay
,
246 menuColormap
, &color
);
248 XFreeColormap(__glutDisplay
, menuColormap
);
254 menuBlack
= color
.pixel
;
255 color
.red
= color
.green
= color
.blue
= 0xffff;
256 status
= XAllocColor(__glutDisplay
,
257 menuColormap
, &color
);
259 XFreeColormap(__glutDisplay
, menuColormap
);
266 /* Now free the placeholder cells. */
267 XFreeColors(__glutDisplay
, menuColormap
,
268 placeHolders
, numPlaceHolders
, 0);
271 menuWhite
= color
.pixel
;
272 menuVisual
= visual
->vinfo
.visual
;
273 menuDepth
= visual
->vinfo
.depth
;
274 /* If using overlays, do not request "save unders". */
276 XFree(overlayVisuals
);
280 XFree(overlayVisuals
);
283 /* Settle for default visual. */
284 menuVisual
= DefaultVisual(__glutDisplay
, __glutScreen
);
285 menuDepth
= DefaultDepth(__glutDisplay
, __glutScreen
);
286 menuColormap
= DefaultColormap(__glutDisplay
, __glutScreen
);
287 menuBlack
= BlackPixel(__glutDisplay
, __glutScreen
);
288 menuWhite
= WhitePixel(__glutDisplay
, __glutScreen
);
289 color
.red
= color
.green
= color
.blue
= 0xaa00;
290 noFaultXAllocColor(__glutDisplay
, menuColormap
,
291 menuVisual
->map_entries
, &color
);
292 menuGray
= color
.pixel
;
294 /* When no overlays are supported, we would like to use X
295 "save unders" to avoid exposes to windows obscured by
296 pop-up menus. However, OpenGL's direct rendering support
297 means OpenGL interacts poorly with X backing store and
298 save unders. X servers do not (in implementation
299 practice) redirect OpenGL rendering destined to obscured
300 window regions into backing store.
302 Implementation solutions exist for this problem, but they
303 are expensive and high-end OpenGL implementations
304 typically provide fast rendering and/or overlays to
305 obviate the problem associated of user interfaces (pop-up
306 menus) forcing redraws of complex normal plane scenes.
307 (See support for overlays pop-up menus above.)
309 Mesa 3D, however, does not support direct rendering.
310 Overlays are often unavailable to Mesa, and Mesa is also
311 relatively slow. For these reasons, Mesa-rendering GLUT
312 programs can and should use X save unders.
314 Look for the GLX extension. If _not_ supported, we are
315 presumably using Mesa so enable save unders. */
317 presumablyMesa
= !XQueryExtension(__glutDisplay
, "GLX",
318 &dummy
, &dummy
, &dummy
);
320 if (presumablyMesa
) {
321 useSaveUnders
= CWSaveUnder
;
331 /* MenuFont overload to indicate menu initalization. */
334 menuFont
= XLoadQueryFont(__glutDisplay
,
335 "-*-helvetica-bold-o-normal--14-*-*-*-p-*-iso8859-1");
337 /* Try back up font. */
338 menuFont
= XLoadQueryFont(__glutDisplay
, "fixed");
341 __glutFatalError("could not load font.");
344 fontHeight
= menuFont
->ascent
+ menuFont
->descent
;
345 menuCursor
= XCreateFontCursor(__glutDisplay
, XC_arrow
);
349 menuGraphicsContextSetup(Window win
)
353 if (blackGC
!= None
) {
356 gcvals
.font
= menuFont
->fid
;
357 gcvals
.foreground
= menuBlack
;
358 blackGC
= XCreateGC(__glutDisplay
, win
,
359 GCFont
| GCForeground
, &gcvals
);
360 gcvals
.foreground
= menuGray
;
361 grayGC
= XCreateGC(__glutDisplay
, win
, GCForeground
, &gcvals
);
362 gcvals
.foreground
= menuWhite
;
363 whiteGC
= XCreateGC(__glutDisplay
, win
, GCForeground
, &gcvals
);
367 __glutSetMenu(GLUTmenu
* menu
)
369 __glutCurrentMenu
= menu
;
373 unmapMenu(GLUTmenu
* menu
)
376 unmapMenu(menu
->cascade
);
377 menu
->cascade
= NULL
;
380 menu
->highlighted
= NULL
;
381 XUnmapWindow(__glutDisplay
, menu
->win
);
385 finishMenu(Window win
, int x
, int y
)
390 unmapMenu(__glutMappedMenu
);
391 XUngrabPointer(__glutDisplay
, CurrentTime
);
393 /* Popping up an overlay popup menu will install its own
394 colormap. If the window associated with the menu has an
395 overlay, install that window's overlay colormap so the
396 overlay isn't left using the popup menu's colormap. */
397 if (__glutMenuWindow
->overlay
) {
398 XInstallColormap(__glutDisplay
,
399 __glutMenuWindow
->overlay
->colormap
->cmap
);
402 /* This XFlush is needed to to make sure the pointer is
403 really ungrabbed when the application's menu callback is
404 called. Otherwise, a deadlock might happen because the
405 application may try to read from an terminal window, but
406 yet the ungrab hasn't really happened since it hasn't been
408 XFlush(__glutDisplay
);
410 if (__glutMenuStatusFunc
) {
411 if (win
!= __glutMenuWindow
->win
) {
412 /* The button release may have occurred in a window other
413 than the window requesting the pop-up menu (for
414 example, one of the submenu windows). In this case, we
415 need to translate the coordinates into the coordinate
416 system of the window associated with the window. */
417 rc
= XTranslateCoordinates(__glutDisplay
, win
, __glutMenuWindow
->win
,
418 x
, y
, &x
, &y
, &dummy
);
419 assert(rc
!= False
); /* Will always be on same screen. */
421 __glutSetWindow(__glutMenuWindow
);
422 __glutSetMenu(__glutMappedMenu
);
424 /* Setting __glutMappedMenu to NULL permits operations that
425 change menus or destroy the menu window again. */
426 __glutMappedMenu
= NULL
;
428 __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE
, x
, y
);
430 /* Setting __glutMappedMenu to NULL permits operations that
431 change menus or destroy the menu window again. */
432 __glutMappedMenu
= NULL
;
434 /* If an item is selected and it is not a submenu trigger,
435 generate menu callback. */
436 if (__glutItemSelected
&& !__glutItemSelected
->isTrigger
) {
437 __glutSetWindow(__glutMenuWindow
);
438 /* When menu callback is triggered, current menu should be
439 set to the callback menu. */
440 __glutSetMenu(__glutItemSelected
->menu
);
441 __glutItemSelected
->menu
->select(
442 __glutItemSelected
->value
);
444 __glutMenuWindow
= NULL
;
447 #define MENU_BORDER 1
449 #define MENU_ARROW_GAP 6
450 #define MENU_ARROW_WIDTH 8
453 mapMenu(GLUTmenu
* menu
, int x
, int y
)
455 XWindowChanges changes
;
457 int subMenuExtension
, num
;
459 /* If there are submenus, we need to provide extra space for
460 the submenu pull arrow. */
461 if (menu
->submenus
> 0) {
462 subMenuExtension
= MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
;
464 subMenuExtension
= 0;
467 changes
.stack_mode
= Above
;
468 mask
= CWStackMode
| CWX
| CWY
;
469 /* If the menu isn't managed (ie, validated so all the
470 InputOnly subwindows are the right size), do so. */
471 if (!menu
->managed
) {
477 XWindowChanges itemupdate
;
479 itemupdate
.y
= (num
- 1) * fontHeight
+ MENU_GAP
;
480 itemupdate
.width
= menu
->pixwidth
;
481 itemupdate
.width
+= subMenuExtension
;
482 XConfigureWindow(__glutDisplay
, item
->win
,
483 CWWidth
| CWY
, &itemupdate
);
487 menu
->pixheight
= MENU_GAP
+
488 fontHeight
* menu
->num
+ MENU_GAP
;
489 changes
.height
= menu
->pixheight
;
490 changes
.width
= MENU_GAP
+
491 menu
->pixwidth
+ subMenuExtension
+ MENU_GAP
;
492 mask
|= CWWidth
| CWHeight
;
493 menu
->managed
= True
;
495 /* Make sure menu appears fully on screen. */
496 if (y
+ menu
->pixheight
>= __glutScreenHeight
) {
497 changes
.y
= __glutScreenHeight
- menu
->pixheight
;
501 if (x
+ menu
->pixwidth
+ subMenuExtension
>=
503 changes
.x
= __glutScreenWidth
-
504 menu
->pixwidth
+ subMenuExtension
;
509 /* Rember where the menu is placed so submenus can be
510 properly placed relative to it. */
514 XConfigureWindow(__glutDisplay
, menu
->win
, mask
, &changes
);
515 XInstallColormap(__glutDisplay
, menuColormap
);
516 /* XXX The XRaiseWindow below should not be necessary because
517 the XConfigureWindow requests an Above stack mode (same as
518 XRaiseWindow), but some Sun users complained this was still
519 necessary. Probably some window manager or X server bug on
521 XRaiseWindow(__glutDisplay
, menu
->win
);
522 XMapWindow(__glutDisplay
, menu
->win
);
526 startMenu(GLUTmenu
* menu
, GLUTwindow
* window
,
527 int x
, int y
, int x_win
, int y_win
)
531 assert(__glutMappedMenu
== NULL
);
532 grab
= XGrabPointer(__glutDisplay
, __glutRoot
, True
,
533 ButtonPressMask
| ButtonReleaseMask
,
534 GrabModeAsync
, GrabModeAsync
,
535 __glutRoot
, menuCursor
, CurrentTime
);
536 if (grab
!= GrabSuccess
) {
537 /* Somebody else has pointer grabbed, ignore menu
541 __glutMappedMenu
= menu
;
542 __glutMenuWindow
= window
;
543 __glutItemSelected
= NULL
;
544 if (__glutMenuStatusFunc
) {
546 __glutSetWindow(window
);
547 __glutMenuStatusFunc(GLUT_MENU_IN_USE
, x_win
, y_win
);
553 paintSubMenuArrow(Window win
, int x
, int y
)
558 p
[0].y
= p
[4].y
= y
- menuFont
->ascent
+ 1;
559 p
[1].x
= p
[0].x
+ MENU_ARROW_WIDTH
- 1;
560 p
[1].y
= p
[0].y
+ (menuFont
->ascent
/ 2) - 1;
564 p
[3].y
= p
[0].y
+ menuFont
->ascent
- 2;
565 XFillPolygon(__glutDisplay
, win
,
566 whiteGC
, p
, 4, Convex
, CoordModeOrigin
);
567 XDrawLines(__glutDisplay
, win
, blackGC
, p
, 5, CoordModeOrigin
);
571 paintMenuItem(GLUTmenuItem
* item
, int num
)
573 Window win
= item
->menu
->win
;
576 int subMenuExtension
;
578 if (item
->menu
->submenus
> 0) {
579 subMenuExtension
= MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
;
581 subMenuExtension
= 0;
583 if (item
->menu
->highlighted
== item
) {
588 y
= MENU_GAP
+ fontHeight
* num
- menuFont
->descent
;
589 XFillRectangle(__glutDisplay
, win
, gc
,
590 MENU_GAP
, y
- fontHeight
+ menuFont
->descent
,
591 item
->menu
->pixwidth
+ subMenuExtension
, fontHeight
);
592 XDrawString(__glutDisplay
, win
, blackGC
,
593 MENU_GAP
, y
, item
->label
, item
->len
);
594 if (item
->isTrigger
) {
595 paintSubMenuArrow(win
,
596 item
->menu
->pixwidth
+ MENU_ARROW_GAP
+ 1, y
);
601 paintMenu(GLUTmenu
* menu
)
605 int y
= MENU_GAP
+ fontHeight
* i
- menuFont
->descent
;
609 if (item
->menu
->highlighted
== item
) {
610 paintMenuItem(item
, i
);
612 /* Quick render of the menu item; assume background
613 already cleared to gray. */
614 XDrawString(__glutDisplay
, menu
->win
, blackGC
,
615 2, y
, item
->label
, item
->len
);
616 if (item
->isTrigger
) {
617 paintSubMenuArrow(menu
->win
,
618 menu
->pixwidth
+ MENU_ARROW_GAP
+ 1, y
);
627 static GLUTmenuItem
*
628 getMenuItem(GLUTmenu
* menu
, Window win
, int *which
)
633 if (menu
->searched
) {
634 __glutFatalError("submenu infinite loop detected");
636 menu
->searched
= True
;
640 if (item
->win
== win
) {
642 menu
->searched
= False
;
645 if (item
->isTrigger
) {
646 GLUTmenuItem
*subitem
;
648 subitem
= __glutGetMenuItem(__glutMenuList
[item
->value
],
651 menu
->searched
= False
;
658 menu
->searched
= False
;
663 getMenuItemIndex(GLUTmenuItem
* item
)
679 menu
= __glutMappedMenu
;
681 if (win
== menu
->win
) {
684 menu
= menu
->cascade
;
690 getMenuByNum(int menunum
)
692 if (menunum
< 1 || menunum
> menuListSize
) {
695 return __glutMenuList
[menunum
- 1];
699 getUnusedMenuSlot(void)
703 /* Look for allocated, unused slot. */
704 for (i
= 0; i
< menuListSize
; i
++) {
705 if (!__glutMenuList
[i
]) {
709 /* Allocate a new slot. */
711 if (__glutMenuList
) {
712 __glutMenuList
= (GLUTmenu
**)
713 realloc(__glutMenuList
, menuListSize
* sizeof(GLUTmenu
*));
715 /* XXX Some realloc's do not correctly perform a malloc
716 when asked to perform a realloc on a NULL pointer,
717 though the ANSI C library spec requires this. */
718 __glutMenuList
= (GLUTmenu
**) malloc(sizeof(GLUTmenu
*));
720 if (!__glutMenuList
) {
721 __glutFatalError("out of memory.");
723 __glutMenuList
[menuListSize
- 1] = NULL
;
724 return menuListSize
- 1;
728 __glutMenuModificationError(void)
730 /* XXX Remove the warning after GLUT 3.0. */
731 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
732 __glutFatalError("menu manipulation not allowed while menus in use.");
737 menuItemEnterOrLeave(GLUTmenuItem
* item
,
742 if (type
== EnterNotify
) {
743 GLUTmenuItem
*prevItem
= item
->menu
->highlighted
;
745 if (prevItem
&& prevItem
!= item
) {
746 /* If there's an already higlighted item in this menu
747 that is different from this one (we could be
748 re-entering an item with an already cascaded
749 submenu!), unhighlight the previous item. */
750 item
->menu
->highlighted
= NULL
;
751 paintMenuItem(prevItem
, getMenuItemIndex(prevItem
));
753 item
->menu
->highlighted
= item
;
754 __glutItemSelected
= item
;
755 if (item
->menu
->cascade
) {
756 if (!item
->isTrigger
) {
757 /* Entered a menu item that is not a submenu trigger,
758 so pop down the current submenu cascade of this
760 unmapMenu(item
->menu
->cascade
);
761 item
->menu
->cascade
= NULL
;
763 GLUTmenu
*submenu
= __glutMenuList
[item
->value
];
765 if (submenu
->anchor
== item
) {
766 /* We entered the submenu trigger for the submenu
767 that is already up, so don't take down the
771 /* Submenu already popped up for some other submenu
772 item of this menu; need to pop down that other
774 unmapMenu(item
->menu
->cascade
);
775 item
->menu
->cascade
= NULL
;
780 /* Make sure the menu item gets painted with
782 paintMenuItem(item
, num
);
784 /* If already up, should already be highlighted. */
787 /* LeaveNotify: Handle leaving a menu item... */
788 if (item
->menu
->cascade
&&
789 item
->menu
->cascade
->anchor
== item
) {
790 /* If there is a submenu casacaded from this item, do not
791 change the highlighting on this item upon leaving. */
793 /* Unhighlight this menu item. */
794 item
->menu
->highlighted
= NULL
;
795 paintMenuItem(item
, num
);
797 __glutItemSelected
= NULL
;
799 if (item
->isTrigger
) {
800 if (type
== EnterNotify
&& !alreadyUp
) {
801 GLUTmenu
*submenu
= __glutMenuList
[item
->value
];
804 item
->menu
->x
+ item
->menu
->pixwidth
+
805 MENU_ARROW_GAP
+ MENU_ARROW_WIDTH
+
806 MENU_GAP
+ MENU_BORDER
,
807 item
->menu
->y
+ fontHeight
* (num
- 1) + MENU_GAP
);
808 item
->menu
->cascade
= submenu
;
809 submenu
->anchor
= item
;
814 /* Installs callback functions for use by glut_event.c The point
815 of this is so that GLUT's menu code only gets linked into
816 GLUT binaries (assuming a static library) if the GLUT menu
819 installMenuCallbacks(void)
821 __glutMenuItemEnterOrLeave
= menuItemEnterOrLeave
;
822 __glutFinishMenu
= finishMenu
;
823 __glutPaintMenu
= paintMenu
;
824 __glutStartMenu
= startMenu
;
825 __glutGetMenuByNum
= getMenuByNum
;
826 __glutGetMenu
= getMenu
;
827 __glutGetMenuItem
= getMenuItem
;
831 glutCreateMenu(GLUTselectCB selectFunc
)
833 XSetWindowAttributes wa
;
837 if (__glutMappedMenu
) {
838 __glutMenuModificationError();
840 if (!__glutDisplay
) {
841 __glutOpenXConnection(NULL
);
844 installMenuCallbacks();
846 menuid
= getUnusedMenuSlot();
847 menu
= (GLUTmenu
*) malloc(sizeof(GLUTmenu
));
849 __glutFatalError("out of memory.");
854 menu
->managed
= False
;
855 menu
->searched
= False
;
857 menu
->select
= selectFunc
;
859 menu
->cascade
= NULL
;
860 menu
->highlighted
= NULL
;
863 wa
.override_redirect
= True
;
864 wa
.background_pixel
= menuGray
;
865 wa
.border_pixel
= menuBlack
;
866 wa
.colormap
= menuColormap
;
867 wa
.event_mask
= StructureNotifyMask
| ExposureMask
|
868 ButtonPressMask
| ButtonReleaseMask
|
869 EnterWindowMask
| LeaveWindowMask
;
870 /* Save unders really only enabled if useSaveUnders is set to
871 CWSaveUnder, ie. using Mesa 3D. See earlier comments. */
872 wa
.save_under
= True
;
873 menu
->win
= XCreateWindow(__glutDisplay
, __glutRoot
,
874 /* Real position determined when mapped. */
876 /* Real size will be determined when menu is manged. */
878 MENU_BORDER
, menuDepth
, InputOutput
, menuVisual
,
879 CWOverrideRedirect
| CWBackPixel
| CWBorderPixel
|
880 CWEventMask
| CWColormap
| useSaveUnders
,
882 menuGraphicsContextSetup(menu
->win
);
883 __glutMenuList
[menuid
] = menu
;
892 if (__glutCurrentMenu
) {
893 return __glutCurrentMenu
->id
+ 1;
900 glutSetMenu(int menuid
)
904 if (menuid
< 1 || menuid
> menuListSize
) {
905 __glutWarning("glutSetMenu attempted on bogus menu.");
908 menu
= __glutMenuList
[menuid
- 1];
910 __glutWarning("glutSetMenu attempted on bogus menu.");
918 __glutSetMenuItem(GLUTmenuItem
* item
, const char *label
,
919 int value
, Bool isTrigger
)
924 item
->label
= __glutStrdup(label
);
926 __glutFatalError("out of memory.");
928 item
->isTrigger
= isTrigger
;
929 item
->len
= (int) strlen(label
);
931 item
->pixwidth
= XTextWidth(menuFont
, label
, item
->len
) + 4;
932 if (item
->pixwidth
> menu
->pixwidth
) {
933 menu
->pixwidth
= item
->pixwidth
;
935 menu
->managed
= False
;
940 glutAddMenuEntry(const char *label
, int value
)
942 XSetWindowAttributes wa
;
945 if (__glutMappedMenu
) {
946 __glutMenuModificationError();
948 entry
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
950 __glutFatalError("out of memory.");
952 entry
->menu
= __glutCurrentMenu
;
953 __glutSetMenuItem(entry
, label
, value
, False
);
954 wa
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
955 entry
->win
= XCreateWindow(__glutDisplay
,
956 __glutCurrentMenu
->win
, MENU_GAP
,
957 __glutCurrentMenu
->num
* fontHeight
+ MENU_GAP
, /* x & y */
958 entry
->pixwidth
, fontHeight
, /* width & height */
959 0, CopyFromParent
, InputOnly
, CopyFromParent
,
961 XMapWindow(__glutDisplay
, entry
->win
);
962 __glutCurrentMenu
->num
++;
963 entry
->next
= __glutCurrentMenu
->list
;
964 __glutCurrentMenu
->list
= entry
;
968 glutAddSubMenu(const char *label
, int menu
)
970 XSetWindowAttributes wa
;
971 GLUTmenuItem
*submenu
;
973 if (__glutMappedMenu
) {
974 __glutMenuModificationError();
976 submenu
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
978 __glutFatalError("out of memory.");
980 __glutCurrentMenu
->submenus
++;
981 submenu
->menu
= __glutCurrentMenu
;
982 __glutSetMenuItem(submenu
, label
, /* base 0 */ menu
- 1, True
);
983 wa
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
984 submenu
->win
= XCreateWindow(__glutDisplay
,
985 __glutCurrentMenu
->win
, MENU_GAP
,
986 __glutCurrentMenu
->num
* fontHeight
+ MENU_GAP
, /* x & y */
987 submenu
->pixwidth
, fontHeight
, /* width & height */
988 0, CopyFromParent
, InputOnly
, CopyFromParent
,
990 XMapWindow(__glutDisplay
, submenu
->win
);
991 __glutCurrentMenu
->num
++;
992 submenu
->next
= __glutCurrentMenu
->list
;
993 __glutCurrentMenu
->list
= submenu
;
997 glutAttachMenu(int button
)
999 if (__glutMappedMenu
) {
1000 __glutMenuModificationError();
1002 installMenuCallbacks();
1003 if (__glutCurrentWindow
->menu
[button
] < 1) {
1004 __glutCurrentWindow
->buttonUses
++;
1006 __glutChangeWindowEventMask(
1007 ButtonPressMask
| ButtonReleaseMask
, True
);
1008 __glutCurrentWindow
->menu
[button
] = __glutCurrentMenu
->id
+ 1;