/*ScianEvents.c
  Eric Pepke
  March 8, 1990
  Event stuff for scian
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianWindows.h"
#include "ScianEvents.h"
#include "ScianGarbageMan.h"
#include "ScianSockets.h"
#include "ScianScripts.h"
#include "ScianColors.h"
#include "ScianIDs.h"
#include "ScianTimers.h"
#include "ScianMainWindow.h"
#include "ScianObjWindows.h"
#include "ScianVisWindows.h"
#include "ScianPreferences.h"
#include "ScianObjFunctions.h"
#include "ScianDialogs.h"
#include "ScianDraw.h"
#include "ScianStyle.h"
#include "ScianLists.h"
#include "ScianObjWindows.h"

ObjPtr dropObject = NULLOBJ;		/*Object to drop in window*/
int dropX, dropY;			/*Global coordinates of place to drop*/
#ifdef INTERACTIVE
int curMouse = LEFTMOUSE;		/*Current mouse button down*/
Bool interactiveMoving = false;		/*True iff moving interactively*/
Bool contextHelp = false;		/*Context sensitive help*/
#endif
#ifdef IRISNTSC
int NTSCOn = 0;				/*True iff NTSC is on*/
#endif
long lastGC = 0;			/*Time of last garbage collection*/
#define GCEVERY	((HEARTBEAT) * 10)	/*One garbage collection per ten seconds*/

Event events[MAXNEVENTS];
int curEvent = 0;			/*Current and last events for queue*/
int lastEvent = 0;			/*Queue empty when they're the same*/
Bool knownUserEvent;			/*There is a user event known*/

#define MAXNTASKS	100		/*Maximum # of tasks*/

TaskType doTasks[MAXNTASKS];		/*Tasks to do*/
int tasksLeft = 0;			/*Number of tasks left*/

#define MAXNDEFMES	100		/*Maximum # of deferred messages*/

int curDef = 0;				/*Current deferred message set*/
WinInfoPtr lastCursorWindow = 0;	/*Last cursor window*/

struct
    {
	ObjPtr object;			/*The object to send the message*/
	int message;			/*The message to send*/
    } defMessages[2][MAXNDEFMES];
int defMesLeft = 0;

#ifdef INTERACTIVE
long lastClickTime = 0;			/*The time the last click was done*/
long lastKeyTime = 0;			/*The last time of a keypress*/
int lastClickX, lastClickY;		/*The last click X and Y*/ 
int sentKey = 0;			/*Last key that has been sent*/
WinInfoPtr keyWindow = 0;		/*Window the last key was sent to*/
#endif

int running = 1;			/*True iff running*/

#ifdef INTERACTIVE
/*Values for mouse switchboard*/
#define MOUSEPRESS	1		/*Select, drag, etc. with mouse*/
#define MOUSEMENU	2		/*Bring up mouse menu*/
#define MOUSEROTATE	3		/*Rotate with the mouse*/

/*Mouse switchboard*/
int mouseSwitchboard[3] =
    {
	MOUSEMENU,
	MOUSEROTATE,
	MOUSEPRESS
    };
#endif

void DoTask(task)
TaskType task;
/*Asks the system to do task at its earliest possible convenience*/
{
    doTasks[tasksLeft++] = task;
}

void DeferMessage(object, message)
ObjPtr object;
int message;
/*Asks the system to do task at its earliest possible convenience*/
{
    defMessages[curDef][defMesLeft] . object = object;
    defMessages[curDef][defMesLeft] . message = message;
    ++defMesLeft;
}

void DoUniqueTask(task)
TaskType task;
/*Asks the system to do task at its earliest possible convenience,
  but only if it hasn't already been requested*/
{
    int k;
    for (k = 0; k < tasksLeft; ++k)
    {
	if (doTasks[k] == task)
	{
	    return;
	}
    }
    doTasks[tasksLeft++] = task;
}

void DoQuit()
/*Asks to quit on the next loop*/
{
    running = 0;
}

ObjPtr QuitReply(window, button)
WinInfoPtr window;
int button;
{
    if (button == 0)
    {
	DoQuit();
    }
}

void MaybeQuit()
/*Maybe quits on the next loop*/
{
    if (OptionDown())
    {
	DoQuit();
    }
    else
    {
	WinInfoPtr alertWindow;
	alertWindow = AlertUser(UIERRORALERT, (WinInfoPtr) 0, "Do you really want to quit SciAn?",
		QuitReply, 2, "Quit", "Cancel");
	SetVar((ObjPtr) alertWindow, INHIBITLOGGING, ObjTrue);
    }
}

