/* $Id: ckmco2.c,v 1.9 91/12/27 21:28:52 fdc Exp $
 * $Source: /uw/mackermit/RCS/ckmco2.c,v $
 *------------------------------------------------------------------
 * $Log:	ckmco2.c,v $
 * Revision 1.9  91/12/27  21:28:52  fdc
 * Change fatal to macfatal, make sure all lines width 80 or less.
 * 
 * Revision 1.8  91/12/15  23:16:45  rick
 * ut9
 * 
 * Revision 1.7  91/10/13  13:43:05  rick
 * UT(7)
 * 
 * Revision 1.6  91/10/01  12:16:12  rick
 * UT(5)
 * 
 * Revision 1.5  91/09/25  12:16:26  rick
 * Command window in TE. Multiple vt100 windows for command window.
 * 
 * Revision 1.4  91/09/12  21:50:31  rick
 * UT(3). Install on watsun
 * 
 * Revision 1.3  1991/09/12  16:42:51  rick
 * Cleanups.
 *
 * Revision 1.2  1991/09/10  22:21:37  rick
 * Update to UTexas(2)
 *
 * Revision 1.1  1991/09/10  19:17:45  rick
 * Initial revision
 *
 *------------------------------------------------------------------
 * $Endlog$
 */

/*
 * FILE ckmco2.c
 *
 * Module of mackermit: contains code for dealing with the Mac side
 * of terminal emulation.
 */

/*
  Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
  York.  Permission is granted to any individual or institution to use this
  software as long as it is not sold for profit.  This copyright notice must be
  retained.  This software may not be included in commercial products without
  written permission of Columbia University.
*/
#include "ckcdeb.h"
#include "ckcasc.h"
#include "ckmdef.h"
#include "ckmasm.h"		/* Assembler code */
#include "ckmres.h"		/* kermit resources */
#include "ckmcon.h"		/* defines, etc. for terminal emulator */
#include "ckmptp.h"		/* ckm* Prototypes */

extern int escape;

#define ABS(a)	((a) < 0 ? -(a) : (a))

char **myclip_h;		/* internal clipboard */
int myclip_size;		/* size of above */
int my_scrapcount;		/* the value of PScrapStuff->scrapCount when we cut */

extern Boolean usingRAMdriver,
	       have_128roms;	/* true if we are a Plus or better */

RgnHandle dummyRgn;		/* dummy region for ScrollRect */
				/* Initialized in mac_init */

long MyCaretTime;		/* (UoR) ticks between flashes */

extern Cursor *textcurs, *normcurs, *watchcurs;	/* mouse cursor shapes */

extern	int		to_printer;		/*JAO*/
extern	int		to_screen;		/*JAO*/
extern	int		printer_is_on_line_num;	/*JAO*/
extern	Handle	hPrintBuffer;			/*JAO*/
extern	long	lPrintBufferSize;		/*JAO*/
extern	long	lPrintBufferChars;		/*JAO*/
extern	long	lPrintBufferAt;			/*JAO*/

extern	DialogPtr	bufferingDialog;	/*JAO*/
extern	DialogPtr	overflowingDialog;	/*JAO*/

extern	MenuHandle menus[];	/* handle on our menus */  /*JAO*/

/* keyboard handling stuff */

extern char keytable[512];	/* the key redefintion flag table */
extern modrec modtable[NUMOFMODS];	/* modifier records */

#define myKeyCodeMask	0x7F00
#define keyModifierMask	0x1F00
#define ctrlCodeMask	0x1F
#define metaOrBits	0x80

#define UnmodMask	0x80		/* action bits */
#define CapsMask	0x40
#define CtrlMask	0x20
#define MetaMask	0x10


Boolean have_scriptmgr = FALSE;

long old_KCHR, old_SICN;	/* pointers to current system key script, icon */
long cur_KCHR;
Cursor *lastCursor=0L;			/* what we set the cursor to last */


/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/* keyboard event handling routines                                         */
/****************************************************************************/


InitKeyStuff()
{
    have_scriptmgr = NGetTrapAddress(num_UnknownTrap, 1) !=
		     NGetTrapAddress(num_ScriptTrap, 1);
		     
    if (have_scriptmgr) {
	old_KCHR = GetScript( smRoman, smScriptKeys);
	old_SICN = GetScript( smRoman, smScriptIcon);
    }
    cur_KCHR = old_KCHR;

    UpdateOptKey(1);	/* get things set right initially */
}

UpdateOptKey(int enable)
{
    int i;
    int futzit = 0;
    
    if (enable) {
	for (i = 0; i < NUMOFMODS; i++) {
	/* shift what to look for into high byte */
	    if ((modtable[i].modbits) & (optionKey >> 4))	/* if Option is selected */
	        futzit = 1;
	}
    } else {	/* allways turn off when disabling window */
        futzit = 0;
    }
    
    (void) FutzOptKey(futzit);
}

FutzOptKey(int enable)
{
    int err;
    
    if (have_scriptmgr) {		/* if we are system 4.1 or later... */
	if (enable) {	/* no deadkeys */
	    if (cur_KCHR != old_KCHR)
		return (1);	/* we are allready fine */
	    if (GetEnvirons(smKeyScript) == smRoman) {  /* only if in roman script */
		/* set the key map */
		err = SetScript (smRoman, smScriptKeys, NODEAD_KCHR);
		if (err != noErr) {
		    printerr ("Trouble setting custom keymap (KCHR):", err);
		    return (0);
		}
		/* set the icon */
		err = SetScript (smRoman, smScriptIcon, NODEAD_SICN);
		if (err != noErr) {
		    printerr ("Trouble setting custom keymap icon (SICN):", err);
		    return (0);
		}
		KeyScript (smRoman);
		cur_KCHR = NODEAD_KCHR;
		return (1);	/* success! */
	    } else {
	        printerr("Can't disable Option key -- you have a non-US keyboard",0);
	        return (0);
	    }
	} else {	/* back to normal */
	    if (cur_KCHR == old_KCHR)
		return (1);	/* we are allready fine */
	    /* set the key map */
	    err = SetScript (smRoman, smScriptKeys, old_KCHR);
	    if (err != noErr) {
		printerr ("Trouble resetting default keymap (KCHR):", err);
		return (0);
	    }
	    /* set the icon */
	    err = SetScript (smRoman, smScriptIcon, old_SICN);
	    if (err != noErr) {
		printerr ("Trouble resetting default keymap icon (SICN):", err);
		return (0);
	    }
	    KeyScript (smRoman);
	    cur_KCHR = old_KCHR;
	    return (1);		/* success! */
	}
    } else {
	/* do something or other to do the old way */
	/* printerr("Kermit can't disable Option on old systems",0); */
    }
    return (0);
}


/****************************************************************************/
/* return the ASCII character which is generated by the keyCode specified */
/* with no modifiers pressed */
/****************************************************************************/
unsigned char DeModifyChar (long keyCode, long modifiers)
{
    long c;
    long mystate;
    short s_keycode;
    Handle kchr_h;
    THz curZone;

#ifdef COMMENT
    ProcHandle KeyTrans;

    if (keyCode > 64)
	KeyTrans = (ProcHandle) 0x2A2;	/* keypad decode */
    else
	KeyTrans = (ProcHandle) 0x29E;	/* keyboard decode */

    SaveRegs ();		/* save all registers */
    AllRegs ();

    /* setup regs for procedure call */
    /* loadD1 ((long) modifiers); */		/* no modifiers */
    loadD1 ((long) 0);		/* no modifiers */
    loadD2 ((long) keyCode);	/* set the keycode */
    loadA0 (*KeyTrans);		/* load the content of Key1Trans to A0 */

    /* run the translation routine */
    execute ();			/* call the Key1Trans procedure */

    /* move the result from reg D0 to c */
    loadA0 (&c);		/* set destination address */
    pushD0 ();			/* move register D0 to stack */
    poptoA0 ();			/* load the stacktop to (A0) */

    RestoreRegs ();		/* restore all registers */
    AllRegs ();

#endif /* COMMENT */

    if (have_scriptmgr) {		/* if we are system 4.1 or later... */
        mystate = 0;
	
	kchr_h = GetResource('KCHR', cur_KCHR);
	if (kchr_h == NIL) {
	    printerr("DeModifyChar: couldn't get KCHR address",0);
	    return(0);
	}
	LoadResource(kchr_h);
	HLock(kchr_h);
	
	s_keycode = (modifiers & 0xff00) | (keyCode & 0xff);

	c = KeyTrans(*kchr_h, s_keycode, &mystate);
	HUnlock(kchr_h);
	curZone = GetZone();		/* as per John Norstad's (Disinfectant) */
	SetZone(HandleZone(kchr_h));	/* "Toolbox Gotchas" */
	ReleaseResource(kchr_h);
	SetZone(curZone);
    }    
    return (c);
}				/* DeModifyChar */



unsigned char obuf[2] = {1, 0};	/* single char output buffer */

/****************************************************************************/
/* send a character to the line if it is in ASCII range. Do local echo if */
/* necessary */
/****************************************************************************/
OutputChar (struct termw *termw, unsigned char c)
{

    /*
     * PWP: NO 7 bit masking!!!  If we do this, then I can't use Emacs, and
     * the European users will be VERY unhappy, 'cause they won't be able to
     * send all of their characters.
     */

    obuf[1] = c;		/* store character */
    writeps (obuf);		/* and write it out */

    if (duplex != 0) {
	cursor_erase (termw);	/* remove from screen */
	printem (termw, &obuf[1], 1);	/* Echo the char to the screen */
	flushbuf(termw);		/* flush the character */
	cursor_draw(termw);		/* put it back */
    }
}				/* OutputChar */

#ifdef COMMENT
/****************************************************************************/
/* Bittest returns the setting of an element in a Pascal PACKED ARRAY [0..n]
   OF Boolean such as the KeyMap argument returned by GetKey
/****************************************************************************/
Boolean
bittest (bitmap, bitnum)
char bitmap[];
int bitnum;
{
    return (0x01 & (bitmap[bitnum / 8] >> (bitnum % 8)));
}				/* bittest */

/* PWP: or, as a macro, */
#define bittest(bitmap,bitnum)	(0x01 & (bitmap[bitnum / 8] >> (bitnum % 8)))

#endif /* COMMENT */


