/* amigadisplay.c --- Amiga interface for niftyterm
 *
 * Copyright 1989, Chris Newman.  Changes Copyright 1989, Todd Williamson.
 * All Rights Reserved
 * Permission is granted to copy, modify, and use this as long
 * as this notice remains intact.  This is a nifty program.
 *
 * Parts of this program were swiped from Joe Keane's jterm, and other parts
 * were swiped from ITC's h19 terminal.
 *
 * DISCLAIMER: the author (and maintainer) of this program is not responsible
 * for any damage or other problems caused by it.
 *
 * $Author: ppessi $ $Revision: 1.15 $ $Date: 1993/11/15 09:10:17 $
 */

static char rcsid[]=
 "$Id: amigadisplay.c,v 1.15 1993/11/15 09:10:17 ppessi Exp $";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "nifty.h"
#include "amiga.h"
#include "display.h"
#include "dispmacros.h"
#include "niftyprofile.h"
#include "national.h"
#include "nio.h"

#include <graphics/gfxmacros.h>
#include <devices/timer.h>
#include <utility/tagitem.h>

/* imported Amiga specific globals */
extern struct KeyMap *myKeyMap;
extern struct Device *ConsoleDevice;
extern struct IOStdReq console_IO;

/* imported globals */
extern enum iotype iotype;          /* what device using (controls break menu item) */
extern char altismeta;		    /* is alt key meta key? */
extern int emulation;		    /* current emulation mode */
extern int vt100_yucky_wrap_mode;   /* hack to do v100's wrap mode */
extern int ansi_LNM;		    /* ANSI line feed/new line mode */
extern struct nifty_display disp;   /* generic display data structure */
extern int shared;                  /* serial device shared? */

/* external functions */
extern int lstrncmp();
extern int cursormap;
struct TextFont *GetFonts();

/* static functions */ 
static LONG natText( struct RastPort *rp, STRPTR string, unsigned long count );

/* The styles that we ask for  */
#define	STYLE_MASK	(UNDERLINE | BOLD | ITALIC)
#define	ATTRIB_MASK	(UNDERLINE | BOLD | ITALIC | ALTERNATE | DOUBLE1 | DOUBLE2)

#define	POSX(num)	(w->BorderLeft + (num) * fonts.w)
#define	POSY(num)	(w->BorderTop + (num) * fonts.h)

#define	SETFONT(style, y) \
{\
    register int sty = ((style) | (STYLEFLAG(y) & DOUBLE_MASK)) & ATTRIB_MASK;\
    if(fonts.attr[sty] == NULL) GetFonts(sty, 1);\
    else SetFont(rp, fonts.attr[sty]);\
    SetSoftStyle(rp, fonts.usealgo[sty & ATTRIB_MASK], -1);\
}

#define PUTS(x, y, s, len) \
  if(disp.invert) SetDrMd(rp, DEFDRMD | INVERSVID);\
  Move(rp, STYLEFLAG(y) & DOUBLE_MASK ? POSX((x)*2) : POSX(x),\
       POSY(y) + fonts.b); natText(rp, (s), (len));\
  if(disp.invert) SetDrMd(rp, DEFDRMD)

#define SPUTS(x, y, stng, len, style) /* this macro outputs a string with styles */\
{   int	tempwflag = STYLEFLAG(y) & DOUBLE_MASK ? 2 : 1;			    \
    PUTS((x), (y), (stng), (len));						    \
    if (XOR((style) & INVERSE,(style) &	FLASH && flashmode)) {		    \
	BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(x*tempwflag), POSY(y),\
	                  fonts.w*(len)*tempwflag, fonts.h, INVERT);\
    }\
}

#define	GETXPOS(x)	(((x)-w->BorderLeft)/fonts.w)
#define	GETYPOS(y)	(((y)-w->BorderTop)/fonts.h)
#define	ISSELECTION	(selection.sx != selection.ex || selection.sy != selection.ey)
#define	OUTSIDESEL(x,y)	((y)<selection.sy || ((y)==selection.sy && (x) < selection.sx) ? -1 : ((y)>selection.ey || ((y)==selection.ey && (x) >= selection.ex) ? 1 : 0))
#define	INSEL(x,y)	(!OUTSIDESEL(x,y))
#define	SELTOPY		(selection.sy + (selection.sx ? 1 : 0))
#define	SELBOTY		(selection.ey - 1)
#define	POSITIVE(x)	((x) < 0 ? 0 : (x))
#define	ENDDIST(x,y)	(POSITIVE(selection.ey-(y))*disp.winwidth + disp.winwidth - 1 - (x) + selection.ex)
#define	STDIST(x,y)	(POSITIVE((y)-SELTOPY-1)*disp.winwidth + disp.winwidth - 1 - selection.sx + (x))
#define	NEAREND(x,y)	(ENDDIST(x,y) > STDIST(x,y))
#define	REVSEL		(selection.sy > selection.ey || (selection.sy == selection.ey && selection.sx > selection.ex))

/* The default drawing mode 
 */
#define DEFDRMD JAM2
/* This is the minimum number of pixels taken up by the left + right borders.
 * used to figure out the width of the window in columns of text.
 */
#define WBORDER 8
/* This is the number of extra pixels from the width when the size gadget takes up
 * a column
 */
#define SIZECOLUMN 14
/* Ditto top + bottom
 */
#define HBORDER 5
#define SIZEROW 8
/* This is a fudge-factor to make the menu title's select box be the right size */
#define MENUFUDGE 9
/* This is another fudge-factor, this time the approximate size of the "A" symbol in the menus */
#define MSYMSIZE 35

/* I didn't want to have to type this 
 */
#define FONTNAME tf_Message.mn_Node.ln_Name

/* Useful MinTerms 
 */
#define INVERT 0x50
#define WHITE  0x00
#define BLACK  0xff

/* Blitter call made to entire display.  Pass in the MinTerm. 
 */
#define	INVERTALL(mode) BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(0), POSY(0),\
                        modes.x, modes.y, (mode))

/* menu information
 */
#define	M_COPY	    0
#define	M_PASTE	    (M_COPY+1)
#define	M_MOVE	    (M_PASTE+1)
#define	M_EXECUTE   (M_MOVE+1)
#define	M_CLEARSCN  (M_EXECUTE+1)
#define M_FLUSH     (M_CLEARSCN+1)
#define M_BREAK     (M_FLUSH+1)
#ifdef OWNICONIFY
#define M_ICONIFY   (M_BREAK+1)
#define	M_QUIT	    (M_ICONIFY+1)
#else
#define	M_QUIT	    (M_BREAK+1)
#endif
#define	M_CURSOR    (M_QUIT+1)
#define	M_INVERT    (M_CURSOR+1)
#define	M_RESET	    (M_INVERT+1)
#define	M_TOGSPEED  (M_RESET+1)
#define	M_DELETE    (M_TOGSPEED+1)
#define	M_BACKSPACE (M_DELETE+1)
#define	M_80COL	    (M_BACKSPACE+1)
#define	M_EMACS	    (M_80COL+1)
#define M_UNLISTEN  (M_EMACS+1)

#define NUM_EMENU   M_CURSOR

struct Menu nationalmenu = {
   NULL, 120, 0, 55, 10, MENUENABLED, (BYTE *)"National", 0, 0, 0, 0, 0
};

struct Menu controlmenu = {
    &nationalmenu, 55, 0, 45, 10, MENUENABLED, (BYTE *)"Control", 0, 0, 0, 0, 0
};

struct Menu editmenu = {
    &controlmenu, 2, 0, 45, 10, MENUENABLED, (BYTE *)"Edit", 0, 0, 0, 0, 0
};


/* So I don't have to initialize EVERYthing 
 */
struct MenuItem protoitem = {
    NULL, 0, 0, 0, 0, HIGHCOMP | ITEMTEXT | ITEMENABLED, 0, NULL, NULL, 0, NULL, 0
};

struct IntuiText prototext = {
    0, 1, JAM2, 0, 0, NULL, NULL, NULL
};

/* The text of the menu followed by the HotKey for it.  Adding a menu option is as
 * easy as adding it here, and giving it a number (and changing NUM_EMENU if 
 * necessary), and then adding your handler to domenu()
 */
