/*
 * Name:	MG 2a Amiga terminal window I/O, with all kinds o' trimmings.
 * This module is 'way too big. Last Edit:	01-Dec-87
 * mic@emx.cc.utexas.edu Created:	21-Apr-86 mic@emx.cc.utexas.edu
 */

#undef	MANX
#undef	LATTICE
#include "rexx.h"
#include "no_dir.h"
#include "compiler.h"
#include "change_font.h"
#include "v11.h"
#include "mouse.h"
#include "no_metakey.h"
#include "promptwait.h"
#include "nrow.h"
#include "ncol.h"
#include "init_cols.h"
#include "init_rows.h"
#include "do_menu.h"
#ifndef	NO_METAKEY
#include "metabit.h"
#endif
#include "use_arp.h"
#ifdef	USE_ARP
#include <arpfunctions.h>
#endif

/*
 * Lots of includes.
 */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <devices/console.h>
#include <devices/inputevent.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <graphics/clip.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <graphics/layers.h>
#include <graphics/text.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>

#undef TRUE
#undef FALSE
#include "def.h"		/* includes sysdef.h and ttydef.h	 */
#include "window.h"

#ifdef	REXX
#include "key.h"
#endif

#ifdef	CHANGE_FONT
#include <libraries/diskfont.h>
#endif

#ifdef LATTICE
#ifdef	CHANGE_FONT
#include <proto/diskfont.h>
#endif
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/console.h>
#else
#include <functions.h>
#endif

#ifdef	ANSI
#include <string.h>
#include <stdlib.h>
#endif

#ifdef USE_ARP
struct ArpBase *ArpBase;
#endif
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

#ifndef	NO_DIR
extern struct Task *FindTask();
static BPTR     StartLock;
char            WindowTitle[MAXPATH + 20], *MyDirName;
#ifndef MANX
extern BPTR     DupLock(), CurrentDir();
#endif
#endif				/* NO_DIR */
#ifdef	DO_MENU
extern struct Menu *InitEmacsMenu();
#endif

#ifndef	LATTICE
extern int      Enable_Abort;	/* Do NOT allow abort!		 */
#endif

/*
 * External MG functions and variables
 */
extern char     version[];	/* Version information		 */
extern int      ttrow;		/* Current cursor row		 */
extern int      use_metakey;	/* Do meta characters?		 */

/*
 * My own, internal functions
 */
static VOID amigaclean PROTO((void));
static void setmaxima PROTO((void));
static struct Screen *wbscreen PROTO((void));
static void ttreopen PROTO((int resize));
static int handle_kbd PROTO((int timeout));
static int dispatch PROTO((struct IntuiMessage * msg));
static int nextkey PROTO((void));
static void qkey PROTO((short k));
#ifdef	MOUSE
static void qmouse PROTO((short x, short y, unsigned short qual));
#endif
#ifdef	DO_MENU
static void qmenu PROTO((unsigned short code));
#endif

#ifdef MANX
/* these are in localproto.h, but this is not included for MANX3.6 */
VOID	ttclose();
VOID	setttysize();
VOID	syscleanup();
VOID	ttflush();
VOID	panic();
#endif
/*
 * Library bases (used by glue libraries)
 */

#ifndef	LATTICE
#ifdef	CHANGE_FONT
struct DiskFontBase * DiskfontBase;
#endif
#endif

/*
 * Intuition window and menu variables.  MG gets used a lot, because it gets
 * reconfigured on the fly for the amiga-set-font and toggle-border
 * operations.
 */

#define WINDOWGADGETS (WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE)
#define WINDOWFLAGS	(WINDOWGADGETS | ACTIVATE)

struct NewWindow MG = {
	0, 0,			/* start position       	 */
	0, 0,			/* width, height (set by ttopen) */
	0, 1,			/* detail pen, block pen	 */
#ifdef	DO_MENU
	MENUPICK |		/* If menu is used		 */
#endif
#ifdef	MOUSE
	MOUSEBUTTONS |		/* If mouse is used		 */
#endif
	INTUITICKS | RAWKEY |
	CLOSEWINDOW | NEWSIZE,	/* IDCMP flags			 */
	0,			/* window flags	(set by ttopen)	 */
	NULL,			/* pointer to first user gadget */
	NULL,			/* pointer to user checkmark	 */
	NULL,			/* title (filled in later)	 */
	NULL,			/* pointer to screen (none)	 */
	NULL,			/* pointer to superbitmap	 */
	220, 40,		/* minimum size	(small!)	 */
	0, 0,			/* maximum size (set by ttopen)	 */
	WBENCHSCREEN		/* screen in which to open	 */
};

