Source to jet/console.c
#include <Events.h>
#include <Memory.h>
#include <OSUtils.h>
#include <Quickdraw.h>
#include <Resources.h>
#include <Windows.h>
#include <Script.h>
#include "jet.h"
#if 0
#define NEWKEY /* use the new key press method (doesn't quite work) */
#endif
#define MAXUNFRESH 10
#define DEF_FLAGS CURS_ON
#define KBDSIZE 0x80
#define ASIZE 1024
#define LEFT 4
#define INVERSE 1
typedef void (*Vfunc)(int);
#define paint cpaint
INLINE static void flash(void);
INLINE static void paint(int);
INLINE static void gotoxy(int, int);
INLINE static void clrline(int);
INLINE static void clear(void);
INLINE static void clrchar(int, int);
INLINE static void clrfrom(int, int, int, int);
INLINE static void delete_line(int);
INLINE static void insert_line(int);
INLINE static void markinvers(int, int);
INLINE static void smooth(void);
static void setbgcol(int);
static void setfgcol(int);
static void putesc(int);
static void escy1_putch(int);
static void escy_putch(int);
static void normal_putch(int);
INLINE static void put_ch(int);
static void kbdvec1(void);
static void kbdvec2(void *p);
static long kbdvec3(void);
static long ikbd(void);
unsigned char kbshft;
_KBDVECS kbd;
long keymsg;
short keymod;
static char keytbl[256];
short maxx, maxy;
static short flags;
static Vfunc state;
static Rect crect;
static short cx, cy;
static short escy1;
static short linelen;
static short savex, savey;
static short sincerefresh = 0;
static char scrbuf[(long)TOTROWS*TOTCOLS];
static char xtrabuf[(long)TOTROWS*(TOTCOLS+1)];
char *base[TOTROWS];
char *xbase[TOTROWS];
static char **screen;
static char **extras;
static RgnHandle rgn;
static _IOREC keyrec;
static char kbdbuf[KBDSIZE];
static char alinevars[ASIZE];
void init_console(void)
{
int r;
__aline = (__LINEA *)(alinevars + ASIZE);
memset(scrbuf, ' ', (long)TOTROWS * TOTCOLS);
memset(xtrabuf, '\0', (long)TOTROWS * (TOTCOLS + 1));
for (r = 0; r < TOTROWS; r++) {
base[r] = scrbuf + r * (long)TOTCOLS;
xbase[r] = xtrabuf + r * ((long)TOTCOLS + 1);
}
flags = DEF_FLAGS;
V_CEL_MX = maxx = pref.maxx;
V_CEL_MY = maxy = pref.maxy;
linelen = maxx + 1;
screen = base + (TOTROWS - maxy - 1);
extras = xbase + (TOTROWS - maxy - 1);
state = normal_putch;
kbd.midivec = kbdvec1;
kbd.vkbderr = kbdvec1;
kbd.vmiderr = kbdvec1;
kbd.statvec = kbdvec2;
kbd.mousevec = kbdvec2;
kbd.clockvec = kbdvec2;
kbd.joyvec = kbdvec2;
kbd.midisys = kbdvec3;
kbd.ikbdsys = ikbd;
keyrec.ibuf = kbdbuf;
keyrec.ibufsiz = KBDSIZE;
keyrec.ibufhd = 0;
keyrec.ibuftl = 0;
keyrec.ibuflow = 0x20;
keyrec.ibufhi = 0x20;
rgn = NewRgn();
}
long c_conws(const char *buf)
{
const char *s = buf;
const char *string;
int count = 0;
Rect r;
if (flags & CURS_FSTATE)
curs_off();
scrollBottom();
string = s;
while (*s) {
if ((*s >= ' ') && (cx < maxx) && (state == normal_putch)) {
count++;
*(screen[cy] + cx++) = *s++;
markinvers(cx, cy);
} else {
if (count) {
r.left = crect.left;
crect.left += count * fwidth;
crect.right = crect.left + fwidth;
r.top = cy * fheight;
r.bottom = r.top + ftotal;
r.right = crect.left;
InvalRect(&r);
count = 0;
}
if (!emptySelection())
if (((cy == selectedChars.top) && (cx >= selectedChars.left)) ||
((cy == selectedChars.bottom) && (cx <= selectedChars.right)) ||
((cy > selectedChars.top) && (cy < selectedChars.bottom)))
clearSelection();
put_ch(*s++);
#if 0
/* put_ch might turn the cursor on ? */
if (flags & CURS_FSTATE)
curs_off();
#endif
string = s;
}
}
if (count) {
r.left = crect.left;
crect.left += count * fwidth;
crect.right = crect.left + fwidth;
r.top = cy * fheight;
r.bottom = r.top + ftotal;
r.right = crect.left;
InvalRect(&r);
if (!emptySelection())
if (((cy == selectedChars.top) && (cx >= selectedChars.left)) ||
((cy == selectedChars.bottom) && (cx <= selectedChars.right)) ||
((cy > selectedChars.top) && (cy < selectedChars.bottom)))
clearSelection();
}
return 0;
}
/*
* this routine is called very often from the mint kernel. this allows
* the event loop to keep up with events.
*/
long b_constat(short dev)
{
eventloop();
if (dev != 2)
return 0;
return (keyrec.ibufhd != keyrec.ibuftl) ? -1 : 0;
}
long b_costat(short dev)
{
if (dev != 2)
return 0;
return -1;
}
long b_conin(short dev)
{
short h;
long r;
if (dev != 2)
return 0;
while (keyrec.ibufhd == keyrec.ibuftl)
eventloop();
h = keyrec.ibufhd + 4;
if (h >= keyrec.ibufsiz)
h = 0;
r = *((long *)(keyrec.ibuf + h));
keyrec.ibufhd = h;
return r;
}
long b_conout(short dev, short c)
{
if (dev != 2)
return 0;
if (flags & CURS_FSTATE)
curs_off();
scrollBottom();
put_ch(c);
return 1;
}
long b_kbshift(short mode)
{
return kbshft;
}
static void kbdvec1(void)
{
DebugStr("\pkbdvec1 called");
}
static void kbdvec2(void *p)
{
DebugStr("\pkbdvec2 called");
}
static long kbdvec3(void)
{
DebugStr("\pkbdvec3 called");
return 0;
}
static short mac2keys(long message, short modifiers, unsigned long *keys)
{
short nkeys;
unsigned long code, scan;
#if 0
static Ptr transData;
static long transState;
#endif
kbshft = 0;
nkeys = 1;
scan = ((unsigned long)message >> 8) & 0xff;
if (modifiers & optionKey) {
if (((modifiers & controlKey) && !pref.swap_control) ||
((modifiers & shiftKey) && pref.swap_control))
kbshft = 0x0c;
#if 0
else {
if (transData == 0) {
#if 0
transData = GetScriptManagerMVariable(smKCHRCache);
#else
Handle h = GetResource(KCHR, 0);
long size = GetHandleSize(h);
transData = NewPtr(size);
BlockMove(*h, transData, size);
#endif
transState = 0;
}
/* translate the code into pure ASCII. */
code = KeyTrans(transData, scan, &transState);
/* pre-insert an escape so emacs thinks we typed meta-key. */
keys[0] = 033;
keys[1] = code | (scan << 16);
return 2;
}
#endif
}
switch (scan) {
#ifdef NEWKEY
case 0x7e: /* up arrow */
code = 0;
scan = 0x48;
break;
case 0x7d: /* down arrow */
code = 0;
scan = 0x50;
break;
case 0x7c: /* right arrow */
code = 0;
scan = 0x4d;
break;
case 0x7b: /* left arrow */
code = 0;
scan = 0x4b;
break;
#else
case 0x7e: /* up arrow */
code = 033;
scan = 0;
nkeys = 2;
keys[1] = 'A';
break;
case 0x7d: /* down arrow */
code = 033;
scan = 0;
nkeys = 2;
keys[1] = 'B';
break;
case 0x7c: /* right arrow */
code = 033;
scan = 0;
nkeys = 2;
keys[1] = 'C';
break;
case 0x7b: /* left arrow */
code = 033;
scan = 0;
nkeys = 2;
keys[1] = 'D';
break;
#endif
case 0x7a: /* F1 */
code = 0;
scan = 0x3b;
break;
case 0x78: /* F2 */
code = 0;
scan = 0x3c;
break;
case 0x60: /* F5 */
code = 0;
scan = 0x3f;
break;
case 0x61: /* F6 */
code = 0;
scan = 0x40;
break;
default:
code = (unsigned long)message & 0xff;
if (kbshft == 0x0c) {
switch (code) {
case '1': /* F1 */
code = 0;
scan = 0x3b;
break;
case '2': /* F2 */
code = 0;
scan = 0x3c;
break;
case '5': /* F5 */
code = 0;
scan = 0x3f;
break;
case '6': /* F6 */
code = 0;
scan = 0x40;
break;
default:
break;
}
}
if (pref.swap_delete)
if (scan == 0x75)
code = 0x08;
else if (scan == 0x33)
code = 0x7f;
if ((code == ' ') &&
(((modifiers & controlKey) && !pref.swap_control) ||
((modifiers & optionKey) && pref.swap_control))) {
code = 0;
scan = 0;
}
break;
}
keys[0] = code | (scan << 16);
return nkeys;
}
static long ikbd(void)
{
short t;
short nkeys;
unsigned long keys[2], *kp;
nkeys = mac2keys(keymsg, keymod, keys);
kp = keys;
while (nkeys--) {
t = keyrec.ibuftl + 4;
if (t >= keyrec.ibufsiz)
t = 0;
*((unsigned long *)(keyrec.ibuf + t)) = *kp++;
keyrec.ibuftl = t;
}
return 0;
}
_KBDVECS *x_kbdvbase(void)
{
return &kbd;
}
/* this needs to be modified to return the correct value for
the modem device too. see biosfs_init in biosfs.c (1.10) */
_IOREC *x_iorec(short dev)
{
if (dev == 1) {
return &keyrec;
}
return (_IOREC *)-EINVAL;
}
/* return some very large address for the screen base so that
* mint doesn't try to move it.
*/
long x_physbase(void)
{
return 0x7fffffff;
}
long x_logbase(void)
{
return -EINVAL;
}
long x_bconmap(short dev)
{
return -EINVAL;
}
long x_keytbl(void *nrml, void *shft, void *caps)
{
return (long)keytbl;
}
/* routines for flashing the cursor */
INLINE static void
markinvers(int x, int y)
{
if (flags & FINVERSE) {
*(extras[y]) = '\255';
*(extras[y] + x) |= INVERSE;
} else
*(extras[y] + x) &= ~INVERSE;
}
INLINE static void
smooth(void)
{
if (sincerefresh >= MAXUNFRESH) {
BeginUpdate(myWindow);
refresh();
EndUpdate(myWindow);
/* eventloop(); */
}
}
/* flash(): invert the character currently under the cursor */
INLINE static void
flash(void)
{
GetClip(rgn);
ClipRect(&crect);
InvertRect(&crect);
SetClip(rgn);
}
/* make sure the cursor is off */
void curs_off(void)
{
if (flags & CURS_ON) {
if (flags & CURS_FSTATE) {
flash();
flags &= ~CURS_FSTATE;
}
}
}
/* OK, show the cursor again (if appropriate) */
void curs_on(void)
{
if (flags & CURS_ON) {
/* if the cursor is flashing, we cheat a little and leave it off
* to be turned on again (if necessary) by the VBL routine
*/
if (pref.curs_flash) {
return;
}
if (!(flags & CURS_FSTATE)) {
flags |= CURS_FSTATE;
flash();
}
}
}
/* enable/disable the cursor. */
void curs_enable()
{
flags |= CURS_ON;
}
void curs_disable()
{
flags &= ~CURS_ON;
}
/*
* paint(c): put character 'c' at current position.
*/
INLINE static void
paint(int c)
{
*(screen[cy] + cx) = c;
markinvers(cx + 1, cy);
InvalRect(&crect);
flags &= ~CURS_FSTATE;
}
/*
* gotoxy (x, y): move current cursor address to (x, y)
* makes sure that (x, y) will be legal
*/
INLINE static void
gotoxy(int x, int y)
{
if (x > maxx) x = maxx;
else if (x < 0) x = 0;
if (y > maxy) y = maxy;
else if (y < 0) y = 0;
if (flags & CURS_FSTATE)
curs_off();
cx = x;
cy = y;
crect.left = LEFT;
if (x != 0)
crect.left += cx * fwidth;
crect.top = cy * fheight;
crect.right = crect.left + fwidth;
crect.bottom = crect.top + ftotal;
}
/*
* clrline(n): clear line n
*/
INLINE static void
clrline(int n)
{
Rect r;
if (!emptySelection() &&
((n >= selectedChars.top) && (n <= selectedChars.bottom)))
clearSelection();
r = conRect;
if (n != maxy)
r.bottom = (n + 1) * fheight;
r.top = r.bottom - fheight;
memset(screen[n], ' ', TOTCOLS);
memset(extras[n], '\0', TOTCOLS + 1);
InvalRect(&r);
}
/*
* clear(): clear the whole screen
*/
INLINE static void
clear(void)
{
int r;
clearSelection();
for (r = 0; r <= maxy; r++) {
memset(screen[r], ' ', TOTCOLS);
memset(extras[r], '\0', TOTCOLS + 1);
}
InvalRect(&conRect);
}
/*
* clrchar(x, y): clear the (x,y) position on screen v
*/
INLINE static void
clrchar(int x, int y)
{
Rect r;
if (!emptySelection())
if (((y == selectedChars.top) && (x >= selectedChars.left)) ||
((y == selectedChars.bottom) && (x <= selectedChars.right)) ||
((y > selectedChars.top) && (y < selectedChars.bottom)))
clearSelection();
*(screen[y] + x) = ' ';
markinvers(x + 1, y);
r.left = LEFT + x * fwidth;
r.top = y * fheight;
r.right = r.left + fwidth;
r.bottom = r.top + ftotal;
InvalRect(&r);
}
/*
* clrfrom(x1, y1, x2, y2): clear from position (x1,y1) to
* position (x2, y2) inclusive. It is assumed that y2 >= y1.
*/
INLINE static void
clrfrom(int x1, int y1, int x2, int y2)
{
int i;
Rect r;
if (!emptySelection())
if (((y1 == selectedChars.top) && (x1 <= selectedChars.left)) ||
((y2 == selectedChars.top) && (x2 >= selectedChars.right)) ||
((y1 == selectedChars.bottom) && (x1 <= selectedChars.right)) ||
((y2 == selectedChars.bottom) && (x2 >= selectedChars.right)) ||
((y1 < selectedChars.top) && (y2 > selectedChars.bottom)) ||
((y1 > selectedChars.top) && (y2 < selectedChars.bottom)))
clearSelection();
/* clear from x1 on first line */
if (y2 > y1) {
for (i = x1; i <= maxx; i++) {
*(screen[y1] + i) = ' ';
markinvers(i + 1, y1);
}
r.left = LEFT + x1 * fwidth;
r.top = y1 * fheight;
r.right = LEFT + (maxx + 1) * fwidth;
r.bottom = r.top + ftotal;
InvalRect(&r);
/* clear to x2 of last line */
for (i = 0; i <= x2; i++) {
*(screen[y2] + i) = ' ';
markinvers(i + 1, y2);
}
r.left = LEFT;
r.top = y2 * fheight;
r.right = LEFT + (x2 + 1) * fwidth;
r.bottom = r.top + ftotal;
InvalRect(&r);
} else {
for (i = x1; i <= x2; i++) {
*(screen[y1] + i) = ' ';
markinvers(i + 1, y1);
}
r.left = LEFT + x1 * fwidth;
r.top = y1 * fheight;
r.right = LEFT + (x2 + 1) * fwidth;
r.bottom = r.top + ftotal;
InvalRect(&r);
}
/* clear intermediate lines if any */
for (i = y1+1; i < y2; i++)
clrline(i);
sincerefresh += y2 - y1 + 1;
smooth();
}
/*
* delete_line(n): delete line n. The screen below this
* line is scrolled up, and the bottom line is cleared.
*/
#define scroll() delete_line(0)
INLINE static void
delete_line(int n)
{
Rect sRect;
short r, nrows;
char **tbase, **xtbase, *save, *xsave;
sRect = conRect;
if (n == 0) {
nrows = TOTROWS - 1;
tbase = base;
xtbase = xbase;
} else {
sRect.top = n * fheight;
nrows = maxy - n;
tbase = screen;
xtbase = extras;
}
save = tbase[n];
xsave = xtbase[n];
for (r = n; nrows--; r++) {
tbase[r] = tbase[r+1];
xtbase[r] = xtbase[r+1];
}
tbase[r] = save;
xtbase[r] = xsave;
sRect.bottom -= fheight;
InvalRect(&sRect);
selectedChars.top -= 1;
selectedChars.bottom -= 1;
/* clear the last line */
clrline(maxy);
sincerefresh++;
smooth();
}
/*
* insert_line(n): scroll all of the screen starting at line n down,
* and then clear line n.
*/
INLINE static void
insert_line(int n)
{
Rect sRect;
short r;
char *save, *xsave;
save = screen[maxy];
xsave = extras[maxy];
for (r = maxy - 1; r >= n ; --r) {
screen[r+1] = screen[r];
extras[r+1] = extras[r];
}
screen[r+1] = save;
extras[r+1] = xsave;
sRect = conRect;
sRect.top = n * fheight;
InvalRect(&sRect);
/* clear line n */
clrline(n);
}
/*
* special states for handling ESC b x and ESC c x. Note that for now,
* color is ignored.
*/
static void
setbgcol(int c)
{
state = normal_putch;
}
static void
setfgcol(int c)
{
state = normal_putch;
}
/*
* putesc(c): handle the control sequence ESC c
*/
static void
putesc(int c)
{
int cx1, cy1;
cx1 = cx; cy1 = cy;
switch (c) {
case 'A': /* cursor up */
gotoxy(cx1, cy1-1);
break;
case 'B': /* cursor down */
gotoxy(cx1, cy1+1);
break;
case 'C': /* cursor right */
gotoxy(cx1+1, cy1);
break;
case 'D': /* cursor left */
gotoxy(cx1-1, cy1);
break;
case 'E': /* clear home */
if (flags & CURS_FSTATE)
curs_off();
clear();
/* fall through... */
case 'H': /* cursor home */
gotoxy(0, 0);
break;
case 'I': /* cursor up, insert line */
if (cy1 == 0) {
if (flags & CURS_FSTATE)
curs_off();
insert_line(0);
}
else
gotoxy(cx1, cy1-1);
break;
case 'J': /* clear below cursor */
if (flags & CURS_FSTATE)
curs_off();
clrfrom(cx1, cy1, maxx, maxy);
break;
case 'K': /* clear remainder of line */
if (flags & CURS_FSTATE)
curs_off();
clrfrom(cx1, cy1, maxx, cy1);
break;
case 'L': /* insert a line */
gotoxy(0, cy1);
insert_line(cy1);
break;
case 'M': /* delete line */
gotoxy(0, cy1);
delete_line(cy1);
break;
case 'Y':
state = escy_putch;
return; /* YES, this should be 'return' */
case 'b':
state = setfgcol;
return;
case 'c':
state = setbgcol;
return;
case 'd': /* clear to cursor position */
if (flags & CURS_FSTATE)
curs_off();
clrfrom(0, 0, cx1, cy1);
break;
case 'e': /* enable cursor */
if (flags & CURS_FSTATE)
curs_off();
flags |= CURS_ON;
break;
case 'f': /* disable cursor */
if (flags & CURS_FSTATE)
curs_off();
flags &= ~CURS_ON;
break;
case 'j': /* save cursor position */
savex = cx;
savey = cy;
break;
case 'k': /* restore saved position */
gotoxy(savex, savey);
break;
case 'l': /* clear line */
gotoxy(0, cy1);
clrline(cy1);
break;
case 'o': /* clear from start of line to cursor */
if (flags & CURS_FSTATE)
curs_off();
clrfrom(0, cy1, cx1, cy1);
break;
case 'p': /* reverse video on */
flags |= FINVERSE;
break;
case 'q': /* reverse video off */
flags &= ~FINVERSE;
break;
case 'v': /* wrap on */
pref.fwrap = true;
break;
case 'w':
pref.fwrap = false;
break;
}
state = normal_putch;
}
/*
* escy1_putch(c): for when an ESC Y + char has been seen
*/
static void
escy1_putch(int c)
{
gotoxy(c - ' ', escy1 - ' ');
state = normal_putch;
}
/*
* escy_putch(c): for when an ESC Y has been seen
*/
static void
escy_putch(int c)
{
escy1 = c;
state = escy1_putch;
}
/*
* normal_putch(c): put character 'c'. This is the default
* for when no escape, etc. is active
*/
static void
normal_putch(int c)
{
/* control characters */
if (c < ' ') {
switch (c) {
case '\r':
gotoxy(0, cy);
return;
case '\n':
if (cy == maxy) {
if (flags & CURS_FSTATE)
curs_off();
scroll();
}
else
gotoxy(cx, cy+1);
return;
case '\b':
gotoxy(cx-1, cy);
return;
case '\007': /* BELL */
SysBeep(5);
return;
case '\033': /* ESC */
state = putesc;
return;
case '\t':
gotoxy((cx + 8) & ~7, cy);
return;
default:
return;
}
}
if ((cx > maxx) && pref.fwrap) { /* if we need to wrap */
if (flags & CURS_FSTATE)
curs_off();
cx = 0;
crect.left = LEFT;
crect.right = crect.left + fwidth;
normal_putch('\n');
}
if (!emptySelection())
if (((cy == selectedChars.top) && (cx >= selectedChars.left)) ||
((cy == selectedChars.bottom) && (cx <= selectedChars.right)) ||
((cy > selectedChars.top) && (cy < selectedChars.bottom)))
clearSelection();
paint(c);
cx++;
/*
* Don't move cursor right if at limit
* Wrapping will take care of it when it is time.
*/
if (cx <= maxx) {
crect.left = crect.right;
crect.right += fwidth;
}
}
INLINE static void
put_ch(int c)
{
(*state)(c & 0x00ff);
}
void refresh(void)
{
static Point one1 = { 1, 1 };
char **p, **e, *x;
int i, r, r1, r2;
Rect iRect,sect;
int none;
sincerefresh = 0;
iRect = (*myWindow->visRgn)->rgnBBox;
/* <PCB> we need to know if the cursor will get obliterated, and mark it as so. */
if (RectInRgn(&crect, myWindow->visRgn))
flags &= ~CURS_FSTATE;
r1 = iRect.top / fheight;
r2 = (iRect.bottom + fheight - 1) / fheight;
p = base + curVal + r1;
e = xbase + curVal + r1;
for (r = r1; r < r2; r++) {
MoveTo(LEFT, r*fheight + fascent);
StdText(maxx+1, (Ptr)*p++, one1, one1);
if (**e++) {
x = *(e - 1);
none = true;
sect.top = r * fheight;
sect.bottom = sect.top + ftotal;
for (i = 1; i <= maxx + 1; i++)
if (*++x & INVERSE) {
none = false;
sect.left = LEFT + (i - 1) * fwidth;
sect.right = sect.left + fwidth;
InvertRect(§);
}
if (none)
*x = '\0'; /* No longer any attributes on this line */
}
}
}
void resize(short rows, short columns)
{
short v;
v = (rows - 1) - maxy;
V_CEL_MX = maxx = columns - 1;
V_CEL_MY = maxy = rows - 1;
linelen = maxx + 1;
screen = base + (TOTROWS - maxy - 1);
extras = xbase + (TOTROWS - maxy - 1);
gotoxy(cx, cy + v);
}
/* called to update the cursor */
void swap_curs(void)
{
static long nextblink = 0;
long time;
if (!(flags & CURS_ON) || (curVal != maxVal))
return;
if (pref.curs_flash) {
time = TickCount();
if (time >= nextblink) {
nextblink = time + GetCaretTime();
flash();
flags ^= CURS_FSTATE;
}
} else {
curs_on();
}
}