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.