#ifdef INTERACTIVE
void QueueDevices()
/*Queues up all the devices that will be used by Scian*/
{
    qdevice(KEYBD);
    qdevice(PADPF1);
    qdevice(F1KEY);
    qdevice(PADPF2);
    qdevice(F2KEY);
    qdevice(PADPF3);
    qdevice(F3KEY);
    qdevice(PADPF4);
    qdevice(F4KEY);
    qdevice(F5KEY);
    qdevice(F6KEY);
    qdevice(F7KEY);
    qdevice(F8KEY);
    qdevice(F9KEY);
    qdevice(F10KEY);
    qdevice(F11KEY);
    qdevice(F12KEY);
    qdevice(MOUSE1);
    qdevice(MOUSE2);
    qdevice(MOUSE3);
    qdevice(UPARROWKEY);
    qdevice(DOWNARROWKEY);
    qdevice(LEFTARROWKEY);
    qdevice(RIGHTARROWKEY);
    qdevice(WINFREEZE);
    qdevice(WINTHAW);
#ifdef DEPTHCHANGE
    qdevice(DEPTHCHANGE);
#endif
#ifdef IRIS
#ifdef WINQUIT
    qdevice(WINQUIT);
#endif
    qdevice(WINSHUT);
    qdevice(BUT157);
#endif
}

void UnqueueDevices()
/*Unqueues all the devices*/
{
#ifdef DEPTHCHANGE
    unqdevice(DEPTHCHANGE);
#endif
    unqdevice(WINFREEZE);
    unqdevice(WINTHAW);
    unqdevice(KEYBD);
    unqdevice(PADPF1);
    unqdevice(F1KEY);
    unqdevice(PADPF2);
    unqdevice(F2KEY);
    unqdevice(PADPF3);
    unqdevice(F3KEY);
    unqdevice(PADPF4);
    unqdevice(F4KEY);
    unqdevice(F5KEY);
    unqdevice(F6KEY);
    unqdevice(F7KEY);
    unqdevice(F8KEY);
    unqdevice(F9KEY);
    unqdevice(F10KEY);
    unqdevice(F11KEY);
    unqdevice(F12KEY);
    unqdevice(MOUSE1);
    unqdevice(MOUSE2);
    unqdevice(MOUSE3);
    unqdevice(UPARROWKEY);
    unqdevice(DOWNARROWKEY);
    unqdevice(LEFTARROWKEY);
    unqdevice(RIGHTARROWKEY);
#ifdef IRIS
#ifdef WINQUIT
    qdevice(WINQUIT);
#endif
    unqdevice(WINSHUT);
    unqdevice(BUT157);
#endif
}
#endif

void LongOperation()
/*Signals the user that a long operation is about to occur
  This version sets to the watchcursor*/
{
    /*
    MySetCursor(WATCHCURSOR);
    */
}

void EndLongOperation()
/*Ends a long operation*/
{
    /*
    if (curCursor == WATCHCURSOR)
    {
	MySetCursor(0);
    }
    */
}

#ifdef INTERACTIVE
void SetContextSensitiveHelp()
/*Sets up the system so that the next press click is a help*/
{
    MySetCursor(QUESTIONCURSOR);
    contextHelp = true;
}
#endif

#ifdef INTERACTIVE
#ifdef PROTO
void KeyDown(int theKey, long flags)
#else
void KeyDown(theKey, flags)
int theKey;
long flags;
#endif
/*Does a keyDown for theKey with flags.*/
{
    WinInfoPtr window;
    window = inputWindow;
    if (!inputWindow)
    {
	/*No focus; return*/
	return;
    }
    SelWindow(inputWindow);

    if (!selWinInfo)
    {
	/*Pathological case.  Return.*/
	return;
    }

    {
	FuncTyp actionMethod;		/*The method to call for the action*/

	actionMethod = GetMethod((ObjPtr) selWinInfo, KEYDOWN);
	if (actionMethod)
	{
	    /*There's a key down routine.  Call it.*/
	    if (theKey == '\r')
	    {
		/*Kludge to get it to flush stuff before enter key*/
		FlushKeystrokes();
	    }
	    (*actionMethod)(selWinInfo, theKey, flags);
	}
    }
}
#endif

void FlushKeystrokes()
/*Flushes the keystrokes to an object that takes keystrokes*/
{
#ifdef INTERACTIVE
    if (sentKey && IsValidWindow(keyWindow))
    {
	WinInfoPtr tempInput;
	Screencoord l, r, b, t; 
	WinInfoPtr oldWindow;

	oldWindow = selWinInfo;

	tempInput = inputWindow;
	inputWindow = keyWindow;
	sentKey = 0;
	KeyDown(0, 0);
	inputWindow = tempInput;

	if (oldWindow)
	{
	    SelWindow(oldWindow);
	}
    }
#endif
}

Bool Mouse(x, y)
int *x, *y;
/*Returns in x and y the current mouse position in local coordinates*/
{
#ifdef INTERACTIVE
    int ox, oy;

    UD();

    CurOffset(&ox, &oy);

    *x = getvaluator(MOUSEX) - ox;
    *y = getvaluator(MOUSEY) - oy;

    switch (curMouse)
    {
	case MB_LEFT:
	    return getbutton(MOUSE1);
	case MB_MIDDLE:
	    return getbutton(MOUSE2);
	case MB_RIGHT:
	    return getbutton(MOUSE3);
    }
#else
    return false;
#endif
}

