
/*
 *  S_TERM.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 *  Terminal window server.
 *	-Echo what is received in the window.
 *	-Transmit stuff typed on keyboard, echoing locally.
 *
 *  Use FTERM on the other Amiga to connect.
 *
 *  NOTE!!!!  Spawned tasks are ... TASKS, not processes.  No DOS calls
 *  allowed.
 */

#include "defs.h"

int NHandlers;

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

PORT *HdPort;
PORT *LisPort;
short HandShakeSig;

int spawn_handler ARGS((void));
void __saveds term_task ARGS((void));
void HandleIoctl ARGS((short, short, char, WIN *, IOCON *, short*));
void setsize ARGS((IOCON *, void *, WIN *));

int
brk()
{
    return(0);
}

void
#ifdef LATTICE
_main(str)
#else
_main(len,str)
#endif
char *str;
{
    MSG *msg;
    long mask, pmask, hdmask;
    PROC *proc = (PROC *)FindTask(NULL);

    onbreak(brk);

    if (strncmp(str, "__dnet", 6) != 0) {
	Version("STerm", VERSION, STERM_VERSION);
	_exit(0);
    }

    LisPort= DListen(PORT_IALPHATERM);
    WaitPort(&proc->pr_MsgPort);
    ReplyMsg(GetMsg(&proc->pr_MsgPort));
    if (!LisPort)
	exit(1);
    HdPort = CreatePort(NULL, 0);
    HandShakeSig = AllocSignal(-1);
    pmask = 1 << LisPort->mp_SigBit;
    hdmask= 1 << HdPort->mp_SigBit;

    IntuitionBase   = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
    GfxBase	    = (struct GfxBase *)OpenLibrary("graphics.library", 0);
    while (mask = Wait(SIGBREAKF_CTRL_C|pmask|hdmask)) {
	if (mask & SIGBREAKF_CTRL_C)
	    break;
	if (mask & hdmask) {
	    while (msg = GetMsg(HdPort)) {
		--NHandlers;
		FreeMem(msg, sizeof(*msg));
	    }
	}
	if (mask & pmask) {
	    while (spawn_handler())
		;
	}
    }
    DUnListen(LisPort);
    while (NHandlers) {
	WaitPort(HdPort);
	msg = GetMsg(HdPort);
	FreeMem(msg, sizeof(*msg));
	--NHandlers;
    }
    DeletePort(HdPort);
    CloseLibrary((LIB *)IntuitionBase);
    CloseLibrary((LIB *)GfxBase);
}

/*
 *  Spawn a handler to accept the new connection, if any.  Task sends
 *  a message to HdPort when through.
 */

int
spawn_handler()
{
    long oldhan = NHandlers;

    CreateTask("Term.channel", 0, (APTR)term_task, 2048);
    Wait(1 << HandShakeSig);
    return (oldhan != NHandlers);
}

static NW Nw = {
    64, 64, 400, 100, -1, -1,
    CLOSEWINDOW|NEWSIZE,
    WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|ACTIVATE|NOCAREREFRESH,
    NULL, NULL, (unsigned char *)"DNET-Term", NULL, NULL, 32, 32, -1, -1,
    WBENCHSCREEN
};