struct menudef {
    char *text, com, ser;
};

struct menudef EMenus[] = {
    {"Copy", 'c', 0},
    {"Paste", 'v', 0},
    {"Move", 'm', 0},
    {"Execute", 'e', 0},
    {"Clear Screen", 'l', 0},
    {"Flush", 'f', 0},
    {"Break", 'b', 1},
#ifdef OWNICONIFY
    {"Iconify", 'i', 0},
#endif
    {"Quit", 'q', 0},
    {NULL, 0, 0}
};

struct menudef CMenus[] = {
    {"change cursor", 0, 0},
    {"invert", 0, 0},
    {"soft reset", 0, 0},
    {NULL, 0, 0}
};

#define	TM_TOGSPEED	0
#define	TM_DELETE	1
#define	TM_BACKSPACE	2
#define	TM_80COL	3
#define	TM_EMACS	4
#define TM_UNLISTEN     5

/* The toggled menus.  These are automatically added after the end of the 
 * control menus.
 */
struct tmenu {
    char *off, *on, com;
    int curmode;
} ToggleMenus[] = {
    {"slow display",
     "fast display", 0, 0},

    {"Delete to Backspace",
     "Delete to Normal", 0, 0},
    {"Backspace to Delete",
     "Backspace to Normal", 0, 0},

    {"80 Columns",
     "Variable Columns", 0, 0},
    {"emacs mode on",
     "emacs mode off", 0, 0},
    {"unlisten",
     "listen", 0, 0},

    {NULL, NULL, 0, 0}
};


/* max font name length. I'm told that OpenDiskFont() can't handle more than
 * 30 anyway.
 */
#define	MAXFONT	    30
/* font information
 */
struct {
    char main[MAXFONT];			    /* main font base */
    char alt[MAXFONT];			    /* alternate character set font (as, ae) */
    char wide[MAXFONT];			    /* wide font */
    char top[MAXFONT];			    /* top of double height */
    char bot[MAXFONT];			    /* bottom of double height */
    char altwide[MAXFONT];		    /* alternate wide font */
    char alttop[MAXFONT];		    /* alternate top font */
    char altbot[MAXFONT];		    /* alternate bottom font */
    char narrow[MAXFONT];		    /* for 132 column mode */
    int usealgo[ATTRIB_MASK + 1];           /* which styles do we do algorithmically? */
    struct TextFont *attr[ATTRIB_MASK + 1]; /* array of fonts */
    int	 w;				    /* font width */
    int	 h;				    /* font height */
    int	 b;				    /* font baseline offset from top */
} fonts;

/* DNet specifics */
extern union {
    struct FileHandle *fh;
    long chan;
} io;

/* Amiga Specifics */
#define LBDOWN 	1
#define LBUP   	2
#define LMOVE	4

char *pubscname = NULL;


struct Window *w;
/* I keep my own copy of the Window's RastPort structure */
struct RastPort *rp, mrp;

struct TagItem nwtags[] = {
  { WA_PubScreenName, NULL },
  { WA_PubScreenFallBack, 1 },
  { TAG_DONE, 0 }
};

struct ExtNewWindow nw = {
	0, 0, 640, 200, 0, 1,
	CLOSEWINDOW | NEWSIZE | MENUPICK | MOUSEBUTTONS | MOUSEMOVE |
	    RAWKEY,
	SMART_REFRESH | WINDOWCLOSE | WINDOWSIZING | WINDOWDEPTH | 
            WINDOWDRAG | NOCAREREFRESH | ACTIVATE | WFLG_NW_EXTENDED,
	NULL,
	NULL,
	(UBYTE *)NULL,
	NULL,
        NULL,
	100, 30, -1, -1,
	WBENCHSCREEN,
	nwtags
};

/* window manager specific
 */
/* window manager specific
 */
struct {
    int	    speed;		/* speed of the display */
    int	    backspace;		/* if true, map backspace to delete */
    int	    delete;		/* if true, map delete to backspace */
    int	    col80;		/* if true, lock width to 80 col */
    int	    emacs;		/* if true, mouse clicks move cursor */
    int     national;		/* if true, use 7 bit char set */
    int     ctrl8bit;		/* if true, use 8 bit control chars */
    char   *progname;		/* pointer to program name */
    int	    x, y;		/* size of screen in wm coords */
} modes;
int visual;
struct {
    int	sx, sy,	ex, ey;			/* selection range */
} selection;
Boolean	DisplayOff = 1;		/* if true, the display is turned off */
Boolean	flashmode = 0;		/* current flash mode: 0 = not inverse */
Boolean cursormode = 0;         /* current cursor state 1=visible */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
char basetitle[MAXHOSTNAMELEN];
char Title[MAXHOSTNAMELEN + 16];
ULONG flashtime = 0;

/* natText -- nationalized Text function
 */
static LONG 
natText(struct RastPort *rp, STRPTR string, unsigned long count)
{
  if (modes.national) {
    UBYTE shown[MAXWIDTH + 1];
    long cnt = count;
    for (; --cnt >= 0; )
      shown[cnt] = national_table[(UBYTE)string[cnt]];
    return Text(rp, shown, count);
  } else 
    return Text(rp, string, count);
}

/* fill the screen with E's
 */
void
dstestscreen(void)
{
    DSTEST('E', disp.style);
    (void) redraw_display(1);
    dscursoroff();
}

/* resize the display
 */
int resize_display(void)
{
    register int style;
    int xsz, ysz;
    Boolean tempflag;

    xsz = w->Width - w->BorderRight - w->BorderLeft;
    ysz = w->Height - w->BorderBottom - w->BorderTop;
    modes.x = xsz;
    modes.y = ysz;
    xsz /= fonts.w;
    ysz /= fonts.h;

    if (modes.col80 && xsz > 80)
	xsz = 80;

    if (xsz == disp.winwidth && ysz == disp.winheight)
	return (DisplayOff = 0);

    if (DisplayOff = (xsz < MINWIDTH || ysz < MINHEIGHT)) {
	if (ysz > 0 && xsz > 15) {
	    if (disp.invert) {
		INVERTALL(BLACK);
	    }
	    else {
	        INVERTALL(WHITE);
	    }
	    cursormode = 0;
	    Move(rp, POSX(xsz)/2 - 7*fonts.w, POSY(ysz/2+1));
	    if(disp.invert) SetDrMd(rp, DEFDRMD | INVERSVID);
	    Text(rp, "Make me BIGGER", 14);
	    if(disp.invert) SetDrMd(rp, DEFDRMD);
	}
	return 0;
    }

    BOUNDSCK(xsz, MINWIDTH, disp.maxwidth);
    BOUNDSCK(ysz, MINHEIGHT, disp.maxheight);

    if ((disp.scrolltop == 0 && disp.scrollbot == disp.winheight-1) ||
	 disp.scrollbot >= ysz)
	disp.scrollbot = ysz-1;
    if (disp.scrolltop >= ysz)
	disp.scrolltop = 0;
    if (disp.cury >= ysz) {
	tempflag = DisplayOff;
	DisplayOff = 1;
	dsscroll(0,disp.winheight,disp.cury-ysz+1);
	DisplayOff = tempflag;
	disp.cury = ysz-1;
    }
    disp.winwidth = xsz, disp.winheight = ysz;
    dssizewindow(xsz, ysz, 0);

    DSRESIZE();

    niosize(ysz, xsz, modes.x, modes.y);
    strcpy(Title, basetitle);
    strcat(Title, " (");
    strcat(Title, itos(xsz));
    strcat(Title, " x ");
    strcat(Title, itos(ysz));
    strcat(Title, ")");
    SetWindowTitles(w, Title, (UBYTE *)-1);

    return 1;
}

/* redraw one line -- used for double width
 */
