/******************************************************************************
 JXApplication.cc

	Initializes global objects and manages the event loop.

	Urgent tasks are performed after the current event and are then deleted.

	Idle tasks are performed whenever the event loop is idle.  It is safe
	to delete a JXIdleTask object because it will automatically remove itself
	from the task list.

	BASE CLASS = JXDirector

	Copyright © 1996-97 by John Lindal. All rights reserved.

 ******************************************************************************/

#include <JXApplication.h>
#include <JXDisplay.h>
#include <JXWindow.h>
#include <JXIdleTask.h>
#include <JXUrgentTask.h>
#include <JXMenuManager.h>
#include <JXHelpManager.h>
#include <JXDocumentManager.h>
#include <JXMDIServer.h>
#include <JXAssert.h>
#include <jXEventUtil.h>
#include <jXGlobals.h>

#include <JThisProcess.h>
#include <ace/Reactor.h>
#include <ace/Service_Config.h>
#include <sys/time.h>

#include <JString.h>
#include <jTime.h>
#include <jDirUtil.h>
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <jMissingProto.h>
#include <jAssert.h>

static const JCharacter* kDisplayOptionName = "-display";

const time_t kTimerStart = J_TIME_T_MAX/1000U;	// milliseconds before rollover
const Time kMaxSleepTime = 50;					// 0.05 seconds (in milliseconds)

const JSize kWaitForChildCount = 50;

/******************************************************************************
 Constructor

 ******************************************************************************/

JXApplication::JXApplication
	(
	int*	argc,
	char*	argv[]
	)
	:
	JXDirector(NULL),
	itsIgnoreDisplayDeletedFlag(kFalse),
	itsIgnoreTaskDeletedFlag(kFalse)
{
	// initialize object

	itsDisplayList = new JPtrArray<JXDisplay>;
	assert( itsDisplayList != NULL );

	itsCurrDisplayIndex = 1;

	itsIdleTaskStack = new IdleTaskStack;
	assert( itsIdleTaskStack != NULL );

	itsIdleTasks = new JPtrArray<JXIdleTask>;
	assert( itsIdleTasks != NULL );

	itsCurrentTime         = 0;
	itsMaxSleepTime        = 0;
	itsLastIdleTime        = 0;
	itsLastIdleTaskTime    = 0;
	itsWaitForChildCounter = 0;

	itsUrgentTasks = new JPtrArray<JXUrgentTask>;
	assert( itsUrgentTasks != NULL );

	itsUTInsertIndex = 1;

	itsActiveUrgentTasks = new JPtrArray<JXUrgentTask>;
	assert( itsActiveUrgentTasks != NULL );

	itsHasBlockingWindowFlag = kFalse;
	itsHadBlockingWindowFlag = kFalse;
	itsRequestQuitFlag       = kFalse;

	// initialize global objects

	JXCreateGlobals(this);

	// create display -- requires JXGetApplication() to work

	JString displayName;
	ParseBaseOptions(argc, argv, &displayName);

	JXDisplay* display;
	if (!JXDisplay::Create(displayName, &display))
		{
		cerr << argv[0];
		if (displayName.IsEmpty())
			{
			cerr << ": Can't open display '" << XDisplayName(NULL) << '\'';
			}
		else
			{
			cerr << ": Can't open display '" << displayName << '\'';
			}
		cerr << endl;
		JThisProcess::Exit(1);
		}

	// start the timer

#ifndef __MWERKS__

	itimerval timerInfo;
	timerInfo.it_interval.tv_sec  = kTimerStart;
	timerInfo.it_interval.tv_usec = 0;
	timerInfo.it_value.tv_sec     = kTimerStart;
	timerInfo.it_value.tv_usec    = 0;
	setitimer(ITIMER_REAL, &timerInfo, NULL);

#endif
}

/******************************************************************************
 Destructor

 ******************************************************************************/