static short    borderless = TRUE;	/* Flag for borderless window	 */
static short    toggle_zooms = TRUE;	/* Does toggling border zoom?	 */
static int      last_top, last_left, last_height, last_width;

struct Window  *EmW = NULL;	/* Our window			 */
struct Screen  *EmS = NULL;	/* Our screen (usually WB)	 */
short           toggling = FALSE;	/* Prevent menu wiping		 */
#ifndef	V11
struct Screen   WBInfo;		/* Info about the WB screen	 */
#endif
struct TextFont *EmFont = NULL;	/* Our font (usually TOPAZ_xx)	 */

#ifdef	DO_MENU
static struct Menu *EmacsMenu = NULL;	/* Our menu		 */
#endif

#if	0
static ULONG    class;		/* Intuition event	 */
static USHORT   code,		/* information	 */
                qualifier;
static APTR     address;
static SHORT    x, y;
#endif	/* 0 */
static LONG     intuitionMsgMask;/* Signal Mask		 */

/* * * * * * * * * * * * * console I/O * * * * * * * * * * * * * * * * */

#define	CSI	0x9b		/* Command Sequence Introducer	 */
#define	NOBUF	512		/* About 1/4 screen		 */
#define	NIBUF	256		/* Input buffer			 */

static KCHAR    ibuf[NIBUF];	/* keyboard input buffer	 */
static int      ibufo, nibuf;	/* head, # of bytes in ibuf	 */

static LONG     tickcount;	/* # intuiticks	since last char	 */

static struct MsgPort *conWritePort = NULL;	/* I/O ports 		 */
static struct IOStdReq *conWriteMsg = NULL;	/* I/O messages		 */
struct Library *ConsoleDevice;	/* used by RawKeyConvert */
static unsigned char outbuf[NOBUF + 7];	/* output buffer	 */
static unsigned char *obuf;	/* first output char	 */
int             nobuf;		/* # of bytes in above	 */
int             nrow;		/* Terminal size, rows.	 */
int             ncol;		/* Terminal size, cols.	 */

/* * * * * * * * * functions to open/reopen the window * * * * * * * * * */

/*
 * Open up the virtual terminal MG communicates with. Set up the window,
 * console, and menu strip.
 */

ttopen()
{

#ifndef	LATTICE
	Enable_Abort = 0;	/* Disable ^C	 */
#endif

	/* Set the window size, set the flags and title, and open it */

	setmaxima();
	MG.Flags = WINDOWFLAGS;
	MG.Flags |= borderless ? BORDERLESS : WINDOWSIZING;
#ifndef	NO_DIR
	MG.Title = (UBYTE *) WindowTitle;
#else
	MG.Title = (UBYTE *) version;
#endif

	if ((EmW = OpenWindow(&MG)) == NULL)
		amigaclean();
	SetFont(EmW->RPort, EmFont);

	/*
	 * Once the window is created, get the Intuition signal bit, set up
	 * the menu, and tell the virtual terminal how big it is.
	 */
	setttysize();
	intuitionMsgMask = 1L << EmW->UserPort->mp_SigBit;
#ifdef	DO_MENU
	if (toggling == FALSE)
		EmacsMenu = InitEmacsMenu(EmW);
	if (EmacsMenu == NULL)
		amigaclean();
	SetMenuStrip(EmW, EmacsMenu);
#endif

	/*
	 * Attach a console device (purely for output now) to our window
	 */

	if ((conWritePort = CreatePort("Emacs.con.write", 0L)) == NULL)
		amigaclean();
	if ((conWriteMsg = CreateStdIO(conWritePort)) == NULL)
		amigaclean();

	if (OpenConsole(conWriteMsg, NULL, EmW) != 0)
		amigaclean();

	ConsoleDevice = (struct Library *) conWriteMsg->io_Device;
	nibuf = ibufo = 0;

	return (0);
}

/*
 * Set up the initial state of the window.  Opens up libraries, decides how
 * big the initial window should be, and whether it should be borderless.
 */

VOID
sysinit()
{
#ifndef	NO_DIR
	long            len;
	BPTR            MyDirLock;
#endif

	GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L);
	if (GfxBase == NULL)	/* Graphics lib	 */
		amigaclean();

	IntuitionBase = (struct IntuitionBase *)	/* Intuition	 */
		OpenLibrary("intuition.library", 0L);
	if (IntuitionBase == NULL)
		amigaclean();

