~ubuntu-branches/ubuntu/precise/mesa/precise-updates

« back to all changes in this revision

Viewing changes to src/glut/glx/glut_menu.c

  • Committer: Bazaar Package Importer
  • Author(s): Christopher James Halse Rogers
  • Date: 2011-08-04 16:25:08 UTC
  • mfrom: (1.2.37 upstream)
  • Revision ID: james.westby@ubuntu.com-20110804162508-kujg82moxerjg1kk
Tags: 7.11-0ubuntu1
* Fake merge from Debian experimental, updating previous changelog entries.
  New upstream release fixes infrequent X crash (LP: #800778).
  Remaining Ubuntu changes:
 - debian/control
    + Drop lesstif-dev from Build-Depends; it's in Universe.
    + Comment out GLw libs since it depends on lesstif-dev.
    + Drop i686 swx11 libgl package.
    + Add libdrm-dev to mesa-common-dev Depends.
    + Drop libwayland-dev from Build-Depends; it's in Universe.
    + Update Breaks for Ubuntu versions
    + Enable llvm on armel as well as i386 and amd64
  - debian/rules
    + Use --disable-glw for swx11 targets too.
    + Don't enable motif for swx11 targets.
    + Use lzma compression for binary debs to save CD space.
    + Drop unloved mach64 driver.
    + Use --enable-shared-dricore to claw back CD space.
    + Enable llvmpipe software rasteriser.
    + Enable llvm on armel as well as i386 and amd64
  - debian/patches
    + 100_no_abi_tag.patch
    + 101_ubuntu_hidden_glname.patch
    + 103_savage-expose_fbmodes_with_nonzero_alpha.patch
  - rules, libgl1-mesa-{glx,dev,swx11,swx11-dev}.install.in,
    libgl1-mesa-{glx,swx11}.{postinst,prerm}.in, libgl1-mesa-dev.links.in:
    Install libGL.so* in /usr/lib/mesa to allow things to work with
    alternatives.
  - debian/not-installed:
    + Drop i686 files; we don't build 686-optimised packages in the first
      place.
  - debian/gbp.conf
    + Point at Ubuntu branch to make git-buildpackage less narky.
  - 113_fix_tls.diff: Fix crashes in unrelated code due to TLS usage.
  - debian/patches/111_export_searchdirs_in_dripc.diff:
    + Add drisearchdirs variable to dri.pc so the Xserver can pick up the
      alternate DRI driver dirs.
  - debian/patches/115_llvm_dynamic_linking.diff
    + Dynamically link DRI drivers to libllvm.  Saves ~6MiB per DRI driver.
  - debian/patches/116_use_shared_galliumcore.diff:
  - debian/libgl1-mesa-dri.install.in:
    + Link gallium DRI drivers against shared gallium routines to save CD
      space.
* debian/rules:
* debian/libgl1-mesa-dri-experimental.install.{i386,amd64}.in
  - Explicitly install i915g only when it has been built, matching what is
    done with r300g.
* debian/rules:
* debian/control:
* debian/libegl1-mesa{,-dev}.install.in:
* debian/libegl1-mesa.symbols:
  - Enable the Wayland EGL backend.
* debian/rules:
* debian/libegl1-mesa.{postinst,prerm,install}.in:
* debian/libegl1-mesa-dev.{install,links}.in:
* debian/libgles{1,2}-mesa.install.in:
* debian/libgles{1,2}-mesa-dev.links.in:
* debian/libopenvg1-mesa{,-dev}.install.in:
* debian/libopenvg1-mesa-dev.links.in:
  - Use alternatives for libEGL to match the handling of libGL.
    libEGL (and associated GL|ES and OpenVG libraries) now live in
    /usr/lib/$MULTIARCH/mesa-egl.  (LP: #812639)
* debian/patches/118_fix_24bpp_software_rendering.diff:
  - Cherry pick upstream patch from master fixing graphical corruption when
    using a 24bpp framebuffer and software rendering. (LP: #810339)
* debian/rules:
* debian/clean:
  - Generate xmlpool pot file and clean up other po files for
    pkgbinarymangler's benefit (LP: #410264).
* debian/patches/119_r600g_gnome_shell_rendering_fix.diff:
  - Cherry pick upstream commit fixing rendering corruption in gnome-shell
    (and therefore likely Unity as well).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
/* Copyright (c) Mark J. Kilgard, 1994, 1997. */
3
 
 
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. */
7
 
 
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. */
11
 
 
12
 
#ifdef __VMS
13
 
#include <GL/vms_x_fix.h>
14
 
#endif
15
 
 
16
 
#include <stdlib.h>
17
 
#include <string.h>
18
 
#include <stdio.h>
19
 
#include <errno.h>
20
 
#include <assert.h>
21
 
 
22
 
#include <unistd.h>
23
 
#include <X11/Xlib.h>
24
 
#include <X11/cursorfont.h>  /* for XC_arrow */
25
 
 
26
 
#include "glutint.h"
27
 
#include "layerutil.h"
28
 
 
29
 
void (CDECL *__glutMenuStatusFunc) (int, int, int);
30
 
GLUTmenuItem *__glutItemSelected;
31
 
GLUTmenu **__glutMenuList = NULL;
32
 
 
33
 
static int menuListSize = 0;
34
 
static XFontStruct *menuFont = NULL;
35
 
static Cursor menuCursor;
36
 
static Colormap menuColormap;
37
 
static Visual *menuVisual;
38
 
static int menuDepth;
39
 
static int fontHeight;
40
 
static GC blackGC, grayGC, whiteGC;
41
 
static unsigned long menuBlack, menuWhite, menuGray;
42
 
static unsigned long useSaveUnders;
43
 
 
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.  */
49
 
static void
50
 
noFaultXAllocColor(Display * dpy, Colormap cmap, int cmapSize,
51
 
  XColor * color)
52
 
{
53
 
  XColor *ctable, subColor;
54
 
  int i, bestmatch;
55
 
  double mindist;       /* 3*2^16^2 exceeds 32-bit long int
56
 
                           precision. */
57
 
 
58
 
  for (;;) {
59
 
    /* First try just using XAllocColor. */
60
 
    if (XAllocColor(dpy, cmap, color)) {
61
 
      return;
62
 
    }
63
 
 
64
 
    /* Retrieve color table entries. */
65
 
    /* XXX alloca canidate. */
66
 
    ctable = (XColor *) malloc(cmapSize * sizeof(XColor));
67
 
    for (i = 0; i < cmapSize; i++)
68
 
      ctable[i].pixel = i;
69
 
    XQueryColors(dpy, cmap, ctable, cmapSize);
70
 
 
71
 
    /* Find best match. */
72
 
    bestmatch = -1;
73
 
    mindist = 0.0;
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) {
80
 
        bestmatch = i;
81
 
        mindist = dist;
82
 
      }
83
 
    }
84
 
 
85
 
    /* Return result. */
86
 
    subColor.red = ctable[bestmatch].red;
87
 
    subColor.green = ctable[bestmatch].green;
88
 
    subColor.blue = ctable[bestmatch].blue;
89
 
    free(ctable);
90
 
    if (XAllocColor(dpy, cmap, &subColor)) {
91
 
      *color = subColor;
92
 
      return;
93
 
    }
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
97
 
       again... */
98
 
  }
99
 
}
100
 
 
101
 
static int
102
 
ifSunCreator(void)
103
 
{
104
 
  char *xvendor, *glvendor, *renderer;
105
 
  int isSunCreator = 0; /* Until proven that it is. */
106
 
  int savedDisplayMode = 0;
107
 
  char *savedDisplayString = 0;
108
 
  GLUTwindow *window;
109
 
 
110
 
#define VENDOR_SUN "Sun Microsystems"
111
 
#define RENDERER_CREATOR "Creator"
112
 
 
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)) {
119
 
 
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) {
125
 
      window = NULL;
126
 
    } else {
127
 
      savedDisplayMode = __glutDisplayMode;
128
 
      savedDisplayString = __glutDisplayString;
129
 
      __glutDisplayMode = GLUT_RGB | GLUT_SINGLE;
130
 
      __glutDisplayString = NULL;
131
 
      window = __glutCreateWindow(NULL, 0, 0, 1, 1, 0);
132
 
    }
