Example 20-1. The animate.c program (continued)
panel = (Panel)xv_create(frame, PANEL,
PANEL_LAYOUT, PANEL_VERTICAL,
NULL);
xv_create(panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Quit",
PANEL_NOTIFY_PROC, exit,
NULL);
xv_create(panel, PANEL_SLIDER,
PANEL_LABEL_STRING, "Millisecs Between Frames",
PANEL_VALUE, 0,
PANEL_MAX_VALUE, 120,
PANEL_NOTIFY_PROC, adjust_speed,
NULL);
xv_create(panel, PANEL_CHOICE,
PANEL_LABEL_STRING, "Glyphs",
PANEL_LAYOUT, PANEL_HORIZONTAL,
PANEL_DISPLAY_LEVEL, PANEL_ALL,
PANEL_CHOICE_STRINGS, "Horse", "Man", "Boy", "Eye", NULL,
PANEL_NOTIFY_PROC, change_glyph,
NULL);
window_fit(panel);
canvas = (Canvas)xv_create(frame, CANVAS,
XV_WIDTH, 64,
XV_HEIGHT, 64,
CANVAS_X_PAINT_WINDOW, TRUE,
NULL);
canvas_win = (Window)xv_get(canvas_paint_window(canvas), XV_XID);
window_fit(frame);
dpy = (Display *)xv_get(frame, XV_DISPLAY);
_font = (Xv_Font)xv_find(frame, FONT,
FONT_NAME, "icon",
NULL);
font = (XFontStruct *)xv_get(_font, FONT_INFO);
gcvalues.font = font->fid;
gcvalues.foreground = BlackPixel(dpy, DefaultScreen(dpy));
gcvalues.background = WhitePixel(dpy, DefaultScreen(dpy));
gcvalues.graphics_exposures = False;
gc = XCreateGC(dpy, RootWindow(dpy, DefaultScreen(dpy)),
GCForeground | GCBackground | GCFont | GCGraphicsExposures,
&gcvalues);
xv_main_loop(frame);
}
void
change_glyph(item, value)
Panel_item item;
int value;
{
cnt = 0;
if (value == 0) {
max_images = ArraySize(horses);
Notifier
The Notifier 469
Example 20-1. The animate.c program (continued)
images = horses;
} else if (value == 1) {
max_images = ArraySize(men);
images = men;
} else if (value == 2) {
max_images = ArraySize(boys);
images = boys;
} else if (value == 3) {
max_images = ArraySize(eyes);
images = eyes;
}
XClearWindow(dpy, canvas_win);
}
/*ARGSUSED*/
Notify_value
animate(client, which)
Notify_client client;
int which;
{
XDrawImageString(dpy, canvas_win, gc, 5, 40, images[cnt], 1);
cnt = (cnt + 1) % max_images;
return NOTIFY_DONE;
}
void
adjust_speed(item, value)
Panel_item item;
int value;
{
if (value > 0) {
timer.it_value.tv_usec = (value + 20) * 1000;
timer.it_interval.tv_usec = (value + 20) * 1000;
notify_set_itimer_func(frame, animate,
ITIMER_REAL, &timer, NULL);
} else
/* turn it off */
notify_set_itimer_func(frame, NOTIFY_FUNC_NULL,
ITIMER_REAL, NULL, NULL);
}
Figure 20-1 shows one frame of an animated horse sequence produced by animate.c.
20.5.4 Handling SIGTERM
The SIGTERM signal is a software terminate signal. If you receive a SIGTERM signal, another
process is telling your application to terminate. Rather than handling this signal with
notify_set_signal_func(), you could use notify_set_destroy_func(). It
takes the form:
Notify_func
notify_set_destroy_func(client, destroy_func)
470 XView Programming Manual
Notify_client client;
Notify_func destroy_func;
Figure 20-1. Output of animate.c
Like the other Notifier functions, this one also returns the previously set function that was
handling this signal. Note that it only interprets SIGTERM—it does not get called if the appli-
cation chooses to kill itself by calling either exit() or xv_destroy() on the base frame
or if the user selects the quit option from the title bar (provided by the OPEN LOOK window
manager).
20.5.5 Handling SIGCHLD
Let’s say that you want to fork a process to run another program.
UNIX requires that you per-
form some housekeeping on that process. The minimum housekeeping required is to notice
when that process dies and to reap it. Normally, the system call wait() is used to do this.
By default, wait() will block until a spawned process has terminated. When wait()
returns, it has information about the process that died. Rather than have the application sit
and wait for a spawned (child) process to die, it should continue processing events and be
notified automatically when the process dies.
To handle this, you can register a wait3 event handler* that the Notifier will call whenever
a child process changes state (dies) by calling the following:
Notify_func
notify_set_wait3_func(client, wait3_func, pid)
static Notify_client client;
Notify_func wait3_func;
int pid;
In the above call, the pid identifies the particular child process that the client wants to wait
for. The wait3_func is the function to call when that child has died. Another reason that
an application should use notify_set_wait3_func() is the semantics of the wait3
*The name wait3 event originates from the wait3 (2) system call. There are other forms of wait including wait(),
wait2(), and wait4(). Since wait4() is not generally available, wait3() is the most efficient form of the
wait functionality.
Notifier
The Notifier 471
(2) system call. wait3 (2) will return with status about any process that has changed state.
If two clients are managing different child processes, they should hear only about their own
process. The Notifier keeps straight which client is managing which process.
20.5.5.1 Reaping dead processes
Many clients using child process control simply need to perform the required reaping after a
child process dies. These clients can use the predefined notify_default_wait3() as
their wait3 event handler. This default handler does nothing but return—the fact that it
handled the event is good enough for UNIX. The Notifier automatically removes a dead pro-
cess’s wait3 event handler.
The code segment in Example 20-2 demonstrates how a wait3 handler can be set up.
Example 20-2. Demonstrating a wait3 handler
#include <xview/notify.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
/* canvas created in another part of the program */
extern Canvas canvas;
/* declare our own wait3 handler */
Notify_value my_wait3_handler();
fork_it(argv)
char *argv[ ];
{
int pid;
/* here’s the fork -- two processes are going to execute code
* from this point down.
*/
switch (pid = fork()) {
case -1:
perror("fork");
return;
case 0: /* this is the child of the fork -- the new process */
/* execute the specified command */
execvp(*argv, argv); /* execvp doesn’t return unless failed */
perror("execvp");
_exit(0); /* don’t call exit() -- man exec() for info */
default: /* this is the parent -- the original process */
/* Register a wait3 event handler */
(void) notify_set_wait3_func(canvas, my_wait3_handler, pid);
}
/* parent returns -- child is happily processing away */
}
static Notify_value
my_wait3_handler(me, pid, status, rusage)
Notify_client me;
int pid;
472 XView Programming Manual
Example 20-2. Demonstrating a wait3 handler (continued)
union wait *status;
struct rusage *rusage;
{
if (WIFEXITED(*status)) {
/* Child process exited with return code */
printf("child exited with status %d\n", status–>w_retcode);
/* Tell the Notifier that you handled this event */
return (NOTIFY_DONE);
}
/* Tell the Notifier that you ignored this event */
return (NOTIFY_IGNORED);
}
Example 20-2 is a simple example for demonstration only. There are other things to consider
for a complete and proper method for forking new processes. See Section 20.8, “Reading
and Writing through File Descriptors,” for a full example program that uses
notify_set_wait3_func().
20.6 Interaction with RPC
XView provides a function that allows XView and RPC to easily work together. RPC pro-
vides for a client and an RPC server. The client makes a remote procedure call to send a data
packet to the server. When the packet arrives, the server calls a dispatch routine, performs
whatever service is requested, sends back the reply, and the procedure call returns to the cli-
ent.
The RPC server is similar to the XView notifier; it waits for requests and dispatches them to a
procedure. The RPC server typically does some initialization (using the function svc_reg-
ister . . . ) and then calls svc_run(), which is similar to xv_main_loop(). XView
works with RPC by incorporating svc_run()’s functionality into the notifier.
If you use an RPC server that also needs to work with XView, you should place the XView
function notify_enable_rpc_svc() in your main() program, and do not call
svc_run(). This function takes an int that tells the notifier whether it should handle RPC
requests.
void
notify_enable_rpc_svc(bool);
int bool;
Using this approach, xv_main_loop() handles incoming RPC requests; dispatching them
just as if svc_run() had been called.*
If notify_enable_rpc_svc() is enabled, performance will be affected by the addi-
tional call to svc_getreqset().
*Internally XView uses svc_getreqset(&ibits) which acts the same way as notify_dispatch(). It
checks svc_fdset and calls svc_getreqset() if a svc_fdset descriptor is readable, meaning a request is
coming in.
Notifier
The Notifier 473
Get Volume 7A: XView 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.