JXApplication::~JXApplication()
{
	JXCloseHelpManager();

	itsIgnoreDisplayDeletedFlag = kTrue;

	itsDisplayList->DeleteAll();
	delete itsDisplayList;

	JXDeleteGlobals1();

	itsIgnoreTaskDeletedFlag = kTrue;

	itsIdleTaskStack->DeleteAll();
	delete itsIdleTaskStack;

	itsIdleTasks->DeleteAll();
	delete itsIdleTasks;

	itsActiveUrgentTasks->DeleteAll();
	delete itsActiveUrgentTasks;

	itsUrgentTasks->DeleteAll();
	delete itsUrgentTasks;

	JXDeleteGlobals2();
}

/******************************************************************************
 Quit (virtual)

	Derived classes can override this to perform additional operations
	or to give the user a warning.  They *must* call the inherited version
	in order for it to take effect.

 ******************************************************************************/

void
JXApplication::Quit()
{
	itsRequestQuitFlag = kTrue;
}

/******************************************************************************
 OpenDisplay

 ******************************************************************************/

JBoolean
JXApplication::OpenDisplay
	(
	const JCharacter*	displayName,
	JIndex*				displayIndex
	)
{
	assert( displayName != NULL && displayName[0] != '\0' );

	JXDisplay* display;
	if (JXDisplay::Create(displayName, &display))
		{
		// DisplayOpened() appends new JXDisplay* to our list
		*displayIndex = itsDisplayList->GetElementCount();
		return kTrue;
		}
	else
		{
		JString errorStr = "Unable to connect to ";
		errorStr += displayName;
		(JGetUserNotification())->ReportError(errorStr);

		*displayIndex = 0;
		return kFalse;
		}
}

/******************************************************************************
 DisplayOpened

	OpenDisplay() assumes that Append() is called.

 ******************************************************************************/

void
JXApplication::DisplayOpened
	(
	JXDisplay* display
	)
{
	itsDisplayList->Append(display);
	(JXGetAssertHandler())->DisplayOpened(display);
}

/******************************************************************************
 DisplayClosed

 ******************************************************************************/

void
JXApplication::DisplayClosed
	(
	JXDisplay* display
	)
{
	if (!itsIgnoreDisplayDeletedFlag)
		{
		itsDisplayList->Remove(display);
		(JXGetAssertHandler())->DisplayClosed(display);
		}
}

/******************************************************************************
 SetCurrentDisplay

 ******************************************************************************/

void
JXApplication::SetCurrentDisplay
	(
	const JIndex index
	)
{
	assert( itsDisplayList->IndexValid(index) );
	itsCurrDisplayIndex = index;
}

void
JXApplication::SetCurrentDisplay
	(
	JXDisplay* display
	)
{
	JIndex index;
	if (itsDisplayList->Find(display, &index))
		{
		itsCurrDisplayIndex = index;
		}
}

/******************************************************************************
 FindDisplay

	Returns kTrue if the given display exists.

 ******************************************************************************/

JBoolean
JXApplication::FindDisplay
	(
	const Display*	xDisplay,
	JXDisplay**		display
	)
{
	const JSize count = itsDisplayList->GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		JXDisplay* d = itsDisplayList->NthElement(i);
		if (d->GetXDisplay() == xDisplay)
			{
			*display = d;
			return kTrue;
			}
		}

	*display = NULL;
	return kFalse;
}

/******************************************************************************
 DisplayBusyCursor

	Displays the watch cursor in all windows on all displays.

 ******************************************************************************/

void
JXApplication::DisplayBusyCursor()
{
	const JSize count = itsDisplayList->GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		(itsDisplayList->NthElement(i))->DisplayCursorInAllWindows(kJXBusyCursor);
		}
}

/******************************************************************************
 DisplayInactiveCursor

	Displays the X cursor in all windows on all displays.

 ******************************************************************************/

void
JXApplication::DisplayInactiveCursor()
{
	const JSize count = itsDisplayList->GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		(itsDisplayList->NthElement(i))->DisplayCursorInAllWindows(kJXInactiveCursor);
		}
}

/******************************************************************************
 HideAllWindows

 ******************************************************************************/

void
JXApplication::HideAllWindows()
{
	const JSize count = itsDisplayList->GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		(itsDisplayList->NthElement(i))->HideAllWindows();
		}
}

/******************************************************************************
 Run

	When this function returns, JX and JCore have been shut down and
	the application object has been deleted.

 ******************************************************************************/

