XmNinputCallback@XmCR_INPUT@ButtonPress, ButtonRelease, @@KeyPress, KeyRelease
_ A common convention we've included in this program is the double use of the drawing_area_callback()
function. This technique is known as function overloading, since the same function is used by more than one source.
We are using the routine as the input callback for the DrawingArea widget, as well as the activate callback for the
PushButton gadget. Whenever the PushButton is activated, the callback function is invoked and passed an
XmPushButtonCallbackStruct with the reason field set to XmCR_ACTIVATE.
It is beyond the scope of this book to discuss at length or even introduce the use of Xlib; for that, see Volume One,
Xlib Programming Manual. However, there are a couple of details concerning the use of Xlib functions that are
noteworthy. For efficiency in use of the X protocol, Xlib drawing calls typically do not carry a lot of information
about the drawing to be done. Instead, drawing characteristics such as the foreground and background colors, fill style,
line weight, and so on, are defined in a graphics context (GC), which is cached in the X server. Any drawing function
that wishes to use a particular GC must include the handle returned by a GC creation call.
If many different routines are going to use the same GC, the programmer should try to make the handle to it generally
available. The natural tendency is to declare the GC as a global variable. However, as a program gets large, it is easy
to get carried away with the use of global variables. As a result, programs tend to get overly complicated and
decentralized. To avoid this problem, you can use the XmNuserData resource (inherited from the Manager widget
class) as a temporary holding area for arbitrary pointers and values. Since this program is small, it may not be worth
the overhead of a call to XtGetValues() to avoid a global variable. It is up to you if you want to use the
XmNuserData resource; this particular example just shows one way of avoiding global variables.
If you play with the program a little, you will soon find that you can draw right through the PushButton gadget in the
DrawingArea. Because gadgets do not have windows, the DrawingArea widget indiscriminately allows you to draw
through any gadget children it may be managing. Similarly, activating the PushButton clears the DrawingArea
window, but it does not repaint the PushButton. None of the manager widgets, including the DrawingArea, check if
the user (or the application) is overwriting or erasing gadgets. Changing the PushButton from a gadget to a widget
solves the immediate problem. However, it is generally not a good idea to use a DrawingArea widget as both a
drawing canvas and as a place to have user−interface elements such as PushButtons.
For conventional geometry management involving DrawingArea widgets, you have two choices. You can write your
own geometry management routine (as demonstrated for BulletinBoard widgets in Section #sbboard in Chapter 8,
Manager Widgets) or you can place the DrawingArea inside another manager that does more intelligent geometry
management. The nice part about this alternative is that the other manager widgets are no more or less intelligent
about graphics and repainting than the DrawingArea widget. They don't provide a callback for Expose events, but
you can always add translations for those events, if you need them.
11.2.2 Redrawing a DrawingArea
In the source code when an Expose event or a Resize event occurs, the drawing is not retained and as a result the
DrawingArea is always cleared. This problem was intentional for the first example because we wanted to focus on the
use of the input callback routine. -However, when you use the DrawingArea widget, you must always be prepared to
repaint whatever is supposed to be displayed in the widget at any time.
As you may already know, most X servers support a feature called backing store, which saves the contents of
windows, even when they are obscured by other windows, and repaints them when they are exposed. When backing
store is enabled and there is enough memory available for the server, X will repaint all damaged windows without
ever notifying the application that anything happened. However, you should never rely on this behavior, since you
never know if the X server supports backing store, or if it has enough memory to save the contents of your windows.
All applications are ultimately responsible for redrawing their windows' contents whenever necessary.
11 The DrawingArea Widget 11.2.2 Redrawing a DrawingArea
287
For a painting application like that in the source code the easiest way to make sure that a window can be repainted
whenever necessary is to draw both into the window and into an offscreen pixmap. The contents of the pixmap can be
copied back into the window as needed. the source code demonstrates such a program. The offscreen pixmap is copied
back to the window with XCopyArea() to redisplay the drawing when the XmNexposeCallback is called.
XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4.
/* draw2.c −− extremely simple drawing program that demonstrates
* how to draw into an off screen pixmap in order to retain the
* contents of the DrawingArea widget. This allows us to redisplay
* the widget if it needs repainting (expose events).
*/
#include <Xm/DrawingA.h>
#include <Xm/PushBG.h>
#include <Xm/RowColumn.h>
#define WIDTH 400 /* arbitrary width and height values */
#define HEIGHT 300
Pixmap pixmap; /* used to redraw the DrawingArea */
main(argc, argv)
int argc;
char *argv[];
{
Widget toplevel, drawing_a, pb;
XtAppContext app;
GC gc;
void drawing_area_callback();
XtSetLanguageProc (NULL, NULL, NULL);
toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0,
&argc, argv, NULL,
XmNwidth, WIDTH,
XmNheight, HEIGHT,
NULL);
/* Create a DrawingArea widget. */
drawing_a = XtVaCreateWidget ("drawing_a",
xmDrawingAreaWidgetClass, toplevel,
NULL);
/* add callback for all mouse and keyboard input events */
XtAddCallback (drawing_a, XmNinputCallback, drawing_area_callback, NULL);
XtAddCallback (drawing_a, XmNexposeCallback, drawing_area_callback, NULL);
gc = XCreateGC (XtDisplay (drawing_a),
RootWindowOfScreen (XtScreen (drawing_a)), 0, NULL);
XtVaSetValues (drawing_a, XmNuserData, gc, NULL);
XSetForeground (XtDisplay (drawing_a), gc,
WhitePixelOfScreen (XtScreen (drawing_a)));
/* create a pixmap the same size as the drawing area. */
pixmap = XCreatePixmap (XtDisplay (drawing_a),
RootWindowOfScreen (XtScreen (drawing_a)), WIDTH, HEIGHT,
DefaultDepthOfScreen (XtScreen (drawing_a)));
/* clear pixmap with white */
XFillRectangle (XtDisplay (drawing_a), pixmap, gc, 0, 0, WIDTH, HEIGHT);
/* drawing is now drawn into with "black"; change the gc for future */
XSetForeground (XtDisplay (drawing_a), gc,
BlackPixelOfScreen (XtScreen (drawing_a)));
11 The DrawingArea Widget 11.2.2 Redrawing a DrawingArea
288
/* add a pushbutton the user can use to clear the canvas */
pb = XtVaCreateManagedWidget ("Clear",
xmPushButtonGadgetClass, drawing_a,
NULL);
/* if activated, call same callback as XmNinputCallback. */
XtAddCallback (pb, XmNactivateCallback, drawing_area_callback, NULL);
XtManageChild (drawing_a);
XtRealizeWidget (toplevel);
XtAppMainLoop (app);
}
/* Callback routine for DrawingArea's input and expose callbacks
* as well as the PushButton's activate callback. Determine which
* it is by testing the cbs−>reason field.
*/
void
drawing_area_callback(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
static Position x, y;
XmDrawingAreaCallbackStruct *cbs =
(XmDrawingAreaCallbackStruct *) call_data;
XEvent *event = cbs−>event;
Display *dpy = event−>xany.display;
if (cbs−>reason == XmCR_INPUT) {
/* activated by DrawingArea input event −− draw lines.
* Button Down events anchor the initial point and Button
* Up draws from the anchor point to the button−up point.
*/
if (event−>xany.type == ButtonPress) {
/* anchor initial point (i.e., save its value) */
x = event−>xbutton.x;
y = event−>xbutton.y;
} else if (event−>xany.type == ButtonRelease) {
/* draw full line; get GC and use in XDrawLine() */
GC gc;
XtVaGetValues (widget, XmNuserData, &gc, NULL);
XDrawLine (dpy, cbs−>window, gc, x, y,
event−>xbutton.x, event−>xbutton.y);
/* draw into the pixmap as well for redrawing later */
XDrawLine (dpy, pixmap, gc, x, y,
event−>xbutton.x, event−>xbutton.y);
x = event−>xbutton.x;
y = event−>xbutton.y;
}
}
if (cbs−>reason == XmCR_EXPOSE || cbs−>reason == XmCR_ACTIVATE) {
GC gc;
if (cbs−>reason == XmCR_ACTIVATE) /* Clear button pushed */
widget = XtParent (widget); /* get the DrawingArea widget */
XtVaGetValues (widget, XmNuserData, &gc, NULL);
if (cbs−>reason == XmCR_ACTIVATE) { /* Clear button pushed */
/* to clear a pixmap, reverse foreground and background */
XSetForeground (dpy, gc, WhitePixelOfScreen (XtScreen (widget)));
/* ...and fill rectangle the size of the pixmap */
11 The DrawingArea Widget 11.2.2 Redrawing a DrawingArea
289
Get Volume 6A: Motif Programming Manual now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.