Unix Manuals

X Programming

This article was kindly contributed by Manu Anand (linx2002in@yahoo.com).


Warning: include() [function.include]: URL file-access is disabled in the server configuration in /home/2332/unix-man/www.unix-manuals.com/public_html/tutorials/xlib/xlib.html on line 20

Warning: include(http://www.unix-manuals.com/topad.php) [function.include]: failed to open stream: no suitable wrapper could be found in /home/2332/unix-man/www.unix-manuals.com/public_html/tutorials/xlib/xlib.html on line 20

Warning: include() [function.include]: Failed opening 'http://www.unix-manuals.com/topad.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in /home/2332/unix-man/www.unix-manuals.com/public_html/tutorials/xlib/xlib.html on line 20

NOTICE - New forum for all UNIX learners, users and programmers - click here

In the bad old days Linux, like its parent UNIX, lacked a decent graphical user interface.This led to the howls of protest from users around the world of Linux being as much cryptic as other UNIX derivatives. It was rightly tagged as the OS of the geek. The X Window System lends a face to Linux.

The X Window System is a network oriented, device independent system. Network Oriented means that there is a client end and a server end. There could be more than one client for a given server. Basically client programs make calls to the X server. Xlib maps requests to the X server or provides utility functions. Actually X converts calls to C language functions to the X server requests which actually implements that request. Just as an aside, all the conversation between client and server is carried out using the X Network Protocol,which might be carried over any reasonable implementation i.e. TCP/IP, DecNet etc.

Graphical Programming in X basically follows the asynchronous model i.e. "I won't do anything until you ask me to". This might sound like a typical response from your 7 year old. It means that an X program sits in a loop waiting for interesting things to happen. The following snippet illustrates it:

        while(connected to server){
                Receive next event
                Handle the event
        }

To run an X program you need to have the necessary header files and libraries in /usr/X11R6/include and /usr/X11R6/lib respectively. Chances are that if you're running any GUI version of Linux and haven't really fiddled with the system you would be having all the headers and libraries at their default places. The following program is the most basic of all Xlib programs. It simply makes a connection to the X server, prints some info and quits.

/*connect.c -Program to connect to the X Server
On Linux compile with gcc connect.c -L/usr/X11R6/lib -lX11
*/


#include<X11/Xlib.h>
#include<X11/Xutil.h>
#include<stdio.h>


int main(){
        Display *display_name;
        int depth,screen,connection;


        /*Opening display and setting defaults*/
        display_name = XOpenDisplay(NULL);
        screen = DefaultScreen(display_name);
        depth = DefaultDepth(display_name,screen);
        connection = ConnectionNumber(display_name);


        /*Displaying the info gathered*/
        printf("The display is::%s\n",XDisplayName((char*)display_name));
        printf("Width::%d\tHeight::%d\n",
                                DisplayWidth(display_name,screen),
                                DisplayHeight(display_name,screen));
        printf("Connection number is %d\n",connection);


        if(depth == 1)
                printf("You live in prehistoric times\n");
        else
                printf("You've got a coloured monitor with depth of %d\n",
                        depth);


        /*Closing the display*/
        XCloseDisplay(display_name);
}
        

The first and foremost step, in case of all X programs is a request for establishing a connection to the X server. This is accomplished by the XOpenDisplay function call.We pass a NULL as the parameter to indicate that our client and server are on the same machine. After establishing a connection we seek a little info in form of the screen we are connected to(remember each display may have multiple screens),the depth of the screen(colors/bit - for example 16bit screen screen will return 16). Finally we close the connection by using XCloseDisplay().

After this introduction we move on to more serious stuff i.e. opening a window on the display. The following program attempts to open a window on the screen. This window automatically terminates after 10 seconds.

/* window.c --This program opens a window on the display
* Use "gcc -o window window.c -L/usr/X11R6/lib -lX11
* to compile this code.
*/


#include <X11/X.h>
#include <X11/Xlib.h>


int main(void)
{
Display *display;
Window window, rootwindow;
int screen;


display = XOpenDisplay(NULL);
screen = DefaultScreen(display);
rootwindow = RootWindow(display,screen);
window = XCreateSimpleWindow(display, rootwindow,
                0, 0, 100, 100, 1, 0, 0);
XMapWindow(display, window);
XFlush(display);


sleep(10);
XCloseDisplay(display);
return (0);
}



Various objects are made for us by the X server. These objects like windows, cursors etc. return a handle.We can manipulate the objects later by utilizing X routines that accept these handles as parameters. Moreover there are a lot of functions in X which are also implemented as macros.This is probably done to aid efficiency as a macro does not involve function call. However as C compilers get smart they might already be inlining appropriate function so that a function call is almost as fast as a macro.

The RootWindow macro returns the root window. Root Window denotes the screen that forms the background of our window.There is only one Root Window per
display. In our case RootWindow returns essentially the background of the screen which is the parent window for all windows on the display. The next important point of action is the XCreateSimpleWindow() function. This function effectively creates a window on the display. Officially the parameters of this function are:-

XCreateSimpleWindow(
        Display *display,               /*Our display*/
        Window rootwindow,              /*parent Window*/
        int x,                          /*Starting x coordinate*/
        int y,                          /*Starting y coordinate*/
        unsigned int width,             /*Width of the window*/
        unsigned int height,            /*Height of the Window*/
        unsigned int border_width,      /*Width of the border*/
        unsigned long border,           /*Specify border pixel value*/
        unsigned long bkground          /*Color of the background*/
)



XMapWindow() call actually maps the window onto the screen. This is analogous to ShowWindow() function in the Windoze API.A similar function is XMapRaised() which accepts same parameters but raises the window over all existing windows on the display. Xlib maintains a queue of output requests to the X server. XFlush() call basically flushes out this request by sending all requests to the X Server as part of one communication. Ideally this call should be given after all output calls. Finally sleep(10) waits for 10 seconds before closing the display.

This was probably the easiest way to draw a window. Now we discuss a more involved way to make a window appear on the screen.

/*window2.c --Program to display a window on the screen.
 * compile as gcc window2.c -L/usr/X11R6/lib -lX11
 */



#include<X11/Xlib.h>
#include<X11/Xutil.h>
#include<stdio.h>


#define BORDER_WIDTH 2


/* Program wide globals */
Display *theDisplay;
int theScreen;
int theDepth;


Window OpenWindow(int x, int y, int width, int height, int flag){
        XSetWindowAttributes theWindowAttributes;
        unsigned long theWindowMask;
        XSizeHints theSizeHints;
        Window theNewWindow;


        /*Setting the attributes*/
        theWindowAttributes.border_pixel
                =BlackPixel(theDisplay,theScreen);
        theWindowAttributes.background_pixel
                = WhitePixel(theDisplay,theScreen);
        theWindowAttributes.override_redirect = True;



        theWindowMask = CWBackPixel|CWBorderPixel|CWOverrideRedirect;


        theNewWindow = XCreateWindow( theDisplay,
                        RootWindow(theDisplay,theScreen),
                        x,y,width,height,
                        BORDER_WIDTH,theDepth,
                        InputOutput,
                        CopyFromParent,
                        theWindowMask,
                        &theWindowAttributes);

        theSizeHints.flags = PPosition | PSize;
        theSizeHints.x = x;
        theSizeHints.y = y;
        theSizeHints.width = width;
        theSizeHints.height = height;


        XSetNormalHints(theDisplay,theNewWindow,&theSizeHints);


        XMapWindow(theDisplay,theNewWindow);
        XFlush(theDisplay);


        return theNewWindow;
}


void main(void){
        Window theWindow;

        theDisplay = XOpenDisplay(NULL);
        if(theDisplay == NULL){
                fprintf(stderr,"Error::Given display cannot be opened");
                return;
        }
        theScreen = DefaultScreen(theDisplay);
        theDepth = DefaultDepth(theDisplay,theScreen);
        theWindow = OpenWindow(100,100,200,200,0);


        sleep(10);
        XCloseDisplay(theDisplay);
}

This program is more robust as all the code of drawing the window is moved to another procedure. Moreover window attributes as well as hints to the window manager are also involved. Let us begin stepping through the program.

After establishing a connection to the X server,there is an error detection routine which checks if the connection was successful.A successful connection results in a call to OpenWindow() which opens a window on the display. The two new chaps in OpenWindow() are XSetWindowAttributes and XSizeHints. Both of these are structures. While XSetWindowAttributes is needed to set various attributes of the window, XSizeHints is needed to set information to be used by window manager.

Two other functions that need to be explained are XCreateWindow() and XSetNormalHints(). XCreateWindow() has all the ingredients of our XCreateSimpleWindow() function and more. A bare bone display is as follows:-

XCreateWindow(
        Display *display,                   /*Our display screen*/
        Window parent,                      /*The parent window*/
        int x,int y,                        /*Starting coordinates*/
        unsigned int width,unsigned int height, /*Proportions of the window*/
        unsigned int border_width,          /*Width of the border*/
        int depth,                          /*Depth of the screen*/
        unsigned int Class,                 /*Type of window*/
        Visual *visual,                     /*visual of the window*/
        unsigned long attributemask,        /*Mask of the attributes*/
        XSetWindowAttibutes windowsattrib); /*Structure of window attribs*/
)