void
JXApplication::Run()
{
	while (1)
		{
		HandleOneEvent();

		if (itsRequestQuitFlag || !HasSubdirectors())
			{
			itsRequestQuitFlag = kTrue;
			if (Close())
				{
				break;		// we have been deleted
				}
			else
				{
				itsRequestQuitFlag = kFalse;
				}
			}
		}
}

/******************************************************************************
 Close (virtual protected)

	This will fail unless Quit() has been called.

 ******************************************************************************/

JBoolean
JXApplication::Close()
{
	assert( itsRequestQuitFlag );

	const JSize count = itsDisplayList->GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		JXDisplay* display = itsDisplayList->NthElement(i);
		(display->GetMenuManager())->CloseCurrentMenus();
		}

	return JXDirector::Close();		// deletes us if successful
}

/******************************************************************************
 UpdateCurrentTime (protected)

	Calculate our current time from the current timer value.

 ******************************************************************************/

void
JXApplication::UpdateCurrentTime()
{
#ifndef __MWERKS__

	itimerval timerInfo;
	getitimer(ITIMER_REAL, &timerInfo);
	itsCurrentTime = JRound(1000 * (kTimerStart -
		 (timerInfo.it_value.tv_sec + timerInfo.it_value.tv_usec/1e6)));

#endif
}

/******************************************************************************
 HandleOneEvent (private)

	We process one event for each display.  If there are none, we idle.

	To process custom events, override HandleCustomEvent().

 ******************************************************************************/

void
JXApplication::HandleOneEvent()
{
	itsHadBlockingWindowFlag = kFalse;

	UpdateCurrentTime();
	const JBoolean allowSleep = HandleCustomEvent();

	UpdateCurrentTime();
	JBoolean hasEvents = kFalse;

	JPtrArrayIterator<JXDisplay> iter(itsDisplayList);
	JXDisplay* display;
	JIndex displayIndex = 0;
	while (iter.Next(&display))
		{
		displayIndex++;
		itsCurrDisplayIndex = displayIndex;		// itsCurrDisplayIndex might change during event
		if (XPending(*display) != 0)
			{
			hasEvents = kTrue;

			// get the next event

			XEvent xEvent;
			XNextEvent(*display, &xEvent);

			if (xEvent.type != MotionNotify)
				{
				itsLastIdleTime = itsCurrentTime;
				}

			// dispatch the event

			display->HandleEvent(xEvent, itsCurrentTime);
			}
		else
			{
			display->Idle(itsCurrentTime);
			}
		}

	PopAllIdleTaskStack();

	// Perform idle tasks when we didn't receive any events and
	// during long intervals of "mouse moved".

	if (!hasEvents)
		{
		PerformIdleTasks();
		itsLastIdleTime = itsCurrentTime;
		PerformUrgentTasks();
		if (allowSleep)
			{
			JWait(itsMaxSleepTime / 1000.0);
			}
		}
	else if (hasEvents &&
			 itsCurrentTime - itsLastIdleTime > itsMaxSleepTime)
		{
		PerformIdleTasks();
		itsLastIdleTime = itsCurrentTime;
		PerformUrgentTasks();
		}
	else
		{
		PerformUrgentTasks();
		}
}

/******************************************************************************
 HandleCustomEvent (virtual protected)

	This function exists to support real-time applications which need
	to modify the main event loop.  It is called every time through the
	main event loop.  (HandleCustomEventWhileBlocking() is called when a
	blocking dialog window is active.)

	The derived class is not required to return after handling a single
	event, but if it waits too long before returning, then the graphical
	interface will react sluggishly.

	The return value controls whether or not the main event loop idles
	by calling JWait().  The default is to return kTrue to avoid hogging
	CPU time.  If the derived class handles the sleeping via some other
	system call, then it should return kFalse.  Otherwise, it might return
	kTrue if there were no events and kFalse if there were.

 ******************************************************************************/

JBoolean
JXApplication::HandleCustomEvent()
{
	return kTrue;
}

