home *** CD-ROM | disk | FTP | other *** search
- /* $Id: mac-vt.c,v 1.6 89/05/06 17:13:35 lee Exp $
- **
- ** vt.c - a replacement dev:console driver for the Keynote musical expression
- ** language.
- **
- ** Provides a cursor, constant-width characters, and (someday) all the escape
- ** sequences for vt100 emulation. The driver (handler) is installed at
- ** run-time (for MPW 1.0) by doing the following call (before doing any I/O,
- ** since that would auto-initialize the default system driver).
- **
- ** _addDevHandler(1, 'CONS', vt_faccess, vt_close, vt_read, vt_write, vt_ioctl);
- **
- ** WARNING: this code assumes the MPW 1.0 development system. It hooks itself
- ** in as a device driver using the undocumented _addDevHandler call, which
- ** may change in future releases of MPW. It expects the system driver to be
- ** in slot 1 - this also may change.
- **
- ** If you are not using MPW, some or all of this driver may be unnecessary.
- ** The vt_getch and vt_peekch functions probably ARE necessary and probably
- ** work correctly under any development system. They mainly provide the
- ** ability to stat the console to do non-blocking input. They completely
- ** avoid MPW's stdio (and all other libraries for that matter), although
- ** the vt_read call provides an stdio interface if desired (blocking input
- ** only, of course). They DO assume that the mac has been initialized and
- ** the event queue has been set up (InitGraf et.al.).
- **
- ** The problem is printf. If your development system has an acceptable
- ** implementation (MPW *does NOT*), then just comment out the definition
- ** of MPW in Keynote source file machdep.h and give 'er a try. If, however,
- ** you want to use my vt_putch or vt_write routines, you will have to figure
- ** out how to hook them into your system. The only thing to beware of is to
- ** call vt_open before using vt_putch. This is done automatically via the
- ** vt_faccess call for MPW 1.0.
- **
- ** Steven A. Falco 4/30/87 moss!saf
- */
- #include <types.h>
- #include <quickdraw.h>
- #include <toolutils.h>
- #include <fonts.h>
- #include <events.h>
- #include <windows.h>
- #include <dialogs.h>
- #include <menus.h>
- #include <desk.h>
- #include <textedit.h>
- #include <scrap.h>
- #include <segload.h>
- #include <resources.h>
- #include <osutils.h>
- #include <ioctl.h>
- #include <fcntl.h>
- #include "vt.h"
- #include <stdio.h>
-
- #define MAXROW 24
- #define MAXCOL 80
- #define TABVAL 8
-
- #define ENONE 0
- #define EFIRST 1
- #define EBRACKET 2
-
- /* note - the pixel location is based on the character baseline. */
- #define XL(x) ((x) * vt_font.widMax + 4) /* x offset from chars to pixels */
- #define YL(y) (((y) + 1) * vt_line - 2) /* y offset from chars to pixels */
- #define HOP MoveTo(XL(vt_col), YL(vt_row)); GetPen(&vt_pen)
-
- /* BLANK(1,1) scrubs one cell. (1,2) does a cell and the cell below it,
- * while (2,1) does a cell and the cell to the right of it. etc.
- */
- #define BLANK(i, j) SetRect(&vt_one_char, vt_pen.h, vt_pen.v - vt_above, \
- vt_pen.h + (i) * vt_font.widMax, \
- vt_pen.v + vt_below + ((j) - 1) * vt_line); \
- EraseRect(&vt_one_char)
-
- #define CURSOR SetRect(&vt_one_char, vt_pen.h, vt_pen.v - vt_above, \
- vt_pen.h + vt_font.widMax, vt_pen.v + vt_below); \
- PenMode(patXor); \
- PaintRect(&vt_one_char); \
- PenMode(patCopy)
-
- #define SAVE vt_saverow = vt_row; vt_savecol = vt_col
-
- #define RESTORE vt_row = vt_saverow; vt_col = vt_savecol
-
- WindowPtr vt;
- FontInfo vt_font;
- int vt_line, vt_above, vt_below;
- int vt_col, vt_savecol;
- int vt_row, vt_saverow;
- Rect vt_one_char;
- Rect vt_rect;
- Rect screenRect;
- Point vt_pen;
- int vt_rawf;
- int vt_esc;
- int vt_inprog; /* number in progress */
- int vt_x, vt_y; /* for motion escape code */
-
- static int firsttime = 0;
- static int linesize = 0;
- static char *lineptr;
- static char linebuf[256];
-
- /* initialize the world. We call this based on the "firsttime" flag - not an
- * every faccess call! - we only want to do it once...
- */
- vt_open()
- {
- char *vt_title;
- int i;
-
- InitGraf(&qd.thePort);
- InitFonts();
- FlushEvents(everyEvent, 0);
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(nil);
- InitCursor();
-
- vt_title = "";
- screenRect = qd.screenBits.bounds;
- SetRect(&vt_rect, 4, 20 + 4, screenRect.right - 4, screenRect.bottom - 4);
- vt = NewWindow(nil, &vt_rect, vt_title, true, plainDBox, -1, false, 0);
- SetPort(vt);
- vt_rect.left -= 4; /* make vt_rect local - only used for scrolls hereafter */
- vt_rect.right -= 4;
- vt_rect.top -= (20 + 4);
- vt_rect.bottom -= (20 + 4);
-
- TextFont(monaco); /* this driver only supports constant-width */
- TextSize(9);
- GetFontInfo(&vt_font);
- PenMode(patCopy);
- vt_line = vt_font.ascent + vt_font.descent + vt_font.leading;
- vt_above = vt_font.ascent; /* dereference structure for efficiency */
- vt_below = vt_font.descent;
- vt_rawf = 0; /* start out cooked */
- vt_esc = ENONE; /* no escape sequence yet */
-
- vt_row = vt_col = 0; /* start out at home */
- HOP; /* actually move there */
- CURSOR; /* put up a cursor (XOR) */
-
- return;
- }
-
- /* scroll the whole screen up one line-height */
- vt_scroll()
- {
- RgnHandle scroll_rgn;
-
- scroll_rgn = NewRgn();
- ScrollRect(&vt_rect, 0, -vt_line, scroll_rgn);
- DisposeRgn(scroll_rgn);
-
- return;
- }
-
- /* put a character on the screen. Set state flags so we can process sequences
- * of characters (to do escape sequences.)
- * Assume we are sitting on the cursor and vt_pen is valid
- */
- vt_putch(c)
- int c;
- {
- if(vt_esc > ENONE) { /* we are in the middle of an escape sequence */
- escape_code(c);
- } else if(c >= ' ' && c <= '~') { /* it is printable */
- printable_code(c);
- } else { /* it is control */
- control_code(c);
- }
-
- return;
- }
-
- /* it is a simple character */
- printable_code(c)
- int c;
- {
- BLANK(1, 1); /* take away the cursor */
- DrawChar(c); /* paint in the character */
- if(++vt_col >= MAXCOL) { /* time to auto-linefeed */
- vt_col = 0;
- if(++vt_row >= MAXROW) {/* scroll it */
- vt_row = MAXROW - 1; /* not so far please... */
- vt_scroll();
- }
- }
- HOP; /* move to the cell */
- CURSOR; /* paint in the cursor */
-
- return;
- }
-
- /* process a control code - all are exactly 1 character in length */
- control_code(c)
- int c;
- {
- int i, j;
-
- switch(c) {
- case '\007': /* bell */
- SysBeep(20); /* beep for 20/60 seconds */
- break;
- case '\010': /* backspace */
- if(vt_col > 0) { /* can't backspace past left margin */
- CURSOR; /* exor it again to remove */
- vt_col--;
- HOP; /* jump back one */
- CURSOR; /* and a new cursor */
- } else {
- SysBeep(20); /* be a pain */
- }
- break;
- case '\011': /* tab */
- j = vt_col; /* stack this for re-entrancy */
- for(i = 0; i < (TABVAL - (j % TABVAL)); i++) {
- vt_putch(' '); /* just do some spaces */
- }
- break;
- case '\012': /* line feed */
- CURSOR; /* kill the old cursor */
- if(!vt_rawf) { /* do both cr and lf */
- vt_col = 0; /* the cr part is easy */
- }
- if(++vt_row >= MAXROW) {/* scroll it */
- vt_row = MAXROW - 1; /* not so far please... */
- vt_scroll();
- }
- HOP;
- CURSOR;
- break;
- case '\015': /* carriage return */
- CURSOR; /* kill the old cursor */
- vt_col = 0; /* the cr part is easy */
- if(!vt_rawf) { /* do both cr and lf */
- if(++vt_row >= MAXROW) {/* scroll it */
- vt_row = MAXROW - 1; /* not so far please... */
- vt_scroll();
- }
- }
- HOP;
- CURSOR;
- break;
- case '\033': /* escape */
- vt_esc = EFIRST; /* ok - start an escape sequence */
- break;
- /* the ones we don't handle come next */
- case '\013': /* vertical tab */
- case '\014': /* form feed */
- default:
- break;
- } /* end of switch */
-
- return;
- }
-
- escape_code(c)
- int c;
- {
- switch(vt_esc) {
- case EFIRST:
- switch(c) {
- case '[':
- vt_esc = EBRACKET; /* remember we saw it */
- vt_inprog = vt_x = vt_y = 0; /* clear these */
- break;
-
- default:
- vt_esc = ENONE; /* blew it */
- break;
- }
- break;
-
- case EBRACKET:
- switch(c) {
- case 'H': /* home (or motion) */
- vt_x = vt_inprog; /* column number */
- vt_inprog = 0;
- /* adjust for brain damaged notion of screen starting at (1,1) */
- if(--vt_x < 0) {
- vt_x = 0;
- }
- if(--vt_y < 0) {
- vt_y = 0;
- }
- CURSOR; /* take it away */
- vt_row = vt_y;
- vt_col = vt_x;
- HOP;
- CURSOR;
- vt_esc = ENONE; /* code is complete */
- break;
-
- case 'J': /* clear to end of screen */
- if(vt_row + 1 < MAXROW) { /* something below us */
- SAVE;
- vt_row++; /* drop down a row */
- vt_col = 0; /* and over to the beginning */
- HOP; /* actually move there */
- BLANK(MAXCOL, MAXROW - vt_row);
- RESTORE;
- HOP;
- }
- /* fall through to clear the fractional part */
- case 'K': /* clear to end of line */
- BLANK(MAXCOL - vt_col, 1); /* erase all on the row */
- CURSOR; /* replace the cursor */
- vt_esc = ENONE;
- break;
-
- case '0': /* a row or column number */
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- vt_inprog *= 10; /* make room */
- vt_inprog += c - '0'; /* add in the new digit */
- break;
-
- case ';': /* end of row number */
- vt_y = vt_inprog;
- vt_inprog = 0;
- break;
-
- default:
- vt_esc = ENONE; /* blew it */
- break;
- }
- break;
-
- default:
- vt_esc = ENONE; /* blew it */
- break;
- }
-
- return;
- }
-
- /* low level reader - call this directly if you also want to use peek - that
- * way you get raw-mode and avoid all character buffers. But note - this code
- * does NOT initialize the world first. You must call vt_open() manually!
- */
- vt_getch()
- {
- EventRecord x;
- int rc;
-
- SystemTask();
-
- /* GetNextEvent returns a boolean enum - make it an integer */
- while(GetNextEvent(autoKeyMask | keyDownMask, &x) == false) {
- SystemTask(); /* wait for it */
- }
- rc = x.message & 0xff;
- if(x.modifiers & cmdKey) { /* it is a control character */
- rc &= 0x1f; /* so fold it */
- }
- if(!vt_rawf && rc == '\004') { /* cooked mode and a ^D spells EOF */
- return(EOF);
- }
- return(rc);
- }
-
- /* return a character but don't pull it off the queue! Don't block if nothing is
- * available - just return a null.
- */
- vt_peekch()
- {
- EventRecord x;
- int rc;
-
- SystemTask();
-
- /* EventAvail returns a boolean enum - make it an integer */
- if(EventAvail(autoKeyMask | keyDownMask, &x) == true) { /* something */
- rc = x.message & 0xff;
- if(x.modifiers & cmdKey) { /* it is a control character */
- rc &= 0x1f; /* so fold it */
- }
- if(!vt_rawf && rc == '\004') { /* cooked mode and a ^D spells EOF */
- return(EOF);
- } else { /* normal character */
- return(rc);
- }
- /*NOTREACHED*/
- } else {
- return(0); /* nothing - call it a null */
- }
- /*NOTREACHED*/
- }
-
- /* handle system requests for open, rename, and delete. Only open is legal.
- */
- vt_faccess(fname, mode, perm)
- char *fname;
- int mode;
- int perm;
- {
- /* if we are not the correct driver, return and let the system try
- * another driver.
- */
- if(EqualString(fname, "dev:console", false, true) == false) {
- return(-1);
- }
-
- /* this driver only handles opens */
- if(mode == F_OPEN) {
- if(firsttime == 0) { /* only do it once */
- vt_open();
- firsttime++;
- }
- return(0); /* but always claim success */
- }
-
- /* tell them the request is bogus */
- return(0x40000016);
- }
-
- /* not much to do */
- vt_close()
- {
- return(0);
- }
-
- /* return as many characters as asked for, up to a carriage return. Someday
- * we need to add raw mode here.
- */
- vt_read(ap)
- IOSTR *ap;
- {
- /* fill the buffer if it is empty */
- if(linesize <= 0) {
- linesize = vt_readline(linebuf, 256);
- lineptr = linebuf;
- }
-
- /* copy till either user is satisfied or we hit the end of the line.
- * We must leave the count field set in the ap structure to show how
- * much more is to be done.
- */
- for( ; (ap->count > 0) && (linesize > 0); ap->count--, linesize--) {
- *(ap->buffer)++ = *lineptr++;
- }
-
- return(0);
- }
-
- /* read until a carriage return (or we fill the buffer). Return how much we
- * got.
- */
- vt_readline(bp, size)
- char *bp;
- int size;
- {
- int i;
-
- for(i = size; i; bp++, i--) {
- *bp = vt_getch();
-
- if(*bp == '\010' && i < size) {
- bp -= 2;
- i += 2;
- }
-
- if(*bp == '\004' || *bp == '\n') {
- i--;
- break;
- }
- }
-
- return(size - i);
- }
-
- /* loop characters to the screen */
- vt_write(ap)
- IOSTR *ap;
- {
- for( ; ap->count; ap->count--) { /* must leave count at 0 on exit */
- vt_putch(*(ap->buffer)++);
- }
-
- return(0);
- }
-
- /* this routine turns out to be essential because the system intends to ask us
- * for the optimum buffer size. We return -1 to tell it to choose for us.
- */
- vt_ioctl(fd, op, arg)
- int fd;
- int op;
- int *arg;
- {
- switch(op) {
- case FIOINTERACTIVE:
- return(0);
-
- case TIOFLUSH:
- return(0);
-
- /* I don't trust this! We would have to clear screen and reset
- * point sizes for it to be safe.
- */
- case TIOSPORT:
- /* vt = (WindowPtr) *arg; */
- return(0);
-
- case TIOGPORT:
- *arg = (int) vt;
- return(0);
-
- default:
- return(-1);
- }
- /*NOTREACHED*/
- }
-
- /* this stuff should be via ioctl but why make it so hard? */
- vt_raw()
- {
- vt_rawf = 1;
-
- return;
- }
-
- vt_cooked()
- {
- vt_rawf = 0;
-
- return;
- }
-
- /* this code is a reverse compile of the cruntime.o module. */
- /*
- write(fd, buf, cnt)
- int fd;
- char *buf;
- int cnt;
- {
- IOSTR *ind;
- int foo;
-
- if(fd < 0) {
- _uerror(0x16, 0);
- return(-1);
- }
-
- ind = _getIOPort(&fd);
-
- if(!ind) {
- return(-1);
- }
-
- if(!(ind->flags & 2)) {
- _uerror(0x09, 0);
- return(-1);
- }
-
- ind->count = cnt;
- ind->buffer = buf;
- foo = (*(ind->handler->l_write))(ind);
-
- if(foo) {
- _uerror(foo, ind->errcode);
- return(-1);
- } else {
- return(cnt - (ind->count));
- }
- }
- */
-
- /* this bypasses printf and is useful for debugging to avoid recursion.
- * Prints a string.
- */
- /*
- lpr(s)
- char *s;
- {
- while(*s != 0) {
- vt_putch(*s++);
- }
-
- return;
- }
- */
-
- /* this one prints an integer in hex with leading zeros. */
- /*
- lpx(x)
- unsigned int x;
- {
- int i, j;
-
- for(i = 0; i < 8; i++) {
- j = (x >> (28 - (i * 4))) & 0xf;
- if(j <= 9) {
- vt_putch(j + '0');
- } else {
- vt_putch(j - 10 + 'a');
- }
- }
-
- return;
- }
- */
-
- /* Start of code for da support. We use these for a gp text editor among
- * other things. Note - there is no way to pass the file name in. Oh
- * well. Most of this code is stolen from the sample application which
- * Apple distributes with MPW and hence is copyright Apple Computer Corp.
- */
-
- /*
- * Resource ID constants.
- */
- # define appleID 128
- # define fileID 129
- # define editID 130
-
- # define appleMenu 0
-
- # define fileMenu 1
- # define quitCommand 1
-
- # define editMenu 2
- # define undoCommand 1
- # define cutCommand 3
- # define copyCommand 4
- # define pasteCommand 5
- # define clearCommand 6
-
- # define menuCount 3
-
- /*
- * HIWORD and LOWORD macros, for readability.
- */
- # define HIWORD(aLong) (((aLong) >> 16) & 0xFFFF)
- # define LOWORD(aLong) ((aLong) & 0xFFFF)
-
- /*
- * Global Data objects, used by routines external to main().
- */
- MenuHandle MyMenus[menuCount]; /* The menu handles */
- int Doneflag; /* Becomes TRUE when File/Quit chosen */
-
- da_mode(fname)
- char *fname;
- {
- EventRecord myEvent;
- WindowPtr theActiveWindow, whichWindow;
- GrafPtr savePort;
- char forcedDA[256];
-
- setupMenus();
-
- for (Doneflag = 0; !Doneflag; ) {
- /*
- * Main Event tasks:
- */
- SystemTask();
-
- /* if a name was passed in, assume it is a DA - we fake an open
- * of it. DA's are funny in that the name contains a leading
- * NULL character. So we stick one in 'cause they are hard to
- * type. This results in a bizzare C string but the Desk Manager
- * is happy...
- */
- if(fname[0] != '\0') {
- forcedDA[0] = '\0'; /* DA names (usually) have this */
- strcpy(forcedDA + 1, fname); /* don't step on the null */
- fname[0] = '\0'; /* open it only once */
- GetPort(&savePort);
- (void) OpenDeskAcc(forcedDA);
- SetPort(savePort);
- continue;
- }
-
- theActiveWindow = FrontWindow(); /* Used often, avoid repeated calls */
- /*
- * Handle the next event.
- */
- if ( ! GetNextEvent(everyEvent, &myEvent)) {
- continue; /* not for us */
- }
- /*
- * In the unlikely case that the active desk accessory does not
- * handle mouseDown, keyDown, or other events, GetNextEvent() will
- * give them to us! So before we perform actions on some events,
- * we check to see that the affected window in question is really
- * our window.
- */
- switch (myEvent.what) {
- case mouseDown:
- switch (FindWindow(&myEvent.where, &whichWindow)) {
- case inSysWindow:
- SystemClick(&myEvent, whichWindow);
- break;
- case inMenuBar:
- doCommand(MenuSelect(&myEvent.where));
- break;
- case inDrag:
- case inGrow:
- /* No such - Fall through */
- case inContent:
- if (whichWindow != theActiveWindow) {
- SelectWindow(whichWindow);
- }
- break;
- default:
- break;
- }/*endsw FindWindow*/
- break;
-
- case keyDown:
- case autoKey:
- if (vt == theActiveWindow) {
- if (myEvent.modifiers & cmdKey) {
- doCommand(MenuKey(myEvent.message & charCodeMask));
- }
- }
- break;
-
- case activateEvt:
- if ((WindowPtr) myEvent.message == vt) {
- if (myEvent.modifiers & activeFlag) {
- DisableItem(MyMenus[editMenu], 0);
- } else {
- EnableItem(MyMenus[editMenu], 0);
- }
- DrawMenuBar();
- }
- break;
-
- default:
- break;
-
- }/*endsw myEvent.what*/
-
- }/*endfor Main Event loop*/
-
- trashMenus();
- return;
- }
-
- setupMenus()
- {
- register MenuHandle *pMenu;
- /*
- * Set up the desk accessories menu.
- * We install the desk accessory names from the 'DRVR' resources.
- */
- MyMenus[appleMenu] = GetMenu(appleID);
- AddResMenu(MyMenus[appleMenu], (ResType) 'DRVR');
- /*
- * Now the File and Edit menus.
- */
- MyMenus[fileMenu] = GetMenu(fileID);
- MyMenus[editMenu] = GetMenu(editID);
- /*
- * Now insert all of the application menus in the menu bar.
- *
- * "Real" C programmers never use array indexes
- * unless they're constants :-)
- */
- for (pMenu = &MyMenus[0]; pMenu < &MyMenus[menuCount]; ++pMenu) {
- InsertMenu(*pMenu, 0);
- }
- DisableItem(MyMenus[editMenu], 0);
-
- DrawMenuBar();
-
- return;
- }
-
- trashMenus()
- {
- ClearMenuBar(); /* remove menus */
- DrawMenuBar(); /* show it */
-
- ReleaseResource(MyMenus[appleMenu]);
- ReleaseResource(MyMenus[fileMenu]);
- ReleaseResource(MyMenus[editMenu]);
-
- return;
- }
-
- /*
- * Process mouse clicks in menu bar
- */
- doCommand(mResult)
- long mResult;
- {
- int theMenu, theItem;
- char daName[256];
- GrafPtr savePort;
-
- theItem = LOWORD(mResult);
- theMenu = HIWORD(mResult); /* This is the resource ID */
-
- switch (theMenu) {
- case appleID:
- GetItem(MyMenus[appleMenu], theItem, daName);
- GetPort(&savePort);
- (void) OpenDeskAcc(daName);
- SetPort(savePort);
- break;
-
- case fileID:
- switch (theItem) {
- case quitCommand:
- Doneflag++; /* Request exit */
- break;
- default:
- break;
- }
- break;
- case editID:
- /*
- * If this is for a 'standard' edit item,
- * run it through SystemEdit.
- */
- if (theItem <= clearCommand) {
- SystemEdit(theItem-1);
- }
- break;
-
- default:
- break;
-
- }/*endsw theMenu*/
-
- HiliteMenu(0);
-
- return;
- }
-