
/* Terminality - a portable terminal handling library
 * Copyright (C) 1998-2002, Emil Mikulic.
 * This is LGPL - look at COPYING.LIB
 */

/* Project:     Terminality
 * File:	tn_c_w32.h
 * Author:      Emil Mikulic
 * Description: Provide the Win32 implementation
 */

/* Special note to people using the MSVC IDE (Developer Studio):
 * Set your tab stops to 8 or I will hunt you down and beat you
 * in the head with my rubber piggy.
 *	-- Emil
 */

/* Terminality - Win32 backend
 * Intended for Windows 95/98/ME/NT/2K/XP. Usually works with:
 *  - Microsoft Visual C++ 5.0, 6.0
 *  - Cygnus CygWin32 B19
 *  - Borland C++ 5.1.1 (compiles but has some glitches)
 */

#define TN_C_IMPLEMENTED

static char tn_w32_rcsid[] =
"$Id: tn_c_w32.h,v 1.1 2002/07/26 08:18:14 darkmoon Exp $";



#include <xmem.h>

#ifdef __BORLANDC__
#define _inline __inline
#endif



/* Console function error reporting */
void conserr(char *f, int l)
{
	char buf[2048];

	sprintf(buf, "Console error in file %s, line %d", f, l);

	MessageBox(NULL, buf, "Console error", MB_OK);
	PostQuitMessage(1);
}



/* Current currattr */
unsigned char currattr;

/* Console handles */
HANDLE consout, consin;
CHAR_INFO *doublebuf;
int CON_COLS, CON_ROWS, curs_x, curs_y;



/* initcons()
   Must call this before you start using Terminality */
void initcons(void)
{
	CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */

	/* Get standard input/output handles */
	consout = GetStdHandle(STD_OUTPUT_HANDLE);
	PERR(consout != INVALID_HANDLE_VALUE);
	consin = GetStdHandle(STD_INPUT_HANDLE);
	PERR(consin != INVALID_HANDLE_VALUE);

	/* get the number of character cells in the current buffer */ 
	PERR( GetConsoleScreenBufferInfo(consout, &csbi) );
	CON_COLS = csbi.dwSize.X;

	/* fix Win2K/XP scrolling consoles */
	CON_ROWS = csbi.dwSize.Y = csbi.srWindow.Bottom + 1;
	PERR( SetConsoleScreenBufferSize(consout, csbi.dwSize) );

	/* Init double buffer */
	doublebuf = (CHAR_INFO*)xmalloc(sizeof(CHAR_INFO)*CON_ROWS*CON_COLS);
	currattr = 7;
	clrscr();
}



/* Cleanup after using terminal */
void donecons(void)
{
	/* Reset */
	setcolor(7,0);
	set_cursor(line);

	/* Go to bottom of screen like ncurses */
	gotoxy(1, CON_ROWS);
	update();

	/* Kill copy buffer */
	free(doublebuf);
}



/* Update screen */
void update(void)
{
	COORD bufsize, coord = {0, 0};
	SMALL_RECT bufrect;

	/* Copy double buffer to consout */
	bufsize.X = CON_COLS;
	bufsize.Y = CON_ROWS;
	
	bufrect.Top = 0;
	bufrect.Left = 0;
	bufrect.Bottom = CON_ROWS-1;
	bufrect.Right = CON_COLS-1;

	PERR( WriteConsoleOutput(
		consout, doublebuf, bufsize, coord, &bufrect) );

	/* update cursor pos */
	coord.X = curs_x;
	coord.Y = curs_y;
	PERR( SetConsoleCursorPosition(consout, coord) );
}



