home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume3 / clover / part01 / clover.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-10  |  15.0 KB  |  626 lines

  1. /* $Header$ */
  2.  
  3. /* 
  4.  * clover.c - Gosper's LispM "smoking clover" color table hack
  5.  * 
  6.  * Author:    Christopher A. Kent
  7.  *         Western Research Laboratory
  8.  *         Digital Equipment Corporation
  9.  * Date:    Wed Feb 22 1989
  10.  */
  11.  
  12. /*
  13.  * $Log$
  14.  */
  15.  
  16. static char rcs_ident[] = "$Header$";
  17.  
  18. #include <stdio.h>
  19.  
  20. #include <X11/Xos.h>
  21. #include <X11/Xlib.h>
  22. #include <X11/Xutil.h>
  23. #include <X11/Xatom.h>
  24. #include <X11/Xresource.h>
  25. #include <X11/StringDefs.h>
  26. #include <X11/Intrinsic.h>
  27. #include <X11/cursorfont.h>
  28.  
  29. #define    NCOLORS    16
  30. #define    R    5432
  31. #define S    4321
  32.  
  33. #define    MIN(x, y)    ((x) < (y))?(x):(y)
  34. #define    MAX(x, y)    ((x) > (y))?(x):(y)
  35. #define    CEIL(a, b)    ((a)+(b)-1)/(b)
  36. #define    FLOOR(a, b)    CEIL((a)-(b), (b))
  37.  
  38. Display        *dpy;            /* the display */
  39. Window        w;            /* the window */
  40. int        screen;            /* the screen to use */
  41. Colormap    colorMap;        /* private Colormap resource */
  42. XColor        *colors;        /* color sets */
  43. int        cmap = 0;        /* which color set to use now */
  44. Visual        *v;            /* the appropriate visual */
  45. int        nColors;        /* how many colors (per v) */
  46. Pixel        fg_pixel;        /* specified fg color (ignored) */
  47. Pixel        bg_pixel;        /* specified bg color */
  48. Pixel        borderColor;        /* ... border color */
  49. Cardinal    borderWidth;        /* ... border width */
  50. String        geometry;        /* ... geometry */
  51. int        posX, posY;        /* ... position */
  52. int        width, height;        /* ... sizes */
  53. int        one = 1;
  54. int        radius = R;        /* ... 'radius' of clover */
  55. int        speed = S;        /* ... initial speed */
  56. int        maxColors = NCOLORS;    /* ... maximum colors to use */
  57.  
  58. char        *malloc();
  59.  
  60. #define    XtNgeometry    "geometry"
  61. #define    XtCGeometry    "Geometry"
  62. #define    XtNcolors    "colors"
  63. #define    XtCColors    "Colors"
  64. #define    XtNradius    "radius"
  65. #define    XtCRadius    "Radius"
  66. #define    XtNspeed    "speed"
  67. #define    XtCSpeed    "Speed"
  68.  
  69. static XrmOptionDescRec opTable[] = {
  70. {"-geometry",   "*geometry",    XrmoptionSepArg,  (caddr_t) NULL},
  71. {"-colors",    "*colors",    XrmoptionSepArg,  (caddr_t) NULL}, 
  72. {"-radius",    "*radius",    XrmoptionSepArg,  (caddr_t) NULL},
  73. {"-speed",    "*speed",    XrmoptionSepArg,  (caddr_t) NULL}, 
  74. };
  75.  
  76. static XtResource resources[] = {    /* don't really need some of these */
  77.         {XtNforeground,  XtCForeground,  XtRPixel,  sizeof(Pixel), 
  78.          (Cardinal)&fg_pixel,  XtRString,  "black"}, 
  79.         {XtNbackground,  XtCBackground,  XtRPixel,  sizeof(Pixel), 
  80.          (Cardinal)&bg_pixel,  XtRString,  "white"}, 
  81.         {XtNborderWidth,  XtCBorderWidth,  XtRInt,  sizeof(int), 
  82.          (Cardinal)&borderWidth,  XtRInt,  (caddr_t) &one}, 
  83.         {XtNborder,  XtCBorderColor,  XtRPixel,  sizeof(Pixel), 
  84.          (Cardinal)&borderColor,  XtRString,  "black"},
  85.         {XtNgeometry,  XtCGeometry,  XtRString,  sizeof(char *), 
  86.          (Cardinal)&geometry,  XtRString,  (caddr_t) NULL}, 
  87.         {XtNcolors,  XtCColors,  XtRInt,  sizeof(int), 
  88.          (Cardinal)&maxColors,  XtRInt,  (caddr_t) &maxColors}, 
  89.         {XtNradius,  XtCRadius,  XtRInt,  sizeof(int), 
  90.          (Cardinal)&radius,  XtRInt,  (caddr_t) &radius}, 
  91.         {XtNspeed,  XtCSpeed,  XtRInt,  sizeof(int), 
  92.          (Cardinal)&speed,  XtRInt,  (caddr_t) &speed}, 
  93. };
  94.  
  95. main(argc, argv)
  96. char    **argv;
  97. {
  98.     Widget        widg;
  99.     XEvent        xev;
  100.     XSizeHints    hint;
  101.     char        text[10];
  102.     int        i;
  103.     KeySym        key;
  104.     Pixmap        p, cloverPixmap();
  105.     XWindowAttributes    attr;
  106.     Bool        mapped = False;
  107.     Cursor        spiral;
  108.     int        cycleDelay, cycleDecrement = 0, currentSpeed;
  109.  
  110.     /*
  111.      * We cheat here by using the Toolkit to do the initialization work.
  112.      * We just ignore the top-level widget that gets created.
  113.      */
  114.  
  115.     widg = XtInitialize("clover", "clover", opTable, XtNumber(opTable), 
  116.             &argc, argv);
  117.     dpy = XtDisplay(widg);
  118.     screen = DefaultScreen(dpy);
  119.  
  120.     XtGetApplicationResources(widg, (caddr_t) NULL, resources, 
  121.                 XtNumber(resources), NULL, (Cardinal) 0);
  122.  
  123.     posX = posY = -1;
  124.     width = height = 0;
  125.     if (geometry) {
  126.         int    mask, gx, gy, gw, gh;
  127.  
  128.         mask = XParseGeometry(geometry, &gx, &gy, &gw, &gh);
  129.         if (mask & WidthValue)
  130.             width = gw;
  131.         if (mask & HeightValue)
  132.             height = gh;
  133.         if (mask & XValue)
  134.             if (mask & XNegative)
  135.                 posX = DisplayWidth(dpy, screen) - 
  136.                     width + posX;
  137.             else
  138.                 posX = gx;
  139.         if (mask & YValue)
  140.             if (mask & YNegative)
  141.                 posY = DisplayHeight(dpy, screen) -
  142.                     height + posY;
  143.             else
  144.                 posY = gy;
  145.     }
  146.  
  147.     hint.width = width = width ? width : DisplayWidth(dpy, screen);
  148.     hint.height = height = height ? height : DisplayHeight(dpy, screen);
  149.     hint.x = posX >= 0 ? posX : (DisplayWidth(dpy, screen) - width)/2;
  150.     hint.y = posY >= 0 ? posY : (DisplayHeight(dpy, screen) - height)/2;
  151.     hint.flags = PPosition | PSize;
  152.  
  153.     w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 
  154.             hint.x, hint.y, 
  155.             hint.width, hint.height, 
  156.             borderWidth, 
  157.             BlackPixel(dpy, screen), 
  158.             WhitePixel(dpy, screen));
  159.     XSetStandardProperties(dpy, w, "Smoking Clover", "clover", None, 
  160.                 argv, argc, &hint);
  161.     XSelectInput(dpy, w, 
  162.         ButtonPressMask | ExposureMask | 
  163.         KeyPressMask | StructureNotifyMask);
  164.  
  165.     buildColormaps();
  166.  
  167.     p = cloverPixmap(radius);
  168.     
  169.     spiral = XCreateFontCursor(dpy, XC_box_spiral);
  170.     
  171.     XSynchronize(dpy, True);
  172.     XMapRaised(dpy, w);
  173.  
  174.     cycleDelay = speed;
  175.     currentSpeed = speed;
  176.     while(1) {
  177.         if (mapped && !XPending(dpy)) {
  178.             if (--cycleDelay <= 0) {
  179.                 cycle();
  180.                 cycleDelay = currentSpeed;
  181.                 if (++cycleDecrement%16 == 0)
  182.                     currentSpeed >>= 1;
  183.             }
  184.             continue;
  185.         }
  186.  
  187.         XNextEvent(dpy, &xev);
  188.         switch(xev.type) {
  189.         case Expose:
  190.         case MapNotify:
  191.             mapped = True;
  192.             break;
  193.  
  194.         case ConfigureNotify:
  195.             XSynchronize(dpy, True);
  196.             XDefineCursor(dpy, w, spiral);
  197.             XGetWindowAttributes(dpy, w, &attr);
  198.             width = attr.width;
  199.             height = attr.height;
  200.             XFreePixmap(dpy, p);
  201.             p = cloverPixmap(radius);
  202.             XClearWindow(dpy, w);
  203.             XUndefineCursor(dpy, w);
  204.             XSynchronize(dpy, False);
  205.             break;
  206.             
  207.         case UnmapNotify:
  208.             mapped = False;
  209.             break;
  210.  
  211.         case MappingNotify:
  212.             XRefreshKeyboardMapping(&xev);
  213.             break;
  214.  
  215.         case ButtonPress:
  216.             currentSpeed = speed;
  217.             break;
  218.  
  219.         case KeyPress:
  220.             i = XLookupString(&xev, text, 10, &key, 0);
  221.             if (i == 1 && text[0] == 'q')
  222.                 exit(0);
  223.             break;
  224.         }
  225.     }
  226. }
  227.  
  228. /*
  229.  * Decide how many colors to use (depends on the available Visuals).  Create a
  230.  * private Colormap and that many sets of color cells.  Load the first set with
  231.  * a random selection of colors, and the rest so that the colors slide through
  232.  * the pixel values.
  233.  *
  234.  * It might seem more straightforward to allocate nColors Colormaps, load them
  235.  * and explicitly install them, but well-behaved clients should not install
  236.  * Colormaps explicitly.  Rather, they should change the Colormap attribute and
  237.  * wait for the window manager to do the installation.  This slows things down
  238.  * a lot, so we do explict XStoreColors calls to cycle through the color sets.
  239.  *
  240.  */
  241.  
  242. /*
  243.  * There are nColors sets of (nColors+1) colors each.  Slots [0..nColors-1]
  244.  * rotate in subsequent sets; slot nColors is always the background color.
  245.  */
  246.  
  247. #define    COLOR(a, b)    colors[((a)*(nColors+1))+(b)]
  248.  
  249. buildColormaps()
  250. {
  251.     int        i, j;
  252.     XColor        *c1, *c2, tempColor, bgColor;
  253.     Visual        *findVisual();
  254.  
  255.     v = findVisual();
  256.     if (v == NULL) {
  257.         printf("Sorry, clover needs a writable colormap to run\n");
  258.         exit(0);
  259.     }
  260.  
  261.     /*
  262.      * Find out the color values of the specified background...
  263.      */
  264.  
  265.     bgColor.pixel = bg_pixel;
  266.     XQueryColor(dpy, DefaultColormap(dpy, screen), &bgColor);
  267.     
  268.     colors = (XColor *) malloc((nColors+1) * nColors * sizeof(XColor));
  269.  
  270.     colorMap = XCreateColormap(dpy, w, v, AllocAll);
  271.  
  272.     /*
  273.      * allocate random colors into first color map, with bg at the end
  274.      */
  275.     
  276.     for (i = 0; i < nColors; i++) {
  277.         c1 = &COLOR(0, i);
  278.         c1->pixel = i;
  279.         if (i == 0) {
  280.             c1->red = 0;
  281.             c1->green = (unsigned short) -65535/6;
  282.             c1->blue = 65535/6;
  283.         } else {
  284.             c2 = &COLOR(0, i-1);
  285.             c1->red = c2->red - random()/6;
  286.             c1->green = c2->green - random()/6;
  287.             c1->blue = c2->blue - random()/6;
  288.         }
  289.         c1->flags = DoRed | DoGreen | DoBlue;
  290.  
  291.         for (j = 1; j < nColors; j++)
  292.             COLOR(j, i).pixel = i;
  293.     }
  294.     c1 = &COLOR(0, nColors);
  295.     c1->pixel = nColors;
  296.     c1->red = bgColor.red;
  297.     c1->green = bgColor.green;
  298.     c1->blue = bgColor.blue;
  299.     c1->flags = DoRed | DoGreen | DoBlue;
  300.     XStoreColors(dpy, colorMap, colors, nColors+1);
  301.  
  302.  
  303.     /* rotate colors (but not pixels) through other colors */
  304.     for (i = 1; i < nColors; i++) {
  305.         tempColor = COLOR(i-1, 0);
  306.         for (j = 0; j < nColors-1; j++) {
  307.             c1 = &COLOR(i, j);
  308.             c2 = &COLOR(i-1, j+1);
  309.             c1->red = c2->red;
  310.             c1->green = c2->green;
  311.             c1->blue = c2->blue;
  312.             c1->flags = c2->flags;
  313.         }
  314.         c1 = &COLOR(i, nColors-1);
  315.         c2 = &tempColor;
  316.         c1->red = c2->red;
  317.         c1->green = c2->green;
  318.         c1->blue = c2->blue;
  319.         c1->flags = c2->flags;
  320.  
  321.         COLOR(i, nColors) = COLOR(i-1, nColors);
  322.     }
  323. }
  324.  
  325. /*
  326.  * Create the image in memory. Assumes several things have already been set up
  327.  * in global variables.
  328.  */
  329.  
  330. char    *bits;
  331. int    trace = 0;
  332. #define    getPixel(x, y)        bits[(y)*width+(x)]
  333. #define    putPixel(x, y, v)    bits[(y)*width+(x)] = (v)%nColors;
  334. xputPixel(x, y, v)
  335. {
  336.     printf("%d, %d -> %d/%d\n", x, y, v, v%nColors);
  337.     xputPixel(x, y, v);
  338. }
  339.  
  340. Pixmap
  341. cloverPixmap(r)
  342. {
  343.     XImage    *im, *simpleImage(), *cloverImage();
  344.     Pixmap    p;
  345.     XSetWindowAttributes    attr;
  346.  
  347.     im = cloverImage(r);
  348.     p = XCreatePixmap(dpy, w, width, height, XDefaultDepth(dpy, screen));
  349.     XPutImage(dpy, p, DefaultGC(dpy, screen), im,
  350.                     0, 0, 0, 0, width, height);
  351.     XDestroyImage(im);
  352.  
  353.     attr.background_pixmap = p;
  354.     attr.colormap = colorMap;
  355.     attr.bit_gravity = CenterGravity;
  356.     XChangeWindowAttributes(dpy, w, 
  357.             CWBackPixmap | CWColormap | CWBitGravity,
  358.             &attr);
  359.  
  360.     return p;
  361. }
  362.  
  363. XImage *
  364. simpleImage()
  365. {
  366.     int    i, j;
  367.     XImage    *im;
  368.  
  369.     bits = malloc(width * height);
  370.  
  371.     for (i = 0; i < height; i++)
  372.         for (j = 0; j< width; j++)
  373.             bits[i * width + j] = i % nColors;
  374.  
  375.     im = XCreateImage(dpy, v, 
  376.             XDefaultDepth(dpy, screen), 
  377.             ZPixmap, 0, bits, width, height, 0, width);
  378.     free(bits);
  379.     return im;
  380. }
  381.  
  382. /*
  383.  * Basically the algorithm is to draw a series of Bresenham lines from the
  384.  * center.  The "interference pattern" is built by incrementing the pixel value
  385.  * of (x,y) every time it's touched; the resulting pattern is a product of the
  386.  * vagaries of integer arithmetic.
  387.  */
  388.  
  389. XImage *
  390. cloverImage(r)
  391. {
  392.     XImage    *im;
  393.     int    maxX, maxY, midX, midY, x, f, y;
  394.     int    v, yy, x1, y1;
  395.     int    i, o;
  396.     char    *b;
  397.  
  398.     bits = malloc(width * height);
  399.     if (bits == NULL) {
  400.         perror("No memory");
  401.         exit(-1);
  402.     }
  403.  
  404.     maxX = width - 1;
  405.     maxY = height - 1;
  406.     midX = maxX / 2;
  407.     midY = maxY / 2;
  408.  
  409.     for (y = 0; y < height; y++) {
  410.         b = &bits[y*width];
  411.         for (x = 0; x < width; x++)
  412.             *b++ = nColors;        /* fill in background */
  413.     }
  414.  
  415.     /*
  416.      * Fill in the first semi-quadrant.
  417.      */
  418.  
  419.     x = r;
  420.     f = 0;
  421.     for (y = 0; y < x; y++) {
  422.         if (f > x) {
  423.             x--;
  424.             f = f-x - (x-1);
  425.         }
  426.         clipLine(midX, midY, x+midX, y+midY, 0, 0, maxX, maxY);
  427.         f = f+y + y+1;
  428.     }
  429.  
  430.     /*
  431.      * Copy to the other seven, adjusting the horizontal and diagonal.
  432.      */
  433.  
  434.     for (x = midX; x < maxX; x++) {
  435. /*        putPixel(x, midY, (getPixel(x, midY) << 1) - 1);*/
  436.         if (x - midX + midY <= maxY)
  437.             putPixel(x, x-midX+midY, 
  438.                 (getPixel(x, x-midX+midY) << 1) - 1);
  439.         yy = MIN(maxY, x + midY - midX);
  440.         for (y = midY; y <= yy; y++) {
  441.             v = getPixel(x, y);
  442.             x1 = x;
  443.             y1 = y;
  444.             for (i = 0; i < 4; i++) {
  445.                 if ((y1 < maxY) && (y1 > 0)) {
  446.                     putPixel(midX + midX - x1, y1, v);
  447.                     putPixel(x1, y1, v);
  448.                 }
  449.                 o = x1;
  450.                 x1 = midX + midY - y1;
  451.                 y1 = midY + o - midX;
  452.             }
  453.         }
  454.     }
  455.  
  456.     im = XCreateImage(dpy, v, 
  457.             XDefaultDepth(dpy, screen), 
  458.             ZPixmap, 0, bits, width, height, 0, width);
  459.     free(bits);
  460.     return im;
  461. }
  462.  
  463. /*
  464.  * (xe, ye) and (xf, yf) are the corners of a rectangle to clip a line to.
  465.  * (x0, y0) and (xn, yn) are the endpoints of the line to clip.
  466.  * The function argument that's being computed is the semi-quadrant;
  467.  *  dx and dy are used to determine whether we're above or below the diagonal,
  468.  *  since (x0, y0) is always the midpoint of the pattern.
  469.  * (The LispM has the origin at lower left, instead of upper left, so
  470.  * the numbers don't correspond to the normal Cartesian plane quadrants.)
  471.  *
  472.  * This routine is very general, but the calling code only builds lines in the
  473.  * first semi-quadrant and then copies them everywhere else.
  474.  */
  475.  
  476. clipLine(x0, y0, xn, yn, xe, ye, xf, yf)
  477. {
  478.     int    dx, dy;
  479.  
  480.     dx = abs(xn - x0);
  481.     dy = abs(yn - y0);
  482.  
  483.     if (xn > x0) {                /* moving right */
  484.         if (yn >= y0) {            /* moving up */
  485.             if (dx > dy)        /* below diagonal */
  486.                 line(0, x0, y0, dx, dy, xe, ye, xf, yf);
  487.             else
  488.                 line(1, y0, x0, dy, dx, ye, xe, yf, xf);
  489.         } else {
  490.             if (dx > dy)
  491.                 line(7, x0, -y0, dx, dy, xe, -yf, xf, -ye);
  492.             else
  493.                 line(6, -y0, x0, dy, dx, -yf, xe, -ye, xf);
  494.         }
  495.     } else {
  496.         if (yn >= y0) {
  497.             if (dx > dy)
  498.                 line(3, -x0, y0, dx, dy, -xf, ye, -xe, yf);
  499.             else
  500.                 line(2, y0, -x0, dy, dx, ye, -xf, yf, -xe);
  501.         } else {
  502.             if (dx > dy)
  503.                 line(4, -x0, -y0, dx, dy, -xf, -yf, -xe, -ye);
  504.             else
  505.                 line(5, -y0, -x0, dy, dx, -yf, -xf, -ye, -xe);
  506.         }
  507.     }
  508. }
  509.  
  510. #define    plot(x, y)    putPixel((x), (y), getPixel((x), (y))+1)
  511.  
  512. /*
  513.  * Clip symmetric segment (x0, y0) thru (xn, yn) to the rectangle 
  514.  * (xe, ye) < (xf, yf).
  515.  *
  516.  * The original says:
  517.  *
  518.  * "This routine incorrectly assumes that the subsegment starts prior to the
  519.  * midpoint of the supersegment.  The 'divide for nearest integer' (i.e.,
  520.  * divide for remainder of minimum magnitude), which is simulated by the FLOOR
  521.  * and CEIL of num and (dx <<1), always rounds up on the half integer case, but
  522.  * should round down (for symmetry) if startup is in 2nd half. It would be
  523.  * nice to have these other flavors of divide.'
  524.  */
  525.  
  526. line(fun, x0, y0, dx, dy, xe, ye, xf, yf)
  527. {
  528.     int    x, num, lx;
  529.     int    xx, y, x00, f;
  530.     int    x11;
  531.  
  532.     x = MAX(x0,  MAX(xe, 
  533.             (dy == 0)? xe :
  534.                    x0 + CEIL(dx * (((ye - y0)<<1) - 1), 
  535.                         (dy << 1))));
  536.     num = dx + 2*dy*(x - x0);
  537.     lx = MIN(xf, (dy == 0) ? xf :
  538.                 x0 + CEIL(dx * (((yf - y0)<<1) - 1), 
  539.                         (dy << 1)));
  540.     xx = MIN(lx, x0 + (dx>>1));
  541.     y = y0 + FLOOR(num, (dx<<1));
  542.     f = (FLOOR(num, (dx<<1)) - dx) >> 1;
  543.  
  544.     for (x00 = x; x00 < xx; x00++,f+=dy) {
  545.         if (f+f > dx) {
  546.             f -= dx;
  547.             y++;
  548.         }
  549.         switch(fun) {
  550.         case 0:    plot(x00, y);    break;
  551.         case 1:    plot(y, x00);    break;
  552.         case 2:    plot(-y, x00);    break;
  553.         case 3:    plot(-x00, y);    break;
  554.         case 4:    plot(-x00, -y);    break;
  555.         case 5:    plot(-y, -x00);    break;
  556.         case 6:    plot(y, -x00);    break;
  557.         case 7:    plot(x00, -y);    break;
  558.         }
  559.     }
  560.  
  561.     for (x11 = x00; x11 < lx; x11++, f+=dy) {
  562.         if (f + f > dx) {
  563.             f -= dx;
  564.             y++;
  565.         }
  566.         switch(fun) {
  567.         case 0:    plot(x11, y);    break;
  568.         case 1:    plot(y, x11);    break;
  569.         case 2:    plot(-y, x11);    break;
  570.         case 3:    plot(-x11, y);    break;
  571.         case 4:    plot(-x11, -y);    break;
  572.         case 5:    plot(-y, -x11);    break;
  573.         case 6:    plot(y, -x11);    break;
  574.         case 7:    plot(x11, -y);    break;
  575.         }
  576.     }
  577. }
  578.  
  579. /*
  580.  * Install the next set of colors in the cycle.
  581.  */
  582.  
  583. cycle()
  584. {
  585.     cmap = ++cmap % nColors;
  586.     XStoreColors(dpy, colorMap, &colors[cmap * (nColors+1)], nColors+1);
  587. }
  588.  
  589. /*
  590.  * Find an appropriate visual (and set the screen and nColors as a side effect)
  591.  * to run on.
  592.  */
  593.  
  594. int    classes[] = {
  595.     PseudoColor,
  596.     DirectColor,
  597.     GrayScale, 
  598.     0
  599. };
  600.  
  601. Visual *
  602. findVisual()
  603. {
  604.     int        howMany, i, max, *class;
  605.     XVisualInfo    *vip, vTemplate;
  606.  
  607.     for (class = classes; *class; class++) {
  608.         vTemplate.class = *class;
  609.         vip = XGetVisualInfo(dpy, VisualClassMask, 
  610.                     &vTemplate, &howMany);
  611.         if (vip) {
  612.             max = 0;
  613.             for (i = 0; i < howMany; i++) {
  614.                 if (vip->colormap_size > max)
  615.                     v = vip->visual;
  616.                     max = vip->colormap_size;
  617.             }
  618.             screen = vip->screen;
  619.             nColors = MIN(maxColors, vip->colormap_size);
  620.             nColors--;
  621.             return v;
  622.         }
  623.     }
  624.     return NULL;
  625. }
  626.