133
 
 
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)) {
138
 
        isSunCreator = 1;
139
 
      }
140
 
    }
141
 
    /* Destroy the temporary window for glGetString if one
142
 
       needed to be created. */
143
 
    if (window) {
144
 
      __glutDestroyWindow(window, window);
145
 
      __glutDisplayMode = savedDisplayMode;
146
 
      __glutDisplayString = savedDisplayString;
147
 
    }
148
 
  }
149
 
  return isSunCreator;
150
 
}
151
 
 
152
 
static void
153
 
menuVisualSetup(void)
154
 
{
155
 
  XLayerVisualInfo template, *visual, *overlayVisuals;
156
 
  XColor color;
157
 
  Status status;
158
 
  Bool presumablyMesa;
159
 
  int layer, nVisuals, i, dummy;
160
 
  unsigned long *placeHolders = NULL;
161
 
  int numPlaceHolders = 0;
162
 
  Bool allocateHigh;
163
 
 
164
 
  allocateHigh = ifSunCreator();
165
 
 
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 
177
 
 
178
 
         using the default visual will probably minimize
179
 
         colormap flashing problems. Suggested by Thomas Roell
180
 
         (thomas@xig.com). */
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;
196
 
            useSaveUnders = 0;
197
 
            XFree(overlayVisuals);
198
 
            return;
199
 
          }
