home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / EDITOR / MG2A_SRC.ZIP / SYS / AMIGA / TTYIO.C < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-23  |  25.3 KB  |  989 lines

  1. /*
  2.  * Name:    MG 2a
  3.  *        Amiga terminal window I/O, with all kinds o' trimmings.
  4.  *        This module is 'way too big.
  5.  * Last Edit:    01-Dec-87 mic@emx.cc.utexas.edu
  6.  * Created:    21-Apr-86 mic@emx.cc.utexas.edu
  7.  */
  8.  
  9. /*
  10.  * Lots of includes.
  11.  */
  12.  
  13. #include <exec/types.h>
  14. #include <exec/nodes.h>
  15. #include <exec/lists.h>
  16. #include <exec/tasks.h>
  17. #include <exec/ports.h>
  18. #include <exec/io.h>
  19. #include <devices/console.h>
  20. #include <devices/inputevent.h>
  21. #include <libraries/dos.h>
  22. #include <graphics/clip.h>
  23. #include <graphics/view.h>
  24. #include <graphics/rastport.h>
  25. #include <graphics/layers.h>
  26. #include <graphics/text.h>
  27. #include <graphics/gfxbase.h>
  28. #include <intuition/intuition.h>
  29. #include <intuition/intuitionbase.h>
  30. #include <libraries/diskfont.h>
  31.  
  32. #undef    TRUE            /* avoid redefinition messages         */
  33. #undef    FALSE
  34. #include "def.h"        /* includes sysdef.h and ttydef.h    */
  35.  
  36. /*
  37.  * External Amiga functions.
  38.  */
  39. extern    LONG             AbortIO();
  40. extern    LONG             CloseDevice();
  41. extern    LONG             CloseLibrary();
  42. extern    LONG             CloseWindow();
  43. extern    struct    MsgPort        *CreatePort();
  44. extern    struct    IOStdReq    *CreateStdIO();
  45. extern    LONG             DeletePort();
  46. extern    LONG             DeleteStdIO();
  47. extern    struct    IntuiMessage    *GetMsg();
  48. #ifndef    V11
  49. extern    LONG            GetScreenData();
  50. #endif
  51. extern    int             OpenConsole();
  52. extern    char            *OpenLibrary();
  53. extern    struct    Window        *OpenWindow();
  54. extern    struct TextFont        *OpenDiskFont();
  55. extern    LONG             RectFill();
  56. extern    LONG             ReplyMsg();
  57. extern    LONG             RawKeyConvert();
  58. extern    LONG             SetAPen();
  59. extern    LONG             SetDrMd();
  60. extern    LONG             Wait();
  61.  
  62. #ifdef    DO_MENU
  63. extern    LONG             ClearMenuStrip();    /* menu functions */
  64. extern    struct    Menu        *InitEmacsMenu();
  65. extern    struct    MenuItem    *ItemAddress();
  66. extern    LONG             SetMenuStrip();
  67. #endif
  68.  
  69. #ifdef    MANX
  70. extern    int    Enable_Abort;        /* Do NOT allow abort!        */
  71. #endif
  72.  
  73. /*
  74.  * External MG functions and variables
  75.  */
  76. extern    int    quit();            /* Defined by "main.c"        */
  77. extern    char    version[];        /* Version information        */
  78. extern    int    ttrow;            /* Current cursor row        */
  79. extern    int    use_metakey;        /* Do meta characters?        */
  80.  
  81. /*
  82.  * Non-int internal functions.  P?() is used to conditionally indicate
  83.  * ANSI-style prototype arguments for compilers (i.e. Lattice) that
  84.  * support them.
  85.  */
  86. #ifdef    LATTICE
  87. #define    P1(a)    a,
  88. #define    P2(a,b)    a,b
  89. #define    P3(a,b,c) a,b,c
  90. #else
  91. #define    P1(a)
  92. #define    P2(a,b)
  93. #define P3(a,b,c)
  94. #endif
  95.  
  96. VOID        panic(P1(char *));
  97. VOID        setttysize();
  98. VOID        ttclose();
  99. VOID        ttflush();
  100. VOID        ttnflush(P1(int));
  101. VOID        ttputc(P1(unsigned char));
  102.  
  103. static VOID    cleanup();
  104. static VOID    firstwin();
  105. static VOID    qkey(P1(KCHAR));
  106. #ifdef    DO_MENU
  107. static VOID    qmenu(P1(USHORT));
  108. #endif
  109. #ifdef    MOUSE
  110. static VOID    qmouse(P3(SHORT, SHORT, USHORT));
  111. #endif
  112. static VOID    ttreopen(P1(int)) ;
  113. static VOID    setmaxima() ;
  114. static struct Screen    *wbscreen();
  115.  
  116. /*
  117.  * Library bases (used by glue libraries)
  118.  */
  119.  
  120. struct    IntuitionBase    *IntuitionBase;
  121. struct    GfxBase        *GfxBase;
  122. ULONG            DiskfontBase;
  123.  
  124. /*
  125.  * Intuition window and menu variables.  MG gets used a lot, because it
  126.  * gets reconfigured on the fly for the amiga-set-font and toggle-border
  127.  * operations.
  128.  */
  129.  
  130. #define WINDOWGADGETS (WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE)
  131. #define WINDOWFLAGS    (WINDOWGADGETS | ACTIVATE)
  132.  
  133. struct NewWindow MG = {
  134.     0,    0,            /* start position           */
  135.     0,    0,            /* width, height (set by ttopen)*/
  136.     0,    1,                 /* detail pen, block pen    */
  137. #ifdef    DO_MENU
  138.     MENUPICK |            /* If menu is used        */
  139. #endif
  140. #ifdef    MOUSE
  141.     MOUSEBUTTONS |             /* If mouse is used        */
  142. #endif
  143.     INTUITICKS | RAWKEY |
  144.     CLOSEWINDOW | NEWSIZE,        /* IDCMP flags            */
  145.     0,                /* window flags    (set by ttopen)    */
  146.     NULL,                /* pointer to first user gadget */
  147.     NULL,                /* pointer to user checkmark    */ 
  148.     NULL,                /* title (filled in later)    */
  149.     NULL,                /* pointer to screen (none)    */
  150.     NULL,                /* pointer to superbitmap    */
  151.     220,40,                /* minimum size    (small!)    */
  152.     0, 0,                /* maximum size (set by ttopen)    */
  153.     WBENCHSCREEN            /* screen in which to open    */ 
  154. };
  155.  
  156. static short    borderless = TRUE;    /* Flag for borderless window    */
  157. static short    toggle_zooms = TRUE;    /* Does toggling border zoom?    */
  158. static int    last_top, last_left, last_height, last_width;
  159.  
  160. struct Window    *EmW = NULL;        /* Our window            */
  161. struct Screen    *EmS = NULL;        /* Our screen (usually WB)    */
  162. short        toggling = FALSE;    /* Prevent menu wiping        */
  163. #ifndef    V11
  164. struct Screen    WBInfo;            /* Info about the WB screen    */
  165. #endif
  166. struct TextFont *EmFont = NULL;        /* Our font (usually TOPAZ_xx)    */
  167.  
  168. #ifdef    DO_MENU
  169. static struct Menu    *EmacsMenu = NULL;    /* Our menu        */
  170. #endif
  171.  
  172. static ULONG        class;            /* Intuition event    */
  173. static USHORT        code,            /*   information    */
  174.             qualifier;
  175. static APTR        address;
  176. static SHORT        x, y;
  177. static LONG        intuitionMsgBit;    /* Signal bit        */
  178. #define INTUITION_MESSAGE ((LONG) (1L << intuitionMsgBit))
  179.  
  180. /* * * * * * * * * * * * * console I/O * * * * * * * * * * * * * * * * */
  181.  
  182. #define    CSI    0x9b            /* Command Sequence Introducer    */
  183. #define    NOBUF    512            /* About 1/4 screen        */
  184. #define    NIBUF    256            /* Input buffer            */
  185.  
  186. static KCHAR        ibuf[NIBUF];    /* keyboard input buffer    */
  187. static int        ibufo, nibuf;    /* head, # of bytes in ibuf    */
  188.  
  189. #ifndef    PROMPTWAIT
  190. #define    PROMPTWAIT 20            /* ticks to wait before timeout    */
  191. #endif
  192. static    LONG        tickcount;    /* # intuiticks    since last char    */
  193.  
  194. static struct MsgPort    *conWritePort = NULL;    /* I/O ports         */
  195. static struct IOStdReq    *conWriteMsg = NULL;    /* I/O messages        */
  196. struct Device        *ConsoleDevice;            /* used by RawKeyConvert*/
  197. static unsigned char    outbuf[NOBUF+7];    /* output buffer    */
  198. static unsigned char    *obuf;            /* first output char    */
  199. int            nobuf;            /* # of bytes in above    */
  200. int            nrow;            /* Terminal size, rows.    */
  201. int            ncol;            /* Terminal size, cols.    */
  202.  
  203. /* * * * * * * * * functions to open/reopen the window * * * * * * * * * */
  204.  
  205. /*
  206.  * Open up the virtual terminal MG communicates with. Set up the window,
  207.  * console, and menu strip.
  208.  */
  209.  
  210. ttopen()
  211. {
  212.  
  213. #ifdef    MANX
  214.     Enable_Abort = 0;                /* Disable ^C    */
  215. #endif
  216.  
  217.     /* firstwin() is only called the very first time we open the window */
  218.     if (toggling == FALSE)
  219.         firstwin();
  220.  
  221.     /* Set the window size, set the flags and title, and open it */
  222.  
  223.     setmaxima();
  224.     MG.Flags = WINDOWFLAGS;
  225.     MG.Flags |= borderless ? BORDERLESS : WINDOWSIZING;
  226.     MG.Title = (UBYTE *) &version[0];
  227.  
  228.     if ((EmW = OpenWindow(&MG)) == NULL)
  229.         cleanup();
  230.     SetFont(EmW->RPort, EmFont);
  231.  
  232.     /* Once the window is created, get the Intuition signal bit, set up
  233.      * the menu, and tell the virtual terminal how big it is.
  234.       */
  235.     setttysize();
  236.     intuitionMsgBit = EmW->UserPort->mp_SigBit;
  237. #ifdef    DO_MENU
  238.     if (toggling == FALSE)
  239.         EmacsMenu = InitEmacsMenu(EmW);
  240.     if (EmacsMenu == NULL)
  241.         cleanup();
  242.     SetMenuStrip(EmW, EmacsMenu);
  243. #endif
  244.  
  245.     /* Attach a console device (purely for output now) to our window
  246.      */
  247.  
  248.     if ((conWritePort = CreatePort("Emacs.con.write", 0L)) == NULL)
  249.         cleanup();
  250.     if ((conWriteMsg = CreateStdIO(conWritePort)) == NULL)
  251.         cleanup();
  252.  
  253.     if (OpenConsole(conWriteMsg,NULL,EmW) != 0)
  254.         cleanup();
  255.  
  256.     ConsoleDevice = conWriteMsg->io_Device;
  257.     nibuf = ibufo = 0;
  258.  
  259.     return (0);
  260. }
  261.  
  262. /*
  263.  * Set up the initial state of the window.  Opens up libraries, decides how
  264.  * big the initial window should be, and whether it should be borderless.
  265.  */
  266.  
  267. static VOID firstwin()
  268. {
  269.     GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L);
  270.     if (GfxBase == NULL)                /* Graphics lib    */
  271.         cleanup();
  272.  
  273.     IntuitionBase = (struct IntuitionBase *)     /* Intuition    */
  274.         OpenLibrary("intuition.library", 0L);
  275.     if (IntuitionBase == NULL)
  276.         cleanup();
  277.  
  278.     DiskfontBase = (ULONG) OpenLibrary("diskfont.library", 0L);
  279.     if (DiskfontBase == NULL)
  280.         cleanup();
  281.  
  282.     /* Get our screen and font, then figure out if we can go borderless
  283.     */
  284.     if ((EmS = wbscreen()) == NULL)
  285.         cleanup();
  286.     EmFont = OpenDiskFont(EmS->Font);
  287.     if ((EmS->Width >= ((INIT_COLS * EmFont->tf_XSize) + LR_BORDER)) &&
  288.         (EmS->Height >= ((INIT_ROWS * EmFont->tf_YSize) + TB_BORDER)))
  289.         borderless = FALSE;
  290.  
  291.     /* Set the size of the initial window and fake the last one
  292.      */
  293.     last_width = MG.Width = EmS->Width;
  294.     last_height = MG.Height = EmS->Height;
  295.     last_left = MG.LeftEdge = 0;
  296.     last_top = MG.TopEdge = 0;
  297.  
  298.     bcopy(outbuf,"\x9b0 p", 4);    /* preload cursor off sequence */
  299.     obuf = outbuf + 4;
  300. }
  301.  
  302. /*
  303.  * Make sure the window isn't bigger than NROW * NCOL, while accounting
  304.  * for borders & such.  Since the window might not be at its largest right
  305.  * now, deadstop both the current width and the maxwidth.
  306.  */
  307.  
  308. static VOID setmaxima()
  309. {
  310.     register int maxw, maxh;
  311.  
  312.     MG.MaxWidth = EmS->Width;
  313.     MG.MaxHeight = EmS->Height;
  314.     maxw = NCOL * EmFont->tf_XSize + (borderless ? 0 : LR_BORDER);
  315.     maxh = NROW * EmFont->tf_YSize + (borderless ? TOP_OFFSET : TB_BORDER);
  316.  
  317.     if (MG.MaxWidth > maxw)        MG.MaxWidth = maxw;
  318.     if (MG.Width > maxw)        MG.Width = maxw;
  319.  
  320.     if (MG.MaxHeight > maxh)    MG.MaxHeight = maxh;
  321.     if (MG.Height > maxh)        MG.Height = maxh;
  322. }
  323.  
  324.  
  325. /* Return a pointer the workbench screen, using GetScreenData() to do
  326.  * things like a good citizen.  Left the V11 code in as a reminder
  327.  * that what works is not always the _best_ way to do things.
  328.  * Thanks to Tom Rokicki for reminding me (mpk) this had to be done.
  329.  */
  330.  
  331. static struct Screen
  332. *wbscreen()
  333. {
  334. #ifndef    V11
  335.     return GetScreenData(&WBInfo, (ULONG) sizeof(WBInfo),
  336.         WBENCHSCREEN, NULL) ? &WBInfo : ((struct Screen *)NULL);
  337. #else
  338.     register struct Screen    *s;
  339.     Forbid();
  340.     for (s = IntuitionBase->FirstScreen; s ; s = s->NextScreen)
  341.         if ((s->Flags & SCREENTYPE) == WBENCHSCREEN)
  342.             break;
  343.     Permit();
  344.     return (s);
  345. #endif
  346. }
  347.  
  348. /*
  349.  * Hide the window and open it up again.  If resize is TRUE, they're
  350.  * being called as part of a resize operation, so assume that the
  351.  * NewWindow structure is set correctly.  Otherwise, store the current
  352.  * window size and position in the NewWindow structure.
  353.  *
  354.  * These two functions are split so we can do things like ttreopen() and
  355.  * tticon() cleanly.
  356.  */
  357.  
  358. VOID
  359. tthide(resize)
  360. int resize;
  361. {
  362.     toggling = TRUE;
  363.     if (resize == FALSE) {             /* if we're resizing,    */
  364.         MG.LeftEdge = EmW->LeftEdge; /* use current window size    */
  365.         MG.TopEdge = EmW->TopEdge;
  366.         MG.Width = EmW->Width;
  367.         MG.Height = EmW->Height;
  368.     }
  369.     ttclose();                /* reset to zero    */
  370. }
  371.  
  372. VOID
  373. ttshow(resize)
  374. int resize;
  375. {
  376.     ttopen();                /* re-open tty window    */
  377.     ttinit();                /* re-initalize tty    */
  378.     sgarbf = TRUE;                /* screen was trashed    */
  379.     if (resize == TRUE)
  380.         nrow = ncol = -1;        /* trash screen size    */
  381.     refresh();                /* and redraw it    */
  382.     toggling = FALSE;            /* Ok, done        */
  383. }
  384.  
  385. /*
  386.  * ttreopen() was split into the two functions above when tticon()
  387.  * was introduced.
  388.  */
  389.  
  390. static VOID
  391. ttreopen(resize)
  392. int resize;
  393. {
  394.     tthide(resize);
  395.     ttshow(resize);
  396. }
  397.  
  398. /* * * * * * * * * * * * functions to close the window * * * * * * * * */
  399.  
  400. /*
  401.  * Close the virtual terminal.  If toggling, don't release all
  402.  * the other resources we've allocated.
  403.  */
  404. VOID
  405. ttclose()
  406. {
  407.     ttflush();
  408.     CloseDevice(conWriteMsg);
  409.     DeleteStdIO(conWriteMsg);    conWriteMsg = NULL;
  410.     DeletePort(conWritePort);    conWritePort = NULL;
  411. #ifdef    DO_MENU
  412.     ClearMenuStrip(EmW);
  413. #endif
  414.     CloseWindow(EmW);
  415.     if (toggling == FALSE)
  416.         cleanup();        /* clean up everything    */
  417. #ifdef    MANX
  418.     Enable_Abort = 1;
  419. #endif
  420. }
  421.  
  422.  
  423. /*
  424.  * Clean up.  Done only when we're really closing up shop
  425.  */
  426.  
  427. static VOID
  428. cleanup()
  429. {
  430.     if (conWriteMsg)    DeleteStdIO(conWriteMsg);
  431.     if (conWritePort)    DeletePort(conWritePort);
  432. #ifdef    DO_MENU
  433.     if (EmacsMenu)        DisposeMenus(EmacsMenu);
  434. #endif
  435.     if (EmFont)        CloseFont(EmFont);
  436.     if (DiskfontBase)    CloseLibrary(DiskfontBase);
  437.     if (IntuitionBase)    CloseLibrary(IntuitionBase);
  438.     if (GfxBase)        CloseLibrary(GfxBase);
  439. }
  440.  
  441. /* * * * * * * * functions that diddle the window and reopen it * * * * * */
  442.  
  443. /*
  444.  * Toggle between a borderless window and a sizeable window. This lets you
  445.  * use the whole screen if you want. Bound to "amiga-toggle-border".
  446.  */
  447.  
  448. togglewindow(f, n)
  449. {
  450.  
  451.     if ((borderless = !borderless) == TRUE) {/* *always* save last     */
  452.         last_top = EmW->TopEdge;    /* bordered window size     */
  453.         last_left = EmW->LeftEdge;
  454.         last_width = EmW->Width;
  455.         last_height = EmW->Height;
  456.     }
  457.  
  458.     if (toggle_zooms == FALSE) {        /* just use current size */
  459.         ttreopen(FALSE);    
  460.         return (TRUE);
  461.     }
  462.  
  463.     /* zooming -- if borderless, go as big as possible.  If
  464.      * bordered, set to last saved value of bordered window
  465.      */
  466.     if (borderless) {
  467.         MG.LeftEdge = 0;
  468.         MG.TopEdge = 0;
  469.         MG.Width = MG.MaxWidth;
  470.         MG.Height = MG.MaxHeight;
  471.     } else {
  472.         MG.LeftEdge = last_left;
  473.         MG.TopEdge = last_top;
  474.         MG.Width = last_width;
  475.         MG.Height = last_height;
  476.     }
  477.     ttreopen(TRUE);            /* open with new size    */
  478.     return (TRUE);
  479. }
  480.  
  481. /*
  482.  * Modify the action of "amiga-toggle-border", reporting outcome to user.
  483.  * Bound to "amiga-zoom-mode".
  484.  */
  485. togglezooms(f, n)
  486. {
  487.     toggle_zooms = !toggle_zooms;
  488.     ewprintf("Toggling border %s",
  489.         toggle_zooms ?  "expands window to screen size" :
  490.                 "retains current window size");
  491.     return (TRUE);
  492. }
  493.  
  494. #ifdef    CHANGE_FONT
  495. /*
  496.  * Select a different font for the MG window. This does not work very well with
  497.  * proportional fonts, so we ask the user to confirm before he uses one. It's
  498.  * available if you want to be able to use your own disk font (or Topaz 11
  499.  * under 1.2) to edit with.
  500.  */
  501.  
  502. setfont(f, n)
  503. {
  504.     register int    s, size;
  505.     register struct TextFont *newfont;
  506.     char        fontname[80], fontpath[84], fontsize[3];
  507.     struct TextAttr    ta;
  508.  
  509.     /* If negative size, reset to default font
  510.      */
  511.     if ((f & FFARG) && (n <= 0)) {
  512.         CloseFont(EmFont);            /* return old font  */
  513.         EmFont = OpenDiskFont(EmS->Font);    /* screen's default */
  514.         ttreopen(FALSE);            /* no resize        */
  515.         ewprintf("Now using default font");
  516.         return (TRUE);
  517.     }
  518.  
  519.     if ((s = ereply("Font name: ",fontname, sizeof(fontname))) != TRUE)
  520.         return (s);
  521.     strcpy(fontpath,fontname);
  522.     strncat(fontpath,".font",sizeof(fontpath));/* make name */
  523.  
  524.     /* Get font size */
  525.     if (f & FFARG)
  526.         size = n;
  527.     else {
  528.         if ((s = ereply("Font size: ",
  529.                 fontsize, sizeof(fontsize))) != TRUE)
  530.             return (s);
  531.         size = atoi(fontsize);
  532.     }
  533.  
  534.     /* Set up text attributes */
  535.     ta.ta_Name = (UBYTE *)fontpath;
  536.     ta.ta_YSize = size;
  537.     ta.ta_Style = FS_NORMAL;
  538.     ta.ta_Flags = 0;
  539.  
  540.     /* Look for the font */
  541.     ewprintf("Looking for %s %d...",fontname,size);
  542.     if ((newfont = OpenDiskFont(&ta)) == NULL) {
  543.         ewprintf("Can't find %s %d!",fontname,size);
  544.         return (FALSE);
  545.     } 
  546.  
  547.     /* Found it! Check before using it */
  548.     if ((newfont->tf_YSize != size) &&
  549.         ((s = eyesno("Size unavailable - use closest")) != TRUE)) {
  550.         CloseFont(newfont);
  551.         return (FALSE);
  552.     }
  553.     if ((newfont->tf_Flags & FPF_PROPORTIONAL) &&
  554.         (((s = eyesno("Use proportional font")))!= TRUE)) {
  555.             CloseFont(newfont);
  556.             return (FALSE);
  557.     }
  558.  
  559.     /* Get rid of old font and reopen with the new one */
  560.     CloseFont(EmFont);
  561.     EmFont = newfont;
  562.     ttreopen(FALSE);
  563.     ewprintf("Now using font %s %d",fontname,EmFont->tf_YSize);
  564.     return (TRUE);
  565. }
  566. #endif
  567.  
  568. /* * * * * * * * * * * * * console output functions  * * * * * * * * * * * * */
  569.  
  570. /*
  571.  * Write a single character to the screen. Buffered for speed, so ttflush()
  572.  * does all the work.
  573.  */
  574. VOID
  575. ttputc(c)
  576. unsigned char c;
  577. {
  578.     obuf[nobuf++] = c;
  579.     if (nobuf >= NOBUF)
  580.         ttflush();
  581. }
  582.  
  583. /*
  584.  * Flush characters from the output buffer.  If the # of characters is
  585.  * greater than a certain ad-hoc value, turn the cursor off while doing
  586.  * the write. To avoid extra writes, the output buffer has been preloaded
  587.  * with the cursor-off sequence.  Outbuf is large enough to hold the extra
  588.  * 7 characters.
  589.  */
  590. #define    MIN_OFF    8
  591. VOID
  592. ttflush()
  593. {
  594.     if (nobuf > 0) {
  595.         if (nobuf <= MIN_OFF)    /* don't turn off for short writes */
  596.             ConWrite(conWriteMsg, obuf, nobuf);
  597.         else {
  598.             obuf[nobuf++] = '\x9b';
  599.             obuf[nobuf++] = ' ';
  600.             obuf[nobuf++] = 'p';
  601.             ConWrite(conWriteMsg, outbuf, nobuf + 4);
  602.         }
  603.         nobuf = 0;
  604.     }
  605. }
  606.  
  607. /*
  608.  * The caller intends to output an escape sequence, but only flush
  609.  * the buffer if there's not enough room to hold the complete sequence.
  610.  * This avoids breaking up escape sequences when we turn the cursor
  611.  * off in ttflush(), at the expense of some extra function calls.
  612.  */
  613. VOID ttnflush(n)
  614. int n;
  615. {
  616.     if ((nobuf + n) > NOBUF)
  617.         ttflush();
  618. }
  619.  
  620. /* * * * * * * * * * * * * console input functions  * * * * * * * * * * * * */
  621.  
  622. /*
  623.  * Read a character (really a KCHAR, > 8 bits), blocking till a character
  624.  * is put in the input buffer and can be returned.
  625.  */
  626. ttgetc()
  627. {
  628.     return handle_kbd(FALSE);
  629. }
  630.  
  631. /*
  632.  * Return TRUE if we've waited for 2 seconds and nothing has happened,
  633.  * else return false.
  634.  */
  635.  
  636. ttwait()
  637. {
  638.     return handle_kbd(TRUE);    /* time out after 2 sec */
  639. }
  640.  
  641. /*
  642.  * Common routine for handling character input, with and without timeout.
  643.  * Handle events until:
  644.  *
  645.  *    1) a character is put in the input buffer
  646.  *    2) if timeout == TRUE, PROMPTWAIT IntuiTicks have gone by
  647.  *
  648.  * If timeout == FALSE, the input character is returned and removed from
  649.  *    the input buffer.
  650.  *
  651.  * If timeout == TRUE, returns TRUE if the read timed out, else FALSE.
  652.  *    Leaves any character typed in the input buffer.
  653.  */
  654.  
  655. static handle_kbd(timeout)
  656. register int timeout;
  657. {
  658.     register struct    IntuiMessage *message;    /* IDCMP message     */
  659.     register LONG    wakeupmask;        /* which signals?    */
  660.     register int    charfound;        /* got a character yet?    */
  661.     KCHAR        k;
  662.  
  663.     tickcount = 0;                /* *always* zero the count */
  664.     if (nibuf)                /* any chars? return if so */
  665.         return timeout ? FALSE : nextkey();
  666.  
  667.     charfound = FALSE;            /* nope -- have to wait    */
  668.     do {
  669.         wakeupmask = Wait(INTUITION_MESSAGE);
  670.  
  671.         /*  Handle Intuiticks specially for speed */
  672.         while(message =    GetMsg(EmW->UserPort))
  673.             if (message->Class == INTUITICKS) {
  674.                 tickcount++;
  675.                 ReplyMsg(message);
  676.             } else if (dispatch(message) == TRUE)
  677.                 charfound = TRUE;
  678.  
  679.         /* time out if enough ticks have gone by without
  680.          * any keyboard input.  We do this *after* all the
  681.          * events in the current list have been dispatched.
  682.          */
  683.         if (timeout && (tickcount > PROMPTWAIT))
  684.             break;
  685.     } while (charfound == FALSE);
  686.  
  687.  
  688.     /* If called by ttwait(), return FALSE if a character was found.
  689.      * Else return the next character in the input buffer
  690.     */
  691.     return timeout ? (!charfound) : nextkey();
  692. }
  693.  
  694. /*
  695.  * Handle the events we handle...  The result returned indicates if we've put
  696.  * a character in the input buffer.
  697.  */
  698. #ifdef    DO_METAKEY
  699. #define    IEQUALIFIER_ALT        (IEQUALIFIER_RALT | IEQUALIFIER_LALT)
  700. #endif
  701.  
  702. static dispatch(msg)
  703. register struct IntuiMessage *msg;
  704. {
  705. #ifdef    DO_MENU
  706.     register struct    MenuItem    *item;
  707. #endif
  708.  
  709.     register int            txheight, txwidth;
  710.     register struct RastPort    *rp;
  711.     int                dx, dy, fgpen, drmode;
  712.  
  713.     static struct InputEvent FakedEvent = { NULL, IECLASS_RAWKEY, 0, 0, 0 };
  714.     unsigned char            keybuf[64], altbuf[64];
  715.     int                keylen, altlen, i;
  716. #ifndef    V11
  717.     APTR                deadcodes;
  718. #endif
  719.  
  720.     class =    msg->Class;        /* grab the info before we     */
  721.     code = msg->Code;        /* reply to the message        */
  722.     qualifier = msg->Qualifier;
  723.     address = msg->IAddress;
  724.     x = msg->MouseX;
  725.     y = msg->MouseY;
  726. #ifndef    V11
  727.     if (class == RAWKEY)        /* get dead key info        */
  728.         deadcodes = (APTR) *address;
  729. #endif
  730.     ReplyMsg(msg);            /* return it to Intuition    */
  731.  
  732.     switch(class) {            /* see what the fuss is about    */
  733.     case RAWKEY:
  734.         FakedEvent.ie_Code = code;
  735.         FakedEvent.ie_Qualifier = qualifier;
  736. #ifndef    V11
  737.         FakedEvent.ie_EventAddress = deadcodes;
  738. #endif
  739.         keylen = (int) RawKeyConvert(&FakedEvent,
  740.             keybuf,    (LONG)sizeof(keybuf), NULL);
  741.  
  742. #ifdef    DO_METAKEY
  743.         /* Special mapping for ALT-ed keys.  The intent is to get
  744.          * around keymaps where the ALT'ed characters map to
  745.          * things other than (0x80 | (c)).  This may not work
  746.          * for all possible keymaps, but it seems to be ok
  747.          * for the keymaps distributed with 1.2.
  748.          */
  749.         if ((qualifier & IEQUALIFIER_ALT) && use_metakey) {
  750.             FakedEvent.ie_Qualifier &= ~IEQUALIFIER_ALT;
  751.             altlen = (int) RawKeyConvert(&FakedEvent, altbuf,
  752.                 (LONG)sizeof(altbuf), NULL);
  753.             if (altlen == 1)
  754.                 altbuf[0] |= 0x80;
  755.             for (i = 0; i < altlen ; i++)
  756.                 qkey((KCHAR) altbuf[i]);
  757.             return (altlen > 0) ? TRUE : FALSE;
  758.         }
  759. #endif
  760.         /* non-ALTed key */
  761.         for (i = 0; i < keylen; i++)
  762.             qkey((KCHAR) keybuf[i]);
  763.         return (keylen > 0) ? TRUE : FALSE;
  764.         break;
  765.  
  766. #ifdef    DO_MENU
  767.     case MENUPICK:
  768.         if (code == MENUNULL)
  769.             return (FALSE);
  770.         while (code != MENUNULL) {/* handle multiple selection    */
  771.             qmenu(code);
  772.             item = ItemAddress(EmacsMenu,(LONG) code);
  773.             code = item->NextSelect;
  774.         }
  775.         return (TRUE);        /* puts KMENU in event queue    */
  776.         break;
  777. #endif
  778.  
  779. #ifdef    MOUSE
  780.     case MOUSEBUTTONS:            /* fake the mouse key    */
  781.         if (code != SELECTDOWN)        /* ignore SELECTUP    */
  782.             return (FALSE);
  783.         qmouse(x, y, qualifier);
  784.         return (TRUE);
  785.         break;
  786. #endif
  787.  
  788.     case NEWSIZE:
  789.         /* Sometimes when you resize the window to make it smaller,
  790.          * garbage is left at the right and bottom sides of the
  791.          * window. This code is devoted to (somehow) getting rid
  792.          * of this garbage.  Any suggestions?
  793.          */
  794.  
  795.         rp = EmW->RPort;
  796.         fgpen = rp->FgPen;        /* save params        */
  797.         drmode = rp->DrawMode;
  798.         SetDrMd(rp, (LONG) JAM1);
  799.         SetAPen(rp, (LONG) EmW->RPort->BgPen);
  800.  
  801.         /* Check the bottom of the window
  802.          */
  803.         txheight = EmW->Height - EmW->BorderTop - EmW->BorderBottom;
  804.         if (dy = (txheight % FontHeight(EmW)))
  805.             RectFill(rp,
  806.                 (LONG) EmW->BorderLeft,
  807.                 (LONG) EmW->BorderTop + txheight - dy - 1,
  808.                 (LONG) (EmW->Width - 1) - EmW->BorderRight,
  809.                 (LONG) (EmW->Height - 1) - EmW->BorderBottom);
  810.  
  811.         /* Check the right side
  812.          */
  813.         txwidth = EmW->Width - EmW->BorderLeft - EmW->BorderRight;
  814.         if (dx = txwidth % FontWidth(EmW))
  815.             RectFill(rp,
  816.                 (LONG) EmW->BorderLeft + txwidth - dx - 1,
  817.                 (LONG) EmW->BorderTop,
  818.                 (LONG) (EmW->Width - 1) - EmW->BorderRight,
  819.                 (LONG) (EmW->Height - 1) - EmW->BorderBottom);
  820.  
  821.         SetDrMd(rp, (LONG) drmode);
  822.         SetAPen(rp, (LONG) fgpen);    /* restore colors */
  823.  
  824.         /* Tell the console device to resize itself */
  825.         ttputc(CSI);
  826.         ttputc('t');
  827.         ttputc(CSI);
  828.         ttputc('u');
  829.         ttflush();
  830.  
  831.         /* Signal the editor that a new size has occurred.
  832.          * I may break down and do this asynchronously...
  833.          */
  834.         qkey(KRESIZE);
  835.         return (TRUE);            /* we done (finally)    */
  836.         break;
  837.  
  838.         case CLOSEWINDOW:
  839.         /* Calling quit() directly is not a guaranteed win. */
  840.         quit(FFRAND, 1);
  841.         return (FALSE);
  842.                 break;
  843.  
  844.     default:
  845.         panic("HandleMsg: unknown event!!!");
  846.         break;
  847.     }
  848.     return(FALSE);
  849. }
  850.  
  851. /*
  852.  * Return the current size of the virtual terminal in nrow and ncol,
  853.  * making sure we don't go beyond the size of the internal video array.
  854.  * Assumes the current font is monospaced.
  855.  */
  856. VOID
  857. setttysize()
  858. {
  859.     nrow = (EmW->Height - TOP_OFFSET
  860.             - EmW->BorderBottom) / FontHeight(EmW);
  861.     ncol = (EmW->Width - EmW->BorderLeft
  862.             - EmW->BorderRight) / FontWidth(EmW);
  863.     if (nrow < 1)        nrow = 1;
  864.     if (nrow > NROW)    nrow = NROW;
  865.     if (ncol < 1)        ncol = 1;
  866.     if (ncol > NCOL)    ncol = NCOL;
  867. }
  868.  
  869. /*
  870.  * Exit as soon as possible, after displaying the message.
  871.  */
  872. VOID
  873. panic(s)
  874. char *s;
  875. {
  876.     ewprintf(s);        /* put message at bottom    */
  877.     Delay((ULONG) 90);    /* wait 1.5 seconds        */
  878.     ttclose();        /* get rid of window &resources    */
  879.     exit(10000);        /* go 'way            */
  880. }
  881.  
  882. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  883.  *             Event buffer management         *
  884.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  885.  
  886. /*
  887.  * Return next key in the input buffer, if any available.  Returns -1 if not.
  888.  */
  889. static int
  890. nextkey()
  891. {
  892.     register KCHAR k;
  893.  
  894.     if (nibuf <= 0) {        /* shouldn't happen, but could... */
  895.         nibuf = 0;
  896.         return -1;
  897.     } else {
  898.         k = ibuf[ibufo++];
  899.         nibuf--;
  900.         ibufo %= NIBUF;
  901.         return (int) k;
  902.     }
  903. }            
  904.  
  905. /*
  906.  * Return true if there are some characters available in the input buffer.
  907.  */
  908. typeahead()
  909. {
  910.     return (nibuf > 0);
  911. }
  912.  
  913. /*
  914.  * Add a key to the input queue
  915.  */
  916. static VOID
  917. qkey(k)
  918. KCHAR k;
  919. {
  920.     if (nibuf < NIBUF)
  921.         ibuf[(ibufo + nibuf++) % NIBUF] = k;
  922. }
  923.  
  924. #ifdef    MOUSE
  925. /*
  926.  * Add a mouse event to the input queue, calculating the row and column
  927.  * value from the current height and width of the window's font.
  928.  */
  929.  
  930. static VOID
  931. qmouse(x, y, qual)
  932. SHORT x, y;
  933. USHORT qual;
  934. {
  935.     register int    myqual = MQ_NOQUAL;
  936.     register int    row, col;
  937.     register WINDOW    *wp;    
  938.  
  939.     /* get row, column    */
  940.     col = (x - EmW->BorderLeft) / FontWidth(EmW);
  941.     row = (y - TOP_OFFSET) / FontHeight(EmW);
  942.  
  943.     /* find out which kind of window was clicked in */
  944.     for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
  945.         if ((row >= wp->w_toprow) && 
  946.             (row <= (wp->w_toprow + wp->w_ntrows)))
  947.             break;
  948.     if (wp == NULL)
  949.         myqual |= MQ_ECHO;
  950.     else if (row == (wp->w_toprow + wp->w_ntrows))
  951.         myqual |= MQ_MODE;
  952.     else
  953.         myqual |= MQ_WINDOW;
  954.  
  955.     /* figure out qualifiers    */
  956.     if (qual & IEQUALIFIER_CONTROL)
  957.         myqual |= MQ_CTRL;
  958.     if (qual & (IEQUALIFIER_LSHIFT | IEQUALIFIER_LSHIFT))
  959.         myqual |= MQ_SHIFT;
  960.     if (qual & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
  961.         myqual |= MQ_ALT;
  962.  
  963.     /*
  964.       * Queue up the whole mess.  If user didn't click in the echo
  965.      * line, transmit the x, y values to the mouse function
  966.      */
  967.     qkey(KW___MOUSE + myqual);
  968.     if (MQ_WHERE(myqual) != MQ_ECHO) {
  969.         qkey(M_X_ZERO + col);
  970.         qkey(M_Y_ZERO + row);
  971.     }
  972. }
  973. #endif
  974.  
  975. #ifdef    DO_MENU
  976. /*
  977.  * Add a menu event to the queue.
  978.  */
  979. static VOID
  980. qmenu(code)
  981. USHORT code;
  982. {
  983.     qkey(KMENU);        /* menu key sequence    */
  984.     qkey(((KCHAR) MENUNUM(code)) + MN_OFFSET);
  985.     qkey(((KCHAR) ITEMNUM(code)) + MN_OFFSET);
  986.     qkey(((KCHAR) SUBNUM(code)) + MN_OFFSET);
  987. }
  988. #endif
  989.