void
redraw_line(y)
int y;
{
    register int    x;
    register char   *temp;
    int		    homex, style, linelen;
    char	    *tempmin, tempc;
    short	    wflag = STYLEFLAG(y) & DOUBLE_MASK ? 2 : 1;

    SETFONT(0, y);
    BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(0), POSY(y), 
                      fonts.w*disp.winwidth, fonts.h, disp.invert ? BLACK : WHITE);
    if ((linelen = LINELEN(y)) >= disp.winwidth/wflag)
	linelen = disp.winwidth/wflag-1;
    if (linelen && !STYLEFLAG(y)) {
	temp = (tempmin = TEXTLINE(y)) + linelen;
	tempc = *temp;
	*temp = 0;
	PUTS(0, y, tempmin, strlen(tempmin));
	*temp = tempc;
    } else if (linelen && STYLEFLAG(y) > 0) {
	temp = tempmin = TEXTLINE(y);
	style = STYLEPOS(0, y);
	for (homex = x = 0; x <= linelen; x++) {
	    if (x < linelen && STYLEPOS(x, y) == style) {
		temp++;
		continue;
	    }
	    tempc = *temp;
	    *temp = 0;
	    if (style & ATTRIB_MASK)
		SETFONT(style, y);
	    SPUTS(homex, y, tempmin, x-homex, style);
	    *temp = tempc;
	    tempmin = temp++;
	    if (style & ATTRIB_MASK)
		SETFONT(0, y);
	    if (x < linelen) {
		homex = x;
		style = STYLEPOS(x, y);
	    }
	}
    }
    SETFONT(disp.style, disp.cury);
}

/* redraw the display
 */
int redraw_display(resize)
int resize;
{
    register int    x, y;
    register char   *temp;
    int		    homex, style, linelen, wflag;
    char	    *tempmin, tempc;

    if(resize || DisplayOff)
	resize = resize_display();
    if(DisplayOff)
	return (resize);
    SETFONT(0, 0);
    if (disp.invert) {
	INVERTALL(BLACK);
    } else
        INVERTALL(WHITE);
    cursormode = 0;
    for (y = 0; y < disp.winheight; y++) {
	wflag = STYLEFLAG(y) & DOUBLE_MASK ? 2 : 1;
	SETFONT(STYLEFLAG(y), y);
	if ((linelen = LINELEN(y)) >= disp.winwidth/wflag)
	    linelen = disp.winwidth/wflag-1;
	if (linelen && !STYLEFLAG(y)) {
	    temp = (tempmin = TEXTLINE(y)) + linelen;
	    tempc = *temp;
	    *temp = 0;
	    PUTS(0, y, tempmin, strlen(tempmin));
	    *temp = tempc;
	} else if (linelen && STYLEFLAG(y) > 0) {
	    temp = tempmin = TEXTLINE(y);
	    style = STYLEPOS(0, y);
	    for (homex = x = 0; x <= linelen; x++) {
		if (x < linelen && STYLEPOS(x, y) == style) {
		    temp++;
		    continue;
		}
		tempc = *temp;
		*temp = 0;
		if (style & ATTRIB_MASK)
		    SETFONT(style, y);
		SPUTS(homex, y, tempmin, x-homex, style);
		*temp = tempc;
		tempmin = temp++;
		if (style & ATTRIB_MASK)
		    SETFONT(0, y);
		if (x < linelen) {
		    homex = x;
		    style = STYLEPOS(x, y);
		}
	    }
	}
    }
    if (ISSELECTION) {
	invertSelection();
    } else
	dscursoron();
    SETFONT(disp.style, disp.cury);

    return (resize);
}

/* concat three strings to make fontname base
 */
void
setfontname(dest, base, flags)
char *dest, *base, *flags;
{
    strcpy(dest, base);
    strcat(dest, flags);
}

/* change the base font
 */
int
SetBaseFont(font)
char *font;
{
    char *c, *t, temp[MAXFONT];
    
    for(c=font, t=temp; (*t = *c) && *c != '/'; c++, t++);
    if(!(*c)) return -1;
    *t='\0', c++;
    strcpy(fonts.main, temp);
    setfontname(fonts.alt, temp, "v");
    setfontname(fonts.wide, temp, "w");
    setfontname(fonts.top, temp, "t");
    setfontname(fonts.bot, temp, "b");
    setfontname(fonts.altwide, temp, "vw");
    setfontname(fonts.alttop, temp, "vt");
    setfontname(fonts.altbot, temp, "vb");
    setfontname(fonts.narrow, temp, "n");

    fonts.h = atoi(c);
    return (0);
}

/* load in font #i
 */
struct TextFont *GetFonts(i, set)
int i, set;
{
    char    fontname[MAXFONT];
    int	    retries = 0, mem=0, disk=0;
    struct TextAttr ta;
    struct TextFont *tf=NULL, *tfd=NULL;

    i &= ATTRIB_MASK;
    switch (i & (DOUBLE1 | DOUBLE2 | ALTERNATE)) {
	case DOUBLE1 | DOUBLE2:
	    (void) strcpy(fontname, fonts.wide);
	    break;
	case DOUBLE1 | DOUBLE2 | ALTERNATE:
	    (void) strcpy(fontname, fonts.altwide);
	    break;

	case DOUBLE1:
	    (void) strcpy(fontname, fonts.top);
	    break;

	case DOUBLE1 | ALTERNATE:
	    (void) strcpy(fontname, fonts.alttop);
	    break;

	case DOUBLE2:
	    (void) strcpy(fontname, fonts.bot);
	    break;

	case DOUBLE2 | ALTERNATE:
	    (void) strcpy(fontname, fonts.altbot);
	    break;

	case ALTERNATE:
	    (void) strcpy(fontname, fonts.alt);
	    break;

	default:
	    (void) strcpy(fontname, fonts.main);
	    break;
    }
    ta.ta_YSize = fonts.h;
    ta.ta_Style = (i & STYLE_MASK);
    ta.ta_Flags = 0;
    do {
        strcat(fontname, ".font");
 	ta.ta_Name = (STRPTR)fontname;
	if(tf = OpenFont(&ta)) 
	    if(!lstrncmp(tf->FONTNAME, fontname, strlen(tf->FONTNAME))) 
	        if(tf->tf_YSize == ta.ta_YSize) {
		    mem = 1;
		    if(tf->tf_Style == ta.ta_Style) 
		        mem = 2;
		}
	if((mem < 2) && (tfd = OpenDiskFont(&ta))) 
	    if(!lstrncmp(tfd->FONTNAME, fontname, strlen(tfd->FONTNAME))) 
	        if(tfd->tf_YSize == ta.ta_YSize) 
		    disk = 1;
  	if(disk || mem) {
	    if(disk && mem) 
	        if(tfd->tf_Style > tf->tf_Style) 
		    mem = 0;
	    if(!mem) {
	        if(tf) CloseFont(tf);
		tf = tfd;
	    } else
	        if(tfd) CloseFont(tfd);
    	    fonts.usealgo[i] = (~(tf->tf_Style) & ta.ta_Style);
	    fonts.attr[i] = tf;
	    if(set) SetFont(rp, fonts.attr[i]);
	    return fonts.attr[i];
	    break;
	}
	if(tf) {
	    CloseFont(tf);
	    tf = NULL;
	}
	if(tfd) {
	    CloseFont(tfd);
	    tf = NULL;
	}
	if (i & ALTERNATE && retries == 0)
	    (void) strcpy(fontname, fonts.main);
	else if (i & ALTERNATE && retries == 1)
	    (void) strcpy(fontname, "niftyv");
	else if (retries == 0)
	    (void) strcpy(fontname, "nifty"), retries++;
	else {
	    (void) strcpy(fontname, "topaz");
	    ta.ta_YSize = 8;
	}
    } while (++retries <= 3);
}

/* set up for the needed fonts
 */
void
SetFonts(void)
{
    int	    i;
    struct TextFont *tf;

    for (i=0; i<=ATTRIB_MASK; i++) {
	fonts.attr[i] = NULL;
	fonts.usealgo[i] = 0;
    }	
    tf = GetFonts(0, 0);
    fonts.w = tf->tf_XSize;
    fonts.h = tf->tf_YSize;
    fonts.b = tf->tf_Baseline;
}

/* put a string at x, y
 */