/******************************************************************************
 HandleOneEventForWindow

	We process one event for one window on one display.  We process Expose
	events for everybody.  We toss mouse and keyboard events for all other
	windows.  We perform only the idle tasks passed in via idleTasks.
	idleTasks can be NULL.

	In order to allow menus inside the window, we always pass all events
	to the current mouse and keyboard grabber windows.

	Returns kTrue if we processed an event for the specified window.

 ******************************************************************************/

const JSize kEventWindowCount = 3;

struct DiscardEventInfo
{
	JXDisplay*	display;
	Window*		eventWindow;	// pass to GetNextWindowEvent()

	DiscardEventInfo(JXDisplay* d, Window* w)
		:
		display(d), eventWindow(w)
		{ };
};

JBoolean
JXApplication::HandleOneEventForWindow
	(
	JXWindow*		window,
	const JBoolean	origAllowSleep
	)
{
	itsHasBlockingWindowFlag = kTrue;
	itsHadBlockingWindowFlag = kFalse;

	if (itsIdleTaskStack->IsEmpty())
		{
		PushIdleTaskStack();
		}

	UpdateCurrentTime();
	JBoolean allowSleep = origAllowSleep;
	HandleCustomEventWhileBlocking(&allowSleep);

	UpdateCurrentTime();
	JBoolean windowHasEvents = kFalse;

	const JXDisplay* uiDisplay = window->GetDisplay();

	Window eventWindow [ kEventWindowCount ];
	eventWindow[0] = window->GetXWindow();

	JPtrArrayIterator<JXDisplay> iter(itsDisplayList);
	JXDisplay* display;
	JIndex displayIndex = 0;
	while (iter.Next(&display))
		{
		displayIndex++;
		itsCurrDisplayIndex = displayIndex;		// itsCurrDisplayIndex might change during event
		if (XPending(*display) != 0)
			{
			// get mouse and keyboard grabbers

			eventWindow[1] = eventWindow[2] = None;

			JXWindow* grabber;
			if (display == uiDisplay && display->GetMouseGrabber(&grabber))
				{
				eventWindow[1] = grabber->GetXWindow();
				}
			if (display == uiDisplay && display->GetKeyboardGrabber(&grabber))
				{
				eventWindow[2] = grabber->GetXWindow();
				}

			// process one event

			XEvent xEvent;
			if (display == uiDisplay &&
				XCheckIfEvent(*display, &xEvent, GetNextWindowEvent,
							  reinterpret_cast<char*>(eventWindow)))
				{
				windowHasEvents = kTrue;
				if (xEvent.type != MotionNotify)
					{
					itsLastIdleTime = itsCurrentTime;
					}
				display->HandleEvent(xEvent, itsCurrentTime);
				}
			else if (XCheckIfEvent(*display, &xEvent, GetNextBkgdEvent, NULL))
				{
				display->HandleEvent(xEvent, itsCurrentTime);
				}
			else if (display == uiDisplay && display->GetMouseContainer() == window)
				{
				display->Idle(itsCurrentTime);
				}
			else
				{
				display->Update();
				}

			// discard mouse and keyboard events

			DiscardEventInfo discardInfo(display, NULL);
			if (display == uiDisplay)
				{
				discardInfo.eventWindow = eventWindow;
				}
			while (XCheckIfEvent(*display, &xEvent, DiscardNextEvent,
								 reinterpret_cast<char*>(&discardInfo)))
				{ };
			}
		else if (display == uiDisplay && display->GetMouseContainer() == window)
			{
			display->Idle(itsCurrentTime);
			}
		else
			{
			display->Update();
			}
		}

	// Perform idle tasks when we didn't receive any events and
	// during long intervals of "mouse moved".

	if (!windowHasEvents)
		{
		PerformIdleTasks();
		itsLastIdleTime = itsCurrentTime;
		PerformUrgentTasks();
		if (allowSleep)
			{
			JWait(itsMaxSleepTime / 1000.0);
			}
		}
	else if (windowHasEvents &&
			 itsCurrentTime - itsLastIdleTime > itsMaxSleepTime)
		{
		PerformIdleTasks();
		itsLastIdleTime = itsCurrentTime;
		PerformUrgentTasks();
		}
	else
		{
		PerformUrgentTasks();
		}

	itsHasBlockingWindowFlag = kFalse;
	itsHadBlockingWindowFlag = kTrue;

	return windowHasEvents;
}

