This chapter describes the steps you follow to develop a typical application for the Display Postscript system and explains the steps that such an application performs. The chapter then presents a sample application.
Before reading this chapter, be sure you understand the following components and concepts, covered in Chapter 2; if you understand these, you are ready to start:
To develop a typical application, you follow six main steps, as shown in Figure 3-1. (Steps 3 through 5 take much less time than the others.)
Step 1: Design the Application
In WS, Display PostScript system applications are written in C and send PostScript language code to a context, usually an X server. To design an application you must make several decisions; For example, you must decide:
For a typical simple application, the following design decisions are usually best:
A complete discussion of application design is beyond the scope of this book. To help you see and understand how design decisions affect XDPS applications, the WS distribution kit includes source files for several sample applications. (For more information about these sample applications, see Section 3.5.)
Step 2: Write Your C Code and PostScript Language Code
After you have designed your application, you write the C-language code and the PostScript language procedures that your application sends.
It is also possible to write applications that read PostScript language code from the user's keyboard or from a file. For a sample program of this type, see the program DPStest. By default, the source files for DPStest are installed in the directory /usr/examples/dps/dpstest. (For instructions on running the program, see Section 3.5.)
Step 3: Convert Your PostScript Language Procedures
If you have written any PostScript language procedures for your application, you should convert them to wraps, that is, to routines that can be called from your C-language code. To convert the PostScript language procedures, you process them with the pswrap translation program.
For each PostScript language input file, pswrap can produce two output files: a C-callable procedure and an associated header file.
Steps 4 and 5: Compile and Link
After you have converted your PostScript language procedures to C-callable routines, you compile and link your source files. That is, you compile your main C-language file with:
You link your application with the Client Library and with the X libraries. (For instructions on compiling and linking XDPS applications, see Section 3.4.)
Step 6: Run and Debug Your Application
You are now ready to run and debug your application.
All applications send PostScript language statements to a context. Typically, the context is an execution context - in XDPS, the PostScript interpreter of an X server. Most XDPS applications perform three main steps:
Step 1: Initialization
Typically, to initialize an XDPS application, you perform three steps:
Step 2: Communication
After initializing, most XDPS applications call custom wraps, singleops, or other Client Library routines to send text and PostScript language statements to a context. For example, to send information to a context, an application might either call a custom wrap or call one of two Client Library routines: DPSWritePostScript (for PostScript language statements) or DPSWriteData (for data).
To process text or errors from a context, the Client Library calls the text-handling routine or error-handling routine that the application assigned when creating the context. The Client Library defines a default text-handling routine (DPSDefaultTextBackstop) and a default error-handling routine (DPSDefaultErrorProc). Although these routines are called default routines, to use them you must specify them explicitly when creating a context. (For more information on the default routines, see their descriptions in Chapter 5.)
Step 3: Termination
Terminating a typical XDPS application is like terminating any other typical X application. When you terminate an application, the X Window System destroys the application's contexts, their spaces, and any other X resources belonging to the application.
This section presents examplemain, a simple program that shows the fundamentals of XDPS programming. The program uses the Display PostScript system to paint a shaded square in a window of the user's screen, as shown in Figure 3-2.
The examplemain program uses the Xlib interface to X, calls a custom wrap to pick the shade of gray for painting, and calls a Client Library single-operator procedure to do the actual painting.
The sample application examplemain performs the following operations:
Unlike a more complete application, examplemain does not handle resizing of the X window. (For information about window resizing in XDPS applications, see Section 4.5.)
Example 3-1 is a complete listing of examplemain.c, the main C language file of the sample application.
/* * examplemain.c -- Simple X application that uses the DPS * system to draw a shaded square in a window, then exits * when the user clicks the mouse. */
#include <stdio.h>
#include <Xlib.h> /* Standard X Window C-lang library */ #include <dpsXclient.h> /* X interface to DPS Client Library */ #include <dpsops.h> /* Declarations of singleops */
#include "examplewraps.h" /* Interface to wrapped PS lang code*/
main () { Display *dpy; /* An X display */ Window window; /* A window of the X display */ DPSContext context; /* A single PostScript context */ float grayLevel; /* The shade of gray for the square */ XEvent event; /* An X event */ void TextOut(); /* Forward declaration */ void FatalError(); /* Forward declaration */ /* * Open a connection to the X display specified in the arg * to the XOpenDisplay routine. The NULL argument causes * XOpenDisplay to open a connection to the display specified * by the DISPLAY variable of the user's environment. */ dpy = XOpenDisplay(NULL); /* * If unable to open the display, return an error message and * exit immediately. */ if (dpy == NULL) FatalError("Can't open display.\n"); /* * Create a window on the X display. When mapped, the * window will be 10 pixels from the left edge and 20 pixels * from the upper edge. The window will be 800 pixels high * by 800 pixels wide, with a black border 1 pixel wide * and a white background. */ window = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 10, 20, 800, 800, 1, BlackPixel(dpy, DefaultScreen(dpy)), WhitePixel(dpy, DefaultScreen(dpy))); /* * Select the X event types that the window accepts from * the X server. The window accepts Expose events and * ButtonPress events. */ XSelectInput(dpy, window, ExposureMask | ButtonPressMask); /* * Create a PostScript execution context to draw in the window. * The origin of the context's coordinate grid is the point * (0, 800) of the window. The origin is therefore the bottom * left corner of the window (the typical origin for a * PostScript context). */ context = XDPSCreateSimpleContext(dpy, window, DefaultGC(dpy, DefaultScreen(dpy)), 0, 800, TextOut, DPSDefaultErrorProc, NULL); /* * If unable to create the context, return an error message * and exit immediately. */ if (context == NULL) FatalError("DPS refused to create a context.\n"); /* * Map the window--that is, make it appear on the display. * The window will appear only after the window manager of * the X server is free to process the mapping request. * When the window appears, the context receives an Expose * event as notification. */ XMapWindow(dpy, window); /* * Generate a random number that corresponds to the shade * of gray (the graylevel) to be used when painting. * To generate this number, call the ChooseGray routine, * which is exported from the examplewraps.c file. * ChooseGray sends wrapped PostScript language code to * the context, which then executes the code. */ ChooseGray(context, &grayLevel); /* * Set the current graylevel to the shade of gray chosen by * ChooseGray. Setting the graylevel does not cause any * painting; so you can set the graylevel even if the window * has not yet appeared. */ DPSsetgray(context, grayLevel); /* * Wait for events from the X server; process each one * received. For each Expose event, paint the same gray square * in the same place on the display. To do this, call the * DPSrectfill routine, a single-operator wrapped procedure * declared in dpsops.h, a DPS Client Library header file. * The bottom left corner of the square is 100 points above * the origin and 100 points to the right of it. Each side of * the square is 300 points. * * When a ButtonPress event is received, exit the * event-processing loop. */ for (;;) { XNextEvent(dpy, &event); if (event.type == Expose) { DPSrectfill(context, 100.0, 100.0, 300.0, 300.0); } else if (event.type == ButtonPress) { break; } } /* * Exit in an orderly manner. First, destroy the context by * destroying its space (its memory). Next, destroy * the window. Finally, close the connection to the X display. */ DPSDestroySpace(DPSSpaceFromContext(context)); XDestroyWindow(dpy, window); XCloseDisplay(dpy); } /* * Output procedure for plain text messages from the context. * Output is sent directly to standard error. */ void TextOut(context, buffer, count) DPSContext context; char *buffer; unsigned count; { fwrite(buffer, 1, count, stderr); fflush(stderr); } /* * Error procedure. The application has encountered an error * from which it cannot recover, so exit immediately. */ void FatalError(msg) char *msg; { fprintf(stderr, msg); exit(1); }
Example 3-2 is a complete listing of examplewraps.psw, the PostScript language source file for the wrapped procedure called by the sample application examplemain.
Processing examplewraps.psw with the pswrap translator produces two output files: examplewraps.c and examplewraps.h. These output files must then be compiled with examplemain.c.
/* * examplewraps.psw -- source file for wrapped PostScript * language procedure * * This is an example of PostScript language code to be converted * to Client Library calls by pswrap. * * This PostScript language routine, ChooseGray, generates a random * number that corresponds to the graylevel (shade of gray) to be * used when the Display PostScript system paints. Note that the * PostScript operator rand always generates the same sequence of * random numbers. So each time the program examplemain runs, * ChooseGray chooses the same graylevel. */
defineps ChooseGray (DPSContext ctx| float *result) rand % Pick a random number between 0 and 2^31 - 1. 2 31 exp % 2^31 div % Random number between 0.0 and 1.0 result % Return result. endps
By default, all the program-specific files needed to compile, link, and run examplemain are installed in the /usr/examples/dps/gray-square directory of your system. For instructions on compiling and linking, see Section 3.4.
After you code an application, you build it by compiling and linking it. The following sections describe how to build an application, assuming that you are using the WS make utility. (For more information, see the make(1) reference page.)
Section 3.4.5 includes a complete makefile for the examplemain program presented in Section 3.3.2.
Before building an XDPS application, make sure that the main source module includes the appropriate X header files and the WS-specific Client Library header file, dpsXclient.h.
The dpsXclient.h file is the only Client Library header file that all XDPS applications must include. It, in turn, includes all other Client Library header files, except psops.h, dpsops.h, and dpsexcept.h.
If your application calls singleops, you should also include psops.h or dpsops.h, or both, depending on which defines the singleops that your application calls. If your application uses the exception handling capability of the Display PostScript system, you must also include dpsexcept.h. (Not to be confused with error handling; exception handling is an advanced capability that few applications require.)
You compile the main C-language module of your XDPS application with:
The Display PostScript system header files (among them, dpsXclient.h, psops.h, and dpsops.h) are installed in the directory /usr/include/DPS. To automatically include these files at compilation, add the following statement to your makefile:
CFLAGS = -I/usr/include/DPS
The option -I/usr/include/DPS causes the Workstation Software C compiler to search for include files in /usr/include/DPS.
You link your XDPS application with the following libraries, in the order listed:
Library | Linker Option |
Client Library | -ldps |
Xlib extensions for Display PostScript system | -lXext |
DECwindows toolkit library | -ldwt |
Xlib library | -lX11 |
Workstation Station math library | -lm |
Your makefile can automatically convert PostScript language procedures to
C-callable routines by running the
pswrap
translation program. For example, if the PostScript language procedures
have file names ending in
.psw,
the following
make
statements convert the procedures automatically:
.SUFFIXES: $(.SUFFIXES) .psw .h .psw.o: $*.psw ${PSWRAP} -o $*.c $*.psw $(CC) $(CFLAGS) -c $*.c rm $*.c
.psw.h: $*.psw ${PSWRAP} -h $*.h $*.psw > /dev/null
Example 3-3 shows a complete Makefile that builds the examplemain program presented earlier in this chapter.
# @(#)Makefile 1.5 9/2/88
DESTDIR= EXAMPLETOPDIR=${DESTDIR}/usr/examples/dps EXAMPLESUBDIR=${EXAMPLETOPDIR}/gray-square
INSTALLLIST = Makefile examplemain.c *.psw
OBJS = examplemain.o examplewraps.o
PSWRAP= ${DESTDIR}/usr/bin/pswrap
.SUFFIXES: $(.SUFFIXES) .psw .h .psw.o: $*.psw ${PSWRAP} -o $*.c $*.psw $(CC) $(CFLAGS) -c $*.c rm $*.c
.psw.h: $*.psw ${PSWRAP} -h $*.h $*.psw > /dev/null
.SUFFIXES: .uil .uid
CFLAGS = -g -I${DESTDIR}/usr/include/X11 \ -I${DESTDIR}/usr/include/DPS \ -I${DESTDIR}/usr/include -I.
LIBS = ${DESTDIR}/usr/lib/libdps.a \ ${DESTDIR}/usr/lib/libXext.a \ ${DESTDIR}/usr/lib/libdwt.a \ ${DDIFROOT}/usr/lib/libddif.a \ ${DESTDIR}/usr/lib/libX11.a \ -lm
all: examplemain
examplemain: $(OBJS) $(CC) -o examplemain $(OBJS) $(LIBS)
examplemain.o: examplemain.c examplewraps.h
clean: rm -f *.o examplemain examplewraps.[ch] \#* *~ core
clobber: clean -rm -f *
relink:: rm -f examplemain
relink:: all
In addition to examplemain, the WS software includes source listings of several other sample XDPS applications.
WS includes source listings and makefiles for four related sample programs: calc0, calc1, calc2, and calc3. Each of these sample programs is an implementation of the same application: a desktop calculator. Although all four programs present a similar user interface (shown in Figure 3-3), the source code of each program shows a different approach to XDPS application design.
For the location of the sample calculator programs, see Table 3-1, which lists and describes the sample Display PostScript system applications included in WS.
Program Name | Description | Location |
calc0 | Calculator coded mainly in C, with one window and one context | /usr/examples/dps/calc0 |
calc1 | Calculator coded mainly in the PostScript language, with one window and one context | /usr/examples/dps/calc1 |
calc2 | Calculator coded mainly in C, with multiple windows and one context | /usr/examples/dps/calc2 |
calc3 | Calculator coded mainly in C, with multiple windows, multiple contexts, and intercontext communication | /usr/examples/dps/calc3 |
DPStest | Executes PostScript language statements entered from the keyboard | /usr/examples/dps/dpstest |
examplemain | Displays a gray square generated from a custom wrap and a singleop | /usr/examples/dps/gray-square |
psclock | An implementation of xclock that uses the Display PostScript system | /usr/examples/dps/psclock |
psdraw | A graphic editor that paints PostScript language images; a complex sample application | /usr/examples/dps/psdraw |
pyro | Displays fireworks generated from custom wraps | /usr/examples/dps/pyro |
To run a sample application, you must first build it by following these steps:
You can then run the program by entering its name at the system prompt. (For more information on building XDPS applications, see Section 3.4.)
Table 3-2 lists common XDPS programming tasks, shows the operators (in bold type), and Client Library routines for performing each task.
Task | Associated Routines and Operators |
Create an execution context | XDPSCreateSimpleContext or XDPSCreateContext |
Create a text context | XDPSCreateTextContext |
Use the default text handler | DPSDefaultTextBackstop |
Use the default error handler | DPSDefaultErrorBackstop |
Find the space of a context | DPSSpaceFromContext |
Find the default user space origin | currentXoffset |
Set the default user space origin | setXoffset |
Find the GC of a context | currentXgcdrawable |
Set the GC of a context | setXgcdrawable |
Restart a context | DPSResetContext |
Find the current drawable | currentXgcdrawable |
Set the current drawable | setXgcdrawable |
Convert between PostScript language IDs and XIDs | XDPSXIDFromContext XDPSXIDFromSpace XDPSContextFromXID XDPSSpaceFromXID |
Destroy a space | DPSDestroySpace |
Destroy a context | DPSDestroyContext |