#ifdef INTERACTIVE
static ObjPtr AbortScript(owner, button)
ObjPtr owner;
int button;
/*Callback routine to abort a script*/
{
    if (button)
    {
	/*Continue*/
	runningScript = true;
    }
    else
    {
	/*Real abort*/
	EndScript();
    }
}

void AbortScriptAlert()
/*Puts up an alert to abort the script*/
{
    WinInfoPtr errWindow;
    runningScript = false;
    errWindow = AlertUser(UICAUTIONALERT, (WinInfoPtr) 0,
	"SciAn is currently running a script.  Would you like to abort the \
script or continue running it?",
        AbortScript, 2, "Abort", "Continue");
    SetVar((ObjPtr) errWindow, HELPSTRING,
        NewString("SciAn expects no user interaction while running a script.  \
This is because changing the state of the windows of SciAn while running a \
script can cause the script to generate an error.  It is best either to \
abort the script or continue and not interfere with it.  However, if you are really \
sure you know what you are doing, you can interact with SciAn now and press the \
Continue button later.  This is not recommended for the inexperienced."));
}

#ifdef PROTO
void MouseDown(int whichButton, int x, int y, long flags)
#else
void MouseDown(whichButton, x, y, flags)
int whichButton;
int x, y;
long flags;
#endif
/*Does a mouseDown.
  whichButton should be MB_LEFT, MB_MIDDLE, or MB_RIGHT.
  x and y are in global coordinates.*/
{
    int action;				/*The action to do*/
    FuncTyp actionMethod;		/*The method to call for the action*/
    long ox, oy;
    WinInfoPtr mouseWindow;

    curMouse = whichButton;

    mouseWindow = WhichWindow(x, y);
    if (!mouseWindow && !(IsValidWindow(mouseWindow)))
    {
	/*No focus; return*/
	return;
    }

    SelWindow(mouseWindow);
   
    if (!selWinInfo)
    {
	/*Pathological case.  Return.*/
	return;
    }

    getorigin(&ox, &oy);
    x = x - ox;
    y = y - oy;

    interactiveMoving = true;

    /*Determine the action based on the mouse switchboard*/
    switch(whichButton)
    {
	case MB_LEFT:
	    action = mouseSwitchboard[0];
	    break;
	case MB_MIDDLE:
	    action = mouseSwitchboard[1];
	    break;
	case MB_RIGHT:
	    action = mouseSwitchboard[2];
	    break;
    }

    /*Do the action*/
    switch(action)
    {
	case MOUSEROTATE:
	    actionMethod = GetMethod((ObjPtr) selWinInfo, PRESS);
	    if (actionMethod)
	    {
		/*There's a press routine.  Call it.*/
		if (contextHelp)
		{
		    (*actionMethod)(selWinInfo, x, y, T_HELP | flags);
		    contextHelp = false;
		    MySetCursor(0);
		}	
		else
		{
		    (*actionMethod)(selWinInfo, x, y, T_ROTATE | flags);
		}
	    }
	    break;
	case MOUSEPRESS:
	    if (dragBuffer)
	    {
		dropX = getvaluator(MOUSEX);
		dropY = getvaluator(MOUSEY);
		MySetCursor(0);
	    }
	    actionMethod = GetMethod((ObjPtr) selWinInfo, PRESS);
	    if (actionMethod)
	    {
		/*There's a press routine.  Call it.*/
		if (contextHelp)
		{
		    (*actionMethod)(selWinInfo, x, y, T_HELP | flags);
		    contextHelp = false;
		    MySetCursor(0);
		}	
		else
		{
		    (*actionMethod)(selWinInfo, x, y, T_PRESS | flags);
		}
	    }
	    if (dragBuffer)
	    {
		DeleteThing(dragBuffer);
		dragBuffer = 0;
	    }

	    break;
	case MOUSEMENU:

	    /*See if there are super menus*/
	    while (mouseWindow = (WinInfoPtr) GetVar((ObjPtr) mouseWindow, SUPERWINDOW))
	    {
		SelWindow(mouseWindow);
	    }

	    if (0 || !selWinInfo -> mainMenu)
	    {
		/*There's no menu.  Try to build one.*/
		BuildMenu(selWinInfo);
	    }
	    if (contextHelp)
	    {
		EnableFunctionMenus(selWinInfo);
	    }
	    else
	    {
	        AdjustFunctionMenus(selWinInfo);
	    }
	    if (selWinInfo -> mainMenu)
	    {
		/*There's a menu.  Do it.*/
		dopup(selWinInfo -> mainMenu);
	    }
	    break;
    }
    interactiveMoving = false;
}
#endif

