Sunday, January 26, 2014

X Server

Preface
             This post tells about the X server architecture and a basic program used to interact with the X server and its anatomy.

What is X server?
            It's heart of the LINUX graphics subsystem. It provides a basic framework for GUI using which users can create and move windows on the display device.X is designed to provide mechanism ( what capabilities are to be provided) but not policy (how those capabilities are to be provided).  

X server architecture
             X is a client-server architecture model. What does it mean?
           
Client-server architecture is a general mechanism for handling a shared resource that several programs may want to access simultaneously.

 In the case of X, the shared resources are the drawing area and the input channel. If every process was allowed to write on it at its will, several processes may want to draw at the same place, resulting in an unpredictable chaos.

 Thus, only one process is allowed to get access to the drawing area: the X server. The processes wanting to draw stuff or get inputs send requests to the X servers (they are "clients").

X server also send messages without explicit client's requests to keep them informed of what is going on. These messages sent by the server on its own behalf are called "events"

Sample program to interact with the X server          

     This program will create a simple window and draw the message on this window. Use gcc XExample.c -o XExample -lX11 to build this application and run it using ./XExample

 Clients, generally, use the X library (Xlib) to interact with the X server. The Xlib wraps the functionality provided by the X server to ease the user to write the applications. So all the client applications using the Xlib functionality link to it using -lX11.

XExample.c
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
        Display *d;
        Window w;
        XEvent e;
        char *msg = "Hello, World!";
        int s;

        /* open connection with the server */
        d = XOpenDisplay(NULL);
        if (d == NULL) {
                fprintf(stderr, "Cannot open display\n");
                exit(1);
        }

        s = DefaultScreen(d);

        /* create window */
        w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
                        BlackPixel(d, s), WhitePixel(d, s));

        /* select kind of events we are interested in */
        XSelectInput(d, w, ExposureMask | KeyPressMask);

        /* map (show) the window */
        XMapWindow(d, w);

        /* event loop */
        while (1) {
                XNextEvent(d, &e);

                switch(e.type)
                {
                        case  Expose:
                                XDrawString(d, w, DefaultGC(d, s), 50, 50, msg, strlen(msg));
                                break;
                        case KeyPress:
                                XCloseDisplay(d);
                                return 0;
                }
        }
}
  

       The program starts with the basic legal stuff, The header files.
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
  
          Here the matter of interest is <X11/Xlib.h>. Every client application must include this header file. This contains the basic structure declarations and macros.

1)      Connecting to the X server:

 Display *d;
 d = XOpenDisplay(NULL);
   
          First we open a connection to the server. This can be done by XOpenDisplay. The syntax is 
Display *XOpenDisplay(char *display_name); 

         The XOpenDisplay function returns a Display structure that serves as the connection to the X server and that contains all the information about that X server.   After a successful call to XOpenDisplay, all of the screens in the display can be used by the client.

 XOpenDisplay connects your application to the X server through TCP or DECnet communications protocols , if server and client are located remotely,  or through some local inter-process communication protocol, if server and client are located on the same machine.

If XOpenDisplay does not  succeed, it returns NULL.

2)      Creating  a window
           
            Next is creating the window on the display controlling by the X server, the information of which is provided in “Display” structure.
    w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
                       BlackPixel(d, s), WhitePixel(d, s));

The syntax for the XCreateSimpleWindow is:
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*/
)

Display:  Is the pointer obtained after connection to the X server by XOpenDisplay.

rootwindow:  This indicates the parent window of the window we are creating. The window we create appears inside its parent. This we can get by RootWindow(Display *, int  screen_number). The screen number we will get by DefaultScreen(Display*).

x, y : These are the co-ordinates of the upper left corner of the window. These dimensions, like every dimensions in X, are in pixels.

Width, height: These are the width and height of the window in pixels.

border_width : This is the width of the window border. This is nothing to do with the border appended by the window manager.

border: This indicates the color of the border pixel. The macro BlackPixel(Display *, int screen_number) will give the black pixel value in the X server.

Background: This is the color of the window to be appeared on the screen.

     As we discussed X is based upon client-server architecture, The X server keeps sending events to clients to keep it informed about the modifications in thee X server.  

      The XCreateWindow will generate a CreateNotify event to the client.

3)      Events selection

            Next we will tell to the X server in the types of events we are interested in. We use the XSelectInput for this. This will take a OR ( | ) of events we need. Here we used the ExposureMask event which is generated when the created window is exposed on the screen and the KeyPressMask event is generated when any key is pressed on the created window.  
XSelectInput(struct *display, Window w, ExposureMask | KeyPressMask);

4)      Mapping window

        Creating the window with the XCreateSimpleWindow doesn’t make it appear on the screen, we have to map the window to the display with the XMapWindow. Again this will generate an event ,MapNotify.
  XMapWindow(Display *d, Window w);

5)      Events processing

      All the events sent by the X server are stored in a queue which is per client. This queue contains the events selected by the client using XSelectInput.  The client read these events one by one , using XNextEvent, and processes those events.   
 while (1)
 {
        XNextEvent(d, &e);
         switch(e.type)
        {
              case  Expose:
                    XDrawString(d, w, DefaultGC(d, s), 50, 50, msg, strlen(msg));
                     break;
               case KeyPress:
                     XCloseDisplay(d);
                     return 0;
          }
 }

   Each time when window is exposed “Expose” event is generated and we are going to draw string on the screen using XDrawString (This function is self explanatory).When any key is pressed on the window, “KeyPress” event is generated and we are closing the display to the connection to the X server using XCloseDisplay and returning from the function.

PS: Thanks for reading and comments are welcome. 

PPS:In the coming posts we will be discuss in depth about the X server  and its communication with the Client. Please stay tuned.