200
 
        }
201
 
      }
202
 
      for (i = 0; i < nVisuals; i++) {
203
 
        visual = &overlayVisuals[i];
204
 
        if (visual->vinfo.colormap_size >= 3) {
205
 
          if (allocateHigh) {
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. */
216
 
            }
217
 
          }
218
 
          menuColormap = XCreateColormap(__glutDisplay, __glutRoot,
219
 
            visual->vinfo.visual, AllocNone);
220
 
          if (placeHolders) {
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);
225
 
            if (!status) {
226
 
              XFreeColormap(__glutDisplay, menuColormap);
227
 
              free(placeHolders);
228
 
              placeHolders = NULL;
229
 
              continue;
230
 
            }
231
 
          }
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);
241
 
          if (!status) {
242
 
            XFreeColormap(__glutDisplay, menuColormap);
243
 
            if (placeHolders) {
244
 
              free(placeHolders);
245
 
              placeHolders = NULL;
246
 
            }
247
 
            continue;
248
 
          }
249
 
          menuGray = color.pixel;
250
 
          color.red = color.green = color.blue = 0x0000;
251
 
          status = XAllocColor(__glutDisplay,
252
 
            menuColormap, &color);
253
 
          if (!status) {
254
 
            XFreeColormap(__glutDisplay, menuColormap);
255
 
            if (placeHolders) {
256
 
              free(placeHolders);
257
 
              placeHolders = NULL;
258
 
            }
259
 
            continue;
260
 
          }
261
 
          menuBlack = color.pixel;
262
 
          color.red = color.green = color.blue = 0xffff;
263
 
          status = XAllocColor(__glutDisplay,
264
 
            menuColormap, &color);
265
 
          if (!status) {
266
 
            XFreeColormap(__glutDisplay, menuColormap);
267
 
            if (placeHolders) {
268
 
              free(placeHolders);
269
 
              placeHolders = NULL;
270
 
            }
271
 
            continue;
272
 
          }
273
 
          if (placeHolders) {
274
 
            /* Now free the placeholder cells. */
275
 
            XFreeColors(__glutDisplay, menuColormap,
276
 
              placeHolders, numPlaceHolders, 0);
277
 
            free(placeHolders);
278
 
            placeHolders = NULL;
279
 
          }
280
 
          menuWhite = color.pixel;