#ifdef IRISNTSC
void SetNTSC()
/*Sets to NTSC mode*/
{
    NTSCOn = true;
#ifdef GRAPHICS
    setmonitor(NTSC);
#ifdef GL4D
    setvideo(CG_MODE, CG2_M_MODE2);
    setvideo(DE_R1, DER1_G_170);
#endif
#endif
}

void Set60()
/*Sets to 60 Hz mode*/
{
    NTSCOn = false;
#ifdef GRAPHICS
    setmonitor(HZ60);
#endif
}

void ToggleNTSC()
/*Toggles in and out of NTSC mode*/
{
    NTSCOn = NTSCOn ? false : true;
#ifdef GRAPHICS
    setmonitor(NTSCOn ? NTSC : HZ60);
#ifdef GL4D
    if (NTSCOn)
    {
	setvideo(CG_MODE, CG2_M_MODE2);
	setvideo(DE_R1, DER1_G_170);
    }
#endif
#endif
}
#endif

Bool AltDown()
/*Returns true iff the alt key is down*/
{
#ifdef INTERACTIVE
    return (getbutton(LEFTALTKEY) || getbutton(RIGHTALTKEY)) ? true : false;
#else
    return false;
#endif
}

Bool ShiftDown()
/*Returns true iff the shift key is down*/
{
#ifdef INTERACTIVE
    return (getbutton(LEFTSHIFTKEY) || getbutton(RIGHTSHIFTKEY)) ? true : false;
#else
    return false;
#endif
}

Bool OptionDown()
/*Returns true iff the option key is down*/
{
#ifdef INTERACTIVE
    return (getbutton(LEFTCTRLKEY) || getbutton(RIGHTCTRLKEY) ||
		getbutton(CAPSLOCKKEY)) ? true : false;
#else
    return false;
#endif
}

Bool UserEvent()
/*Determine if there is a user event (mouse down or key down or close
  window) in the queue*/
{
    int k;

    if (knownUserEvent)
    {
	return true;
    }

#ifndef SIGNALS
    NewEvents();
#endif

    k = curEvent;
    while (k != lastEvent)
    {
	k = (k + 1) % MAXNEVENTS;
	if (ISUSEREVENT(events[k] . type))
	{
	    knownUserEvent = true;
	    return true;
	}
    }
    return false;
}

#ifdef PROTO
void PostEvent(Event *event)
#else
void PostEvent(event)
Event *event;
#endif
/*Posts an event just like event, except for time*/
{
    int k;
    char *a, *b;
    struct tms buffer;

    lastEvent = (lastEvent + 1) % MAXNEVENTS;

    a = (char *) event;
    b = (char *) &(events[lastEvent]);

    for (k = 0; k < sizeof(Event); ++k)
    {
	b[k] = a[k];
    }
    events[lastEvent] . time = times(&buffer);
    if (ISUSEREVENT(event -> type))
    {
	knownUserEvent = true;
    }
}