#ifdef USE_ARP
	if (!(ArpBase = (struct ArpBase *) OpenLibrary("arp.library", 0L)))
		panic("Compiled with USE_ARP, but arp.library not found");
#endif
#ifdef	REXX
	openrexx();
#endif
#ifndef NO_DIR
	/*
	 * The following attempt to be clever assigns the external StartLock
	 * to the lock on the current directory, then switches our CurrentDir
	 * to a duplicate of that lock so we can restore the original lock on
	 * exit.
	 */

	strcpy(WindowTitle, version);
	strcat(WindowTitle, ": ");
	MyDirName = strchr(WindowTitle, '\0');
	StartLock = ((struct Process *) FindTask(0L))->pr_CurrentDir;
	(void) CurrentDir(MyDirLock = DupLock(StartLock));
	len = PathName(MyDirLock, MyDirName, MAXPATH);
#endif				/* NO_DIR */

#ifdef	CHANGE_FONT
#ifndef	LATTICE
	DiskfontBase = (struct DiskFontBase *) OpenLibrary("diskfont.library", 0L);
#else
	DiskfontBase = OpenLibrary("diskfont.library", 0L);
#endif
	if (DiskfontBase == NULL)
		amigaclean();
#endif

	/* Get our screen and font, then figure out if we can go borderless */
	if ((EmS = wbscreen()) == NULL)
		amigaclean();
#ifdef	CHANGE_FONT
	EmFont = OpenDiskFont(EmS->Font);
#else
	EmFont = OpenFont(EmS->Font);
#endif
	if ((EmS->Width >= ((INIT_COLS * EmFont->tf_XSize) + LR_BORDER)) &&
	    (EmS->Height >= ((INIT_ROWS * EmFont->tf_YSize) + TB_BORDER)))
		borderless = FALSE;

	/*
	 * Set the size of the initial window and fake the last one
	 */
	last_width = MG.Width = EmS->Width;
	last_height = MG.Height = EmS->Height;
	last_left = MG.LeftEdge = 0;
	last_top = MG.TopEdge = 0;

	strcpy(outbuf, "\x9b" "0 p");	/* preload cursor off sequence */
	obuf = outbuf + 4;
}

/*
 * Make sure the window isn't bigger than NROW * NCOL, while accounting for
 * borders & such.  Since the window might not be at its largest right now,
 * deadstop both the current width and the maxwidth.
 */

static VOID 
setmaxima()
{
	register int    maxw, maxh;

	MG.MaxWidth = EmS->Width;
	MG.MaxHeight = EmS->Height;
	maxw = NCOL * EmFont->tf_XSize + (borderless ? 0 : LR_BORDER);
	maxh = NROW * EmFont->tf_YSize + (borderless ? TOP_OFFSET : TB_BORDER);

	if (MG.MaxWidth > maxw)
		MG.MaxWidth = maxw;
	if (MG.Width > maxw)
		MG.Width = maxw;

	if (MG.MaxHeight > maxh)
		MG.MaxHeight = maxh;
	if (MG.Height > maxh)
		MG.Height = maxh;
}


/*
 * Return a pointer the workbench screen, using GetScreenData() to do things
 * like a good citizen.  Left the V11 code in as a reminder that what works
 * is not always the _best_ way to do things. Thanks to Tom Rokicki for
 * reminding me (mpk) this had to be done.
 */

static struct Screen
               *
wbscreen()
{
#ifndef	V11
	return GetScreenData((char *) &WBInfo, (long) sizeof(WBInfo),
		   WBENCHSCREEN, NULL) ? &WBInfo : ((struct Screen *) NULL);
#else
	register struct Screen *s;
	Forbid();
	for (s = IntuitionBase->FirstScreen; s; s = s->NextScreen)
		if ((s->Flags & SCREENTYPE) == WBENCHSCREEN)
			break;
	Permit();
	return (s);
#endif
}

/*
 * Hide the window and open it up again.  If resize is TRUE, they're being
 * called as part of a resize operation, so assume that the NewWindow
 * structure is set correctly.  Otherwise, store the current window size and
 * position in the NewWindow structure.
 * 
 * These two functions are split so we can do things like ttreopen() and
 * tticon() cleanly.
 */

VOID
tthide(resize)
	int             resize;
{
	toggling = TRUE;
	if (resize == FALSE) {	/* if we're resizing,	 */
		MG.LeftEdge = EmW->LeftEdge;	/* use current window size	 */
		MG.TopEdge = EmW->TopEdge;
		MG.Width = EmW->Width;
		MG.Height = EmW->Height;
	}
	ttclose();		/* reset to zero	 */
}

