[comp.windows.x] Computer Language X article

erc@pai.UUCP (Eric Johnson) (07/13/90)

.......

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 :-)

You'll find the article on pages 34 - 47 of the July, 1990 issue
(vol. 7, no. 7) of Computer Language.  Blame me for any errors in 
the program :-)

---------------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