From ab561cad5a4c06b08205132b38b0c343833af0eb Mon Sep 17 00:00:00 2001 From: Emanuele Aliberti Date: Sun, 17 Mar 2002 22:15:39 +0000 Subject: [PATCH] Initial code for the VT-100 terminal emulator that runs in the W32 subsystem and connects to the PSXSS.EXE (WIP). svn path=/trunk/; revision=2732 --- posix/apps/csrterm/00readme.txt | 90 +++ posix/apps/csrterm/Makefile | 23 + posix/apps/csrterm/console.c | 1243 +++++++++++++++++++++++++++++++ posix/apps/csrterm/csrterm.c | 582 +++++++++++++++ posix/apps/csrterm/csrterm.h | 53 ++ posix/apps/csrterm/csrterm.rc | 38 + posix/apps/csrterm/readme.txt | 79 ++ posix/apps/csrterm/vt100.c | 1124 ++++++++++++++++++++++++++++ posix/apps/csrterm/vt100.h | 320 ++++++++ 9 files changed, 3552 insertions(+) create mode 100644 posix/apps/csrterm/00readme.txt create mode 100644 posix/apps/csrterm/Makefile create mode 100644 posix/apps/csrterm/console.c create mode 100644 posix/apps/csrterm/csrterm.c create mode 100644 posix/apps/csrterm/csrterm.h create mode 100644 posix/apps/csrterm/csrterm.rc create mode 100644 posix/apps/csrterm/readme.txt create mode 100644 posix/apps/csrterm/vt100.c create mode 100644 posix/apps/csrterm/vt100.h diff --git a/posix/apps/csrterm/00readme.txt b/posix/apps/csrterm/00readme.txt new file mode 100644 index 00000000000..4172f568646 --- /dev/null +++ b/posix/apps/csrterm/00readme.txt @@ -0,0 +1,90 @@ +08/08/96 - John L. Miller, johnmil@cs.cmu.edu, johnmil@jprc.com + +FILES INCLUDED: + 00readme.txt - this file + VT100.H - Definitions for VT-100 emulator. + VT100.C - Front end parsing code for VT-100 emulator + CONSOLE.C - Back-end code to allow VT-100 in WinNt/Win95 console + +Many UNIX users take terminals for granted, as something you get for free +with the operating system. Unfortunately, this isn't the case for many +non-unix operating systems, especially PC-based ones. After a number of +projects, I decided it would be nice if there was source publicly available +for doing VT-100 emulation. + +The files included with this distribution are not a complete implementation +of VT-100 terminal emulation, but do provide complete enough coverage to +use many vt-100 functions over the network. For instance, its enough to +use EMACS to edit, or to connect up to your favorite mud with ANSI color +and graphics characters. + +The VT-100 emulator is broken into two parts. The first is the front end, +vt100.c and vt100.h. These files were written to be fairly device-independant, +though admittedly if you're running under a 16-bit operating system instead +of a 32-bit, you might need to change some of the 'int' values to 'long.' +Otherwise, it should work 'as-is'. + +The second part is a back-end. The back-end is responsible for doing the +workhorse activities. The front-end parses a character stream, and decides +whether to clear a part of the screen, or move the cursor, or switch fonts. +Then it calls routines in the back-end to perform these activities. + +The back-end functions are, for the most part, very straight forward, and +quite easy to implement compared to writing a vt-100 emulator from scratch. +CONSOLE.C is a back-end for use in console (command, dos) windows under +Windows 95 and Windows NT. This console vt-100 emulator is also being used +in my TINTIN-III port and kerberized encrypted telnet port. + + +TO USE THIS VT-100 EMULATOR: + +First, it's intended to be linked directly into source code. You'll need +to change printf's and puts' in your source code to call vtprintf() and +vtputs() instead. You can add additional functions to vt100.c as you see +fit to handle other output functions like putchar() and write(). Another +routine you may want to use is vtProcessedTextOut(), which accepts a +buffer to output, and a count of characters in that buffer. + +Second, you need to make sure that your source code calls vtInitVT100() +before it does ANYTHING else. This initializes the vt-100 emulator. + +Third, if you want to use this VT-100 emulator with anything besides +Windows NT and Windows 95 consoles, you'll need to implement your own +back end. The list of functions you will need to supply, as well as what +they need to do is contained in vt100.h. The list (minus descriptions) +is as follows: + + int beInitVT100Terminal(); + int beAbsoluteCursor(int row, int col); + int beOffsetCursor(int row, int column); + int beRestoreCursor(void); + int beSaveCursor(void); + int beSetTextAttributes(int fore, int back); + int beRawTextOut(char *text, int len); + int beEraseText(int rowFrom, int colFrom, int rowTo, int colTo); + int beDeleteText(int rowFrom, int colFrom, int rowTo, int colTo); + int beInsertRow(int row); + int beTransmitText(char *text, int len); + int beAdvanceToTab(void); + int beClearTab(int col); + int beSetScrollingRows(int fromRow, int toRow); + int beRingBell(void); + int beGetTermMode(); + int beSetTermMode(int newMode); + +For details on what each of these does, read the descriptions of each +function included in vt100.h, and read over CONSOLE.C for examples. I've +included copious comments in all of these files to try to make them as +easy to use as possible. + +In any case, it should be easier than writing a VT-100 emulator from +scratch. + +KNOWN BUGS - + +o Many features of VT-100 emulation aren't implemented. This includes + support for graphics character set 0 and many of the + answerback functions. + +Well, good luck! + diff --git a/posix/apps/csrterm/Makefile b/posix/apps/csrterm/Makefile new file mode 100644 index 00000000000..c93f44eae42 --- /dev/null +++ b/posix/apps/csrterm/Makefile @@ -0,0 +1,23 @@ +# $Id: Makefile,v 1.1 2002/03/17 22:15:39 ea Exp $ +# +# POSIX+ Terminal Emulator that runs in the W32 subsystem. +# +PATH_TO_TOP=../../../.. +PATH_TO_TOP_PSX=../.. + +TARGET_NAME=csrterm +TARGET_TYPE=program +TARGET_APPTYPE=console +TARGET_CFLAGS =-I$(PATH_TO_TOP_PSX)/include +TARGET_SDKLIBS=ntdll.a kernel32.a +TARGET_OBJECTS=\ + $(TARGET_NAME).o \ + console.o \ + vt100.o \ + $(TARGET_NAME).coff + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +# EOF diff --git a/posix/apps/csrterm/console.c b/posix/apps/csrterm/console.c new file mode 100644 index 00000000000..66702c4124f --- /dev/null +++ b/posix/apps/csrterm/console.c @@ -0,0 +1,1243 @@ +/* console.c + * + * AUTHOR: John L. Miller, johnmil@cs.cmu.edu / johnmil@jprc.com + * DATE: 8/4/96 + * + * Copyright (c) 1996 John L. Miller + * + * Full permission is granted to use, modify and distribute + * this code, provided: + * 1) This comment field is included in its entirity + * 2) No money is charged for any work including or based on + * portions of this code. + * + * If you're a nice person and find this useful, I'd appreciate a + * note letting me know about it. e-mail is usually what spurs me + * on to improve and support software I've written. + * + * This file contains functions intended to provide the back + * end to a console window for my semi-vt100 emulator. + */ + +/* Note - one HUGE difference between console windows and terminal + * windows. Console windows displays start at (0,0). Terminal displays + * start at (1,1). YUCK! + */ + +#include +#include "vt100.h" + +int topScrollRow=TOP_EDGE; +int bottomScrollRow=BOTTOM_EDGE; + +/* This variable will contain terminal configuration flags, such as + * reverse/standard video, whether wrapping is enabled, and so on. + */ +int conTermMode; + +/* Variable to hold the cursor position for save/restore cursor calls */ +COORD cursorPosSave={1,1}; + +/* Handles to the current console for input and output */ +HANDLE hConIn, hConOut; + +/* Array of all the tabs which are currently set. Ironically, I think the + * primary emulator can CLEAR tags, but not set them. + */ +int tabSet[132]={0}; +int numTabs = 0; + + +/* This section contains console-specific color information. NT consoles can + * have Red, blue, green, and intensity flags set. Hence, 4 con_colors. + */ +#define NUM_CON_COLORS 4 + +/* Foreground and background colors are separated out */ +int conForeColors, conBackColors; + +/* mapping between foreground and background console colors: needed + * when reverse video is being used + */ +int conColorMapping[NUM_CON_COLORS][2] = +{ + {FOREGROUND_RED, BACKGROUND_RED}, + {FOREGROUND_BLUE, BACKGROUND_BLUE}, + {FOREGROUND_GREEN, BACKGROUND_GREEN}, + {FOREGROUND_INTENSITY, BACKGROUND_INTENSITY} +}; + + +/* Device-independant foreground and background flags stored here. + * probably a bad division of labor, but hey, since we don't use + * all of their flags in our console stuff (and hence can't retrieve + * them), the information has to live SOMEWHERE. + */ + +int scForeFlags, scBackFlags; + +/* Defines for array indexing for translation of flags */ +#define SC_FLAG 0 +#define CONSOLE_FLAG 1 + +/* Color mapping between SC (the vt-100 emulator device independant + * flags) and NT console character specific flags. Flags which have no analog + * are set to 0. Note that all global character attributes (character set + * underline, bold, reverse) are all stored in foreground only + */ +const int scForeMapping[NUM_SC_ATTRIBUTES][2] = +{ + {SC_RED,FOREGROUND_RED}, + {SC_GREEN,FOREGROUND_GREEN}, + {SC_BLUE,FOREGROUND_BLUE}, + {SC_BOLD,FOREGROUND_INTENSITY}, + {SC_UL,0}, + {SC_BL,0}, + {SC_RV,0}, + {SC_ASCII,0}, + {SC_G0,0}, + {SC_G1,0}, + {SC_GRAPHICS,0} +}; + +/* Background color mapping between SC and console */ +const int scBackMapping[NUM_SC_ATTRIBUTES][2] = +{ + {SC_RED,BACKGROUND_RED}, + {SC_GREEN,BACKGROUND_GREEN}, + {SC_BLUE,BACKGROUND_BLUE}, + {SC_BOLD,BACKGROUND_INTENSITY}, + {SC_UL,0}, + {SC_BL,0}, + {SC_RV,0}, + {SC_ASCII,0}, + {SC_G0,0}, + {SC_G1,0}, + {SC_GRAPHICS,0} +}; + +/* These arrays map character vals 0-255 to new values. + * Since the G0 and G1 character sets don't have a direct analog in + * NT, I'm settling for replacing the ones I know what to set them + * to. + */ +char G0Chars[256]; +char G1Chars[256]; + +/* These four sets of variables are just precomputed combinations of + * all the possible flags to save time for masking. + */ +int allFore[2], allBack[2]; +int bothFore[2], bothBack[2]; + + +/* FORWARD DECLARATIONS */ +int +RawPrintLine( + char *text, + int len, + int scrollAtEnd + ); + +int +Scroll( + int row + ); +/* END FORWARD DECLARATIONS */ + + + +/* beInitVT100Terminal() - + * + * This function is called by the VT100 emulator as soon as the + * front-end terminal is initialized. It's responsible for setting + * initial state of the terminal, and initing our many wacky variables. + */ + +int +beInitVT100Terminal() +{ + int i; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + /* Set tabs to every 8 spaces initially */ + numTabs = 0; + for (numTabs=0; numTabs < 132/8; numTabs++) + tabSet[numTabs] = (numTabs+1)*8; + + /* Init the cursor save position to HOME */ + cursorPosSave.X = 1; + cursorPosSave.Y = 1; + + /* Disable scrolling window limits */ + topScrollRow=TOP_EDGE; + bottomScrollRow=BOTTOM_EDGE; + + conTermMode = ANSI_MODE|WRAP_MODE|REPEAT_MODE; + + hConIn = GetStdHandle(STD_INPUT_HANDLE); + hConOut = GetStdHandle(STD_OUTPUT_HANDLE); + + /* Init our time-saving mask variables */ + allFore[SC_FLAG] = allBack[SC_FLAG] = 0; + allFore[CONSOLE_FLAG] = allBack[CONSOLE_FLAG] = 0; + bothFore[SC_FLAG] = bothBack[SC_FLAG] = 0; + bothFore[CONSOLE_FLAG] = bothBack[CONSOLE_FLAG] = 0; + + for (i=0; i= csbi.dwSize.X) + { + cursorPos.X -= csbi.dwSize.X; + cursorPos.Y += 1; + } + + if (cursorPos.Y < 0) + cursorPos.Y = 0; + + SetConsoleCursorPosition(hConOut, cursorPos); + + return(0); +} + + +/* beRestoreCursor - + * + * Saved cursor position should be stored in a static + * variable in the back end. This function restores the + * cursor to the position stored in that variable. + */ + +int +beRestoreCursor(void) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + COORD cursorPos; + + GetConsoleScreenBufferInfo(hConOut, &csbi); + + cursorPos = csbi.dwCursorPosition; + + cursorPos.Y += cursorPosSave.Y; + + SetConsoleCursorPosition(hConOut, cursorPos); + + return(0); +} + +/* beSaveCursor - + * + * The back-end should maintain a static variable with the + * last STORED cursor position in it. This function replaces + * the contents of that variable with the current cursor position. + * The cursor may be restored to this position by using the + * beRestoreCursor function. + */ + +int +beSaveCursor(void) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(hConOut, &csbi); + cursorPosSave = csbi.dwCursorPosition; + + cursorPosSave.Y -= csbi.srWindow.Top; + + return(0); +} + + +/* beGetTextAttributes - + * + * given a pointer to 'fore'ground and 'back'ground ints, + * fill them with a device-independant description of the + * current foreground and background colors, as well as any + * font information in the foreground variable. + */ + +int +beGetTextAttributes( + int *fore, + int *back + ) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + int i; + + /* Since it's entirely possible that the text attributes were + * changed without our terminal being notified, we might as well + * make sure they're accurate. + */ + + /* First, strip out everything in the screen buffer variables + * that we can detect + */ + + scForeFlags &= ~bothFore[SC_FLAG]; + scBackFlags &= ~bothBack[SC_FLAG]; + + /* Now, find out what the real settings are, and set the + * flag values accordingly. + */ + + GetConsoleScreenBufferInfo(hConOut, &csbi); + + + /* If reverse video is set, we need to reverse our color mappings + * before any calculations get made. + */ + + if (scForeFlags & SC_RV) + { + int tmpFore, tmpBack; + + tmpFore = csbi.wAttributes & conForeColors; + tmpBack = csbi.wAttributes & conBackColors; + + csbi.wAttributes &= ~(conForeColors | conBackColors); + + for (i=0; i (csbi.dwSize.X - csbi.dwCursorPosition.X)) + end = (csbi.dwSize.X - csbi.dwCursorPosition.X); + + /* If we're in non-ascii mode, we need to do a little + * magic to get the right characters out. + */ + + if (scForeFlags & SC_G1) + { + for (i=0; i toRow) + return(-1); + + topScrollRow = fromRow; + bottomScrollRow = toRow; + + return(0); +} + + +/* beRingBell - + * + * Ring the system bell once. + */ + +int +beRingBell(void) +{ + MessageBeep((UINT)-1); + return(0); +} + + +/* beGetTermMode - + * + * Return the value of conTermMode, which is the terminal settings which + * can be queried/set by [?#h/l. + */ + +int +beGetTermMode() +{ + return(conTermMode); +} + + +/* beSetTermMode - + * + * Set the terminal as requested, assuming we can. Right now we only handle a + * couple of the possible flags, but we store many of the others. + */ + +int beSetTermMode( + int newMode + ) +{ + int i, changes; + CONSOLE_SCREEN_BUFFER_INFO csbi; + COORD newSize; + SMALL_RECT newWindowRect; + DWORD dwConMode; + + changes = conTermMode ^ newMode; + + /* For each bit set in 'changes', determine the + * appropriate course of action. + */ + + for (i=0; i < NUM_TERM_ATTR_MODES; i++) + { + if (termAttrMode[i] & changes) + { + switch(termAttrMode[i]) + { + case COL132_MODE: + GetConsoleScreenBufferInfo(hConOut, &csbi); + newSize.Y = csbi.dwSize.Y; + newSize.X = (newMode & COL132_MODE) ? 132 : 80; + if (newSize.X != csbi.dwSize.X) + { + newWindowRect.Top = csbi.srWindow.Top; + newWindowRect.Bottom = csbi.srWindow.Bottom; + newWindowRect.Left = 0; + newWindowRect.Right = csbi.dwSize.X - 1; + SetConsoleScreenBufferSize(hConOut, newSize); + SetConsoleWindowInfo(hConOut, TRUE, &newWindowRect); + } + break; + + case WRAP_MODE: + GetConsoleMode(hConOut,&dwConMode); + if ( (newMode & WRAP_MODE) + && (! (dwConMode & ENABLE_WRAP_AT_EOL_OUTPUT)) + ) + { + dwConMode |= ENABLE_WRAP_AT_EOL_OUTPUT; + SetConsoleMode(hConOut, dwConMode); + } + if ( (!(newMode & WRAP_MODE)) + && (dwConMode & ENABLE_WRAP_AT_EOL_OUTPUT) + ) + { + dwConMode &= ~ENABLE_WRAP_AT_EOL_OUTPUT; + SetConsoleMode(hConOut, dwConMode); + } + break; + + case CURSORAPPL_MODE: + case ANSI_MODE: + case SMOOTHSCROLL_MODE: + case REVSCREEN_MODE: + case ORIGINREL_MODE: + case REPEAT_MODE: + /* bugbug - we don't handle any of these. */ + break; + } + } + } + + conTermMode = newMode; + + return(0); +} diff --git a/posix/apps/csrterm/csrterm.c b/posix/apps/csrterm/csrterm.c new file mode 100644 index 00000000000..15862a3f292 --- /dev/null +++ b/posix/apps/csrterm/csrterm.c @@ -0,0 +1,582 @@ +/* $Id: csrterm.c,v 1.1 2002/03/17 22:15:39 ea Exp $ + * + * PROJECT : ReactOS Operating System / POSIX+ Environment Subsystem + * DESCRIPTION: CSRTERM - A DEC VT-100 terminal emulator for the PSX subsystem + * DESCRIPTION: that runs in the Win32 subsystem. + * COPYRIGHT : Copyright (c) 2001-2002 Emanuele Aliberti + * LICENSE : GNU GPL v2 + * DATE : 2001-05-05 + * AUTHOR : Emanuele Aliberti + * NOTE : This IS a Win32 program, but will fail if the PSX subsystem + * NOTE : is not installed. The PSX subsystem consists of two more + * NOTE : files: PSXSS.EXE, PSXDLL.DLL. + * WARNING : If you use this program under a real NT descendant, be + * WARNING : sure to have disabled the PSX subsystem. + * -------------------------------------------------------------------- + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write + * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + * MA 02139, USA. + * + * -------------------------------------------------------------------- + * 2002-03-16 EA Today it actually compiled. + */ +#include +#include +#include + +#define NTOS_MODE_USER +#include +#include + +#include "vt100.h" +#include "csrterm.h" + +/*** OPTIONS *********************************************************/ + +#define PRIVATE static + +#define INPUT_QUEUE_SIZE 32 + +#ifdef NDEBUG +#define TRACE +#else +#define TRACE OutputDebugString(__FUNCTION__) +#endif + +/*** GLOBALS *********************************************************/ + +PRIVATE LPCSTR MyName = "CSRTERM"; +PRIVATE CSRTERM_SESSION Session; + +/*** PRIVATE FUNCTIONS ***********************************************/ +VOID STDCALL Debug_Print (LPCSTR Format, ...) +{ + CHAR Buffer [512]; + va_list ArgumentPointer; + + va_start(ArgumentPointer, Format); + vsprintf(Buffer, Format, ArgumentPointer); + va_end(ArgumentPointer); + OutputDebugStringA (Buffer); +} +/********************************************************************** + * OutPort/2 PRIVATE + * + * DESCRIPTION + * Notify to PSXSS that input data is ready by sending a + * software interrupt on the \POSIX+\SessionPort port. + */ +PRIVATE DWORD STDCALL OutPort (PCHAR Buffer, ULONG Size) +{ + NTSTATUS Status; + PSX_TERMINAL_READ TerminalRead; +TRACE; + if (Size > 0) + { + /* LPC */ + TerminalRead.Header.MessageType = LPC_NEW_MESSAGE; + TerminalRead.PsxHeader.Context = PSX_CONNECTION_TYPE_TERMINAL; + TerminalRead.PsxHeader.Procedure = PSX_TERMINAL_INTERRUPT; + /* Terminal I/O */ + TerminalRead.Size = Size; + RtlCopyMemory (TerminalRead.Buffer, Buffer, Size); +#if 0 + Status = NtRequestWaitReplyPort ( + Session.ServerPort.Handle, + & TerminalRead + /* FIXME */ + ); +#endif + if (!NT_SUCCESS(Status)) + { + vtprintf ("%s: %s: NtRequestWaitReplyPort failed with %08x\n", + MyName, __FUNCTION__, Status); + return 0; + } + } + return Size; +} +/********************************************************************** + * ProcessConnectionRequest/1 PRIVATE + * + * DESCRIPTION + * Initialize our data for managing the control connection + * initiated by the PSXSS.EXE process. + */ +PRIVATE NTSTATUS STDCALL ProcessConnectionRequest (PPSX_MAX_MESSAGE Request) +{ +TRACE; + Session.SsLinkIsActive = TRUE; + return STATUS_SUCCESS; +} +/********************************************************************** + * ProcessRequest/1 PRIVATE + * + * DESCRIPTION + * + */ +PRIVATE NTSTATUS STDCALL ProcessRequest (PPSX_MAX_MESSAGE Request) +{ +TRACE; + /* TODO */ + vtprintf("TEST VT-100\n"); + + return STATUS_SUCCESS; +} +/********************************************************************** + * PsxSessionPortListener/1 PRIVATE + * + * DESCRIPTION + * Manage messages from the PSXSS, that is LPC messages we get + * from the PSXSS process to our \POSIX+\Sessions\P port. + * + * NOTE + * This function is the thread 's entry point created in + * CreateSessionObiects(). + */ +PRIVATE DWORD STDCALL PsxSessionPortListener (LPVOID Arg) +{ + NTSTATUS Status; + LPC_TYPE RequestType; + PSX_MAX_MESSAGE Request; + PPSX_MAX_MESSAGE Reply = NULL; + BOOL NullReply = FALSE; + +TRACE; + + while (TRUE) + { + Reply = NULL; + while (!NullReply) + { + Status = NtReplyWaitReceivePort ( + Session.Port.Handle, + 0, + (PLPC_MESSAGE) Reply, + (PLPC_MESSAGE) & Request + ); + if (!NT_SUCCESS(Status)) + { + break; + } + RequestType = PORT_MESSAGE_TYPE(Request); + switch (RequestType) + { + case LPC_CONNECTION_REQUEST: + ProcessConnectionRequest (& Request); + NullReply = TRUE; + continue; + case LPC_CLIENT_DIED: + case LPC_PORT_CLOSED: + case LPC_DEBUG_EVENT: + case LPC_ERROR_EVENT: + case LPC_EXCEPTION: + NullReply = TRUE; + continue; + default: + if (RequestType != LPC_REQUEST) + { + NullReply = TRUE; + continue; + } + } + Reply = & Request; + Reply->PsxHeader.Status = ProcessRequest (& Request); + NullReply = FALSE; + } + if ((STATUS_INVALID_HANDLE == Status) || + (STATUS_OBJECT_TYPE_MISMATCH == Status)) + { + break; + } + } + Session.SsLinkIsActive = FALSE; + TerminateThread (GetCurrentThread(), Status); +} +/********************************************************************** + * CreateSessionObiects/1 PRIVATE + * + * DESCRIPTION + * Create the session objects which are mananged by our side: + * + * \POSIX+\Sessions\P + * \POSIX+\Sessions\D + */ +PRIVATE NTSTATUS STDCALL CreateSessionObjects (DWORD Pid) +{ + NTSTATUS Status; + ULONG Id = 0; + OBJECT_ATTRIBUTES Oa; + +TRACE; + + + /* Critical section */ + Status = RtlInitializeCriticalSection (& Session.Lock); + if (!NT_SUCCESS(Status)) + { + vtprintf ( + "%s: %s: RtlInitializeCriticalSection failed with %08x\n", + MyName, __FUNCTION__, Status); + return Status; + } + /* Port and port management thread */ + swprintf ( + Session.Port.NameBuffer, + PSX_NS_SESSION_PORT_TEMPLATE, + PSX_NS_SUBSYSTEM_DIRECTORY_NAME, + PSX_NS_SESSION_DIRECTORY_NAME, + Pid + ); + RtlInitUnicodeString (& Session.Port.Name, Session.Port.NameBuffer); + InitializeObjectAttributes (& Oa, & Session.Port.Name, 0, NULL, NULL); + Status = NtCreatePort (& Session.Port.Handle, & Oa, 0, 0, 0x10000); + if (!NT_SUCCESS(Status)) + { + RtlDeleteCriticalSection (& Session.Lock); + vtprintf ("%s: %s: NtCreatePort failed with %08x\n", + MyName, __FUNCTION__, Status); + return Status; + } + Session.Port.Thread.Handle = + CreateThread ( + NULL, + 0, + PsxSessionPortListener, + 0, + CREATE_SUSPENDED, + & Session.Port.Thread.Id + ); + if ((HANDLE) NULL == Session.Port.Thread.Handle) + { + Status = (NTSTATUS) GetLastError(); + NtClose (Session.Port.Handle); + RtlDeleteCriticalSection (& Session.Lock); + vtprintf ("%s: %s: CreateThread failed with %d\n", + MyName, __FUNCTION__, Status); + return Status; + } + /* Section */ + swprintf ( + Session.Section.NameBuffer, + PSX_NS_SESSION_DATA_TEMPLATE, + PSX_NS_SUBSYSTEM_DIRECTORY_NAME, + PSX_NS_SESSION_DIRECTORY_NAME, + Pid + ); + RtlInitUnicodeString (& Session.Section.Name, Session.Section.NameBuffer); + InitializeObjectAttributes (& Oa, & Session.Section.Name, 0, 0, 0); + Status = NtCreateSection ( + & Session.Section.Handle, + 0, /* DesiredAccess */ + & Oa, + NULL, /* SectionSize OPTIONAL */ + PAGE_READWRITE, /* Protect 4 */ + SEC_COMMIT, /* Attributes */ + 0 /* FileHandle: 0=pagefile.sys */ + ); + if (!NT_SUCCESS(Status)) + { + NtClose (Session.Port.Handle); + NtTerminateThread (Session.Port.Thread.Handle, Status); + RtlDeleteCriticalSection (& Session.Lock); + vtprintf ("%s: %s: NtCreateSection failed with %08x\n", + MyName, __FUNCTION__, Status); + return Status; + } + Session.Section.BaseAddress = NULL; + Session.Section.ViewSize = 0; + Status = NtMapViewOfSection ( + Session.Section.Handle, + NtCurrentProcess(), + & Session.Section.BaseAddress, + 0, /* ZeroBits */ + 0, /* Commitsize */ + 0, /* SectionOffset */ + & Session.Section.ViewSize, + ViewUnmap, + 0, /* AllocationType */ + PAGE_READWRITE /* Protect 4 */ + ); + if (!NT_SUCCESS(Status)) + { + NtClose (Session.Port.Handle); + NtTerminateThread (Session.Port.Thread.Handle, Status); + NtClose (Session.Section.Handle); + RtlDeleteCriticalSection (& Session.Lock); + vtprintf ("%s: %s: NtMapViewOfSection failed with %08x\n", + MyName, __FUNCTION__, Status); + return Status; + } + return Status; +} + +/********************************************************************** + * CreateTerminalToPsxChannel/0 PRIVATE + * + * DESCRIPTION + * + */ +PRIVATE NTSTATUS STDCALL CreateTerminalToPsxChannel (VOID) +{ + PSX_CONNECT_PORT_DATA ConnectData; + ULONG ConnectDataLength = sizeof ConnectData; + SECURITY_QUALITY_OF_SERVICE Sqos; + NTSTATUS Status; + +TRACE; + + + /* + * Initialize the connection data object before + * calling PSXSS. + */ + ConnectData.ConnectionType = PSX_CONNECTION_TYPE_TERMINAL; + ConnectData.Version = PSX_LPC_PROTOCOL_VERSION; + /* + * Try connecting to \POSIX+\SessionPort. + */ + Status = NtConnectPort ( + & Session.ServerPort.Handle, + & Session.ServerPort.Name, + & Sqos, + NULL, + NULL, + 0, + & ConnectData, + & ConnectDataLength + ); + if (STATUS_SUCCESS != Status) + { + vtprintf ("%s: %s: NtConnectPort failed with %08x\n", + MyName, __FUNCTION__, Status); + return Status; + } + Session.Identifier = ConnectData.PortIdentifier; + return STATUS_SUCCESS; +} + +/********************************************************************** + * InitializeSsIoChannel PRIVATE + * + * DESCRIPTION + * Create our objects in the system name space + * (CreateSessionObjects) and then connect to the session port + * (CreateControChannel). + */ +PRIVATE NTSTATUS STDCALL InitializeSsIoChannel (VOID) +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Pid = GetCurrentProcessId(); + +TRACE; + + + Status = CreateSessionObjects (Pid); + if (STATUS_SUCCESS != Status) + { + vtprintf ("%s: %s: CreateSessionObjects failed with %08x\n", + MyName, __FUNCTION__, Status); + return Status; + } + Status = CreateTerminalToPsxChannel (); + if (STATUS_SUCCESS != Status) + { + vtprintf ("%s: %s: CreateTerminalToPsxChannel failed with %08x\n", + MyName, __FUNCTION__, Status); + return Status; + } + return STATUS_SUCCESS; +} +/********************************************************************** + * PsxCreateLeaderProcess/1 PRIVATE + * + * DESCRIPTION + * Create a new PSXSS process. + */ +PRIVATE NTSTATUS STDCALL PsxCreateLeaderProcess (char * Command) +{ + +TRACE; + + if (NULL == Command) + { + Command = "/bin/sh"; + } + /* TODO: request PSXSS to init the process slot */ + return STATUS_NOT_IMPLEMENTED; +} +/********************************************************************** + * PrintInformationProcess/0 + * + * DESCRIPTION + */ +PRIVATE VOID STDCALL PrintInformationProcess (VOID) +{ + +TRACE; + + vtputs ("Leader:"); + vtprintf (" UniqueProcess %08x\n", Session.Client.UniqueProcess); + vtprintf (" UniqueThread %08x\n", Session.Client.UniqueThread); +} +/********************************************************************** + * PostMortem/0 + * + * DESCRIPTION + */ +PRIVATE INT STDCALL PostMortem (VOID) +{ + DWORD ExitCode; + +TRACE; + + + PrintInformationProcess (); + if (TRUE == GetExitCodeProcess (Session.Client.UniqueProcess, & ExitCode)) + { + vtprintf (" ExitCode %d\n", ExitCode); + } + return 0; +} +/********************************************************************** + * InputTerminalEmulator/0 + * + * DESCRIPTION + * Process user terminal input. + * + * NOTE + * This code is run in the main thread. + */ +PRIVATE BOOL STDCALL InputTerminalEmulator (VOID) +{ + HANDLE StandardInput; + INPUT_RECORD InputRecord [INPUT_QUEUE_SIZE]; + DWORD NumberOfEventsRead = 0; + INT CurrentEvent; + + +TRACE; + + StandardInput = GetStdHandle (STD_INPUT_HANDLE); + if (INVALID_HANDLE_VALUE == StandardInput) + { + return FALSE; + } + while ((TRUE == Session.SsLinkIsActive) && + ReadConsoleInput ( + StandardInput, + InputRecord, + (sizeof InputRecord) / sizeof (INPUT_RECORD), + & NumberOfEventsRead + )) + { + for ( CurrentEvent = 0; + (CurrentEvent < NumberOfEventsRead); + CurrentEvent ++ + ) + { + switch (InputRecord [CurrentEvent].EventType) + { + case KEY_EVENT: + OutPort (& InputRecord [CurrentEvent].Event.KeyEvent.uChar.AsciiChar, 1); + break; + case MOUSE_EVENT: + /* TODO: send a sequence of move cursor codes */ + /* InputRecord [CurrentEvent].Event.MouseEvent; */ + break; + case WINDOW_BUFFER_SIZE_EVENT: + /* TODO: send a SIGWINCH signal to the leader process. */ + /* InputRecord [CurrentEvent].Event.WindowBufferSizeEvent.dwSize; */ + break; + /* Next events should be ignored. */ + case MENU_EVENT: + vtprintf ("%s: %s: MENU_EVENT received from CSRSS\n", MyName, __FUNCTION__); + case FOCUS_EVENT: + vtprintf ("%s: %s: FOCUS_EVENT received from CSRSS\n", MyName, __FUNCTION__); + break; + } + } + NumberOfEventsRead = 0; + } + return TRUE; +} +/********************************************************************** + * Startup/1 + * + * DESCRIPTION + * Initialize the program. + */ +PRIVATE VOID STDCALL Startup (LPSTR Command) +{ + NTSTATUS Status; + DWORD ThreadId; + + +TRACE; + + /* PSX process info */ + Session.Client.UniqueProcess = INVALID_HANDLE_VALUE; + Session.Client.UniqueThread = INVALID_HANDLE_VALUE; + /* Initialize the VT-100 emulator */ + vtInitVT100 (); + /* Connect to PSXSS */ + Status = InitializeSsIoChannel (); + if (!NT_SUCCESS(Status)) + { + vtprintf ("%s: failed to connect to PSXSS (Status=%08x)!\n", + MyName, Status); + exit (EXIT_FAILURE); + } + /* Create the leading process for this session */ + Status = PsxCreateLeaderProcess (Command); + if (!NT_SUCCESS(Status)) + { + vtprintf ("%s: failed to create the PSX process (Status=%08x)!\n", + MyName, Status); + exit (EXIT_FAILURE); + } +} +/********************************************************************** + * Shutdown/0 PRIVATE + * + * DESCRIPTION + * Shutdown the program. + */ +PRIVATE INT STDCALL Shutdown (VOID) +{ + +TRACE; + + /* TODO: try exiting cleanly: close any open resource */ + /* TODO: notify PSXSS the session is terminating */ + RtlDeleteCriticalSection (& Session.Lock); + return PostMortem (); +} +/********************************************************************** + * + * ENTRY POINT PUBLIC + * + *********************************************************************/ +int main (int argc, char * argv []) +{ + +TRACE; + + Startup (argv[1]); /* Initialization */ + InputTerminalEmulator (); /* Process user input */ + return Shutdown (); +} +/* EOF */ diff --git a/posix/apps/csrterm/csrterm.h b/posix/apps/csrterm/csrterm.h new file mode 100644 index 00000000000..d3e1626fcf5 --- /dev/null +++ b/posix/apps/csrterm/csrterm.h @@ -0,0 +1,53 @@ +#ifndef _CSRTERM_H +#define _CSRTERM_H + +/* PSX session: CSR terminal emulator side */ + +#define NAME_BUFFER_SIZE 64 + +typedef struct _PSXSS_PORT +{ + UNICODE_STRING Name; + WCHAR NameBuffer [NAME_BUFFER_SIZE]; + HANDLE Handle; + +} PSXSS_PORT, * PPSXSS_PORT; + +typedef struct _CSRTERM_SESSION_PORT +{ + UNICODE_STRING Name; + WCHAR NameBuffer [NAME_BUFFER_SIZE]; + HANDLE Handle; + struct { + HANDLE Handle; + DWORD Id; + } Thread; + +} CSRTERM_SESSION_PORT; + +typedef struct _CSRTERM_SESSION_SECTION +{ + UNICODE_STRING Name; + WCHAR NameBuffer [NAME_BUFFER_SIZE]; + HANDLE Handle; + ULONG Size; + PVOID BaseAddress; + ULONG ViewSize; + +} CSRTERM_SESSION_SECTION; + +typedef struct _CSRTERM_SESSION +{ + ULONG Identifier; /* PortID for ServerPort in PSXSS */ + PSXSS_PORT ServerPort; /* \POSIX+\SessionPort */ + CSRTERM_SESSION_PORT Port; /* \POSIX+\Sessions\P */ + CSRTERM_SESSION_SECTION Section; /* \POSIX+\Sessions\D */ + CLIENT_ID Client; + CRITICAL_SECTION Lock; + BOOL SsLinkIsActive; + +} CSRTERM_SESSION, * PCSRTERM_SESSION; + +#define LOCK_SESSION RtlEnterCriticalSection(& Session.Lock) +#define UNLOCK_SESSION RtlLeaveCriticalSection(& Session.Lock) +#endif /* ndef _CSRTERM_H */ diff --git a/posix/apps/csrterm/csrterm.rc b/posix/apps/csrterm/csrterm.rc new file mode 100644 index 00000000000..0458d6c2020 --- /dev/null +++ b/posix/apps/csrterm/csrterm.rc @@ -0,0 +1,38 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "CSR Terminal Emulator for POSIX+\0" + VALUE "FileVersion", RES_STR_FILE_VERSION + VALUE "InternalName", "csrterm\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalFilename", "csrterm.exe\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/posix/apps/csrterm/readme.txt b/posix/apps/csrterm/readme.txt new file mode 100644 index 00000000000..d51519669ee --- /dev/null +++ b/posix/apps/csrterm/readme.txt @@ -0,0 +1,79 @@ +$Id: readme.txt,v 1.1 2002/03/17 22:15:39 ea Exp $ + +csrterm - a CSR client terminal emulator for the POSIX+ subsystem + +SYNOPSYS + + csrterm [program] + + program program to be run in the terminal; if none is given, + the shell for the current user (W32 session's) is + used. + +DESCRIPTION + + csrterm emulates a DEC VT-100 terminal (on top of the CSRSS + subsystem, hence the name) which is the controlling terminal + for a process [program] running in the context of the PSX + subsystem. csrterm is a Win32 console application, not a PSX + application. The process created by the PSX subsystem on behalf + of csrterm is not the child of the csrterm instance that + requested it. csrterm simply performs terminal I/O in the CSRSS + world (the W32 world!) for [program]. + +NOTES + + The role of csrterm is creating a session in the PSX subsystem + managing any I/O for it. This is how it works: + + 1. csrterm creates two well known named objects in the system + name space that will allow the PSX subsystem server to build + the I/O channel for the session. To let the PSX subsystem + process recognize the objects, they contain a numeric suffix + which is the process identifier (n) the system gives to each + instance of csrterm: + + \POSIX+\Session\Pn LPC port (IPC rendez-vous object) + \POSIX+\Session\Dn section (shared memory object) + + csrterm also creates a new thread to manage the calls though + the LPC port. Port Pn is used by the subsystem to control the + terminal which csrterm emulates. + + 2. csrterm connects to the PSX subsystem session port + + \POSIX+\SessionPort + + and asks the subsystem to create a new session. + + 3. The PSX subsystem, if it decides to accept the request, creates + a new session for that calling instance of csrterm (n), and in + turn connects back to the terminal control port + + \POSIX+\Session\Pn + + 4. When csrterm makes the PSX subsystem create the new session, it + also tells the subsystem what program should be the session + leader process. The PSX subsystem creates that process (the + image file to start must be marked IMAGE_SUBSYSTEM_POSIX_GUI or + IMAGE_SUBSYSTEM_POSIX_CUI). + + 5. The requested process [program] runs in the context of the + PSX subsystem and performs any terminal I/O via the channel + csrterm and the PSX susbstem created. + +REVISIONS + 2001-05-05 created + 2002-03-03 simplified + +AUTHOR + + Emanuele Aliberti + +CREDITS + + John L. Miller (johnmil@cs.cmu.edu, johnmil@jprc.com) code for + a basic VT-100 emulator for Win32 consoles is used to process + tc* calls output. + +EOF diff --git a/posix/apps/csrterm/vt100.c b/posix/apps/csrterm/vt100.c new file mode 100644 index 00000000000..5d7f52dc9ae --- /dev/null +++ b/posix/apps/csrterm/vt100.c @@ -0,0 +1,1124 @@ +/* vt100.c + * + * AUTHOR: John L. Miller, johnmil@cs.cmu.edu / johnmil@jprc.com + * DATE: 8/4/96 + * + * Copyright (c) 1996 John L. Miller + * + * Full permission is granted to use, modify and distribute + * this code, provided: + * 1) This comment field is included in its entirity + * 2) No money is charged for any work including or based on + * portions of this code. + * + * If you're a nice person and find this useful, I'd appreciate a + * note letting me know about it. e-mail is usually what spurs me + * on to improve and support software I've written. + * + */ + +/* This is the main code body for my generic vt-100 emulator. This code + * body provides parsing for most of the vt-100 escape sequences, but it + * doesn't actually do anything with some of them. The idea behind this + * parser is to provide a generic front-end that you can initialize, then + * send all of your output to. The output is parsed by the routines in this + * program, then spit out to a back-end. + * + * What back-end you say? Well, the one you have to supply yourself. There's a + * dozen or so routines you have to provide for character-based I/O, cursor + * movement, erasing and deleting text, and setting text and terminal attributes. + * + * For a list of the routines your back end must supply, read vt100.h closely. + * + * In case it's not obvious, these routines were written for a system running win32. + * for vt100.c and vt100.h, most of the code should be easily portable to other + * operating systems. Yeah, like they NEED a vt-100 emulator :p + */ + +#include +#include +#include +#include "vt100.h" + + +/* NOTE - many of the functions look like they should + * take X-Y pairs. Bear in mind this is a text display, + * so for this we're talking ROWS and COLUMNS. So parm + * lists go (row, column), which is the opposite of (x, y). + */ + +char cBuffer[MAXVTBUFLEN+1]; /* terminal output buffer for unprocessed characters */ +int BufLen; /* Number of characters in cBuffer waiting for output */ + +/* List of all device-independant colors. These colors will be transmitted to a + * back-end routine responsible for setting the foreground and background text + * colors appropriately. Note that the color combinations are ordered to correspond + * with combinations of red (0x1), green (0x2) and blue (0x4) bit flags. + */ + +int ScColorTrans[8]= { 0, + SC_RED, + SC_GREEN, + SC_RED|SC_GREEN, + SC_BLUE, + SC_RED|SC_BLUE, + SC_RED|SC_GREEN, + SC_RED|SC_GREEN|SC_BLUE + }; + + +/* List of terminal attributes which we track (used by [?#h and [?#l) */ + +int termAttrMode[NUM_TERM_ATTR_MODES] = { 0, + CURSORAPPL_MODE, + ANSI_MODE, + COL132_MODE, + SMOOTHSCROLL_MODE, + REVSCREEN_MODE, + ORIGINREL_MODE, + WRAP_MODE, + REPEAT_MODE, + }; + +/* FORWARD FUNCTION DECLARATIONS - + * these functions are intended for use only in this module */ + +static int ProcessBracket(int Start); +static int ProcessEscape(int Start); +static int ProcessControl(int Start); +static int ProcessBuffer(void); + +/* END FORWARD FUNCTION DECLARATIONS */ + + + +/* vtputs() - + * + * front-end 'puts()' substitute. Call this routine instead + * of 'puts()', and it'll pass the output through the vt100 emulator. + */ + +vtputs(char *f) +{ + char cbuf[1024]; + + strcpy(cbuf,f); + strcat(cbuf,"\n"); + vtProcessedTextOut(cbuf, strlen(cbuf)); + + return(0); +} + + +/* vtprintf - + * + * This routine is a substitute for printf(). Call this routine with the + * same parameters you would use for printf, and output will be channelled + * through the vt-100 emulator. + */ + +vtprintf(char *format, ...) +{ + char cbuf[1024]; + va_list va; + + va_start(va, format); + vsprintf(cbuf,format, va); + va_end(va); + + vtProcessedTextOut(cbuf, strlen(cbuf)); + + return(0); +} + +/* vtInitVT100 - + * + * Set the initial state of the VT-100 emulator, and call the initialization + * routine for the back end. This routine MUST be invoked before any other, or + * the VT-100 emulator will most likely roll over and die. + */ + +vtInitVT100(void) +{ + int i=0; + + cBuffer[0]='\0'; + BufLen=0; + + beInitVT100Terminal(); /* call the back-end initialization. */ + + return(0); +} + + +/* ProcessBracket - + * + * Helper routine for processing escape sequences. By the time this + * routine is invoked, '[' has already been read in the input + * stream. 'Start' is an index in cBuffer to the ''. + * + * If an escape sequence is successfully parsed, return the index of the + * first character AFTER the escape sequence. Otherwise, return 'Start'. + * + */ + +static int ProcessBracket(int Start) +{ + int End; /* Current character being examined in cBuffer */ + int args[10], numargs=0; /* numerical args after [ */ + int iMode; + int i; + int itmp=0; + int left; + int iForeground, iBackground; + + /* If there's no valid escape sequence, return as we were called. */ + + if ((cBuffer[Start+1] != '[')||(Start+2 >= BufLen)) + return(Start); + + End = Start + 2; + + /* Loop through the buffer, hacking out all numeric + * arguments (consecutive string of digits and + * semicolons + */ + + do { + itmp = 0; /* itmp will hold the current arguments integer value */ + + /* the semicolon is a delimiter */ + if (cBuffer[End] == ';') + { + End++; + + if (End >= BufLen) + return(Start); + } + + /* Parse this argument into a number. */ + + while (isdigit(cBuffer[End])) + { + itmp = itmp*10 + (cBuffer[End]-'0'); + End++; + if (End >= BufLen) + return(Start); + } + + /* Save the numeric argument if we actually + * parsed a number. + */ + + if (End != Start + 2) + args[numargs++] = itmp; + + } while (cBuffer[End] == ';'); + + /* At this point, we've come across a character that isn't a number + * and isn't a semicolon. This means it is the command specifier. + */ + + /* Number of characters left in the input stream. I don't use + * this as rigorously as I should here. + */ + + left = BufLen - End; + + /* Return if there's definitely not enough characters for a + * full escape sequence. + */ + + if (left <= 0) + return(Start); + + /* Giant switch statement, parsing the command specifier that followed + * up [arg;arg;...arg + */ + + switch (cBuffer[End]) + { + /* Cursor Up: Esc [ Pn A */ + case 'A': + beOffsetCursor(numargs ? -args[0] : -1, 0); + End += 1; + break; + + /* Cursor Down: Esc [ Pn B */ + case 'B': + beOffsetCursor(numargs ? args[0] : 1, 0); + End += 1; + break; + + /* Cursor Right: Esc [ Pn C */ + case 'C': + beOffsetCursor(0, numargs ? args[0] : 1); + End += 1; + break; + + /* Cursor Left: Esc [ Pn D */ + case 'D': + beOffsetCursor(0, numargs ? -args[0] : -1); + End += 1; + break; + + /* Direct Addressing : Esc [ Pn(row);Pn(col)H or + * Esc [ Pn(row);Pn(col)f + */ + case 'H': + case 'f': + if (numargs == 0) + beAbsoluteCursor(1,1); + else if (numargs == 1) + beAbsoluteCursor(args[0] > 0 ? args[0] : 1,1); + else if (numargs == 2) + beAbsoluteCursor(args[0] > 0 ? args[0] : 1, args[1] > 0 ? args[1] : 1); + + End += 1; + break; + + /* Erase from Cursor to end of screen Esc [ J + * Erase from Beginning of screen to cursor Esc [ 1 J + * Erase Entire screen Esc [ 2 J + */ + case 'J': + if (numargs == 0) + beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE); + else if (args[0] == 1) + beEraseText(TOP_EDGE, LEFT_EDGE, CUR_ROW, CUR_COL); + else + beEraseText(TOP_EDGE, LEFT_EDGE, BOTTOM_EDGE, RIGHT_EDGE); + + End += 1; + break; + + /* Erase from Cursor to end of line Esc [ K + * Erase from Beginning of line to cursor Esc [ 1 K + * Erase Entire line Esc [ 2 K + */ + case 'K': + if (numargs == 0) + beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE); + else if (args[0] == 1) + beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, CUR_COL); + else + beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, RIGHT_EDGE); + + End += 1; + break; + + + /* Set Graphics Rendition: + * ESC[#;#;....;#m + * The graphics rendition is basically foreground and background + * color and intensity. + */ + case 'm': + beGetTextAttributes(&iForeground, &iBackground); + + if (numargs < 1) + { + /* If we just get ESC[m, treat it as though + * we should shut off all extra text + * attributes + */ + + iForeground &= ~(SC_BOLD|SC_UL|SC_BL|SC_RV|SC_GRAPHICS|SC_G0|SC_G1); + iForeground |= SC_ASCII; + + beSetTextAttributes(iForeground, iBackground); + End += 1; + break; + } + + /* Loop through all the color specs we got, and combine them + * together. Note that things like 'reverse video', 'bold', + * and 'blink' are only applied to the foreground. The back end + * is responsible for applying these properties to all text. + */ + for (i=0; i < numargs; i++) + { + switch(args[i]) + { + /* 0 for normal display */ + case 0: + iForeground &= ~SC_BOLD; + break; + + /* 1 for bold on */ + case 1: + iForeground |= SC_BOLD; + break; + + /* 4 underline (mono only) */ + case 4: + iForeground |= SC_UL; + break; + + /* 5 blink on */ + case 5: + iForeground |= SC_BL; + break; + + /* 7 reverse video on */ + case 7: + iForeground |= SC_RV; + break; + + /* 8 nondisplayed (invisible) BUGBUG - not doing this. */ + + + /* 30-37 is bit combination of 30+ red(1) green(2) blue(4) + * 30 black foreground + */ + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + iForeground &= ~(SC_RED|SC_GREEN|SC_BLUE); + iForeground |= ScColorTrans[args[i]-30]; + break; + + /* 40-47 is bit combo similar to 30-37, but for background. */ + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + iBackground &= ~(SC_RED|SC_GREEN|SC_BLUE); + iBackground |= ScColorTrans[args[i]-30]; + break; + } + } + + beSetTextAttributes(iForeground, iBackground); + + End += 1; + break; + + + /* + * Set with Esc [ Ps h + * Reset with Esc [ Ps l + * Mode name Ps Set Reset + * ------------------------------------------------------------------- + * Keyboard action 2 Locked Unlocked + * Insertion 4 Insert Overwrite + * Send - Receive 12 Full Echo + * Line feed/New line 20 New line Line feed + */ + case 'h': + case 'l': + /* BUGBUG - many of the terminal modes are set with '?' as the + * first character rather than a number sign. These are dealt with + * later in this switch statement because they must be. Most other + * settings are just ignored, however. + */ + End += 1; + break; + + /* Insert line Esc [ Pn L */ + case 'L': + beInsertRow(CUR_ROW); + End += 1; + break; + + /* Delete line Esc [ Pn M */ + case 'M': + do { + beDeleteText(CUR_ROW,LEFT_EDGE, CUR_ROW, RIGHT_EDGE); + } while (--args[0] > 0); + + End += 1; + break; + + /* Delete character Esc [ Pn P */ + case 'P': + do { + beDeleteText(CUR_ROW, CUR_COL, CUR_ROW, CUR_COL); + } while (--args[0] > 0); + End += 1; + break; + + /* Set the Scrolling region Esc [ Pn(top);Pn(bot) r */ + case 'r': + if (numargs == 0) + beSetScrollingRows(TOP_EDGE,BOTTOM_EDGE); + else if (numargs == 2) + beSetScrollingRows(args[0],args[1]); + End += 1; + break; + + /* Print screen or region Esc [ i + * Print cursor line Esc [ ? 1 i + * Enter auto print Esc [ ? 5 i + * Exit auto print Esc [ ? 4 i + * Enter print controller Esc [ 5 i + * Exit print controller Esc [ 4 i + */ + case 'i': + /* BUGBUG - print commands are not acted upon. */ + End += 1; + break; + + + /* Clear tab at current column Esc [ g + * Clear all tabs Esc [ 3 g + */ + case 'g': + if (numargs == 0) + beClearTab(CUR_COL); + else + if ((numargs == 1) && (args[0] == 3)) + beClearTab(ALL_TABS); + + End += 1; + break; + + /* BUGBUG - queries which require responses are ignored. */ + + /* Esc [ c DA:Device Attributes + * or + * + * Esc [ x DECREQTPARM: Request Terminal Parameters + * * values other than 1 are ignored. Upon + * receipt of a value of 1, the following + * response is sent: + * Esc[3;;;;;1;0x + * + * * Where , , , and + * are as for VT100s with the following + * exceptions: + * Values of 5 and 6 bits per + * character are sent as 7 bits. + * , + * These two numbers will always + * be the same. 9600 baud is + * sent for 7200 baud. + * + * Esc [ Ps n DSR: Device Status Report + * * Parameter values other than 5, 6, are ignored. + * If the parameter value is 5, the sequence + * Esc [ O n is returned. If the parameter value is + * 6, the CPR: Cursor Position Report sequence + * Esc [ Pn ; Pn R is returned with the Pn set to + * cursor row and column numbers. + * + * Cursor Controls: + * ESC[#;#R Reports current cursor line & column + */ + + /* spec [h [l + * Cursor key ?1 Application Cursor + * ANSI/VT52 ?2 ANSI VT52 + * Column ?3 132 80 + * Scrolling ?4 Smooth Jump + * Screen ?5 Reverse Normal + * Origin ?6 Relative Absolute + * Wraparound ?7 Wrap Truncate + * Auto key repeat ?8 Repeating No repeat + */ + case '?': + /* We didn't catch the numeric argument because the '?' stopped + * it before. Parse it now. + */ + args[0]=0; + while (isdigit(cBuffer[++End])) + { + if (End >= BufLen) + return(Start); + args[0] = 10*args[0] + (cBuffer[End]-'0'); + } + + /* If we don't handle this particular '?' command (and + * there are plenty we don't) then just ignore it. + */ + if ( (args[0] > 8) + ||( (cBuffer[End] != 'l') && (cBuffer[End] != 'h')) + ) + { + End++; + if (End >= BufLen) + return(Start); + break; + } + + /* This command sets terminal status. Get the current status, + * determine what needs to be changed, and send it back. + */ + + iMode = beGetTermMode(); + + /* If we need a given mode and it's not already set, set it. */ + + if ((cBuffer[End] == 'h') && (!(iMode & termAttrMode[args[0]]))) + { + beSetTermMode(iMode | termAttrMode[args[0]]); + } + + /* likewise, clear it as appropriate */ + if ((cBuffer[End] == 'l') && (iMode & termAttrMode[args[0]])) + { + beSetTermMode(iMode & ~termAttrMode[args[0]]); + } + + End++; + break; + + /* If it's an escape sequence we don't treat, pretend as though we never saw + * it. + */ + default: + End += 1; + break; + } + + return(End); + +} + + +/* ProcessEscape - + * + * At this point, has been seen. Start points to the escape + * itself, and then this routine is responsible for parsing the + * rest of the escape sequence, and either pawning off further parsing, + * or acting upon it as appropriate. + * + * Note that the escape sequences being parsed are still contained in cBuffer. + */ + +static int ProcessEscape(int Start) +{ + int End; + int left; + int fore, back; + int i; + + /* if it's definitely not a full escape sequence, return that we haven't + * seen it. + */ + if ((cBuffer[Start] != 27)||(Start+1 >= BufLen)) + return(Start); + + End = Start + 1; + + /* At this point, if the sequence is x, 'End' points at + * x + */ + + /* left = number of characters left unparsed in the buffer. */ + left = BufLen - End -1; + + /* Main switch statement - parse the escape sequence according to the + * next character we see. + */ + + switch (cBuffer[End]) + { + /* Hard Reset Esc c BUGBUG - not imp'd. */ + case 'c': + End += 1; + break; + + /* Cursor up Esc A */ + case 'A': + beOffsetCursor(-1,0); + End += 1; + break; + + /* Cursor down Esc B */ + case 'B': + beOffsetCursor(1,0); + End += 1; + break; + + /* Cursor right Esc C */ + case 'C': + beOffsetCursor(0,1); + End += 1; + break; + + /* Cursor left Esc D */ + case 'D': + beOffsetCursor(0,-1); + End += 1; + break; + + /* Newline command: Esc E */ + case 'E': + beRawTextOut("\n",strlen("\n")); + End += 1; + break; + + /* Invoke the Graphics character set Esc F */ + case 'F': + beGetTextAttributes(&fore, &back); + if (! (fore & SC_GRAPHICS)) + { + fore &= ~(SC_ASCII|SC_G0|SC_G1); + fore |= SC_GRAPHICS; + beSetTextAttributes(fore, back); + } + End += 1; + break; + + /* Invoke the ASCII character set Esc G */ + case 'G': + beGetTextAttributes(&fore, &back); + if (! (fore & SC_ASCII)) + { + fore &= ~(SC_G0|SC_G1|SC_GRAPHICS); + fore |= SC_ASCII; + beSetTextAttributes(fore, back); + } + End += 1; + break; + + /* Move the cursor to (1,1): Home cursor Esc H */ + case 'H': + beAbsoluteCursor(TOP_EDGE,LEFT_EDGE); + End += 1; + break; + + /* Reverse line feed Esc I */ + case 'I': + beOffsetCursor(-1,0); + End += 1; + break; + + /* Erase to end of screen Esc J */ + case 'J': + beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE); + End += 1; + break; + + /* Erase to end of line Esc K */ + case 'K': + beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE); + End += 1; + break; + + /* Reverse Line: Esc M */ + case 'M': + beAbsoluteCursor(CUR_ROW, LEFT_EDGE); + beOffsetCursor(-1,0); + End += 1; + break; + + /* Switch to G1 graphics character set. Esc N */ + case 'N': + beGetTextAttributes(&fore, &back); + if (! (fore & SC_G1)) + { + fore &= ~(SC_G0|SC_ASCII|SC_GRAPHICS); + fore |= SC_G1; + beSetTextAttributes(fore, back); + } + End += 1; + break; + + /* Switch to G0 graphics character set Esc O */ + case 'O': + beGetTextAttributes(&fore, &back); + if (! (fore & SC_G0)) + { + fore &= ~(SC_G1|SC_ASCII|SC_GRAPHICS); + fore |= SC_G0; + beSetTextAttributes(fore, back); + } + End += 1; + break; + + /* Print cursor line Esc V BUGBUG - unimp'd */ + case 'V': + End += 1; + break; + + /* Enter print controller Esc W BUGBUG - unimp'd */ + case 'W': + End += 1; + break; + + /* Exit print controller Esc X BUGBUG - unimp'd */ + case 'X': + End += 1; + break; + + /* Cursor address Esc Y row col BUGBUG - unimp'd and needed */ + case 'Y': + End += 1; + break; + + /* Identify terminal type Esc Z */ + case 'Z': + beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE)); + End += 1; + break; + + /* One of dozens of escape sequences starting [. Parse further */ + case '[': + /* pass in the escape as the starting point */ + End = ProcessBracket(End-1); + break; + + /* Print screen Esc ] BUGBUG - unimp'd */ + case ']': + End += 1; + break; + + /* Enter auto print Esc ^ BUGBUG - unimpd' */ + case '^': + End += 1; + break; + + /* Exit auto print Esc - BUGBUG - unimpd' */ + case '-': + End += 1; + break; + + /* Alternate keypad Esc = BUGBUG - unimpd' */ + case '=': + End += 1; + break; + + /* Numeric keypad Esc > BUGBUG - unimpd' */ + case '>': + End += 1; + break; + + /* Enter ANSI mode Esc < BUGBUG - unimpd' */ + case '<': + End += 1; + break; + + /* Save cursor position & Attributes: Esc 7 */ + case '7': + beSaveCursor(); + End += 1; + break; + + /* Restore cursor position & attributes: Esc 8 */ + case '8': + beRestoreCursor(); + End += 1; + break; + + /* Set character size - BUGBUG - unimp'd. + * # 1 Double ht, single width top half chars + * # 2 Double ht, single width lower half chars + * # 3 Double ht, double width top half chars + * # 4 Double ht, double width lower half chars + * # 5 Single ht, single width chars + * # 6 Single ht, double width chars + */ + case '#': + End += 1; + break; + + /* Select character set + * ESC ( A British + * ESC ( C Finnish + * ESC ( E Danish or Norwegian + * ESC ( H Swedish + * ESC ( K German + * ESC ( Q French Canadian + * ESC ( R Flemish or French/Belgian + * ESC ( Y Italian + * ESC ( Z Spanish + * ESC ( 1 Alternative Character + * ESC ( 4 Dutch + * ESC ( 5 Finnish + * ESC ( 6 Danish or Norwegian + * ESC ( 7 Swedish + * ESC ( = Swiss (French or German) + */ + case '(': + case ')': + /* BUGBUG - most character sets aren't implemented. */ + beGetTextAttributes(&fore, &back); + switch (cBuffer[++End]) + { + case 'B': /* ESC ( B North American ASCII set */ + i=SC_ASCII; + break; + + case '0': /* ESC ( 0 Line Drawing */ + i=SC_G1; + break; + + case '2': /* ESC ( 2 Alternative Line drawing */ + i=SC_G0; + break; + + default: + /* Make sure the screen mode isn't set. */ + i = 0xFFFFFFFF; + break; + } + + if (! (fore & i)) + { + fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS); + fore |= i; + beSetTextAttributes(fore, back); + } + + End += 1; + break; + + /* Unknown escape sequence */ + default: + { + char cbuf[80]; + sprintf(cbuf,"%c", cBuffer[End]); + beRawTextOut(cbuf+Start,6); + End += 1; + } + } + + return(End); +} + + +/* ProcessControl - + * + * Process a probable escape or control sequence + * starting at the supplied index. Return the index + * of the first character *after* the escape sequence we + * process. + * In the case of an incomplete sequence, ie one + * that isn't completed by the end of the buffer, return + * 'Start'. + * In the case of an invalid sequence, + * note the invalid escape sequence, and return Start+1 + */ + +static int ProcessControl(int Start) +{ + int fore, back; + int End = Start; + + /* Check to make sure we at least have enough characters + * for a control character + */ + + if (Start >= BufLen) + return(Start); + + switch (cBuffer[Start]) + { + /* NUL 0 Fill character; ignored on input. + * DEL 127 Fill character; ignored on input. + */ + case 0: + case 127: + End += 1; + break; + + /* ENQ 5 Transmit answerback message. */ + case 5: + beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE)); + End += 1; + break; + + /* BEL 7 Ring the bell. */ + case 7: + beRingBell(); + End += 1; + break; + + /* BS 8 Move cursor left. */ + case 8: + beOffsetCursor(0,-1); + End += 1; + break; + + /* HT 9 Move cursor to next tab stop. */ + case 9: + beAdvanceToTab(); + End += 1; + break; + + /* LF 10 Line feed; causes print if in autoprint. */ + case 10: + beOffsetCursor(1,0); + End += 1; + break; + + /* VT 11 Same as LF. + * FF 12 Same as LF. + */ + case 11: + case 12: + beOffsetCursor(1,0); + End += 1; + break; + + /* CR 13 Move cursor to left margin or newline. */ + case 13: + beOffsetCursor(1,0); + beAbsoluteCursor(CUR_ROW,LEFT_EDGE); + End += 1; + break; + + /* SO 14 Invoke G1 character set. */ + case 14: + beGetTextAttributes(&fore, &back); + if (! (fore & SC_G1)) + { + fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS); + fore |= SC_G1; + beSetTextAttributes(fore, back); + } + End += 1; + break; + + /* SI 15 Invoke G0 character set. */ + case 15: + beGetTextAttributes(&fore, &back); + if (! (fore & SC_G0)) + { + fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS); + fore |= SC_G0; + beSetTextAttributes(fore, back); + } + End += 1; + break; + + /* CAN 24 Cancel escape sequence and display checkerboard. BUGBUG - not imp'd. + * SUB 26 Same as CAN. + */ + case 24: + End += 1; + break; + + /* ESC 27 Introduce a control sequence. */ + case 27: + End = ProcessEscape(Start); + break; + + /* Print any other control character received. */ + default: + { + char buf[4]; + sprintf(buf,"^%c",'A' + cBuffer[Start] - 1); + beRawTextOut(buf, 2); + End += 1; + } + break; + } + + return(End); +} + + +/* ProcessBuffer - + * + * Process the current contents of the terminal character buffer. + */ +static int ProcessBuffer(void) +{ + int Start=0,End=0; + + /* Null-terminate the buffer. Why? Heck, why not? */ + + cBuffer[BufLen] = '\0'; + + /* Loop through the entire buffer. Start will be incremented + * to point at the start of unprocessed text in the buffer as + * we go. + */ + while (Start < BufLen) + { + End = Start; + + /* Since we null-terminated, null < 27 and we have a termination condition */ + while ((cBuffer[End] > 27)||(cBuffer[End] == 10)||(cBuffer[End] == 13)) + End++; + + /* At this point, if Start < End, we have a string of characters which + * doesn't have any control sequences, and should be printed raw. + */ + + if (End > Start) + beRawTextOut(cBuffer+Start, End-Start); + + if (End >= BufLen) + { + break; + } + + /* At this point, 'End' points to the beginning of an escape + * sequence. We'll reset 'start' to be AFTER parsing the + * escape sequence. Note that if the escape sequence + * is incomplete, ProcessControl should return the + * same value passed in. Otherwise, it'll return the + * index of the first character after the valid + * escape sequence. + */ + + Start = ProcessControl(End); + + if (Start == End) + { + /* The escape sequence was incomplete. + * Move the unprocessed portion of the input buffer + * to start at the beginning of the buffer, then + * return. + */ + + while (End < BufLen) + { + cBuffer[End-Start] = cBuffer[End]; + End += 1; + } + + BufLen = End-Start; + return(0); + } + } + + /* If we made it this far, Start == Buflen, and so we've finished + * processing the buffer completely. + */ + BufLen = 0; + cBuffer[BufLen] = '\0'; + + return(0); +} + + +/* vtProcessedTextOut - + * + * Output characters to terminal, passing them through the vt100 emulator. + * Return -1 on error + */ +int +vtProcessedTextOut(char *cbuf, int count) +{ + /* If we have a buffer overflow, error out if we haven't already crashed. */ + + if ((count + BufLen) > MAXVTBUFLEN) + { + beRawTextOut("ERROR: VT-100 internal buffer overflow!",39); + return(-1); + } + + /* Otherwise, add our requested information to the + * output buffer, and attempt to parse it. + */ + + memcpy(cBuffer + BufLen, cbuf, count); + BufLen += count; + + return(ProcessBuffer()); +} + diff --git a/posix/apps/csrterm/vt100.h b/posix/apps/csrterm/vt100.h new file mode 100644 index 00000000000..808bf765ce2 --- /dev/null +++ b/posix/apps/csrterm/vt100.h @@ -0,0 +1,320 @@ +/* vt100.h + * + * AUTHOR: John L. Miller, johnmil@cs.cmu.edu / johnmil@jprc.com + * DATE: 8/4/96 + * + * Copyright (c) 1996 John L. Miller + * + * Full permission is granted to use, modify and distribute + * this code, provided: + * 1) This comment field is included in its entirity + * 2) No money is charged for any work including or based on + * portions of this code. + * + * If you're a nice person and find this useful, I'd appreciate a + * note letting me know about it. e-mail is usually what spurs me + * on to improve and support software I've written. + * + */ + + +/* This identifier should be spit back to the computer when a terminal + * id is requested. + */ +#define ANSWERBACK_MESSAGE "vt100" + +/* Various terminal-related modes Entries are as follows: + * Identification esc. ID If set, if clear + */ + + /* Keyboard action 2 Locked Unlocked */ +#define CAPS_MODE 0x00000001 + /* Insertion 4 Insert Overwrite */ +#define INSERT_MODE 0x00000002 + /* Send - Receive 12 Full Echo */ +#define FULLDUPLEX_MODE 0x00000004 + /* Line feed/New line 20 New line Line feed */ +#define NEWLINE_MODE 0x00000008 + + +#define NUM_TERM_ATTR_MODES 9 /* We only track eight '?' escape sequences */ + + /* Cursor key ?1 Application Cursor */ +#define CURSORAPPL_MODE 0x00000010 + /* ANSI/VT52 ?2 ANSI VT52 */ +#define ANSI_MODE 0x00000020 + /* Column ?3 132 80 */ +#define COL132_MODE 0x00000040 + /* Scrolling ?4 Smooth Jump */ +#define SMOOTHSCROLL_MODE 0x00000080 + /* Screen ?5 Reverse Normal */ +#define REVSCREEN_MODE 0x00000100 + /* Origin ?6 Relative Absolute */ +#define ORIGINREL_MODE 0x00000200 + /* Wraparound ?7 Wrap Truncate */ +#define WRAP_MODE 0x00000400 + /* Auto key repeat ?8 Repeating No repeat */ +#define REPEAT_MODE 0x00000800 + + + /* Print form feed ?18 Yes No */ +#define PRINTFF_MODE 0x00001000 + /* Print extent ?19 Full screen Scrolling region */ +#define PRINTFULLSCR_MODE 0x00002000 + /* Keypad application 'Esc =' numeric 'Esc >' */ +#define KEYPADNUMERIC_MODE 0x00004000 + /* default mode that we start the emulator with */ +#define DEFAULT_MODE (NEWLINE_MODE|ANSI_MODE|REPEAT_MODE) + + /* This constant is VERY important - the size of the buffer for + * unprocessed vt-100 prints! + */ +#define MAXVTBUFLEN 4096 + + /* Constants used in place of actual row and column numbers + * for the cursor movement and text erasing and deleting functions. + */ +#define CUR_ROW -999 +#define CUR_COL -999 +#define ALL_TABS -1999 + +#define LEFT_EDGE 0 +#define RIGHT_EDGE 12000 +#define TOP_EDGE 0 +#define BOTTOM_EDGE 12000 + + /* Text attribute definitions; color, font, bold. */ +#define NUM_SC_ATTRIBUTES 11 + +#define SC_RED 0x0001 +#define SC_GREEN 0x0002 +#define SC_BLUE 0x0004 +#define SC_BOLD 0x0010 +#define SC_UL 0x0020 /* Underlined */ +#define SC_BL 0x0040 /* Blinking */ +#define SC_RV 0x0080 /* Reverse video */ +#define SC_ASCII 0x0100 /* Normal ASCII (USASCII) */ +#define SC_G0 0x0200 /* graphics set G0 */ +#define SC_G1 0x0400 /* Graphics set G1 */ +#define SC_GRAPHICS 0x0800 /* Good question */ + + +/* forward variable declarations */ + +extern int termAttrMode[NUM_TERM_ATTR_MODES]; +extern int alltermAttrModes; + + +/* prototypes from vt100.c */ + +/* functions meant for use outside of the emulator */ + +int vtputs(char *f); +int vtprintf(char *format, ...); +int vtInitVT100(void); +int vtProcessedTextOut(char *cbuf, int count); + + +/* Prototype for functions which MUST BE SUPPLIED BY THE BACK END!!! */ + +/* Back-end specific initialization is performed in this function. + * this is gauranteed to be called before any other requests are made + * of the back end. + */ + +/* beInitVT100Terminal() - + * + * This function is called by the VT100 emulator as soon as the + * front-end terminal is initialized. It's responsible for setting + * initial state of the terminal, and initing our many wacky variables. + */ +int beInitVT100Terminal(); + + +/* beAbsoluteCursor - + * + * Given an input row and column, move the cursor to the + * absolute screen coordinates requested. Note that if the + * display window has scrollbars, the column is adjusted + * to take that into account, but the row is not. This allows + * for large scrollback in terminal windows. + * + * ROW must be able to accept CUR_ROW, TOP_EDGE, BOTTOM_EDGE, + * or a row number. + * + * COLUMN must be able to accept CUR_COL, LEFT_EDGE, RIGHT_EDGE, + * or a column number. + */ +int beAbsoluteCursor(int row, int col); + + +/* beOffsetCursor - + * + * Given an input row and column offset, move the cursor by that + * many positions. For instance, row=0 and column=-1 would move + * the cursor left a single column. + * + * If the cursor can't move the requested amount, results are + * unpredictable. + */ +int beOffsetCursor(int row, int column); + + +/* beRestoreCursor - + * + * Saved cursor position should be stored in a static + * variable in the back end. This function restores the + * cursor to the position stored in that variable. + */ +int beRestoreCursor(void); + + +/* beSaveCursor - + * + * The back-end should maintain a static variable with the + * last STORED cursor position in it. This function replaces + * the contents of that variable with the current cursor position. + * The cursor may be restored to this position by using the + * beRestoreCursor function. + */ +int beSaveCursor(void); + + +/* beGetTextAttributes - + * + * given a pointer to 'fore'ground and 'back'ground ints, + * fill them with a device-independant description of the + * current foreground and background colors, as well as any + * font information in the foreground variable. + */ +int beGetTextAttributes(int *fore, int *back); + + +/* beSetTextAttributes - + * + * Given a foreground and a background device independant (SC) color and font + * specification, apply these to the display, and save the state in the + * static screen variables. + * + * Note that many font-specific constants (bold/underline/reverse, G0/G1/ASCII) + * are stored ONLY in the foreground specification. + */ +int beSetTextAttributes(int fore, int back); + + +/* beRawTextOut- + * + * The name of this function is misleading. Given a pointer to + * ascii text and a count of bytes to print, print them to the + * display device. If wrapping is enabled, wrap text. If there is a + * scrolling region set and the cursor is in it, + * scroll only within that region. 'beRawTextOut' means that it's guaranteed + * not to have control sequences within the text. + */ +int beRawTextOut(char *text, int len); + + +/* beEraseText - + * + * Given a 'from' and a 'to' position in display coordinates, + * this function will fill in all characters between the two + * (inclusive) with spaces. Note that the coordinates do NOT + * specify a rectangle. erasing from (1,1) to (2,2) erases + * all of the first row, and the first two characters of the + * second. + * + * Note that this routine must be able to handle TOP_EDGE, + * BOTTOM_EDGE, LEFT_EDGE, RIGHT_EDGE, CUR_ROW, and CUR_COL + * in the appropriate parameters. + */ +int beEraseText(int rowFrom, int colFrom, int rowTo, int colTo); + + +/* beDeleteText - + * + * Given a screen cursor 'from' and 'to' position, this function + * will delete all text between the two. Text will be scrolled + * up as appropriate to fill the deleted space. Note that, as in + * beEraseText, the two coordinates don't specify a rectangle, but + * rather a starting position and ending position. In other words, + * deleting from (1,1) to (2,2) should move the text from (2,3) to the + * end of the second row to (1,1), move line 3 up to line 2, and so on. + * + * This function must be able to process TOP_EDGE, BOTTOM_EDGE, LEFT_EDGE, + * RIGHT_EDGE, CUR_ROW, and CUR_COL specifications in the appropriate + * variables as well as regular row and column specifications. + */ +int beDeleteText(int rowFrom, int colFrom, int rowTo, int colTo); + + +/* beInsertRow - + * + * Given a row number or CUR_ROW, TOP_EDGE or BOTTOM_EDGE as an input, + * this function will scroll all text from the current row down down by one, + * and create a blank row under the cursor. + */ +int beInsertRow(int row); + + +/* beTransmitText - + * + * Given a pointer to text and byte count, this routine should transmit data + * to whatever host made the request it's responding to. Typically this routin + * should transmit data as though the user had typed it in. + */ +int beTransmitText(char *text, int len); + + +/* beAdvanceToTab - + * + * This routine will destructively advance the cursor to the + * next set tab, or to the end of the line if there are no + * more tabs to the right of the cursor. + */ + +int beAdvanceToTab(void); + + +/* beClearTab - + * + * This function accepts a constant, and will try to clear tabs + * appropriately. Its argument is either + * ALL_TABS, meaning all tabs should be removed + * CUR_COL, meaning the tab in the current column should be wiped, or + * a column value, meaning if there's a tab there it should be wiped. + * + */ +int beClearTab(int col); + + +/* beSetScrollingRows - + * + * Given a pair of row numbers, this routine will set the scrolling + * rows to those values. Note that this routine will accept + * TOP_ROW and BOTTOM_ROW as values, meaning that scrolling should + * be enabled for the entire display, regardless of resizing. + */ +int beSetScrollingRows(int fromRow, int toRow); + + +/* beRingBell - + * + * Ring the system bell once. + */ +int beRingBell(void); + + +/* beGetTermMode - + * + * Return the value of conTermMode, which is the terminal settings which + * can be queried/set by [?#h/l. + */ +int beGetTermMode(); + + +/* beSetTermMode - + * + * Set the terminal as requested, assuming we can. Right now we only handle a + * couple of the possible flags, but we store many of the others. + */ +int beSetTermMode(int newMode);