VOID
ttshow(resize)
	int             resize;
{
	ttopen();		/* re-open tty window	 */
	ttinit();		/* re-initalize tty	 */
	sgarbf = TRUE;		/* screen was trashed	 */
	if (resize == TRUE)
		nrow = ncol = -1;	/* trash screen size	 */
	refresh(FFARG, 1);	/* and redraw it	 */
	toggling = FALSE;	/* Ok, done		 */
}

/*
 * ttreopen() was split into the two functions above when tticon() was
 * introduced.
 */

static          VOID
ttreopen(resize)
	int             resize;
{
	tthide(resize);
	ttshow(resize);
}

/* * * * * * * * * * * * functions to close the window * * * * * * * * */

/*
 * Close the virtual terminal.  If toggling, don't release all the other
 * resources we've allocated.
 */
VOID
ttclose()
{
	ttflush();
	if (conWriteMsg!=NULL) {
		CloseDevice((struct IORequest *) conWriteMsg);
		DeleteStdIO(conWriteMsg);
		conWriteMsg = NULL;
	}
	if (conWritePort!=NULL) {
		DeletePort(conWritePort);
		conWritePort = NULL;
	}
	if (EmW!=NULL) {
#ifdef	DO_MENU
		ClearMenuStrip(EmW);
#endif
		CloseWindow(EmW);
		EmW=NULL;
	}
#ifndef	LATTICE
	Enable_Abort = 1;
#endif
}

/*
 * To explain the many paths out of mg:
 * 
 * ttclose: Shuts down the window & associated IO devices. This gets called to
 * go to a "quiescent" state - iconified, or waiting for rexx.
 * 
 * syscleanup: Closes everything, in preperation for shutting down permanently.
 * 
 * amigaclean: Calls the above two routines in order, then exits quietly Used
 * when we're not up yet, and something fails to open.
 * 
 * panic: Displays a message in the echo area, then does an amigaclean. Used to
 * die in emergencies from an up state.
 */

/*
 * Shut 'er down scotty, she's sucking mud! Usefull even when we're not all
 * the way up yet.
 */
VOID
amigaclean()
{
	ttclose();
	syscleanup();
	exit(20);
}

/*
 * System dependent cleanup for the Amiga.
 */
VOID
syscleanup()
{
#ifdef	REXX
	closerexx();
#endif
#ifndef	NO_DIR
	UnLock(CurrentDir(StartLock));	/* restore startup directory	 */
#endif

	if (conWriteMsg)
		DeleteStdIO(conWriteMsg);
	if (conWritePort)
		DeletePort(conWritePort);
#ifdef	DO_MENU
	if (EmacsMenu)
		DisposeMenus();
#endif
	if (EmFont)
		CloseFont(EmFont);
#ifdef	CHANGE_FONT
	if (DiskfontBase)
		CloseLibrary((struct Library *) DiskfontBase);
#endif
	if (IntuitionBase)
		CloseLibrary((struct Library *) IntuitionBase);
	if (GfxBase)
		CloseLibrary((struct Library *) GfxBase);
#ifdef USE_ARP
	if (ArpBase)
		CloseLibrary((struct Library *) ArpBase);
#endif

}

/* * * * * * * * functions that diddle the window and reopen it * * * * * */

/*
 * Toggle between a borderless window and a sizeable window. This lets you
 * use the whole screen if you want. Bound to "amiga-toggle-border".
 */

togglewindow(f, n)
{

	if ((borderless = !borderless) == TRUE) {	/* *always* save last	 */
		last_top = EmW->TopEdge;	/* bordered window size	 */
		last_left = EmW->LeftEdge;
		last_width = EmW->Width;
		last_height = EmW->Height;
	}
	if (toggle_zooms == FALSE) {	/* just use current size */
		ttreopen(FALSE);
		return (TRUE);
	}
	/*
	 * zooming -- if borderless, go as big as possible.  If bordered, set
	 * to last saved value of bordered window
	 */
	if (borderless) {
		MG.LeftEdge = 0;
		MG.TopEdge = 0;
		MG.Width = MG.MaxWidth;
		MG.Height = MG.MaxHeight;
	} else {
		MG.LeftEdge = last_left;
		MG.TopEdge = last_top;
		MG.Width = last_width;
		MG.Height = last_height;
	}
	ttreopen(TRUE);		/* open with new size	 */
	return (TRUE);
}

/*
 * Modify the action of "amiga-toggle-border", reporting outcome to user.
 * Bound to "amiga-zoom-mode".
 */