/****************************************************************************/
/* Process a character received from the keyboard */
/****************************************************************************/
handle_char (struct termw *termw, EventRecord *evt)
{
    short i;
    short len;
    short theCode;
    short modCode;
    short theModBits;
    char flags;
    char tmpstr[256];
    unsigned char c;

    /* (UoR) check for auto repeated keys */
    if ((termw->autorepeat == FALSE) && (evt->what == autoKey))
	return;

    ObscureCursor ();		/* PWP: hide the cursor until next move */

    modCode = evt->modifiers & keyModifierMask;
    theCode = ((evt->message & myKeyCodeMask) >> 8) + (modCode >> 1);
    
    /* check for a special code for this key */
    if (BitTst (keytable, theCode)) {
	GetMacro (theCode, &flags, tmpstr);	/* get the macrostring */

	if (flags) {		/* check special flags */
	    switch (flags) {
	      case shortBreak:
		sendbreak (5);
		return;

	      case longBreak:
		sendbreak (70);
		return;

	      case leftArrowKey:
		do_arrow (termw, leftARROW);
		return;

	      case rightArrowKey:
		do_arrow (termw, rightARROW);
		return;

	      case upArrowKey:
		do_arrow (termw, UPARROW);
		return;

	      case downArrowKey:
		do_arrow (termw, DOWNARROW);
		return;
		
	      case keycomma:
	      case keyminus:
	      case keyperiod:
	      /* there is no keyslash */
	      case key0:
	      case key1:
	      case key2:
	      case key3:
	      case key4:
	      case key5:
	      case key6:
	      case key7:
	      case key8:
	      case key9:
		do_keypad(termw, flags - keycomma);
		return;
		
	      case keypf1:
	      case keypf2:
	      case keypf3:
	      case keypf4:
		do_pfkey(termw, flags - keypf1);
		return;
		
	      case keyenter:
		do_keyenter(termw);
		return;		
	    }
	}
	/* send key macro string */

	/*
	 * PWP: note, we DON'T have to convert it to a Pascal string, 'cause
	 * the macros are now stored as Pascal strings
	 */
	writeps (tmpstr);	/* send it to the line */
	if (duplex != 0)
	    printps(termw,tmpstr);	/* echo it locally */
	return;
    }
    for (i = 0; i < NUMOFMODS; i++) {
	/* shift what to look for into high byte */
	theModBits = modtable[i].modbits << 4;
	len = strlen (modtable[i].prefix);

	if ((theModBits || len) &&
	    ((theModBits & modCode) == (theModBits & keyModifierMask))) {
	    /* send prefix if there is one */
	    if (len) {
		/* PWP: these are saved as Pascal strings now */
		BlockMove (modtable[i].prefix, tmpstr, (modtable[i].prefix[0] + 1));
		writeps (tmpstr);	/* send it to the line */
		if (duplex != 0)
		    printps(termw,tmpstr);	/* echo it locally */
	    }

	    /*
	     * get the unmodified ASCII code if the unmodify action bit is
	     * active
	     */
	    if (theModBits & UnmodMask)
		c = DeModifyChar ((long) ((evt->message & myKeyCodeMask) >> 8),
				  (long) (modCode & shiftKey));
			 /* PWP: we pass through the shiftedness of this key */
	    else
		c = evt->message & charCodeMask;	/* otherwise get the
							 * standard char */

	    /* make an uppercase character if the caps action bit is active */
	    if ((theModBits & CapsMask) && islower (c))
		c = toupper (c);

	    /* make a control character if the control action bit is active */
	    if (theModBits & CtrlMask)
		c &= ctrlCodeMask;

	    /* PWP: for Meta characters (yes, I use Emacs) */
	    if (theModBits & MetaMask)
		c |= metaOrBits;

	    if (!checkescape(c))
		OutputChar (termw, c);
	    return;
	}			/* if */
    }				/* for */

    /* get the ASCII code and send it */
    c = evt->message & charCodeMask;
    if (!checkescape(c))
	OutputChar(termw, c);
}				/* handle_char */


/*
 * checkescape
 * Handle escape sequences in connected mode
 * Return TRUE if character used.
 */
#define NKBUF 10
checkescape (char c)
{
    int x;
    static int esclevel = 0;
    static char *kbp, kbuf[NKBUF];
    extern int cmdmsk;

    switch (esclevel) {
    case 0:				/* no escapes pending */
	if ((c & 0x7f) == escape) {
	    esclevel++;
	    return TRUE;		/* we ate the char */
	}
	return FALSE;			/* we did not process char */

    case 1:				/* process escape command */
	if (doesc(c)) {			/* if CMDQ */
	    esclevel++;
	    kbp = kbuf;			/* preset buffer */
	    *kbp++ = CMDQ;
	}
	else
	    esclevel = 0;
	return TRUE;			/* we ate the char */

    case 2:				/* quoted backslash sequence */
	c &= cmdmsk;
	*kbp = c;
	if ((c != '\r') && (c != '\n')) {
	    if (&kbuf[NKBUF-1] != kbp)	/* if buffer not full */
		kbp++;
	    return TRUE;
	}
	*kbp = 0;
	esclevel = 0;

	kbp = kbuf;
	x = xxesc(&kbp);
	if (x >= 0) {
	    c = dopar(x);
	    ttoc(c);
	} else {		/* Invalid backslash code. */
	    putchar(BEL);
	}
	return TRUE;
    }
}


/*
 * console_char
 * Handle keyboard events while not connected
 * Return TRUE if we inserted a character into the console buffer.
 *
 * THIS ROUTINE LIKELY NEEDS MORE WORK HANDLING SPECIAL CHARACTERS.
 * 
 */
console_char (struct termw *termw, EventRecord *evt)
{
    short i;
    short len;
    short theCode;
    short modCode;
    short theModBits;
    char flags;
    char tmpstr[256];
    unsigned char c;

    /* (UoR) check for auto repeated keys */
    if ((termw->autorepeat == FALSE) && (evt->what == autoKey))
	return;

    ObscureCursor ();		/* PWP: hide the cursor until next move */

    modCode = evt->modifiers & keyModifierMask;
    theCode = ((evt->message & myKeyCodeMask) >> 8) + (modCode >> 1);
    
    /* check for a special code for this key */
    if (BitTst (keytable, theCode)) {
	GetMacro (theCode, &flags, tmpstr); /* get the macrostring */

	if (flags) {			/* check special flags */
	    switch (flags) {
	    case shortBreak:
/*		sendbreak (5); */
		return FALSE;

	    case longBreak:
/*		sendbreak (70); */
		return FALSE;

	    case leftArrowKey:
/* 		do_arrow (leftARROW); */
		return FALSE;

	    case rightArrowKey:
/* 		do_arrow (rightARROW); */
		return FALSE;

	    case upArrowKey:
/* 		do_arrow (UPARROW); */
		return FALSE;

	    case downArrowKey:
/* 		do_arrow (DOWNARROW); */
		return FALSE;
		
	    case keycomma:
	    case keyminus:
	    case keyperiod:
		/* there is no keyslash */
	    case key0:
	    case key1:
	    case key2:
	    case key3:
	    case key4:
	    case key5:
	    case key6:
	    case key7:
	    case key8:
	    case key9:
/*		do_keypad(flags - keycomma); */
		return FALSE;
		
	    case keypf1:
	    case keypf2:
	    case keypf3:
	    case keypf4:
/*		do_pfkey(flags - keypf1); */
		return FALSE;
		
	    case keyenter:
/*		do_keyenter(); */
		return FALSE;		
	    }
	}
	/* send key macro string */
	writecb(tmpstr);
	return TRUE;
    }

    for (i = 0; i < NUMOFMODS; i++) {
	/* shift what to look for into high byte */
	theModBits = modtable[i].modbits << 4;
	len = strlen (modtable[i].prefix);

	if ((theModBits || len) &&
	    ((theModBits & modCode) == (theModBits & keyModifierMask))) {
	    /* send prefix if there is one */
	    if (len) {
		/* PWP: these are saved as Pascal strings now */
		BlockMove (modtable[i].prefix, tmpstr, 
			   (modtable[i].prefix[0] + 1));
		writecb(tmpstr);	/* put in console buffer */
		if (duplex != 0)
		    printps(termw,tmpstr);	/* echo it locally */
	    }

	    /*
	     * get the unmodified ASCII code if the unmodify action bit is
	     * active
	     */
	    if (theModBits & UnmodMask)
		c = DeModifyChar ((long) ((evt->message & myKeyCodeMask) >> 8),
				  (long) (modCode & shiftKey));
	    /* PWP: we pass through the shiftedness of this key */
	    else
		c = evt->message & charCodeMask; /* otherwise get the
						  * standard char */

	    /* make an uppercase character if the caps action bit is active */
	    if ((theModBits & CapsMask) && islower (c))
		c = toupper (c);

	    /* make a control character if the control action bit is active */
	    if (theModBits & CtrlMask)
		c &= ctrlCodeMask;

	    /* PWP: for Meta characters (yes, I use Emacs) */
	    if (theModBits & MetaMask)
		c |= metaOrBits;

	    writecbc(c);
	    return TRUE;
	}				/* if */
    }					/* for */

    /* get the ASCII code and send it */
    writecbc(evt->message & charCodeMask);
    return TRUE;
}


/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/* general rectangle routines                                               */
/****************************************************************************/



/****************************************************************************/
/*
 * Routine makerect
 *
 * Make a rectangle in r starting on line lin and column col extending
 * numlin lines and numcol characters.
 *
 */
/****************************************************************************/
makerect(struct termw *termw,Rect *r,int lin,int col,int numlin,int numcol) {
    r->top = lin * termw->lineheight + TOPMARGIN;
    r->left = col * termw->charwidth + LEFTMARGIN;
    r->bottom = r->top + numlin * termw->lineheight;
    r->right = r->left + numcol * termw->charwidth;
}				/* makerect */

/* (PWP) do what makerect does, then invert the rect */
invertchars(struct termw *termw, int lin, int col, int numlin, int numcol)
{
    Rect r;
    r.top = lin * termw->lineheight + TOPMARGIN;
    r.left = col * termw->charwidth + LEFTMARGIN;
    r.bottom = r.top + numlin * termw->lineheight;
    r.right = r.left + numcol * termw->charwidth;
    InvertRect (&r);
}

/****************************************************************************/
/* Connect support routines */
/****************************************************************************/
void term_new_font (struct termw *termw)
{
    FontInfo fi;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    SetFontLock(false);
    termw->font_is_locked = false;
    
    TextFont(termw->current_font);	/* make sure to set this font */
    TextSize(termw->current_size);
    
    GetFontInfo (&fi);
    
    termw->lineheight = fi.ascent + fi.descent + fi.leading;
    termw->chardescent = fi.descent;
    /* termw->charwidth = fi.widMax; */
    termw->charwidth = CharWidth('W');		/* idea from NCSA telnet 2.3 */

    SetPort (savePort);		/* there just has to be a better way */
}
    
/* consetup is called once at startup */
struct termw *consetup (int boxid)
{
    struct termw *termw;
    GrafPtr savePort;
    extern struct termw *termwl;
    
    termw = (struct termw *)malloc(sizeof(struct termw));
    if (!termw)
	macfatal("consetup: no memory for terminal window", 0);
    bzero((char *)termw, sizeof(struct termw));
    termw->next = termwl;		/* add to list */
    termwl = termw;
    termw->current_font = VT100FONT;
    termw->current_size = 9;
    termw->oldlin = -1;
    termw->from_lin = -1;
    termw->screensize = INIT_SCREENSIZE; /* number of lines on screen */
    termw->graphicsinset[0] = ASCII_SET;
    termw->graphicsinset[1] = ASCII_SET;
    termw->graphicsinset[2] = LAT1_SET;
    termw->graphicsinset[3] = LAT1_SET;
    termw->Gr_set = 1;			/* (PWP) current chosen RH set */
    termw->old_Gl_set = -1;		/* set to come back to after */
					/* single shift GL */
    termw->autowrap = TRUE;		/* Autowrap on by default */
    termw->autorepeat = TRUE;		/* (UoR) auto repeat flag */
    termw->dispcontchar = TRUE;		/* do not show control characters */
    termw->blockcursor = TRUE;		/* show block or underline cursor */
    termw->cursor_shown = TRUE;		/* (PWP) show the cursor */
    termw->blinkcursor = TRUE;		/* true if we make the cursor blink */
    termw->charflg = CF_OUTC;		/* state variable */

    setWindowLoc(boxid);
#ifdef notdef
    termw->window = GetNewWindow (boxid, (Ptr) &termw->terminalWRec, 
				 (WindowPtr) -1L);
#else
    termw->window = GetNewWindow (boxid, (Ptr) 0L, (WindowPtr) -1L);
#endif

    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    PenMode (srcCopy);		/* (PWP) was patXor */
    flushio ();			/* Get rid of pending characters */

    termw->current_font = VT100FONT;	/* (UoR) Set initial font to VT100 */
    termw->current_size = 9;
    term_new_font(termw);
    
    if (termw->screeninvert)
	TextMode (srcBic);
    else
	TextMode (srcOr);
    TextFace (0);		/* PWP: be safe.  We allways stay like this */

    init_term(termw);		/* Set up some terminal variables */

    /* normal char mode, home cursor, clear screen, and save position */
    norm_home_clear_save(termw);

    cursor_draw(termw);		/* (UoR) be sure to draw it */

    SetPort (savePort);		/* there just has to be a better way */
	
	return termw;
}				/* consetup */

