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;