togglezooms(f, n)
{
	toggle_zooms = !toggle_zooms;
	ewprintf("Toggling border %s",
		 toggle_zooms ? "expands window to screen size" :
		 "retains current window size");
	return (TRUE);
}

#ifdef	CHANGE_FONT
/*
 * Select a different font for the MG window. This does not work very well
 * with proportional fonts, so we ask the user to confirm before he uses one.
 * It's available if you want to be able to use your own disk font (or Topaz
 * 11 under 1.2) to edit with.
 */

setfont(f, n)
{
	register int    s, size;
	register struct TextFont *newfont;
	char            fontname[80], fontpath[84], fontsize[3];
	struct TextAttr ta;

	/*
	 * If negative size, reset to default font
	 */
	if ((f & FFARG) && (n <= 0)) {
		CloseFont(EmFont);	/* return old font  */
		EmFont = OpenDiskFont(EmS->Font);	/* screen's default */
		ttreopen(TRUE);	/* we changed fonts, so resize	    */
		ewprintf("Now using default font");
		return (TRUE);
	}
	if ((s = ereply("Font name: ", fontname, sizeof(fontname))) != TRUE)
		return (s);
	strcpy(fontpath, fontname);
	strncat(fontpath, ".font", sizeof(fontpath));	/* make name */

	/* Get font size */
	if (f & FFARG)
		size = n;
	else {
		if ((s = ereply("Font size: ",
				fontsize, sizeof(fontsize))) != TRUE)
			return (s);
		size = atoi(fontsize);
	}

	/* Set up text attributes */
	ta.ta_Name = (UBYTE *) fontpath;
	ta.ta_YSize = size;
	ta.ta_Style = FS_NORMAL;
	ta.ta_Flags = 0;

	/* Look for the font */
	ewprintf("Looking for %s %d...", fontname, size);
	if ((newfont = OpenDiskFont(&ta)) == NULL) {
		ewprintf("Can't find %s %d!", fontname, size);
		return (FALSE);
	}
	/* Found it! Check before using it */
	if ((newfont->tf_YSize != size) &&
	    ((s = eyesno("Size unavailable - use closest")) != TRUE)) {
		CloseFont(newfont);
		return (FALSE);
	}
	if ((newfont->tf_Flags & FPF_PROPORTIONAL) &&
	    (((s = eyesno("Use proportional font"))) != TRUE)) {
		CloseFont(newfont);
		return (FALSE);
	}
	/* Get rid of old font and reopen with the new one */
	CloseFont(EmFont);
	EmFont = newfont;
	ttreopen(TRUE);	/* Different font sizes means we may need to resize */
	ewprintf("Now using font %s %d", fontname, EmFont->tf_YSize);
	return (TRUE);
}
#endif

/* * * * * * * * * * * * * console output functions  * * * * * * * * * * * * */

/*
 * Write a single character to the screen. Buffered for speed, so ttflush()
 * does all the work.
 */
VOID
#ifdef NO_PROTO
ttputc(c)
	unsigned char	c;
#else
ttputc PROTO((unsigned char c))
#endif
{
	obuf[nobuf++] = c;
	if (nobuf >= NOBUF)
		ttflush();
}

/*
 * Flush characters from the output buffer.  If the # of characters is
 * greater than a certain ad-hoc value, turn the cursor off while doing the
 * write. To avoid extra writes, the output buffer has been preloaded with
 * the cursor-off sequence.  Outbuf is large enough to hold the extra 7
 * characters.
 */
#define	MIN_OFF	8
VOID
ttflush()
{
	if (nobuf > 0) {
		if (nobuf <= MIN_OFF)	/* don't turn off for short writes */
			ConWrite(conWriteMsg, obuf, nobuf);
		else {
			obuf[nobuf++] = '\x9b';
			obuf[nobuf++] = ' ';
			obuf[nobuf++] = 'p';
			ConWrite(conWriteMsg, outbuf, nobuf + 4);
		}
		nobuf = 0;
	}
}

/*
 * The caller intends to output an escape sequence, but only flush the buffer
 * if there's not enough room to hold the complete sequence. This avoids
 * breaking up escape sequences when we turn the cursor off in ttflush(), at
 * the expense of some extra function calls.
 */
VOID 
ttnflush(n)
	int             n;
{
	if ((nobuf + n) > NOBUF)
		ttflush();
}

/* * * * * * * * * * * * * console input functions  * * * * * * * * * * * * */

/*
 * Read a character (really a KCHAR, > 8 bits), blocking till a character is
 * put in the input buffer and can be returned.
 */