void term_reset (struct termw *termw)
{
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    PenMode (srcCopy);		/* (PWP) was patXor */
    flushio ();			/* Get rid of pending characters */
    screen_to_bottom(termw);	/* slide the visible region to active area */

    termw->graphicsinset[0] = ASCII_SET;
    termw->graphicsinset[1] = GRAF_SET;
    termw->Gl_set = 0;
    termw->Gr_set = 1;
    termw->textstyle = 0;
    termw->current_style = 0;
    termw->draw_sing_chars = 0;
    termw->font_is_locked = FALSE;
    termw->screeninvert = FALSE;	/* (UoR) inverted screen flag */
    termw->insert = FALSE;
    termw->newline = FALSE;		/* (UoR) linefeed mode by default */
    termw->autowrap = TRUE;		/* Autowrap on by default */
    termw->relorigin = FALSE;		/* (UoR) relative origin off */
    termw->autorepeat = TRUE;		/* (UoR) auto repeat flag */
    termw->appl_mode = FALSE;		/* (PWP) keypad application mode */
    termw->curskey_mode = FALSE;	/* (PWP) cursor key application mode */
    termw->smoothscroll = FALSE;	/* do smooth scrolling (PWP: or not) */
    termw->scroll_amount = 0;		/* no pending scroll */
    termw->refresh_amount = 0;		/* no pending refresh */
    termw->dispcontchar = TRUE;	/* do not show control characters */
    termw->blockcursor = TRUE;		/* show block or underline cursor */
    termw->cursor_shown = TRUE;	/* (PWP) show the cursor */
    termw->mouse_arrows = FALSE;	/* mouse down in screen does arrow keys */
    termw->visible_bell = FALSE;	/* true if we do blink instead of bell */
    termw->eightbit_disp = FALSE;	/* default to 7 bits */
    termw->blinkcursor = TRUE;		/* true if we make the cursor blink */


    termw->have_selection = FALSE;	/* (PWP) we have no selected text */
    
    termw->saved_tlin = 0;
    termw->saved_blin = 0;
    
    termw->current_font = VT100FONT;	/* (UoR) Set initial font to VT100 */
    termw->current_size = 9;
    term_new_font(termw);
#ifdef COMMENT
    TextMode (srcXor);		/* (UoR) use XOR mode (for inverse) */
#endif
    if (termw->screeninvert)
	TextMode (srcBic);
    else
	TextMode (srcOr);
    TextFace (0);		/* PWP: be safe.  We allways stay like this */
    
    /* normal char mode, home cursor, clear screen, and save position */
    norm_home_clear_save(termw);
    
    cursor_draw(termw);		/* (UoR) be sure to draw it */

    SetPort (savePort);		/* there just has to be a better way */
}				/* consetup */

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


/*************************************************/
/* cursor drawing stuff                          */
/*************************************************/

Boolean cursor_rect (struct termw *termw, int line, int col, Rect *r)
{
    /* if cursor not on screen */
    if (line - termw->display_topline >= termw->screensize)
    	return FALSE;
	
    if (col >= 80)			/* make it look like a VT100 */
	col = 79;
    
    /* Get character rectangle */
    makerect(termw,r, line - termw->display_topline, col, 1, 1);
    if (!termw->blockcursor) {
	r->top = r->bottom;
	r->bottom = r->top + 2;
    }

    return TRUE;
}					/* cursor_rect */

void cursor_draw (struct termw *termw)
{
    Rect r;
    GrafPtr savePort;

    if (!termw->cursor_shown) 		/* (PWP) not if we are hiding cursor */
	return;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    if (!termw->cursor_invert) {
	if (cursor_rect(termw,termw->curlin, termw->curcol, &r)) {
	    if (termw->in_front) {
		InvertRect (&r);
	    } else {
		PenMode (patXor);
		FrameRect (&r);
		PenMode (patCopy);
	    }
	}
    }

    /* replace mouse cursor */
    if ((termw->oldlin >= 0) && (!termw->mousecurs_drawn)) {
	makerect(termw ,&r, termw->oldlin, termw->oldcol, 1, 1);
	PenMode (patXor);
	FrameRect (&r);
	PenMode (patCopy);
	termw->mousecurs_drawn = TRUE;
    }
    
    termw->cursor_invert = TRUE;
    termw->cur_drawn = TRUE;

    SetPort (savePort);		/* there just has to be a better way */
}				/* cursor_draw */


void cursor_erase (struct termw *termw)
{
    Rect r;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    if (termw->cursor_invert) {
	if (cursor_rect(termw, termw->curlin, termw->curcol, &r)) {
	    if (termw->in_front) {
		InvertRect (&r);
	    } else {
		PenMode (patXor);
		FrameRect (&r);
		PenMode (patCopy);
	    }
	}
    }
    /* remove mouse cursor */    
    if ((termw->oldlin >= 0) && (termw->mousecurs_drawn)) {
	makerect(termw ,&r, termw->oldlin, termw->oldcol, 1, 1);
	PenMode (patXor);
	FrameRect (&r);
	PenMode (patCopy);
 	termw->mousecurs_drawn = FALSE;
   }

    termw->cursor_invert = FALSE;
    termw->cur_drawn = FALSE;

    SetPort (savePort);		/* there just has to be a better way */
}				/* cursor_erase */

void flash_cursor (struct termw *termw)
{
    register long tc;
    Rect r;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    tc = TickCount ();
    if (((tc - termw->last_flash) > MyCaretTime) ||
	(tc - termw->last_flash) < 0L) {
	termw->last_flash = tc;

	if (termw->cur_drawn) {
	    if (cursor_rect(termw,termw->curlin, termw->curcol, &r)) {
		/* PWP: only blink if asked for */
		if (termw->blinkcursor && termw->in_front) {
		    InvertRect (&r);
		    termw->cursor_invert = !termw->cursor_invert;
		} else if (!termw->cursor_invert) {	/* make sure that the cursor
						 * shows up */
		    if (termw->in_front) {
			InvertRect (&r);
		    } else {
			PenMode (patXor);
			FrameRect (&r);
			PenMode (patCopy);
		    }
		    termw->cursor_invert = TRUE;
		}
	    }
	}
    }

    SetPort (savePort);		/* there just has to be a better way */
}				/* flash_cursor */


/****************************************************************************/
/* PWP -- like waitasec(), but don't get any characters.  Used for 
   visable bell. */
/****************************************************************************/
waitnoinput ()
{
    long ticks = 2, end_time;

    Delay (ticks, &end_time);	/* pause for 1/30th second */
}				/* waitnoinput */


/****************************************************************************/
/* (UoR) get any characters, and pause for a while */
/****************************************************************************/
waitasec (struct termw *termw)
{
    waitnoinput();
    inpchars(termw);
}				/* waitasec */


/****************************************************************************/
/* updateCursor -- taken from NCSA Telnet for the Macintosh, v 2.2   */
/****************************************************************************/
void updateCursor (struct termw *termw, int force, WindowPeek myfrontwindow)
{
    static Point lastPoint;
    static int optwasdown = 0;
    Cursor *thisCursor;
    int optDown;
    KeyMap allthekeys;	/* Someplace to put the keymap */
    char *cp_allthekeys = (char *) &allthekeys;	/* $$$ HACK HACK */
    int newlin, newcol;
    Point MousePt;
    Rect r;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    GetMouse(&MousePt);

    GetKeys(allthekeys);
    optDown = cp_allthekeys[7] & 4;	/* should be symbolic */

    if ( (!force) && (!in_background) && (MousePt == lastPoint) && 
	(optDown == optwasdown)) {
	SetPort (savePort);
	return;
    }
    
    if (force)
	lastCursor=0L;
	
    if (in_background) {
	lastCursor = 0L;		/* allways force if in background */
	thisCursor = normcurs;		/* default cursor shape */
    } else if (protocmd != 0) {		/* if doing a transfer */
	thisCursor = watchcurs;		/* in forground and doing a transfer */
    } else if (((myfrontwindow == termw->window) ||
		((myfrontwindow == NIL) &&
		 (FrontWindow() == termw->window))) &&
	       PtInRect(MousePt, &termw->ScreenRect)) {
	if (termw->mouse_arrows || optDown) {
	    newlin = (MousePt.v - TOPMARGIN) / termw->lineheight;
	    newcol = (MousePt.h - LEFTMARGIN + termw->charwidth/2) / 
		termw->charwidth;
	
	    if ((termw->oldlin != newlin) || (termw->oldcol != newcol)) {
		PenMode (patXor);	/* For FrameRect calls */
		if (termw->oldlin >= 0) { /* if old rectangle */
		    if (termw->mousecurs_drawn) {
			makerect(termw,&r, termw->oldlin, termw->oldcol, 1, 1);
			FrameRect (&r);
		    }
		} else {	       /* else if drawing for the first time */
		    HideCursor ();
		}
		
		makerect(termw,&r, newlin, newcol, 1, 1);
		FrameRect (&r);
		PenMode (patCopy);	/* reset to normal pen mode */
	
		termw->oldlin = newlin;
		termw->oldcol = newcol;
		termw->mousecurs_drawn = TRUE;
	    }
	    lastPoint=MousePt;
	    optwasdown=optDown;
	    SetPort (savePort);		/* there just has to be a better way */
	    return;
	} else {			
	    thisCursor = textcurs;
	}
    } else {
	thisCursor  = normcurs;		/* default cursor shape */
    }
	
    if  (lastCursor!= thisCursor) {
	SetCursor(thisCursor);
	lastCursor = thisCursor;
    }

    lastPoint=MousePt;
    optwasdown=optDown;

    if (termw->oldlin >= 0) {	     /* if we hade drawn a movement outline */
	if (termw->mousecurs_drawn) {
	    PenMode (patXor);	/* For FrameRect calls */
	    makerect(termw,&r, termw->oldlin, termw->oldcol, 1, 1);
	    FrameRect (&r);
	}
	
	termw->oldlin = -1;
 	termw->mousecurs_drawn = FALSE;
	ShowCursor ();
	PenMode (patCopy);	/* reset to normal pen mode */
    }

    SetPort (savePort);		/* there just has to be a better way */
}


/****************************************************************************/
/* Put characters onto the actual screen                                    */
/****************************************************************************/

static int to_mac_style[] = {
	normal,	underline, italic, underline|italic,
	bold|condense, bold|condense|underline,
	bold|condense|italic, bold|condense|underline|italic
};

/****************************************************************************/
/* flushbuf(termw) -- draw all the buffered characters on the screen */
/****************************************************************************/
void flushbuf (struct termw *termw)
{
    register int i, scrl_amt;
    Rect r;

    if (termw->out_maxcol == 0)
	return;			/* Nothing to flush */

    if (to_printer) {					/*JAO*/
	for (i = termw->out_mincol; i < termw->out_maxcol; i++) {
	    (*hPrintBuffer)[lPrintBufferAt++] = 
		termw->scr[termw->curlin+termw->display_topline][i];
	    if (lPrintBufferAt == lPrintBufferSize)
		lPrintBufferAt = 0L;
	    lPrintBufferChars++;
	    if (lPrintBufferChars == 1L)
		updatepstat();
	    if (lPrintBufferChars == lPrintBufferSize) {
		overflowingDialog = GetNewDialog(OVERFLOWINGBOXID, NILPTR, 
						 (WindowPtr) - 1);
		DrawDialog(overflowingDialog);
		break;					/* PWP */
	    }
	}
    }

    if (!to_screen) {
	termw->out_maxcol = 0;		/* Say no more chars to output */
	return;				/*JAO*/
    }
    
    /* save because flushscroll() resets scroll_amount */
    scrl_amt = termw->scroll_amount;
    if (scrl_amt) {
	/*
	 * If we have pending scrolling, and we are about to draw outside 
	 * of the region that will be refreshed when the scrolling happens, 
	 * do the scrolling now.
	 */
    	if ((termw->refresh_amount < 0) && 
	    ((termw->curlin < termw->saved_blin + termw->refresh_amount)
	     || (termw->curlin > termw->saved_blin)))
	{
	    flushscroll(termw);
	}
	else if ((termw->refresh_amount > 0) && 
		 ((termw->curlin < termw->saved_tlin)
		  || (termw->curlin > termw->saved_tlin+termw->refresh_amount)))
	{
	    flushscroll(termw);
	}
	else
	{
	    termw->out_maxcol = 0;	/* Say no more chars to output */
	    return;
	}
    }
    
    if (termw->have_selection)
	maybe_nuke_selection (termw, termw->curlin, termw->curlin);
    
    /*
     * PWP: Why have two routines to do the same thing?  I centralized
     * all the font crap into draw_w_line_attrs so I didn't need
     * to keep track of code here too.  Side benifit -- we can nuke
     * the outbuf array.
     */
    draw_w_line_attrs(termw, termw->curlin + termw->display_topline, 
		      termw->curlin, termw->out_mincol, termw->out_maxcol, 
		      (scrl_amt == 0));
    termw->out_maxcol = 0;		/* Say no more chars to output */
}				/* flushbuf */