void NewEvents()
/*Get all new events and deposit them in the event queue.  Do this every once
  in a while.*/
{
#ifdef INTERACTIVE
    /*Look for an event*/

    while (qtest())
    {
        long whichDevice;
        short theData;
	WinInfoPtr window;
	struct tms buffer;

        whichDevice = qread(&theData);
	lastEvent = (lastEvent + 1) % MAXNEVENTS;

	events[lastEvent] . type = ET_UNKNOWN;
	events[lastEvent] . time = times(&buffer);
	events[lastEvent] . flags = 0;
	/*Determine if the modifier keys are held down*/
	if (getbutton(LEFTSHIFTKEY) || getbutton(RIGHTSHIFTKEY))
	{
	    events[lastEvent] . flags |= F_SHIFTDOWN;
	}
	if (getbutton(LEFTCTRLKEY) || getbutton(RIGHTCTRLKEY) || getbutton(CAPSLOCKKEY))
	{
	    events[lastEvent] . flags |= F_OPTIONDOWN;
	}
	if (getbutton(LEFTALTKEY) || getbutton(RIGHTALTKEY))
	{
	    events[lastEvent] . flags |= F_OPTIONDOWN;
	}

        switch (whichDevice)
        {
	    case INPUTCHANGE:
		if (theData && (window = GetWinInfo(theData)))
		{
		    events[lastEvent] . type = ET_SET_WINDOW;
		    events[lastEvent] . data . window = window;
		}
		break;
#ifdef DEPTHCHANGE
	    case DEPTHCHANGE:
		if (theData && (window = GetWinInfo(theData)))
		{
		    events[lastEvent] . type = ET_DEPTHCHANGE_WINDOW;
		    events[lastEvent] . data . window = window;
		}
		break;
#endif
	    case WINFREEZE:
		if (theData && (window = GetWinInfo(theData)))
		{
		    events[lastEvent] . type = ET_ICONIFY_WINDOW;
		    events[lastEvent] . data . window = window;
		}
		break;
	    case WINTHAW:
		if (theData && (window = GetWinInfo(theData)))
		{
		    events[lastEvent] . type = ET_DEICONIFY_WINDOW;
		    events[lastEvent] . data . window = window;
		}
		break;
             case KEYBD:
		if (events[lastEvent] . time - lastKeyTime < CLICK2TIME)
		{ 
		    events[lastEvent] . flags |= F_DOUBLECLICK; 
		    lastKeyTime = events[lastEvent] . time;
		}
		events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = theData;
		break;
	    case MOUSE1:
		if (theData)
		{
		    int newX, newY;

		    /*Determine if this is potentially a double click*/
		    newX = getvaluator(MOUSEX);
		    newY = getvaluator(MOUSEY);
		    if (events[lastEvent] . time - lastClickTime < CLICK2TIME &&
			ABS(newX - lastClickX) < CLICK2DIST &&
			ABS(newY - lastClickY) < CLICK2DIST)
		    {
			events[lastEvent] . flags |= F_DOUBLECLICK;
		    }

		    lastClickX = newX;
		    lastClickY = newY;

		    events[lastEvent] . type = ET_MOUSE_DOWN;
		}
		else
		{
		    events[lastEvent] . type = ET_MOUSE_UP;
		    lastClickTime = events[lastEvent] . time;
		}
		events[lastEvent] . data . mouse . x = getvaluator(MOUSEX);
		events[lastEvent] . data . mouse . y = getvaluator(MOUSEY);
 		events[lastEvent] . data . mouse . mouseButton = MB_LEFT;
		break;
	    case MOUSE2:
		if (theData)
		{
		    int newX, newY;

		    /*Determine if this is potentially a double click*/
		    newX = getvaluator(MOUSEX);
		    newY = getvaluator(MOUSEY);
		    if (events[lastEvent] . time - lastClickTime < CLICK2TIME &&
			ABS(newX - lastClickX) < CLICK2DIST &&
			ABS(newY - lastClickY) < CLICK2DIST)
		    {
			events[lastEvent] . flags |= F_DOUBLECLICK;
		    }

		    lastClickX = newX;
		    lastClickY = newY;

		    events[lastEvent] . type = ET_MOUSE_DOWN;
		}
		else
		{
		    events[lastEvent] . type = ET_MOUSE_UP;
		    lastClickTime = events[lastEvent] . time;
		}
		events[lastEvent] . data . mouse . x = getvaluator(MOUSEX);
		events[lastEvent] . data . mouse . y = getvaluator(MOUSEY);
 		events[lastEvent] . data . mouse . mouseButton = MB_MIDDLE;
		break;
	    case MOUSE3:
		if (theData)
		{
		    int newX, newY;

		    /*Determine if this is potentially a double click*/
		    newX = getvaluator(MOUSEX);
		    newY = getvaluator(MOUSEY);
		    if (events[lastEvent] . time - lastClickTime < CLICK2TIME &&
			ABS(newX - lastClickX) < CLICK2DIST &&
			ABS(newY - lastClickY) < CLICK2DIST)
		    {
			events[lastEvent] . flags |= F_DOUBLECLICK;
		    }

		    lastClickX = newX;
		    lastClickY = newY;

		    events[lastEvent] . type = ET_MOUSE_DOWN;
		}
		else
		{
		    events[lastEvent] . type = ET_MOUSE_UP;
		    lastClickTime = events[lastEvent] . time;
		}
		events[lastEvent] . data . mouse . x = getvaluator(MOUSEX);
		events[lastEvent] . data . mouse . y = getvaluator(MOUSEY);
 		events[lastEvent] . data . mouse . mouseButton = MB_RIGHT;
		break;
	    case REDRAW:
		if (theData && (window = GetWinInfo(theData)))
		{
		    events[lastEvent] . type = ET_RESHAPE_WINDOW;
		    events[lastEvent] . data . window = window;
		}
		break;
	    case PIECECHANGE:
		if (theData && (window = GetWinInfo(theData)))
		{
		    events[lastEvent] . type = ET_RESHAPE_WINDOW;
		    events[lastEvent] . data . window = window;
		}
		break;
	    case UPARROWKEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_UP_ARROW;
		}
		break;
	    case DOWNARROWKEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_DOWN_ARROW;
		}
		break;
	    case LEFTARROWKEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_LEFT_ARROW;
		}
		break;
	    case RIGHTARROWKEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_RIGHT_ARROW;
		}
		break;
#ifdef GL4D
	    case BUT157:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_PRINT_SCREEN;
		}
		break;
