
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "wterm.h"

//****************************************************************************
//    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//    PURPOSE: initialization function, processes message loop
//****************************************************************************

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HANDLE hInstance;				// Current instance
HANDLE hPrevInstance;				// Previous instance
LPSTR lpCmdLine;				// Command line parameters
int nCmdShow;					// Show-window type: open or icon

{
	MSG		msg;
	WNDCLASS	Win_Class;
	HWND		hWnd;				// Main window handle
	BOOL		Message_available;
	unsigned	int Num_Bytes;
	static short xscreen, yscreen;

	if (!hPrevInstance)
	{
		// Set up window class structure with parameters that describe the
		// main Wterm window.

		Win_Class.style = CS_HREDRAW | CS_VREDRAW; // Class style(s).
		Win_Class.cbClsExtra = 0;                  // No per-class extra data
		Win_Class.cbWndExtra = 0;                  // No per-window extra data
		Win_Class.hInstance = hInstance;           // Application that owns the class

		Win_Class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
		Win_Class.hCursor = LoadCursor(NULL, IDC_ARROW);
		Win_Class.hbrBackground = GetStockObject(WHITE_BRUSH); 

		Win_Class.lpfnWndProc	= MainWndProc;		// Function to receive messages
		Win_Class.lpszMenuName	=  "Wterm_Menu";	// Name of menu in .RC file
		Win_Class.lpszClassName	= "WtermWClass";	// Class name

		// Register the window class

		RegisterClass(&Win_Class);

		xscreen = GetSystemMetrics (SM_CXSCREEN);
		yscreen = GetSystemMetrics (SM_CYSCREEN);

		// Create a main window for this application instance

		hWnd = CreateWindow(
			"WtermWClass",			// See RegisterClass() call
			"DOS/RMX Sample Application",	// Text for window title bar
			WS_OVERLAPPEDWINDOW,		// Window style
			CW_USEDEFAULT,			// initial x  pos
			CW_USEDEFAULT,			// initial y  pos
			xscreen / 2,			// length  x
			yscreen / 2,			// length  y
			NULL,				// Overlapped windows have no parent
			NULL,				// Use the window class menu
			hInstance,			// This instance owns this window
			NULL				// Pointer not needed
		);

		// If CreateWindow call failled - just say NO

		if (!hWnd)
			return (FALSE);
	}
	else
		{
			MessageBeep(2);
			return (FALSE);
		}

	hInst = hInstance;			//  save token for this instance

	// set icon for Wterm

	SetClassWord(hWnd, GCW_HICON,
				LoadIcon(
					hInstance,
					MAKEINTRESOURCE(WTERMICON) ));

	// Make the window visible and update client area

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);					// Sends WM_PAINT message

	// initialize global variables

	xCaret		= 0;
	yCaret		= 0;
	response	= 0;
	Connected	= FALSE;
	Num_Bytes	= 0;
	cxBuffer	= MAX_COLUMN ;
	cyBuffer	= MAX_ROW;

	if((pBuffer = malloc (cxBuffer * cyBuffer)) == NULL)
	{
		MessageBox(hWnd,
			"Cannot allocate enought memory"
			" for Wterm.", "iRMX Terminal",
			MB_ICONEXCLAMATION | MB_OK);
		return (FALSE);
	}
	else
		for (y=0; y < cyBuffer; y++)
			for (x=0; x < cxBuffer; x++)
				BUFFER(x, y) = ' ';


	// Initialize input/output buffer @ iRMX comm. area

	//	Note: We are using 32 bytes of DOS memory at the end
	//		of the BIOS communication area, starting at 4E0H

	if( in_buf->count > 29 )
		in_buf->count	= 0;

	out_buf->flag_byte	= 0;
	out_buf->data_byte	= 0;

	// Process windows messages
	// Loop until a WM_QUIT message is received
	//		If there is a message available, process it.
	//		Otherwise check the RMX output buffer and handle
	//		any available characters.

	while(TRUE)
		{
			Message_available = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

			if(Message_available)
			{
				if(msg.message == WM_QUIT) break;

				TranslateMessage((LPMSG)&msg);
				DispatchMessage((LPMSG)&msg);
			}
			else
			{
				if( (Connected) && (in_buf->count > 0) && (!Stop_output))
				{
					// copy data from iRMX to local buffer

					for(Num_Bytes=0; Num_Bytes < in_buf->count; Num_Bytes++)
						MsgBuff[Num_Bytes] = in_buf->data_byte[Num_Bytes];

					in_buf->count = 0;	// tell RMX we read the data
								// then display text
					Process_Text_Buffer(hWnd, Num_Bytes, (LPSTR)MsgBuff);
				}
			}
		}			// end forever loop

	return (msg.wParam);	   // Returns the value from PostQuitMessage
}


