interpose a new function in front of the object’s client-destroy event handler using the fol-
lowing routine:
Notify_error
notify_interpose_destroy_func(client, destroy_func)
Notify_client client;
Notify_func destroy_func;
For an XView object, destruction may originate from several sources:
• The application may be terminated by a software interrupt from the calling process.
• The connection to the server has been lost and the Notifier is informing the application
that it is dying.
• xv_destroy(
object)
was called from within the application.
• The user may select the “Quit” menu item from the window manager’s pulldown menu.
Each of these scenarios results in a different destruction method. When the object is going to
be destroyed, the process happens in two phases. First, the object’s destroy-interposer is
called, informing it of the impending destruction. At this point, the interposer can veto the
destruction or it can allow it to take place—at which time the Notifier proceeds to phase two,
the actual object destruction.
Destroy event handlers use a status parameter to determine which phase of destruction the
Notifier is in—whether the Notifier is requesting if it is feasible for the client to be ter-
minated at present (phase one, DESTROY_CHECKING) or if it is making a request to terminate
(phase two, DESTROY_CLEANUP or DESTROY_PROCESS_DEATH).
The destroy-interpose function takes the following form:
Notify_value
destroy_func(client, status)
Notify_client client;
Destroy_status status;
typedef enum destroy_status {
DESTROY_PROCESS_DEATH,
DESTROY_CHECKING,
DESTROY_CLEANUP,
DESTROY_SAVE_YOURSELF,
} Destroy_status;
If the status argument is DESTROY_CHECKING and the client cannot terminate at present,
the destroy event handler should call notify_veto_destroy(), indicating that termina-
tion would not be advisable at this time, and return normally. If the client can terminate at
present, then the destroy handler should do nothing; a subsequent call will tell the client to
actually destroy itself. This veto option is used, for example, to give a text subwindow the
chance to ask the user to confirm the saving of any editing changes when quitting a tool.
If status is
DESTROY_PROCESS_DEATH, then the client can count on the entire process
dying and should do whatever it needs to do to clean up its outside entanglements, such as
updating a file used by a text subwindow. Since the process is about to die, it need not free
allocated memory which is implicitly freed by the process’s termination.
494 XView Programming Manual
Since the entire process is dying, you cannot display a notice or do any sort of prompting
with the user to try to veto this request.
However, if status is DESTROY_CLEANUP then the client is asked to destroy itself and to be
very tidy about cleaning up all the process internal resources that it is using, as well as its
outside entanglements. This may be called on frames which are not the sole frames used by
the application. If a frame is dismissed but the application is still running (e.g., dismissing a
pinned up menu), then the frame should clean up—including freeing allocated memory.
If the status is set to DESTROY_SAVE_YOURSELF when the window manager has sent a
WM_SAVE_YOURSELF message to all the clients on the desktop, this means the user may have
selected the “Save Workspace” option from an OPEN LOOK window manager’s menu. Basi-
cally, this means that the application should save its current state in such a way that it could
be resumed in the same state at a later time. For example, a text editing program would
update the file being edited, a graphics program would output its image display to a file, or
whatever.
When the user selects “Save Workspace” from an OPEN LOOK window manager, XView sets
its status to DESTROY_SAVE_YOURSELF and then checks to see if the attribute
FRAME_WM_COMMAND_ARGC_ARGV is set. XView informs the window manager of the option
and the window manager writes the application’s default command-line options to
˜/.openwin-init. If FRAME_WM_COMMAND_ARGC_ARGV is set, the window manager
appends any user-specified command-line options to the default values written to
˜/.openwin-init.
Normally FRAME_WM_COMMAND_ARGC_ARGV is set when an application is initialized with
xv_init(). In the special case when there are two base frames for an application,
FRAME_WM_COMMAND_ARGC_ARGV should be set to -1 for one of the base frames (and for
additional subsequent base frames for the application). This prevents the application from
appending two sets of command-line options to ˜/.openwin-init.
20.9.8.1 Interposing a client destroy handler
We present an example of interposing in front of the frame’s client-destroy event handler.
The following program displays a frame, a panel, and a panel button labeled “Quit.” When
the user chooses the panel button or the frame’s “Quit” menu selection, the interposer is
called and indicates the fact that the frame is about to go away.
Example 20-6. The interpose.c program
/*
* interpose.c -- shows how to use an interpose destroy function
*/
#include <xview/xview.h>
#include <xview/frame.h>
#include <xview/panel.h>
#include <xview/notice.h>
Frame frame;
Notify_value
destroy_func(client, status)
Notifier
The Notifier 495
Example 20-6. The interpose.c program (continued)
Notify_client client;
Destroy_status status;
{
if (status == DESTROY_CHECKING) {
int answer = notice_prompt(client, NULL,
NOTICE_MESSAGE_STRINGS, "Really Quit?", NULL,
NOTICE_BUTTON_YES, "No",
NOTICE_BUTTON_NO, "Yes",
NULL);
if (answer == NOTICE_YES)
notify_veto_destroy(client);
} else if (status == DESTROY_CLEANUP) {
puts("cleaning up");
/* allow frame to be destroyed */
return notify_next_destroy_func(client, status);
} else if (status == DESTROY_SAVE_YOURSELF)
puts("save yourself?");
else
puts("process death");
return NOTIFY_DONE;
}
main (argc, argv)
int argc;
char *argv[ ];
{
Panel panel;
int quit();
xv_init (XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);
frame = (Frame)xv_create (NULL, FRAME,
FRAME_LABEL, argv[0 ],
XV_WIDTH, 200,
XV_HEIGHT, 100,
NULL);
notify_interpose_destroy_func(frame, destroy_func);
panel = (Panel)xv_create (frame, PANEL, NULL);
(void) xv_create (panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Quit",
PANEL_NOTIFY_PROC, quit,
NULL);
xv_main_loop(frame);
}
int
quit()
{
xv_destroy_safe(frame);
return XV_OK;
}
496 XView Programming Manual
The first time the interposer is called, the status is DESTROY_CHECKING. Here, we display a
notice prompting the user to confirm the quit. We want the default action to be “No,” so we
make the NOTICE_YES button have the label “No” and the NOTICE_NO button have the label
“Yes.” This is because the notice’s default button is the NOTICE_YES button.
If the user selects the default “No” choice, then notify_veto_client() is called, veto-
ing the destruction request. Otherwise, the routine returns and the destruction sequence con-
tinues as usual. The routine is then called again, notifying that it is being destroyed this time.
It calls notify_next_destroy_func() to allow the process to continue. This is where
any process cleaning up should take place.
20.9.8.2 Enabling panel item interposition
The attribute PANEL_POST_EVENTS saves the application from the burden of installing a base
event handler on a panel item. This replaces the default event handler, which for perfor-
mance reasons, does not use the notifier. Setting attribute PANEL_POST_EVENTS to TRUE
replaces the default panel item event handler with another one that calls
notify_post_event(). Note that setting this attribute alone makes no functional dif-
ference between the panel item and any other panel items (besides the difference in perfor-
mance due to events being dealt through the notifier rather than being sent directly).
If
PANEL_POST_EVENTS is TRUE, a client may then proceed to call notify_inter-
pose_event_func() on a panel item. All interposers on panel items set up this way
should be of type NOTIFY_IMMEDIATE. The interposer reaches the next or default event
handler via the notify_next_event_func() call.
This attribute has no bearing whatsoever on the PANEL_EVENT_PROC mechanism. The client
may still replace the default event handler for a particular event by using
PANEL_EVENT_PROC.
The following example illustrates interposing on a panel item:
ptxt = xv_create(panel, PANEL_TEXT,
PANEL_LABEL_STRING, "Text:",
PANEL_POST_EVENTS, TRUE,
NULL );
/* make textfield all upper case */
notify_interpose_event_func(ptxt, all_upper, NOTIFY_IMMEDIATE);
[...............]
Notify_value
all_upper ( client, event, arg, type )
Notify_client client;
Notify_event event;
Notify_arg arg;
Notify_event_type type;
{
int id = event_action((Event *) event);
if ( isascii(id) && islower(id) )
event_set_action((Event *) event, toupper(id));
Notifier
The Notifier 497
else if ( id == ACTION_PASTE )
/* whatever, ..... */
/*
* panel_default_handle_event, text_handle_event,
* next interposer, .....
*/
return notify_next_event_func(client, event, arg, type);
}
20.10 Notifier Control
The Notifier is started automatically by calling xv_main_loop(). The function
xv_main_loop() takes a window object (typically the base frame of the application) and
sets XV_SHOW to TRUE. Then it calls notify_start(), and the Notifier is running. The
function notify_start() loops through the Notifier’s processing loop, waiting for events
in which its clients have expressed interest. This continues until the application calls
notify_stop() or there are no more clients registered. At this time, notify_start()
returns and the application continues or finishes. Essentially, the Notifer enters a loop,
blocks until there is input (which it dispatches), and then blocks to wait for more input.
When notify_start() is called, the Notifier takes over and none of the application code
is executed unless it is directly or indirectly called by callback procedure. If the callback
routine calls notify_stop(), when that callback function returns, notify_start()
returns and, as a result, xv_main_loop() returns. The Notifier can be stopped by using
this method despite that there are still clients registered with the Notifier. At this point, the
application may call notify_start() again, or it may attempt to do either implicit or
explicit dispatching of events.
Explicit dispatching is done by calling notify_dispatch(). Here, the Notifier steps
through one iteration of its normal control loop. This allows you to monitor events during
the execution of a time consuming or computationally complex portion of the program. Say
your application generates a complex fractal image. The process is initiated from the selec-
tion of a panel button. Because the generation of fractals is very time consuming, you may
wish to check every once in a while to see if the user has generated any events that need to be
processed—like selecting a panel button labeled Stop. In this case, the callback procedure
for the panel button should call notify_stop() and return. The Notifier returns to the
top-level, notify_start() returns (and thus, xv_main_loop() returns), and your
application begins to generate the fractal image. During each iteration of the loop (or more
often, if necessary), notify_dispatch() is called to ensure processing of any pending
events that the user might have generated.
Implicit dispatching indicates that you are going to make calls to read() or select(),
system calls that block. If notify_do_dispatch() is called, then events the user gener-
ates (such as moving the mouse or selecting a panel button) will continue to be processed
even though the read() has not yet returned.
Implicit and explicit dispatching may be used before or after the call to xv_main_loop()
or notify_start(), but it is not permitted to call these functions while the Notifier is
looping on its own (from within a call to notify_start()). Therefore, you should never
498 XView Programming Manual
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.