void
wmputs(x, y, stng, style)
int	x, y;
unsigned char	*stng;
int	style;
{
    int len, pos; 
    char temp[MAXWIDTH];
    int wflag = STYLEFLAG(y) & DOUBLE_MASK ? 2 : 1;

    if (disp.insert) {
	register int linelen = LINELEN(y);
	for (pos = 0; pos < MAXWIDTH-1 && (temp[pos] = stng[pos]); pos++);
	for (len = x; pos < MAXWIDTH-1 && len < linelen; len++)
	    temp[pos++] = TEXTPOS(len, y);
	temp[MAXBOUND(pos,disp.winwidth/wflag-1)] = '\0';
	stng = temp;
    } 
    {
        register int tx = SLIMIT((x),disp.winwidth);
        register int ty = SLIMIT((y),disp.winheight);
        register char *temp = (stng);
        if (temp != NULL && *temp) {
	    for ((len) = LINELEN(ty); (len) < tx; (len)++) {
	        TEXTPOS((len), ty) = ' ';
	        STYLEPOS((len), ty) = 0;
	    }
	    /* stuff the characters into the buffer */
	    for ((len)=0;*temp;) {
	        (len)++;
	        TEXTPOS(tx,ty) = *temp++;
	        STYLEPOS(tx++,ty) = (style);
		if (tx >= disp.winwidth) {
		    LINELENST(ty) = disp.winwidth;
		    tx=disp.winwidth-1;
		    break;
	        }
	    }
	    if (LINELEN(ty) < tx)
	        LINELENST(ty) = tx;
	    STYLEFLAG(y) |= (style);
        }
    }

    if (DisplayOff)
	return;

#if 0    
    BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(x*wflag), POSY(y), 
                      fonts.w*len*wflag, fonts.h, disp.invert ? BLACK : WHITE);
#endif

    SPUTS(x, y, stng, len, style);
}

/* grab a selection -- caller must free the buffer when finished.
 */
char *grabSelection()
{
    char *buffer, *temp;
    int sx = selection.sx, ex = selection.ex, endx, endl;
    int sy = selection.sy, ey = selection.ey;
    
    if (!ISSELECTION)
	return (NULL);

    if (sy > ey || (sy == ey && sx > ex)) {
	register int tmp;

	tmp = sy, sy=ey, ey = tmp;
	tmp = sx, sx=ex, ex = tmp;
    }

    temp = buffer = AllocVec(((ey - sy + 2) * (disp.winwidth + 1)), MEMF_CLEAR);

    for (;sy <= ey; sy++) {
	endx = LINELEN(sy);
	if (sy == ey) {
	    MAXBOUNDCK(endx, ex);
	    endl = ex;
	} else
	    endl = disp.winwidth;
	for (;sx < endx; sx++)
	    *temp++ = TEXTPOS(sx, sy);
	sx = 0;
	if (endl > LINELEN(sy))
	    *temp++ = '\n';
    }
    *temp = 0;

    return (buffer);
}

/* invert/extend the selection range
 */
void
extendSelection(sx, sy, ex, ey)
int sx, sy, ex, ey;
{
    int topy, boty;

    if (sy > ey || (sy == ey && sx > ex)) {
	register int tmp;

	tmp = sy, sy=ey, ey = tmp;
	tmp = sx, sx=ex, ex = tmp;
    }
    topy = sy + (sx ? 1 : 0);
    boty = ey - 1;
    if (sx == ex && sy == ey)
	return;
    if (sy == ey) 
        BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(sx), POSY(sy), 
	                  fonts.w*(ex-sx), fonts.h, INVERT);
    else {
	if (sx && sx < disp.winwidth)
	    BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(sx), POSY(sy),
	                      fonts.w*(disp.winwidth-sx), fonts.h, INVERT);
	if (boty-topy+1 > 0)
	    BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(0), POSY(topy),
	                  fonts.w*disp.winwidth, fonts.h*(boty-topy+1), INVERT);
	if (ex)
	    BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(0), POSY(ey), fonts.w*ex,
	                      fonts.h, INVERT);
    }
}

void
invertSelection()
{
    extendSelection(selection.sx, selection.sy, selection.ex, selection.ey);
}

void
killSelection()
{
    invertSelection();
    selection.ey=selection.sy;
    selection.ex=selection.sx;
}

/* 
 * toggle a menu item
 */
void
changemenu(which)
int which;
{
    struct MenuItem *mi;
    char *clear, *set, *temp;
    long width=0;

    ClearMenuStrip(w);
    clear = ToggleMenus[which].off;
    set = ToggleMenus[which].on;
    if (!(ToggleMenus[which].curmode = !ToggleMenus[which].curmode))
	temp = clear, clear = set, set = temp;

    for(mi=controlmenu.FirstItem; strcmp(((struct IntuiText *)mi->ItemFill)->IText, clear);
          mi=mi->NextItem);
    ((struct IntuiText *)mi->ItemFill)->IText = (UBYTE *)set;
    for(mi=controlmenu.FirstItem; mi; mi=mi->NextItem) {
        int wid;
        if((wid = (TextLength(&(w->WScreen->RastPort), (char *)(((struct IntuiText *)mi->ItemFill)->IText), 
		   strlen(((struct IntuiText *)mi->ItemFill)->IText)) + ((mi->Flags & COMMSEQ) ? 
		   MSYMSIZE + TextLength(&(w->WScreen->RastPort), &(mi->Command), 1) : 0))) > width)
	    width = wid;
    }
    for(mi=controlmenu.FirstItem; mi; mi=mi->NextItem) 
        mi->Width = width;
    SetMenuStrip(w, &editmenu);
}

/* set a menu item
 */
void
setmenu(which, val)
int which, val;
{
    ToggleMenus[which].curmode = val;
}

/* Menu dispatcher
 */
int domenu(msg, buf, max)
struct IntuiMessage *msg;
char *buf;
int max;
{
    int count = 0;
    char *selstring, *temp, replied = 0;
    long mnum = MENUNUM(msg->Code);
    long item = ITEMNUM(msg->Code);
    long menu = NUM_EMENU * mnum + item;

    if (mnum == 2) {
      donatmenu(msg, item - 1);
      return (0);
    }
    if (iotype != serial && mnum == 0 && (item >= M_BREAK)) menu++;
    if ((msg->Code == MENUNULL) || (mnum == NOMENU) || (item == NOITEM))
        return (0);
    switch (menu) {
        case M_CLEARSCN:
            dscursoroff();
	    if (disp.cury) {
		dsscroll(0,disp.winheight,disp.cury);
		disp.cury=0;
	    }
	    dsfunction(ERASETO_EOS);
	    dscursoron();
	    break;

	case M_PASTE:
	    count = ClipPaste(buf, max);
	    break;

	case M_MOVE: case M_EXECUTE:
	    if ((temp = selstring = grabSelection())) {
		while ((*buf++ = *temp++) && ++count < max);
		FreeVec(selstring);
		if (menu == M_EXECUTE && count <= max)
		    *(buf-1) = '\n', ++count;
	    }
	    break;

	case M_COPY:
	    if (ISSELECTION) {
		temp = selstring = grabSelection();
	        ClipCut(temp);
		FreeVec(selstring);
	    }
	    break;

	case M_TOGSPEED:
	    modes.speed = !modes.speed;
	    changemenu(TM_TOGSPEED);
	    break;

	case M_INVERT:
	    dsinvert(disp.invert ? INVERT_OFF : INVERT_ON);
	    break;

	case M_RESET:
	    dscursoroff();
	    dsfunction(RESET_DISPLAY);
	    dscursoron();
	    break;

        case M_FLUSH:
	    nioctl(CIO_FLUSH, 0, 0);
	    break;

	case M_BREAK:
	    nioctl(CIO_BREAK, 0, 0);
	    break;

#ifdef OWNICONIFY
	case M_ICONIFY:
	    ReplyMsg((struct Message *)msg);
	    replied = 1;
   	    unmapwindow();
	    iconify();
	    break;
#endif	    
	case M_QUIT:
	    ReplyMsg((struct Message *)msg);
	    nclose();
	    dsquit();
	    amigaquit();
	    exit(0);
	    break;

	case M_BACKSPACE:
	    modes.backspace = !modes.backspace;
	    changemenu(TM_BACKSPACE);
	    break;

	case M_DELETE:
	    modes.delete = !modes.delete;
	    changemenu(TM_DELETE);
	    break;

	case M_80COL:
	    modes.col80 = !modes.col80;
	    changemenu(TM_80COL);
	    if(modes.col80) dssizewindow(80, 0, 1);
	    else WindowLimits(w, 0, 0, -1, -1);
	    redraw_display(1);
	    break;

	case M_CURSOR:
	    dscursoroff();
	    if(disp.visual & CUR_BLOCK)
	    	disp.visual &= ~CUR_BLOCK;
	    else
	    	disp.visual |= CUR_BLOCK;
	    dscursoron();
	    break;

	case M_EMACS:
	    modes.emacs = !modes.emacs;
	    changemenu(TM_EMACS);
	    break;

	case M_UNLISTEN:
	    if (nabort()) {
		changemenu(TM_UNLISTEN);

		/* Loop until this menu is again selected */
		do {
		    if (msg)
		        ReplyMsg(msg);
		    else
		        WaitPort(w->UserPort);
		    msg = (struct IntuiMessage *)GetMsg(w->UserPort);
		} while (!msg ||
			 msg->Class != MENUPICK ||
			 MENUNUM(msg->Code) * NUM_EMENU + 
			 ITEMNUM(msg->Code) != M_UNLISTEN ||
			 !nunabort());
		changemenu(TM_UNLISTEN);
	    }
	    break;
    }
    if(!replied) ReplyMsg((struct Message *)msg);
    return (count);
}