// static private

Bool
JXApplication::GetNextWindowEvent
	(
	Display*	display,
	XEvent*		event,
	char*		arg
	)
{
	const Window currWindow = (event->xany).window;

	Window* eventWindow = reinterpret_cast<Window*>(arg);
	for (JIndex i=0; i<kEventWindowCount; i++)
		{
		if (currWindow == eventWindow[i])
			{
			return True;
			}
		}

	return False;
}

Bool
JXApplication::GetNextBkgdEvent
	(
	Display*	display,
	XEvent*		event,
	char*		arg
	)
{
	// Expose:           redraw window
	// ConfigureNotify:  redraw window correctly when resized
	// Map/UnmapNotify:  redraw window when deiconified
	// SelectionRequest: paste to other program
	// SelectionClear:   yield ownership so paste works correctly in CSF

	if (event->type == Expose || event->type == ConfigureNotify ||
		event->type == MapNotify || event->type == UnmapNotify ||
		event->type == SelectionRequest || event->type == SelectionClear)
		{
		return True;
		}
	else
		{
		return False;
		}
}

Bool
JXApplication::DiscardNextEvent
	(
	Display*	display,
	XEvent*		event,
	char*		arg
	)
{
	DiscardEventInfo* info = reinterpret_cast<DiscardEventInfo*>(arg);

	if (info->eventWindow != NULL &&
		GetNextWindowEvent(display, event, reinterpret_cast<char*>(info->eventWindow)))
		{
		return False;
		}
	else if (event->type == KeyPress || event->type == KeyRelease ||
			 event->type == ButtonPress || event->type == ButtonRelease ||
			 event->type == MotionNotify ||
			 JXWindow::IsDeleteWindowMessage(info->display, *event))
		{
		return True;
		}
	else
		{
		return False;
		}
}

/******************************************************************************
 HandleCustomEventWhileBlocking (virtual protected)

	This function exists to support real-time applications which need
	to modify the subsidiary event loop that runs while a blocking dialog is
	active.  It is called every time through the event loop.

	Derived classes may just call HandleCustomEvent(), but they must first
	be sure that it is safe to do so.  The subsidiary event loop is only
	called when the main event loop is blocked.  This means that there may
	be an arbitrary amount of state stored on the execution stack and
	the affected objects may not be re-entrant.

	The derived class is not required to return after handling a single
	event, but if it waits too long before returning, then the graphical
	interface will react sluggishly.  This is especially important if the
	blocking window is a progress display, because the more often one
	returns, the sooner the window will disappear, thereby returning to
	the main event loop.

	*allowSleep controls whether or not the event loop idles by calling
	JWait().  The initial value is determined by JX.  If the derived class
	handles the sleeping via some other system call, then it should set it
	to kFalse.  It is not usually a good idea to set it to kTrue if it is
	initially kFalse.

 ******************************************************************************/

void
JXApplication::HandleCustomEventWhileBlocking
	(
	JBoolean* allowSleep
	)
{
}

/******************************************************************************
 InstallIdleTask

	We append the task so it will be performed next time if PerformIdleTasks
	is executing.  This insures that we will eventually reach the end
	of the task list.

 ******************************************************************************/

void
JXApplication::InstallIdleTask
	(
	JXIdleTask* newTask
	)
{
	if (!itsIdleTasks->Includes(newTask))
		{
		itsIdleTasks->Prepend(newTask);
		}
}

/******************************************************************************
 RemoveIdleTask

 ******************************************************************************/

inline void
JXApplication::RemoveIdleTask
	(
	JXIdleTask* task
	)
{
	if (!itsIgnoreTaskDeletedFlag)
		{
		itsIdleTasks->Remove(task);

		const JSize count = itsIdleTaskStack->GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			(itsIdleTaskStack->NthElement(i))->Remove(task);
			}
		}
}

/******************************************************************************
 PushIdleTaskStack (private)

 ******************************************************************************/

void
JXApplication::PushIdleTaskStack()
{
	itsIdleTaskStack->Append(itsIdleTasks);

	itsIdleTasks = new JPtrArray<JXIdleTask>;
	assert( itsIdleTasks != NULL );
}