/* (helper function) */
_inline void directprint(const char *str)
{
	int ofs, i;

	for (i=0; str[i]; i++)
	{

		/* scroll buffer if at bottom of screen */
		if (curs_y == CON_ROWS)
		{
			memmove(doublebuf,
				doublebuf+sizeof(CHAR_INFO)*CON_COLS,
				sizeof(CHAR_INFO)*CON_COLS);

			curs_y--;
			clreol();
		}

		ofs = curs_y * CON_COLS + curs_x;

		/* write to buffer */
		switch (str[i])
		{
		case '\n':
			curs_x = 0;
			curs_y++;
			break;

		case '\t':
			curs_x += 8;
			curs_x -= (curs_x & 7);
			break;

		case '\r':
			curs_x = 0;
			break;

		default:
			doublebuf[ofs].Attributes = currattr;
			doublebuf[ofs].Char.AsciiChar = str[i];

			/* advance cursor */
			ofs++;
			curs_x++;
		};

		/* wrap at EOL */
		if (curs_x >= CON_COLS) {
			curs_x -= CON_COLS;
			curs_y++;
		}
	}
}



/* Print to window */
int printw(const char *format, ...)
{
	char s[2048];
	va_list argl;
	int ret;

	va_start(argl,format);
	ret = _vsnprintf(s, 2048, format, argl);
	va_end(argl);
	PERR( ret != -1); /* buffer overflow */

	/* write the formatted string to the console */
	directprint(s);

	return ret;
}



/* set attribute to attr */
void setattr(unsigned char attr)
{
	/* set attributes for new writes to the console */
	PERR( SetConsoleTextAttribute(consout, attr) );
}



/* set text color */
void textcolor(color fg)
{
	currattr = (currattr & FG_MASK) | (fg & BG_MASK);
}



/* set text background */
void textbackground(color bg)
{
	currattr = (currattr & BG_MASK) | ((bg << BG_BITS) & FG_MASK);
}



/* set color */
void setcolor(color f, color b)
{
	currattr = (f & BG_MASK) | ((b << BG_BITS) & FG_MASK);
}



color getbgcolor(void)
{
	return (currattr & FG_MASK) >> BG_BITS;
}



color getfgcolor(void)
{
	return currattr & BG_MASK;
}



/* write char */
void writech(chtype c)
{
	char ch[2];

	ch[0] = c;
	ch[1] = 0;

	directprint(ch);
}



/* Clear screen and home cursor */
void clrscr(void)
{
	int i;

	for (i=0; i<CON_ROWS*CON_COLS; i++)
	{
		doublebuf[i].Attributes = currattr;
		doublebuf[i].Char.AsciiChar = ' ';
	}

	gotoxy(1,1);
}



/* cursor movement */
void gotoxy(int x, int y)
{
	curs_x = x-1;
	curs_y = y-1;
}



/* set textattr */
void textattr(char attr)
{
	currattr = attr;
}



/* get cursor X position */
int wherex(void)
{
	return curs_x;
}



/* get cursor Y position */
int wherey(void)
{
	return curs_y;
}



/* Clear to end of line */
void clreol(void)
{
	int i, ofs = curs_y * CON_COLS + curs_x;

	for (i=curs_x; i<CON_COLS; i++)
	{
		doublebuf[i].Attributes = currattr;
		doublebuf[i].Char.AsciiChar = ' ';
	}
}



/* Delay */
void delay(int ms)
{
	Sleep( (DWORD)(ms) );
}



/* ...glass teletype */
int has_color(void)
{
	return 1;
}



/* Get a character from the keyboard */
key readkey(void)
{
	key k = _getch();
	if (k == 224) /* we have a control char */
		return (CONTROL_KEY | _getch());
	else
		return k;
}



/* Check if any keys have been hit */
int keypressed(void)
{
#ifdef __BORLANDC__
	return kbhit();
#else
	return _kbhit();
#endif
}



/* Beep/alarm/bell */
int beep(void)
{
	return MessageBeep(0xFFFFFFFF);
}



/* Black-and-white terminal functions */
void normvideo(void)
{
	textattr(7);
}



void lowvideo(void)
{
	textattr(8);
}



void highvideo(void)
{
	textattr(15);
}



/* Sets cursor */
void set_cursor(cursor c)
{
	CONSOLE_CURSOR_INFO cci;

	if (c == none)
		cci.bVisible = FALSE;
	else
		cci.bVisible = TRUE;

	if (c == rect)
		cci.dwSize = 100;
	else
		cci.dwSize = 32;

	PERR( SetConsoleCursorInfo(consout, &cci) );

	glb_cursor = c;
}