/*
 * Build up a national menu
 */
void
setup_natmenu(void)
{
  long width; long me = 1;
  struct nation *n = nations;
  struct MenuItem *li;
    
  nationalmenu.Width = 
    TextLength(&(w->WScreen->RastPort), nationalmenu.MenuName, 
	       strlen(nationalmenu.MenuName)) + MENUFUDGE;
    
  li = initmenu(&nationalmenu, NULL, "National", 'n');
  li->Flags |= MENUTOGGLE | CHECKIT;
  if (modes.national)
    li->Flags |= CHECKED;
  width = li->Width;
  li->MutualExclude = 0;

  while(n->n_name) {
      li = initmenu(&nationalmenu, li, n->n_name, 0);
      me <<= 1;
      li->MutualExclude = ~(1|me);
      li->Flags |= CHECKIT;
      if (n == used_nation) 
	li->Flags |= CHECKED;
      width = (li->Width > width) ? li->Width : width;
      n++;
  }
  width += CHECKWIDTH;

  /* Set constant width */
  for (li = nationalmenu.FirstItem; li; li=li->NextItem) {
    li->Width = width;
    ((struct IntuiText *)li->ItemFill)->LeftEdge = CHECKWIDTH;
  }
}

/*
 *  Handle the national menu
 */
void
donatmenu(struct IntuiMessage *msg, int nation)
{
  ReplyMsg((struct Message *)msg);

  if (nation < 0) {
    modes.national = !modes.national;
  } else {
    init_national(nation);
  }
  (void) redraw_display(1);
}

/*
 * Dispatch the national menu
 */
void
quitnatmenu()
{
    struct MenuItem *mi;

    mi = nationalmenu.FirstItem;
    while (mi) {
	struct MenuItem *omi = mi;
	mi = mi->NextItem;
	FreeVec(omi);
    }
}

/* Mouse selection handler
 */
void
doselection(action, x, y)
int action, x, y;
{
    int selection_on = ISSELECTION;

    if (action == LBDOWN) {
	if (ISSELECTION)
	    killSelection();
	selection.sx = selection.ex = x;
	selection.sy = selection.ey = y;

    } 
    else if((action == LBUP) || (action == LMOVE)) {
	if (selection.ex != x || selection.ey != y)
	    extendSelection(selection.ex,selection.ey,x,y);
	selection.ex=x;
	selection.ey=y;
	if(action == LBUP) {
		register int tmp;
		tmp=selection.sy, selection.sy=selection.ey, selection.ey=tmp;
		tmp=selection.sx, selection.sx=selection.ex, selection.ex=tmp;
	}
    }
    if (ISSELECTION ^ selection_on)
	dscursorblink();	
}

/* cursor display routine
 */
void
dscursoron(void)
{
    if (!DisplayOff) {
	int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;
	if(!cursormode) {
	    cursormode = 1;
	    if (disp.visual == CUR_BLOCK) {
		BltBitMapRastPort(rp->BitMap, 1, 1, rp, POSX(disp.curx*wflag),
				  POSY(disp.cury), fonts.w*wflag, fonts.h, INVERT);
	    }
	    else if (disp.visual == CUR_UNDERLINE) {
		BltBitMapRastPort(rp->BitMap, 1, 1, rp, POSX(disp.curx*wflag),
				  POSY(disp.cury)+fonts.b+1, fonts.w*wflag, 1, INVERT);
	    }
	}
    }
}

void
dscursorblink(void)
{
    if (!DisplayOff) {
	int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;
	cursormode = !cursormode;
	if (disp.visual == CUR_BLOCK) {
            BltBitMapRastPort(rp->BitMap, 1, 1, rp, POSX(disp.curx*wflag),
		              POSY(disp.cury), fonts.w*wflag, fonts.h, INVERT);
        }
	else if (disp.visual == CUR_UNDERLINE) {
            BltBitMapRastPort(rp->BitMap, 1, 1, rp, POSX(disp.curx*wflag),
		              POSY(disp.cury)+fonts.b+1, fonts.w*wflag, 1, INVERT);
        }
    }
}

void
dscursoroff(void)
{
    if (!DisplayOff) {
	int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;
	if (ISSELECTION) 
	    killSelection();
	if(cursormode) {
	    cursormode = 0;
	    if (disp.visual == CUR_BLOCK) {
		BltBitMapRastPort(rp->BitMap, 1, 1, rp, POSX(disp.curx*wflag),
				  POSY(disp.cury), fonts.w*wflag, fonts.h, INVERT);
	    }
	    else if (disp.visual == CUR_UNDERLINE) {
		BltBitMapRastPort(rp->BitMap, 1, 1, rp, POSX(disp.curx*wflag),
				  POSY(disp.cury)+fonts.b+1, fonts.w*wflag, 1, INVERT);
	    }
	}
    }
}


/* scrolling routines
 */
void
dsscroll(y, numlines, amount)
int y, numlines, amount;
{
    if(ABS(amount) < numlines && !DisplayOff) {
        if(disp.invert) SetBPen(rp, 1);	
	ScrollRaster(rp, 0, amount*fonts.h, POSX(0), POSY(y), 
	             POSX(disp.winwidth),
		     POSY(y)+numlines*fonts.h-1);
        if(disp.invert) SetBPen(rp, 0);	
    } else if(ABS(amount) >= numlines) 
        BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(0), POSY(y), 
	             fonts.w*disp.winwidth, numlines*fonts.h, 
		     disp.invert ? BLACK : WHITE);

    if(ABS(amount)<(numlines)) {
        register char *templ;
        register short *templs;
        register int i, maxy;
        if(amount>0) 
            for(i=y,maxy=y+numlines-amount;i<maxy;i++){
                templ=disp.plines[i],templs=disp.pstyles[i];
                disp.plines[i]=disp.plines[i+amount];
		disp.pstyles[i]=disp.pstyles[i+amount];
		disp.plines[i+amount]=templ,disp.pstyles[i+amount]=templs;
	}
	else 
	    for(i=y+numlines-1,maxy=y-amount;i>=maxy;i--){
		templ=disp.plines[i],templs=disp.pstyles[i];
		disp.plines[i]=disp.plines[i+amount];
		disp.pstyles[i]=disp.pstyles[i+amount];
	    	disp.plines[i+amount]=templ,disp.pstyles[i+amount]=templs;
	}
    }

    if (amount > 0) {
	MAXBOUNDCK(amount, numlines);
	DSEMPTYLINE(y+numlines-amount,y+numlines);
    } else {
	if ((amount = -amount) > numlines)
	    amount = numlines;
	DSEMPTYLINE(y,y+amount);
    }
}

