/*@ cc -g -c @I @F @L -o @R.o
*/

/*
** A. J. Fountain,
** Imperial Software Technology,
** 120 Hawthorne Avenue,
** Suite 101,
** Palo Alto,
** CA 94301
**
** Telephone: (650) 688-0200
** Fax:       (650) 688-1054
**
** Imperial Software Technology (IST),
** Kings Court,
** 185 Kings Road,
** Reading, 
** Berkshire,
** United Kingdom RG1 4EX.
**
** Telephone: +44 189 587055
** Fax:       +44 189 589005
** 
** http://www.ist-inc.com
** email: af@ist-inc.com
*/

#ifndef   lint
static char *sccsid[] = {"%Z%%Q%%M%	%I%"} ; /* %E% */
#endif /* lint */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <XErrorHandler.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 ;

/*
** 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 ;
}

#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) ;
}

#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) ;
	}
}

#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) ;
}

#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) ;
}