/****************************************************************************/
/* set_style(termw, style)
 - set the correct stuff for displaying chars in style   */
/****************************************************************************/
void set_style (struct termw *termw, int style)
{
    int m_sty, m_font;
    static int o_sty = 0, o_font = 0;
    
    if (style == termw->current_style) 
	return;
    termw->current_style = style;
    
    m_sty = to_mac_style[style & STY_MSTY];
    m_font = ((style & STY_FONT) >> 3) + termw->current_font;
    termw->draw_sing_chars = ((style & VT_BLINK)
		       || (CharWidth('W')!=CharWidth('i'))
		       || (!RealFont(termw->current_font, termw->current_size)));
/* printerr("draw_sing_chars == ", draw_sing_chars); */

    if (!have_128roms && (m_sty & bold)) {	/* if on an old mac and bolding */
	if (m_font == VT100FONT) {
	    m_font = VT100BOLD;
	    m_sty &= ~(bold|condense);
	} else {
	    termw->draw_sing_chars = 1;
	}
    }

    if (m_font != o_font) {
	TextFont (m_font);		/* new font */
	o_font = m_font;
    }
    if (m_sty != o_sty) {
	TextFace (m_sty);		/* new text face */
	o_sty = m_sty;
    }
}

/****************************************************************************/
/****************************************************************************/
void draw_w_line_attrs (struct termw *termw, register int lin, register int v,
		  register int l_col, register int r_col,
		  int must_drawblanks)
{
    register int o, i, sty, j;
    register char *cp, *ap;
    register int max;
    Rect r;
    
    if (termw->scroll_amount)
	DebugStr("\pscroll_amount");

    /***** I shouldn't have to put this here! *****/
    if (termw->screeninvert) {
	BackPat(qd.black);
	PenPat(qd.white);
	TextMode (srcBic);
    } else {
	BackPat(qd.white);
	PenPat(qd.black);
	TextMode (srcOr);
    }

    if ((v < 0) || (v > termw->screensize))
	printerr("draw_w_line_attrs, v out of range:", v);

    cp = termw->scr[lin];
    ap = termw->scr_attrs[lin];
    
    /*
     * find the last character that is not a plain space character
     */
    sty = 0;	/* the style for normal blank space */
    for (max = r_col-1; max >= l_col; max--) {
			/* PWP: we should never see a NUL in the line here */
	if ((cp[max] != ' ') || (ap[max] != sty))
	    break;
    }
    max++;
    if (max > r_col) max = r_col;

    if (must_drawblanks && max < r_col) {
	makerect(termw,&r, v, max, 1, r_col-max);
	EraseRect (&r);		/* set r to bkPat */
    }


    /*
     * loop through all the characters up to max, looking for the longest
     * string of characters all of the same style.  Draw them, switch styles,
     * and repeat until max.
     */
    sty = ap[l_col];
    o = l_col;
    i = l_col;
    do {
    	if ((ap[i] != sty) || (i == max)) {	/* if this style != current style */
	    if (must_drawblanks) {
		makerect(termw,&r, v, o, 1, i-o);
		EraseRect (&r);
	    }
	    
	    if (sty != termw->current_style)
		set_style(termw, sty);
		
	    if (termw->draw_sing_chars) {
		for (j = o; j < i; j++) {
		    MOVETOCHAR(j, v);
		    DrawChar((short) (cp[j] & 0377));
		}
	    } else {	/* non-blinking */
		MOVETOCHAR(o, v);
		DrawText (cp, (short) o, (short) i-o);	/* Output this part */
	    }
	    
	    if (sty & VT_INVERT) {
		makerect(termw,&r, v, o, 1, i-o);
		InvertRect (&r);
	    }
	    
	    if (!termw->font_is_locked) {
		SetFontLock(true);
		termw->font_is_locked = true;
	    }
	    
	    o = i;	/* now left extent == current */
	    sty = ap[i];	/* new current style */
	}
	i++;
    } while (i <= max);
}

/****************************************************************************/
/****************************************************************************/
void scroll_term (struct termw *termw)
{
    register int new_topline, delta, lin, i;
    int fl, fc, tl, tc;
    Rect r;			/* cannot be register */

    new_topline = termw->screensize - termw->display_totlines + 
	GetCtlValue (termw->t_vscroll);
    if ((new_topline > 0) ||
	(new_topline <  termw->screensize - MAX_SCREENSIZE)) {
    	printerr("BUG: in scroll_term(), new_topline out of range:",
		 new_topline);
	return;
    }
    if ((delta = (termw->display_topline - new_topline)) == 0)
	return;		/* we didn't move */

    makerect(termw,&r, 0, 0, termw->screensize, MAXCOL);
    
    /* if whole screen */
    if ((delta >= termw->screensize) || (-delta >= termw->screensize)) {
    	EraseRect(&r);
	
    	lin = new_topline;			/* new top line */
	for (i = 0; i < termw->screensize; i++) {
	    draw_w_line_attrs(termw, lin, i, 0, MAXCOL, 0);
	    lin++;
	}
	termw->display_topline = new_topline;

	if (termw->have_selection)
	    invert_text(termw,
			termw->from_lin,
			termw->from_col,
			termw->to_lin,
			termw->to_col);
	    
	return;	/* we are done */
    }

    /* if we get here, we are not doing the whole screen */
    ScrollRect (&r, 0, delta * termw->lineheight, dummyRgn);

    if (delta > 0) {	/* scrolling down (pushing top arrow) */
    	lin = new_topline;			/* new top line */
	for (i = 0; (i < delta) && (i < termw->screensize); i++) {
	    draw_w_line_attrs(termw, lin, i, 0, MAXCOL, 0);
	    lin++;
	}
	termw->display_topline = new_topline;

	if (termw->have_selection &&
	    (termw->from_lin < termw->display_topline + delta) &&
	    (termw->to_lin >= termw->display_topline)) {
	    if (termw->from_lin < termw->display_topline) {
		fl = termw->display_topline;
		fc = 0;
	    } else {
		fl = termw->from_lin;
		fc = termw->from_col;
	    }
	    if (termw->to_lin >= termw->display_topline + delta) {
		tl = termw->display_topline + delta - 1;
		tc = MAXCOL;
	    } else {
		tl = termw->to_lin;
		tc = termw->to_col;
	    }
	    invert_text(termw, fl, fc, tl, tc);
	}
    } else {		
	/* scrolling up (pushing bottom arrow) */
	/* one past old bottom line*/
    	lin = termw->display_topline + termw->screensize;
	/*********** PWP: delta is negative here ****************/
	i = termw->screensize + delta;
	if (i < 0) i = 0;	/* bounds */
	while (i < termw->screensize)
	    draw_w_line_attrs(termw, lin++, i++, 0, MAXCOL, 0);
	termw->display_topline = new_topline;

	if (termw->have_selection &&
	    (termw->from_lin <
	     (termw->display_topline + termw->screensize)) &&
	    (termw->to_lin >=
	     (termw->display_topline + termw->screensize + delta))) {
	    if (termw->from_lin <
		termw->display_topline + termw->screensize + delta) {
		fl = termw->display_topline + termw->screensize + delta;
		fc = 0;
	    } else {
		fl = termw->from_lin;
		fc = termw->from_col;
	    }
	    if (termw->to_lin >= termw->display_topline + termw->screensize) {
		tl = termw->display_topline + termw->screensize - 1;
		tc = MAXCOL;
	    } else {
		tl = termw->to_lin;
		tc = termw->to_col;
	    }
	    invert_text(termw, fl, fc, tl, tc);
	}
    }
}

static pascal void
doscroll (WHICHCONTROL, THECODE)
ControlHandle WHICHCONTROL;
short THECODE;
{
    register int amount = 0, val, max;
	struct termw *termw;
	
	if (FrontWindow() == ctermw->window)
		termw = ctermw	;
	else if (FrontWindow() == ttermw->window)
		termw = ttermw	;
	else
		return;

    if (THECODE == inUpButton)
	amount = -1;
    if (THECODE == inDownButton)
	amount = 1;
    if (amount == 0)
	return;
    val = GetCtlValue (WHICHCONTROL) + amount;
    max = GetCtlMax (WHICHCONTROL);
    if ((val >= 0) && (val <= max)) {
	SetCtlValue (WHICHCONTROL, val);
	scroll_term(termw);
    }
}				/* doscroll */


/****************************************************************************/
/* we move the displayed region to the bottom when we recieve characters */
/****************************************************************************/

screen_to_bottom (struct termw *termw)
{
    if (termw->display_topline != toplin) {
	SetCtlValue (termw->t_vscroll, termw->display_totlines - termw->screensize);
	scroll_term(termw);
    }
}

/****************************************************************************/
/* update_vscroll - adjust the scaling of the vertical scroll bar, or  */
/*    	      	    disable it if we havn't saved anything back yet */
/****************************************************************************/
update_vscroll (struct termw *termw)
{
    SetCtlMax (termw->t_vscroll, termw->display_totlines-termw->screensize);
    SetCtlValue (termw->t_vscroll, termw->display_totlines-termw->screensize);
    if (termw->in_front && termw->display_totlines > termw->screensize)
	HiliteControl (termw->t_vscroll, 0);
    else
	HiliteControl (termw->t_vscroll, 255);
}

/****************************************************************************/
/****************************************************************************/
void t_pagescroll (struct termw *termw, int code, int amount, 
		   ControlHandle ctrlh)
{
    Point myPt;
    register int val, max;

    max = GetCtlMax (ctrlh);
    val = GetCtlValue (ctrlh);
    
    do {
	GetMouse (&myPt);
	if (TestControl (ctrlh, myPt) != code)
	    continue;
	
	val += amount;
	if (val < 0)
	    val = 0;
	if (val > max)
	    val = max;
	SetCtlValue (ctrlh,  val);
	scroll_term(termw);
    } while (StillDown ());
}				/* pagescroll */


termmouse (struct termw *termw, EventRecord *evt)
{
    int actrlcode;
    long ticks;
    ControlHandle acontrol;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* save the current port */
    SetPort (termw->window);

    GlobalToLocal (&evt->where);/* convert to local */
    if (termw->mouse_arrows || (evt->modifiers & optionKey)) {
	if (PtInRect (evt->where, &termw->ScreenRect)) { /* In terminal content? */
	    mouse_cursor_move(termw, evt);	    
	    SetPort (savePort);		/* restore previous port */
	    return;			/* yes, do mouse stuff */
	}
    }
    cursor_erase(termw);
    actrlcode = FindControl (evt->where, termw->window, &acontrol);
    switch (actrlcode) {	
      case inUpButton:
      case inDownButton:
	(void) TrackControl (acontrol, evt->where, (ProcPtr) doscroll);
	break;

      case inPageUp:
	t_pagescroll (termw, actrlcode, -(termw->screensize/2), acontrol);
	break;

      case inPageDown:
	t_pagescroll (termw, actrlcode, (termw->screensize/2), acontrol);
	break;

      case inThumb:
	(void) TrackControl (acontrol, evt->where, (ProcPtr) NIL);
	scroll_term(termw);
	break;
	
      case 0:		/* in the window content itself */
        /* $$$ SHOULD DO SOMETHING ABOUT DOUBLE CLICKS HERE!! */
	mouse_region_select(termw, evt);
	break;
    }
    /* MOVETOCHAR(termw->curcol, termw->curlin - termw->display_topline); */
    cursor_draw(termw);
    SetPort (savePort);		/* restore previous port */
}


/* 
 * dir is 'A' (up), 'B' (down), 'C' (right), or 'D' (left) 
 */
void do_arrow(struct termw *termw, unsigned char dir)
{
    OutputChar(termw, '\033');		/* ESC */
    if (termw->curskey_mode)
    	OutputChar(termw, 'O');	/* SS3 */
    else
    	OutputChar(termw, '[');	/* CSI */
    OutputChar(termw, dir);
}