void
dssizewindow(width, height, maxwidthp)
int width, height, maxwidthp;
{
    int ow = w->Width, nw;
    int oh = w->Height, nh;
    
    if(width)
        nw = MIN(width * fonts.w + w->BorderLeft + w->BorderRight, w->WScreen->Width);
    else
        nw = ow;

    if(height)
        nh = MIN(height * fonts.h + w->BorderTop + w->BorderBottom, w->WScreen->Height);
    else 
        nh = oh;
    
    if((nw-ow) || (nh-oh)) {
        SizeWindow(w, nw - ow, nh - oh);
        WaitPort(w->UserPort);
    }
    if(maxwidthp) {
        WindowLimits(w, 0, 0, nw, 0);
    }
}
    
/* show additional switches for display manager
 */
void
dsshowusage(void)
{
    puts("Amiga specific options are:\n"
         "  -80                set to fixed 80 columns.\n"
         "  -f <font>/<size>   set the name and size of the default font.\n"
         "  -ic                start up with invisible cursor.\n"
         "  -p <name>          set the program name.\n"
         "  -sl                start up in slow mode.\n"
         "  -g <l>/<t>/<w>/<h> set up window geometry.");
}

void
dogeometry(gs)
char *gs;
{
    char geo[30];
    
    if (gs != NULL && gs[0] != '\0') {
        char *l, *t, *w, *h, *c; 
	int i=0;
        strcpy(geo, gs);
	for(l=c=geo; *c; c++) {
	    if(*c == '/') {
	        *c = '\0';
		switch (i++) {
		    case 0: t = c+1; break;
		    case 1: w = c+1; break;
		    case 2: h = c+1; break;
		}
	    }
	}
	nw.LeftEdge = atoi(l);
	modes.y = atoi(t);
	disp.winwidth = modes.col80 ? 80 : atoi(w);
	disp.winheight = atoi(h);
    } else {
        nw.LeftEdge = nw.TopEdge = modes.y = 0;
	disp.winwidth  = disp.winheight = -1;
    }
}

/* Set basetitle 
 */
void
setbasetitle(const char * new)
{
  static int already_set = 0;

  if (!already_set) {
    already_set = 1;
    strncpy(basetitle, new, sizeof(basetitle));
  }
}

/* parse one argument from the command line
 */
#define ARGUMENT (*cont ? cont : (used++, next))
int
dsparsearg(c, cont, next)
char c, *cont, *next;
{
    int	used = 0;

    switch (c) {
	case 'p':
	    setbasetitle(ARGUMENT);
	    break;
	    
	case 'i':
	    disp.visual = CUR_BLOCK | CUR_INVISIBLE;
	    break;

	case '8':
	    ToggleMenus[TM_80COL].curmode = modes.col80 = 1;
	    break;

	case 's':
	    ToggleMenus[TM_TOGSPEED].curmode = modes.speed = 1;
	    break;

	case 'f':
	    if (SetBaseFont(ARGUMENT) < 0)
		fatalError("Badly formed font name.\n");
	    break;
	
	case 'g':
	    dogeometry(ARGUMENT);
	    break;

	case 'v':
	    disp.invert = 1;
	    break;

	case 'S':
	    if (pubscname) free(pubscname);
	    pubscname = ARGUMENT;
	    break;

	default:
	    used--;
	    break;
    }

    return (used+1);
}

/* pre-display initialize routine -- setting defaults
 */
void
dsinit(void)
{
    char *temp;
    
    /* set wm specific characteristics */
    modes.backspace = !getprofileswitch(PROGNAME ".normalBackspace", 1);
    setmenu(TM_BACKSPACE, modes.backspace);
    modes.delete = !getprofileswitch(PROGNAME ".normalDelete", 1);
    setmenu(TM_DELETE, modes.delete);
    modes.col80 = getprofileswitch(PROGNAME ".fixedColumns", 0);
    setmenu(TM_80COL, modes.col80);
    modes.emacs = getprofileswitch(PROGNAME ".emacsmode", 0);
    disp.blink = getprofileswitch(PROGNAME ".cursorblink", 0);
    setmenu(TM_EMACS, modes.emacs);
    modes.speed = 0;
    modes.national = getprofileswitch(PROGNAME ".national", 0);
    modes.ctrl8bit = getprofileswitch(PROGNAME ".ctrl8bit", 1);

    /* set the default public screen */
    if (temp = getprofile(PROGNAME ".pubScreenName")) {
      if (pubscname = malloc(strlen(temp) + 1)) 
	strcpy(pubscname, temp);
    }

    /* set the default font values */
    if(((temp = getprofile(PROGNAME ".basefont")) == NULL) || 
       SetBaseFont(temp) < 0)
	(void) SetBaseFont(DEFAULTFONT);

    /* set the default geometry */
    dogeometry(getprofile(PROGNAME ".geometry"));
    if(temp = getprofile(PROGNAME ".sizegadget")) {
        if((*temp == 'c') || (*temp == 'C')) nw.Flags |= SIZEBRIGHT;
        else if((*temp == 'r') || (*temp == 'R')) nw.Flags |= SIZEBBOTTOM;
        else if((*temp == 'n') || (*temp == 'N')) nw.Flags &= ~WINDOWSIZING;
    }

    visual = CUR_BLOCK;
    if(temp = getprofile(PROGNAME ".cursor")) {
	if((*temp == 'u') || (*temp == 'U')) visual = CUR_UNDERLINE;
	else if((*temp == 'i') || (*temp == 'I')) visual = CUR_INVISIBLE;
    }
    disp.invert = getprofileswitch(PROGNAME ".inverse", 0);

    natinit();
}

struct MenuItem *initmenu(menu, last, str, com)
struct Menu *menu;
struct MenuItem *last;
char *str, com;
{
    struct IntuiText *it;
    struct MenuItem *mi;

    mi = (struct MenuItem *)AllocVec(sizeof(*mi) + sizeof(*it), MEMF_PUBLIC);
    if (mi == NULL) 
      return mi;
    it = (struct IntuiText *)(mi + 1);
    *mi = protoitem; *it = prototext;
    if (last) last->NextItem = mi;
    else menu->FirstItem = mi;
    mi->TopEdge = last ? last->TopEdge + w->WScreen->RastPort.TxHeight + 1 : 0;
    mi->Height = w->WScreen->RastPort.TxHeight + 1; 
    mi->Width = TextLength(&(w->WScreen->RastPort), str, strlen(str));
    mi->ItemFill = (APTR)it;
    if (com) {
        mi->Command = com;
	mi->Flags |= COMMSEQ;
	mi->Width += MSYMSIZE + TextLength(&(w->WScreen->RastPort), &(mi->Command), 1);
    }
    it->TopEdge = 1;
    it->IText = (UBYTE *)str;
    return mi;
}

void
setupmenus(void)
{
    long width=0;
    struct menudef *newmenu;
    struct tmenu *tm;
    struct MenuItem *li=NULL;
    
    editmenu.Width = TextLength(&(w->WScreen->RastPort), editmenu.MenuName, strlen(editmenu.MenuName)) + MENUFUDGE;
    controlmenu.Width = TextLength(&(w->WScreen->RastPort), controlmenu.MenuName, strlen(controlmenu.MenuName)) + MENUFUDGE;
    
    newmenu = EMenus;
    while(newmenu->text) {
	if(iotype == serial || !(newmenu->ser)) {
	    li = initmenu(&editmenu, li, newmenu->text, newmenu->com);
	    width = (li->Width > width) ? li->Width : width;
	}
	newmenu++;
    }
    for(li=editmenu.FirstItem; li; li=li->NextItem)
        li->Width = width;

    newmenu = CMenus;
    li = NULL;
    width = 0;
    while(newmenu->text) {
	li = initmenu(&controlmenu, li, newmenu->text, newmenu->com);
	width = (li->Width > width) ? li->Width : width;
	newmenu++;
    }
    tm = ToggleMenus;
    while (tm->off) {
	if((iotype == serial && shared) || ((tm+1)->off)) {
	    li = initmenu(&controlmenu, li, (tm->curmode ? tm->on : tm->off), tm->com);
	    width = (li->Width > width) ? li->Width : width;
	}
	tm++;
    }
    for(li=controlmenu.FirstItem; li; li=li->NextItem)
        li->Width = width;

    setup_natmenu();
}

#ifdef OWNICONIFY
/* Do Not do ANYTHING after calling unmapwindow!! This is only used for 
 * iconification.  Saves the state information, then closes the window.
 */
