[comp.windows.x] How do I use a read/write color cell?

jec@cbnews.att.com (John Collier) (09/27/90)

I've been fiddling around with a "paint mixer" program, mostly just to learn
how things work (those of you who've already done this may smile and nod your
heads knowingly...:-).  This is the "traditional" paint mixer where I show
three sliders, one each for red, green, and blue intensities, with the idea
that when I move one of the sliders, the background color inside a window 
will change dynamically.  This would be really nice; I just can't seem to make
it work.

I made a call to XAllocColorCells to allocate one read/write color cell.  The
call looks like:

   unsigned long *plane_masks = 0 ;

   int rtn = XAllocColorCells (dsp, cmap, True, plane_masks, 0, &pixel, 1) ;

This seems to yield a valid value for "pixel" usually something "25".  I 
also check the return code and it never comes back "0", so it seems like this
part works okay.  Later I call XStoreColor to initialize the color cell to
"black":

   XColor xc ;

   xc.pixel = pixel ;
   xc.red = 0 ;
   xc.green = 0 ;
   xc.blue = 0 ;
   xc.flags = DoRed | DoGreen | DoBlue ;

   XStoreColor (dsp, cmap, &xc) ;

As far as I can tell, this works.  My Xlib Reference Manual doesn't mention a
return code, so I don't know if I can check one to see it the call worked.
Also, my test area shows black, which may or may not mean anything under the
circumstances.

Once I've allocated this, I assign the background color of an OpenLook Static
Text widget to use the "pixel" I got from the previous call.  This looks like:

   XFreeColors (dsp, cmap, old_pixel_value, 1, 0) ;   /* Free previous color */

   XColor xc ;

   xc.pixel = new_pixel ;    /* Pixel value obtained from XAllocColorCells */

   int rtn = XAllocColor (dsp, cmap, &xc) ;

   /* Build an Xt Args list with XtNbackground set to "new_pixel" */
   /* Use XtSetValues to set the widget background color to "new_pixel" */

I check the return code from "XAllocColor", but it is never zero, so I assume
this call worked as well.  Later, in the callback function for the slider, I
call XStoreColor again to set a new RGB value for the "pixel".  It is 
identical to the one above except only one field is set and "flags" is set
correspondingly.  I also left-shift the color by 8 (<< 8) before I call
XStoreColor.

Now, with all of that, when I run the program and move the sliders, the window
background never changes color.  If someone could tell me what I'm doing wrong
or, conversely, a better way to do it right, I would really appreciate it.
The explanation can deal with what I want to do on the Xlib level; I can
translate it into my "stuff" from there.  

Any ideas, help, or suggestions would be appreciated.

-- 
-------------------------------------------------------------------------------
 Working around here is like going into a swamp | John Collier, AT&T Bell Labs
 full of alligators...you'll either be eaten    | ARPA:  jec@cbnea.ATT.COM
 alive or wind up with some really nice luggage!| uucp/e-mail:  att!cbnea!jec

mouse@SHAMASH.MCRCIM.MCGILL.EDU (der Mouse) (09/27/90)

[Code examples edited to compress this posting -dM]

> [...] the background color inside a window will change dynamically.

> I made a call to XAllocColorCells to allocate one read/write color
> cell.  The call looks like:

>    unsigned long *plane_masks = 0 ;
>    int rtn = XAllocColorCells (dsp, cmap, True, plane_masks, 0, &pixel, 1) ;

Well, there's no need to make plane_masks a variable, but that's not a
problem (except perhaps in a stylistic sense).

> This seems to yield a valid value for "pixel" usually something "25".
> I also check the return code and it never comes back "0", so it seems
> like this part works okay.  Later I call XStoreColor to initialize
> the color cell to "black":

>    XColor xc ;
>    xc.pixel = pixel ; xc.red = xc.green = xc.blue = 0 ;
>    xc.flags = DoRed | DoGreen | DoBlue ;
>    XStoreColor (dsp, cmap, &xc) ;

> As far as I can tell, this works.  [...]