281
 
          menuVisual = visual->vinfo.visual;
282
 
          menuDepth = visual->vinfo.depth;
283
 
          /* If using overlays, do not request "save unders". */
284
 
          useSaveUnders = 0;
285
 
          XFree(overlayVisuals);
286
 
          return;
287
 
        }
288
 
      }
289
 
      XFree(overlayVisuals);
290
 
    }
291
 
  }
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;
302
 
 
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.
310
 
 
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.)
317
 
 
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.
322
 
 
323
 
     Look for the GLX extension.  If _not_ supported, we are
324
 
     presumably using Mesa so enable save unders. */
325
 
 
326
 
  presumablyMesa = !XQueryExtension(__glutDisplay, "GLX",
327
 
    &dummy, &dummy, &dummy);
328
 
 
329
 
  if (presumablyMesa) {
330
 
    useSaveUnders = CWSaveUnder;
331
 
  } else {
332
 
    useSaveUnders = 0;
333
 
  }
334
 
}
335
 
 
336
 
static void
337
 
menuSetup(void)
338
 
{
339
 
  if (menuFont) {
340
 
    /* MenuFont overload to indicate menu initalization. */
341
 
    return;
342
 
  }
343
 
  menuFont = XLoadQueryFont(__glutDisplay,
344
 
    "-*-helvetica-bold-o-normal--14-*-*-*-p-*-iso8859-1");
345
 
  if (!menuFont) {
346
 
    /* Try back up font. */
347
 
    menuFont = XLoadQueryFont(__glutDisplay, "fixed");
348
 
  }
349
 
  if (!menuFont) {
350
 
    __glutFatalError("could not load font.");
351
 
  }
352
 
  menuVisualSetup();
353
 
  fontHeight = menuFont->ascent + menuFont->descent;
354
 
  menuCursor = XCreateFontCursor(__glutDisplay, XC_arrow);
355
 
}
356
 
 
357
 
static void
358
 
menuGraphicsContextSetup(Window win)
359
 
{
360
 
  XGCValues gcvals;
361
 
 
362
 
  if (blackGC != None) {
363
 
    return;
364
 
  }
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);
373
 
}
374
 
 
375
 
void
376
 
__glutSetMenu(GLUTmenu * menu)
377
 
{
378
 
  __glutCurrentMenu = menu;
379
 
}
380
 
 
381
 
static void
382
 
unmapMenu(GLUTmenu * menu)
383
 
{
384
 
  if (menu->cascade) {
385
 
    unmapMenu(menu->cascade);
386
 
    menu->cascade = NULL;
387
 
  }
388
 
  menu->anchor = NULL;
389
 
  menu->highlighted = NULL;
390
 
  XUnmapWindow(__glutDisplay, menu->win);
391
 
}
392
 
 
393
 
static void
394
 
finishMenu(Window win, int x, int y)
395
 
{
396
 
  Window dummy;
397
 
  int rc;
398
 
 
399
 
  unmapMenu(__glutMappedMenu);
400
 
  XUngrabPointer(__glutDisplay, CurrentTime);
401
 
 
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);
409
 
  }
410
 
 
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
416
 
     flushed out. */
417
 
  XFlush(__glutDisplay);
418
 
 
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. */
429
 
    }
430
 
    __glutSetWindow(__glutMenuWindow);
431
 
    __glutSetMenu(__glutMappedMenu);
432
 
 
433
 
    /* Setting __glutMappedMenu to NULL permits operations that
434
 
       change menus or destroy the menu window again. */
435
 
    __glutMappedMenu = NULL;
436
 
 
437
 
    __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y);
438
 
  }
439
 
  /* Setting __glutMappedMenu to NULL permits operations that
440
 
     change menus or destroy the menu window again. */
441
 
  __glutMappedMenu = NULL;
442
 
 
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);
452
 
  }
453
 
  __glutMenuWindow = NULL;
454
 
}
455
 
 
456
 
#define MENU_BORDER 1
457
 
#define MENU_GAP 2
458
 
