mic@ut-ngp.UUCP (Mic Kaczmarczik) (04/23/86)
[.... If I had a hammer, I'd hammer on the line eater bug ....] Greetings, This file, and two more, comprise an Amiga terminal driver for the version of MicroEmacs that was posted to the net last week. I compiled it with Manx Aztec C, but it should not be that hard to move to Lattice, since Aztec seems to have more restrictions (16-bit ints, no structure assignment). There are two versions of the terminal driver: one that uses AmigaDOS for vanilla terminal I/O, and one that uses Intuition for terminal I/O, mouse clicks, and menu selections. The function keys work with both drivers; using the Intuition driver, clicking the mouse in a window moves dot to that position, just as if you'd moved to that window and typed ^F and ^N to get there. Enjoy! Mic Kaczmarczik ...!ihnp4!seismo!ut-sally!ut-ngp!mic cc.kaczmarczik@a20.utexas.edu -----------------------------CUT HERE---------------------------------------- -h- Readme Wed Apr 23 08:52:51 1986 Readme This distribution is in three files, in portable archive format: 1) System routines (subdirectory sys/amiga). Abort.c is there because the Aztec library doesn't provide it. 2) The AmigaDOS terminal interface (subdirectory tty/amigados). It basically opens a RAW: window and reads characters one at a time from the window. 3) The Intuition terminal interface (tty/intuition). This one demonstrates the use of the console device with an Intuition window, and translates non-keyboard events into key sequences that the rest of the driver code can later parse. To get this going, you need the MicroEmacs distribution from mod.sources. Unpack this file and one of the terminal drivers into the main directory, compile and link. Happy hacking, Mic Kaczmarczik UUCP: ...!ihnp4!seismo!ut-sally!ut-ngp!mic ARPA: mic@a20.cc.utexas.edu Mail: 313B W. 35th Austin, TX 75752 -h- abort.c Wed Apr 23 08:52:51 1986 abort.c /* * Name: MicroEmacs * Amiga dummy abort function * Version: 31 * Created: 18-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic */ extern int ttclose(); abort() { ttclose(); exit(1000); } -h- fileio.c Wed Apr 23 08:52:51 1986 fileio.c /* * Name: MicroEMACS * Version: 31 * Commodore Amiga file I/O. * Last edit: 15-Apr-86 * By: Mic Kaczmarczik * ...ihnp4!seismo!ut-sally!ut-ngp!mic * * Read and write ASCII files. All * of the low level file I/O knowledge is here. * Pretty much vanilla standard I/O. */ #include "def.h" static FILE *ffp; /* * Open a file for reading. */ ffropen(fn) char *fn; { if ((ffp=fopen(fn, "r")) == NULL) return (FIOFNF); return (FIOSUC); } /* * Open a file for writing. * Return TRUE if all is well, and * FALSE on error (cannot create). */ ffwopen(fn) char *fn; { register int fd; FILE *fdopen(); if ((fd=creat(fn, 0)) < 0 || (ffp=fdopen(fd, "w")) == NULL) { eprintf("Cannot open file for writing"); return (FIOERR); } return (FIOSUC); } /* * Close a file. * Should look at the status. */ ffclose() { fclose(ffp); return (FIOSUC); } /* * Write a line to the already * opened file. The "buf" points to the * buffer, and the "nbuf" is its length, less * the free newline. Return the status. * Check only at the newline. */ ffputline(buf, nbuf) register char buf[]; { register int i; for (i=0; i<nbuf; ++i) putc(buf[i]&0xFF, ffp); putc('\n', ffp); if (ferror(ffp) != FALSE) { eprintf("Write I/O error"); return (FIOERR); } return (FIOSUC); } /* * Read a line from a file, and store the bytes * in the supplied buffer. Stop on end of file or end of * line. Don't get upset by files that don't have an end of * line on the last line; this seem to be common on CP/M-86 and * MS-DOS (the suspected culprit is VAX/VMS kermit, but this * has not been confirmed. If this is sufficiently researched * it may be possible to pull this kludge). Delete any CR * followed by an LF. */ ffgetline(buf, nbuf) register char buf[]; { register int c; register int i; i = 0; for (;;) { c = getc(ffp); if (c == '\r') { /* Delete any non-stray */ c = getc(ffp); /* carriage returns. */ if (c != '\n') { if (i >= nbuf-1) { eprintf("File has long line"); return (FIOERR); } buf[i++] = '\r'; } } if (c==EOF || c=='\n') /* End of line. */ break; if (i >= nbuf-1) { eprintf("File has long line"); return (FIOERR); } buf[i++] = c; } if (c == EOF) { /* End of file. */ if (ferror(ffp) != FALSE) { eprintf("File read error"); return (FIOERR); } if (i == 0) /* Don't get upset if */ return (FIOEOF); /* no newline at EOF. */ } buf[i] = 0; return (FIOSUC); } /* * Make a file name for a backup copy. */ fbackupfile(fname) char *fname; { strcat(fname,"~"); return (TRUE); } /* * The string "fn" is a file name. * Perform any required case adjustments. All sustems * we deal with so far have case insensitive file systems. * We zap everything to lower case. The problem we are trying * to solve is getting 2 buffers holding the same file if * you visit one of them with the "caps lock" key down. * On UNIX file names are dual case, so we leave * everything alone. */ adjustcase(fn) register char *fn; { register int c; while ((c = *fn) != 0) { if (c>='A' && c<='Z') *fn = c + 'a' - 'A'; ++fn; } } -h- sleep.c Wed Apr 23 08:52:51 1986 sleep.c /* * Name: MicroEmacs * AmigaDOS sleep function * Version: 31 * Last Edit: 18-Apr-86 * Created: 18-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic */ /* There are really 60 ticks/second, but I don't want to wait that */ /* long when matching parentheses... */ #define TICKS 45 extern long Delay(); sleep(n) int n; { if (n > 0) Delay((long) n * TICKS); } -h- spawn.c Wed Apr 23 08:52:51 1986 spawn.c /* * Name: MicroEMACS * Spawn an AmigaDOS subprocess * Version: 31 * Last edit: 17-Apr-86 * By: ...!ihnp4!seismo!ut-sally!ut-ngp!mic */ #include "def.h" #include <libraries/dos.h> #include <libraries/dosextens.h> /* * Create a subjob with a copy * of the command intrepreter in it. Since the command * interpreter doesn't run in the same window, there's no * need for a refresh. Ah, the beauties of windowing system... */ spawncli(f, n, k) { struct FileHandle *newcli, *Open(); eprintf("[Starting new CLI]"); newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess", MODE_NEWFILE); if (newcli == (struct FileHandle *) 0) { eprintf("Can't create new CLI window"); return (FALSE); } Execute("", newcli, 0L); Close(newcli); return (TRUE); }
mic@ut-ngp.UUCP (Mic Kaczmarczik) (04/23/86)
This is the file 2 of 3 of the Amiga code for MicroEmacs. It contains the AmigaDOS terminal driver. Mic Kaczmarczik ...!ihnp4!seismo!ut-sally!ut-ngp!mic -------------------------------amigados.ar------------------------------------ -h- tty.c Wed Apr 23 08:53:26 1986 tty.c /* * Name: MicroEMACS * Amiga console device virtual terminal display * Version: 31 * Last Edit: 21-Apr-86 * Created: 19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Modified: 21-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * In ttresize() use ttmove(HUGE,HUGE) and cursor * position report to get the new window size */ #include "def.h" #define BEL 0x07 /* BEL character. */ #define ESC 0x1B /* ESC character. */ extern int ttrow; extern int ttcol; extern int tttop; extern int ttbot; extern int tthue; int tceeol = 3; /* Costs, ANSI display. */ int tcinsl = 17; int tcdell = 16; /* * Initialize the terminal when the editor * gets started up. This is a no-op on the Amiga. */ ttinit() { } /* * Clean up the terminal, in anticipation of * a return to the command interpreter. This is a no-op * on the Amiga. */ tttidy() { } /* * Move the cursor to the specified * origin 0 row and column position. Try to * optimize out extra moves; redisplay may * have left the cursor in the right * location last time! */ ttmove(row, col) { if (ttrow!=row || ttcol!=col) { ttputc(ESC); ttputc('['); asciiparm(row+1); ttputc(';'); asciiparm(col+1); ttputc('H'); ttrow = row; ttcol = col; } } /* * Erase to end of line. */ tteeol() { ttputc(ESC); ttputc('['); ttputc('K'); } /* * Erase to end of page. */ tteeop() { ttputc(ESC); ttputc('['); ttputc('J'); } /* * Make a noise. */ ttbeep() { ttputc(BEL); ttflush(); } /* * Convert a number to decimal * ascii, and write it out. Used to * deal with numeric arguments. */ asciiparm(n) register int n; { register int q; q = n/10; if (q != 0) asciiparm(q); ttputc((n%10) + '0'); } /* * Insert a block of blank lines onto the * screen, using a scrolling region that starts at row * "row" and extends down to row "bot". Deal with the one * line case, which is a little bit special, with special * case code. * * Since we don't really have a scrolling region, * delete the block of lines that would have been deleted if * we'd had one, then insert blank lines to move the rest * of the screen back to where it belongs. This idea from * the Heath driver. */ ttinsl(row, bot, nchunk) { register int i; if (row == bot) { /* Funny case. */ if (nchunk != 1) abort(); ttmove(row, 0); tteeol(); return; } ttmove(1+bot-nchunk, 0); if (nchunk > 0) { /* Delete a chunk of lines */ ttputc(ESC); /* nchunk in size. Rest of */ ttputc('['); /* screen moves up. */ asciiparm(nchunk); ttputc('M'); } ttmove(row, 0); if (nchunk > 0) { /* Insert a chunk nchunk in size*/ ttputc(ESC); /* before current line, sliding */ ttputc('['); /* rest of screen down. */ asciiparm(nchunk); ttputc('L'); } ttrow = row; /* End up on current line */ ttcol = 0; } /* * Delete a block of lines, with the uppermost * line at row "row", in a screen slice that extends to * row "bot". The "nchunk" is the number of lines that have * to be deleted. This is done by deleting nchunk lines at * the appropriate spot, then inserting nchunk lines to make * up for the empty space at the bottom of the virtual scrolling * region. */ ttdell(row, bot, nchunk) { register int i; if (row == bot) { /* One line special case */ ttmove(row, 0); tteeol(); return; } if (nchunk > 0) { ttmove(row, 0); ttputc(ESC); ttputc('['); asciiparm(nchunk); ttputc('M'); } ttmove(1+bot-nchunk,0); if (nchunk > 0) { ttputc(ESC); /* For all lines in chunk */ ttputc('['); /* INS line before bottom */ asciiparm(nchunk); /* Bottom of window (and rest */ ttputc('L'); /* of screen) moves down */ } ttrow = HUGE; /* Force ttmove() to do the move*/ ttcol = HUGE; ttmove(bot-nchunk,0); } /* * No-op. */ ttwindow(top,bot) { } /* * No-op. */ ttnowindow() { } /* * Set the current writing color to the * specified color. Watch for color changes that are * not going to do anything (the color is already right) * and don't send anything to the display. */ ttcolor(color) register int color; { if (color != tthue) { if (color == CTEXT) { /* Normal video. */ ttputc(ESC); ttputc('['); ttputc('m'); } else if (color == CMODE) { /* Reverse video. */ ttputc(ESC); ttputc('['); ttputc('7'); ttputc('m'); } tthue = color; /* Save the color. */ } } /* * This routine is called by the * "refresh the screen" command to try and resize * the display. The new size, which must be deadstopped * to not exceed the NROW and NCOL limits, is stored * back into "nrow" and "ncol". Display can always deal * with a screen NROW by NCOL. Look in "window.c" to * see how the caller deals with a change. */ #define CSI 0x9B ttresize() { register int newnrow, newncol; ttmove(HUGE,HUGE); /* Go to bottom right */ ttputc(CSI); /* See where cursor went to */ ttputc('6'); ttputc('n'); ttflush(); if (ttgetc() != CSI) /* parse the report */ returnf; if ((newnrow = readparm(';')) == -1) return; if ((newncol = readparm('R')) == -1) return; if (newnrow < 1) /* check limits. */ newnrow = 1; else if (newnrow > NROW) newnrow = NROW; if (newncol < 1) newncol = 1; else if (newncol > NCOL) newncol = NCOL; nrow = newnrow; ncol = newncol; } /* * Read an unsigned number from the terminal */ static readparm(delim) char delim; { register int parm; register int c; parm = 0; while ((c=ttgetc()) >= '0' && c <= '9') parm = 10 * parm + c - '0'; return (c == delim) ? parm : -1; } -h- ttydef.h Wed Apr 23 08:53:26 1986 ttydef.h /* * Name: MicroEMACS * Amiga console device virtual terminal header file * Version: 31 * Last edit: 20-Apr-86 * Created: 20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * */ #define GOSLING 1 /* Compile in fancy display. */ #define MEMMAP 0 /* Not memory mapped video. */ #define NROW 23 /* Rows. */ #define NCOL 77 /* Columns (AmigaDOS) */ /* * Special keys for the default Amiga console device keymap. * Function key codes are in the form <CSI>v~ * where v is a 1 or 2-digit code between 0 and 19, * so they comprise the first 20 entries in the key * table. The next 12 entries are for the help and * arrow keys. There is no shifted value for the * HELP key. */ #define KF1 K01 #define KF2 K02 #define KF3 K03 #define KF4 K04 #define KF5 K05 #define KF6 K06 #define KF7 K07 #define KF8 K08 #define KF9 K09 #define KF10 K0A #define KSF1 K0B #define KSF2 K0C #define KSF3 K0D #define KSF4 K0E #define KSF5 K0F #define KSF6 K10 #define KSF7 K11 #define KSF8 K12 #define KSF9 K13 #define KSF10 K14 #define KUP K15 #define KSUP K16 #define KDOWN K17 #define KSDOWN K18 #define KLEFT K19 #define KSLEFT K1A #define KRIGHT K1B #define KSRIGHT K1C #define KHELP K1D -h- ttyio.c Wed Apr 23 08:53:26 1986 ttyio.c /* * Name: MicroEMACS * AmigaDOS terminal I/O * Version: 31 * Compiler: Manx Aztec C * Last edit: 19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Created: 19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic */ #include "def.h" #include "sysdef.h" #include <libraries/dos.h> #include <libraries/dosextens.h> #define NIBUF 128 /* Probably excessive. */ #define NOBUF 512 /* Not too big for 750/730. */ struct FileHandle *tty; struct FileHandle *Open(); char obuf[NOBUF]; /* Output buffer */ int nobuf; /* # of bytes in above */ char ibuf[NIBUF]; /* Input buffer */ int nibuf; /* # of bytes in above */ int nrow; /* Terminal size, rows. */ int ncol; /* Terminal size, columns. */ #if MANX extern int Enable_Abort; #endif extern char *version[]; /* Defined in "version.c" */ /* * This routine gets called once, to set up the * terminal channel. */ ttopen() { char WindowName[80]; nrow = 23; ncol = 77; nobuf = nibuf = 0; #if MANX Enable_Abort = 0; /* Disable ^C during file I/O */ #endif strcpy(WindowName,"RAW:1/1/639/199/"); strcat(WindowName,version[0]); tty = Open(WindowName,MODE_NEWFILE); if (tty == (struct FileHandle *) 0) { printf("Can't open Emacs window!\n"); exit(200); } } /* * This function gets called just * before we go back home to the command interpreter. * On the Amiga it closes up the virtual terminal window. */ ttclose() { if (tty != (struct FileHandle *) 0L) { ttflush(); Close(tty); } tty = (struct FileHandle *) 0L; Enable_Abort = 1; } /* * Write a character to the display. * On the Amiga, terminal output is buffered, and * we just put the characters in the big array, * after cheching for overflow. */ ttputc(c) { if (nobuf >= NOBUF) ttflush(); obuf[nobuf++] = c; } /* * This function does the real work of * flushing out buffered I/O on the Amiga. All * we do is blast out the block with a write call. */ ttflush() { if (nobuf > 0) { Write(tty,obuf,(long) nobuf); nobuf = 0; } } /* * Read a character from the terminal, * performing no editing and doing no echo at all. */ ttgetc() { unsigned char c; /* must be unsigned! */ Read(tty,&c,1L); return ((int) c); } -h- ttykbd.c Wed Apr 23 08:53:26 1986 ttykbd.c /* * Name: MicroEMACS * Amiga virtual terminal keyboard, default console keymap. * Version: 31 * Last edit: 20-Apr-86 * Created: 19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Supports all the default console device * (RAW:) input codes for the Amiga keyboard. * Modified: 20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * [UT-14A] Added processing for GR_to_META, so * characters in the alternate character set * can automatically map to META characters. * This is a rather nice thing to have... */ #include "def.h" #define ESC 0x1B /* Escape, arrows et al. */ #define CSI 0x9B /* Amiga CSI */ /* * The function keys on the Amiga send back * escape sequences of the form <ESC>[code~, where code * is a one or two-character code for the key. To make * it easier to decode, we place the internal key values * for these codes in this table. */ short consolemap[] = { KF1, KF2, KF3, KF4, KF5, KF6, KF7, KF8, KF9, KF10, KSF1, KSF2, KSF3, KSF4, KSF5, KSF6, KSF7, KSF8, KSF9, KSF10 }; #define NFUNCKEYS ((sizeof consolemap)/(sizeof consolemap[0])) /* * Names for the keys with basic keycode * between KFIRST and KLAST (inclusive). This is used by * the key name routine in "kbd.c". KFIRST is KRANDOM, which * we don't bind anything useful to. */ char *keystrings[] = { NULL, "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "Shift-F1", "Shift-F2", "Shift-F3", "Shift-F4", "Shift-F5", "Shift-F6", "Shift-F7", "Shift-F8", "Shift-F9", "Shift-F10", "Up", "Shift-Up", "Down", "Shift-Down", "Left", "Shift-Left", "Right", "Shift-Right", "Help", NULL, NULL }; /* * Read in a key, doing the low level mapping * of ASCII code to 11 bit code. This level deals with * mapping the special keys into their spots in the C1 * control area. The C0 controls go right through, and * get remapped by "getkey". * * If GR_to_META is TRUE, characters in the Amiga alternate * character set get mapped to META characters. If FALSE, * they get sent straight through. */ extern int GR_to_META; /* defined in "kbd.c" */ getkbd() { register int c; register int n; loop: c = ttgetc(); if (c == CSI) { c = ttgetc(); /* Unshifted arrow keys */ if (c == 'A') return (KUP); if (c == 'B') return (KDOWN); if (c == 'C') return (KRIGHT); if (c == 'D') return (KLEFT); if (c == 'T') return (KSUP); if (c == 'S') return (KSDOWN); if (c == '?') { ttgetc(); /* discard '~' */ return (KHELP); } /* Shifted left, right */ if (c == ' ') { c = ttgetc(); if (c == 'A' || c == '@') return (c == 'A') ? (KSLEFT) : (KSRIGHT); goto loop; /* try again, sucker */ } /* Function keys */ if (c >= '0' && c <= '9') { n = 0; do { n = 10*n + c - '0'; c = ttgetc(); } while (c>='0' && c<='9'); if (c == '~' && n < NFUNCKEYS) { c = consolemap[n]; if (c != KRANDOM) return (c); goto loop; } else goto loop; /* Try again */ } goto loop; /* Try again */ } /* Meta keys -- also check for high bit set */ if ((c == ESC) || (GR_to_META && (c & 0x80))) { if (c == ESC) c = ttgetc(); c &= 0x7F; /* Strip high bit */ if (ISLOWER(c) != FALSE) /* Copy the standard */ c = TOUPPER(c); /* META code. */ if (c>=0x00 && c<=0x1F) c = KCTRL | (c+'@'); return (KMETA | c); } return (c); } /* * Terminal specific keymap initialization. * Attach the special keys to the appropriate built * in functions. Bind all of the assigned graphics in the * Amiga alternate character set to self-insert. * As is the case of all the keymap routines, errors * are very fatal. */ ttykeymapinit() { register SYMBOL *sp; register int i; keydup(KUP, "back-line"); keydup(KDOWN, "forw-line"); keydup(KRIGHT, "forw-char"); keydup(KLEFT, "back-char"); keydup(KHELP, "help"); keydup(KSUP, "goto-bob"); keydup(KSDOWN, "goto-eob"); keydup(KSRIGHT, "goto-eop"); keydup(KSLEFT, "goto-bop"); keydup(KF1, "down-window"); keydup(KF2, "forw-page"); keydup(KF3, "enlarge-window"); keydup(KF4, "forw-window"); keydup(KF5, "split-window"); keydup(KF6, "file-visit"); keydup(KF7, "file-save"); keydup(KF8, "start-macro"); keydup(KF9, "bind-to-key"); keydup(KF10, "display-version"); keydup(KSF1, "up-window"); keydup(KSF2, "back-page"); keydup(KSF3, "shrink-window"); keydup(KSF4, "back-window"); keydup(KSF5, "only-window"); keydup(KSF6, "file-read"); keydup(KSF7, "file-write"); keydup(KSF8, "end-macro"); keydup(KSF9, "display-bindings"); keydup(KSF10, "quit"); /* * Bind all positions that correspond * to characters in the Amiga alternate * character set to "ins-self". These characters may * be used just like any other character. */ if ((sp=symlookup("ins-self")) == NULL) abort(); for (i=0xA0; i<0xFF; ++i) { if (binding[i] != NULL) abort(); binding[i] = sp; ++sp->s_nkey; } }
mic@ut-ngp.UUCP (Mic Kaczmarczik) (04/23/86)
This file is part 3 of 3 the MicroEmacs Amiga code. It contains the Intuition version of the terminal driver. Mic Kaczmarczik ...!ihnp4!seismo!ut-sally!ut-ngp!mic mic@a20.cc.utexas.edu ---------------------------------CUT HERE------------------------------------- -h- console.c Wed Apr 23 08:53:04 1986 console.c /* * These functions are taken directly from the * console.device chapter in the Amiga V1.1 * ROM Kernel Manual. */ #include <exec/types.h> #include <exec/io.h> #include <devices/console.h> #include <libraries/dos.h> #include <intuition/intuition.h> extern LONG OpenDevice(); extern LONG DoIO(); extern LONG SendIO(); /* * Open a console device, given a read request * and a write request message. */ int OpenConsole(writerequest,readrequest,window) struct IOStdReq *writerequest; struct IOStdReq *readrequest; struct Window *window; { LONG error; writerequest->io_Data = (APTR) window; writerequest->io_Length = (ULONG) sizeof(*window); error = OpenDevice("console.device", 0L, writerequest, 0L); /* clone required parts of the request */ readrequest->io_Device = writerequest->io_Device; readrequest->io_Unit = writerequest->io_Unit; return((int) error); } /* * Output a single character * to a specified console */ int ConPutChar(request,character) struct IOStdReq *request; char character; { request->io_Command = CMD_WRITE; request->io_Data = (APTR)&character; request->io_Length = (ULONG)1; DoIO(request); /* caution: read comments in manual! */ return(0); } /* * Output a NULL-terminated string of * characters to a console */ int ConPutStr(request,string) struct IOStdReq *request; char *string; { request->io_Command = CMD_WRITE; request->io_Data = (APTR)string; request->io_Length = (LONG)-1; DoIO(request); return(0); } /* * Write out a string of predetermined * length to the console */ int ConWrite(request,string,len) struct IOStdReq *request; char *string; int len; { request->io_Command = CMD_WRITE; request->io_Data = (APTR)string; request->io_Length = (LONG)len; DoIO(request); return(0); } /* * Queue up a read request * to a console */ int QueueRead(request,whereto) struct IOStdReq *request; char *whereto; { request->io_Command = CMD_READ; request->io_Data = (APTR)whereto; request->io_Length = (LONG)1; SendIO(request); return(0); } -h- menu.c Wed Apr 23 08:53:04 1986 menu.c /* * Dynamic Intuition menu functions * from the author of PDTerm */ #include "exec/types.h" #include "exec/memory.h" #include "graphics/text.h" #include "intuition/intuition.h" #define MEMFLAGS (LONG) (MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR) extern char *AllocMem(); extern LONG FreeMem(); /* * Create an instance of an Intuitext structure * containing text, positioned at (left,top) */ struct IntuiText *NewIText(text, left, top) char *text; int left, top; { struct IntuiText *newtext = NULL; newtext = (struct IntuiText *) AllocMem((LONG) sizeof(*newtext), MEMFLAGS); newtext->IText = (UBYTE *)text; newtext->FrontPen = 0; newtext->BackPen = 1; newtext->DrawMode = JAM2; newtext->LeftEdge = left; newtext->TopEdge = top; newtext->ITextFont = NULL; newtext->NextText = NULL; return(newtext); } /* * Add an Intuitext structure to the end of * list of them, returning a pointer to the * new Intuitext. */ struct IntuiText *AddIText(IText, text) struct IntuiText *IText; char *text; { struct IntuiText *newtext = NULL; newtext = (struct IntuiText *) AllocMem((LONG) sizeof(*newtext), MEMFLAGS); newtext->IText = (UBYTE *)text; newtext->FrontPen = IText->FrontPen; newtext->BackPen = IText->BackPen; newtext->DrawMode = IText->DrawMode; newtext->LeftEdge = IText->LeftEdge; newtext->TopEdge = IText->TopEdge + 11; newtext->ITextFont = IText->ITextFont; newtext->NextText = NULL; IText->NextText = newtext; return(newtext); } /* * Dispose of a lise of IntuiText structures */ DisposeIText(IText) struct IntuiText *IText; { struct IntuiText *cur, *next; cur = IText; for(next = cur->NextText; cur != NULL; next = next->NextText) { FreeMem(cur,(LONG) sizeof(*cur)); cur = next; } } /* * Dynamic menu constructor functions */ #define InterMenuWidth 15 /* * Create a new menu structure. */ struct Menu *NewMenu(menuname, width, height) char *menuname; int width, height; { struct Menu *menu = NULL; menu = (struct Menu *) AllocMem((LONG) sizeof(*menu), MEMFLAGS); menu->NextMenu = NULL; menu->LeftEdge = 0; menu->TopEdge = 0; menu->Width = width; menu->Height = height; menu->Flags = MENUENABLED; menu->MenuName = menuname; menu->FirstItem = NULL; return(menu); } /* * Create a new menu and add it to * the end of the list of menus. */ struct Menu *AddMenu(menus, MenuName, width, height) struct Menu *menus; char *MenuName; int width, height; { struct Menu *newmenu; newmenu = NewMenu(MenuName, width, height); newmenu->LeftEdge = menus->LeftEdge + menus->Width + InterMenuWidth; menus->NextMenu = newmenu; return(newmenu); } /* * Create a new menu item */ struct MenuItem *NewMenuItem(name, width, height) char *name; int width, height; { struct MenuItem *newitem = NULL; struct IntuiText *NewIText(), *newtext = NULL; newitem = (struct MenuItem *) AllocMem((LONG) sizeof(*newitem), MEMFLAGS); newtext = NewIText(name,0,1); newitem->NextItem = NULL; newitem->ItemFill = (APTR) newtext; newitem->LeftEdge = 0; newitem->TopEdge = 0; newitem->Width = width; newitem->Height = height; newitem->Flags = ITEMTEXT | ITEMENABLED | HIGHCOMP; newitem->MutualExclude = 0; newitem->SelectFill = NULL; newitem->Command = 0; newitem->SubItem = NULL; newitem->NextSelect = 0; return(newitem); } /* * Start the menu item list for a menu. */ struct MenuItem *AddNewMenuItem(menu, name, width, height) struct Menu *menu; char *name; int width, height; { struct MenuItem *newitem, *NewMenuItem(); newitem = NewMenuItem(name, width, height); menu->FirstItem = newitem; return(newitem); } /* * Add to a menu item list */ struct MenuItem *AddItem(items, name) struct MenuItem *items; char *name; { struct MenuItem *newitem, *NewMenuItem(); newitem = NewMenuItem(name, items->Width, items->Height); newitem->TopEdge = items->TopEdge + items->Height; newitem->LeftEdge = items->LeftEdge; items->NextItem = newitem; return(newitem); } /* * Start a list of subitems for a menu item. */ struct MenuItem *AddNewSubItem(item, name, width, height) struct MenuItem *item; char *name; int width, height; { struct MenuItem *newitem, *NewMenuItem(); newitem = NewMenuItem(name, width, height); item->SubItem = newitem; newitem->LeftEdge = item->Width; return(newitem); } /* * Dispose of a menu item. */ DisposeItem(item) struct MenuItem *item; { DisposeIText((struct ItuiText *)item->ItemFill); FreeMem(item,(LONG) sizeof(*item)); } /* * Dispose of a list of menu items. */ DisposeItems(items) struct MenuItem *items; { struct MenuItem *cur, *next; cur = items; for(next = cur->NextItem; cur != NULL; next = next->NextItem){ DisposeItem(cur); cur = next; } } /* * Dispose of a single menu */ DisposeMenu(menu) struct Menu *menu; { DisposeItems(menu->FirstItem); FreeMem(menu,(LONG) sizeof(*menu)); } /* * Get rid of a menu list. */ DisposeMenus(menus) struct Menu *menus; { struct Menu *cur, *next; cur = menus; for(next = cur->NextMenu; cur != NULL; next = next->NextMenu){ DisposeMenu(cur); cur = next; } } -h- tty.c Wed Apr 23 08:53:04 1986 tty.c /* * Name: MicroEMACS * Amiga console device virtual terminal display * Version: 31 * Last Edit: 19-Apr-86 * Created: 19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * * Drives the Amiga console device display. The code is a combination * of the Heath and ANSI terminal drivers. */ #include "def.h" #define BEL 0x07 /* BEL character. */ #define ESC 0x1B /* ESC character. */ extern int ttrow; extern int ttcol; extern int tttop; extern int ttbot; extern int tthue; int tceeol = 3; /* Costs, ANSI display. */ int tcinsl = 17; int tcdell = 16; /* * Initialize the terminal when the editor * gets started up. This is a no-op on the Amiga. */ ttinit() { } /* * Clean up the terminal, in anticipation of * a return to the command interpreter. This is a no-op * on the Amiga. */ tttidy() { } /* * Move the cursor to the specified * origin 0 row and column position. Try to * optimize out extra moves; redisplay may * have left the cursor in the right * location last time! */ ttmove(row, col) { if (ttrow!=row || ttcol!=col) { ttputc(ESC); ttputc('['); asciiparm(row+1); ttputc(';'); asciiparm(col+1); ttputc('H'); ttrow = row; ttcol = col; } } /* * Erase to end of line. */ tteeol() { ttputc(ESC); ttputc('['); ttputc('K'); } /* * Erase to end of page. */ tteeop() { ttputc(ESC); ttputc('['); ttputc('J'); } /* * Make a noise. */ ttbeep() { ttputc(BEL); ttflush(); } /* * Convert a number to decimal * ascii, and write it out. Used to * deal with numeric arguments. */ asciiparm(n) register int n; { register int q; q = n/10; if (q != 0) asciiparm(q); ttputc((n%10) + '0'); } /* * Insert a block of blank lines onto the * screen, using a scrolling region that starts at row * "row" and extends down to row "bot". Deal with the one * line case, which is a little bit special, with special * case code. * Since we don't really have a scrolling region, * delete the block of lines that would have been deleted if * we'd had one, then insert blank lines to move the rest * of the screen back to where it belongs. This idea from * the Heath driver. */ ttinsl(row, bot, nchunk) { register int i; if (row == bot) { /* Funny case. */ if (nchunk != 1) abort(); ttmove(row, 0); tteeol(); return; } ttmove(1+bot-nchunk, 0); if (nchunk > 0) { /* Delete a chunk of lines */ ttputc(ESC); /* nchunk in size. Rest of */ ttputc('['); /* screen moves up. */ asciiparm(nchunk); ttputc('M'); } ttmove(row, 0); if (nchunk > 0) { /* Insert a chunk nchunk in size*/ ttputc(ESC); /* before current line, sliding */ ttputc('['); /* rest of screen down. */ asciiparm(nchunk); ttputc('L'); } ttrow = row; /* End up on current line */ ttcol = 0; } /* * Delete a block of lines, with the uppermost * line at row "row", in a screen slice that extends to * row "bot". The "nchunk" is the number of lines that have * to be deleted. This is done by deleting nchunk lines at the * appropriate spot, then inserting nchunk lines to make up for * the empty space at the bottom of the virtual scrolling region. */ ttdell(row, bot, nchunk) { register int i; if (row == bot) { /* One line special case */ ttmove(row, 0); tteeol(); return; } if (nchunk > 0) { ttmove(row, 0); ttputc(ESC); ttputc('['); asciiparm(nchunk); ttputc('M'); } ttmove(1+bot-nchunk,0); if (nchunk > 0) { ttputc(ESC); /* For all lines in chunk */ ttputc('['); /* INS line before bottom */ asciiparm(nchunk); /* Bottom of window (and rest */ ttputc('L'); /* of screen) moves down */ } ttrow = HUGE; ttcol = HUGE; ttmove(bot-nchunk,0); } /* * No-op. */ ttwindow(top,bot) { } /* * No-op. */ ttnowindow() { } /* * Set the current writing color to the * specified color. Watch for color changes that are * not going to do anything (the color is already right) * and don't send anything to the display. */ ttcolor(color) register int color; { if (color != tthue) { if (color == CTEXT) { /* Normal video. */ ttputc(ESC); ttputc('['); ttputc('m'); } else if (color == CMODE) { /* Reverse video. */ ttputc(ESC); ttputc('['); ttputc('7'); ttputc('m'); } tthue = color; /* Save the color. */ } } /* * This routine is called by the * "refresh the screen" command to try and resize * the display. The new size, which must be deadstopped * to not exceed the NROW and NCOL limits, is stored * back into "nrow" and "ncol". Display can always deal * with a screen NROW by NCOL. Look in "window.c" to * see how the caller deals with a change. */ #define CSI 0x9B extern int ttsize(); /* Defined by "ttyio.c" */ ttresize() { int newnrow, newncol; ttsize(&newnrow,&newncol); /* ask tty how big it is */ if (newnrow < 1) /* check limits. */ newnrow = 1; else if (newnrow > NROW) newnrow = NROW; if (newncol < 1) newncol = 1; else if (newncol > NCOL) newncol = NCOL; nrow = newnrow; ncol = newncol; } /* * Read an unsigned parameter from the console */ static readparm(delim) char delim; { register int parm; register int c; parm = 0; while ((c=ttgetc()) >= '0' && c <= '9') parm = 10 * parm + c - '0'; return (c == delim) ? parm : -1; } -h- ttydef.h Wed Apr 23 08:53:04 1986 ttydef.h /* * Name: MicroEMACS * Amiga console device virtual terminal header file * Version: 31 * Last edit: 20-Apr-86 * Created: 20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Add Intuition menu support * */ #define GOSLING 1 /* Compile in fancy display. */ #define MEMMAP 0 /* Not memory mapped video. */ #define NROW 23 /* Rows. */ #define NCOL 77 /* Columns (AmigaDOS) */ /* * Special keys for the default Amiga console device keymap. * Function key codes are in the form <CSI>v~ * where v is a 1 or 2-digit code between 0 and 19, * so they comprise the first 20 entries in the key * table. The next 12 entries are for the help and * arrow keys. There is no shifted value for the * HELP key. */ #define KF1 K01 #define KF2 K02 #define KF3 K03 #define KF4 K04 #define KF5 K05 #define KF6 K06 #define KF7 K07 #define KF8 K08 #define KF9 K09 #define KF10 K0A #define KSF1 K0B #define KSF2 K0C #define KSF3 K0D #define KSF4 K0E #define KSF5 K0F #define KSF6 K10 #define KSF7 K11 #define KSF8 K12 #define KSF9 K13 #define KSF10 K14 #define KUP K15 #define KSUP K16 #define KDOWN K17 #define KSDOWN K18 #define KLEFT K19 #define KSLEFT K1A #define KRIGHT K1B #define KSRIGHT K1C #define KHELP K1D /* The 'menu' key doesn't really appear on the * Amiga keyboard. When ttgetc() sees a menu * event, it saves the menu number and item, * then stuffs the sequence for KMENU into * the input buffer. */ #define KMENU K1E /* * Similarly, this is the way we shoehorn the * mouse into the input stream, using the last * function key value for this purpose. */ #define KMOUSE K1F /* * Intuition menu interface. Each set of menu items * kept in a table of MenuBinding structures, which * is in turn kept in a table of MenuInfo structures. * * These tables are indexed via the menu and item * numbers to find the internal extended name of * the function associated with a certain item. */ struct MenuBinding { char *Command; char *Binding; }; struct MenuInfo { char *Name; /* name of menu */ short NumItems; /* # of items */ short MenuWidth; /* width, in CHARACTERS */ struct MenuBinding *Items; /* item name, internal binding */ }; #define NITEMS(arr) (sizeof(arr) / (sizeof(arr[0]))) -h- ttyio.c Wed Apr 23 08:53:04 1986 ttyio.c /* * Name: MicroEmacs * Amiga terminal-dependent I/O (Intuition) * Strategy and much code borrowed from the Lattice C * example in the ROM Kernel manual. * Version: 31 * Last edit: 21-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Created: 21-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic */ /* * Lots of includes. */ #include <exec/types.h> #include <exec/nodes.h> #include <exec/lists.h> #include <exec/tasks.h> #include <exec/ports.h> #include <exec/io.h> #include <devices/console.h> #include <libraries/dos.h> #include <graphics/clip.h> #include <graphics/view.h> #include <graphics/rastport.h> #include <graphics/layers.h> #include <graphics/text.h> #include <intuition/intuition.h> #undef TRUE #undef FALSE #include "def.h" #include "sysdef.h" /* * External functions. Declared explicitly * to avoid problems with different compilers. */ extern char *OpenLibrary(); extern int OpenConsole(); extern struct Window *OpenWindow(); extern struct MsgPort *CreatePort(); extern struct IOStdReq *CreateStdIO(); extern struct Menu *InitEmacsMenu(); extern struct IntuiMessage *GetMsg(); extern LONG AbortIO(); extern LONG CloseDevice(); extern LONG ReplyMsg(); extern LONG SetMenuStrip(); extern LONG Wait(); /* * Terminal I/O global variables */ #define NIBUF 128 /* Probably excessive. */ #define NOBUF 512 /* Not too big for 750/730. */ unsigned char obuf[NOBUF]; /* Output buffer */ int nobuf; /* # of bytes in above */ unsigned char ibuf[NIBUF]; /* Input buffer */ int ibufo, nibuf; /* head, # of bytes in ibuf */ int nrow; /* Terminal size, rows. */ int ncol; /* Terminal size, columns. */ extern char *version[]; /* Version information */ /* (I cheat and use it for */ /* the window title) */ /* * Intuition global variables */ #define WINDOWGADGETS (WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE) struct NewWindow MicroEMACS = { 0, 0, /* start position */ 640, 200, /* width, height */ 0, 1, /* detail pen, block pen */ MENUPICK | CLOSEWINDOW | /* IDCMP flags */ MOUSEBUTTONS | NEWSIZE, WINDOWGADGETS | ACTIVATE, /* window flags */ NULL, /* pointer to first user gadget */ NULL, /* pointer to user checkmark */ NULL, /* title (filled in later) */ NULL, /* pointer to screen (none) */ NULL, /* pointer to superbitmap */ 360,99,639,199, /* sizing limits min and max */ WBENCHSCREEN /* screen in which to open */ }; struct IntuitionBase *IntuitionBase; /* library bases */ struct GfxBase *GfxBase; struct Window *EmacsWindow; /* Our window */ struct Menu *EmacsMenu; /* Our menu */ struct MsgPort *consoleWritePort; /* I/O ports */ struct MsgPort *consoleReadPort; int intuitionMsgBit, /* Signal bits */ consoleMsgBit; struct IOStdReq *consoleWriteMsg; /* I/O messages */ struct IOStdReq *consoleReadMsg; unsigned char letter; /* Console input buffer */ USHORT class, /* Intuition event */ code, /* information */ qualifier; APTR address; SHORT x, y; /* * Some definitions */ #define INTUITION_MESSAGE ((LONG) 1 << intuitionMsgBit) #define CONSOLE_MESSAGE ((LONG) 1 << consoleMsgBit) /* * Open up the virtual terminal MicroEMACS communicates with. * Set up the window, console, and menu strip. */ extern int Enable_Abort; /* Do NOT allow abort! */ ttopen() { Enable_Abort = 0; /* Disable ^C */ GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", (LONG) 0); if (GfxBase == NULL) /* Graphics lib */ cleanup(1); IntuitionBase = (struct IntuitionBase *) /* Intuition */ OpenLibrary("intuition.library", (LONG) 0); if (IntuitionBase == NULL) cleanup(2); /* Create our window */ MicroEMACS.Title = (UBYTE *) version[0]; EmacsWindow = OpenWindow(&MicroEMACS); if (EmacsWindow == NULL) cleanup(2); /* Ports for reading and writing */ consoleWritePort = CreatePort("Emacs.con.write",(LONG) 0); if (consoleWritePort == NULL) cleanup(5); consoleWriteMsg = CreateStdIO(consoleWritePort); if (consoleWriteMsg == NULL) cleanup(6); consoleReadPort = CreatePort("Emacs.con.read",(LONG) 0); if (consoleReadPort == NULL) cleanup(7); consoleReadMsg = CreateStdIO(consoleReadPort); if (consoleReadMsg == NULL) cleanup(8); /* attach the console device to our window */ if (OpenConsole(consoleWriteMsg,consoleReadMsg,EmacsWindow) != 0) cleanup(10); /* attach a menu strip to the window */ EmacsMenu = InitEmacsMenu(); SetMenuStrip(EmacsWindow, EmacsMenu); /* determine signal bit #'s */ intuitionMsgBit = EmacsWindow->UserPort->mp_SigBit; consoleMsgBit = consoleReadPort->mp_SigBit; /* initialize console read and input buffer */ QueueRead(consoleReadMsg,&letter); nibuf = ibufo = 0; /* Determine initial size of virtual terminal. */ ttsize(&nrow,&ncol); return (0); } /* * Close the virtual terminal, de-allocating * everything we have allocated. */ ttclose() { ttflush(); AbortIO(consoleReadMsg); CloseDevice(consoleWriteMsg); cleanup(0); Enable_Abort = 1; } /* * Write a single character to the screen. * Buffered for extra speed, so ttflush() * does all the work. */ ttputc(c) unsigned char c; { obuf[nobuf++] = c; if (nobuf >= NOBUF) ttflush(); } /* * Flush characters from the output buffer. * Just blast it out with a console write call. */ ttflush() { if (nobuf > 0) { ConWrite(consoleWriteMsg, obuf, nobuf); nobuf = 0; } } /* * Input buffer management. * * The input buffer is a circular queue of characters * that is updated and manipulated by the macros and * functions below. This allows multiple Intuition * events (menus, console input, etc.) to be buffered * up until Emacs asks for them. */ #define CharsBuffered() (nibuf > 0 ) #define TypeInChar(c) if (nibuf < (NIBUF - 1))\ ibuf[(ibufo + nibuf++) % NIBUF] = c; /* * Return the next character in the input buffer. */ static GetInputChar() { unsigned char ch; if (nibuf <= 0) /* this shouldn't happen. */ return 0; ch = ibuf[ibufo++]; ibufo %= NIBUF; nibuf--; return (int) ch; } /* * Get a character for Emacs, without echo or * translation. Basically, handle Intuition * events until we get one that signifies * a character was typed in some way. */ ttgetc() { register struct IntuiMessage *message; /* IDCMP message */ register LONG wakeupmask; register int charfound; /* have we got a character yet? */ if (CharsBuffered()) /* check input buffer */ return GetInputChar(); /* return immediately have one */ charfound = FALSE; do { wakeupmask = Wait(INTUITION_MESSAGE|CONSOLE_MESSAGE); if (wakeupmask & CONSOLE_MESSAGE) { /* Keyboard */ GetMsg(consoleReadPort); /* free message */ TypeInChar(letter); /* do this FIRST */ QueueRead(consoleReadMsg, &letter); charfound = TRUE; } if (wakeupmask & INTUITION_MESSAGE) { /* Intuition */ while(message = GetMsg(EmacsWindow->UserPort)) { class = message->Class; code = message->Code; qualifier = message->Qualifier; address = message->IAddress; x = message->MouseX; y = message->MouseY; ReplyMsg(message); /* Need ||= here because next event may */ /* not result in a character... */ charfound = charfound || HandleEvent(); } /* while (GetMsg()) */ } /* if Intuition event */ } while (charfound == FALSE); return GetInputChar(); /* finally got a character. */ } /* * Handle the events we handle... * The result indicates if we've * put a character in the input buffer. * All the event information is global, * because there's a lot of it... */ extern int quit(); /* Defined by "main.c" */ static HandleEvent() { switch(class) { case MENUPICK: /* fake the menu key */ if (code != MENUNULL) return (DoMenu()); else return (FALSE); /* No character found */ break; case MOUSEBUTTONS: /* fake the mouse key */ return (DoMouse()); break; case CLOSEWINDOW: /* Call quit() directly */ quit(FALSE, 1, KRANDOM); /* This loses if in a */ return (FALSE); /* dialogue... */ break; } return(FALSE); /* No char found */ } /* * Handle a menu selection by hand-crafting * an escape sequence that looks like a function * key to the terminal driver. Save the * menu number and item number until the * menu execution function can ask for it. */ int LastMenuNum; /* Menu number for KMENU `key' */ int LastItemNum; /* Menu item for KMENU `key' */ int LastSubItem; /* Subitem number (for completeness) */ #define CSI 0x9b /* Amiga command sequence introducer */ static DoMenu() { struct MenuItem *item, *ItemAddress(); while (code != MENUNULL) { item = ItemAddress(EmacsMenu,(LONG) code); LastMenuNum = MENUNUM(code); /* number of menu */ LastItemNum = ITEMNUM(code); /* item number */ LastSubItem = SUBNUM(code); /* subitem code */ code = item->NextSelect; } TypeInChar(CSI); /* fake the MENU key */ TypeInChar('M'); TypeInChar('~'); return (TRUE); /* found a character! */ } /* * Return the last menu selection numbers to * the caller. Used by "ttymenu.c". */ ttmenu(menu,item,subitem) int *menu, *item, *subitem; { *menu = LastMenuNum; *item = LastItemNum; *subitem = LastSubItem; LastMenuNum = (USHORT)-1; /* Forget the values */ LastItemNum = (USHORT)-1; /* so they don't get re-used */ LastSubItem = (USHORT)-1; } /* * Handle a mouse selection by inserting * a "MOUSE key" (teer) sequence into the * input buffer and saving the x, y and * qualifier values for later. */ SHORT LastMouseX, LastMouseY; /* Position of mouse */ USHORT LastQualifier; /* Qualifier (shift key?) */ static DoMouse() { /* Save last mouse position */ if (code != SELECTDOWN) return (FALSE); LastMouseX = x - EmacsWindow->BorderLeft; LastMouseY = y - EmacsWindow->BorderTop; LastQualifier = qualifier; TypeInChar(CSI); /* fake the MOUSE key */ TypeInChar('P'); /* P for Pointer */ TypeInChar('~'); return (TRUE); /* found a character! */ } /* * Return the last mouse click values to * the caller. X and Y are translated * so that (0,0) is at the edge of the * top and left borders. Used by "ttymouse.c". */ ttmouse(x,y,qualifier) SHORT *x, *y; USHORT *qualifier; { *x = LastMouseX; *y = LastMouseY; *qualifier = LastQualifier; LastMouseX = (SHORT)-1; LastMouseY = (SHORT)-1; LastQualifier = (USHORT)-1; } /* * Return the current size of the virtual * terminal to the caller. Placed in * ttyio.c because it uses information * that is only available to the virtual * terminal handler. * * Assumes the WorkBench screen default * font is TOPAZ_EIGHTY (8 wide by 8 high). */ ttsize(rows,cols) int *rows, *cols; { *rows = (EmacsWindow->Height - /* have to take borders */ EmacsWindow->BorderTop - /* into account. */ EmacsWindow->BorderBottom) / 8; *cols = (EmacsWindow->Width - EmacsWindow->BorderLeft - EmacsWindow->BorderRight) / 8; } /* * Clean up. * Fall through all the possible cases (0 means * get rid of everything and start with the case * that fits the error situation. */ extern LONG ClearMenuStrip(); extern LONG DeleteStdIO(); extern LONG DeletePort(); extern LONG CloseWindow(); extern LONG CloseLibrary(); static cleanup(prob) { switch (prob) { case 0: ClearMenuStrip(EmacsWindow); DisposeMenus(EmacsMenu); case 10: case 8: DeleteStdIO(consoleReadMsg); case 7: DeletePort(consoleReadPort); case 6: DeleteStdIO(consoleWriteMsg); case 5: DeletePort(consoleWritePort); case 4: CloseWindow(EmacsWindow); case 2: if (GfxBase != NULL) CloseLibrary(GfxBase); case 1: if (IntuitionBase != NULL) CloseLibrary(IntuitionBase); break; } return(0); } -h- ttykbd.c Wed Apr 23 08:53:04 1986 ttykbd.c /* * Name: MicroEMACS * Amiga virtual terminal keyboard, default console keymap. * Version: 31 * Last edit: 20-Apr-86 * Created: 19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Supports all the default console device * (RAW:) input codes for the Amiga keyboard. * Modified: 20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * [UT-14] Added processing for GR_to_META, so * characters in the alternate character set * can automatically map to META characters. * This is a rather nice thing to have... * Added KMENU to key definitions, bound to amigamenu(). * This goes with the Intuition terminal driver. */ #include "def.h" #define ESC 0x1B /* Escape, arrows et al. */ #define CSI 0x9B /* Amiga CSI */ /* * The function keys on the Amiga send back * escape sequences of the form <ESC>[code~, where code * is a one or two-character code for the key. To make * it easier to decode, we place the internal key values * for these codes in this table. */ short consolemap[] = { KF1, KF2, KF3, KF4, KF5, KF6, KF7, KF8, KF9, KF10, KSF1, KSF2, KSF3, KSF4, KSF5, KSF6, KSF7, KSF8, KSF9, KSF10 }; #define NFUNCKEYS ((sizeof consolemap)/(sizeof consolemap[0])) /* * Names for the keys with basic keycode * between KFIRST and KLAST (inclusive). This is used by * the key name routine in "kbd.c". KFIRST is KRANDOM, which * we don't bind anything useful to. "Amiga Menus" is * special; we implement menus as another function key, * but there isn't really any "MENU" key on the keyboard. */ char *keystrings[] = { NULL, "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "Shift-F1", "Shift-F2", "Shift-F3", "Shift-F4", "Shift-F5", "Shift-F6", "Shift-F7", "Shift-F8", "Shift-F9", "Shift-F10", "Up", "Shift-Up", "Down", "Shift-Down", "Left", "Shift-Left", "Right", "Shift-Right", "Help", "The Menu", "The Mouse" }; /* * Read in a key, doing the low level mapping * of ASCII code to 11 bit code. This level deals with * mapping the special keys into their spots in the C1 * control area. The C0 controls go right through, and * get remapped by "getkey". * * If GR_to_META is TRUE, characters in the Amiga alternate * character set get mapped to META characters. If FALSE, * they get sent straight through. */ extern int GR_to_META; /* defined in "kbd.c" */ getkbd() { register int c; register int n; loop: c = ttgetc(); if (c == CSI) { c = ttgetc(); if (c == 'P') { /* mouse sequence */ ttgetc(); /* First because it's */ return (KMOUSE); /* used a lot. */ } if (c == 'M') { /* (fake) menu key */ ttgetc(); /* discard '~' */ return (KMENU); } /* Arrow keys */ if (c == 'A') return (KUP); if (c == 'B') return (KDOWN); if (c == 'C') return (KRIGHT); if (c == 'D') return (KLEFT); if (c == 'T') return (KSUP); if (c == 'S') return (KSDOWN); if (c == '?') { /* HELP key */ ttgetc(); /* discard '~' */ return (KHELP); } /* Shifted left, right arrow */ if (c == ' ') { c = ttgetc(); if (c == 'A' || c == '@') return (c == 'A') ? (KSLEFT) : (KSRIGHT); goto loop; /* try again, sucker */ } /* Function keys */ if (c >= '0' && c <= '9') { n = 0; do { n = 10*n + c - '0'; c = ttgetc(); } while (c>='0' && c<='9'); if (c == '~' && n < NFUNCKEYS) { c = consolemap[n]; if (c != KRANDOM) return (c); goto loop; } else goto loop; /* Try again */ } goto loop; /* Try again */ } /* Meta keys -- also check for high bit set */ if ((c == ESC) || (GR_to_META && (c & 0x80))) { if (c == ESC) c = ttgetc(); c &= 0x7F; /* Strip high bit */ if (ISLOWER(c) != FALSE) /* Copy the standard */ c = TOUPPER(c); /* META code. */ if (c>=0x00 && c<=0x1F) c = KCTRL | (c+'@'); return (KMETA | c); } return (c); } /* * Terminal specific keymap initialization. * Attach the special keys to the appropriate built * in functions. Bind all of the assigned graphics in the * Amiga alternate character set to self-insert. * * Bind the fake KMENU code to amigamenu() to do * menu selection as transparently as possible. * Ditto with KMOUSE. As is the case of all the * keymap routines, errors are very fatal. */ extern int amigamenu(); /* Defined by "ttymenu.c" */ extern int amigamouse(); /* Defined by "ttymouse.c" */ ttykeymapinit() { register SYMBOL *sp; register int i; /* ADD these bindings instead of duplicating it */ keyadd(KMENU, amigamenu, "amiga-menu"); keyadd(KMOUSE, amigamouse, "amiga-mouse"); keydup(KUP, "back-line"); keydup(KDOWN, "forw-line"); keydup(KRIGHT, "forw-char"); keydup(KLEFT, "back-char"); keydup(KHELP, "help"); keydup(KSUP, "goto-bop"); keydup(KSDOWN, "goto-eop"); keydup(KSRIGHT, "forw-word"); keydup(KSLEFT, "back-word"); keydup(KF1, "down-window"); keydup(KF2, "forw-page"); keydup(KF3, "enlarge-window"); keydup(KF4, "forw-window"); keydup(KF5, "split-window"); keydup(KF6, "file-visit"); keydup(KF7, "file-save"); keydup(KF8, "start-macro"); keydup(KF9, "bind-to-key"); keydup(KF10, "display-version"); keydup(KSF1, "up-window"); keydup(KSF2, "back-page"); keydup(KSF3, "shrink-window"); keydup(KSF4, "back-window"); keydup(KSF5, "only-window"); keydup(KSF6, "file-read"); keydup(KSF7, "file-write"); keydup(KSF8, "end-macro"); keydup(KSF9, "display-bindings"); keydup(KSF10, "quit"); /* * Bind all positions that correspond * to characters in the Amiga alternate * character set to "ins-self". These characters may * be used just like any other character. */ if ((sp=symlookup("ins-self")) == NULL) abort(); for (i=0xA0; i<0xFF; ++i) { if (binding[i] != NULL) abort(); binding[i] = sp; ++sp->s_nkey; } } -h- ttymenu.c Wed Apr 23 08:53:04 1986 ttymenu.c /* * Name: MicroEmacs * Commodore Amiga Intuition menus * Version: 31 * Last edit: 20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Created: 20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic */ #include <exec/types.h> #include <exec/memory.h> #include <graphics/text.h> #include <libraries/diskfont.h> #include <intuition/intuition.h> #undef TRUE #undef FALSE #include "def.h" /* Also includes "sysdef.h" and "ttydef.h" */ /* * When ttgetc() sees a menu selection event, it stuffs * the sequence <CSI>M~ into the input buffer, and * caches the menu number and item number for later. * This sequence is translated into the internal key code * KMENU, similar to KHELP and the other function keys. * * The menu item names are chosen to be relatively close * to the extended function names, so a user can usually * figure out the key binding of a menu item by searching * through the "display-bindings" buffer for something * that's close. */ /* * Commands for managing files */ static struct MenuBinding FileItems[] = { { "Read File", "file-read", }, { "Save File", "file-save", }, { "Visit File", "file-visit", }, { "Write File", "file-write" }, { "Insert File", "file-insert" }, { "Set File Name", "set-file-name" }, { "Quit", "quit", } }; /* * Commands for various editing functions */ static struct MenuBinding EditItems[] = { { "Forward Del Char", "forw-del-char" }, { "Kill Line", "kill-line" }, { "Yank", "yank" }, { "Delete Blank Lines", "del-blank-lines" }, { "NL And Indent", "ins-nl-and-indent" }, { "Twiddle", "twiddle" }, { "Quote Character", "quote" } }; /* * Movement commands */ static struct MenuBinding MoveItems[] = { { "Beg. Of Line", "goto-bol" }, { "Beg. Of Paragraph", "goto-bop" }, { "Beg. Of Buffer", "goto-bob" }, { "End Of Line", "goto-eol" }, { "End Of Paragraph", "goto-eop" }, { "End Of Buffer", "goto-eob" }, { "Goto Line", "goto-line" }, { "Show Cursor Pos", "display-position" } }; /* * Commands for searching and replacing */ static struct MenuBinding SearchItems[] = { { "Search Again", "search-again" }, { "Forward I-Search", "forw-i-search" }, { "Backward I-Search", "back-i-search" }, { "Forward Search", "forw-search" }, { "Backward Search", "back-search" }, { "Query Replace", "query-replace" }, }; /* * Commands that manipulate words */ static struct MenuBinding WordItems[] = { { "Forward Word", "forw-word" }, { "Backward Word", "back-word" }, { "Forward Del Word", "forw-del-word" }, { "Backward Del Word", "back-del-word" }, { "Capitalize Word", "cap-word" }, { "Lowercase Word", "lower-word" }, { "Uppercase Word", "upper-word" }, { "Fill Paragraph", "fill-paragraph" }, { "Set Fill Column", "set-fill-column" } }; /* * Region management */ static struct MenuBinding RegionItems[] = { { "Set Mark", "set-mark" }, { "Swap Dot And Mark", "swap-dot-and-mark" }, { "Kill Region", "kill-region" }, { "Copy Region", "copy-region" }, { "Lowercase Region", "lower-region" }, { "Uppercase Region", "upper-region" } }; /* * Buffer management */ static struct MenuBinding BufferItems[] = { { "Use Buffer", "use-buffer" }, { "Kill Buffer", "kill-buffer" }, { "Display Buffers", "display-buffers" }, { "Rename Buffer", "rename-buffer" } }; /* * Commands for manipulating windows */ static struct MenuBinding WindowItems[] = { { "Refresh Screen", "refresh" }, { "Reposition Window", "reposition-window" }, { "Split Window", "split-window" }, { "One Window", "only-window" }, { "Next Window", "forw-window" }, { "Previous Window", "back-window" }, { "Move Window Down", "down-window" }, { "Move Window Up", "up-window" }, { "Enlarge Window", "enlarge-window" }, { "Shrink Window", "shrink-window" } }; /* * Miscellaneous commands */ static struct MenuBinding MiscItems[] = { { "About MicroEMACS", "display-version" }, { "Start Macro", "start-macro" }, { "End Macro", "end-macro" }, { "Execute Macro", "execute-macro" }, { "Extended Command", "extended-command" }, { "Bind Key", "bind-to-key" }, { "Display Bindings", "display-bindings" }, { "CLI Subprocess", "spawn-cli" } }; /* * The following table contains the titles, number of * items, and pointers to, the individual menus. * * The MenuWidth field is the number of characters * in the longest item name. */ static struct MenuInfo EMInfo[] = { { "File", NITEMS(FileItems), 13, &FileItems[0] }, { "Edit", NITEMS(EditItems), 18, &EditItems[0] }, { "Move", NITEMS(MoveItems), 18, &MoveItems[0] }, { "Search", NITEMS(SearchItems), 17, &SearchItems[0] }, { "Word", NITEMS(WordItems), 17, &WordItems[0] }, { "Region", NITEMS(RegionItems), 17, &RegionItems[0] }, { "Buffer", NITEMS(BufferItems), 15, &BufferItems[0] }, { "Window", NITEMS(WindowItems), 17, &WindowItems[0] }, { "Misc", NITEMS(MiscItems), 16, &MiscItems[0] } }; /* * Initialize the MicroEmacs menus. * * Returns a pointer to the first menu header in * the menu list back to the caller. * * Makes the assumption that the font for * the window being used is 8 pixels wide -- * a safe bet with the version 1.1 ROM Kernel. */ struct Menu *InitEmacsMenu() { struct Menu *EMMenu, *NewMenu(), *AddMenu(); struct MenuItem *AddNewMenuItem(), *AddItem(), *AddNewSubItem(); register struct Menu *cm; register struct MenuInfo *cinf, *lastinfo;/* current menu info */ register struct MenuBinding *cb; /* current ``binding'' */ lastinfo = &EMInfo[NITEMS(EMInfo)]; /* sentinel for loop */ for (cinf = EMInfo; cinf < lastinfo; cinf++) { if (cinf == EMInfo) /* create menu header */ EMMenu = cm = NewMenu(cinf->Name, strlen(cinf->Name) * 8 + 6, 10); else cm = AddMenu(cm, cinf->Name,strlen(cinf->Name) * 8 + 6, 10); MakeMenu(cinf,cm,cinf->MenuWidth * 8 + 2, 10); } return EMMenu; } /* * Put menu items in a menu. Width and * height control the respective * sizes of the menu items. */ static MakeMenu(cinf,cm,width,height) struct MenuInfo *cinf; struct Menu *cm; int width, height; { struct Menu *NewMenu(), *AddMenu(); struct MenuItem *AddNewMenuItem(), *AddItem(), *AddNewSubItem(); register struct MenuBinding *cb, *lastbinding; register struct MenuItem *ci; lastbinding = &cinf->Items[cinf->NumItems]; for (cb = cinf->Items; cb < lastbinding; cb++) { if (cb == cinf->Items) ci = AddNewMenuItem(cm, cb->Command, width, height); else ci = AddItem(ci, cb->Command); } } /* * Menu command. Get the name of the command to * perform from the menu binding table, then * run the command if it is found and has the * right type. Print an error if there is anything * wrong. */ extern ttmenu(); /* Defined in "ttyio.c" */ /* Returns last menu selection */ amigamenu(f, n, k) { register SYMBOL *sp, *symlookup(); register int s; char *xname; int MenuNum, ItemNum, SubItem; ttmenu(&MenuNum,&ItemNum,&SubItem); /* check for spurious numbers */ if ((MenuNum < 0) || (MenuNum >= NITEMS(EMInfo))) return (FALSE); if ((ItemNum < 0) || (ItemNum >= EMInfo[MenuNum].NumItems)) return (FALSE); xname = EMInfo[MenuNum].Items[ItemNum].Binding; if ((sp=symlookup(xname)) != NULL) return ((*sp->s_funcp)(f, n, KRANDOM)); eprintf("Unknown menu command"); return (ABORT); } -h- ttymouse.c Wed Apr 23 08:53:04 1986 ttymouse.c /* * Name: MicroEmacs * (Simple) Commodore Amiga mouse handling * Version: 31 * Last edit: 21-Apr-86 * Created: 21-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic */ #include <exec/types.h> #include <intuition/intuition.h> #undef TRUE #undef FALSE #include "def.h" extern int ttmouse(); /* Defined by "ttyio.c" */ extern int forwline(); /* Defined by "line.c" */ extern int forwchar(); /* Defined by "basic.c" */ /* * Handle the mouse click that's been passed * by ttgetc() and position dot where the * user pointed at. */ amigamouse(f, n, k) { register WINDOW *wp; register int dot; register int c; register int col; register int newrow, newcol; SHORT x, y; USHORT qualifier; /* figure out new screen position of dot */ ttmouse(&x, &y, &qualifier); newcol = x / 8; newrow = y / 8; /* find out which window was clicked in */ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) if ((newrow >= wp->w_toprow) && (newrow < (wp->w_toprow + wp->w_ntrows))) break; /* make sure click was in a bona fide window */ if (wp == NULL) return (ABORT); /* move dot to top left of selected window */ curwp = wp; curbp = wp->w_bufp; curwp->w_dotp = wp->w_linep; curwp->w_doto = 0; /* go forward the correct # of lines */ forwline(FALSE, newrow - curwp->w_toprow, KRANDOM); /* go forward the correct # of characters */ /* need to count them out because of tabs */ col = dot = 0; while ((col < newcol) && (dot < llength(curwp->w_dotp))) { c = lgetc(curwp->w_dotp, dot++); if (c == '\t') col |= 0x07; else if (ISCTRL(c) != FALSE) ++col; ++col; } forwchar(FALSE, dot, KRANDOM); return (TRUE); }