#include "mint.h"
#include "fasttext.h"

#ifdef FASTTEXT

#ifdef __GNUC__
#define INLINE inline
#define ITYPE long	/* gcc's optimizer likes 32 bit integers */
#else
#define INLINE
#define ITYPE int
#endif

#define CONDEV	(2)

static SCREEN *current;

static void paint P_((SCREEN *, int, char *)),
	 paint8c P_((SCREEN *, int, char *)),
	 paint16m P_((SCREEN *, int, char *));

INLINE static void curs_off P_((SCREEN *)), curs_on P_((SCREEN *));
INLINE static void flash P_((SCREEN *));
static void normal_putch P_((SCREEN *, int));
static void escy_putch P_((SCREEN *, int));

static	char *chartab[256];

static long scrnsize;

short hardscroll;
static char *hardbase, *oldbase;

typedef void (*Vfunc) P_((SCREEN *, int));

#define base *((char **)0x44eL)
#define escy1 *((short *)0x4acL)

static Vfunc state;

static short hardline;
static void (*vpaint) P_((SCREEN *, int, char *));

void init P_((void));
void hardware_scroll P_((SCREEN *));
INLINE static char *PLACE P_((SCREEN *, int, int));
INLINE static void gotoxy P_((SCREEN *, int, int));
INLINE static void clrline P_((SCREEN *, int));
INLINE static void clear P_((SCREEN *));
INLINE static void clrchar P_((SCREEN *, int, int));
INLINE static void clrfrom P_((SCREEN *, int, int, int, int));
INLINE static void delete_line P_((SCREEN *, int));
INLINE static void insert_line P_((SCREEN *, int));
static void setbgcol P_((SCREEN *, int));
static void setfgcol P_((SCREEN *, int));
static void putesc P_((SCREEN *, int));
static void escy1_putch P_((SCREEN *, int));
INLINE static void put_ch P_((SCREEN *, int));

/* routines for flashing the cursor for screen v */
/* flash(v): invert the character currently under the cursor */

INLINE static void
flash(v)
	SCREEN *v;
{
	char *place, d;
	ITYPE i, j, vplanes;

	vplanes = v->planes + v->planes;
	place = v->cursaddr;

	for (j = v->cheight; j > 0; --j) {
		d = (*place) ^ 0xff;
		for (i = 0; i < vplanes; i+=2)
			place[i] = d;

		place += v->planesiz;
	}
}

/* make sure the cursor is off */

INLINE
static void
curs_off(v)
	SCREEN *v;
{
	if (v->flags & CURS_ON) {
		if (v->flags & CURS_FSTATE) {
			flash(v);
			v->flags &= ~CURS_FSTATE;
		}
	}
}

/* OK, show the cursor again (if appropriate) */

INLINE static void
curs_on(v)
	SCREEN *v;
{
	if (v->hidecnt) return;

	if (v->flags & CURS_ON) {
	/* if the cursor is flashing, we cheat a little and leave it off
	 * to be turned on again (if necessary) by the VBL routine
	 */
		if (v->flags & CURS_FLASH) {
			v->curstimer = 2;
			return;
		}
		if (!(v->flags & CURS_FSTATE)) {
			v->flags |= CURS_FSTATE;
			flash(v);
		}
	}
}