#define MENU_ARROW_GAP 6
459
 
#define MENU_ARROW_WIDTH 8
460
 
 
461
 
static void
462
 
mapMenu(GLUTmenu * menu, int x, int y)
463
 
{
464
 
  XWindowChanges changes;
465
 
  unsigned int mask;
466
 
  int subMenuExtension, num;
467
 
 
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;
472
 
  } else {
473
 
    subMenuExtension = 0;
474
 
  }
475
 
 
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) {
481
 
    GLUTmenuItem *item;
482
 
 
483
 
    item = menu->list;
484
 
    num = menu->num;
485
 
    while (item) {
486
 
      XWindowChanges itemupdate;
487
 
 
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);
493
 
      item = item->next;
494
 
      num--;
495
 
    }
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;
503
 
  }
504
 
  /* Make sure menu appears fully on screen. */
505
 
  if (y + menu->pixheight >= __glutScreenHeight) {
506
 
    changes.y = __glutScreenHeight - menu->pixheight;
507
 
  } else {
508
 
    changes.y = y;
509
 
  }
510
 
  if (x + menu->pixwidth + subMenuExtension >=
511
 
    __glutScreenWidth) {
512
 
    changes.x = __glutScreenWidth -
513
 
      menu->pixwidth + subMenuExtension;
514
 
  } else {
515
 
    changes.x = x;
516
 
  }
517
 
 
518
 
  /* Rember where the menu is placed so submenus can be
519
 
     properly placed relative to it. */
520
 
  menu->x = changes.x;
521
 
  menu->y = changes.y;
522
 
 
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
529
 
     these machines?? */
530
 
  XRaiseWindow(__glutDisplay, menu->win);
531
 
  XMapWindow(__glutDisplay, menu->win);
532
 
}
533
 
 
534
 
static void
535
 
startMenu(GLUTmenu * menu, GLUTwindow * window,
536
 
  int x, int y, int x_win, int y_win)
537
 
{
538
 
  int grab;
539
 
 
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
547
 
       activation. */
548
 
    return;
549
 
  }
550
 
  __glutMappedMenu = menu;
551
 
  __glutMenuWindow = window;
552
 
  __glutItemSelected = NULL;
553
 
  if (__glutMenuStatusFunc) {
554
 
    __glutSetMenu(menu);
555
 
    __glutSetWindow(window);
556
 
    __glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win);
557
 
  }
558
 
  mapMenu(menu, x, y);
559
 
}
560
 
 
561
 
static void
562
 
paintSubMenuArrow(Window win, int x, int y)
563
 
{
564
 
  XPoint p[5];
565
 
 
566
 
  p[0].x = p[4].x = x;
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;
570
 
  p[2].x = p[1].x;
571
 
  p[2].y = p[1].y + 1;
572
 
  p[3].x = p[0].x;
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);
577
 
}
578
 
 
579
 
static void
580
 
paintMenuItem(GLUTmenuItem * item, int num)
581
 
{
582
 
  Window win = item->menu->win;
583
 
  GC gc;
584
 
  int y;
585
 
  int subMenuExtension;
586
 
 
587
 
  if (item->menu->submenus > 0) {
588
 
    subMenuExtension = MENU_ARROW_GAP + MENU_ARROW_WIDTH;
589
 
  } else {
590
 
    subMenuExtension = 0;
591
 
  }
592
 
  if (item->menu->highlighted == item) {
593
 
    gc = whiteGC;
594
 
  } else {
595
 
    gc = grayGC;
596
 
  }
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);
606
 
  }
607
 
}
608
 
 
609
 
static void
610
 
paintMenu(GLUTmenu * menu)
611
 
{
612
 
  GLUTmenuItem *item;
613
 
  int i = menu->num;
614
 
  int y = MENU_GAP + fontHeight * i - menuFont->descent;
615
 
 
616
 
  item = menu->list;
617
 
  while (item) {
618
 
    if (item->menu->highlighted == item) {
619
 
      paintMenuItem(item, i);
620
 
    } else {
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);
628
 
      }
629
 
    }