XSetNormalHints() simply sends hints to the window manager informing it about position,size and dimensions.The flags field has been set to PPosition|PSize which effectively translates that the program chooses the position and size.

After understanding the process of opening a window we are all set to learn about drawing in a window. X provides calls for drawing points,lines and arcs. Circles and ellipses are thought of as an extension of arc function. In accordance with the GUI packages all these functions have descriptive names like XDrawPoint(), XDrawLine(), XDrawRectangle(), XDrawArc() etc. We present a program which draws standard shapes into the present window.

/*Program to draw lines and rectangles--draw.c
*On Linux compile as gcc draw.c -L /usr/X11R6/lib -lX11
*/


#include<X11/Xlib.h>
#include<X11/Xutil.h>
#include<stdio.h>
#define BORDER_WIDTH 2


/* Program wide globals */
Display *theDisplay;
int theScreen;
int theDepth;
unsigned long theBlackPixel;
unsigned long theWhitePixel;


void initX(void){
}


void drawLine(Window theWindow,GC theGC,int x1,int y1,int x2,int y2){
        XDrawLine(theDisplay,theWindow,theGC,x1,y1,x2,y2);


}


void drawRectangle(Window theWindow,GC theGC,int x,int y,int width,int height){
        XDrawRectangle(theDisplay,theWindow,theGC,x,y,width,height);
}


