/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #undef TRUE #undef FALSE #include "def.h" /* includes 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); }