/* char to send is n + ',' */
void do_keypad (struct termw *termw, int n)
{
    if (termw->appl_mode) {
    	OutputChar(termw, '\033');		/* ESC */
    	OutputChar(termw, 'O');	/* SS3 */
	OutputChar(termw, (unsigned char) n + 'l');
    } else {
    	OutputChar(termw, (unsigned char) n + ',');	/* normal digit or glyph */
    }
}

/* pf1 == 0 ... pf4 == 3 */
void do_pfkey(struct termw *termw, int n)
{
    OutputChar(termw, '\033');		/* ESC */
    OutputChar(termw, 'O');	/* SS3 */
    OutputChar(termw, (unsigned char) n + 'P');
}

void do_keyenter(struct termw *termw)
{
    if (termw->appl_mode) {
    	OutputChar(termw, '\033');		/* ESC */
    	OutputChar(termw, 'O');	/* SS3 */
	OutputChar(termw, 'M');
    } else {
    	OutputChar(termw, '\015');
    }
}

void mouse_cursor_move (struct termw *termw, EventRecord *evt)
{
    int mouselin;
    int mousecol;
    int tempcol;
    int templin;
    int i;
    Point MousePt;

    MousePt = evt->where;
    mouselin = (MousePt.v - TOPMARGIN) / termw->lineheight;
    mousecol = (MousePt.h - LEFTMARGIN + termw->charwidth/2) / 
	termw->charwidth;
    tempcol = termw->curcol;
    templin = termw->curlin;

    if (mousecol < tempcol)
	for (i = tempcol; i > mousecol; i--) {
	    do_arrow (termw, leftARROW);
	    waitasec (termw);
	    /* If tabs are used, we may go too far, so end loop */
	    if (termw->curcol <= mousecol)
		i = mousecol;
	}

    if (mouselin < templin)
	for (i = templin; i > mouselin; i--) {
	    do_arrow (termw, UPARROW);
	    waitasec (termw);
	}

    else if (mouselin > templin)
	for (i = templin; i < mouselin; i++) {
	    do_arrow (termw, DOWNARROW);
	    waitasec (termw);
	}

    if (termw->curlin == mouselin)
	tempcol = termw->curcol;	/* for short lines */

    if (tempcol < mousecol)
	for (i = tempcol; i < mousecol; i++) {
	    do_arrow (termw, rightARROW);
	    waitasec (termw);
	    /* If tabs are used, we may go too far, so end loop */
	    if (termw->curcol >= mousecol)
		i = mousecol;
	}
}				/* mouse_cursor_move */

void invert_text(struct termw *termw, 
			int from_lin, 
			int from_col, 
			int to_lin, 
			int to_col)
{
    int t;
    
    if (from_lin > to_lin) {		/* make from < to */
    	t = to_lin;
	to_lin = from_lin;
	from_lin = t;
    	t = to_col;
	to_col = from_col;
	from_col = t;
    }
    
    from_lin -= termw->display_topline;	/* convert to screen coords */
    if (from_lin < 0) {
	from_lin = 0;
	from_col = 0;
    }
    
    /* if down out of sight, forget it */
    if (from_lin >= termw->screensize)
	return;
	
    to_lin -= termw->display_topline;	/* convert to screen coords */
    
    if (to_lin < 0)			/* if up out of sight, forget it */
	return;
	
    if (to_lin >= termw->screensize) {
	to_lin = termw->screensize-1;
	to_col = MAXCOL;
    }

    if (from_lin == to_lin) {		/* if only one line */
	if (from_col > to_col) {
    	    t = to_col;
	    to_col = from_col;
	    from_col = t;
	}
				  
	if (from_col != to_col)      /* then invert the characters in between */
	    invertchars(termw, from_lin, from_col, 1, to_col - from_col);
    } else {
	if (from_col < MAXCOL)
	    invertchars(termw, from_lin, from_col, 1, MAXCOL - from_col);
	t = to_lin - from_lin - 1;
	if (t > 0)
	    invertchars(termw, from_lin+1, 0, t, MAXCOL);
	if (to_col > 0)
	    invertchars(termw, to_lin, 0, 1, to_col);
    }
}


int typeof_char(unsigned char c)
{
    if ((c == ' ') || (c == '\240'))
    	return (0);			/* whitespace char */

    if (((c >= '0') && (c <= '9'))
    	|| ((c >= 'A') && (c <= 'Z'))
    	|| ((c >= 'a') && (c <= 'z'))
    	|| ((c >= '\300') && (c <= '\377') && (c != '\327') && (c != '\367'))
    	|| ((c >= '\271') && (c <= '\276') && (c != '\273'))
    	|| (c == '\262') || (c == '\263') || (c == '\252'))
	return (1);			/* alpha-numeric char */

    return (2);				/* printing, non-alphanum char */
}


int all_spaces (struct termw *termw, register int r, register int c)
{
    register int i;
    
    for (i = c; i < MAXCOL; i++)
	if ((termw->scr[r][i] != ' ')
	    || (termw->scr_attrs[r][i] != 0))
	    return (0);			/* found a non-space */

    return (1);				/* everything was spaces */
}


void point_to_mouse_low_high (struct termw *termw, 
			 Point *MousePt_p,
			 int n_clicks,
			 int *mouselin_p,
			 int *mousecol_p,
			 int *mousecol_lp,
			 int *mousecol_hp)
{
    int real_lin, real_col;
    int ch_type, ch_attr;
    int i;
    
    real_lin = (MousePt_p->v-TOPMARGIN) / termw->lineheight + 
	termw->display_topline;
    if (real_lin < termw->display_topline)
	real_lin = termw->display_topline;
    if (real_lin >= termw->display_topline + termw->screensize)
	real_lin = termw->display_topline + termw->screensize-1;

    *mouselin_p = real_lin;
    
    real_col = (MousePt_p->h - LEFTMARGIN + termw->charwidth/2) / 
	termw->charwidth;
    if (real_col < 0) real_col = 0;
    if (real_col > MAXCOL) real_col = MAXCOL;

    /*
     * We spoof things a bit here -- if the rest of the line is all blanks,
     * then we treat it as a single character (CRLF, really), and pretend
     * that the user clicked the mouse in the first blank character after all
     * text.
     */
    if (all_spaces(termw,real_lin, real_col))
    {
    	for (i = real_col; i >= 0; i--)
	    if ((termw->scr[real_lin][i] != ' ')
	        || (termw->scr_attrs[real_lin][i] != 0))
		break;
	real_col = i+1;
	if (real_col < 0) real_col = 0;
	if (real_col > MAXCOL) real_col = MAXCOL;
    }
    
    *mousecol_p = real_col;

    if (n_clicks == 0)		/* if a SINGLE click */
    {
    	*mousecol_lp = real_col;
	*mousecol_hp = real_col;
    }
    else if (n_clicks == 1)	/* if a DOUBLE click */
    {
	ch_type = typeof_char (termw->scr[real_lin][real_col]);
	ch_attr = termw->scr_attrs[real_lin][real_col];
	for (i = real_col-1; i >= 0; i--)
	  /* if a different type */
	    if ((typeof_char (termw->scr[real_lin][i]) != ch_type)
		/* or colored different */
	        || (termw->scr_attrs[real_lin][i] != ch_attr))
	      /* then it isn't the same kind of char */
	      break;			
	*mousecol_lp = i+1;
	for (i = real_col+1; i < MAXCOL; i++)
	  /* if a different type */
	    if ((typeof_char (termw->scr[real_lin][i]) != ch_type)
		/* or colored different */
	        || (termw->scr_attrs[real_lin][i] != ch_attr)) 
	      /* then it isn't the same kind of char */
	      break;
	*mousecol_hp = i;
    }
    else				/* a TRIPLE click */
    {
    	*mousecol_lp = 0;
	*mousecol_hp = MAXCOL;
    }
}


void mouse_region_select (struct termw *termw, EventRecord *evt)
{
    int mouselin;
    int mousecol_l, mousecol_h, real_mousecol;
    int i, shift, sval, smax;
    int old_from_lin, old_from_col, old_to_lin, old_to_col;
    Point MousePt;
    /* used for double-click determination */
    static Point prev_mouse_point = {0, 0};
    static long prev_mouse_up = 0L;
    static int n_clicks_here = 0;

    MousePt = evt->where;

    /* if no selection, then a shift drag is just a drag */
    if (termw->have_selection)
	shift = (evt->modifiers) & shiftKey;
    else
	shift = 0;

    if (((evt->when - prev_mouse_up) <= GetDblTime())
    	&& (ABS(MousePt.v - prev_mouse_point.v) < termw->lineheight)
	&& (ABS(MousePt.h - prev_mouse_point.h) < termw->charwidth))
	n_clicks_here = (n_clicks_here + 1) % 3;
    else
    	n_clicks_here = 0;		/* just one click */

    prev_mouse_point = MousePt;			/* save for next time */
    prev_mouse_up = TickCount ();		/* save when the mouse went up */

    /* if not adding to region, remove old one */
    if (!shift && termw->have_selection)
    	invert_text(termw,
		    termw->from_lin,
		    termw->from_col,
		    termw->to_lin,
		    termw->to_col);


    point_to_mouse_low_high(termw,&MousePt, n_clicks_here, &mouselin,
    			     &real_mousecol, &mousecol_l, &mousecol_h);

    if (shift) {
	/*
	 * Swap from_* and to_* if closer to from.  This sets the further-away
	 * side as the anchor, and the closer one as the part we are changing.
	 */
	if (ABS((MAXCOL * termw->from_lin + termw->from_col) -
		(MAXCOL * mouselin + real_mousecol)) <
	    ABS((MAXCOL * termw->to_lin + termw->to_col)
		- (MAXCOL * mouselin + real_mousecol))) {
    	    i = termw->to_lin;
	    termw->to_lin = termw->from_lin;
	    termw->from_lin = i;
    	    i = termw->to_col;
	    termw->to_col = termw->from_col;
	    termw->from_col = i;
	}
    } else {
	termw->from_lin = mouselin;
	termw->from_col = mousecol_l;
	termw->to_lin = mouselin;
	termw->to_col = mousecol_h;
	
	/* Select the text if a double or triple click. */
	if (termw->from_col != termw->to_col)
    	    invert_text(termw,
			termw->from_lin,
			termw->from_col,
			termw->to_lin,
			termw->to_col);
    }
    /* save in case we have to swap which point is the anchor */
    old_from_lin = termw->from_lin;
    old_from_col = termw->from_col;
    old_to_lin = termw->to_lin;
    old_to_col = termw->to_col;
    
    while (StillDown()) {
	GetMouse(&MousePt);
	point_to_mouse_low_high(termw,&MousePt, n_clicks_here, &mouselin,
    				 &real_mousecol, &mousecol_l, &mousecol_h);

	/*
	 * If above or below screen, auto-scroll the slider and select more.
	 */
	
	if (mouselin < termw->display_topline) {
	    sval = GetCtlValue (termw->t_vscroll) - 1;
	    smax = GetCtlMax (termw->t_vscroll);
	    if ((sval >= 0) && (sval <= smax)) {
		SetCtlValue (termw->t_vscroll, sval);
		scroll_term(termw);
	    }
	    mouselin = termw->display_topline;
	    real_mousecol = 0;

	} else if (mouselin >= termw->display_topline + termw->screensize) {
	    sval = GetCtlValue (termw->t_vscroll) + 1;
	    smax = GetCtlMax (termw->t_vscroll);
	    if ((sval >= 0) && (sval <= smax)) {
		SetCtlValue (termw->t_vscroll, sval);
		scroll_term(termw);
	    }
	    mouselin = termw->display_topline + termw->screensize-1;
	    real_mousecol = MAXCOL;
	}
	
	/*
	 * If we are above the anchor, then the "interesting" side of the 
	 * click extent is mousecol_l, else it is mousecol_h.
	 */
	if ((MAXCOL * termw->from_lin + termw->from_col) > 
	    (MAXCOL * mouselin + real_mousecol))
	{
	    i = mousecol_l;		/* "above" the anchor point */

	    /* but if we were below the anchor, restore and swap */
	    if ((MAXCOL * termw->from_lin + termw->from_col) < 
		(MAXCOL * termw->to_lin + termw->to_col))
	    {
		/* Unselect current text. */
 		invert_text(termw, termw->from_lin, termw->from_col, 
			    termw->to_lin, termw->to_col);

		termw->from_lin = old_to_lin;
		termw->from_col = old_to_col;
		termw->to_lin = old_from_lin;
		termw->to_col = old_from_col;
		
		/* Reselect new (old) text. */
 		invert_text(termw, termw->from_lin, termw->from_col, 
			    termw->to_lin, termw->to_col);
	    }
	} else {
	    i = mousecol_h;		/* "below" the anchor point */

	    /* but if we were above the anchor, restore and swap */
	    if ((MAXCOL * termw->from_lin + termw->from_col) > 
		(MAXCOL * termw->to_lin + termw->to_col))
	    {
		/* Unselect current text. */
 		invert_text(termw, termw->from_lin, termw->from_col, 
			    termw->to_lin, termw->to_col);

		termw->from_lin = old_from_lin;
		termw->from_col = old_from_col;
		termw->to_lin = old_to_lin;
		termw->to_col = old_to_col;
		
		/* Reselect new (old) text. */
 		invert_text(termw, termw->from_lin, termw->from_col, 
			    termw->to_lin, termw->to_col);
	    }
	}
	    
	
	/*
	 * If any new text was selected, invert it.
	 */
	if ((i != termw->to_col) || (mouselin != termw->to_lin)) {
	    invert_text(termw, termw->to_lin, termw->to_col, mouselin, i);
	    termw->to_lin = mouselin;
	    termw->to_col = i;
	}
    }
    
    /* make from < to */	
    if ((MAXCOL * termw->from_lin + termw->from_col) > 
	(MAXCOL * termw->to_lin + termw->to_col)) {
    	i = termw->to_lin;
	termw->to_lin = termw->from_lin;
	termw->from_lin = i;
    	i = termw->to_col;
	termw->to_col = termw->from_col;
	termw->from_col = i;
    }
    
    if ((termw->from_lin != termw->to_lin) ||
	(termw->from_col != termw->to_col))
	termw->have_selection = TRUE;
    else
	termw->have_selection = FALSE;
	
    /*
     * If the mouse wasn't down long enough to be a drag, time double click
     * from the mouse UP.
     */
    if (((TickCount () - prev_mouse_up) <= GetDblTime())
    	&& (ABS(MousePt.v - prev_mouse_point.v) < termw->lineheight)
	&& (ABS(MousePt.h - prev_mouse_point.h) < termw->charwidth)) {
	prev_mouse_up = TickCount ();	/* save when the mouse went up */
    }
}