/******************************************************************************
 PopIdleTaskStack (private)

	Since every object is required to delete its idle tasks, any
	remaining tasks should stay active.  (e.g. tasks installed by
	documents while a progress display is active)

 ******************************************************************************/

void
JXApplication::PopIdleTaskStack()
{
	if (!itsIdleTaskStack->IsEmpty())
		{
		JPtrArray<JXIdleTask>* list = itsIdleTasks;
		itsIdleTasks                = itsIdleTaskStack->LastElement();
		itsIdleTaskStack->RemoveElement(itsIdleTaskStack->GetElementCount());

		const JSize count = list->GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			itsIdleTasks->Append(list->NthElement(i));
			}
		delete list;
		}
}

/******************************************************************************
 PopAllIdleTaskStack (private)

	This could be optimized, but the test for IsEmpty() insures that
	it almost never has to do any work.

 ******************************************************************************/

void
JXApplication::PopAllIdleTaskStack()
{
	while (!itsIdleTaskStack->IsEmpty())
		{
		PopIdleTaskStack();
		}
}

/******************************************************************************
 PerformIdleTasks (private)

 ******************************************************************************/

void
JXApplication::PerformIdleTasks()
{
	itsMaxSleepTime = kMaxSleepTime;

	if (!itsIdleTasks->IsEmpty())		// avoid constructing iterator
		{
		JPtrArrayIterator<JXIdleTask> iter(itsIdleTasks);
		JXIdleTask* task;
		const Time deltaTime = itsCurrentTime - itsLastIdleTaskTime;
		while (iter.Next(&task))
			{
			Time maxSleepTime = itsMaxSleepTime;
			task->Perform(deltaTime, &maxSleepTime);
			if (maxSleepTime < itsMaxSleepTime)
				{
				itsMaxSleepTime = maxSleepTime;
				}
			}
		}

	if (!itsHasBlockingWindowFlag)
		{
		// let sockets broadcast

		CheckACEReactor();

		// let processes broadcast -- not necessary to check each time

		itsWaitForChildCounter++;
		if (itsWaitForChildCounter >= kWaitForChildCount)
			{
			JProcess::CheckForFinishedChild(kFalse);
			itsWaitForChildCounter = 0;
			}
		}

	JXMDIServer* mdiServer = NULL;
	if (JXGetMDIServer(&mdiServer))
		{
		mdiServer->CheckForConnections();
		}

	// save time for next call

	itsLastIdleTaskTime = itsCurrentTime;
}

/******************************************************************************
 CheckACEReactor (protected)

	Bumps the ACE reactor to check sockets, signals, etc.

	This is protected so custom event loops can call it directly.

	It is not generally a good idea to call this if there is a blocking
	window open because it may invoke arbitrary amounts of code, which is
	dangerous since JX is not re-entrant.

 ******************************************************************************/

#ifndef __MWERKS__

void
JXApplication::CheckACEReactor()
{
	ACE_Time_Value timeout(0);
//	ACE_Reactor::run_event_loop(timeout);	// locks up when child process exits

	(ACE_Reactor::instance())->handle_events(timeout);
	ACE_Reactor::check_reconfiguration(NULL);
}

#endif

/******************************************************************************
 InstallUrgentTask

	We insert the task in front of the execution iterator so it will be
	performed next time, if PerformUrgentTasks is executing.  This insures
	that we will eventually reach the end of the task list.

 ******************************************************************************/

void
JXApplication::InstallUrgentTask
	(
	JXUrgentTask* newTask
	)
{
	if (!itsUrgentTasks->Includes(newTask))
		{
		itsUrgentTasks->InsertAtIndex(itsUTInsertIndex, newTask);
		itsUTInsertIndex++;
		}
}

/******************************************************************************
 RemoveUrgentTask

 ******************************************************************************/

void
JXApplication::RemoveUrgentTask
	(
	JXUrgentTask* task
	)
{
	if (!itsIgnoreTaskDeletedFlag)
		{
		JIndex i;
		if (itsUrgentTasks->Find(task, &i))
			{
			itsUrgentTasks->RemoveElement(i);
			if (i < itsUTInsertIndex)
				{
				itsUTInsertIndex--;
				}
			}
		}
}