#endif
	    case PADPF1:
	    case F1KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_1;
		}
		break;
	    case PADPF2:
	    case F2KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_2;
		}
		break;
	    case PADPF3:
	    case F3KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_3;
		}
		break;
	    case PADPF4:
	    case F4KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		events[lastEvent] . data . key = FK_4;
		}
		break;
	    case F5KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_5;
		}
		break;
	    case F6KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_6;
		}
		break;
	    case F7KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_7;
		}
		break;
	    case F8KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_8;
		}
		break;
	    case F9KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_9;
		}
		break;
	    case F10KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_10;
		}
		break;
	    case F11KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_11;
		}
		break;
	    case F12KEY:
		if (theData)
		{
		    events[lastEvent] . type = ET_KEY_DOWN;
		    events[lastEvent] . data . key = FK_12;
		}
		break;
#ifdef IRIS
	    case WINSHUT:
		if (theData && (window = GetWinInfo(theData)))
		{
		    events[lastEvent] . type = ET_CLOSE_WINDOW;
		    events[lastEvent] . data . window = window;
		}
		break;
#ifdef WINQUIT
	    case WINQUIT:
		if (theData)
		{
		    events[lastEvent] . type = ET_QUIT;
		    events[lastEvent] . data . window = 0;
		}
		break;
#endif

#endif
	}
    }
#endif
}