void __saveds
term_task()
{
    void *chan;
    long imask, cmask, conmask;
    WIN *win;
    char notdone = 1;
    short ignorens = 0;
    char conc[4];
    IOCON iocw;
    IOCON iocr;

#ifndef LATTICE
    geta4();
#endif
    chan = DAccept(LisPort);
    if (chan) {
	DQueue(chan, 32);
	DIoctl(chan, CIO_MODE, 7, 0);
	++NHandlers;
	Signal(HdPort->mp_SigTask, 1 << HandShakeSig);
	if (win = OpenWindow(&Nw)) {
	    iocw.io_Command = CMD_WRITE;
	    iocw.io_Data = (APTR)win;
	    iocw.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	    iocw.io_Message.mn_ReplyPort = CreatePort(NULL,0);
	    conmask = 1 << iocw.io_Message.mn_ReplyPort->mp_SigBit;
	    OpenDevice("console.device", 0, &iocw, 0);
	    iocr = iocw;
	    iocr.io_Command = CMD_READ;
	    imask = 1 << win->UserPort->mp_SigBit;
	    cmask = 1 << ((PORT *)chan)->mp_SigBit;

	    iocr.io_Data = (APTR)conc;
	    iocr.io_Length = sizeof(conc);
	    SendIO(&iocr);

	    setsize(&iocw, NULL, win);
	    while (notdone) {
		long mask;
		mask = Wait(imask|cmask|conmask);

		if (mask & imask) {
		    IMESS *im;
		    while (im = (IMESS *)GetMsg(win->UserPort)) {
			switch(im->Class) {
			case CLOSEWINDOW:
			    notdone = 0;
			    break;
			case NEWSIZE:
			    if (ignorens) {
				--ignorens;
				setsize(&iocw, NULL, win);
			    } else {
				setsize(&iocw, chan, win);
			    }
			    break;
			}
			ReplyMsg((MSG *)im);
		    }
		}
		if ((mask & conmask) && CheckIO(&iocr)) {
		    short i;
		    char *ptr;
		    WaitIO(&iocr);
		    ptr = (char *)iocr.io_Data;

		    if (iocr.io_Actual > 0) {
			for (i = 0; i < iocr.io_Actual; ++i) {
			    if (ptr[i] == 13)
				ptr[i] = 10;
			}
			DWrite(chan, ptr, i);
			iocw.io_Data = (APTR)ptr;
			iocw.io_Length = i;
			DoIO(&iocw);
		    }
		    iocr.io_Data = (APTR)conc;
		    iocr.io_Length = sizeof(conc);
		    SendIO(&iocr);
		}
		if (mask & cmask) {
		    long n;
		    char buf[32];
		    while (n = DNRead(chan, buf, sizeof(buf))) {
			if (n == -2) {
			    short cmd, val;
			    char aux;
			    cmd = DGetIoctl(chan, &val, &aux);
			    if (cmd >= 0)
				HandleIoctl(cmd, val, aux, win, &iocw, &ignorens);
			    continue;
			}
			if (n < 0) {
			    notdone = 0;
			    break;
			}
			if (buf[0] == 3)        /*  remote ^C -- done   */
			    notdone = 0;
			iocw.io_Data = (APTR)buf;
			iocw.io_Length = n;
			DoIO(&iocw);
		    }
		}
	    }
	    AbortIO(&iocr);
	    WaitIO(&iocr);
	    CloseDevice(&iocw);
	    DeletePort(iocw.io_Message.mn_ReplyPort);
	    CloseWindow(win);
	}
	DClose(chan);
	{
	    MSG *msg;
	    msg = AllocMem(sizeof(MSG), MEMF_PUBLIC);
	    Forbid();
	    PutMsg(HdPort, msg);
	}
    } else {
	Forbid();
	Signal(HdPort->mp_SigTask, 1 << HandShakeSig);
    }
    RemTask(NULL);
}

void
HandleIoctl(cmd, val, aux, win, iocw, igns)
short cmd, val;
char aux;
WIN *win;
IOCON *iocw;
short *igns;
{
    static short saverows;
    short height, width;
    short dx, dy;

    switch(cmd) {
    case CIO_SETROWS:
	saverows = val;
	break;
    case CIO_SETCOLS:
	width = val * win->RPort->TxWidth + win->BorderLeft + win->BorderRight;
	height= saverows * win->RPort->TxHeight + win->BorderTop + win->BorderBottom;

	dx = win->WScreen->Width - (win->LeftEdge + width);
	if (dx > 0)
	    dx = 0;
	if (-dx > win->LeftEdge) {
	    dx = -win->LeftEdge;
	    width = win->WScreen->Width;
	}

	dy = win->WScreen->Height - (win->TopEdge + height);
	if (dy > 0)
	    dy = 0;
	if (-dy > win->TopEdge) {
	    dy = -win->TopEdge;
	    height = win->WScreen->Height;
	}

	if (dx || dy) {
	    MoveWindow(win, dx, dy);
	}
	if (win->Width != width || win->Height != height) {
	    SizeWindow(win, width - win->Width, height - win->Height);
	    ++*igns;
	}
	break;
    }
}

void
setsize(iocw, chan, win)
IOCON *iocw;
void *chan;
WIN *win;
{
    struct ConUnit *cu = (struct ConUnit *)iocw->io_Unit;
    static char Term[64];

    iocw->io_Data = (APTR)"\033c\033[20h\033[t\033[u";
    iocw->io_Length = 13;
    DoIO(iocw);
    if (chan) {
	DIoctl(chan, CIO_SETROWS, (uword)(cu->cu_YMax+1), 0);
	DIoctl(chan, CIO_SETCOLS, (uword)(cu->cu_XMax+1), 0);
    }
    sprintf(Term, "STERM   %ld x %ld", cu->cu_YMax+1, cu->cu_XMax+1);
    SetWindowTitles(win, Term, (char *)-1);
}