630
 
    i--;
631
 
    y -= fontHeight;
632
 
    item = item->next;
633
 
  }
634
 
}
635
 
 
636
 
static GLUTmenuItem *
637
 
getMenuItem(GLUTmenu * menu, Window win, int *which)
638
 
{
639
 
  GLUTmenuItem *item;
640
 
  int i;
641
 
 
642
 
  if (menu->searched) {
643
 
    __glutFatalError("submenu infinite loop detected");
644
 
  }
645
 
  menu->searched = True;
646
 
  i = menu->num;
647
 
  item = menu->list;
648
 
  while (item) {
649
 
    if (item->win == win) {
650
 
      *which = i;
651
 
      menu->searched = False;
652
 
      return item;
653
 
    }
654
 
    if (item->isTrigger) {
655
 
      GLUTmenuItem *subitem;
656
 
 
657
 
      subitem = __glutGetMenuItem(__glutMenuList[item->value],
658
 
        win, which);
659
 
      if (subitem) {
660
 
        menu->searched = False;
661
 
        return subitem;
662
 
      }
663
 
    }
664
 
    i--;
665
 
    item = item->next;
666
 
  }
667
 
  menu->searched = False;
668
 
  return NULL;
669
 
}
670
 
 
671
 
static int
672
 
getMenuItemIndex(GLUTmenuItem * item)
673
 
{
674
 
  int count = 0;
675
 
 
676
 
  while (item) {
677
 
    count++;
678
 
    item = item->next;
679
 
  }
680
 
  return count;
681
 
}
682
 
 
683
 
static GLUTmenu *
684
 
getMenu(Window win)
685
 
{
686
 
  GLUTmenu *menu;
687
 
 
688
 
  menu = __glutMappedMenu;
689
 
  while (menu) {
690
 
    if (win == menu->win) {
691
 
      return menu;
692
 
    }
693
 
    menu = menu->cascade;
694
 
  }
695
 
  return NULL;
696
 
}
697
 
 
698
 
static GLUTmenu *
699
 
getMenuByNum(int menunum)
700
 
{
701
 
  if (menunum < 1 || menunum > menuListSize) {
702
 
    return NULL;
703
 
  }
704
 
  return __glutMenuList[menunum - 1];
705
 
}
706
 
 
707
 
static int
708
 
getUnusedMenuSlot(void)
709
 
{
710
 
  int i;
711
 
 
712
 
  /* Look for allocated, unused slot. */
713
 
  for (i = 0; i < menuListSize; i++) {
714
 
    if (!__glutMenuList[i]) {
715
 
      return i;
716
 
    }
717
 
  }
718
 
  /* Allocate a new slot. */
719
 
  menuListSize++;
720
 
  if (__glutMenuList) {
721
 
    __glutMenuList = (GLUTmenu **)
722
 
      realloc(__glutMenuList, menuListSize * sizeof(GLUTmenu *));
723
 
  } else {
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 *));
728
 
  }
729
 
  if (!__glutMenuList) {
730
 
    __glutFatalError("out of memory.");
731
 
  }
732
 
  __glutMenuList[menuListSize - 1] = NULL;
733
 
  return menuListSize - 1;
734
 
}
735
 
 
736
 
void
737
 
__glutMenuModificationError(void)
738
 
{
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.");
742
 
}
743
 
 
744
 
 
745
 
static void
746
 
menuItemEnterOrLeave(GLUTmenuItem * item,
747
 
  int num, int type)
748
 
{
749
 
  int alreadyUp = 0;
750
 
 
751
 
  if (type == EnterNotify) {
752
 
    GLUTmenuItem *prevItem = item->menu->highlighted;
753
 
 
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));
761
 
    }
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
768
 
           menu.  */
769
 
        unmapMenu(item->menu->cascade);
770
 
        item->menu->cascade = NULL;