/* (PWP) if the selection is within [tlin,blin], then remove it */

void maybe_nuke_selection (struct termw *termw, int tlin, int blin)
{
    int my_to_lin;

    if (!termw->have_selection)
	return;

    my_to_lin = termw->to_lin;
    if ((termw->to_col == 0) && (termw->from_lin != termw->to_lin))
    	my_to_lin--;

    if (!(((tlin < termw->from_lin) && (blin < termw->from_lin))
	  || ((tlin > my_to_lin) && (blin > my_to_lin))) ) {
	termw->have_selection = FALSE;
	invert_text(termw,
		    termw->from_lin,
		    termw->from_col,
		    termw->to_lin,
		    termw->to_col);
    }
}

/*
 * Copy the current selction to the (internal) clipboard.
 *
 * This is an external, but we don't have to save the GrafPort
 * because we don't do anything to the screen.
 */
scr_copy (struct termw *termw)
{
    int lin, i, rcol;
    long sz;
    char *dp;
    ScrapStuff *pss;
    
    if (myclip_h == NIL) {
	printerr("scr_copy: clip handle not allocated", 0);
	return;
    }
    
    if (termw->have_selection) {
	/****** find out how big the text to copy is ******/
    	if (termw->from_lin == termw->to_lin) {
	    /*
	     * If we are copying to the end of line, we should really only copy
	     * a CR instead of all those trailing blanks.  So we must find out
	     * where the last real character is.
	     */
	    if (termw->to_col >= MAXCOL) {
		for (rcol = MAXCOL; rcol > 0; rcol--)		/* last */
		    if ((termw->scr[termw->to_lin][rcol-1] != ' ')
			|| (termw->scr_attrs[termw->to_lin][rcol-1] != 0))
			break;
	    } else {
	    	rcol = termw->to_col;
	    }
	    sz = rcol - termw->from_col + 1;
	} else {
	    for (rcol = MAXCOL; rcol > termw->from_col; rcol--)	/* first */
		if ((termw->scr[termw->from_lin][rcol-1] != ' ')
		    || (termw->scr_attrs[termw->from_lin][rcol-1] != 0))
		    break;
	    /* chars plus one for the termw->newline */
	    sz = rcol - termw->from_col + 1;
	    /* in between */
	    for (lin = termw->from_lin+1; lin < termw->to_lin; lin++) {
		for (rcol = MAXCOL; rcol > 0; rcol--)
		    if ((termw->scr[lin][rcol-1] != ' ')
			|| (termw->scr_attrs[lin][rcol-1] != 0))
			break;
		sz += rcol + 1;	/* chars plus one for the termw->newline */
	    }
	    if (termw->to_col >= MAXCOL) {
	    	/***** find the last real character *****/
		for (rcol = MAXCOL; rcol > 0; rcol--)		/* last */
		    if ((termw->scr[termw->to_lin][rcol-1] != ' ')
			|| (termw->scr_attrs[termw->to_lin][rcol-1] != 0))
			break;
	    } else {
		rcol = termw->to_col;
	    }
	    sz += rcol;		/* chars */
	    if (termw->to_col >= MAXCOL)
	    	sz++;
	}
	
	/***** Reality Check *****/
	if (sz > 8192) {
	    printerr("Too big to copy: ", sz);
	    return;
	}
	    
	/****** allocate and lock a buffer for the text ******/
	if (sz > GetHandleSize ((Handle) myclip_h)) {
	    HUnlock((Handle) myclip_h);
	    /* $$$ this may fail, but we have no way of knowing. */
	    /* (in assembler, this will return a result, but the pascal */
	    /*  version is a PROCEDURE, so we get zilch.  If this fails */
	    /*  we will probably crash the Mac...) */
	    SetHandleSize((Handle) myclip_h, sz);
	}
	HLock((Handle) myclip_h);
	dp = *myclip_h;
	
	/****** copy the characters over to the clip ******/
    	if (termw->from_lin == termw->to_lin) {
	    if (termw->to_col >= MAXCOL) {
		for (rcol = MAXCOL; rcol > 0; rcol--)		/* last */
		    if ((termw->scr[termw->to_lin][rcol-1] != ' ')
			|| (termw->scr_attrs[termw->to_lin][rcol-1] != 0))
			break;
	    } else {
	    	rcol = termw->to_col;
	    }
	    for (i = termw->from_col; i < rcol; i++)
	    	*dp++ = termw->scr[termw->from_lin][i];
	    if (termw->to_col >= MAXCOL)
	    	*dp++ = CR;		/* add the return */
	} else {
	    /* trim off spaces */
	    for (rcol = MAXCOL; rcol > termw->from_col; rcol--)	/* first */
		if ((termw->scr[termw->from_lin][rcol-1] != ' ')
		    || (termw->scr_attrs[termw->from_lin][rcol-1] != 0))
		    break;
	    for (i = termw->from_col; i < rcol; i++)
		*dp++ = termw->scr[termw->from_lin][i];
	    *dp++ = CR;
	    /* in between */
	    for (lin = termw->from_lin+1; lin < termw->to_lin; lin++) {
		for (rcol = MAXCOL; rcol > 0; rcol--)
		    if ((termw->scr[lin][rcol-1] != ' ')
			|| (termw->scr_attrs[lin][rcol-1] != 0))
			break;
		for (i = 0; i < rcol; i++)
		    *dp++ = termw->scr[lin][i];
		*dp++ = CR;
	    }
	    if (termw->to_col == MAXCOL) {
		for (rcol = MAXCOL; rcol > 0; rcol--)		/* last */
		    if ((termw->scr[termw->to_lin][rcol-1] != ' ')
			|| (termw->scr_attrs[termw->to_lin][rcol-1] != 0))
			break;
	    } else {
		rcol = termw->to_col;
	    }
	    for (i = 0; i < rcol; i++)
		*dp++ = termw->scr[termw->to_lin][i];
	    if (termw->to_col >= MAXCOL)
		*dp++ = CR;
	}
	myclip_size = (dp - *myclip_h);

	/****** check to make sure we didn't overflow the clipboard ******/
	if (myclip_size > sz)
	    macfatal ("Overflow! myclip_size - sz ==",
	    	myclip_size - sz);


	/****** Now copy our internal clipboard to the Macintosh one *****/
	/*
	 * $$$ at this point we really should allocate a second buffer,
	 * and copy the characters of our buffer into it, converting them
	 * from whatever ISO set we are displaying with to the Mac char set.
	 */
	ZeroScrap();
	if (PutScrap(myclip_size, 'TEXT', *myclip_h) != noErr)
		printerr("Couldn't PutScrap", 0);		
	
	pss = InfoScrap();
	my_scrapcount = pss->scrapCount;	/* save this to see if the user */
						/* cuts/copies outside of us */
	
	/****** We are done.  Unlock the handle ******/
	HUnlock((Handle) myclip_h);
    } else {
	SysBeep(3);
    }
}

/*
 * Paste the clipboard into the terminal, by "typing" it in.
 *
 * This also is an external, but we don't have to save the GrafPort
 * because the only time we do anything to the screen, it's through
 * inpchars(), which saves the GrafPort itself.
 */
scr_paste (struct termw *termw)
{
    char *cp, *endp;
    char **h;
    long l, o;
    ScrapStuff *pss;

    pss = InfoScrap();
    if (my_scrapcount == pss->scrapCount) {
    	/* if this is still the same scrap that we made */
	if (myclip_size > 0) {
	    HLock((Handle) myclip_h);
	    cp = *myclip_h;
	    endp = cp + myclip_size;
	    for (; cp < endp; cp++) {
		OutputChar(termw, *cp);
		if (*cp == CR)
		    waitasec (termw);
	    }
	    HUnlock((Handle) myclip_h);
	} else {
	    SysBeep(3);
	}
    } else {	/* we have to get the TEXT scrap from the clipboard */
	h = NewHandle(0);
	l = GetScrap(h, 'TEXT', &o);
	if (l <= 0) {
	    SysBeep(3);
	} else {
	    HLock((Handle) h);
	    cp = *h;
	    endp = cp + l;
	    for (; cp < endp; cp++) {
	    	/* $$$ Should convert *cp to whatever ISO font we are typing */
		OutputChar(termw, *cp);
		if (*cp == CR)
		    waitasec (termw);
	    }
	    HUnlock((Handle) h);
	}	/* end if (l > 0) */
	DisposHandle(h);
    }
}

/*
 * Paste into the command window.
 */
cmd_paste (struct termw *termw)
{
    char *cp, *endp;
    char **h;
    long l, o;
    ScrapStuff *pss;

    pss = InfoScrap();
    if (my_scrapcount == pss->scrapCount) {
    	/* if this is still the same scrap that we made */
	if (myclip_size > 0) {
	    HLock((Handle) myclip_h);
	    cp = *myclip_h;
	    endp = cp + myclip_size;
	    for (; cp < endp; cp++) {
		writecbc(*cp);
		if (*cp == CR)
		    waitasec (termw);
	    }
	    HUnlock((Handle) myclip_h);
	} else {
	    SysBeep(3);
	}
    } else {	/* we have to get the TEXT scrap from the clipboard */
	h = NewHandle(0);
	l = GetScrap(h, 'TEXT', &o);
	if (l <= 0) {
	    SysBeep(3);
	} else {
	    HLock((Handle) h);
	    cp = *h;
	    endp = cp + l;
	    for (; cp < endp; cp++) {
	    	/* $$$ Should convert *cp to whatever ISO font we are typing */
		writecbc(*cp);
		if (*cp == CR)
		    waitasec (termw);
	    }
	    HUnlock((Handle) h);
	}	/* end if (l > 0) */
	DisposHandle(h);
    }
}


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