This looks entirely reasonable, assuming cmap is something sensible
(you don't say where you get cmap from).

> Once I've allocated this, I assign the background color of an
> OpenLook Static Text widget to use the "pixel" I got from the
> previous call.  This looks like:

>    XFreeColors (dsp, cmap, old_pixel_value, 1, 0) ;   /* Free previous color */
>    XColor xc ;
>    xc.pixel = new_pixel ;    /* Pixel value obtained from XAllocColorCells */
>    int rtn = XAllocColor (dsp, cmap, &xc) ;
>    /* Build an Xt Args list with XtNbackground set to "new_pixel" */
>    /* Use XtSetValues to set the widget background color to "new_pixel" */

This looks most peculiar.  Where are you getting old_pixel_value from
and why are you freeing it?  (Also, shouldn't it be &old_pixel_value?
Without a declaration for old_pixel_value I can't be sure.)

XAllocColor allocates a read-only color cell.  You don't say what
you're setting the red/green/blue fields of xc to, so I can't say what
sort of color you're allocating.  The Xlib documentation I have does
not explicitly say what XAllocColor does with the pixel field of the
XColor structure passed in; UTSLing tells me that it is completely
ignored.

However, this shouldn't matter, because you ignore the allocated
read-only cell and proceed to use the read-write cell you allocated
earlier with XAllocColorCells.

> Later, in the callback function for the slider, I call XStoreColor
> again to set a new RGB value for the "pixel".  It is  identical to
> the one above except only one field is set and "flags" is set
> correspondingly.  I also left-shift the color by 8 (<< 8) before I
> call XStoreColor.

(Presumably the value before the left-shift is [0..255].)  I assume the
cell you are trying to store into in the callback is the one from
XAllocColorCells.  The only thing that comes to mind then is, are you
sure the widget's window is using the same colormap as the one you
allocated your read-write cell in?  You don't say where you get the
"cmap" you pass to all these functions.

It might be informative to start off with something other than black.
Pick a color that's not normally present on your screen and set the
color to that initially, instead of black.  If you then still see black
this should tell you something - just what depends on where you see
black when you expect the other color.

If you still have trouble, I'd be glad to discuss it via email (so as
to avoid annoying everyone on xpert with a debugging session :-).

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

erc@pai.UUCP (Eric Johnson) (10/03/90)

I tried to email, but it bounced. Since Xlib and colour is
a general enough topic, here goes.

Regarding:

From: jec@cbnews.att.com (John Collier)
Newsgroups: comp.windows.x
Subject: How do I use a read/write color cell?
Keywords: read,write,alloc,SetColor
Date: 26 Sep 90 19:09:51 GMT
Organization: AT&T Bell Laboratories

I've been fiddling around with a "paint mixer" program, mostly just to learn
how things work (those of you who've already done this may smile and nod your
heads knowingly...:-).  This is the "traditional" paint mixer where I show
three sliders, one each for red, green, and blue intensities, with the idea
that when I move one of the sliders, the background color inside a window 
will change dynamically.  This would be really nice; I just can't seem to make
it work.

I made a call to XAllocColorCells to allocate one read/write color cell.  The
call looks like:

   unsigned long *plane_masks = 0 ;

   int rtn = XAllocColorCells (dsp, cmap, True, plane_masks, 0, &pixel, 1) ;

This seems to yield a valid value for "pixel" usually something "25".  I 
also check the return code and it never comes back "0", so it seems like this
part works okay.  Later I call XStoreColor to initialize the color cell to
"black":

   XColor xc ;

   xc.pixel = pixel ;
   xc.red = 0 ;
   xc.green = 0 ;
   xc.blue = 0 ;
   xc.flags = DoRed | DoGreen | DoBlue ;

   XStoreColor (dsp, cmap, &xc) ;

As far as I can tell, this works.  My Xlib Reference Manual doesn't mention a
return code, so I don't know if I can check one to see it the call worked.
Also, my test area shows black, which may or may not mean anything under the
circumstances.

Once I've allocated this, I assign the background color of an OpenLook Static
Text widget to use the "pixel" I got from the previous call.  This looks like:

   XFreeColors (dsp, cmap, old_pixel_value, 1, 0) ;   /* Free previous color */

   XColor xc ;

   xc.pixel = new_pixel ;    /* Pixel value obtained from XAllocColorCells */

   int rtn = XAllocColor (dsp, cmap, &xc) ;

   /* Build an Xt Args list with XtNbackground set to "new_pixel" */
   /* Use XtSetValues to set the widget background color to "new_pixel" */

I check the return code from "XAllocColor", but it is never zero, so I assume
this call worked as well.  Later, in the callback function for the slider, I
call XStoreColor again to set a new RGB value for the "pixel".  It is 
identical to the one above except only one field is set and "flags" is set
correspondingly.  I also left-shift the color by 8 (<< 8) before I call
XStoreColor.

Now, with all of that, when I run the program and move the sliders, the window
background never changes color.  If someone could tell me what I'm doing wrong
or, conversely, a better way to do it right, I would really appreciate it.
The explanation can deal with what I want to do on the Xlib level; I can
translate it into my "stuff" from there.  

Any ideas, help, or suggestions would be appreciated.

-- 
-------------------------------------------------------------------------------
 Working around here is like going into a swamp | John Collier, AT&T Bell Labs
 full of alligators...you'll either be eaten    | ARPA:  jec@cbnea.ATT.COM
 alive or wind up with some really nice luggage!| uucp/e-mail:  att!cbnea!jec

-

----

Kevin Reichard and I recently had an article on X Window colour
published in _Computer Language_ magazine (the July, 1990 issue).

Enclosed below is the source code for the program described in that
article. I'm a firm believer that working source code examples do 
a lot for the learning process (at least for me). The program allocates
colours and then changes the colour values on the fly. So, I hope it
provides some help.

You might also want to look up XSetWindowBackground(), e.g.:

	XSetWindowBackground( display, window, new_back_pixel );
	XClearWindow( display, window );

---


The July, 1990 issue of Computer Language contains an article on
writing X programs that use color: "Controlling Colors the Xlib Way"
by yours truly and Kevin Reichard. Unfortunately, the accompaning
C program was not included due to space.  The article makes a lot
more sense with the program, so here goes. I'm a firm believer that
source code examples really help. (The program is written to
Release 4 of X. You can easily convert, though.)

---------------cut here for colorfun.c---------------------------------
/*
 *      colorfun.c
 *      X Window demo program that draws a colored box 
 *      in a window. Every second, we change the color.
 *
 *      To compile and run this program, you
 *      will need to link in the X library
 *      (usually called Xlib), usually done with
 *      a command something like:
 *
 *              cc -o colorfun colorfun.c -lX11
 *
 *      (On UNIX systems.)
 *
 *      Note that we use some functions found only
 *      in the Release 4 Xlib.  These functions are
 *      marked as such, so if you have an R3 or
 *      older system you can easily convert.
 *
 *      Written for Computer Language magazine
 *      by E F Johnson and K Reichard, 1990.
 *
 */

#include   <stdio.h>

/*
 *      Most X programs will need Xlib.h and Xutil.h
 */
#include   <X11/Xlib.h>
#include   <X11/Xutil.h>



main( argc, argv )

int     argc;
char    *argv[];

{       /* -- main */
        Display         *display;
        int             screen;
        Window          window;
        GC              gc;
        int             done;
        XColor          the_color;
        Visual          *visual;
        int             depth;
        XFontStruct     *font;
        int             status;
        Colormap        colormap;
        unsigned long   black, white;


        /*
         *      1) Set up X display connection with the server
         *      named in the user's DISPLAY environment variable.
         */
        display = XOpenDisplay( NULL );

        if ( display == (Display *) NULL )
                {
                (void) fprintf( stderr, "Error: Could not open display\n" );
                exit( 1 );      
                }


        /*
         *      2) Find a visual that supports color and get its depth.
         */
        screen = DefaultScreen( display );

        SetUpVisual( display, screen, &visual, &depth );


        /*
         *      3a) Create Color map and get a black and white 
         *      pixel values for the colormap.
         */
        SetUpColormap( display, screen, visual, &colormap, &black, &white );


        /*
         *      3b) Allocate one read-write color cell.
         */
        status = XAllocColorCells( display, colormap, 
                        False, NULL, 0, &the_color.pixel, 1 );

        if ( status == 0 )
                {
                (void) fprintf( stderr, "Error in allocating color cell.\n" );
                XCloseDisplay( display );
                exit( 1 );
                }

        /*
         * Set up some "random" RGB color values.
         */ 
        the_color.red   = ( 1   << 8 );
        the_color.green = ( 104 << 8 );
        the_color.blue  = ( 200 << 8 );

        /*
         *      4) Load a font
         */
        font = XLoadQueryFont( display, "fixed" );

        if ( font == (XFontStruct *) NULL )
                {
                (void) fprintf( stderr, "Error loading fixed font\n" );
                XCloseDisplay( display );
                exit( 1 );
                }


        /*
         *      5) Create a window and set ICCCM values.
         */
        SetUpWindow( display, visual, depth, colormap, black, white, &window );


        /*
         *      6) Create a graphics context (GC) for our window.
         */
        gc = XCreateGC( display, window, 0x0, (XGCValues *) NULL );
        XSetFont( display, gc, font->fid );
        XSetBackground( display, gc, white );


        /*
         *      7) "Map" our window, making it appear
         *      on the screen.
         */
        XMapRaised( display, window );
        XFlush( display );

        
        /*
         *      8) Loop on events, exit on a ButtonPress event.
         */
        done = False;
        while( !done )
                {
                done = CheckForEvents( display, window, gc, 
                                &the_color, black );

                /*
                 *      9) Sleep for a second,
                 *      to space out the color changes.
                 */
                sleep( 1 );


                /*
                 *      10) Change the color in our window.
                 */
                ChangeColor( display, colormap, &the_color );


                /*
                 *      11) Redraw with the new color
                 */
                Redraw( display, window, gc, &the_color, black );
                }
        
        /*
         *      12) Free/Destroy our window, GC, and font.
         */
        XDestroyWindow( display, window );
        XFreeGC( display, gc );
        XFreeFont( display, font );


        /*
         *      13) Close our X display connection and quit.
         */
        XCloseDisplay( display );
        exit( 0 );

}       /* -- main */


CheckForEvents( display, window, gc, the_color, black )

Display         *display;
Window          window;
GC              gc;
XColor          *the_color;
unsigned long   black;


/*
 *      8) Loop on events.
 *      CheckForEvents() checks to see if an X event
 *      is pending.  If so, CheckForEvents() reads in
 *      that event and then acts on it (perhaps by
 *      doing nothing).  We ignore changes to the window's
 *      size (it is assumed the user will be too 
 *      dazzled by the program to even think about resizing
 *      the window).
 */

{       /* -- function CheckForEvents */
        XEvent  event;
        int     done = False;

        /*
         *      8a) Do we have an event waiting?
         */
        if ( XPending( display ) > 0 )
                {
                XNextEvent( display, &event );

                switch( event.type )
                        {
                        /*
                         * 8b) Set a flag if a mouse button
                         * is pressed. We will exit later.
                         */
                        case ButtonPress: 
                                done = True; 
                                break;

                        /*      
                         * 8c) Only redraw our window when
                         * the last Expose event comes in.
                         */
                        case Expose:
                                if ( event.xexpose.count == 0 )
                                        {
                                        Redraw( display, window, 
                                                gc, the_color, black );
                                        }
                                break;
                        }
                }

        return( done );

}       /* -- function CheckForEvents */



Redraw( display, window, gc, the_color, black )

Display         *display;
Window          window;
GC              gc;
XColor          *the_color;
unsigned long   black;


/*
 *      Redraw() redraws our window.  Redraw()
 *      is called whenever the window's contents
 *      need to be refreshed, because the contents
 *      have changed, or because the window system
 *      requests we redraw the window due to "damage".
 */

{       /* -- function Redraw */
        char    string[ 120 ];

        /*
         * Fill in a rectangle, in the given color.
         */
        XSetForeground( display, gc, the_color->pixel );

        XFillRectangle( display, window, gc,
                10, 30, 200, 100 );


        /*
         * Draw text in black
         */
        XSetForeground( display, gc, black );


        /*
         * Display current color values as text
         */
        (void) sprintf( string, "R 0x%5.5x G 0x%5.5x B 0x%5.5x",
                the_color->red,
                the_color->green, 
                the_color->blue );


        XDrawImageString( display, window, gc,
                10, 20,
                string, strlen( string ) );

        /*
         * Send output requests to the X server
         */
        XFlush( display );

}       /* -- function Redraw */



SetUpColormap( display, screen, visual, colormap, black, white )

Display         *display;
int             screen;
Visual          *visual;
Colormap        *colormap;
unsigned long   *black, *white;

/*
 *      SetUpColormap() creats an X colormap using the
 *      given screen and visual.  Color cells (pixels)
 *      for "black" and "white" are also allocated in
 *      our new colormap.
 */

{       /* -- function SetUpColormap */
        int     status;
        XColor  hardwarecolor, exactcolor;


        /*
         * Create a colormap using the
         * visual found in SetUpVisual().
         */
        *colormap = XCreateColormap( display,
                        RootWindow( display, screen ),
                        visual,
                        AllocNone );


        /*
         * Check for failure.
         */
        if ( *colormap != None )
                {
                /* -- Set up "white" in the new colormap */
                status = XAllocNamedColor( display, *colormap, "white",
                                &hardwarecolor,
                                &exactcolor );

                if ( status != 0 )
                        {
                        *white  = hardwarecolor.pixel;


                        /* -- Set up "black" in the new colormap */
                        status = XAllocNamedColor( display, *colormap, "black",
                                        &hardwarecolor,
                                        &exactcolor );

                        *black  = hardwarecolor.pixel;
                        }
                }
        else
                {
                status = 0;
                }

        if ( status == 0 )
                {
                (void) fprintf( stderr, "Error in creating colormap\n" );
                XCloseDisplay( display );
                exit( 1 );
                }

}       /* -- function SetUpColormap */



SetUpVisual( display, screen, visual, depth )

Display *display;
int     screen;
Visual  **visual;
int     *depth;

/*
 *      SetUpVisual() finds a PseudoColor visual
 *      on the given screen.  If none is to be found,
 *      the program will terminate.  *depth is set to
 *      the depth of the found visual.
 */

{       /* -- function SetUpVisual */
        int             number_visuals;
        XVisualInfo     *visual_array, visual_template;

        /*
         * We want a PseudoColor visual on this screen.
         * XGetVisualInfo() will get a list of all
         * the PseudoColor visuals supported on
         * this screen.
         */
        visual_template.class  = PseudoColor;
        visual_template.screen = screen;

        visual_array = XGetVisualInfo( display,
                                VisualClassMask | VisualScreenMask,
                                &visual_template,
                                &number_visuals );

        /* -- Check for success */
        if ( ( number_visuals > 0 ) && ( visual_array != NULL ) )
                {
                /*
                 * We're lazy, we just choose the
                 * first PseudoColor visual
                 */
                *visual = visual_array[0].visual;
                *depth = visual_array[0].depth;

                XFree( visual_array );
                }
        else
                {
                (void) fprintf( stderr, "Error in finding visual\n" );
                XCloseDisplay( display );
                exit( 1 );
                }

}       /* -- function SetUpVisual */



SetUpWindow( display, visual, depth, colormap, fore, back, window )

Display         *display;
Visual          *visual;
int             depth;
Colormap        colormap;
unsigned long   fore, back;
Window          *window;

/*
 *      5) Create a window.  We need to be careful here
 *      to use our visual and colormap.
 */

{       /* -- function SetUpWindow */
        int                     x, y, width, height;
        XSetWindowAttributes    attributes;
        unsigned long           attribute_mask;
        int                     screen;
        XWMHints                *wmhints;
        XSizeHints              *sizehints;



        /*
         * 5a) Determine x, y, width, height. Normally
         * in X, the user should be able to specify this
         * on the UNIX command-line.  For simplicity,
         * we're just using arbitrary values here.
         */
        x      = 10;
        y      = 10;
        width  = 220;
        height = 140;


        /*
         * 5b) Fill in a window attributes structure.
         * fore and back arer the black and white colors
         * allocated in our new colormap. If we use
         * the default BlackPixel() and WhitePixel(),
         * we may get strange values since these
         * may not have been allocated in our new
         * colormap.
         */
        attributes.background_pixel = back;
        attributes.border_pixel     = fore;
        attributes.event_mask       = ExposureMask | ButtonPressMask;
        attributes.colormap         = colormap;


        attribute_mask  = CWBackPixel | CWBorderPixel | 
                          CWEventMask | CWColormap;



        /*
         * 5c)  Create window
         */
        screen  = DefaultScreen( display );

        *window = XCreateWindow( display, 
                        RootWindow( display, screen ), /* parent window */
                        x, y, width, height,
                        2,              /* -- border width */
                        depth,
                        InputOutput,    /* -- window class */
                        visual,         /* -- our visual */
                        attribute_mask,
                        &attributes );


        if ( *window == (Window) None )
                {
                (void) fprintf( stderr, "Error: could not open window\n" );
                XCloseDisplay( display );
                exit( 1 );
                }


        /*
         * 5d) Store a name for the window
         * with the window manager.  Note we are
         * using Latin-1 names, so users in Japan
         * or Arabia might want to use X's compound
         * text mechanism.
         */
        XStoreName( display, *window, "ColorFun" );


        /*
         * 5e) Set window manager hints, R4-only functions!
         */
        wmhints = XAllocWMHints();

        if ( wmhints != NULL )
                {
                wmhints->initial_state = NormalState;
                wmhints->input         = True;
                wmhints->flags         = StateHint | InputHint;

                XSetWMHints( display, *window, wmhints );

                XFree( wmhints );
                }


        /*
         * 5f) Set Size hints, again R4-specific!
         * (This also requires an R4 or ICCCM-compliant
         * window manager.)
         */
        sizehints = XAllocSizeHints();

        if ( sizehints != NULL )
                {
                sizehints->base_width  = width;    
                sizehints->base_height = height;  

                sizehints->flags       = PBaseSize;

                XSetWMNormalHints( display, *window, sizehints );

                XFree( sizehints );
                }

        
}       /* -- function SetUpWindow */



ChangeColor( display, colormap, color )

Display         *display;
Colormap        colormap;
XColor          *color;

/*
 *      Every second, ChangeColor() is called to
 *      modify the current color we are displaying.
 */

{       /* -- function ChangeColor */


        color->flags = DoRed | DoGreen | DoBlue;

        color->red   = (unsigned short) IncrementValue( color->red, 3 );
        color->green = (unsigned short) IncrementValue( color->green, 6 );
        color->blue  = (unsigned short) IncrementValue( color->blue, 5  );

        XStoreColor( display, colormap, color );

}       /* -- function ChangeColor */



IncrementValue( value, amount )

short   value;
int     amount;         /* -- add this amount to value. */

/*
 *      Some common 8-plane color systems seem to just 
 *      use the upper byte of the color values for
 *      Red, Green and Blue (hence all the >> and << 
 *      bit-shift operations).  If you don't like how
 *      these colors look on your system, change 
 *      IncrementValue() and ChangeColor() to do what
 *      you'd like. Play around with them and have fun.
 */

{       /* -- function IncrementValue */
        unsigned short  new_value;

        new_value = ( value >> 8 );

        new_value += amount;

        /*
         * Spend more time with brighter colors.
         */
        if ( new_value > 200 )
                {
                new_value--;
                }

        if ( new_value > 250 )
                {
                new_value = 0;
                }

        return( new_value << 8 );

}       /* -- function IncrementValue */

/*
 *      end of file
 */
---------------cut here------------------------------------------------

Have fun,
-Eric


-- 
Eric F. Johnson               phone: +1 612 894 0313    BTI: Industrial
Boulware Technologies, Inc.   fax:   +1 612 894 0316    automation systems
415 W. Travelers Trail        email: erc@pai.mn.org     and services
Burnsville, MN 55337 USA