This Chapter will deal specifically with the introduction of Xlib - the low level X library. Recall that Xlib provides the means of communication between the application and the X system. The Xlib library of (C) subroutines is large and of a comparable size to Motif. Many of Xlib's routines deal with the creation, maintenance and interaction between windows and applications. Xlib does not have any concept of widgets and thus does not provide provide any (high level) means of interaction. In general, writing complete application solely in Xlib is not a good idea. Motif provides many useful, complete GUI building blocks that should always be used if available. For example, do you really want to write a complete text editing library in Xlib, when Motif provides one for free?
If you use Motif then there should never be any need to resort to Xlib for window creation. Motif is far more powerful and flexible. Consequently, in this and forthcoming Chapters, we will only deal with issues that affect the interfacing of Xlib with Motif and the Xt toolkit.
However, for certain tasks we will have to resort to Xlib. The sort of tasks that we will be concerned with in this text are:
Xlib deals with much lower level objects than widgets. When you write or draw in Xlib reference, may be made to the following:
If we are programming in Xlib alone, we would have to create windows and open displays ourselves. However, if we are using a higher level toolkit such a Motif and require to call on Xlib routines then we need to obtain the above information from an appropriate widget in order pass on appropriate parameter values in the Xlib function calls.
Functions are available to obtain this information readily from a Widget. For example XtDisplay(), XtWindow(), XtScreen() etc. can be used to obtain the ID of a given Xlib Display, Window, or Screen structure respectively from a given widget. Default Values of these structures are also typically used. Functions DefaultDepthofScreen(), RootWindowofScreen() are examples.
Sometimes, in a Motif program, you may have to create an Xlib structure from scratch. The GC is the most frequently created structure that concerns us. The Function XCreateGC() creates a new GC data structure.
We will look at the mechanics of assembling Xlib graphics within a Motif program when we study DrawingAreas in Chapter 16. For the remainder of this Chapter we will continue to introduce basic Xlib concepts. In the coming Sections, reference is made to programs that are described in Chapter 16.
As mentioned in the previous Section, the GC is responsible for setting the properties of lines and basic (2D) shapes. GCs are therefore used with every Xlib drawing function. The draw.c (Section 16.3.1) program illustrates the setting of a variety of GC elements.
To create a GC use the (Xlib) function XCreateGC() . It has 4 parameters:
The XGCValues structure contains elements like foreground, background, line_width, line_style, etc. that we can set for obvious results. The mask has predefined values such as GCForeground, GCBackground, and GCLineStyle.
In draw.c we create a GC structure, gc, and set the foreground.
Xlib provides two macros BlackPixel() and WhitePixel() which will find the default black and white pixel values for a given Display and Screen if the default colourmaps are installed. Note that the reference to BlackPixel() and WhitePixel() can be a little confusing since the pixel colours returned may not necessarily be Black or White. BlackPixel() actually refers to the foreground colour and WhitePixel() refers to the background colour.
Therefore, to create a GC that only sets foreground colour to the default for a given display and screen:
gcv.foreground = BlackPixel(display, screen); gc = XCreateGC(display, screen, GCForeground, &gcv);
where gcv is a XCGValues structure and gc a GC structure and GCForeground sets the mask to only allow alteration of the foreground.
To set both background and foreground:
gcv.foreground = BlackPixel(display, screen); gcv.background = WhitePixel(display, screen); gc = XCreateGC(display, screen, GCForeground | GCBackground, &gcv);
where we use the |
(OR) in the mask parameter that allows
both the values to be set in the XGCValues structure.
An alternative way to change GC elements is to use Xlib convenience functions to set appropriate GC values. Example functions include :
XSetForeground(), XSetBackground(), XSetLineAttributes() .
These set GC values for a given display and gc, for example:
XSetBackground(display, gc, WhitePixel(display, screen));
Further examples of their use are shown in the draw.c program (Section 16.3.1).
The simplest function is XDrawPoint(Display *d, Drawable dr, GC gc, int x, int y) which draws a point at position (x, y) to a given Drawable on a Display. This effectively colours a single pixel on the Display.
The function XDrawPoints(Display *d, Drawable dr, GC gc, XPoint *pts, int n, int mode) is similar except that an n element array of XPoints is drawn. The mode may be defined as being either CoordModeOrigin or CoordModePrevious. The former mode draws all points relative to the origin whilst the latter mode draws relative to the last point.
Other Xlib common drawing functions include:
This function behaves much like XDrawLines().
The shape parameter is either Complex, Nonconvex or Convex and controls how the server may configure the shading operation.
The x, y, width and height define a bounding box for the arc. The arc is drawn(Fig. 15.1) from the centre of the box. The angle1 and angle2 define the start and end points of the arc. The angles specify 1/64th degree steps measured anticlockwise. The angle1 is relative to the 3 o'clock position and angle2 is relative to angle1.
Thus to draw a whole circle set the width and height equal to the diameter of the circle and angle1 = 0, angle2 = 360*64 = 23040.
If a window has been obscured then we will have to redraw the window when it gets re-exposed. This is the responsibility of the application and not the X window manager or system. In order to redraw a window we may have to go through all the drawing function calls that have previously been used to render our window's display. However, re-rendering a display in this manner will be cumbersome and may involve some complicated storage methods -- there maybe be many drawing functions involved and the order in which items are drawn may also be important
Fortunately, X provides a mechanism that overcomes these (and other less serious) problems. The use of Xlib Pixmaps is the best approach.
A Pixmap is an off-screen Drawable area.
We can draw to a Pixmap in the same way as we can draw to a Window. We use the standard Xlib graphics drawing functions (Section 15.3) but instead of specifying a Window ID as the Drawable we provide a Pixmap ID. Note, however, that no immediate visual display effect will occur when drawing to a Pixmap. In order to see any effect we must copy the Pixmap to a Window.
The program draw_input2.c (Section 16.3.3) draws to pixmaps instead of to the window.
To create a Pixmap the XCreatePixmap() function should be used. This function takes 5 arguments and returns a Pixmap structure:
When you have finished using a Pixmap it is a good idea to free the memory in which it has been stored by calling:
XFreePixmap(Display*, Pixmap)
.
If you want to clear a Pixmap (not done automatically) use XFillRectangle() to draw a background coloured rectangle that is the dimension of the whole Pixmap or use XClearArea(), XClearWindow() or similar functions.
To copy a Pixmap onto another Pixmap or a Window use:
XCopyArea(Display *display, Drawable source, Drawable destination, GC gc, int src_x, src_y, int width, int height, int dest_x, int dest_y);
where (src_x, src_y) specify the coordinates in the source pixmap where copy starts, width and height specify the dimensions of the copied area and (dest_x, dest_y) are the start coordinates in the destination where pixels are placed.
Fonts are necessary in Motif as all XmString are drawn to the screen using fonts residing in the X system. A font is a complete set of characters (upper-case and lower-case letters, punctuation marks and numerals) of one size and one typeface. In order for a Motif program to gain access to different typefaces, fonts must be loaded onto the X server. All X fonts are bitmapped.
Not all X servers support all fonts. Therefore it is best to check if a specific font has been loaded correctly within your Motif program. There is a standard X application program, xlsfonts, that lists the fonts available on a particular workstation.
Each font or character set name is referred to by a String name. Fonts are loaded into to an Xlib Font structure using the XLoadFont() function with a given Display ID and font name argument. The function returns a Font structure.
Another similar function is XLoadQueryFont() which takes the same arguments as above but returns an XFontStruct which contains the Font structure plus information describing the font.
An example function, load_font() which loads a font named ``fixed'', which should be available on most systems but is still checked for is given below:
void load_font(XFontStruct **font_info) { Display *display; char *fontname = "fixed"; XFontStruct *font_info; display = XtDisplay(some_widget); /* load and get font info structure */ if (( *font_info = XLoadQueryFont(display, fontname)) == NULL) { /* error - quit early */ printf("Cannot load %s font\n", fontname); exit(1); } }
Motif actually possesses its own font loading and setting functions. These include XmFontListCreate(), XmFontListEntryCreate(), XmFontListEntryLoad() and XmFontListAdd() . These are used in a similar fashion to the Xlib functions above except that they return an XmFontList structure. However, in this book, we will be only using fonts at the Xlib level and these Motif functions will not be considered further.
Sometimes we may need to gain more control of events in X. To do this we will need to resort to Xlib. A specific example of this will be met in the Chapter 16 (the draw_input1.c program), where the default interaction, provided via callbacks in Motif, is inadequate for our required form of interaction.
There are many types of events in Xlib. A special XEvent structure is defined to take care of this.
XEvents exist for all kinds of events, including: mouse button presses, mouse motions, key presses and events concerned with the window management. Most of the mouse/keyboard events are self explanatory and we have already studied them a little. Let us look at some window events further:
Note: There is no guarantee that what has previously been drawn to the window will become immediately visible. In fact, it is totally up to the programmer to make sure that this happens by picking up an XExpose event (See Sections 15.4 and 16.3.3 on Pixmaps and DrawingAreas).
Most Motif applications will not need to do this since they can happily run within the standard application main loop event handling model. If you do need to resort to creating your own (Xlib) event handling routines, be warned: it can quickly become complex, involving a lot of Xlib programming.
Since, for the level of Motif programming described in this text, we will not need to resort to writing elaborate event handlers ourselves we will only study the basics of Motif/Xlib event handling and interaction.
The first step along this path is attaching a callback to an XEvent rather than a Widget callback action. From Motif (or Xt) you attach a callback to a particular event with the function XtAddEventHandler(), which takes 5 parameters:
|
) masks
together.
As an example we could set an expose_callbck() to be called by an Expose event by the following function call:
XtAddEventHandler(widget, ExposureMask, False, expose_callbck, NULL);
To set a callback, motion_callbk(), that responds to left or middle mouse motion -- an event triggered when the mouse is moved whilst an appropriate mouse butten is depresses -- we would write:
XtAddEventHandler(widget, Button1MotionMask | Button2MotionMask, False, motion_callbk, NULL);
There are two other steps that need to be done when writing our own event handler. These are:
These two steps are basically what the XtAppMainLoop() takes care of in normal operation.
Two Xt functions are typically used in this context:
In between the retrieving of the next event and dispatching this event you may want to write some code that intercepts certain events.
Let us look at how the XtAppMainLoop() function is coded. Note the comments show where we may place custom application intercept code.
void XtAppMainLoop(XtAppContext app) { XEvent event; for (;;) /* forever */ { XtAppNextEvent(app, &event); /* Xevent read off queue */ /* inspect structure and intercept perhaps? */ /* intercept code would go here */ XtDispatchEvent(&event); } }
PLENTY OF SCOPE HERE