#ifdef COMMENT
show_inval_rgn(w)
WindowPeek w;
{
    RgnHandle r = NewRgn();

    CopyRgn (w->updateRgn, r);
    OffsetRgn(r,			/* convert to local grafport coords */
	      (((w->port).portBits).bounds).left,
	      (((w->port).portBits).bounds).top);
    FillRgn(r, qd.black);
    DisposeRgn(r);
}
#endif /* COMMENT */

/****************************************************************************/
/*
 * PWP: actually do all the scrolling and refreshing we have promised to
 * do.
 *
 * Method (and many var and fcn names) stolen from X11 xterm.
 */
/****************************************************************************/
void flushscroll (struct termw *termw)
{
    register int i, now;
    Rect r, opened_r;		/* cannot be register */
    GrafPtr currWindow;		/* cannot be register */
    RgnHandle newupdateRgn;
    
    if (termw->scroll_amount == 0) {
    	printerr ("flushscroll() called with no scroll to flush", 0);
    	return;
    }
    
    /* should hide the cursor here if not already hidden */
    
    /* (PWP) if our selected region overlaps, but is not enclosed by the region
       we want to scroll, then remove it, because the region no longer contains
       what the user thought it did. */
    if (termw->have_selection && (termw->saved_tlin != toplin) && 
	(termw->saved_blin != botlin) && 
	((termw->from_lin < termw->saved_tlin) || 
	 (termw->to_lin > termw->saved_blin)) &&
    	((termw->to_lin > termw->saved_tlin)   || 
	 (termw->from_lin < termw->saved_blin))) {
	termw->have_selection = FALSE;
	invert_text(termw, termw->from_lin, termw->from_col, termw->to_lin, 
		    termw->to_col);
    }
    
    if (!termw->in_front) {
	/* if not in front, compensate update region for scrolling */
	GetPort (&currWindow);
	/* scroll the old updateRgn */
	OffsetRgn (((WindowPeek) currWindow)->updateRgn, 0, 
		   -termw->lineheight);
    }
    

    /* 
     * Do the scrolling
     */
    makerect(termw,&r, termw->saved_tlin, 0, termw->saved_blin - 
	     termw->saved_tlin + 1, MAXCOL);
    newupdateRgn = NewRgn();
    
    if (termw->smoothscroll && termw->in_front) {
	int dir = 1;			/* direction */
	if (termw->scroll_amount < 0)
	    dir = -1;
	for (i = 1; i <= termw->scroll_amount*termw->lineheight*dir; i += 1) {
	    /* PWP: wait for a vertical reblank (in a sneaky way) */
	    now = TickCount ();
	    while (TickCount () == now)
		/* wait... */ ;
	    ScrollRect (&r, 0, dir, newupdateRgn);
	}
    } else {
	ScrollRect (&r, 0, termw->scroll_amount * termw->lineheight, 
		    newupdateRgn);
    }

    if (!termw->in_front) {
	if (termw->scroll_amount < 0)
	    makerect(termw,&opened_r, termw->saved_blin + termw->scroll_amount,
		     0, -termw->scroll_amount, MAXCOL);
	else
	    makerect(termw,&opened_r, termw->saved_tlin, 0, 
		     termw->scroll_amount, MAXCOL);
	InvalRgn(newupdateRgn);
	ValidRect(&opened_r);
    }

    DisposeRgn(newupdateRgn);

    termw->scroll_amount = 0;		/* we've done it now */

    /*
     * Now refresh any lines that need to be drawn
     */
    if (termw->refresh_amount < 0)		/* scrolling UP */
    {
	for (i=termw->saved_blin+termw->refresh_amount; i<termw->saved_blin; i++)
	    draw_w_line_attrs(termw, i+termw->display_topline, i, 0, MAXCOL, 0);
    }
    else if (termw->refresh_amount > 0)	/* scrolling DOWN */
    {
	for (i=termw->saved_tlin; i<termw->saved_tlin+termw->refresh_amount; i++)
	    draw_w_line_attrs(termw, i+termw->display_topline, i, 0, MAXCOL, 0);
    }

    termw->refresh_amount = 0;
}


/****************************************************************************/
/*
 * (UoR)
 *
 * Scroll lines within the scroll region upwards from line tlin
 * to line blin (lines are assumed to be in the region)
 *
 * (PWP) scroll_screen is the combination of scroll_up and scroll_down.
 *       dir is the number of lines to scroll, <0 if up, >0 if down.
 *	 (actually, right now only -1 and 1 are handled.)
 */
/****************************************************************************/
void scroll_screen (termw, tlin, blin, delta)
/* these are in scr[][] cordinates */
    struct termw *termw;
    register int tlin;
    register int blin;
    register int delta;
{
    register int i, now;
    char *savedline, *savedattr;  /* temporary to hold screen line pointer */
    Rect r, opened_r;		/* cannot be register */
    GrafPtr currWindow;		/* cannot be register */
    RgnHandle newupdateRgn;

    /*
     * flush out any pending characters.
     */
    if (termw->out_maxcol) 
	flushbuf(termw);

    /*
     * See if we are scrolling something different and have to flush
     * our pending scroll.
     * We do if asked to scroll something different than the current
     * scrolling rgn, or if we are changing direction, or if we have 
     * already collected an entire scrolling region worth of scroll to do.
     */
    if (termw->scroll_amount) {
	i = termw->scroll_amount + delta;
	if (i < 0) i = -i;    /* set i to ABS( old scroll plus new scroll ) */
	if (termw->smoothscroll || 
	    (tlin != termw->saved_tlin) || 
	    (blin != termw->saved_blin) || 
	    ((termw->scroll_amount > 0) && (delta < 0)) ||
	    ((termw->scroll_amount < 0) && (delta > 0)) ||
	    (i >= (termw->saved_blin - termw->saved_tlin))) 
	{
	    flushscroll(termw);
	}
    }

    /*
     * Save up how much to scroll and where for later...
     */
    termw->saved_tlin = tlin;
    termw->saved_blin = blin;
    termw->scroll_amount += delta;
    /*
     * Should really set termw->refresh_amount to 0, then add to it 
     * when we "draw" characters onto lines, but this is safe.
     */
    termw->refresh_amount += delta;
    
    /* printerr("termw->scroll_amount now ", scroll_amount); */

    if (delta < 0)		/* if scrolling UP (forwards) */
    {
	/* adjust the internal character buffers */
	if ((tlin == toplin) && (blin == botlin)) { /* if whole screen */
	    termw->display_totlines -= delta; /* remember delta is negitive */
	    if (termw->display_totlines > MAX_SCREENSIZE)
		termw->display_totlines = MAX_SCREENSIZE; /* bounds */
	    /*top of saved buffer*/
    	    tlin = termw->screensize - termw->display_totlines;
	}
	for (now = 0; now < -delta; now++) {
 	    savedline = termw->scr[tlin];
	    savedattr = termw->scr_attrs[tlin];
	    for (i = tlin+1; i <= blin; i++) {
    		termw->scr[i-1] = termw->scr[i];
    		termw->scr_attrs[i-1] = termw->scr_attrs[i];
	    }
	    termw->scr[blin] = savedline;
	    termw->scr_attrs[blin] = savedattr;
    
	    zeroline(termw,blin);		/* clear the line */
	}

	/* adjust selection */
	if (termw->have_selection && (termw->from_lin >= tlin) && 
	    (termw->to_lin <= blin)) 
       {
	    termw->from_lin += delta;
	    if (termw->from_lin < termw->screensize - MAX_SCREENSIZE)
	    	termw->from_lin = termw->screensize - MAX_SCREENSIZE;
	    termw->to_lin += delta;
	    if (termw->to_lin < termw->screensize - MAX_SCREENSIZE)
	    	termw->to_lin = termw->screensize - MAX_SCREENSIZE;
	}
    }
    else			/* else scrolling DOWN (reverse scroll) */
    {
        /* adjust the internal buffers */
	for (now = 0; now < delta; now++) {
	    savedline = termw->scr[blin];
	    savedattr = termw->scr_attrs[blin];
	    for (i = blin-1; i >= tlin; i--) {
    		termw->scr[i+1] = termw->scr[i];
    		termw->scr_attrs[i+1] = termw->scr_attrs[i];
	    }
	    termw->scr[tlin] = savedline;
	    termw->scr_attrs[tlin] = savedattr;

	    zeroline(termw,tlin);
	}
	
	/* adjust selection */
	if (termw->have_selection && (termw->from_lin >= tlin) && 
	    (termw->to_lin <= blin)) 
	{
	    termw->from_lin += delta;
	    if (termw->from_lin > botlin) termw->from_lin = botlin;
	    termw->to_lin += delta;
	    if (termw->to_lin > botlin) termw->to_lin = botlin;
	}
    }

    /*
     * but if we are smooth (slow) scrolling and in front, do the scroll now.
     * must do this after adjusting internal buffers, so that the refresh
     * lines don't get confused.
     */
    if (termw->smoothscroll && termw->in_front)
    	flushscroll(termw);
}				/* scroll_up */

/****************************************************************************/
/* redraw the terminal screen (we got a redraw event) */
/****************************************************************************/
term_redraw (struct termw *termw)
{
    int i, lin;
    int vtoplin, vbotlin, vleftcol, vrightcol;
    Rect r, *rp;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    if (termw->screeninvert) {
	BackPat(qd.black);
	PenPat(qd.white);
    } else {
	BackPat(qd.white);
	PenPat(qd.black);
    }

    if (termw->scroll_amount)
	flushscroll(termw);

#ifdef COMMENT
    r = terminalWindow->portRect;	/* copy the window size */
    /* r.right -= 15;	*/		/* subtract control */
    /* PWP: clear the screen first */
    /* makerect(termw,&r, 0, 0, termw->screensize, MAXCOL); */
    /* EraseRect (&r); */
#endif /* COMMENT */

    /* See if the scroll bar and grow box need to be updated */
    r = termw->window->portRect;	/* copy of the window size */
    /* r.left = r.right - 16; */
    r.left = rightMARGIN;
    if (RectInRgn (&r, termw->window->visRgn))
    {
	EraseRect (&r);
	/* don't add to invalid while redrawing invalid */
	draw_grow_and_erase_line(termw, 0);
	DrawControls (termw->window);
    }

    /* Update area above first line */
    r = termw->window->portRect;	/* copy of the window size */
    r.bottom = r.top + TOPMARGIN;
    r.right -= 16;
    if (RectInRgn (&r, termw->window->visRgn))
	EraseRect (&r);

    /* Update area below last line */
    r = termw->window->portRect;	/* copy of the window size */
    r.top = termw->bottommargin - 1;
    r.right -= 16;
    if (RectInRgn (&r, termw->window->visRgn))
	EraseRect (&r);

    /* Update area to the left of first column */
    r = termw->window->portRect;	/* copy of the window size */
    r.top = r.top + TOPMARGIN;
    r.bottom = termw->bottommargin;
    r.right = LEFTMARGIN;
    if (RectInRgn (&r, termw->window->visRgn))
	EraseRect (&r);

    
    /* update_vscroll(termw); */
    /* SetCtlValue (termw->t_vscroll, GetCtlValue (t_vscroll)); */

#ifdef COMMENT
    lin = termw->display_topline;
    for (i = 0; i < termw->screensize; i++) {
    	makerect(termw,&r, i, 0, i+1, MAXCOL);
	if (RectInRgn (&r, termw->window->visRgn))
	    draw_w_line_attrs(termw, lin, i, 0, MAXCOL, 1);
	lin++;
    }
#endif

    /* This is an inverse of makerect() -- find the bounding chars */
    rp = &(**(termw->window->visRgn)).rgnBBox;
    vtoplin = (rp->top - TOPMARGIN) / termw->lineheight;
    vbotlin = (rp->bottom - TOPMARGIN +
	       termw->lineheight -1) / termw->lineheight;
    vleftcol = (rp->left - LEFTMARGIN) / termw->charwidth;
    vrightcol = (rp->right - LEFTMARGIN + termw->charwidth -1) / 
	termw->charwidth;

    /* bounds limit it to the actual screen area */
    if (vtoplin < 0) vtoplin = 0;
    if (vbotlin > termw->screensize) vbotlin = termw->screensize;
    if (vleftcol < 0) vleftcol = 0;
    if (vrightcol > MAXCOL) vrightcol = MAXCOL;

#ifdef COMMENT
    debug(F101,"term_redraw bounds vtoplin","",vtoplin);
    debug(F101,"term_redraw bounds vbotlin","",vbotlin);
    debug(F101,"term_redraw bounds vleftcol","",vleftcol);
    debug(F101,"term_redraw bounds vrightcol","",vrightcol);
#endif

    lin = termw->display_topline + vtoplin;
    for (i = vtoplin; i < vbotlin; i++) {
	draw_w_line_attrs(termw, lin, i, vleftcol, vrightcol, 1);
	lin++;
    }
    
    
    if (termw->have_selection)
    	invert_text(termw,
		    termw->from_lin,
		    termw->from_col,
		    termw->to_lin,
		    termw->to_col);

    /* (UoR) only if cursor is showing */
    if (termw->cur_drawn && termw->cursor_invert) {
	termw->cursor_invert = FALSE;	/* (UoR) make sure we draw it */
	cursor_draw(termw);		/* redraw cursor */
	termw->last_flash = TickCount (); /* (UoR) reset timer */
    }

    SetPort (savePort);		/* there just has to be a better way */
}				/* term_redraw */