ttgetc()
{
	return handle_kbd(FALSE);
}

/*
 * Return TRUE if we've waited for 2 seconds and nothing has happened, else
 * return false.
 */

ttwait()
{
	return handle_kbd(TRUE);/* time out after 2 sec */
}

/*
 * Common routine for handling character input, with and without timeout.
 * Handle events until:
 * 
 * 1) a character is put in the input buffer 2) if timeout == TRUE, PROMPTWAIT
 * IntuiTicks have gone by
 * 
 * If timeout == FALSE, the input character is returned and removed from the
 * input buffer.
 * 
 * If timeout == TRUE, returns TRUE if the read timed out, else FALSE. Leaves
 * any character typed in the input buffer.
 */

static 
handle_kbd(timeout)
	register int    timeout;
{
	register struct IntuiMessage *message;	/* IDCMP message 	 */
	register LONG   wakeupmask;	/* which signals?	 */
	register int    charfound;	/* got a character yet?	 */
#ifdef	REXX
	struct key      savekey;/* Save current keystrokes */
	extern struct MsgPort *rexxport;	/* Bit to check for Rexx msgs */
	register int    rexxbit = 1l << rexxport->mp_SigBit;
#endif

	tickcount = 0;		/* *always* zero the count */
	if (nibuf)		/* any chars? return if so */
		return timeout ? FALSE : nextkey();

	charfound = FALSE;	/* nope -- have to wait	 */
	do {
		wakeupmask = Wait(intuitionMsgMask
#ifdef	REXX
				  | rexxbit
#endif
			);

#ifdef		REXX
		/* Now handle any rexx messages if we need them */
		if (wakeupmask & rexxbit) {
			savekey = key;
			disprexx(rexxport);
			update();
			key = savekey;
		}
#endif

		/* Handle Intuiticks specially for speed */
		while (message = (struct IntuiMessage *) GetMsg(EmW->UserPort))
			if (message->Class == INTUITICKS) {
				tickcount++;
				ReplyMsg((struct Message *) message);
			} else if (dispatch(message) == TRUE)
				charfound = TRUE;


		/*
		 * time out if enough ticks have gone by without any keyboard
		 * input.  We do this *after* all the events in the current
		 * list have been dispatched.
		 */
		if (timeout && (tickcount > PROMPTWAIT))
			break;
	} while (charfound == FALSE);


	/*
	 * If called by ttwait(), return FALSE if a character was found. Else
	 * return the next character in the input buffer
	 */
	return timeout ? (!charfound) : nextkey();
}

/*
 * Handle the events we handle...  The result returned indicates if we've put
 * a character in the input buffer.
 */
#ifndef	NO_METAKEY
#define	IEQUALIFIER_ALT		(IEQUALIFIER_RALT | IEQUALIFIER_LALT)
#endif