int createGC(Window theNewWindow,GC *theNewGC){
        XGCValues theGCValues;


        *theNewGC = XCreateGC(theDisplay,theNewWindow,(unsigned long) 0,&theGCValues);
        if(*theNewGC == 0)
                return 0;
        else{
                XSetForeground(theDisplay,*theNewGC,theWhitePixel);
                XSetBackground(theDisplay,*theNewGC,theBlackPixel);
                return 1;
        }
}


Window OpenWindow(int x, int y, int width, int height, int flag,GC *theNewGC){
        XSetWindowAttributes theWindowAttributes;
        unsigned long theWindowMask;
        XSizeHints theSizeHints;
        XWMHints theWMHints;
        Window theNewWindow;


        /*Setting the attributes*/
        theWindowAttributes.border_pixel
                =BlackPixel(theDisplay,theScreen);
        theWindowAttributes.background_pixel
                = WhitePixel(theDisplay,theScreen);
        theWindowAttributes.override_redirect = False;



        theWindowMask = CWBackPixel|CWBorderPixel|CWOverrideRedirect;


        theNewWindow = XCreateWindow( theDisplay,
                        RootWindow(theDisplay,theScreen),
                        x,y,width,height,
                        BORDER_WIDTH,theDepth,
                        InputOutput,
                        CopyFromParent,
                        theWindowMask,
                        &theWindowAttributes);


        theWMHints.initial_state = NormalState;
        theWMHints.flags = StateHint;


        XSetWMHints(theDisplay,theNewWindow,&theWMHints);

        theSizeHints.flags = PPosition | PSize;
        theSizeHints.x = x;
        theSizeHints.y = y;
        theSizeHints.width = width;
        theSizeHints.height = height;


        XSetNormalHints(theDisplay,theNewWindow,&theSizeHints);


        if( createGC(theNewWindow,theNewGC) == 0){
                XDestroyWindow(theDisplay,theNewWindow);
                return( (Window) 0);
                }


        XMapWindow(theDisplay,theNewWindow);
        XFlush(theDisplay);


        return theNewWindow;
}


void main(void){
        Window theWindow;
        GC theGC;

        theDisplay = XOpenDisplay(NULL);

        theScreen = DefaultScreen(theDisplay);
        theDepth = DefaultDepth(theDisplay,theScreen);
        theBlackPixel = WhitePixel(theDisplay,theScreen);
        theWhitePixel = BlackPixel(theDisplay,theScreen);

        theWindow = OpenWindow(100,100,300,300,0,&theGC);
        drawLine(theWindow,theGC,10,10,100,100);
   drawRectangle(theWindow,theGC,100,100,100,100);
        XFlush(theDisplay);

        sleep(10);
        XDestroyWindow(theDisplay,theWindow);
}


This program displays a rectangle and a line connected to one of the edges. To draw anything on the screen we need assistance from a GC. A GC is a reservoir of default values for background, foreground, brush, pen etc. Think of it as a box of crayons.A GC is needed because without its presence we would have to specify each and every trivial aspect to the X server. In our example we just set a few values and let the remaining be at their default values.

A GC is obtained by a call to XCreateGC() function.This GC can then be passed to all drawing functions. Wrapper functions are defined for both line as well as rectangle drawing. Local variables are defined for BlackPixel() and WhitePixel() macros. This basically eases the burden on the X server. As an aside there are functions which draw filled counterparts of the functions described above. These are aptly called XFillRectangle() and XFillArc(). Moreover there are also functions which draw multiple points, lines and arcs like XDrawPoints(), XDrawLines() and XDrawRectangles(). Be sure to check the man pages for details.

That will be all for now.In the next part we will draw circles,ovals and process various type of events.In the meantime play with the programs you just executed. Meandering is the spirit of Linux.


vi Reference

Home


Warning: include() [function.include]: URL file-access is disabled in the server configuration in /home/2332/unix-man/www.unix-manuals.com/public_html/tutorials/xlib/xlib.html on line 434

Warning: include(http://www.unix-manuals.com/botad.php) [function.include]: failed to open stream: no suitable wrapper could be found in /home/2332/unix-man/www.unix-manuals.com/public_html/tutorials/xlib/xlib.html on line 434

Warning: include() [function.include]: Failed opening 'http://www.unix-manuals.com/botad.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in /home/2332/unix-man/www.unix-manuals.com/public_html/tutorials/xlib/xlib.html on line 434

© Copyright 2000-2001, Tom Reader, All Rights Reserved.
UNIX is a registered trademark of The Open Group in the US and other countries.
For more information on the use of the UNIX trademark, click here.
The contents of this site are not connected with or endorsed by The Open Group in any way.