//**************************************************************************
//	FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
//   PURPOSE:  Processes messages
//**************************************************************************

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)

HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{

	HDC		hdc;
	TEXTMETRIC	tm;
	LPPOINT		ptMinMaxInfo;

	FARPROC lpProcAbout;		// pointer to the "About" function

	switch (message)
	{
		case WM_SIZE:

			xClientView = LOWORD (lParam);
			yClientView = HIWORD (lParam);

			break;

		case WM_CREATE:

			hdc = GetDC(hWnd);
			SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
			GetTextMetrics(hdc, &tm);
			cxChar= tm.tmAveCharWidth;
			cyChar= tm.tmHeight;
			ReleaseDC(hWnd, hdc);
			break;

		case WM_GETMINMAXINFO:

			// constrain the sizing of the window to 80 by 25 characters.

			ptMinMaxInfo = (LPPOINT) lParam;

			ptMinMaxInfo[1].x = (GetSystemMetrics (SM_CXSCREEN)+2);
			ptMinMaxInfo[1].y = (GetSystemMetrics (SM_CYSCREEN)- 51);

			ptMinMaxInfo[3].x = (GetSystemMetrics (SM_CXSCREEN) / 4);
			ptMinMaxInfo[3].y = (GetSystemMetrics (SM_CYSCREEN)- 51);

			ptMinMaxInfo[4].x = (GetSystemMetrics (SM_CXSCREEN)+2);
			ptMinMaxInfo[4].y = (GetSystemMetrics (SM_CYSCREEN)- 51);

			break;

		case WM_SETFOCUS:

			CreateCaret (hWnd, NULL, (cxChar/2), cyChar);
			SetCaretPos (xCaret * cxChar, yCaret * cyChar);
			ShowCaret(hWnd);
			break;

		case WM_KILLFOCUS:

			HideCaret (hWnd);
			DestroyCaret();
			break;

		case WM_KEYDOWN:

			switch (wParam)
			{
				case VK_HOME:
						out_buf->data_byte = 0x1D;
						Signal_iRMX(hWnd);
						break;
				case VK_LEFT:
						out_buf->data_byte = 0x1F;
						Signal_iRMX(hWnd);
						break;
				case VK_RIGHT:
						out_buf->data_byte = 0x19;
						Signal_iRMX(hWnd);
						break;
				case VK_UP:
						out_buf->data_byte = 0x1E;
						Signal_iRMX(hWnd);
						break;
				case VK_DOWN:
						out_buf->data_byte = 0x1C;
						Signal_iRMX(hWnd);
						break;
				case VK_DELETE:
						out_buf->data_byte = 0x7F;
						Signal_iRMX(hWnd);
						break;
				case VK_PRIOR:		// page up
						out_buf->data_byte = 0x1E;	// up
						Signal_iRMX(hWnd);
						out_buf->data_byte = 0x1D;	// home
						Signal_iRMX(hWnd);
						break;
				case VK_NEXT:		// page down
						out_buf->data_byte = 0x1C;	// down
						Signal_iRMX(hWnd);
						out_buf->data_byte = 0x1D;	// home
						Signal_iRMX(hWnd);
						break;
			}
			break;

		case WM_CHAR:

			// if connected and iRMX buffer is empty,
			//	then copy this keyboard characters to iRMX input buffer

			if ( (!(out_buf->flag_byte)) && (Connected) )
			{
				KB_Char = (char) wParam;
				switch (KB_Char)
				{
					case '\x11':		// Cntrl-Q
							Stop_output = FALSE;
							out_buf->data_byte = KB_Char;
							break;
					case '\x13':		// Cntrl-S
							Stop_output = TRUE;
							out_buf->data_byte = KB_Char;
							break;
					case '\x1B':		// Escape
							out_buf->data_byte = 0x1B;
							break;
					case '\b':
							out_buf->data_byte = 0x7F;
							break;
					case '\t':
					case '\n':			// linefeed work ok
					case '\r':			// cariage return

					default:
							out_buf->data_byte = KB_Char;
							break;
					}

				Signal_iRMX(hWnd);
			}
			break;

		case WM_PAINT:

			Paint_Window(hWnd);
			break;

		case WM_COMMAND:			// message: command from application menu
			switch(wParam)
			{
			case IDM_ABOUT:

				lpProcAbout = MakeProcInstance(About, hInst);

				DialogBox(hInst,		// this instance
					"AboutBox",			// resource to use
					hWnd,				// parent handle
					lpProcAbout);		// About() instance address

				FreeProcInstance(lpProcAbout);
				break;

			case WTERM_EXIT:

				if(Connected)
				{
					Connected = FALSE;
					MessageBox(hWnd,
						"Wterm disconnected!",
						"iRMX Terminal",
						MB_ICONEXCLAMATION | MB_OK);
				}
				PostMessage(hWnd,WM_CLOSE, 0, 0L);
				break;

			case WTERM_CONNECT:			// not yet implemented - just say OK

				if(!Connected)
				{
					if(Make_Connection(hWnd))
					{
						Connected = TRUE;
						MessageBox(hWnd, "Connection successful", "", MB_OK);
					}
					break;

				}
				else
					MessageBox(hWnd, "Connection established", "", MB_OK);

				break;


			case WTERM_SHUTDOWN:		// not yet implemented - just say OK

				{	// not fully implemented,
					// for now, just inform the user that we have
					// remove the connection.

					Connected = FALSE;
					MessageBox(hWnd,
						"Wterm disconnected!",
						"iRMX Terminal",
						MB_ICONEXCLAMATION | MB_OK);
					}

				break;

			default:			// Lets Windows process it
				return (DefWindowProc(hWnd, message, wParam, lParam));
			}
			break;

		case WM_DESTROY:			// message: window being destroyed

			PostQuitMessage(0);
			break;

		default:		// Pass unused message back to Windows

			return (DefWindowProc(hWnd, message, wParam, lParam));

	}		// end switch message

	return (NULL);
}