void
unmapwindow(void)
{
    struct IntuiMessage *msg;
    
    while(msg = (struct IntuiMessage *)GetMsg(w->UserPort)) ReplyMsg((struct Message *)msg);
    DisplayOff = 1;
    ClearMenuStrip(w);
    nw.TopEdge = w->TopEdge;
    nw.LeftEdge = w->LeftEdge;
    nw.Width = w->Width;
    nw.Height = w->Height;
    nw.MaxWidth = w->MaxWidth;
    nw.MaxHeight = w->MaxHeight;
    nw.MinWidth = w->MinWidth;
    nw.MinHeight = w->MinHeight;
    nw.Flags = w->Flags;
    nw.Title = w->Title;
    CloseWindow(w);
}

/* Undoes a call to unmapwindow() everything should return to the way it was
 * before the call 
 */
void
mapwindow(void) 
{
    if(!(w = OpenWindow(&nw))) 
        fatalError("Can't reopen window!!!!!");
    /* We have a new RastPort!! */
    mrp = *(w->RPort);
    rp = &mrp;
    SetAPen(rp, 1);
    SetBPen(rp, 0);
    SetWrMsk(rp, 1);
    SetDrMd(rp, DEFDRMD);

    SetMenuStrip(w, &editmenu);
    /* Now, everything should be back the way it was before */
    DisplayOff = 0;
    redraw_display(1);
}
#endif
    
/* display start routine -- sets up display
 */
void
dsstart(void)
{
    struct tmenu *togmenu = ToggleMenus;
    struct Screen s;
    int wborder=WBORDER, hborder=HBORDER;
    
    nwtags[0].ti_Data = (ULONG)pubscname;

    if(nw.Flags & WINDOWSIZING) 
        if(nw.Flags & SIZEBBOTTOM) 
	    hborder += SIZEROW;
	else 
	    wborder += SIZECOLUMN;

    SetFonts();

    if(modes.col80) 
        disp.winwidth = 80;
    GetScreenData((char *)&s, sizeof(s), WBENCHSCREEN, NULL);
    hborder += s.Font->ta_YSize;
    nw.TopEdge = (modes.y == -1) ? s.BarHeight+1 : modes.y;
    nw.Width = (disp.winwidth == -1) ? s.Width - nw.LeftEdge : MIN(disp.winwidth * fonts.w + wborder, s.Width - nw.LeftEdge);
    nw.Height = (disp.winheight == -1) ? s.Height - nw.TopEdge : MIN(disp.winheight * fonts.h + hborder, s.Height - nw.TopEdge);
    if(modes.col80) nw.MaxWidth = nw.Width;

    /* This has no effect iff title is already set */
    setbasetitle(PROGNAME);
    nw.Title = (UBYTE *)basetitle;

    if ((w = OpenWindow(&nw)) == NULL)
	fatalError("Can't make new window");
    DisplayOff = 0;
    mrp = *(w->RPort);
    rp = &mrp;
    SetAPen(rp, 1);
    SetBPen(rp, 0);
    SetWrMsk(rp, 1);
    SetDrMd(rp, DEFDRMD);

    setupmenus();
    SetMenuStrip(w, &editmenu);
    
    disp.style = 0;

    allocate_display(MAXWIDTH, MAXHEIGHT);
		     
    reset_display(0);
    Move(rp, 0, 0);
    BltBitMapRastPort(rp->BitMap, 0, 0, rp, w->BorderLeft, w->BorderTop, 
                      w->Width-w->BorderLeft-w->BorderRight,
		      w->Height-w->BorderTop-w->BorderBottom, WHITE);
    selection.sx = selection.sy = selection.ex = selection.ey = 0;
    disp.curx = disp.cury = disp.winheight = disp.winwidth = 0;
    redraw_display(1);
}

/* close window
 */
void
dsquit(void)
{
    struct MenuItem *mi;
    int i;
    
    ClipClose();
    free_display();
    if (w) {
	ClearMenuStrip(w);

	mi = editmenu.FirstItem;
	while (mi) {
	    struct MenuItem *omi = mi;
	    mi = mi->NextItem;
	    FreeVec(omi);
	}

	mi = controlmenu.FirstItem;
	while (mi) {
	    struct MenuItem *omi = mi;
	    mi = mi->NextItem;
	    FreeVec(omi);
	}

	quitnatmenu();

	for (i=0; i<=ATTRIB_MASK; i++) 
	    if(fonts.attr[i]) CloseFont(fonts.attr[i]);
	ModifyIDCMP(w, 0);
	CloseWindow(w);
    }
    DisplayOff = 1;
}

domouse(msg, buf, max) 
struct IntuiMessage *msg;
char *buf;
int max;
{
    int x, y, count=0;

    x = GETXPOS(msg->MouseX);
    y = GETYPOS(msg->MouseY);
    BOUNDSCK(x, 0, disp.winwidth);
    BOUNDSCK(y, 0, disp.winheight-1);

    if(msg->Class == MOUSEBUTTONS) {
        if(msg->Code == SELECTDOWN) {
	    ReportMouse(TRUE, w);
	    doselection(LBDOWN, x, y);
	    if(modes.emacs) {
	        while (y > disp.cury)
		    *buf++ = CTRL('N'), y--, count++;
	        while (y < disp.cury)
		    *buf++ = CTRL('P'), y++, count++;
	        while (x > disp.curx)
		    *buf++ = CTRL('F'), x--, count++;
	        while (x < disp.curx)
		    *buf++ = CTRL('B'), x++, count++;
  	    }
	}
	if(msg->Code == SELECTUP) {
	    ReportMouse(FALSE, w);
	    doselection(LBUP, x, y);
	}
    }
    if(msg->Class == MOUSEMOVE) 
        doselection(LMOVE, x, y);
    return count;
}

/* 
 * Do key converting
 */
long DeadKeyConvert(msg, kbuffer, kbsize, kmap)
struct IntuiMessage *msg;
char *kbuffer;
long kbsize;
struct KeyMap *kmap;
{
    struct InputEvent ievent;
    
    if(!ConsoleDevice) {
        if(OpenDevice("console.device", -1L, (struct IORequest *)&console_IO, 0L)) {
	    puts("Failure to open console.device");
	    dsquit();
	    amigaquit();
	    exit(-1);
	}	    
	ConsoleDevice = console_IO.io_Device;
    }
	
    if(msg->Code & IECODE_UP_PREFIX)
        return 0;

    ievent.ie_Code = msg->Code;
    
    /* swap BS and DEL */
    if (ievent.ie_Code == 70 && modes.delete)
      ievent.ie_Code = 65;
    else if (ievent.ie_Code == 65 && modes.backspace)
      ievent.ie_Code = 70;

    ievent.ie_NextEvent = 0;
    ievent.ie_SubClass = 0;
    ievent.ie_Class = IECLASS_RAWKEY;
    ievent.ie_Qualifier = msg->Qualifier;
    ievent.ie_position.ie_addr = *((APTR *)msg->IAddress);
    /* This waz a mysterious bug */
    if (altismeta) {		/* left alt is meta char */
        ievent.ie_Qualifier &= ~IEQUALIFIER_LALT;
        ievent.ie_position.ie_dead.ie_prev1DownQual &= ~IEQUALIFIER_LALT;
        ievent.ie_position.ie_dead.ie_prev2DownQual &= ~IEQUALIFIER_LALT;
    }
    return (RawKeyConvert(&ievent, kbuffer, kbsize, kmap));
}

/* Handle an Intuition RAWKEY message.  The function keys, cursor keys, and keypad
 * may have either the native Amiga values, or the standard vt100 values.
 */
#define ISCURSOR(c) (((c)>75) && ((c)<80))
#ifdef OWNICONIFY 
#define ICONIFY(c, q) (((q) & AMIGALEFT) && ((q) & AMIGARIGHT) && ((c) == 23))
#endif
char *vt100cstrs[] = {"\033[A", "\033[B", "\033[C", "\033[D"};
char *ansicstrs[] = {"\033OA", "\033OB", "\033OC", "\033OD"};
char *vt52cstrs[] = {"\033A", "\033B", "\033C", "\033D"};
void
do_RawKey(msg)
struct IntuiMessage *msg;
{
    UBYTE buf[10], send[20], *c, *e, *s;
    int count;
    static UWORD iconx=0, icony=0;
    
