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