void
init()
{
	SCREEN *v;
	int i, j;
	char *data, *foo;
	static char chardata[256*16];

	foo = lineA0();
	v = (SCREEN *)(foo - 346);
	
	/* Ehem... The screen might be bigger than 32767 bytes.
	   Let's do some casting... 
	   Erling
	*/
	scrnsize = (v->maxy+1)*(long)v->linelen;
	if (hardscroll > 0) {
		if (!hardbase)
		    hardbase = (char *)(((long)kcore(SCNSIZE(v)+256L)+255L)
					   & 0xffffff00L);

		if (hardbase == 0) {
			ALERT("Insufficient memory for hardware scrolling!");
		} else {
			quickmove(hardbase, base, scrnsize);
			v->cursaddr = v->cursaddr + (hardbase - base);
			oldbase = base;
			base = hardbase;
			Setscreen(hardbase, hardbase, -1);
		}
	}
	hardline = 0;
	if (v->cheight == 8 && v->planes == 2) {
		foo = &chardata[0];
		vpaint = paint8c;
		for (i = 0; i < 256; i++) {
			chartab[i] = foo;
			data = v->fontdata + i;
			for (j = 0; j < 8; j++) {
				*foo++ = *data;
				data += v->form_width;
			}
		}
	} else if (v->cheight == 16 && v->planes == 1) {
		foo = &chardata[0];
		vpaint = paint16m;
		for (i = 0; i < 256; i++) {
			chartab[i] = foo;
			data = v->fontdata + i;
			for (j = 0; j < 16; j++) {
				*foo++ = *data;
				data += v->form_width;
			}
		}
	}
	else
		vpaint = paint;

	if (v->hidecnt == 0) {
	/*
	 * make sure the cursor is set up correctly and turned on
	 */
		(void)Cursconf(0,0);	/* turn cursor off */

		v->flags &= ~(CURS_FLASH|CURS_FSTATE);

	/* now turn the cursor on the way we like it */
		v->hidecnt = 0;
		curs_on(v);
	} else {
		(void)Cursconf(0,0);
		v->flags &= ~CURS_ON;
		v->hidecnt = 1;
	}

	current = v;
	state = normal_putch;
}

/*
 * PLACE(v, x, y): the address corresponding to the upper left hand corner of
 * the character at position (x,y) on screen v
 */
INLINE static
char *PLACE(v, x, y)
	SCREEN *v;
	int x, y;
{
	char *place;
	int i, j;

	place = base + x;
	if (y == v->maxy)
		place += scrnsize - v->linelen;
	else if (y) /* Yo, the screen might be bigger than 32767 bytes...
		       Do a cast to long.	Erling. */
		place += (long)y * v->linelen;

	if ((j = v->planes) > 1) {
		i = (x & 0xfffe);
		while (--j > 0)
			place += i;
	}
	return place;
}

/*
 * paint(v, c, place): put character 'c' at position 'place' on screen
 * v. It is assumed that x, y are proper coordinates!
 * Specialized versions (paint8c and paint16m) of this routine follow;
 * they assume 8 line high characters, medium res. and 16 line/mono,
 * respectively.
 */

static void
paint(v, c, place)
	SCREEN *v;
	int c;
	char *place;
{
	char *data, d, doinverse;
	ITYPE j, planecount;
	int vplanes;
	long vform_width, vplanesiz;

	vplanes = v->planes;

	data = v->fontdata + c;
	doinverse = (v->flags & FINVERSE) ? 0xff : 0;
	vform_width = v->form_width;
	vplanesiz = v->planesiz;

	for (j = v->cheight; j > 0; --j) {
		d = *data ^ doinverse;
		*place = d;
		if (vplanes > 1) {
/* This should work for an arbitrary number of planes. Erling */
		  for(planecount=1;planecount<vplanes;planecount++) 
		    place[(planecount<<1)]=d;
#if 0
		    {
			place[2] = d;
			if (vplanes > 2) {
				place[4] = place[6] = d;
			}
		    }
#endif
		}

		place += vplanesiz;
		data += vform_width;
	}

}

static void
paint8c(v, c, place)
	SCREEN *v;
	int c;
	char *place;
{
	char *data;
	char d, doinverse;
	long vplanesiz;

	data = chartab[c];

	doinverse = (v->flags & FINVERSE) ? 0xff : 0;
	vplanesiz = v->planesiz;

	if (!doinverse) {
		/* line 1 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 2 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 3 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 4 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 5 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 6 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 7 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 8 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;
	} else {
		/* line 1 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 2 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 3 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 4 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 5 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 6 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 7 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 8 */
		d = *data++ ^ doinverse;
		*place = d;
		place[2] = d;
		place += vplanesiz;
	}
}