771
 
      } else {
772
 
        GLUTmenu *submenu = __glutMenuList[item->value];
773
 
 
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
777
 
             submenu.  */
778
 
          alreadyUp = 1;
779
 
        } else {
780
 
          /* Submenu already popped up for some other submenu
781
 
             item of this menu; need to pop down that other
782
 
             submenu cascade.  */
783
 
          unmapMenu(item->menu->cascade);
784
 
          item->menu->cascade = NULL;
785
 
        }
786
 
      }
787
 
    }
788
 
    if (!alreadyUp) {
789
 
      /* Make sure the menu item gets painted with
790
 
         highlighting. */
791
 
      paintMenuItem(item, num);
792
 
    } else {
793
 
      /* If already up, should already be highlighted.  */
794
 
    }
795
 
  } else {
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. */
801
 
    } else {
802
 
      /* Unhighlight this menu item.  */
803
 
      item->menu->highlighted = NULL;
804
 
      paintMenuItem(item, num);
805
 
    }
806
 
    __glutItemSelected = NULL;
807
 
  }
808
 
  if (item->isTrigger) {
809
 
    if (type == EnterNotify && !alreadyUp) {
810
 
      GLUTmenu *submenu = __glutMenuList[item->value];
811
 
 
812
 
      mapMenu(submenu,
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;
819
 
    }
820
 
  }
821
 
}
822
 
 
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
826
 
   API is used. */
827
 
static void
828
 
installMenuCallbacks(void)
829
 
{
830
 
  __glutMenuItemEnterOrLeave = menuItemEnterOrLeave;
831
 
  __glutFinishMenu = finishMenu;
832
 
  __glutPaintMenu = paintMenu;
833
 
  __glutStartMenu = startMenu;
834
 
  __glutGetMenuByNum = getMenuByNum;
835
 
  __glutGetMenu = getMenu;
836
 
  __glutGetMenuItem = getMenuItem;
837
 
}
838
 
 
839
 
int GLUTAPIENTRY 
840
 
glutCreateMenu(GLUTselectCB selectFunc)
841
 
{
842
 
  XSetWindowAttributes wa;
843
 
  GLUTmenu *menu;
844
 
  int menuid;
845
 
 
846
 
  if (__glutMappedMenu) {
847
 
    __glutMenuModificationError();
848
 
  }
849
 
  if (!__glutDisplay) {
850
 
    __glutOpenXConnection(NULL);
851
 
  }
852
 
 
853
 
  installMenuCallbacks();
854
 
 
855
 
  menuid = getUnusedMenuSlot();
856
 
  menu = (GLUTmenu *) malloc(sizeof(GLUTmenu));
857
 
  if (!menu) {
858
 
    __glutFatalError("out of memory.");
859
 
  }
860
 
  menu->id = menuid;
861
 
  menu->num = 0;
862
 
  menu->submenus = 0;
863
 
  menu->managed = False;
864
 
  menu->searched = False;
865
 
  menu->pixwidth = 0;
866
 
  menu->select = selectFunc;
867
 
  menu->list = NULL;
868
 
  menu->cascade = NULL;
869
 
  menu->highlighted = NULL;
870
 
  menu->anchor = NULL;
871
 
  menuSetup();
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. */
884
 
    0, 0,
885
 
  /* Real size will be determined when menu is manged. */
886
 
    1, 1,
887
 
    MENU_BORDER, menuDepth, InputOutput, menuVisual,
888
 
    CWOverrideRedirect | CWBackPixel | CWBorderPixel |
889
 
    CWEventMask | CWColormap | useSaveUnders,
890
 
    &wa);
891
 
  menuGraphicsContextSetup(menu->win);
892
 
  __glutMenuList[menuid] = menu;
893
 
  __glutSetMenu(menu);
894
 
  return menuid + 1;
895
 
}
896
 
 
897
 
/* CENTRY */
898
 
int GLUTAPIENTRY 
899
 
glutGetMenu(void)
900
 
{
901
 
  if (__glutCurrentMenu) {
902
 
    return __glutCurrentMenu->id + 1;
903
 
  } else {
904
 
    return 0;
905
 
  }
906
 
}
907
 
 
908
 