static 
dispatch(msg)
	register struct IntuiMessage *msg;
{
#ifdef	DO_MENU
	register struct MenuItem *item;
#endif

	register int    txheight, txwidth;
	register struct RastPort *rp;
	int             dx, dy, fgpen, drmode;

	static struct InputEvent FakedEvent = {NULL, IECLASS_RAWKEY, 0, 0, 0};
#ifndef	NO_METAKEY
	unsigned char   altbuf[64];
	int             altlen;
#endif
	unsigned char   keybuf[64];
	int             keylen, i;
#ifndef	V11
	APTR            deadcodes;
#endif
	ULONG		class;
	USHORT		code,
			qualifier;
	APTR		address;
	SHORT		x, y;

	class = msg->Class;	/* grab the info before we 	 */
	code = msg->Code;	/* reply to the message		 */
	qualifier = msg->Qualifier;
	address = msg->IAddress;
	x = msg->MouseX;
	y = msg->MouseY;
#ifndef	V11
	if (class == RAWKEY)	/* get dead key info		 */
		deadcodes = (APTR) * address;
#endif
	ReplyMsg((struct Message *) msg);	/* return it to Intuition	 */

	switch (class) {	/* see what the fuss is about	 */
	case RAWKEY:
		FakedEvent.ie_Code = code;
		FakedEvent.ie_Qualifier = qualifier;
#ifndef	V11
		FakedEvent.ie_EventAddress = deadcodes;
#endif
		keylen = (int) RawKeyConvert(&FakedEvent,
				       keybuf, (LONG) sizeof(keybuf), NULL);

#ifndef NO_METAKEY
		/*
		 * Special mapping for ALT-ed keys.  The intent is to allow
		 * extended ASCII characters that are generated without the
		 * ALT key to pass unscathed through getkbd(). However, if
		 * METABIT is 0x80 & use_metaky is on, they will get changed
		 * by getkey(). Hence, Amiga mg should be compiled with
		 * METABIT being 0x80.
		 */
		if ((qualifier & IEQUALIFIER_ALT) && use_metakey) {
			FakedEvent.ie_Qualifier &= ~IEQUALIFIER_ALT;
			altlen = (int) RawKeyConvert(&FakedEvent, altbuf,
					       (LONG) sizeof(altbuf), NULL);
			if (altlen >= 1)
				qkey((KCHAR) (METABIT | altbuf[0]));
			for (i = 1; i < altlen; i++)
				qkey((KCHAR) altbuf[i]);
			return (altlen > 0) ? TRUE : FALSE;
		}
#endif
		/* non-ALTed key */
		for (i = 0; i < keylen; i++)
			qkey((KCHAR) keybuf[i]);
		return (keylen > 0) ? TRUE : FALSE;
		break;

#ifdef	DO_MENU
	case MENUPICK:
		if (code == MENUNULL)
			return (FALSE);
		while (code != MENUNULL) {	/* handle multiple selection	 */
			qmenu(code);
			item = ItemAddress(EmacsMenu, (LONG) code);
			code = item->NextSelect;
		}
		return (TRUE);	/* puts KMENU in event queue	 */
		break;
#endif

#ifdef	MOUSE
	case MOUSEBUTTONS:	/* fake the mouse key	 */
		if (code != SELECTDOWN)	/* ignore SELECTUP	 */
			return (FALSE);
		qmouse(x, y, qualifier);
		return (TRUE);
		break;
#endif

	case NEWSIZE:
		/*
		 * Sometimes when you resize the window to make it smaller,
		 * garbage is left at the right and bottom sides of the
		 * window. This code is devoted to (somehow) getting rid of
		 * this garbage.  Any suggestions?
		 */

		rp = EmW->RPort;
		fgpen = rp->FgPen;	/* save params		 */
		drmode = rp->DrawMode;
		SetDrMd(rp, (LONG) JAM1);
		SetAPen(rp, (LONG) EmW->RPort->BgPen);

		/*
		 * Check the bottom of the window
		 */
		txheight = EmW->Height - EmW->BorderTop - EmW->BorderBottom;
		if (dy = (txheight % FontHeight(EmW)))
			RectFill(rp,
				 (LONG) EmW->BorderLeft,
				 (LONG) EmW->BorderTop + txheight - dy - 1,
				 (LONG) (EmW->Width - 1) - EmW->BorderRight,
			      (LONG) (EmW->Height - 1) - EmW->BorderBottom);

		/*
		 * Check the right side
		 */
		txwidth = EmW->Width - EmW->BorderLeft - EmW->BorderRight;
		if (dx = txwidth % FontWidth(EmW))
			RectFill(rp,
				 (LONG) EmW->BorderLeft + txwidth - dx - 1,
				 (LONG) EmW->BorderTop,
				 (LONG) (EmW->Width - 1) - EmW->BorderRight,
			      (LONG) (EmW->Height - 1) - EmW->BorderBottom);

		SetDrMd(rp, (LONG) drmode);
		SetAPen(rp, (LONG) fgpen);	/* restore colors */

		/* Tell the console device to resize itself */
		ttputc(CSI);
		ttputc('t');
		ttputc(CSI);
		ttputc('u');
		ttflush();

		/*
		 * Signal the editor that a new size has occurred. I may
		 * break down and do this asynchronously...
		 */
		qkey(KRESIZE);
		return (TRUE);	/* we done (finally)	 */
		break;

	case CLOSEWINDOW:
		/* Calling quit() directly is not a guaranteed win. */
		quit(FFRAND, 1);
		return (FALSE);
		break;

	default:
		panic("HandleMsg: unknown event!!!");
		break;
	}
	return (FALSE);
}

/*
 * Return the current size of the virtual terminal in nrow and ncol, making
 * sure we don't go beyond the size of the internal video array. Assumes the
 * current font is monospaced.
 */
VOID
setttysize()
{
	nrow = (EmW->Height - TOP_OFFSET
		- EmW->BorderBottom) / FontHeight(EmW);
	ncol = (EmW->Width - EmW->BorderLeft
		- EmW->BorderRight) / FontWidth(EmW);
	if (nrow < 1)
		nrow = 1;
	if (nrow > NROW)
		nrow = NROW;
	if (ncol < 1)
		ncol = 1;
	if (ncol > NCOL)
		ncol = NCOL;
}