static void
paint16m(v, c, place)
	SCREEN *v;
	int c;
	char *place;
{
	char *data;
	char d, doinverse;
	long vplanesiz;

	data = chartab[c];
	doinverse = (v->flags & FINVERSE) ? 0xff : 0;
	vplanesiz = v->planesiz;

	if (!doinverse) {
		/* line 1 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 2 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 3 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 4 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 5 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 6 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 7 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 8 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 9 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 10 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 11 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 12 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 13 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 14 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 15 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 16 */
		d = *data++;
		*place = d;
		place += vplanesiz;
	} else {
		/* line 1 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 2 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 3 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 4 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 5 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 6 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 7 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 8 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 9 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 10 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 11 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 12 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 13 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 14 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 15 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;

		/* line 16 */
		d = *data++ ^ doinverse;
		*place = d;
		place += vplanesiz;
	}
}

/*
 * gotoxy (v, x, y): move current cursor address of screen v to (x, y)
 * makes sure that (x, y) will be legal
 */

INLINE static void
gotoxy(v, x, y)
	SCREEN *v;
	int x, y;
{
	if (x > v->maxx) x = v->maxx;
	else if (x < 0) x = 0;
	if (y > v->maxy) y = v->maxy;
	else if (y < 0) y = 0;
	curs_off(v);

	v->cx = x;
	v->cy = y;
	v->cursaddr = PLACE(v, x, y);
}

/*
 * clrline(v, r): clear line r of screen v
 */

INLINE static void
clrline(v, r)
	SCREEN *v;
	int r;
{
	long *dst;
	long nbytes;

	nbytes = v->linelen;
	/* Hey, again the screen might be bigger than 32767 bytes.
	   Do another cast... */
	dst = (long *)(base + ((long)r * v->linelen));
	zero((char *)dst, nbytes);
}
	
/*
 * clear(v): clear the whole screen v
 */

INLINE static void
clear(v)
	SCREEN *v;
{
	zero(base, scrnsize);
}

/*
 * clrchar(v, x, y): clear the (x,y) position on screen v
 */

INLINE static void
clrchar(v, x, y)
	SCREEN *v;
	int x, y;
{
	int i, j, vplanes;
	char *place;

	vplanes = v->planes + v->planes;

	place = PLACE(v, x, y);

	for (j = v->cheight; j > 0; --j) {
		for (i = 0; i < vplanes; i+=2)
			place[i] = 0;
		place += v->planesiz;
	}
}

/*
 * clrfrom(v, x1, y1, x2, y2): clear screen v from position (x1,y1) to
 * position (x2, y2) inclusive. It is assumed that y2 >= y1.
 */

INLINE static void
clrfrom(v, x1, y1, x2, y2)
	SCREEN *v;
	int x1,y1,x2,y2;
{
	int i;

	for (i = x1; i <= v->maxx; i++)
		clrchar(v, i, y1);
	if (y2 > y1) {
		for (i = 0; i <= x2; i++)
			clrchar(v, i, y2);
		for (i = y1+1; i < y2; i++)
			clrline(v, i);
	}
}

/*
 * scroll a screen in hardware; if we still have hardware scrolling lines left,
 * just move the physical screen base, otherwise copy the screen back to the
 * hardware base and start over
 */
void
hardware_scroll(v)
	SCREEN *v;
{

	++hardline;
	if (hardline < hardscroll) { /* just move the screen */
		base += v->linelen;
		v->cursaddr = PLACE(v, v->cx, v->cy);
		Setscreen(base, base, -1);
	}
	else {
		hardline = 0;
		quickmove(hardbase, base + v->linelen, scrnsize - v->linelen);
		base = hardbase;
		v->cursaddr = PLACE(v, v->cx, v->cy);
		Setscreen(hardbase, hardbase, -1);
	}
}