void GLUTAPIENTRY 
909
 
glutSetMenu(int menuid)
910
 
{
911
 
  GLUTmenu *menu;
912
 
 
913
 
  if (menuid < 1 || menuid > menuListSize) {
914
 
    __glutWarning("glutSetMenu attempted on bogus menu.");
915
 
    return;
916
 
  }
917
 
  menu = __glutMenuList[menuid - 1];
918
 
  if (!menu) {
919
 
    __glutWarning("glutSetMenu attempted on bogus menu.");
920
 
    return;
921
 
  }
922
 
  __glutSetMenu(menu);
923
 
}
924
 
/* ENDCENTRY */
925
 
 
926
 
void
927
 
__glutSetMenuItem(GLUTmenuItem * item, const char *label,
928
 
  int value, Bool isTrigger)
929
 
{
930
 
  GLUTmenu *menu;
931
 
 
932
 
  menu = item->menu;
933
 
  item->label = __glutStrdup(label);
934
 
  if (!item->label) {
935
 
    __glutFatalError("out of memory.");
936
 
  }
937
 
  item->isTrigger = isTrigger;
938
 
  item->len = (int) strlen(label);
939
 
  item->value = value;
940
 
  item->pixwidth = XTextWidth(menuFont, label, item->len) + 4;
941
 
  if (item->pixwidth > menu->pixwidth) {
942
 
    menu->pixwidth = item->pixwidth;
943
 
  }
944
 
  menu->managed = False;
945
 
}
946
 
 
947
 
/* CENTRY */
948
 
void GLUTAPIENTRY 
949
 
glutAddMenuEntry(const char *label, int value)
950
 
{
951
 
  XSetWindowAttributes wa;
952
 
  GLUTmenuItem *entry;
953
 
 
954
 
  if (__glutMappedMenu) {
955
 
    __glutMenuModificationError();
956
 
  }
957
 
  entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
958
 
  if (!entry) {
959
 
    __glutFatalError("out of memory.");
960
 
  }
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,
969
 
    CWEventMask, &wa);
970
 
  XMapWindow(__glutDisplay, entry->win);
971
 
  __glutCurrentMenu->num++;
972
 
  entry->next = __glutCurrentMenu->list;
973
 
  __glutCurrentMenu->list = entry;
974
 
}
975
 
 
976
 
void GLUTAPIENTRY 
977
 
glutAddSubMenu(const char *label, int menu)
978
 
{
979
 
  XSetWindowAttributes wa;
980
 
  GLUTmenuItem *submenu;
981
 
 
982
 
  if (__glutMappedMenu) {
983
 
    __glutMenuModificationError();
984
 
  }
985
 
  submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
986
 
  if (!submenu) {
987
 
    __glutFatalError("out of memory.");
988
 
  }
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,
998
 
    CWEventMask, &wa);
999
 
  XMapWindow(__glutDisplay, submenu->win);
1000
 
  __glutCurrentMenu->num++;
1001
 
  submenu->next = __glutCurrentMenu->list;
1002
 
  __glutCurrentMenu->list = submenu;
1003
 
}
1004
 
 
1005
 
void GLUTAPIENTRY 
1006
 
glutAttachMenu(int button)
1007
 
{
1008
 
  /* if button >= GLUT_MAX_MENUS, we'll go out of array bounds below */
1009
 
  if (button >= GLUT_MAX_MENUS) {
1010
 
    return;
1011
 
  }
1012
 
  if (__glutMappedMenu) {
1013
 
    __glutMenuModificationError();
1014
 
  }
1015
 
  installMenuCallbacks();
1016
 
  if (__glutCurrentWindow->menu[button] < 1) {
1017
 
    __glutCurrentWindow->buttonUses++;
1018
 
  }
1019
 
  __glutChangeWindowEventMask(
1020
 
    ButtonPressMask | ButtonReleaseMask, True);
1021
 
  __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
1022
 
}
1023
 
/* ENDCENTRY */