draw_grow_and_erase_line (struct termw *termw, int invalidate_it)
{
    Rect r;

    DrawGrowIcon (termw->window);
    /* erase the bottom scroll line (but only if inverted screen) */
    if (!termw->screeninvert) {
	PenMode(patBic);
	MoveTo(0, (termw->window->portRect).bottom - 15);
	LineTo((termw->window->portRect).right - 15,
    		(termw->window->portRect).bottom - 15);
	PenMode(patCopy);
    }
    
    if (invalidate_it) {
	r.top = (termw->window->portRect).bottom - 16;
	r.bottom = (termw->window->portRect).bottom - 14;
	r.left = 0;
	r.right = (termw->window->portRect).right - 16;
	InvalRect(&r);
    }
}

term_activate (struct termw *termw, int mod)
{
    GrafPtr savePort;
    
    GetPort (&savePort);
    SetPort (termw->window);

    cursor_erase (termw);		/* remove cursor from screen */
    termw->in_front = mod & activeFlag;
    if (termw->in_front) {
	HiliteControl (termw->t_vscroll, 0);
	UpdateOptKey(1);
	DisableItem(menus[EDIT_MENU], UNDO_EDIT);
	DisableItem(menus[EDIT_MENU], CLEAR_EDIT);
    } else {
	HiliteControl (termw->t_vscroll, 255);
	UpdateOptKey(0);
	EnableItem(menus[EDIT_MENU], UNDO_EDIT);
	EnableItem(menus[EDIT_MENU], CLEAR_EDIT);
    }
    /* this does the right thing for background too */
    draw_grow_and_erase_line(termw, 1);
    cursor_draw(termw);

    SetPort (savePort);
}

/*
 * This CAN be called external to inpchars(), so save and restore the
 * GrafPort just in case.
 */
set_term_invert (struct termw *termw, int new_inv)
{
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    if (new_inv == termw->screeninvert)
    	return;
	
    if (new_inv) {
	BackPat (qd.black);	/* (UoR) use black background */
	PenPat(qd.white);
	termw->screeninvert = TRUE;
    } else {
	BackPat (qd.white);
	PenPat(qd.black);
	termw->screeninvert = FALSE;
    }
    InvalRect(&termw->window->portRect);/* invalidate whole window rectangle */
    SetPort (savePort);		/* there just has to be a better way */
}

/****************************************************************************/
/* sizevscroll - called when window is created and after a window grow */
/*    	      	    sequence to resize the scroll window's bars. */
/****************************************************************************/
void sizevscroll (struct termw *termw)
{
    register Rect *r;

    r = &termw->window->portRect;/* window size */
    HideControl (termw->t_vscroll);

    MoveControl (termw->t_vscroll, r->right - 15, r->top - 1);
    SizeControl (termw->t_vscroll, 16, r->bottom - r->top - 13);

    SetCtlMin (termw->t_vscroll, 0);
    update_vscroll(termw);
    ShowControl (termw->t_vscroll);
}

/****************************************************************************/
/* initalize the terminal emulator. */
/****************************************************************************/
init_term (struct termw *termw)
{
    register int i, j;
    register char *scp, *acp;
    char *scr_cp, *attr_cp;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);
   
    termw->topmargin = TOPMARGIN;	/* Edges of adjustable window */
    termw->bottommargin = bottomMARGIN;
    
    if ((scr_cp = (char *)NewPtr(((long)(MAXCOL+1) * (long) MAX_SCREENSIZE)))
	== NIL)
      macfatal("Could not allocate screen buffer", 0);
	
    if ((attr_cp = (char *)NewPtr(((long)(MAXCOL+1) * (long) MAX_SCREENSIZE)))
	== NIL)
      macfatal("Could not allocate screen attribute buffer", 0);
	
    if ((termw->real_scr = (ucharptr *) NewPtr ((long)(MAX_SCREENSIZE)
					 * (long) sizeof(ucharptr))) == NIL)
	macfatal("Could not allocate screen buffer", 0);
	
    if ((termw->real_attrs = (ucharptr *) NewPtr ((long)(MAX_SCREENSIZE)
					   * (long) sizeof(ucharptr))) == NIL)
	macfatal("Could not allocate screen buffer", 0);
	
    for (i = 0; i < MAX_SCREENSIZE; i++) {
	/* divvy up screen buffer */
    	termw->real_scr[i] = scr_cp + (i * (MAXCOL+1));
	/* divvy up screen attribute buf */
    	termw->real_attrs[i] = attr_cp + (i * (MAXCOL+1));

	scp = termw->real_scr[i];
	acp = termw->real_attrs[i];
	j = MAXCOL;
	do {			/* put normal spaces in all columns */
	    *scp++ = ' ';
	    *acp++ = 0;
	} while (--j > 0);
	*scp = ' ';		/* Terminate the lines as strings */
	*acp = 0;		/* Terminate the attrs as strings */
    }
    
    termw->scr = &termw->real_scr[MAX_SCREENSIZE - termw->screensize];
    if (termw->scr[0] == NIL)
	macfatal("init_term: scr assignment botched for [0]", 0);
    if (termw->scr[termw->screensize-1] == NIL)
	macfatal("\
init_term: scr assignment botched for [termw->screensize-1]", 0);

    termw->scr_attrs = &termw->real_attrs[MAX_SCREENSIZE - termw->screensize];
    if (termw->scr_attrs[0] == NIL)
	macfatal("init_term: scr assignment botched for [0]", 0);
    if (termw->scr_attrs[termw->screensize-1] == NIL)
	macfatal("\
init_term: scr assignment botched for [termw->screensize-1]", 0);
    
    termw->scrtop = toplin;		/* Scrolling region equals all */
    termw->scrbot = botlin;
    
    termw->scroll_amount = 0;		/* no pending scroll */
    termw->refresh_amount = 0;		/* no pending refresh */
    termw->saved_tlin = 0;
    termw->saved_blin = 0;
    
    termw->display_topline = toplin;	/* init display w/elevator at bottom */
    termw->display_totlines = termw->screensize;
    makerect(termw,&termw->ScreenRect, 0, 0, termw->screensize, MAXCOL);
    /* (UoR) full screen rectangle */
    
    SizeWindow(termw->window,
    	rightMARGIN + 1 + 16,     /* add extra to side for asthetics */
	bottomMARGIN + TOPMARGIN,     /* add extra to bottom for asthetics */
	FALSE);
    /* PWP: make the window match it's real size */

    termw->t_vscroll = GetNewControl (RCMDVSCROLL, termw->window);
    sizevscroll(termw);

    InitKeyStuff();		/* find the original KCHR keymaps */

#ifdef COMMENT
    draw_grow_and_erase_line(0);	/* it's new so don't invalidate it */
#endif

    /* ClipRect(&termw->ScreenRect); */

    SetPort (savePort);		/* there just has to be a better way */
}				/* init_term */

/****************************************************************************/
/* grow_term_to(termw, size) -- change the size of the terminal window to size.
   this is called by growterm() (see below) and the terminal settings dialog
   handler (termsetdialog()).
/****************************************************************************/
grow_term_to (struct termw *termw, int size)
{
    char *savedline;
    int i, j;
    GrafPtr savePort;

    GetPort (&savePort);
    SetPort (termw->window);

    if ((size < 1) || (size > MAX_SCREENSIZE))
       size = 24;	/* the default case */
    
    if (size > termw->display_totlines) { 
	/* 
	 * if getting bigger than we were
	 * We would zero out lines from (termw->screensize-size) to 
	 * (termw->screensize-display_totlines),
	 * but these were already zeroed when the original screen was inited.
	 */
	termw->display_totlines = size;
    }
    
    /* $$$ Make sure to scroll screen to what will be the new bottom here */

    /* adjust cursor row to match stretch */
    termw->curlin += size - termw->screensize;
    if (termw->curlin < 0)
	termw->curlin = 0;
    if (termw->curlin > size-1)
	termw->curlin = size-1;
    
    termw->screensize = size;
    if (termw->screensize > MAX_SCREENSIZE)
    	termw->screensize = MAX_SCREENSIZE;		/* bounds check */

    termw->scr = &termw->real_scr[MAX_SCREENSIZE - termw->screensize];
    termw->scr_attrs = &termw->real_attrs[MAX_SCREENSIZE - termw->screensize];
    
    termw->bottommargin = bottomMARGIN;	/* this changes */
    
    termw->scrtop = toplin;		/* Scrolling region equals all */
    termw->scrbot = botlin;
    termw->display_topline = 0;	/* re-init display w/elevator at bottom */
    makerect(termw,&termw->ScreenRect, 0, 0, termw->screensize, MAXCOL);
    /* (UoR) full screen rectangle */
    
    SizeWindow(termw->window,
    	rightMARGIN + 1 + 16,     /* add extra to side for asthetics */
	bottomMARGIN + TOPMARGIN,     /* add extra to bottom for asthetics */
	FALSE);
    /* PWP: make the window match it's real size */
    sizevscroll(termw);	/* size the scroll bars */

    /* ClipRect(&termw->ScreenRect); */

    /* invalidate whole window rectangle */
    InvalRect (&termw->window->portRect);

    SetPort (savePort);
}				/* grow_term_to */

/****************************************************************************/
/* 
 * growterm() -- called when we get a mouse-down in the lower right corner grow
 * box.
 * Probably all right not to save the grafport, but we do anyway just to be
 * double extra safe.
 */
/****************************************************************************/
growterm (struct termw *termw, Point *p)
{
    long gr;
    int height;
    int width;
    int size;
    Rect growRect;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    growRect = qd.screenBits.bounds;
    growRect.top = 50;		/* minimal horizontal size */
    growRect.left = rightMARGIN + 18;	/* minimal vertical size */
    growRect.right = rightMARGIN + 18;	/* minimal vertical size */

    gr = GrowWindow (termw->window, *p, &growRect);

    if (gr == 0)
	return;
    height = HiWord (gr);
    width = LoWord (gr);

    size = (height - (2 * TOPMARGIN)) / termw->lineheight;
    if (size > MAX_SCREENSIZE)
    	termw->screensize = MAX_SCREENSIZE;		/* bounds check */
    if (size < 1)
    	size = 1;

    grow_term_to(termw, size);

    SetPort (savePort);		/* there just has to be a better way */
}				/* growterm */

/****************************************************************************/
get_term_pos(struct termw *termw, int *top_p, int *left_p)
{
    Point mypoint;
    GrafPtr savePort;
    
    GetPort (&savePort);	/* there just has to be a better way */
    SetPort (termw->window);

    mypoint.v = termw->window->portRect.top;
    mypoint.h = termw->window->portRect.left;
    LocalToGlobal(&mypoint);
    
    if (top_p)
	*top_p = mypoint.v;
    if (top_p)
	*left_p = mypoint.h;
	
    SetPort (savePort);		/* there just has to be a better way */
}

set_term_pos(struct termw *termw, int top, int left)
{
    MoveWindow(termw->window, left, top, TRUE);
}

/*
 * Junk so Emacs will set local variables to be compatible with Mac/MPW.
 * Should be at end of file.
 * This module was apparently formatted with tabs = 8
 * 
 * Local Variables:
 * tab-width: 8
 * End:
 */