/*
 * delete_line(v, r): delete line r of screen v. The screen below this
 * line is scrolled up, and the bottom line is cleared.
 */

#define scroll(v) delete_line(v, 0)

INLINE static void
delete_line(v, r)
	SCREEN *v;
	int r;
{
	long *src, *dst, nbytes;

	if (r == 0) {
		if (hardbase) {
			hardware_scroll(v);
			clrline(v, v->maxy);
			return;
		}
		nbytes = scrnsize - v->linelen;
	}
	else
		nbytes = (long)v->linelen * (v->maxy - r);

	/* Sheeze, how many times do we really have to cast... 
	   Erling.	
	*/

	dst = (long *)(base + ((long)r * v->linelen));
	src = (long *)( ((long)dst) + v->linelen);

	quickmove(dst, src, nbytes);

/* clear the last line */
	clrline(v, v->maxy);
}

/*
 * insert_line(v, r): scroll all of the screen starting at line r down,
 * and then clear line r.
 */

INLINE static void
insert_line(v, r)
	SCREEN *v;
	int r;
{
	long *src, *dst;
	int i, limit;

	limit = v->maxy;
	for (i = limit-1; i >= r ; --i) {
	/* move line i to line i+1 */
	/* AND do some casting to support big screens.
	   Erling
	*/
		src = (long *)(base + ((long)i * v->linelen));
		dst = (long *)(base + ((i+1)*(long)v->linelen));
		quickmove(dst, src, v->linelen);
	}

/* clear line r */
	clrline(v, r);
}

/*
 * special states for handling ESC b x and ESC c x. Note that for now,
 * color is ignored.
 */

static void
setbgcol(v, c)
	SCREEN *v;
	int c;
{
	v->bgcol = c & ((1 << v->planes)-1);
	state = normal_putch;
}

static void
setfgcol(v, c)
	SCREEN *v;
	int c;
{
	v->fgcol = c & ((1 << v->planes)-1);
	state = normal_putch;
}

/*
 * putesc(v, c): handle the control sequence ESC c
 */

static void
putesc(v, c)
	SCREEN *v;
	int c;
{
	int cx, cy;

	cx = v->cx; cy = v->cy;

	switch (c) {
	case 'A':		/* cursor up */
		gotoxy(v, cx, cy-1);
		break;
	case 'B':		/* cursor down */
		gotoxy(v, cx, cy+1);
		break;
	case 'C':		/* cursor right */
		gotoxy(v, cx+1, cy);
		break;
	case 'D':		/* cursor left */
		gotoxy(v, cx-1, cy);
		break;
	case 'E':		/* clear home */
		curs_off(v);
		clear(v);
		/* fall through... */
	case 'H':		/* cursor home */
		gotoxy(v, 0, 0);
		break;
	case 'I':		/* cursor up, insert line */
		if (cy == 0) {
			curs_off(v);
			insert_line(v, 0);
		}
		else
			gotoxy(v, cx, cy-1);
		break;
	case 'J':		/* clear below cursor */
		curs_off(v);
		clrfrom(v, cx, cy, v->maxx, v->maxy);
		break;
	case 'K':		/* clear remainder of line */
		curs_off(v);
		clrfrom(v, cx, cy, v->maxx, cy);
		break;
	case 'L':		/* insert a line */
		gotoxy(v, 0, cy);
		insert_line(v, cy);
		break;
	case 'M':		/* delete line */
		gotoxy(v, 0, cy);
		delete_line(v, cy);
		break;
	case 'Y':
		state = escy_putch;
		return;		/* YES, this should be 'return' */

	case 'b':
		state = setfgcol;
		return;
	case 'c':
		state = setbgcol;
		return;
	case 'd':		/* clear to cursor position */
		curs_off(v);
		clrfrom(v, 0, 0, cx, cy);
		break;
	case 'e':		/* enable cursor */
		curs_off(v);
		v->flags |= CURS_ON;
		v->hidecnt = 1;	/* so --v->hidecnt shows the cursor */
		break;
	case 'f':		/* cursor off */
		curs_off(v);
		v->hidecnt++;
		v->flags &= ~CURS_ON;
		break;
	case 'j':		/* save cursor position */
		v->savex = v->cx;
		v->savey = v->cy;
		break;
	case 'k':		/* restore saved position */
		gotoxy(v, v->savex, v->savey);
		break;
	case 'l':		/* clear line */
		gotoxy(v, 0, cy);
		clrline(v, cy);
		break;
	case 'o':		/* clear from start of line to cursor */
		curs_off(v);
		clrfrom(v, 0, cy, cx, cy);
		break;
	case 'p':		/* reverse video on */
		v->flags |= FINVERSE;
		break;
	case 'q':		/* reverse video off */
		v->flags &= ~FINVERSE;
		break;
	case 'v':		/* wrap on */
		v->flags |= FWRAP;
		break;
	case 'w':
		v->flags &= ~FWRAP;
		break;
	}
	state = normal_putch;
}

