Topics:
#include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h>These files can typically be found in /usr/include/X11. I highly recommend looking though the header files themselves, but don't get caught up on trying to figure out everything at once! The best way to learn them is to continually consult them as you continue to work with X.
Display *dis; int screen; Window win; GC gc;The display points to the X Server. The screen refers to which screen of the display to use. Setting up the connection from the X Client to the X Server typically involves a line like: setenv DISPLAY my.machine.where.ever:0. The my.machine.where.ever is tied in with the Display* and the screen is connected with the :0 part of the variable. The Window controls the actual window itself (duh! ;^). And the GC is the graphics context.
The graphics context has a lot to do with how things are displayed/drawn in the window. Different masks can be set, etc. and you can get pretty funky with it.
Typically calls to get input and output to the window use one or more of the above variables. See the appropriate sections for details.
Here is a simple routine to create a simple X Windows. It assumes that the above variable have been declared globally:
void init_x() { /* get the colors black and white (see section for details) */ unsigned long black,white; /* use the information from the environment variable DISPLAY to create the X connection: */ dis=XOpenDisplay((char *)0); screen=DefaultScreen(dis); black=BlackPixel(dis,screen), /* get color black */ white=WhitePixel(dis, screen); /* get color white */ /* once the display is initialized, create the window. This window will be have be 200 pixels across and 300 down. It will have the foreground white and background black */ win=XCreateSimpleWindow(dis,DefaultRootWindow(dis),0,0, 200, 300, 5, white, black); /* here is where some properties of the window can be set. The third and fourth items indicate the name which appears at the top of the window and the name of the minimized window respectively. */ XSetStandardProperties(dis,win,"My Window","HI!",None,NULL,0,NULL); /* this routine determines which types of input are allowed in the input. see the appropriate section for details... */ XSelectInput(dis, win, ExposureMask|ButtonPressMask|KeyPressMask); /* create the Graphics Context */ gc=XCreateGC(dis, win, 0,0); /* here is another routine to set the foreground and background colors _currently_ in use in the window. */ XSetBackground(dis,gc,white); XSetForeground(dis,gc,black); /* clear the window and bring it on top of the other windows */ XClearWindow(dis, win); XMapRaised(dis, win); };This section is really only about 10 lines of code if you take out all of the goofy comments, and it can be reused with only minor modifications over and over. Some may object to the "cut-and-paste" method of reusing code, but it works... This snippet of code will pop up an X Window on your default server. Most of the calls have a lot of functionality, but this is enough to get you started.
Another important task is closing the window correctly:
void close_x() { /* it is good programming practice to return system resources to the system... */ XFreeGC(dis, gc); XDestroyWindow(dis,win); XCloseDisplay(dis); exit(1); }
For more information about the routines above, see the following sections (where applicable) and/or try the man pages. They are a great resource.
main () { char tmp[512]; while (1) { scanf("%s",tmp); printf("%s",tmp); } }As you can see, "event driven" programming is nothing new or special. The way that events are handled in X is, however, a bit on the fun side. Basicly you have an XEvent. This can be keypress, mouse button press/release, mouse movement, etc. Anything which can be used as input to an X Window.
How can one variable handle all these different types of events you ask? The answer is that it is a big, complex structure! Even though it is big and scary, you don't have to learn every aspect of it to use it.
The key concepts to using the XEvent structure are:
Here is code snippet which gives an example of using XEvent's. It assumes that a call has been made to init_x() (see previous section) where the call:
XSelectInput(dis, win, ExposureMask|ButtonPressMask|KeyPressMask)was made. This call sets up the masks. Now only Expose, ButtonPress and KeyPress events are regarded as valid. The Expose event type refers to when a portion of the window which had been obscured by another window becomes visisble again. This is usually handled by some sort of user routine to repaint the window. Note: the redraws must be handle by the user! The below example assumes a routine redraw() has been written. Also notice how the KeyPress event is handled. Pressing a 'q' will exit the program.
XEvent event; /* the XEvent declaration !!! */ KeySym key; /* a dealie-bob to handle KeyPress Events */ char text[255]; /* a char buffer for KeyPress Events */ /* look for events forever... */ while(1) { /* get the next event and stuff it into our event variable. Note: only events we set the mask for are detected! */ XNextEvent(dis, &event); if (event.type==Expose && event.xexpose.count==0) { /* the window was exposed redraw it! */ redraw(); } if (event.type==KeyPress&& XLookupString(&event.xkey,text,255,&key,0)==1) { /* use the XLookupString routine to convert the invent KeyPress data into regular text. Weird but necessary... */ if (text[0]=='q') { close_x(); } printf("You pressed the %c key!\n",text[0]); } if (event.type==ButtonPress) { /* tell where the mouse Button was Pressed */ printf("You pressed a button at (%i,%i)\n", event.xbutton.x,event.xbutton.y); } } };The printf statements will, of course, print to terminal you ran the X program from.
There are a lot of Event types and masks. Read the man pages on: XSelectInput and XNextEvent for more information. Also see /usr/include/X11 and search for NoEventMask. This will give you a complete list of all the different sorts of events there are. It is worth noting that XNextEvent will block until another event comes through. So if no events occur, your program just sits there. XCheckWindowEvent is similar to XNextEvent (though it takes different arguments), but it returns a true (1) or a false (0) about whether or not an event occured. If an event did occur it fills an XEvent structure with info about the event. In either case, the program execution continues unabated.
Display *dis; Window win; GC gc;have been declared and initialized (ala init_x()). All of these calls are pretty straight forward, but a look at the man pages will give you more useful information. By appending an 's' to the end of each of the above you can get routines to draw more than one of the same (e.g.: XDrawArcs(...)). Something to keep in mind, is that anything that is drawn uses the current foreground and background colors. See Working with Colors for more info!
XDrawString(dis,win,gc,x,y, string, strlen(string));Pretty simple, huh? There are other string routines, which you can learn about by consulting the man page for XDrawString.
Ahem, now on with the discussion. For the purpose of this introduction I am going to stick with two basic ideas: shared and private colormaps. Colormaps is another word for palettes. I am only going to talk about 256 color colormaps.
Getting the colors you want from a shared colormap is really pretty simple. The easiest way is to consult the rgb.txt file (usually found as /usr/X11/lib/X11/rgb.txt). It list all the string names associated with all the colors of the rainbow! Here is how you use them:
void get_colors() { XColor tmp; XParseColor(dis, DefaultColormap(dis,screen), "chartreuse", &tmp); XAllocColor(dis,DefaultColormap(dis,screen),&tmp); chartreuse=tmp.pixel; };Once again, this routine assumes you have called the init_x() routine. It also assumes chartreuse is a globally declared unsigned long int to hold the pixel value which refers to the closest match to chartreuse in the current palette. As you can probably imagine, you don't always get exactly what you ask for. This is why many application use a private colormap (or have it as an option).
Creating a private colormap is a bit more involved, but if you want the colors to be perfect in your window, it is the easiest way.
void create_colormap() { int i; Colormap cmap; XColor tmp[255]; for(i=0;i<255;i++) { tmp[i].pixel=i; tmp[i].flags=DoRed|DoGreen|DoBlue; tmp[i].red=i*256; tmp[i].blue=i*256; tmp[i].green=i*256; } cmap=XCreateColormap(dis,RootWindow(dis,screen), DefaultVisual(dis,screen),AllocAll); XStoreColors(dis, cmap, tmp,255); XSetWindowColormap(dis,win,cmap); }This initializes an array of 256 XColors into which we store the RGB values, set the pixel and flag values. Then the colormap is created (note: the funky call to DefaultVisual, is a clue about how to handle different display types ;^). Next the colors are loaded into the colormap and finally the colormap is set for our window. If you set up a private colormap, you may also want to add a line like: XFreeColormap(dis,cmap); to your close_x() routine.
Now that your know how to get the pixel values for the color you want (either with a share colormap or private), here is what you can do with this information:
XSetForeground(dis,gc,chartreuse); XDrawArc(dis,win,gc,x,1, 17,17,0,23040);You basicly use the pixel value to set the foreground (or background) color and then draw in the color of your choice! What could be simpler?
Before running your program don't forget to set your DISPLAY environment variable! In csh: setenv DISPLAY my.ip.number.here:0. In others try: DISPLAY=my.ip.number.here:0; export DISPLAY.
Here is a very simple sample program for your to try out.
Origional Author of this page:
bhammond@blaze.cba.uga.edu