Error handling in Motif and Xt-based applications
|
30-Oct-02 18:00 GMT
|
Question:
How do I handle errors emanating from the Motif, X Toolkit, and X libraries?
Error handling in X/Motif based applications can be a tricky task in application
programming, because of the asynchronous nature of the X system. Requests are sent to
the X server, and any error or warning messages which result from the request are not
handled immediately at the point of request. The X server sends special XErrorEvent
events to the client, and these are delivered along with other normal events at some
point down the line, possibly well after the request was issued.
There are a number of reasons why the application may want to intercept error and
warning messages emanating from the various toolkits:
-
the application may want to attempt error recovery procedures, or save state in case of emergency
-
the application may want to log the error for later analysis
-
the toolkit does not inform with all the information it could about the error
-
the default behavior of the toolkit from which the error emanates is wrong for the application to hand
-
the way in which the toolkit reports the error to the user is an inconvenience or too crude
-
the message itself may require translation into a more user-friendly form
-
specific messages may need to be loaded from an application-specific error database
Each of the lower level toolkits (Motif, the X Toolkit Intrinsics, the lower level X library) provide their
own error handling routines. Each of the layers utilize the services of the library below, and
refine the behavior for their own purposes in various ways.
Where they differ is that Xt and Xlib provide hooks
such that the application programmer can intercept the error and warning events, and modify
the behavior to suit. Motif only adds handlers to change the output format of errors emanating from its own
library: it does not provide handlers to intercept the library behavior. Since it uses Xt to deliver the
message once the formatted message is in place, it is at the Xt level that handlers need to be installed if
Motif error handling is to be modified.
In general, client functions in the X library are asynchronous in nature: although some return
a Status to the programmer at point of call, most rely on a later XErrorEvent event which arrives from the
X server after the client function has been called.
The X library considers errors to be of two kinds: fatal, and non-fatal. It also groups errors
into two distinct partitions: those which are related to I/O and the status of the connection to the X server
itself, and those which are related to the contents of client requests in general.
In either grouping, whether I/O or protocol request related, the default behavior of the X library
is to print a message to the standard error stream, and then immediately exit. Although
logically some error conditions may well be non-fatal in nature, nonetheless the default behavior of Xlib
is to exit regardless of whether the error is survivable, or whether the application could take
the necessary corrective action.
In Xt, the situation is slightly easier for the programmer in that error and warning messages tend
to be synchronous in nature: they are generally called by the toolkit as the result of a library procedure call being
supplied with invalid data, and the error handlers are invoked immediately at the point of error.
Again, as in Xlib, errors and messages are considered to fall into two groups: fatal, and non-fatal.
Unlike Xlib, error handlers generally are provided at two levels: a low level handler, which
simply prints to the standard error (and, in the case of fatal errors, automatically exits
the application), and a higher level handler, which looks into an error string database to translate
an error key into a message, but which subsequently calls the lower level handler with the message
as parameter. The advantage of the higher level handler is that it is not necessary to embed messages
into the application - these can be externalized so that they can be customized or translated as
required.
The behavior of the Xt Intrinsics is undefined once an error handler has been invoked and
the toolkit believes that the error is fatal in nature. This is not to say that an application
cannot override the default behavior and survive: simply that the authors make no claims about
the potential subsequent robustness of the application in these circumstances.
Note that Xt provides two sets of routines for manipulating error and warning handlers:
those which take an application context parameter (XtAppContext), and those which do
not (but which internally deduce the default application context). This implies that
applications can install per-application-context error and message handlers. It is
difficult to envisage where this would be useful for the general round of applications,
which tend to create a single application context. Be that as it may, the Xt routines without the
XtAppContext parameter are strictly speaking deprecated.
Lastly, Xt does not override the default behavior of the Xlib layer: only in one place does Xt install
error handlers over those of Xlib, in order to guard some protected critical code; post operations, prior Xlib
error handlers are restored.
Motif is similar to Xt in that error handling is handled at the point of error.
Like Xt, it does not override the default behavior of the
Xlib layer: in a few isolated cases, Motif installs error handlers over those of Xlib, in order to
guard some protected critical code; post operations, prior Xlib error handlers are again restored.
Like Xt, it has two sets of routines for handling fatal and non-fatal errors.
There are subtle differences in behavior between Motif 1.2 and 2.1. In version 2.1,
Motif installs its own handler methods by registering a message handler with the Xt
higher level. It does this in the VendorShell class initialize routine. This does not
happen in Motif 1.2.
Regardless, in either case, the logical behavior of Motif is identical to that of Xt, since all the
Motif toolkit does is modify the system so that the format of output is in a style which suits
the toolkit; it calls the lower level Xt handlers in both versions of the toolkit, so that the output
and subsequent behavior of Motif is the same as that of Xt: logically "fatal" errors
terminate the application after printing the message to the standard error stream.
Where Motif differs from Xt is the fact that Motif does not support application-defined message handler
overrides: handling error and messages output in a Motif environment does not require any specific
Motif codes, and any modifications to the messaging system are performed at the Xt and Xlib levels,
which Motif uses for the ultimate delivery mechanism. In other words, if we want to modify the way Motif
delivers error and warning messages to the user, we make the changes through Xt and Xlib calls.
As discussed, Xlib supports two error interfaces: one concerned with I/0 and the connection to the
X server, the other for reporting on the contents of a protocol request.
Taking the latter case first, the interface to the general error handling system is the
routine XSetErrorHandler(), formerly specified as follows:
#include <X11/Xlib.h>
typedef int (*XErrorHandler)(Display *, XErrorEvent *) ;
extern XErrorHandler (*XSetErrorHandler)(XErrorHandler handler) ;
What this means is that XSetErrorHandler() is a routine which takes an application-specific error handler
procedure as argument; it causes the X library to use handler whenever general errors arise ; the routine
returns the previous error handler.
This means that an application program can use the routine to temporarily override Xlib error behavior, installing
its own handler for a given purpose, and then later re-install the default handler when some critical piece of code
is finished. Remember that the Xlib default handler will terminate the application; here we can, for example, add a handler of
our own so that the termination behavior of Xlib is suspended:
#include <stdio.h>
#include <X11/Xlib.h>
static XErrorHandler old_handler = (XErrorHandler) 0 ;
static int ApplicationErrorHandler(Display *display, XErrorEvent *theEvent)
{
(void) fprintf(stderr,
"Ignoring Xlib error: error code %d request code %d\n",
theEvent->error_code,
theEvent->request_code) ;
/* No exit! - but keep lint happy */
return 0 ;
}
...
/* Install our error handler to override Xlib's termination behavior */
old_handler = XSetErrorHandler(ApplicationErrorHandler) ;
...
/* Do code which is survivable in case of error */
code_we_can_survive_in_case_of_error("it says here...") ;
...
/* Restore original X handler */
/* Assumption: the X queue has been temporarily flushed/synchronized */
/* at the end of the application-critical code, otherwise a possible */
/* asynchronously arriving error might arrive further down the stream */
/* after the original handlers are restored. */
/* XFlush(display) */
/* XSync(display, False) ; */
(void) XSetErrorHandler(old_handler) ;
As an alternative, XSetErrorHandler() may be passed a NULL parameter, which
reinstalls the default Xlib error handler in any case. This would be useful where
it is not known whether code from a higher library level has installed its own
handler, so that XSetErrorHandler(old_handler) is not
re-installing default Xlib behavior but that of the intermediary code. Using NULL, we can force
default termination behavior if we really need it.
Notes on XSetErrorHandler()
An error handler which an application installs should not itself generate internal X protocol requests.
I/O or system call errors on the X connection are by their very nature considered to be permanently fatal.
Whether you like it or not, the client side of the X connection will terminate: your only choice here is to
modify the output diagnostic mechanism. If we install an application specific I/O handler, Xlib will
terminate after the handler returns in any case. It is however considered bad form to
install an I/O error handler which does return - you should handle the message, and then
issue the exit call yourself.
If Xlib is going to exit whatever we do, why bother to install a handler? The answer to this is
the fact that there are many reasons why you might want to intercept Xlib's error mechanisms:
you might want to attempt a quick application save-state (although how successful this is likely to be,
if you need to collect data from the X server to perform the feat, is a moot point), or collect
as much information as you can prior to logging the error somewhere for later analysis.
The routine to install an application-speicfic I/O error handler is XSetIOErrorHandler(), formerly
specified as follows:
#include <X11/Xlib.h>
typedef int (*XIOErrorHandler)(Display *) ;
extern XIOErrorHandler XSetIOErrorHandler(XIOErrorHandler handler) ;
Usage of XSetIOErrorHandler() is the same as XSetErrorHandler(): it installs
a custom handler in place of the default Xlib routine, and it returns the previously installed handler.
Again, NULL can be supplied as a parameter to re-install default toolkit operations.
#include <stdio.h>
#include <X11/Xlib.h>
static XIOErrorHandler old_iohandler = (XIOErrorHandler) 0 ;
static int ApplicationIOErrorHandler(Display *display)
{
(void) fprintf(stderr, "Oh Good Grief, what now? Saving...\n") ;
save_state() ;
(void) fprintf(stderr, "Saved.\n") ;
log_diagnostics("Contact the fire department - the computer is on fire") ;
(void) fprintf(stderr, "Thank you for your cooperation.\n") ;
/* Bad form not to exit, but if you do not, Xlib will */
exit(911) ;
}
...
old_iohandler = XSetIOErrorHandler(ApplicationIOErrorHandler) ;
...
/*
** I cannot think of any logical reason why you would
** really want to re-install the default behavior here, whilst
** your application is running: the application is doomed in any case.
**
** Use XSetIOErrorHandler(old_iohandler) or XSetIOErrorHandler(NULL)
** should you think of one.
*/
...
Installing an I/O handler is for emergency situations - where the application
has to remain as fault tolerant as possible in the face of extreme adverse conditions.
This does not mean that the average application can do without - if you want to avoid
leaving a database in a locked or inconsistent state, even if the data is not
particularly valuable in itself, it is not a bad idea to install one so that you potentially
minimise the problem of cleaning up afterwards.
As an aside, the routines XSetErrorHandler() and XSetIOErrorHandler() are
unusual in that they are amongst the very few Xlib routines which do not require a Display * parameter.
Xt provides two sets of diagnostic message handlers: warning handlers, which are non-fatal
in nature, and error handlers which, by default, are. Each set has two associated levels: a low level handler, which
outputs the message, and a higher level handler, which constructs and formats the message prior to calling the low level
handler for dispatch.
There are therefore four primary routines which we need to consider:
-
XtAppSetWarningHandler()
- modifies the way Xt outputs non-fatal warning messages
-
XtAppSetWarningMsgHandler()
- modifies the way Xt formats and constructs non-fatal warning messages
-
XtAppSetErrorHandler()
- modifies the way Xt outputs fatal error messages
-
XtAppSetErrorMsgHandler()
- modifies the way Xt formats and constructs fatal error messages
There are also the routines XtSetWarningHandler(), XtSetWarningMsgHandler(), XtSetErrorHandler(), and XtSetErrorMsgHandler()
which we shall ignore because they have been superseded by the above.
The routine XtAppSetWarningHandler() is similar to the Xlib routines, in that it returns
the previous error handler. It is formally specified as follows:
typedef void (*XtErrorHandler)(String) ;
extern XtErrorHandler XtAppSetWarningHandler(XtAppContext, XtErrorHandler) ;
The XtErrorHandler type is correct, even though this is a warning message routine: there is no
putative XtWarningHandler type, and both XtAppSetWarningHandler() and XtAppSetErrorHandler()
share the same message handler prototypes.
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/MessageB.h>
static void PopupWarningHandler(String message)
{
extern Widget application_shell ; /* Session shell ? */
static Widget warning_dialog = (Widget) 0 ;
XmString xms = (XmString) 0 ;
if (warning_dialog == (Widget) 0) {
warning_dialog = XmCreateWarningDialog(application_shell,
"WarningsDialog",
(ArgList) 0,
(Cardinal) 0) ;
}
xms = XmStringCreateLocalized(message) ;
XtVaSetValues(warning_dialog, XmNmessageString, xms, NULL) ;
XmStringFree(xms) ;
XtManageChild(warning_dialog) ;
}
...
static XtErrorHandler old_message_handler = (XtErrorHandler) 0 ;
void InstallPopupWarningHandler(XtAppContext app_context)
{
old_message_handler = XtAppSetWarningHandler(app_context, PopupWarningHandler) ;
}
void UninstallPopupWarningHandler(XtAppContext app_context)
{
(void) XtAppSetWarningHandler(app_context, old_message_handler) ;
}
As for XtAppSetWarningHandler(), the routine XtAppSetErrorHandler() returns
the previous error handler. It is formally specified as follows:
typedef void (*XtErrorHandler)(String) ;
extern XtErrorHandler XtAppSetErrorHandler(XtAppContext, XtErrorHandler) ;
Recall that the behavior of the Xt Intrinsics is undefined subsequent to error messages emanating from the library.
We could redirect errors to a popup, but there is no guarantee that the popup will appear. The correct action
to take will therefore depend on the mission-critical status of the application, and how
user-friendly we are prepared to be. What we can do is install our own handler to try and
save application state, analogously to the Xlib XSetErrorHandler routine.
The following code sketches out a scheme which saves application state, logs the error,
and lastly attempts a popup message (of which there is no guarantee of success).
static void DoExitCallback(Widget w, XtPointer clientData, XtPointer callData)
{
exit(911) ;
}
static void PopupErrorDialog(String error)
{
extern Widget application_shell ; /* Session shell ? */
static Widget error_dialog = (Widget) 0 ;
XmString xms = (XmString) 0 ;
if (error_dialog == (Widget) 0) {
/* These strings should really be picked up from X resources */
/* So the dialog can be internationalised as appropriate */
XmString ok = XmStringCreateLocalized("Exit") ;
XmString recover = XmStringCreateLocalized("Continue") ;
Arg argv[2] ;
Cardinal argc = 0 ;
XtSetArg(argv[argc], XmNokLabelString, ok) ; argc++ ;
XtSetArg(argv[argc], XmNcancelLabelString, recover) ; argc++ ;
error_dialog = XmCreateErrorDialog(application_shell,
"ErrorsDialog",
argv,
argc) ;
XtAddCallback(error_dialog, XmNokCallback, DoExitCallback, (XtPointer) 0) ;
}
xms = XmStringCreateLocalized(error) ;
XtVaSetValues(error_dialog, XmNmessageString, xms, NULL) ;
XmStringFree(xms) ;
XtManageChild(error_dialog) ;
}
static void IntrinsicsErrorHandler(String error)
{
(void) fprintf(stderr, "Here we go again...\n") ;
/* Save the application state */
save_state() ;
(void) fprintf(stderr, "Saved.") ;
log_diagnostics("Looks like we need the Marines this time") ;
/* This may or may not work */
PopupErrorDialog(error) ;
}
...
static XtErrorHandler old_error_handler = (XtErrorHandler) 0 ;
void InstallErrorHandler(XtAppContext app_context)
{
old_error_handler = XtAppSetErrorHandler(app_context, IntrinsicsErrorHandler) ;
}
void UninstallErrorHandler(XtAppContext app_context)
{
(void) XtAppSetErrorHandler(app_context, old_error_handler) ;
}
XtAppSetWarningMsgHandler and XtAppSetErrorMsgHandler are concerned not with
the way in which the error messages are presented to the user, but with the format and contents of
the messages themselves. They do, however, call the lower-level interfaces internally, so that if you
install your own high level message handlers, you must also remember to invoke the lower level. Invoking
the relevant lower level handlers will be convered below.
These two routines will be handled as a piece: generally, if you want to change the format of
one type of message, you will also want to change the format of the other.
The routines are formally specified as follows:
typedef void (*XtErrorMsgHandler)(String, /* name */
String, /* type */
String, /* class */
String, /* default */
String *, /* params */
Cardinal *) ; /* num_params */
extern XtErrorMsgHandler XtAppSetWarningMsgHandler(XtAppContext, XtErrorMsgHandler) ;
extern XtErrorMsgHandler XtAppSetErrorMsgHandler(XtAppContext, XtErrorMsgHandler) ;
The name parameter identifies this error or message - typically, it will be a key used to lookup
a message in an Error string database through the routine XtGetErrorDatabaseText() or similar.
The type parameter indicates the general kind of error; typically, when used by widget authors, it identifies
the routine which detects the problem, so for example in Motif it might read "CvtStringToWidget", "CvtPixelToRenditionPixel",
and so on.
The class parameter specifies the general class of error or message - for example, in Motif it might read "XtToolkitError"
or "ToolkitError", depending on where Motif believes the error to have emanated.
The default parameter typically is a description of the error as detected in the software, but in the language
of the programmer; typically it is output if the error or message handler fails to find a localized string associated
with the given name and class.
The params and num_params arguments specify extra detail associated with the error message; the error or message
handler will presumably have to format and concatenate this data when present prior to output. In Motif 2.x, there is a protocol
such that the toolkit adds parameters with the first such parameter being the value "XmeWarning", so that the toolkit can identify
whether the error hs emanated from within the toolkit's standard warning handlers, or elsewhere.
As an example, a typical warning handler will have the following design:
void WarningHandler(String name,
String type,
String s_class,
String message,
String *params,
Cardinal *num_params)
{
/*
** Whatever the application context is...
*/
extern XtAppContext app_context ;
/*
** MAX_MESSAGE_STRING to be defined, something like 1024, to
** allow plenty of space for formatting.
**
** Naturally, a safer approach would dynamically allocate space...
*/
char buffer[MAX_MESSAGE_STRING] ;
int i ;
/* Look up any localized version of the message */
XtGetErrorDatabaseText(name, type, s_class, message, buffer, MAX_MESSAGE_STRING) ;
/* Format the message and parameters in our style */
for (i = 0 ; i < *num_params ; i++) {
(void) strcat(buffer, "\n") ;
(void) strcat(buffer, params[i]) ;
}
/*
** Use Xt as the delivery mechanism.
**
** XtWarning(buffer) can be used if the
** default application context is sufficient.
*/
XtAppWarning(app_context, buffer) ;
}
There are four routines to consider: XtAppError() and XtError(), which call the lower level installed error handler,
XtAppWarning() and XtWarning(), which invoke the lower level installed message handler. The function
prototypes are as follows:
extern void XtAppError(XtAppContext app_context, String error) ;
extern void XtError(String error) ;
extern void XtAppWarning(XtAppContext app_context, String warning) ;
extern void XtWarning(String warning) ;
The routines defined below are typical of experiences with various widget sets
and the X environment in general. We can categorize the reasons for the handler as follows:
-
Not all widgets are equal, and freeware ones in particular tend to be not sufficiently robust
in all circumstances. The errors which they raise can however sometimes be survived.
-
Not all window managers are equal, and some have certain anti-social behavior such as grabbing the mouse
at certain times, although this also can be corrected by suitable application code.
-
Not all X implementations are equal, and various X errors are logically survivable.
-
The application needs to save state in the face of errors in order to avoid data loss when
the X system is going down.
Firstly, we define some types for plug-in application save routines:
#ifndef _NO_PROTO
typedef void (*XApplicationSaveHandler)(XtPointer applicationData) ;
#else /* _NO_PROTO */
typedef void (*XApplicationSaveHandler)() ;
#endif /* _NO_PROTO */
This allows us to pass application-specific data through to any plug-in save handler
we may wish to call from the error handling routines we will define.
Secondly, we define some variables to hold state:
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
/* The previously-installed X error handler */
static XErrorHandler default_error = (XErrorHandler) 0 ;
/* The previously-installed X I/O error handler */
static XIOErrorHandler default_ioerror = (XIOErrorHandler) 0 ;
/* The current routine for saving the application */
static XApplicationSaveHandler current_save_handler = (XApplicationSaveHandler) 0 ;
/* Any application-specific data required by the above */
static XtPointer current_save_data = (XtPointer) 0 ;
Now we define the Xlib error handler itself, using gained knowledge of various X problems
which are known to be survivable, and so forth. Not all cases need necessarily be copied -
it depends on the widget sets and so forth used by the application.
/*
** The following routine is entirely heuristic, and
** attempts to survive in the face of some pretty hostile
** interactions between multiple third party widget sets,
** and so forth.
*/
#ifndef _NO_PROTO
static int _XlibErrorHandler(Display *display, XErrorEvent *event)
#else /* _NO_PROTO */
static int _XlibErrorHandler(display, event)
Display *display ;
XErrorEvent *event ;
#endif /* _NO_PROTO */
{
static Boolean property_warned = False ;
Boolean try_recovery = False ;
Boolean probably_fatal = False ;
switch (event->request_code)
{
case 0 :
try_recovery = True ;
break ;
/*
** Generally caused by attempting to draw on a Canvas before it
** is realized. Programmer error, really.
*/
default : if ((event->error_code != BadWindow) &&
(event->error_code != BadDrawable) &&
(event->error_code != BadPixmap)) {
probably_fatal = True ;
}
/* FALLTHROUGH */
/*
** Some window managers (fvwm) grab the mouse
** and do not release it properly when the application needs it.
*/
case X_GrabButton:
if ((event->request_code == X_GrabButton) &&
(event->error_code != BadAccess)) {
probably_fatal = True ;
}
/* FALLTHROUGH */
/*
** X11R6 running on X11R5 WM server
** Probably WM_NAME in XtAppCreateShell which is deprecated code
** in any case.
** This can show up running Linux displaying onto Solaris 8,
** although there are reports of HPUX having the same issue.
*/
case X_DeleteProperty : /* FALLTHROUGH */
if ((event->request_code == X_DeleteProperty) &&
(event->error_code == BadWindow)) {
if (event->resourceid == (XID) 0) {
try_recovery = True ;
}
else {
probably_fatal = True ;
}
}
/* FALLTHROUGH */
/* X11R6 running on X11R5 WM server */
/* Probably WM_NAME in XtAppCreateShell */
/* Which is deprecated code in any case */
/* As above for context */
case X_ChangeProperty :
if ((event->request_code == X_ChangeProperty) &&
(event->error_code == BadAtom)) {
if (event->resourceid == (XID) 0) {
try_recovery = True ;
}
else {
probably_fatal = True ;
}
}
/* FALLTHROUGH */
/*
** Ignore XConfigureWindow errors that some third party widgets raise.
** Again, a widget author error.
*/
case X_ConfigureWindow :
if (event->request_code == X_ConfigureWindow) {
try_recovery = True ;
}
/* FALLTHROUGH */
/*
** XRT/Pane has/had this problem, if configured dynamically
** for orientation post-manage.
*/
case X_CreateWindow:
if ((event->request_code == X_CreateWindow) &&
(event->error_code == BadValue)) {
probably_fatal = True ;
try_recovery = True ;
}
/* FALLTHROUGH */
/*
** Colormap issue... system does not support virtual Colormaps.
** This ought to be survivable unless there are hand-drawn graphics.
*/
case X_CreateColormap :
if (event->request_code == X_CreateColormap) {
try_recovery = True ;
}
/* FALLTHROUGH */
/*
** Out of colors... generally, this can be survived,
** although what the result looks like is another issue.
** The user should have sufficient of the GUI to exit
** gracefully in whatever way is appropriate, close down
** the typical color hoggers like the web browser (no names -
** you know who you are) or desktop publishing package
** (also no names) and then retry.
*/
case X_AllocColor : /* FALLTHROUGH */
case X_AllocColorCells : /* FALLTHROUGH */
case X_AllocNamedColor : /* FALLTHROUGH */
case X_AllocColorPlanes :
if ((event->request_code == X_AllocColor) ||
(event->request_code == X_AllocColorCells) ||
(event->request_code == X_AllocColorPlanes) ||
(event->request_code == X_AllocNamedColor)) {
if ((event->error_code == BadAlloc) ||
(event->error_code == BadColor)) {
try_recovery = True ;
}
}
/* FALLTHROUGH */
/*
** Attempt to write into a read-only colormap
** Survivable, if X doesn't exit on its own behalf...
** Generally, a programming error, not taking sufficient
** care over various Visual types and read/write Colormap
** differences.
*/
case X_StoreColors:
if ((event->request_code == X_StoreColors) &&
(event->error_code == BadAccess)) {
try_recovery = True ;
}
/* FALLTHROUGH */
/*
** Xaw3D shadow problems. Poor widget authoring in the face
** of various Visual and Colormap types.
*/
case X_QueryColors:
if ((event->request_code == X_QueryColors) &&
(event->error_code == BadValue)) {
try_recovery = True ;
}
/* FALLTHROUGH */
/*
** Some errors we regard as minor and ignorable - XFreeColors in particular
*/
case X_FreeColors:
if (probably_fatal == False) {
try_recovery = True ;
}
break ;
}
/*
** Save the Application State, using the plug-in routines.
*/
if ((try_recovery == FALSE) || (probably_fatal == TRUE)) {
if (current_save_handler != (XApplicationSaveHandler) 0) {
(*current_save_handler)(current_save_data) ;
}
}
if (try_recovery == False) {
/*
** Unstack our error handlers - the builtin X ones abort
*/
(void) XSetErrorHandler((XErrorHandler) 0) ;
(void) XSetIOErrorHandler((XIOErrorHandler) 0) ;
(void) fprintf(stderr, "Fatal Xlib Error for request code %d\n",
event->request_code) ;
/* The builtin X handler aborts */
if (default_error != (XErrorHandler) 0) {
(*default_error)(display, event) ;
/* NOT REACHED (Or is it) */
}
}
if (try_recovery == True) {
if ((event->request_code != 0) && (probably_fatal == False)) {
/*
** The X11R5/X11R6 issue cross-display: only warn once,
** or else we get the result per shell.
*/
if (((event->request_code == X_ChangeProperty) &&
(event->error_code == BadAtom)) ||
((event->request_code == X_DeleteProperty) &&
(event->error_code == BadWindow))) {
if (property_warned == False) {
(void) fprintf(stderr,
"Warning: Ignoring Xlib Error for request code %d\n",
event->request_code) ;
property_warned = True ;
}
}
else {
(void) fprintf(stderr,
"Warning: Ignoring Xlib Error for request code %d\n",
event->request_code) ;
}
}
else if (probably_fatal == True) {
(void) fprintf(stderr,
"Warning: Probably fatal Xlib Error for request code %d\n",
event->request_code) ;
}
}
return True ;
}
Now we define the I/O error handler. This is considerably simpler - because not survivable.
We can only try and save state.
#ifndef _NO_PROTO
static int _XlibIoErrorHandler(Display *display)
#else /* _NO_PROTO */
static int _XlibIoErrorHandler(display)
Display *display ;
#endif /* _NO_PROTO */
{
/*
** Your chances of surviving this are precisely zero.
** Most likely, you have lost the connection to the X server.
*/
if (current_save_handler != (XApplicationSaveHandler) 0) {
(*current_save_handler)(current_save_data) ;
}
(void) XSetErrorHandler((XErrorHandler) 0) ;
(void) XSetIOErrorHandler((XIOErrorHandler) 0) ;
/*
** The default handler will abort.
*/
if (default_ioerror != (XIOErrorHandler) 0) {
(*default_ioerror)(display) ;
}
/*
** Just in case it doesn't...
*/
exit(1) ;
}
That covers the Xlib layer. Now we define a few small routines to handle the Xt layer.
Firstly, the general purpose handler:
#ifndef _NO_PROTO
static void _XtErrorHandler(String message, Boolean fatal)
#else /* _NO_PROTO */
static void _XtErrorHandler(message, fatal)
String message ;
Boolean fatal ;
#endif /* _NO_PROTO */
{
(void) fprintf(stderr,
"X Tookit %s: %s\n",
(fatal ? "Fatal error" : "Warning message"),
message) ;
if (fatal) {
/*
** Save State.
*/
if (current_save_handler != (XApplicationSaveHandler) 0) {
(*current_save_handler)(current_save_data) ;
}
exit(1) ;
}
}
Now a couple of wrappers which are to be registered with the X toolkit:
#ifndef _NO_PROTO
static void _XtWarningErrorHandler(String message)
#else /* _NO_PROTO */
static void _XtWarningErrorHandler(message)
String message ;
#endif /* _NO_PROTO */
{
_XtErrorHandler(message, False) ;
}
#ifndef _NO_PROTO
static void _XtFatalErrorHandler(String message)
#else /* _NO_PROTO */
static void _XtFatalErrorHandler(message)
String message ;
#endif /* _NO_PROTO */
{
_XtErrorHandler(message, True) ;
}
Lastly, we simply need a public interface, which initializes the handlers,
and supplies the plug-in save handler for the application:
#ifndef _NO_PROTO
void InitializeErrorHandlers(XtAppContext context,
XApplicationSaveHandler save_handler,
XtPointer save_data)
#else /* _NO_PROTO */
void InitializeErrorHandlers(context, save_handler, save_data)
XtAppContext context ;
XApplicationSaveHandler save_handler ;
XtPointer save_data ;
#endif /* _NO_PROTO */
{
current_save_handler = save_handler ;
current_save_data = save_data ;
if (context != (XtAppContext) 0) {
(void) XtAppSetErrorHandler(context, _XtFatalErrorHandler) ;
(void) XtAppSetWarningHandler(context, _XtWarningErrorHandler) ;
}
default_error = XSetErrorHandler(_XlibErrorHandler) ;
default_ioerror = XSetIOErrorHandler(_XlibIoErrorHandler) ;
}
The sources for the above are:
The following materials are available for further reading. Firstly, for reference
materials on the Motif 2.1 toolkit:
Motif Reference Manual for Motif 2.1,
Antony Fountain and Paula Fergusson,
O'Reilly and Associates,
ISBN: 1-56592-654-4
Buy it from Amazon.com or Amazon.co.uk
The companion Motif 2.1 Programming Manual is currently only
available in electronic form.
It, and the Reference Manual, can be downloaded variously in PDF or HTML format from
www.ist.co.uk/motif/books (Reading, UK)
or www.ist-inc.com/motif/books (Palo Alto, CA)
For a general Xt reference manual, the following is recommended:
X Toolkit Intrinsics Reference Manual,
Edited by David Flanagan,
O'Reilly and Associates,
ISBN: 1-56592-007-4
Buy it from Amazon.com or Amazon.co.uk
For a general X11 library reference manual, the following is recommended:
Xlib Reference Manual,
Edited by Adrian Nye,
O'Reilly and Associates,
ISBN: 1-56592-006-6
Buy it from Amazon.com or Amazon.co.uk
A good one-stop general purpose Motif programming manual is:
Advanced Motif Programming Techniques,
Alistair George and Mark Riches,
Prentice Hall,
ISBN 0-13-219965-3
Unfortunately this book is out of print,
but you may be able to buy it from Amazon.com
This last volume is Motif 1.2 specific, although the general techniques which
are described remain invaluable, irrespective of the version of the toolkit you are using.
Sponsored
by X-Designer - The Leading X/Motif GUI Builder
- Click to download a FREE evaluation
Goto top of page
|