This is tcaphelp.c in view mode; [Download] [Up]
/* oswin32/tcaphelp.c */
char id_tcaphelp[] = "$Id: tcaphelp.c,v 2.18 1996/09/18 20:37:17 steve Exp $";
#include "elvis.h"
#if defined(GUI_TERMCAP) || defined(GUI_OPEN)
# define CHAR CHAR_nt
# define BOOLEAN BOOLEAN_nt
# include <windows.h>
# include <fcntl.h>
# include <io.h>
# include <signal.h>
# undef CHAR
# undef BOOLEAN
# define SMART_LINE_WRAP 1
/* This file includes low-level tty control functions used by the termcap
* user interface. These are:
* ttyinit() - remember the initial serial line configuration
* ttyraw() - switch to the mode that elvis runs it
* ttynormal() - switch back to the mode saved by ttyinit()
* ttyread(buf,len,timeout)- read characters, possibly with timeout
* ttywrite(buf,len) - write characters
* ttytermtype() - return the name of the terminal type
* ttysize() - determine the terminal size
*
* Also, it contains a small terminal emulator to be used when TERM=bios.
*/
/* This variable is defined in guitcap.c */
extern long ttycaught;
static void catchsig(int signo);
/* This variable is used to indicate that the BIOS interface is being used */
static BOOLEAN useconsole = True;
static HANDLE inConsole, outConsole, myConsole, console;
static CONSOLE_SCREEN_BUFFER_INFO consinfo;
static DWORD inMode, outMode;
static int prevWidth, prevHeight;
static BOOLEAN resized;
static WORD origattr;
static WORD attr; /* attribute byte for writing subsequent text */
/* The codepage option stores the code page */
static OPTVAL win32val[1];
#define o_codepage win32val[0].value.number
static int setcp(OPTDESC *opt, OPTVAL *val, CHAR *newval)
{
UINT newcp = atol(newval);
if (!newcp && newval[0] != '0')
{
msg(MSG_ERROR, "[s]$1 requires a numeric value", opt->longname);
return -1;
}
if (!SetConsoleOutputCP(newcp) || !SetConsoleCP(newcp))
{
msg(MSG_ERROR, "[sd]invalid $1 $2", opt->longname, (long)newcp);
return -1;
}
o_codepage = newcp;
return 1;
}
static OPTDESC win32desc[] =
{
{"codepage", "cpg", optnstring, setcp}
};
/* This function catches signals, especially SIGINT */
static void catchsig(signo)
int signo;
{
ttycaught |= (1 << signo);
}
/* remember the original terminal settings */
void ttyinit()
{
SECURITY_ATTRIBUTES sec;
COORD size;
COORD home;
DWORD dummy;
/* get handles of the console */
inConsole = GetStdHandle(STD_INPUT_HANDLE);
assert(inConsole != INVALID_HANDLE_VALUE);
outConsole = GetStdHandle(STD_OUTPUT_HANDLE);
assert(outConsole != INVALID_HANDLE_VALUE);
/* remember the original modes of those handles */
GetConsoleMode(inConsole, &inMode);
GetConsoleMode(outConsole, &outMode);
/* create a new console buffer, the same size as the window */
GetConsoleScreenBufferInfo(outConsole, &consinfo);
sec.nLength = sizeof(sec);
sec.lpSecurityDescriptor = NULL;
sec.bInheritHandle = FALSE;
myConsole = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sec,
CONSOLE_TEXTMODE_BUFFER,
NULL);
assert(myConsole != INVALID_HANDLE_VALUE);
/* set the codepage option */
o_codepage = GetConsoleOutputCP();
SetConsoleCP((UINT)o_codepage);
optinsert("win32", QTY(win32val), win32desc, win32val);
/* change the new buffer's size to match the current buffer's window */
prevWidth = size.X = consinfo.srWindow.Right - consinfo.srWindow.Left + 1;
prevHeight = size.Y = consinfo.srWindow.Bottom - consinfo.srWindow.Top + 1;
SetConsoleScreenBufferSize(myConsole, size);
/* make the default colors of the new console buffer be the same as the
* old console buffer.
*/
attr = origattr = consinfo.wAttributes;
SetConsoleTextAttribute(myConsole, attr);
home.X = home.Y = 0;
FillConsoleOutputAttribute(myConsole, attr, size.X * size.Y, home, &dummy);
FillConsoleOutputCharacter(myConsole, ' ', size.X * size.Y, home, &dummy);
}
/* switch to the tty state that elvis runs in */
void ttyraw(erasekey)
char *erasekey; /* where to store the ERASE key */
{
DWORD newmode;
if (useconsole)
{
/* recheck the console handles, just in case! */
inConsole = GetStdHandle(STD_INPUT_HANDLE);
assert(inConsole != INVALID_HANDLE_VALUE);
outConsole = GetStdHandle(STD_OUTPUT_HANDLE);
assert(outConsole != INVALID_HANDLE_VALUE);
/* switch to "raw" mode, and allow the window to be resized */
newmode = ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
SetConsoleMode(inConsole, newmode);
SetConsoleMode(outConsole, newmode|ENABLE_PROCESSED_OUTPUT);
SetConsoleMode(myConsole, newmode);
}
else
{
/* switch to binary mode */
_setmode(1, _O_BINARY);
}
/* trap ^C signals */
signal(SIGINT, catchsig);
}
/* switch back to the original tty state */
void ttynormal()
{
if (useconsole)
{
/* switch back to the original mode */
SetConsoleMode(inConsole, inMode);
SetConsoleMode(outConsole, outMode);
}
else
{
/* switch to text mode */
_setmode(1, _O_TEXT);
}
}
/* This function switches between screen buffers. */
static void switchcsbi(HANDLE which)
{
/* if no change, then do nothing */
if (which == console)
return;
/* switch to the requested console */
SetConsoleActiveScreenBuffer(which);
console = which;
}
/* Read from keyboard, with timeout. Since Win32 apparently can't read with
* timeout, this function simulates a timeout immediately if timeouts are
* allowed within 0.5 seconds, and waits forever for a keystroke if timeouts
* are either disabled, or are allowed in more that 0.5 seconds. Also,
* if the console is resized it returns -1 to simulate a SIGWINCH signal.
* After a mouse event, it returns -2 so that windows can be redrawn.
*/
int ttyread(buf, len, timeout)
char *buf; /* where to place the input characters */
int len; /* maximum number of characters to read */
int timeout;/* timeout (0 for none) */
{
INPUT_RECORD event[5];/* buffer, holds an input record */
DWORD mode; /* Console mode */
DWORD nevents;/* number of events read into event[] */
int e; /* for counting through event[] */
int got; /* character counter, for keystrokes */
static DWORD prevmb; /* previous mouse button state */
DWORD press; /* bitmap of new button presses */
GUIWIN *gw; /* window where mouse event happened */
static GUIWIN *selgw; /* window where selection is taking place */
int y, x; /* coordinates of mouse within "gw" window */
static BOOLEAN justpressed;/* between a press and the start of a drag */
static int prevy, prevx;/* cell where originally pressed */
static BOOLEAN justdbl;/* between double-click & bogus single-click */
/* reset the "ttycaught" variable */
signal(SIGINT, catchsig);
ttycaught = 0;
/* if timeouts are allowed within 0.5 seconds, then return 0
* immediately unless there's already a keystroke waiting.
*/
if (timeout > 0
&& timeout <= 5
&& (!useconsole || (PeekConsoleInput(inConsole, event, 1, &nevents)
&& nevents == 0)))
{
return 0;
}
/* if resized since last read, then return -1 to simulate the value
* that UNIX's read() call would return after a SIGWINCH.
*/
if (resized)
{
resized = False;
return -1;
}
/* if not using console, then just read from stdin */
if (!useconsole)
{
return fread(buf, len, sizeof(char), stdin);
}
/* initialize the mouse variables */
gw = NULL;
/* disallow SIGINT while in loop */
GetConsoleMode(inConsole, &mode);
SetConsoleMode(inConsole, mode & ~ENABLE_PROCESSED_INPUT);
signal(SIGINT, catchsig); /* just in case of bad timing! */
/* get at least one event. The only events we pay attention to are
* window events (resizing), keyboard events, and mouse events.
*/
got = nevents = 0;
do
{
/* remove the event from the input queue */
if (nevents > 0)
{
for (e = 0; e < nevents; e++)
event[e] = event[e + 1];
}
else if (!ReadConsoleInput(inConsole, event, QTY(event), &nevents))
{
/* How could ReadConsoleInput() fail? */
return -1;
}
/* process the event */
switch (event[0].EventType)
{
case KEY_EVENT:
if (event[0].Event.KeyEvent.bKeyDown)
{
for (x = event[0].Event.KeyEvent.wRepeatCount;
x > 0 && got + 2 < QTY(buf);
x--)
{
if (event[0].Event.KeyEvent.uChar.AsciiChar)
{
buf[got++] = event[0].Event.KeyEvent.uChar.AsciiChar;
}
else if (3 == (char)event[0].Event.KeyEvent.wVirtualScanCode)
{
/* Ctrl-2 should be a NUL character */
buf[got++] = '\0';
}
else if (7 == (char)event[0].Event.KeyEvent.wVirtualScanCode)
{
/* Ctrl-6 should be a ^^ character */
buf[got++] = ELVCTRL('^');
}
else if (!strchr("68*:\x1d", (char)event[0].Event.KeyEvent.wVirtualScanCode))
{
buf[got++] = '#';
buf[got++] = (char)event[0].Event.KeyEvent.wVirtualScanCode;
}
}
}
break;
case MOUSE_EVENT:
/* Ignore if no buttons pressed. Also, ignore anything
* after a double-click until button is released.
*/
if (justdbl)
{
prevmb = event[0].Event.MouseEvent.dwButtonState;
if (prevmb == 0)
justdbl = False;
break;
}
else if (event[0].Event.MouseEvent.dwButtonState == 0
&& prevmb == 0)
{
break;
}
/* Figure out which window the event occurred in */
gw = ttywindow(event[0].Event.MouseEvent.dwMousePosition.Y,
event[0].Event.MouseEvent.dwMousePosition.X,
&y, &x);
if (!gw || (selgw && selgw != gw))
break;
press = (event[0].Event.MouseEvent.dwButtonState & ~prevmb);
prevmb = event[0].Event.MouseEvent.dwButtonState;
/* Make the window become the current window */
(*gui->focusgw)(gw);
eventfocus(gw);
/* process the event */
if (event[0].Event.MouseEvent.dwEventFlags & DOUBLE_CLICK)
{
/* DOUBLE CLICK */
if (event[0].Event.MouseEvent.dwButtonState & 1)
eventclick(gw, y, x, CLICK_TAG);
else
eventclick(gw, y, x, CLICK_UNTAG);
SetConsoleMode(inConsole, mode);
selgw = NULL;
justdbl = True;
return -2;
}
if ((event[0].Event.MouseEvent.dwEventFlags & MOUSE_MOVED) != 0 && justpressed && (x != prevx || y != prevy))
{
/* starting a draw-through */
switch (event[0].Event.MouseEvent.dwButtonState)
{
case 1:
eventclick(gw, prevy, prevx, CLICK_SELCHAR);
break;
case 2:
eventclick(gw, prevy, prevx, CLICK_SELLINE);
break;
default:
eventclick(gw, prevy, prevx, CLICK_SELRECT);
}
justpressed = False;
}
else if (press != 0)
{
/* button was pressed */
if (press & 1)
eventclick(gw, y, x, CLICK_CANCEL);
justpressed = True;
selgw = gw;
}
else if (prevmb == 0)
{
/* last button released */
selgw = NULL;
}
/* move the cursor to follow the mouse */
eventclick(gw, y, x, CLICK_MOVE);
prevx = x;
prevy = y;
SetConsoleMode(inConsole, mode);
return -2;
case WINDOW_BUFFER_SIZE_EVENT:
/* Return -1 to simulate the EINTR error that a UNIX
* read() function would have after SIGWINCH arrived.
* Discard any preceding keystokes.
*/
SetConsoleMode(inConsole, mode);
return -1;
}
/* one event finished */
nevents--;
} while (got == 0 || (got < len - 2 &&
(nevents > 0 ||
(PeekConsoleInput(inConsole, event, 1, &e) && e > 0))));
SetConsoleMode(inConsole, mode);
return got;
}
/* write characters out to the screen */
void ttywrite(buf, len)
char *buf; /* buffer, holds characters to be written */
int len; /* number of characters in buf */
{
static int arg[9]; /* arguments to escape sequence */
static int argno = -1; /* -1 normally, else index of escape sequence argument */
static COORD coord; /* coordinates of cursor position */
static BOOLEAN bgset = False;/* has the background color been set? */
static BOOLEAN boldset = False;/* has the foreground brightness been set? */
SMALL_RECT rect; /* source for inserting/deleting/scrolling */
COORD dest; /* destination for inserting/deletingi/scrolling */
CHAR_INFO ci; /* fill info */
DWORD qty; /* number of characters to fill */
CONSOLE_CURSOR_INFO cci;/* cursor shape */
int i, j;
long dummy;
/* if not using a WIN32 console, then just write the characters to stdout */
if (!useconsole || gui != &guitermcap)
{
_write(1, buf, len);
return;
}
/* handle each character separately */
for (i = 0; i < len; i++)
{
if (buf[i] == '\033')
{
/* start an escape sequence */
for (j = 0; j < QTY(arg); j++)
arg[j] = 0;
argno = 0;
}
else if (console == outConsole && argno == -1)
{
/* most characters written literally to shell console */
for (j = 1; j < len && buf[i + j] != '\033'; j++)
{
}
WriteConsole(console, &buf[i], j, &dummy, NULL);
i += j - 1; /* "- 1" because of "i++" at top of loop */
}
else if (buf[i] == '\007')
{
/* write the bell character */
WriteConsole(console, &buf[i], 1, &dummy, NULL);
}
else if (buf[i] == '\b')
{
coord.X--;
if (coord.X < 0)
{
coord.X += consinfo.dwSize.X;
if (coord.Y > 0)
coord.Y--;
}
SetConsoleCursorPosition(console, coord);
}
else if (buf[i] == '\n')
{
if (coord.Y + 1 < consinfo.dwSize.Y)
{
/* move cursor down on line */
coord.Y++;
SetConsoleCursorPosition(console, coord);
}
else
{
/* scroll screen upward one line */
rect.Top = 1;
rect.Left = 0;
rect.Bottom = consinfo.dwSize.Y;
rect.Right = consinfo.dwSize.X;
dest.X = 0;
dest.Y = 0;
ci.Char.AsciiChar = ' ';
ci.Attributes = attr;
ScrollConsoleScreenBuffer(console, &rect, NULL, dest, &ci);
}
}
else if (buf[i] == '\r')
{
coord.X = 0;
SetConsoleCursorPosition(console, coord);
}
else if ((unsigned)buf[i] < ' ')
{
/* other control characters have no effect */
}
else if (argno < 0)
{
/* normal character */
/* count consecutive normal characters */
for (j = 1; i + j < len && buf[i + j] >= ' '; j++)
{
}
/* write the normal characters all at once. Note
* that the attribute is set on myConsole, regardless
* of which console buffer is currently active.
*/
SetConsoleCursorPosition(console, coord);
SetConsoleTextAttribute(myConsole, attr);
WriteConsole(console, &buf[i], j, &dummy, NULL);
/* move "i" past all but the last character. The "i++"
* in the for() loop will take care of that last one.
*/
i += j - 1;
/* figure out where the cursor belongs after that. */
#ifdef SMART_LINE_WRAP
while (coord.X + j > consinfo.dwSize.X)
{
coord.Y++;
j -= consinfo.dwSize.X;
}
coord.X += j;
if (coord.X == consinfo.dwSize.X)
coord.X--;
#else /* dumb line wrap */
while (coord.X + j >= consinfo.dwSize.X)
{
coord.Y++;
j -= consinfo.dwSize.X;
}
coord.X += j;
#endif
/* check for scrolling */
j = coord.Y - consinfo.dwSize.Y + 1;
if (j > 0)
{
/* scroll screen upward "j" lines */
rect.Top = j;
rect.Left = 0;
rect.Bottom = consinfo.dwSize.Y;
rect.Right = consinfo.dwSize.X;
dest.X = 0;
dest.Y = 0;
ci.Char.AsciiChar = ' ';
ci.Attributes = attr;
ScrollConsoleScreenBuffer(outConsole, &rect, NULL, dest, &ci);
/* this leaves the cursor on the last row */
coord.Y = consinfo.dwSize.Y - 1;
}
}
else
{
/* in an escape sequence... */
switch (buf[i])
{
case '[':
case '?':
/* ignored */
break;
case ';':
/* advance to next argument */
argno++;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/* incorporate this digit into current arg */
arg[argno] = arg[argno] * 10 + buf[i] - '0';
break;
case 'A':
j = (arg[0] ? arg[0] : 1);
if (coord.Y >= j)
coord.Y -= j;
SetConsoleCursorPosition(console, coord);
argno = -1;
break;
case 'B':
j = (arg[0] ? arg[0] : 1);
if (coord.Y + j < consinfo.dwSize.Y)
coord.Y += j;
SetConsoleCursorPosition(console, coord);
argno = -1;
break;
case 'C':
j = (arg[0] ? arg[0] : 1);
if (coord.X + j < consinfo.dwSize.X)
coord.X += j;
SetConsoleCursorPosition(console, coord);
argno = -1;
break;
case 'D':
j = (arg[0] ? arg[0] : 1);
if (coord.X >= j)
coord.X -= j;
SetConsoleCursorPosition(console, coord);
argno = -1;
break;
case 'H':
/* move the cursor */
coord.X = (arg[1] ? arg[1] - 1 : 0);
coord.Y = (arg[0] ? arg[0] - 1 : 0);
SetConsoleCursorPosition(console, coord);
argno = -1;
break;
case 'J':
/* clear the screen */
qty = consinfo.dwSize.X - coord.X +
consinfo.dwSize.X * (consinfo.dwSize.Y - 1 - coord.Y);
if (qty > 0)
{
FillConsoleOutputCharacter(console, ' ', qty, coord, &dummy);
FillConsoleOutputAttribute(console, attr, qty, coord, &dummy);
}
argno = -1;
break;
case 'K':
/* clear to end-of-line */
qty = consinfo.dwSize.X - coord.X;
if (qty > 0)
{
FillConsoleOutputCharacter(console, ' ', qty, coord, &dummy);
if (console == myConsole)
{
FillConsoleOutputAttribute(console, attr, qty, coord, &dummy);
}
}
argno = -1;
break;
case 'L':
/* insert j lines */
j = arg[0] ? arg[0] : 1;
rect.Top = coord.Y;
rect.Left = 0;
rect.Bottom = consinfo.dwSize.Y - j;
rect.Right = consinfo.dwSize.X;
dest.X = 0;
dest.Y = coord.Y + j;
ci.Char.AsciiChar = ' ';
ci.Attributes = attr;
ScrollConsoleScreenBuffer(console, &rect, NULL, dest, &ci);
argno = -1;
break;
case 'M':
/* delete j lines */
j = arg[0] ? arg[0] : 1;
rect.Top = coord.Y + j;
rect.Left = 0;
rect.Bottom = consinfo.dwSize.Y;
rect.Right = consinfo.dwSize.X;
dest.X = 0;
dest.Y = coord.Y;
ci.Char.AsciiChar = ' ';
ci.Attributes = attr;
ScrollConsoleScreenBuffer(console, &rect, NULL, dest, &ci);
argno = -1;
break;
case 'h':
if (arg[0] == 1)
{
switchcsbi(myConsole);
}
else if (arg[0] == 12)
{
cci.dwSize = 50;
cci.bVisible = True;
SetConsoleCursorInfo(myConsole, &cci);
}
argno = -1;
break;
case 'l':
if (arg[0] == 1)
{
switchcsbi(outConsole);
}
else if (arg[0] == 12)
{
cci.dwSize = 20;
cci.bVisible = True;
SetConsoleCursorInfo(myConsole, &cci);
}
argno = -1;
break;
case 'm':
for (j = 0; j <= argno; j++)
{
switch (arg[j])
{
case 0:
attr = origattr;
bgset = False;
boldset = False;
break;
case 1:
if (attr == origattr
&& (attr & FOREGROUND_INTENSITY) != 0)
attr |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
else
attr |= FOREGROUND_INTENSITY;
boldset = True;
break;
case 4:
if (!bgset)
attr ^= BACKGROUND_RED;
break;
case 7:
attr ^= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE
| BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE;
if (!boldset)
attr &= ~FOREGROUND_INTENSITY;
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
attr &= ~(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
if (!boldset)
attr &= ~FOREGROUND_INTENSITY;
if ((arg[j] - 30) & 1)
attr |= FOREGROUND_RED;
if ((arg[j] - 30) & 2)
attr |= FOREGROUND_GREEN;
if ((arg[j] - 30) & 4)
attr |= FOREGROUND_BLUE;
break;
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
attr &= ~(BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE);
if ((arg[j] - 40) & 1)
attr |= BACKGROUND_RED;
if ((arg[j] - 40) & 2)
attr |= BACKGROUND_GREEN;
if ((arg[j] - 40) & 4)
attr |= BACKGROUND_BLUE;
bgset = True;
break;
}
}
/* note that we always set the attribute of
* myConsole, regardless of which console buffer
* is currently active.
*/
SetConsoleTextAttribute(myConsole, attr);
argno = -1;
break;
default:
/* does it look like the end of an unrecognized command? */
if ((buf[i] & 0x40) != 0)
{
argno = -1;
}
}
}
}
}
/* determine the terminal type */
char *ttytermtype()
{
char *type;
type = getenv("TERM");
if (!type)
type = TTY_DEFAULT;
/* are we using the console? */
useconsole = !strcmp(type, "console");
if (useconsole)
{
inConsole = GetStdHandle(STD_INPUT_HANDLE);
if (inConsole == INVALID_HANDLE_VALUE)
{
AllocConsole();
inConsole = GetStdHandle(STD_INPUT_HANDLE);
}
outConsole = GetStdHandle(STD_OUTPUT_HANDLE);
assert(inConsole != INVALID_HANDLE_VALUE);
}
return type;
}
/* This function gets the window size. */
BOOLEAN ttysize(linesptr, colsptr)
int *linesptr; /* where to store the number of rows */
int *colsptr; /* where to store the number of columns */
{
SMALL_RECT size;
if (!useconsole)
return False;
/* Get the console buffer size */
if (!GetConsoleScreenBufferInfo(myConsole, &consinfo))
return False;
prevHeight = *linesptr = consinfo.dwSize.Y;
prevWidth = *colsptr = consinfo.dwSize.X;
/* make the window as large as the console buffer */
size.Top = 0;
size.Left = 0;
size.Bottom = *linesptr - 1;
size.Right = *colsptr - 1;
(void)SetConsoleWindowInfo(myConsole, TRUE, &size);
return True;
}
/* Check for signs of boredom from user, so we can abort a time-consuming
* operation. Here we check to see if SIGINT has been caught recently.
* Returns True to abort an operation, or False to continue it.
*/
BOOLEAN ttypoll(reset)
BOOLEAN reset;
{
return (BOOLEAN)((ttycaught & (1 << SIGINT)) != 0);
}
#endif /* GUI_TERMCAP */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.