/*
 * escy1_putch(v, c): for when an ESC Y + char has been seen
 */
static void
escy1_putch(v, c)
	SCREEN *v;
	int c;
{
	gotoxy(v, c - ' ', escy1 - ' ');
	state = normal_putch;
}

/*
 * escy_putch(v, c): for when an ESC Y has been seen
 */
static void
escy_putch(v, c)
	SCREEN *v;
	int c;
{
	escy1 = c;
	state = escy1_putch;
}

/*
 * normal_putch(v, c): put character 'c' on screen 'v'. This is the default
 * for when no escape, etc. is active
 */

static void
normal_putch(v, c)
	SCREEN *v;
	int c;
{
/* control characters */
	if (c < ' ') {
		switch (c) {
		case '\r':
			gotoxy(v, 0, v->cy);
			return;
		case '\n':
			if (v->cy == v->maxy) {
				curs_off(v);
				scroll(v);
			}
			else
				gotoxy(v, v->cx, v->cy+1);
			return;
		case '\b':
			gotoxy(v, v->cx-1, v->cy);
			return;
		case '\007':		/* BELL */
			(void)bconout(CONDEV, 7);
			return;
		case '\033':		/* ESC */
			state = putesc;
			return;
		case '\t':
			gotoxy(v, (v->cx + 8) & ~7, v->cy);
			return;
		default:
			return;
		}
	}

	(*vpaint)(v, c, v->cursaddr);
	v->flags &= ~CURS_FSTATE;	/* we just erased the cursor */
	v->cx++;
	if (v->cx > v->maxx) {
		if (v->flags & FWRAP) {
			v->cx = 0;
			normal_putch(v, '\n');
			v->cursaddr = PLACE(v, v->cx, v->cy);
		} else {
			v->cx = v->maxx;
		}
	} else {
#if 0
		v->cursaddr = PLACE(v, v->cx, v->cy);
#else
		v->cursaddr++;
		if ( (v->cx & 1) == 0 && v->planes > 1) { /* new word */
			short skipwords = v->planes - 1;
			v->cursaddr += skipwords+skipwords;
		}
#endif
	}
}

INLINE static void
put_ch(v, c)
	SCREEN *v;
	int c;
{
	(*state)(v, c & 0x00ff);
}

static long screen_open	P_((FILEPTR *f));
static long screen_read	P_((FILEPTR *f, char *buf, long nbytes));
static long screen_write P_((FILEPTR *f, const char *buf, long nbytes));
static long screen_lseek P_((FILEPTR *f, long where, int whence));
static long screen_ioctl P_((FILEPTR *f, int mode, void *buf));
static long screen_close P_((FILEPTR *f, int pid));
static long screen_select P_((FILEPTR *f, long p, int mode));
static void screen_unselect P_((FILEPTR *f, long p, int mode));