#ifdef PROTO
Bool OneEvent(Bool processInput)
#else
Bool OneEvent(processInput)
Bool processInput;
#endif
/*Gets and processes one event.  if processInput is true, process input*/
{
    Bool readFromScript = true;
    int k;

    if (logging)
    {
	InhibitLogging(true);
    }
    /*Do the messages remaining*/
    if (defMesLeft)
    {
	int which, defLeft;
	defLeft = defMesLeft;
	which = curDef;
	curDef = curDef ? 0 : 1;
	defMesLeft = 0;
	for (k = 0; k < defLeft; ++k)
	{
	    FuncTyp method;
	    method = GetMethod(defMessages[which][k] . object, defMessages[which][k] . message);
	    if (method)
	    {
		(*method)(defMessages[which][k] . object);
	    }
	}
    }
    if (logging)
    {
	InhibitLogging(false);
    }
    
    /*Do a task that's waiting*/
    if (tasksLeft)
    {
	for (k = 0; k < tasksLeft; ++k)
	{
	    (*(doTasks[k]))();
	}
	tasksLeft = 0;
    }

#ifdef INTERACTIVE
#ifndef SIGNALS
    NewEvents();
#endif
    /*Look for an event*/
    if (curEvent != lastEvent)
    {
	/*There is an event*/
	curEvent = (curEvent + 1) % MAXNEVENTS;
	readFromScript = false;

	knownUserEvent = false;
        switch (events[curEvent] . type)
        {
	    case ET_SET_WINDOW:
		/*Changed windows, reset the moving quality to highest*/
		if (IsValidWindow(events[curEvent] . data . window))
		{
		    inputWindow = events[curEvent] . data . window;
		}
		else
		{
		    inputWindow = 0;
		}
		break;
	    case ET_ICONIFY_WINDOW:
		/*Window has been iconified*/
		if (IsValidWindow(events[curEvent] . data . window))
		{
		    events[curEvent] . data . window -> flags |= WINICONIFIED;
		}
		break;
	    case ET_DEICONIFY_WINDOW:
		/*Window has been iconified*/
		if (IsValidWindow(events[curEvent] . data . window))
		{
		    events[curEvent] . data . window -> flags &= ~WINICONIFIED;
		}
		break;
	    case ET_DEPTHCHANGE_WINDOW:
		/*Window's depth has changed.  See if we need to 
		  change any subwindow*/
		if (IsValidWindow(events[curEvent] . data . window))
		{
		    WinInfoPtr superWindow;
		    ObjPtr subWindows;

		    /*See if it has subwindows and needs to be popped*/
		    superWindow = (WinInfoPtr) GetVar((ObjPtr) events[curEvent] . data . window, SUPERWINDOW);
		    subWindows = GetVar((ObjPtr) superWindow, SUBWINDOWS);

		    if (subWindows && !superWindow)
		    {
			/*Window has sub windows*/
			if (TopWindow() == events[curEvent] . data . window)
			{
			    /*Pop the subwindows*/
			    ThingListPtr runner;
			    runner = LISTOF(subWindows);
			    while (runner)
			    {
				PopWindow((WinInfoPtr) runner -> thing);
				runner = runner -> next;
			    }
			}
			else if (BottomWindow() == events[curEvent] . data . window)
			{
			    ThingListPtr runner;
			    /*This window has just been pushed*/
	
			    /*This may be a superwindow which has been pushed.
			      Check to see if all the subwindows are OK*/
			    superWindow = events[curEvent] . data . window;
			    subWindows = GetVar((ObjPtr) superWindow, SUBWINDOWS);
			    runner = LISTOF(subWindows);
			    while (runner)
			    {
				if (!BelowAllBut((WinInfoPtr) runner -> thing, superWindow))
				{
				    /*Window out of place*/
				    break;
				}
				runner = runner -> next;
			    }

			    if (runner)
			    {
				/*One window is out of place.  Push subwindows*/
				runner = LISTOF(subWindows);
				while (runner)
				{
				    PushWindow((WinInfoPtr) runner -> thing);
				    runner = runner -> next;
				}

				/*Push super window*/
				PushWindow(superWindow);
			    }
			}
		    }
		}
		break;
            case ET_KEY_DOWN:
		if (runningScript)
		{
		    AbortScriptAlert();
		    return;
		}
		if (events[curEvent] . data . key < FK_BASE)
		{
		    sentKey = events[curEvent] . data . key;
		    keyWindow = inputWindow;
		    KeyDown(events[curEvent] . data . key,
			events[curEvent] . flags);
		}
		else
		{
		    switch(events[curEvent] . data . key)
		    {
			case FK_PRINT_SCREEN:
			    if (inputWindow)
			    {
				SelWindow(inputWindow);
				SaveScreen(inputWindow, events[curEvent] . flags);
			    }
			    break;
			case FK_1:
#ifndef RELEASE
			    Undo();
#endif
			    break;
			case FK_2:
			    CutText();
			    break;
			case FK_3:
			    CopyText();
			    break;
			case FK_4:
			    PasteText();
			    break;
			case FK_5:
			    DoShowControls();
			    break;
			case FK_6:
#ifndef RELEASE
			    TestObserver();
#endif
			    break;
			case FK_7:
			    DoObjFunction(OF_PUSHTOBOTTOM);
			    break;
			case FK_8:
			    DoObjFunction(OF_BRINGTOTOP);
			    break;
			case FK_9:
			    DoPickUpObjects();
			    break;
			case FK_10:
#ifdef IRISNTSC
			    ToggleNTSC();
#endif
			    break;
			case FK_11:
			    break;
			case FK_12:
			    SetContextSensitiveHelp();
			    break;
		    }
		}
		break;
	    case ET_MOUSE_DOWN:
		if (runningScript)
		{
		    AbortScriptAlert();
		    return;
		}
		MouseDown(events[curEvent] . data . mouse . mouseButton,
			events[curEvent] . data . mouse . x,
			events[curEvent] . data . mouse . y,
			events[curEvent] . flags);
		break;
	    case ET_RESHAPE_WINDOW:
		if (IsValidWindow(events[curEvent] . data . window))
		{
		    ObjPtr subWindows;
		    long ol, or, ob, ot, nl, nr, nb, nt;
		    long sx, sy, ox, oy;
		    SelWindow(events[curEvent] . data . window);
		    
		    if (!selWinInfo)
		    {
			break;
		    }

		    reshapeviewport();

		    /*Stuff ol, or, ob, ot*/
		    getorigin(&ox, &oy);
		    getsize(&sx, &sy);

		    ol = ((WinInfoPtr) selWinInfo) -> nl;
		    or = ((WinInfoPtr) selWinInfo) -> nr;
		    ob = ((WinInfoPtr) selWinInfo) -> nb;
		    ot = ((WinInfoPtr) selWinInfo) -> nt;

		    nl = ox;
		    nr = ox + sx;
		    nb = oy;
		    nt = oy + sy;

		    if (nl != ol || nr != or || nb != ob || nt != ot)
		    {
			/*Change this window*/
			((WinInfoPtr) selWinInfo) -> ol = ol;
			((WinInfoPtr) selWinInfo) -> or = or;
			((WinInfoPtr) selWinInfo) -> ob = ob;
			((WinInfoPtr) selWinInfo) -> ot = ot;

			((WinInfoPtr) selWinInfo) -> nl = nl;
			((WinInfoPtr) selWinInfo) -> nr = nr;
			((WinInfoPtr) selWinInfo) -> nb = nb;
			((WinInfoPtr) selWinInfo) -> nt = nt;

			if (logging && !GetVar((ObjPtr) selWinInfo, SUPERWINDOW) &&
				!GetPredicate((ObjPtr) selWinInfo, INHIBITLOGGING))
			{
			    char cmd[400];
			    sprintf(cmd, "locate %d %d %d %d\n",
				nl, nr, nb, nt);
			    Log(cmd);
			}
			ReshapeWindow((ObjPtr) selWinInfo);
		    }
		    /*The window has been moved.  Both buffers are bad*/
		    ImInvalid((ObjPtr) selWinInfo);

		    /*Now, see if any of the subwindows need moving*/
		    subWindows = GetVar((ObjPtr) selWinInfo, SUBWINDOWS);
		    if (subWindows)
		    {
			long sox, soy, ssx, ssy;
			long l, r, b, t;
			ObjPtr var;
			real margin[4];

			ThingListPtr runner;
			runner = LISTOF(subWindows);
			while (runner)
			{
			    SelWindow((WinInfoPtr) runner -> thing);

			    /*Get the window position within superwindow*/
			    getorigin(&sox, &soy);
			    getsize(&ssx, &ssy);
			    var = GetVar(runner -> thing, SUBWINDOWMARGIN);
			    if (var)
			    {
				Array2CArray(margin, var);
			    }
			    else
			    {
				margin[0] = 0.0;
				margin[1] = 0.0;
				margin[2] = 0.0;
				margin[3] = 0.0;
			    }

			    /*Adjust*/
			    l = nl + (int) margin[0];
			    r = nr - (int) margin[1];
			    b = nb + (int) margin[2];
			    t = nt - (int) margin[3];

			    if (l != sox || b != soy ||
				r != sox + ssx - 1 || t != soy + ssy - 1)
			    {
				winposition(l, r, b, t);
				ImInvalid(runner -> thing);
			    }

			    runner = runner -> next;
			}
		    }
		}
		break;
	    case ET_DRAW_WINDOW:
		if (IsValidWindow(events[curEvent] . data . window))
		{
		    SelWindow(events[curEvent] . data . window);

		    reshapeviewport();
		    if (selWinInfo)
		    {
			/*Both buffers are bad*/
			ImInvalid((ObjPtr) selWinInfo);
		    }
		}
		break;
	    case ET_CLOSE_WINDOW:
		if (IsValidWindow(events[curEvent] . data . window))
		{
		    CloseWindow(events[curEvent] . data . window);
		}
		break;
	    case ET_QUIT:
		MaybeQuit();
		break;
	}
    }
    else
#endif
    {
	/*If there's an object to drop, drop it.*/
	if (dropObject)
	{
	    WinInfoPtr dropWindow;

	    dropWindow = WhichWindow(dropX, dropY);

	    if (dropWindow)
	    {
		int left, right, bottom, top;
		int ox, oy;

		SelWindow(dropWindow);
		GetWindowBounds(&left, &right, &bottom, &top);
		GetWindowOrigin(&ox, &oy);
		dropX -= ox;
		dropY -= oy;

		if (dropX >= left && dropX <= right &&
		    dropY >= bottom && dropY <= top)
		{
		    FuncTyp method;
		    /*Drop*/
		    method = GetMethod((ObjPtr) selWinInfo, DROPOBJECTS);
		    if (method)
		    {
			if (logging)
			{
			    char cmd[256];
			    sprintf(cmd, "drop %d %d\n", dropX, dropY);
			    Log(cmd);
			    InhibitLogging(true);
			}
			
			(*method)(selWinInfo, dropObject, dropX, dropY);
			if (logging)
			{
			    InhibitLogging(false);
			}
		    }
		}
	    }
	    DeleteThing(dropObject);
	    dropObject = NULLOBJ;
	}

#ifdef CHANGECURSOR
#ifdef INTERACTIVE
#ifdef GRAPHICS
	{
	    WinInfoPtr cursorWindow;
	    FuncTyp method;
	    int x, y;

	    x = getvaluator(MOUSEX);
	    y = getvaluator(MOUSEY);
	    cursorWindow = WhichWindow(x, y);
	    if (cursorWindow != lastCursorWindow)
	    {
		/*Enter and leave messages*/
		if (lastCursorWindow)
		{
		    method = GetMethod((ObjPtr) lastCursorWindow, LEAVECURSOR);
		    if (method)
		    {
			(*method)(lastCursorWindow);
		    }
		}
		if (cursorWindow)
		{
		    method = GetMethod((ObjPtr) cursorWindow, ENTERCURSOR);
		    if (method)
		    {
			SelWindow(cursorWindow);
			(*method)(cursorWindow);
		    }
		}
		lastCursorWindow = cursorWindow;
	    }
	    if (cursorWindow)
	    {
		method = GetMethod((ObjPtr) cursorWindow, IDLECURSOR);
		if (method)
		{
		    SelWindow(cursorWindow);
		    (*method)((ObjPtr) cursorWindow, x, y);
		}
	    }
	}
#endif
#endif
#endif

	/*Idle the windows if appropriate*/
	if (settingUp && runningScript)
	{
	    readFromScript = true;
	}
	else
	{
	    readFromScript = IdleAllWindows() ? false : true;
	}

        if (readFromScript)
	{
	    if (runningScript)
	    {
		ReadScriptLine();
	    }
	    else
	    {
#ifdef SOCKETS
		/*Idle all connections*/
		IdleAllConnections();
#endif
	    }
	}
	IdleTimers();

#ifdef RELEASE
	TrashDay();
#else
	if (GetPrefTruth(PREF_RECKLESSGC))
	{
	    struct tms buffer;
	    long curTime;
	    curTime = times(&buffer);
	    if (curTime - lastGC > GCEVERY)
	    {
		TrashDay();
		lastGC = curTime;
	    }
	}
	else
	{
	    TrashDay();
	}
#endif
    }
}

void MainLoop()
/*Main loop for scian*/
{
    SetSystemClock(0.0);
    while (running)
    {
	EndLongOperation();
	OneEvent(true);
    }
    EndScript();
}

void InitEvents()
{
}

void KillEvents()
{
}