/******************************************************************************
 PerformUrgentTasks (private)

	We don't just delete them all at one time at the end because new ones
	might get registered, and these need to be left there until next time.

 ******************************************************************************/

void
JXApplication::PerformUrgentTasks()
{
	itsUTInsertIndex = 1;

	if (!itsUrgentTasks->IsEmpty())
		{
		JPtrArrayIterator<JXUrgentTask> iter(itsUrgentTasks);
		JXUrgentTask* task;
		while (iter.Next(&task))
			{
			// remove it from the list and store it in case it invokes the event loop

			RemoveUrgentTask(task);
			itsActiveUrgentTasks->Append(task);
			task->Perform();
			itsActiveUrgentTasks->Remove(task);
			delete task;
			}
		}

	JXDisplay::CheckForXErrors();

	// We check in this order so CheckForSignals() can broadcast even
	// if the app is suspended.

	if (!itsHasBlockingWindowFlag && JThisProcess::CheckForSignals() &&
		!IsSuspended())
		{
		Quit();
		}
}

/******************************************************************************
 ParseBaseOptions (static private)

	This is static because it is called before any other code in the
	constructor.

 ******************************************************************************/

void
JXApplication::ParseBaseOptions
	(
	int*		argc,
	char*		argv[],
	JString*	displayName
	)
{
	displayName->Clear();

	for (long i=1; i<*argc; i++)
		{
		if (strcmp(argv[i], kDisplayOptionName) == 0)
			{
			i++;
			if (i >= *argc || argv[i][0] == '-')
				{
				cerr << argv[0] << ": Invalid display option" << endl;
				JThisProcess::Exit(1);
				}
			*displayName = argv[i];
			RemoveCmdLineOption(argc, argv, i-1, 2);
			i -= 2;
			}
		}
}

/******************************************************************************
 RemoveCmdLineOption (static)

 ******************************************************************************/

void
JXApplication::RemoveCmdLineOption
	(
	int*				argc,
	char*				argv[],
	const unsigned long	offset,
	const unsigned long	removeCount
	)
{
	const long firstKeep = offset + removeCount;
	assert( firstKeep <= *argc );

	for (long i=firstKeep; i<*argc; i++)
		{
		argv[i - removeCount] = argv[i];
		}

	*argc -= removeCount;
}

/******************************************************************************
 JXIOErrorHandler (static)

	Calls JXApplication::Abort().

 ******************************************************************************/

int
JXApplication::JXIOErrorHandler
	(
	Display* xDisplay
	)
{
	Abort(JXDocumentManager::kServerDead, kFalse);
	return 0;
}

/******************************************************************************
 Abort (static)

	When something explodes, we get one last chance to clean things up before
	we have to exit.  We trigger a safety save and then give derived classes
	a chance to save other things, like preferences.  If something explodes
	again, we give up and die immediately.

	This function does not return.

 ******************************************************************************/

static JBoolean abortCalled = kFalse;

void
JXApplication::Abort
	(
	const JXDocumentManager::SafetySaveReason	reason,
	const JBoolean								dumpCore
	)
{
	if (!abortCalled)
		{
		abortCalled = kTrue;

		JXDocumentManager* docMgr = NULL;
		if (JXGetDocumentManager(&docMgr))
			{
			docMgr->SafetySave(reason);
			}

		JXApplication* app;
		if (JXGetApplication(&app))
			{
			app->CleanUpBeforeSuddenDeath(reason);
			}
		}
	else
		{
		fprintf(stderr, "\nError inside XIO fatal error handler!\n\n");
		}

	if (dumpCore)
		{
		JThisProcess::Abort();
		}
	else
		{
		JThisProcess::Exit(1);
		}
}

/******************************************************************************
 CleanUpBeforeSuddenDeath (virtual protected)

	*** If the server is dead, you cannot call any code that contacts it.

 ******************************************************************************/

void
JXApplication::CleanUpBeforeSuddenDeath
	(
	const JXDocumentManager::SafetySaveReason reason
	)
{
}