    if(ISCURSOR(msg->Code)) {
        switch (cursormap) {
	    case CURSOR_VT100:
	        nwrite(vt100cstrs[msg->Code-76], 3);
		break;
	    case CURSOR_ANSI:
	        nwrite(ansicstrs[msg->Code-76], 3);
	        break;
	    case CURSOR_VT52:
	        nwrite(vt52cstrs[msg->Code-76], 2);
		break;
        }
	ReplyMsg((struct Message *)msg);
	return;
    }
#ifdef OWNICONIFY
    if(ICONIFY(msg->Code, msg->Qualifier)) {
	ReplyMsg((struct Message *)msg);
	unmapwindow();
        iconify();
	return;
    }	
#endif
    if (count = DeadKeyConvert(msg, (UBYTE *)buf, 10L, myKeyMap)) {
	s = send;
	/* if altismeta, we add ESC before a single char */
	if (altismeta && (msg->Qualifier & IEQUALIFIER_LALT) && (count == 1)) 
	  *s++ = ESC;
        for (c = buf, e = buf + count; c < e; c++) {
	    if (*c == 0x9B && !modes.ctrl8bit) {
	        *s = ESC; s++; *s = '[';
	    } else if (*c == '\015' && ansi_LNM)
		*s = *c, s++, *s = '\012';
	    else if (modes.national)
	        *s = national_kmap[*c];
	    else 
	        *s = *c;
	    s++;
	}

	nwrite(send, s - send);
	ReplyMsg((struct Message *)msg);
	return;
    }
    ReplyMsg((struct Message *)msg);
}

/* read stuff from the input stream
 */
/*int dsfillbuf(buf, len)
char	*buf;
int	len;
{
    int count = 0, c;
    
    switch (c = getc(winin)) {
	case wm_MouseInputToken: {
	    int Act, x, y;

	    wm_SawMouse(&Act, &x, &y);
	    x = GETXPOS(x);
	    y = GETYPOS(y);
	    BOUNDSCK(x, 0, disp.winwidt();
	    BOUNDSCK(y, 0, disp.winheight-1);
	    doselection(Act,x,y);
	    break;
	    }
	    
	case EOF:
	    return (-1);

	case '\037':
	    if (winin->_cnt > 0) {
		count = domenu(getc(winin), buf, len);
		break;
	    }
	default:
	    do {
		if ((c&0377) == wm_MetaInputToken) {
		    *buf++ = c = getc(winin) | 0200;
		    count++;
		    continue;
		*buf++ = c & 0177;
		count++;
	    } while (winin->_cnt > 0 && count < len-1 && (c = getc(winin)) != EOF);
	    break;
    }
    
    return (count);
}*/

void
dscheckflash(void)
{
    register int x, y, xhome;
    int linelen;
    struct timeval tv;

    if (DisplayOff) return;

    GetSysTime(&tv);

    if (tv.tv_secs != flashtime) {
	flashtime = tv.tv_secs;
	flashmode = !flashmode;
	if (disp.blink) 
	    dscursorblink();
	for (y=0; y<disp.winheight; y++)
	    if (STYLEFLAG(y) & FLASH && (linelen = LINELEN(y))) {
		int wflag = STYLEFLAG(y) & DOUBLE_MASK ? 2 : 1;
		for (xhome = -1, x = 0; x <= linelen; x++)
		    if (x < linelen && (STYLEPOS(x,y) & FLASH)) {
			if (xhome < 0)
			    xhome = x;
		    } else if (xhome > -1) {
			BltBitMapRastPort(rp->BitMap, 0, 0, rp, 
					  POSX(xhome * wflag), POSY(y), 
					  (x - xhome) * fonts.w * wflag, 
					  fonts.h, INVERT);
			xhome = -1;
		    } else
			xhome = -1;
	    }
    }
}

/* quick clear of the entire display
 */
void
dsclearscreen(void)
{
    clear_display();
    cursormode = 0;
    INVERTALL(disp.invert ? BLACK : WHITE);
    selection.sx = selection.sy = selection.ex = selection.ey = 0;
    SETFONT(disp.style, disp.cury);
}

/* delete n characters on current line into cursor
 */
void
dsdelete(n)
int n;
{
    char    deleteme[MAXWIDTH];
    int	    pos, i, linelen = LINELEN(disp.cury);
    int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;

    if (disp.curx+n >= disp.winwidth/wflag)
	n = disp.winwidth/wflag - disp.curx - 1;
    if (!n)
	return;
    if ((pos = disp.curx+n) > linelen) {
	if (linelen > disp.curx)
	    dsclear(disp.curx, disp.cury, linelen, disp.cury);
    } else if (linelen) {
	for (i = 0; pos < linelen; pos++)
	    deleteme[i++] = TEXTPOS(pos, disp.cury);
	deleteme[i] = '\0';
	dsclear(linelen-1-n, disp.cury, linelen-1, disp.cury);
	LINELENST(disp.cury) = linelen-n;
	wmputs(disp.curx, disp.cury, deleteme, disp.style);
    }
}

/* set appropriate wm font
 */
void
idssetfont(attribs, line)
short attribs;
int line;
{
    if (!DisplayOff) 
	SETFONT(attribs, line);
}

/* put a string to the display
 */
void
dsputs(s)
char *s;
{
    int len = strlen(s), dlen;
    char *temp, tempc;
    int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;
    
    for (temp = s; len; len -= dlen) {
	if (vt100_yucky_wrap_mode) {
	    disp.curx = 0;
	    NEWLINE();
	    SETFONT(disp.style, disp.cury);
	    vt100_yucky_wrap_mode = 0;
	    wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;
	}
	if (len > disp.winwidth/wflag - disp.curx) {
	    dlen = disp.winwidth/wflag - disp.curx;
	    tempc = temp[dlen];
	    temp[dlen]=0;
	    wmputs(disp.curx, disp.cury, temp, disp.style);
	    temp[dlen]=tempc;
	    if (!disp.wrap)
		dlen = len-1;
	    temp += dlen;
	} else {
	    dlen = len;
	    wmputs(disp.curx, disp.cury, temp, disp.style);
	    if (emulation == EMU_VT102 && disp.curx + dlen == disp.winwidth/wflag)
		disp.curx--, vt100_yucky_wrap_mode = 1;
	}
	if ((disp.curx+=dlen)>=disp.winwidth/wflag) {
	    if (disp.wrap) {
		NEWLINE();
		disp.curx = 0;
	    } else
		disp.curx = disp.winwidth/wflag-1;
	}

	SETFONT(disp.style, disp.cury);
	if (modes.speed) {
	    udelay(100);
	}
    }
}

/* clear region routines
 */
void
dsclear(x, y, x1, y1)
int x, y, x1, y1;
{
    if (x > x1)
	SWAP(x,x1);
    if (y > y1)
	SWAP(y,y1);

    DSCLEAR(x,y,x1,y1);

    if (!DisplayOff) {
	int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;
	BltBitMapRastPort(rp->BitMap, 0, 0, rp, POSX(x*wflag), POSY(y),
	                 (x1-x+1)*fonts.w*wflag, (y1-y+1)*fonts.h,
			 (disp.invert ? BLACK : WHITE));
    }
}

/* invert screen or visual bell
 */
void
dsinvert(invtype)
int invtype;
{
    switch (invtype) {
	case INVERT_ON:
	    if (!disp.invert) {
		disp.invert = 1;
		INVERTALL(INVERT);
		udelay(8000);
	    }
	    break;

	case INVERT_OFF:
	    if (disp.invert) {
		disp.invert = 0;
		INVERTALL(INVERT);
	    }
	    break;
	    
	case VISUAL_BELL:
            INVERTALL(INVERT);
	    udelay(10000);
            INVERTALL(INVERT);
	    break;
    }
}

void
dskeyboard(cmd, val)
int cmd, val;
{
  switch (cmd) {
    case DS_CURSORMODE: case DS_KEYPADAPPMODE:
        cursormap = val;
        break;
  }
}
