- X has a number of primitive graphics functions, all of these functions work in integer
pixel coordinates, with the origin in the upper left corner of the window, the x axis
running from left to right, and the y axis running from top to bottom
- there are no transformations in X, but the graphics primitives are clipped at window
edges
- the XClearWindow procedure is used to clear an entire window, the declaration of this
procedure is:
XClearWindow(display, window)
Display *display;
Window window;
- the background colour for the window is used in this operation
- an area within a window can be cleared by calling the XClearArea procedure
XClearArea(display, window, x, y, width, height, exposures)
Display *display;
Window window;
int x;
int y;
unsigned int width;
unsigned int height;
Bool exposures;
- this procedure clears a rectangular area of the screen, the upper left corner of this
rectangle is (x,y) and the width height is given by the corresponding parameters
- if width is zero, the width is set to the width of the window minus x, that is the clear
will go from x to the right boundary of the window
- similarly if height is zero, the height is set to the height of the window minus y
- the exposures flag indicates whether an exposure events should be generated for this
operation, if the value of this flag is true then an event will be generated, if it is
false no events will be generated - more on this later
- the clear functions can only be used on windows, all the other graphics functions can be
used on any drawable, that is they can be used with both windows and pixmaps
- there are two routines that can be used for drawing points, the declarations of these
routines are:
XDrawPoint(display, drawable, gc, x, y)
Display *display;
Drawable drawable;
GC gc;
int x;
int y;
XDrawPoints(display, drawable, gc, points, npoints, mode)
Display *display;
Drawable drawable;
GC gc;
Xpoint *points;
int npoints;
int mode;
- the XDrawPoint procedure draws a single point at the pixel given by (x,y), the
foreground colour and function in gc are used to draw this point
- the XDrawPoints function is used to draw more than one point, the number of points to be
drawn is given by the parameter npoints, and points is an array containing the coordinates
of the points
- the declaration of Xpoint is
typedef struct {
short x, y;
} Xpoint;
- the mode parameter specifies how the coordinates in the points array are interpreted
- if the value of this parameter is CoordModeOrigin then the coordinates of the point are
treated as being relative to the origin of the window
- if the value of mode is CoordModePrevious then the corrdinates of point i are treated as
being relative to point i-1
- that is the first point in the array is relative to the origin, the second entry in the
array gives the offset to the second point from the first point, etc
- there are three procedures that can be used to draw lines, the simplest of these is
XDrawLine
XDrawLine(display, drawable, gc, x1, y1, x2, y2)
Display *display;
Drawable drawable;
GC gc;
int x1, y1;
int x2, y2;
- this procedure draws a single line, in the drawable given by the parameter drawable
- the end points of the line are (x1, y1) and (x2, y2)
- the appearance of the line is given by the gc parameter, this includes the colour of the
line, its thickness and the function used to draw it
- the XDrawLines procedure is used to draw polyline, the declaration of this procedure is:
XDrawLines(display, drawable, gc, points, npoints, mode)
Display *display;
Drawable drawable;
GC gc;
Xpoint *points;
int npoints;
int mode;
- this procedure draws a polyline that connects the points given by the array points, the
parameter npoints contains the number of points in this array
- if the width of the line is greater than 1, X treats the joins between the line segments
according to the specification in the GC parameter
- the mode parameter is interpreted in the same way as in the XDrawPoints procedure
- if line segments are connected this procedure should be used to draw them, since it
attempts to do a nice job of the joint between line segments
- the XDrawSegments procedure can be used to draw more than 1 non-connected line segments,
the declaration of this procedure is
XDrawSegments(display, drawable, gc, segments, nsegments)
Display *display;
Drawable drawable;
GC gc;
XSegment segments;
int nsegments;
- the parameter segments is an array containing the end points of the line segments to be
drawn
- the definition of XSegment is
typedef struct {
short x1, y1, x2, y2;
} Xsegment;
- it is assumed that segments don't share end points and no special processing is done at
the end of the segments
- the parameter nsegments is the number of entries in the segments array
- the XDrawRectangle procedure can be used to draw rectangles
XDrawRectangle(Display, drawable, gc, x, y, width, height)
Display *display;
Drawable drawable;
GC gc;
int x
int y;
unsigned int width;
unsigned int height;
- this procedure draws the outline of the rectangle that has its origin at (x,y) and the
width of the rectangle is given by the width parameter and the height of the rectangle is
given by the height parameter
- the sides of the rectangle drawn by this procedure are parallel to the x and y axis of
the window
- multiple rectangles are drawn by the XDrawRectangles procedure, the declaration of this
procedure is:
XDrawRectangles(display, drawable, gc, rectangles, nrectangles)
Display *display;
Drawable drawable;
GC gc;
XRectangle *rectangles;
int nrectangles;
- the parameter rectangles is an array of rectangles, and the parameter nrectangles is the
number of rectangles in this array
- the declaration of the XRectangle data structure is:
typedef struct {
int x, y;
unsigned short width, height;
} XRectangle;
- filled rectangles can be drawn using the following two procedures
XFillRectangle(display, drawable, gc, x, y, width, height)
Display *display;
Drawable drawable;
GC gc;
int x;
int y;
unsigned int width;
unsigned int height;
XFillRectangles(display, drawable, gc, rectangles, nrectangles)
Display *display;
Drawable drawable;
GC gc;
XRectangle *rectangles;
int nrectangles;
- these routines behave in essentially the same way as the previous two routines, except
that the inside of the rectangle is filled
- a filled polygon can be produced by calling the XFillPolygon procedure, this procedure
has the following declaration:
XFillPolygon(display, drawable, gc, points, npoints,
shape, mode)
Display *display;
Drawable drawable;
GC gc;
Xpoint *points;
int npoints;
int shape;
int mode;
- the array points contains the polygon vertices and the parameter npoints contains the
number of points in this array, the inside of the polygon defined by these vertices is
filled
- the shape parameter is used to optimize the polygon filling operation, this parameter
gives the server hints about the shape of the polygon, there are three possible values for
this parameter
- Complex - the polygon can have any shape, the path defined by the vertices can
intersect, so polygons that look like bow ties can be filled
- Nonconvex - the polygon could be concave, but the path defined by the vertices doesn't
intersect, this is not as general but more efficient
- Convex - the polygon must be convex, this is the least general case, but it is the most
efficient
- the value of the mode parameter can be either CoordModeOrigin or CoordModePrevious
- X can also draw arcs and filled arcs, the basic arc drawing procedure is:
XDrawArc(display,drawable,gc,x,y,width,height,angle1,angle2)
Display *display;
Drawable drawable;
GC gc;
int x;
int y;
unsigned int width;
unsigned int height;
int angle1;
int angle2;
- this procedure draws with a circular or elliptical arc, the parameters x, y, width, and
height define a rectangle that the arc is draw inside of, if width and height are equal
then a circular arc is produced, otherwise an elliptical arc is produced - this rectangle
defines the size and position of the circle or ellipse that the arc is part of
- the two parameters angle1 and angle2 define the start and end of the arc, both of these
parameters are measured clockwise starting at the positive x-axis, in other words a line
parallel to the x-axis pointing to the right has an angle of zero
- the arc runs counter clockwise starting at angle1 and going to angle2
- the unit of measurement for the angles is degrees/64 and is an integer, to draw a
complete circle angle1 can be set to zero and angle2 set to 360*64
- in the case of elliptical arcs the angles are measured relative to the containing
rectangle, for example a 45 degree line will go through the upper right corner of the
rectangle, that is the angles really aren't specified in degrees when the rectnagle isn't
a square
- multiple arcs can be draw using the XDrawArcs procedure, which has the following
declaration:
XDrawArcs(display,drawable,gc,arcs,narcs)
Display *display;
Drawable drawable;
GC gc;
XArc *arcs;
int narcs;
- the arcs parameter is an array of arcs, and the narcs parameter is the length of this
array
- the declaration of the XArc data structure is:
typedef struct {
short x, y;
unsigned short width, height;
short angle1, angle2;
} XArc;
- the following two procedures can be used to draw filled arcs
XFillArc(display,drawable,gc,x,y,width,height,angle1,angle2)
Display *display;
Drawable drawable;
GC gc;
int x;
int y;
unsigned int width;
unsigned int height;
int angle1;
int angle2;
XFillArcs(display,drawable,gc,arcs,narcs)
Display *display;
Drawable drawable;
GC gc;
XArc *xarcs;
int narcs;
- the X server is responsible for displaying the information in a window, but the client
program executes the commands that specify the information to be drawn in that window
- all the server has is the actual pixels in the window, it doesn't have any description
of the geometry
- this works okay as long as the window is always visible and the user never changes it
size, problems occur when a window is covered and then uncovered, or the user changes the
size of a window
- if a covered part of a window is uncovered, the X server must be able to draw the
freshly exposed part of the window, it needs to know the information to be drawn their
- this could be handled in the server by keeping a copy of all the covered parts of
windows, the contents of the window could be stored in a pixmap and then copied to the
newly exposed part of the window, this has two problems:
- the pixmaps must be stored in the server, for colour displays this requires a
significant amount of memory
- all graphics must be drawn in both the window and the pixmap, this slows down all
graphics operations by a factor of 2
- some X servers do maintain copies of all windows, or have it as an option, but an
application program can't rely on this
- if the window resized there is nothing that the server can do to help, when the window
is resized the contents of the window must be redrawn taking into account the new window
size, this often involves scaling the information that is displayed
- in this case the client program must redraw the window, since it is the only program
that knows how to resize the information in the window
- X has a general mechanism, called events, that can be used to signal when the user has
done something
- these events are usually generated by the server and sent to one or more of the client
programs, these events inform the client programs that they must do something
- as we will see later these events can be used for input processing or for sending
information from one client program to another
- the X server generates an Expose event when the contents of a window must be redrawn
- at the Xt level we can use event handlers to process the events that are sent from the X
server
- an event handler is a procedure, that is like a callback procedure, that can be attached
to a widget, this procedure is used to process a particular type of event that is sent to
the window for that widget
- event handlers have three parameters, the widget where the event occured, client data,
and an event structure, the contents of the event structure depends upon the type of the
event
- an event handler procedure is declared in the following way:
void event_handler(widget, client_data, event, continue)
Widget w;
XtPointer client_data;
XEvent *event;
Boolean *continue;
- the continue parameter is optional, this parameter is a pointer to a boolean variable
that is initialized to True before the event handler is called, if this variable is set to
false by the event handler the event will not be sent to any other event handlers, this is
normally not done, so most of the time this parameter is ignored
- in the case of an exposure event, the event structure is:
typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
int x, y;
int width, height;
int count;
} XExposeEvent;
- most of the fields of this event structure will be discussed later, the last 5 fields
are the ones that are of interest to us now
- each time part of a window is exposed an exposure event is generated, this event
contains a part of the window that is now visible, each of these regions is a rectangle
that is given by the x, y, width and height parameters
- note that there may be several of these areas generated when part of the window is
exposed, so several exposure events may be generated by a single user action
- the count field contains the number of exposure events that follow this exposure event,
if this field is zero there are no more events to follow this one, this is the last
exposure event for the user action
- these fields allow us to optimize the redrawing operation
- for example, we may only want to redraw if the count field is zero, at that point we
redraw the entire contents of the window, once instead of on each exposure event
- similarly, the rectangle in the exposure event can be used to just redraw the part of
the window that has been exposed, this is used when redrawing the contents of the entire
window is quite expensive, we only need to update the part that needs to be changed
- an event handler is added to a widget by calling the XtAddEventHandler procedure, the
declaration of this procedure is:
XtAddEventHandler(widget, event_mask, nonmaskable, proc,
client_data)
Widget w;
EventMask event_mask;
Boolean nonmaskable;
XtEventHandler proc;
XtPointer client_data;
- the event mask parameter specifies the events that the event handler processes, this
field is the or of the codes for these events
- for the exposure event the event code is ExposureMask
- the nonmaskable parameter is set to TRUE if the event handler is used for nonmaskable
events, we will talk about these later, for the time being FALSE should be used as the
value for this parameter
- the parameter proc is the event handler procedure, and the parameter client_data is the
client data to be sent to the event handler
- an event handler can be removed by calling the XtRemoveEventHandler procedure, which has
the following declaration:
XtRemoveEventHandler(widget, event_mask, nonmaskable, proc,
client_data)
Widget widget;
EventMask event_mask;
Boolean nonmaskable;
XtEventHandler proc;
XtPointer client_data;
- the parameters to this procedure are essentially the same as XtAddEventHandler
- for each event in the event_mask, the event handler given by the proc parameter will no
longer be called and if the nonmaskable parameter is TRUE this event handler will no
longer be called for nonmaskable events
- now that we can handle exposure events we can use a core widget for displaying graphical
information, the following example program shows how this can be done
/**************************************************************
*
* drawing.c
*
* A simple program that shows how to set up a widget for
* drawing, and how the Xlib drawing routines are used.
*
**************************************************************/
#include < X11/StringDefs.h >
#include < X11/Intrinsic.h >
#include < X11/Core.h >
#include < X11/Xaw/Box.h >
#include "../lib/lib.h"
void redisplay_event(w, client, ev)
Widget w;
XtPointer client;
XExposeEvent *ev; {
if(ev->count != 0)
return;
XClearWindow(XtDisplay(w),XtWindow(w));
draw_graphics(w);
}
main(argc,argv)
int argc;
char **argv; {
Widget toplevel;
Widget box;
Widget drawing;
Widget quit;
int n;
Arg wargs[10];
toplevel = XtInitialize(argv[0],"drawing", NULL, 0,
&argc, argv);
box = XtCreateManagedWidget("box", boxWidgetClass,
toplevel, NULL, 0);
quit = quit_button(box);
drawing = XtCreateManagedWidget("drawing",coreWidgetClass,
box, NULL, 0);
n = 0;
XtSetArg(wargs[n], XtNheight, 300); n++;
XtSetArg(wargs[n], XtNwidth, 300); n++;
XtSetValues(drawing, wargs, n);
XtAddEventHandler(drawing, ExposureMask, FALSE,
redisplay_event, NULL);
XtRealizeWidget(toplevel);
XtMainLoop();
}
draw_graphics(w)
Widget w; {
Display *display;
Drawable window;
GC gc;
display = XtDisplay(w);
window = XtWindow(w);
gc = XCreateGC(display, window, NULL, NULL);
XSetForeground(display, gc, 1);
XSetBackground(display, gc, 0);
XDrawLine(display, window, gc, 10, 10, 100, 100);
XDrawRectangle(display, window, gc, 75, 110, 150, 100);
XDrawArc(display, window, gc, 75, 110, 150, 100, 45*64, 120*64);
XFreeGC(display, gc);
}