extern long	null_datime	P_((FILEPTR *f, short *time, int rwflag));

DEVDRV screen_device = {
	screen_open, screen_write, screen_read, screen_lseek, screen_ioctl,
	null_datime, screen_close, screen_select, screen_unselect
};

static long
screen_open(f)
	FILEPTR *f;
{

	if (!current) {
		init();
	} else
		return EACCDN;		/* screen in use */

	f->flags |= O_TTY;
	return 0;
}

static long
screen_close(f, pid)
	FILEPTR *f;
	int pid;
{
	if (f->links <= 0) {
		if (hardbase) {
			quickmove(oldbase, base, scrnsize);
			base = oldbase;
			Setscreen(oldbase, oldbase, -1);
		}
		current = 0;
	}
	return 0;
}

static long
screen_write(f, buf, bytes)
	FILEPTR *f; const char *buf; long bytes;
{
	SCREEN *v = current;
	long *r;
	long ret = 0;
	int c;

	(void)checkkeys();
	v->hidecnt++;
	v->flags |= CURS_UPD;		/* for TOS 1.0 */
	r = (long *)buf;
	while (bytes > 0) {
		c = *r++;
		put_ch(v, c);
		bytes -= 4; ret+= 4;
	}
	--v->hidecnt;
	v->flags &= ~CURS_UPD;
	curs_on(v);
	return ret;
}

static long
screen_read(f, buf, bytes)
	FILEPTR *f; char *buf; long bytes;
{
	long *r, ret = 0;

	r = (long *)buf;

	while (bytes > 0) {
		if ( (f->flags & O_NDELAY) && !bconstat(CONDEV) )
			break;
		*r++ = bconin(CONDEV) & 0x7fffffff;
		bytes -= 4; ret += 4;
	}
	return ret;
}

static long
screen_lseek(f, where, whence)
	FILEPTR *f;
	long where;
	int whence;
{
/* terminals always are at position 0 */
	return 0;
}

static long
screen_ioctl(f, mode, buf)
	FILEPTR *f; int mode; void *buf;
{
	long *r = (long *)buf;
	struct winsize *w;

	if (mode == FIONREAD) {
		if (bconstat(CONDEV))
			*r = 1;
		else
			*r = 0;
	}
	else if (mode == FIONWRITE) {
			*r = 1;
	}
	else if (mode == TIOCFLUSH) {
/* BUG: this should flush the input/output buffers */
		return 0;
	}
	else if (mode == TIOCGWINSZ) {
		w = (struct winsize *)buf;
		w->ws_row = current->maxy+1;
		w->ws_col = current->maxx+1;
	}
	else
		return tty_ioctl(f, mode, buf);

	return 0;
}

static long
screen_select(f, p, mode)
	FILEPTR *f; long p; int mode;
{
	struct tty *tty = (struct tty *)f->devinfo;
	int dev = CONDEV;

	if (mode == O_RDONLY) {
		if (bconstat(dev)) {
			return 1;
		}
		if (tty) {
		/* avoid collisions with other processes */
			if (!tty->rsel)
				tty->rsel = p;
		}
		return 0;
	} else if (mode == O_WRONLY) {
		return 1;
	}
	/* default -- we don't know this mode, return 0 */
	return 0;
}

static void
screen_unselect(f, p, mode)
	FILEPTR *f;
	long p;
	int mode;
{
	struct tty *tty = (struct tty *)f->devinfo;

	if (tty) {
		if (mode == O_RDONLY && tty->rsel == p)
			tty->rsel = 0;
		else if (mode == O_WRONLY && tty->wsel == p)
			tty->wsel = 0;
	}
}

#endif /* FASTTEXT */