/*
 * Exit as soon as possible, after displaying the message.
 */
VOID
panic(s)
	char           *s;
{
	ewprintf(s);		/* put message at bottom	 */
	Delay((ULONG) 90);	/* wait 1.5 seconds		 */
	amigaclean();		/* Go Way */
}

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Event
 * buffer management		 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */

/*
 * Return next key in the input buffer, if any available.  Returns -1 if not.
 */
static int
nextkey()
{
	register KCHAR  k;

	if (nibuf <= 0) {	/* shouldn't happen, but could... */
		nibuf = 0;
		return -1;
	} else {
		k = ibuf[ibufo++];
		nibuf--;
		ibufo %= NIBUF;
		return (int) k;
	}
}

/*
 * Return true if there are some characters available in the input buffer.
 */
typeahead()
{
	return (nibuf > 0);
}

/*
 * Add a key to the input queue
 */
static          VOID
qkey(k)
	KCHAR           k;
{
	if (nibuf < NIBUF)
		ibuf[(ibufo + nibuf++) % NIBUF] = k;
}

#ifdef	MOUSE
/*
 * Add a mouse event to the input queue, calculating the row and column value
 * from the current height and width of the window's font.
 */

static          VOID
qmouse(x, y, qual)
	SHORT           x, y;
	USHORT          qual;
{
	register int    myqual = MQ_NOQUAL;
	register int    row, col;
	register struct window *wp;

	/* get row, column	 */
	col = (x - EmW->BorderLeft) / FontWidth(EmW);
	row = (y - TOP_OFFSET) / FontHeight(EmW);

	/* find out which kind of window was clicked in */
	for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
		if ((row >= wp->w_toprow) &&
		    (row <= (wp->w_toprow + wp->w_ntrows)))
			break;
	if (wp == NULL)
		myqual |= MQ_ECHO;
	else if (row == (wp->w_toprow + wp->w_ntrows))
		myqual |= MQ_MODE;
	else
		myqual |= MQ_WINDOW;

	/* figure out qualifiers	 */
	if (qual & IEQUALIFIER_CONTROL)
		myqual |= MQ_CTRL;
	if (qual & (IEQUALIFIER_LSHIFT | IEQUALIFIER_LSHIFT))
		myqual |= MQ_SHIFT;
	if (qual & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
		myqual |= MQ_ALT;

	/*
	 * Queue up the whole mess.  If user didn't click in the echo line,
	 * transmit the x, y values to the mouse function
	 */
	qkey((KCHAR) (KW___MOUSE + myqual));
	if (MQ_WHERE(myqual) != MQ_ECHO) {
		qkey((KCHAR) (M_X_ZERO + col));
		qkey((KCHAR) (M_Y_ZERO + row));
	}
}
#endif

#ifdef	DO_MENU
/*
 * Add a menu event to the queue.
 */
static          VOID
qmenu(code)
	USHORT          code;
{
	qkey(KMENU);		/* menu key sequence	 */
	qkey((KCHAR) (MENUNUM(code) + MN_OFFSET));
	qkey((KCHAR) (ITEMNUM(code) + MN_OFFSET));
	qkey((KCHAR) (SUBNUM(code) + MN_OFFSET));
}
#endif

void
sleep(n)
	int             n;
{
	if (n > 0)
		Delay((long) n * TICKS_PER_SECOND);
}

#ifdef MANX
/* Define some Manx specific functions */
/*
 * the only realloc function for MANX 3.6 is in heapmem.o which allocates
 * storage from a buffer that is fixed in size (read SUCKS ROCKS)!, so 
 * here is a function that is compatible with the documentation for realloc
 * that is in MANX 5.0.
 */
void *
realloc(blk, newlen)
char	*blk;
int	newlen;
{
	char	*nblk;

	if (newlen == 0) {
		free(blk);
		blk=(char *)0;
		return(blk);
	} else {
		nblk = malloc(newlen);
		if (blk != (char *)0) {
			movmem(blk, nblk, newlen);
			free(blk);
		}
		blk=nblk;
	}
	return(blk);
}

/*
 * Again, MANX doesn't seem to have real useful and regular functions
 * in the 3.6 release.  Here is a memcmp function for MANX3.6.
 */
int
m_memcmp(s1,s2,l)
char *s1, *s2;
int	l;
{
    while (l--) {
	if (*s1==*s2)
	    s1++,s2++;
	else {
	    if (*s1<*s2) 
		return -1;
	    else
		return 1;
	}
    }
    return(0);
}
#endif