//**************************************************************************
// Function: Process_Text_Buffer
//
//**************************************************************************

void Process_Text_Buffer(HWND hWnd, WORD Num_Chars, LPSTR lParam)

{
HDC		hdc;
unsigned	i, temp;


hdc = GetDC(hWnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

// scan the text, handle any special characters, and display the rest.

for(i=0; i < Num_Chars; i++)
{
	temp = (char) lParam[i];		// save for routine index case
	HideCaret (hWnd);

	switch (Routine_index)
	{
	case 0 :	// routine$index = 0 - ordinary character
		{
			switch(lParam[i])
			{
			case 0 :			/* CASE 0 -	 */
					break;
			case 1 :			/* CASE 1 - erase rest of screen */

					for (y=(cyBuffer-yCaret-1); y < cyBuffer; y++)
						for (x=(cxBuffer-xCaret); x < cxBuffer; x++)
							BUFFER(x, y) = ' ';
					break;

			case 2 :			/* CASE 2 - erase line */

					y = yCaret;
					for (x=0; x < cxBuffer; x++)
						BUFFER(x, y) = ' ';
					break;

			case 3 :			/* CASE 3 - erase rest of line */

					y = yCaret;
					for (x = xCaret; x < cxBuffer; x++)
						BUFFER(x, y) = ' ';
					break;

			case 4 :			/* CASE 4 - cursor absolute */

					Routine_index = 1;
					break;

			case 5 :			/* CASE 5 - line delete */

					Scroll_Window_Up(yCaret, 0, hWnd);
					break;

			case 6 :			/* CASE 6 - line insert */

					Scroll_Window_Down(yCaret, 0, hWnd);
					break;

			case 7 :			/* CASE 7 - bell */

					MessageBeep(2);
					break;

			case 8 :			/* CASE 8 - backspace */

					if(xCaret > 0)
						--xCaret;
					break;

			case 9 :			/* CASE 9 - tab */

				break;

			case 10 :			/* CASE 0A - linefeed */

					if (yCaret >= cyBuffer-1)
						Scroll_Window_Up(0, 0, hWnd);
					else
						++yCaret;
					break;

			case 11 :			/* CASE 0B - set color */

					Routine_index = 3;
					break;

			case 12 :			/* CASE 0C - erase screen */

					for (y=0; y < cyBuffer; y++)
						for (x=0; x < cxBuffer; x++)
							BUFFER(x, y) = ' ';
					xCaret = 0;
					yCaret = 0;
					break;

			case 13 :			/* CASE 0D - CR */

					xCaret = 0;		// move to beginning of the line
					break;

			case 14 :			/* CASE 0E - literal */
					break;
			case 15 :			/* CASE 0F - set raw mode */
					break;
			case 16 :			/* CASE 10 - set cooked mode */
					break;
			case 17 :			/* CASE 11 - X-ON	*/
					break;
			case 18 :			/* CASE 12		 */
					break;
			case 19 :			/* CASE 13 - X-OFF */
					break;
			case 20 :			/* CASE 14		 */
					break;
			case 21 :			/* CASE 15		 */
					break;
			case 22 :			/* CASE 16		 */
					break;
			case 23 :			/* CASE 17		 */
					break;
			case 24 :			/* CASE 18		 */
					break;

			case 25 :			/* CASE 19 - cursor right */

					if(xCaret >= cxBuffer-1)
					{
						if (yCaret < cyBuffer-1)
						{
							++yCaret;
							xCaret = 0;
						}
					}
					else
						++xCaret;
					break;

			case 26 :			/* CASE 1A  */
					break;
			case 27 :			/* CASE 1B */
					break;

			case 28 :			/* CASE 1C - cursor down */

					if (yCaret != cyBuffer-1)
						++yCaret;
					break;

			case 29 :			/* CASE 1D - cursor home */

					xCaret = 0;
					yCaret = 0;
					break;

			case 30 :			/* CASE 1E - cursor up */

					if(yCaret != 0)
						--yCaret;
					break;

			case 31 :			/* CASE 1F - cursor left */

					if(xCaret == 0)
					{
						if (yCaret > 0)
						{
							--yCaret;
							xCaret = cxBuffer-1;
						}
					}
					else
						--xCaret;
					break;

			default:		// all displayable characters

				// Filter out all characters not supported by windows

				if((lParam[i] < 32)
					|| ((lParam[i] > 126) && (lParam[i] < 145))
					|| ((lParam[i] > 146) && (lParam[i] < 160)))
					break;

				// add char to screen buffer

				BUFFER(xCaret, yCaret) = (char) lParam[i];
				TextOut(hdc,
						xCaret * cxChar,
						yCaret * cyChar,
						&BUFFER(xCaret, yCaret),
						1);

				if (++xCaret > cxBuffer-1)
				{
					xCaret = 0;
					if (yCaret >= cyBuffer-1)
						Scroll_Window_Up(0, 0, hWnd);
					else
						++yCaret;
				}
				break;

			}	/* CASE char */

		}	/* of case 0	=> routine$index = 0 */

		InvalidateRect(hWnd, NULL, FALSE);
		break;

	case 1 :	// routine$index = 1 - second byte of absolute cursor position
		{
			Routine_index = 2;
			temp -= 0x20;
			if (temp < cyBuffer)
				yCaret = temp;
			else
				yCaret = 0;
		}
		break;

	case 2 :	// routine$index = 2 - third byte of absolute cursor position
		{
			Routine_index = 0;
			temp -= 0x20;
			if (temp < cxBuffer)
				xCaret = temp;
			else
				xCaret = 0;
		}
		break;
	case 3 :	// routine$index = 3 - second byte of set foreground color
		{
			Routine_index = 0;
		}
		break;

	default:
		break;

	}	// end of (switch (Routine_index))

SetCaretPos( xCaret *cxChar, yCaret * cyChar);
ShowCaret (hWnd);

}	// end	for(i=0; i < Num_Chars; i++)

ReleaseDC(hWnd, hdc);
}

//**************************************************************************
// Function: Scroll window down
//
//**************************************************************************

void Scroll_Window_Down(unsigned row, unsigned column, HWND hWnd)
{
	static	int		x, y, z;
	unsigned char Temp;

	// scroll down one line starting at the line containing the caret

	for (z=(cyBuffer-1); z > row; z--)		// namly
	{
		for (x=column; x < cxBuffer; x++)
		{
			y = z-1;
			Temp = BUFFER(x, y);
			y = z;
			BUFFER(x, y) = Temp;
		}
	}
	// blank the line containing the caret

	y = row;
	for (x=0; x < cxBuffer; x++)
		BUFFER(x, y) = ' ';

//	SendMessage(hWnd, WM_PAINT, NULL, 1L);
	InvalidateRect(hWnd, NULL, FALSE);
	Paint_Window(hWnd);
}

//**************************************************************************
// Function: Scroll window up
//
//**************************************************************************

void Scroll_Window_Up(unsigned row, unsigned column, HWND hWnd)
{
	static	int		x, y, z;
	unsigned char Temp;

	// scroll up one line

	for (z=row; z < cyBuffer-1; z++)
	{
		for (x=column; x < cxBuffer; x++)
		{
			y = z+1;
			Temp = BUFFER(x, y);
			y = z;
			BUFFER(x, y) = Temp;
		}
	}
	// blank the last line

	y = cyBuffer-1;
	for (x=0; x < cxBuffer; x++)
		BUFFER(x, y) = ' ';

//	SendMessage(hWnd, WM_PAINT, NULL, 1L);
	InvalidateRect(hWnd, NULL, FALSE);
	Paint_Window(hWnd);
}

//**************************************************************************
// Function:	About(HWND, unsigned, WORD, LONG)
// 				Processes the About message and display dialog box.
//**************************************************************************

BOOL FAR PASCAL About( hDlg, message, wParam, lParam )

HWND		hDlg;
unsigned	message;
WORD		wParam;
LONG		lParam;

{
	switch(message)					  // message: initialize dialog box
	{
		case WM_COMMAND:			  // message: received a command
		case WM_LBUTTONDOWN:

				EndDialog( hDlg, TRUE );
				return TRUE;

		case WM_INITDIALOG:
				return TRUE;

		default:					  // Didn't process a message
				return FALSE;
	}
}

//**************************************************************************
//	Function: Signal_iRMX
//
//**************************************************************************

void Signal_iRMX(HWND hWnd)
{
	out_buf->flag_byte = TRUE;

	// Send a message to iRMX driver

	RQSendMessage(Kb_Mux, segment, response, &except);
	if (except != EOK)
	{
		MessageBox(hWnd,
			"SendMessage error!",
			"iRMX Terminal",
			MB_ICONEXCLAMATION | MB_OK);

		// Add your own error handler here
	}
}

//**************************************************************************
// Function:  Paint window
//
//**************************************************************************

void Paint_Window(HWND hWnd)
{
	PAINTSTRUCT		ps;
	HDC			hdc;
	RECT			rClRect;

	unsigned int Display_mem_ptr;
	unsigned int ax;
	static short Max_Screen_lines;	// screen lines when resized
					// for now, it is not needed

	hdc = BeginPaint(hWnd, &ps);

	Max_Screen_lines = yClientView/cyChar;

	Display_mem_ptr = cyBuffer-Max_Screen_lines;

	SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

	ax = 0;	// not needed when use use: for (y=0; y < cyBuffer; y++, ax++)

	for (y=Display_mem_ptr; y < cyBuffer; y++, ax++)
	{
		TextOut(hdc,
			0,
			ax * cyChar,
			&BUFFER(0, y),
			cxBuffer);
	}

	EndPaint(hWnd, &ps);
}


//**************************************************************************
// Function:  Make Connection to iRMX driver
//
//**************************************************************************

BOOL Make_Connection(HWND hWnd)
{
		// Make some calls to the RT extension to find out if the RMX
		// keyboard mail box is present.
		// NOTE:
		//		If the RT extension is not configured then
		//		Windows (and DOS) will get confused!!!

		roottoken = RQGetTaskTokens(rootjob,&except);

		// Display debug messages - you can delete the following four lines
		// and supply your own error handler
		if (except == EOK)
			MessageBox(hWnd, "gettasktoken OK", "", MB_OK);
		else
		{
			MessageBox(hWnd, "gettasktoken error", "", MB_OK);
			return(FALSE);
		}

		vm86j = RQLookupObject(roottoken,&vm86[0],0,&except);

		// Display debug messages - you can delete the following four lines
		// and supply your own error handler
		if (except == EOK)
			MessageBox(hWnd, "lookupobject root OK", "", MB_OK);
		else
		{
			MessageBox(hWnd, "lookupobject root error", "", MB_OK);
			return(FALSE);
		}

		Kb_Mux = RQLookupObject(vm86j,&kb_mb[0],0,&except);
		segment	= Kb_Mux;

		// Display debug messages - you can delete the following four lines
		// and supply your own error handler
		if (except == EOK)
			MessageBox(hWnd, "lookupobject vm86 OK", "", MB_OK);
		else
		{
			MessageBox(hWnd, "lookupobject vm86 error", "", MB_OK);
			return(FALSE);
		}
	return(TRUE);
}

//*************************  End Of Source Code Module *********************
