/* io.c */ #ifdef AMIGA /* Compile with -HPreHeader.q to get "less.h"! */ #else #include "less.h" #endif #include #include #include #include #undef TRUE #undef FALSE extern int sigs; static struct ConCom *tty = NULL; public int nrow = 0; /* Terminal size, rows. */ public int ncol; /* Terminal size, columns. */ static struct Window *LessWindow; int Wind_Spec[4] = { 0, 0, 0, 0 }; /* User-supplied option specifying window size/position: * * [0]: Left edge X - coord. * [1]: Top edge Y - coord. * [2]: Right edge X - coord. * [3]: Bottom edge Y - coord. * * If element 2 or 3 is negative, it is taken to be a relative value to be * subtracted from the maximum screen size. If the resulting window * would be smaller than the minimum, it is quietly enlarged to the * minimum, by expanding to the lower right as far as possible, and then * moving the window toward the upper left if necessary. */ #define W_MINWIDTH 200 #define W_MINHEIGHT 60 extern char version[]; /* Prototypes for functions defined in io.c */ static struct ConCom *OpenConsole __PROTO((struct Window *window)); static void CloseConsole __PROTO((struct ConCom *tty)); static void StartTtyTimer __PROTO((void)); static void StartConRead __PROTO((struct ConCom *tty)); static int ConGetC __PROTO((struct ConCom *tty)); static int ConLookC __PROTO((struct ConCom *tty)); static void ConWrite __PROTO((struct ConCom *tty, char *data, int length)); /****************************************************************/ /* Code to open a console on an existing Intuition window. See RKM Libraries & Devices, pp 277 ff */ #include #include extern struct GfxBase *GfxBase; extern struct IOStdReq * CreateStdIO(); extern struct MsgPort *CreatePort(); struct ConCom { struct IOStdReq *ConWriteReq; /* I/O write request */ struct IOStdReq *ConReadReq; /* I/O read request */ struct MsgPort *RdReplyPort; /* pointer to ReplyPort for console read */ struct timerequest *TimerMsg; /* If != NULL, timer is open */ }; static struct ConCom *OpenConsole ( struct Window *window ); static void CloseConsole ( struct ConCom *tty ); static void StartConRead ( struct ConCom *tty ); static int ConGetC ( struct ConCom *tty ); static int ConLookC ( struct ConCom *tty ); static void ConWrite ( struct ConCom *c, char *data, int length ); static struct ConCom *OpenConsole (struct Window *window) { struct ConCom *tty; struct MsgPort *WrReplyPort, *TimerPort; if ( tty = (struct ConCom *) AllocMem(sizeof(struct ConCom), MEMF_CLEAR | MEMF_PUBLIC) ) { if ( (WrReplyPort = CreatePort(0, 0)) /* reply port for write */ && (tty->RdReplyPort = CreatePort(0, 0)) /* reply port for read */ && (tty->ConWriteReq = CreateStdIO ( WrReplyPort )) && (tty->ConReadReq = CreateStdIO ( tty->RdReplyPort)) ) { tty->ConWriteReq->io_Data = (APTR) window; tty->ConWriteReq->io_Length = sizeof(struct Window); if ( !(OpenDevice ( "console.device", 0, tty->ConWriteReq, 0 )) ) { tty->ConReadReq->io_Device = tty->ConWriteReq->io_Device; tty->ConReadReq->io_Unit = tty->ConWriteReq->io_Unit; /* Try to set up regular interrupts for ^C check */ TimerPort = CreatePort(NULL, 0); if ( TimerPort && ( tty->TimerMsg = (struct timerequest *) CreateExtIO(TimerPort, sizeof(struct timerequest)) ) && !OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *)(tty->TimerMsg), 0) ) return tty; /* All ok! */ /* Main console ports ok, but no timer. We'll live without it... */ if ( TimerPort ) DeletePort ( TimerPort ); if ( tty->TimerMsg ) DeleteExtIO( (struct IORequest *)(tty->TimerMsg), (long)sizeof(struct timerequest) ); tty->TimerMsg = NULL; return tty; } } /* If get here, something went wrong */ if ( tty->ConReadReq ) DeleteStdIO(tty->ConReadReq); if ( tty->RdReplyPort ) DeletePort(tty->RdReplyPort); if ( tty->ConWriteReq ) DeleteStdIO(tty->ConWriteReq); if ( WrReplyPort ) DeletePort(WrReplyPort); } if ( tty ) FreeMem(tty, sizeof(struct ConCom)); return NULL; } static void CloseConsole (struct ConCom *tty) { struct MsgPort *mp; AbortIO(tty->ConReadReq); /* Abort any read in progress */ CloseDevice(tty->ConWriteReq); /* close console device */ mp = tty->ConWriteReq->io_Message.mn_ReplyPort; DeleteStdIO(tty->ConWriteReq); DeletePort(mp); mp = tty->ConReadReq->io_Message.mn_ReplyPort; DeleteStdIO(tty->ConReadReq); DeletePort(mp); if (tty->TimerMsg) { AbortIO((struct IORequest *)(tty->TimerMsg)); CloseDevice((struct IORequest *)(tty->TimerMsg)); DeletePort ( tty->TimerMsg->tr_node.io_Message.mn_ReplyPort ); DeleteExtIO ( (struct IORequest *)(tty->TimerMsg), (long)sizeof(struct timerequest) ); } FreeMem(tty, sizeof(struct ConCom)); return; } /* Request a timer interrupt in 0.25 seconds. Use this to poll for ^C */ static void StartTtyTimer (void) { if ( !tty->TimerMsg ) return; tty->TimerMsg->tr_node.io_Command = TR_ADDREQUEST; tty->TimerMsg->tr_time.tv_secs = 0; tty->TimerMsg->tr_time.tv_micro = 250000L; SendIO ( (struct IORequest *)(tty->TimerMsg) ); } static char buffer; /* buffer for incoming console character */ /* asynchronous console read request--must be called once before any ConGetC requests */ static void StartConRead (struct ConCom *tty) { struct IOStdReq *conr; conr = tty->ConReadReq; conr->io_Command = CMD_READ; conr->io_Length = 1; conr->io_Data = (APTR) &buffer; SendIO(conr); /* asynchronous posting of a read request */ } /* Get a character from the console. */ static int ConGetC (struct ConCom *tty) { struct MsgPort *mp, *tp; struct IOStdReq *rddata; int temp; ULONG ReadMsgBit, ClockMsgBit, MsgBits; mp = tty->RdReplyPort; /* get the read reply port */ ReadMsgBit = 1 << mp->mp_SigBit; if ( tty->TimerMsg ) { tp = tty->TimerMsg->tr_node.io_Message.mn_ReplyPort; ClockMsgBit = 1 << tp->mp_SigBit; } else ClockMsgBit = 0; rddata = NULL; do /* Wait for a character. Wake up periodically so that ^C works */ { MsgBits = Wait(ReadMsgBit | ClockMsgBit); if ( MsgBits & ReadMsgBit ) rddata = (struct IOStdReq *) GetMsg ( mp ); if ( MsgBits & ClockMsgBit ) if ( GetMsg (tp) ) { StartTtyTimer(); chkabort(); } } while ( !rddata ); /* We've got a character... */ temp = buffer; /* get the character */ StartConRead ( tty ); /* set up next read */ return temp; } /* See if a character is available at the console. If so, get it, else return -1. */ static int ConLookC (struct ConCom *tty) { struct MsgPort *mp; struct IOStdReq *rddata; int temp; mp = tty->RdReplyPort; /* get the read reply port */ rddata = (struct IOStdReq *) GetMsg ( mp ); if ( !rddata ) return -1; /* We've got a character... */ temp = buffer; /* get the character */ StartConRead ( tty ); /* set up next read */ return temp; } /* write a specified number of characters from a buffer to console device */ static void ConWrite (struct ConCom *tty, char *data, int length) { struct IOStdReq *wrdata; wrdata = tty->ConWriteReq; wrdata->io_Command = CMD_WRITE; wrdata->io_Length = length; wrdata->io_Data = (APTR) data; DoIO(wrdata); /* waits until write completes before continuing */ } /****************************************************************/ /* * This routine gets called once, to set up the * terminal channel. */ void ttopen (void) { int wl, wt; /* window upper left corner specification */ static struct Screen __aligned WBScreen, *wbsdata; static int IsV2; static struct NewWindow NewLessWindow = { 0, 0, 640, 200, /* position & size (may be changed) */ -1, -1, /* pens */ 0, /* IDCMP */ WINDOWDEPTH | WINDOWSIZING | WINDOWDRAG | WINDOWCLOSE | ACTIVATE | SMART_REFRESH | NOCAREREFRESH, NULL, NULL, /* Gadgets, Checkmark */ "Less 1.4Z: h for help", NULL, NULL, /* Use WB screen */ W_MINWIDTH, W_MINHEIGHT, -1, -1, WBENCHSCREEN }; if (tty) return; if (GfxBase = /* V2.0 + ? */ (struct GfxBase *)OpenLibrary("graphics.library", 36) ) { CloseLibrary ( GfxBase ); IsV2 = 1; if ( !(wbsdata = LockPubScreen("Workbench")) ) { error ( "Can't find Workbench screen" ); quit(); } } else { IsV2 = 0; if ( !GetScreenData ( &WBScreen, sizeof(struct Screen), WBENCHSCREEN, NULL ) ) { error ( "Can't find Workbench screen" ); quit(); } wbsdata = &WBScreen; } if ( (wl = Wind_Spec[0]) < 0 ) wl += wbsdata->Width; if ( wl < 0 ) wl = 0; if ( wl > wbsdata->Width ) wl = wbsdata->Width - W_MINWIDTH; if ( (wt = Wind_Spec[1]) < 0 ) wt += wbsdata->Height; if ( wt < 0 ) wt = 0; if ( wt > wbsdata->Height ) wt = wbsdata->Height - W_MINHEIGHT; if ( wl < 0 || wt < 0 ) { error ( "Window won't fit on screen" ); quit(); } NewLessWindow.LeftEdge = wl; NewLessWindow.TopEdge = wt; NewLessWindow.Width = Wind_Spec[2]; if ( NewLessWindow.Width <= 0 ) NewLessWindow.Width += wbsdata->Width; if ( NewLessWindow.Width <= 0 ) NewLessWindow.Width = 0; NewLessWindow.Height = Wind_Spec[3]; if ( NewLessWindow.Height <= 0 ) NewLessWindow.Height += wbsdata->Height; if ( NewLessWindow.Height <= 0 ) NewLessWindow.Height = 0; if ( NewLessWindow.Width < W_MINWIDTH ) NewLessWindow.Width = W_MINWIDTH; if ( NewLessWindow.Height < W_MINHEIGHT ) NewLessWindow.Height = W_MINHEIGHT; if ( NewLessWindow.LeftEdge + NewLessWindow.Width > wbsdata->Width ) NewLessWindow.Width = wbsdata->Width - NewLessWindow.LeftEdge; if ( NewLessWindow.TopEdge + NewLessWindow.Height > wbsdata->Height ) NewLessWindow.Height = wbsdata->Height - NewLessWindow.TopEdge; if ( NewLessWindow.Width < W_MINWIDTH ) NewLessWindow.LeftEdge = wbsdata->Width - W_MINWIDTH; if ( NewLessWindow.Height < W_MINHEIGHT ) NewLessWindow.TopEdge = wbsdata->Height - W_MINHEIGHT; if ( NewLessWindow.LeftEdge < 0 || NewLessWindow.TopEdge < 0 ) { error ( "Window won't fit on screen" ); quit(); } if (IsV2) UnlockPubScreen(NULL, wbsdata); if (!(LessWindow = (struct Window *) OpenWindow (&NewLessWindow)) ) { error ( "Can't open Less window" ); quit(); } if (!(tty = OpenConsole(LessWindow))) { error ( "Can't open console device" ); quit(); } StartConRead ( tty ); if ( tty->TimerMsg ) StartTtyTimer(); /* enable report of window resizeing and close gadget */ ConWrite(tty, "\x9b\x31\x32;11{", 7); } /* Request size of window information */ void getrowcol (void) { static unsigned char buf[16], *p; if ( !tty) ttopen(); ConWrite(tty, "\x9b\x30\x20\x71", 4); /* request current window size */ p = buf; while ( (*p = ConGetC(tty)) != 0x9b ) /* nothing */; p++; do { *p = ConGetC(tty); } while ( p < &(buf[15]) && *p++ != '\x72' ); *p = '\0'; /* buf has "En;n;n;n|", where E is 0x9b, n is a decimal number */ p = buf+1; do { if ( p = strchr ( p, ';' ) ) p++; /* skip window position */ else break; if ( p = strchr ( p, ';' ) ) p++; else break; nrow = atoi ( p ); /* window height */ if ( p = strchr ( p, ';' ) ) p++; else break; ncol = atoi ( p ); /* window width */ } while (0); /* just once! */ /* arbitrary data integrity checks: */ if ( nrow < 2 || nrow > 99 || ncol < 10 || ncol > 200 ) { /* Window probably too small for the font chosen. We may not be able to even write an error message in the window! */ MyRequester ( "Screen/Font mismatch" ); quit(); } } /* * This function gets called just * before we go back home to the command interpreter. * On the Amiga it closes up the virtual terminal window. */ void ttclose (void) { if (tty != (struct ConCom *) 0L) { CloseConsole(tty); CloseWindow(LessWindow); } tty = NULL; nrow = 0; } /* * Read a character from the terminal, * performing no editing and doing conditional echo * but interpretting window resize and close gadget reports */ int do_echo = 1; /* echo flag */ int ttgetc (void) { unsigned char c; /* must be unsigned! */ while ( (c = ConGetC(tty)) == '\x9b' ) { switch (c = ConGetC(tty)) { case '1': /* raw input event */ if ( (c = ConGetC(tty)) == '1' ) quit(); /* close gadget */ else if ( c == '2' ) /* window resize */ { while ( (c = ConGetC(tty)) != '|') /* nothing */; winch(); } break; case '?': /* Help key */ if ( (c = ConGetC(tty)) == '~' ) return 'h'; break; case 'A': case 'T': /* Arrow up */ c = 'b'; break; case 'B': case 'S': /* Arrow down */ c = ' '; break; case 'D': /* Arrow left */ c = 'k'; break; case 'C': /* Arrow right */ c = '\r'; break; case ' ': /* Shifted left or right */ if ( (c = ConGetC(tty)) == 'A' ) c = 'u'; else c = 'd'; break; default: continue; } break; } if ( c == 3 ) { sigs |= 01; psignals(); /* no return */ } if (do_echo) ttwrite(&c, 1); return ((int) c); } /* * Check for ^C in the tty window. This routine will throw * away any characters waiting in the tty input buffer. Returns when * there's nothing in the input queue or one of the following has been * recognized: * * Close gadget (exits) * Window resize (resets window and returns) * ^C (sets sigs and returns) */ int chk_sigs (void) { int c; for (;;) { if ( (c = ConLookC(tty)) < 0 ) return sigs; switch ( c ) { case '\x9b': /* raw input event */ if ( (c = ConGetC(tty)) != '1' ) break; /* unexpected raw input */ if ( (c = ConGetC(tty)) == '1' ) quit(); /* close gadget */ else if ( c == '2' ) /* window resize */ { while ( (c = ConGetC(tty)) != '|') /* nothing */; winch(); return sigs; } break; case 3: sigs |= 01; return sigs; } } } /* * Write a buffer of characters to the display. */ void ttwrite (char *buffer, int length) { ConWrite ( tty, buffer, length ); } /* * Write a string to the terminal */ void ttputs (char *s) { ConWrite ( tty, s, strlen(s) ); } /* fake termcap output */ /* This takes the place of the original tputs(x,y,z), using only the first parameter */ void Tputs (char *s) { flush(); if ( s ) ConWrite ( tty, s, strlen(s) ); } /* * We may not have a window: display a message in a requester */ int MyRequester (char *s) { static struct IntuiText __aligned Ok = { AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, AUTOLEFTEDGE, AUTOTOPEDGE, AUTOITEXTFONT, "Ok!", AUTONEXTTEXT }, IMsg = { AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, AUTOLEFTEDGE, AUTOTOPEDGE + 20, /* Hope this is enough for default font */ AUTOITEXTFONT, "", AUTONEXTTEXT }, LessMsg = { AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, AUTOLEFTEDGE, AUTOTOPEDGE, AUTOITEXTFONT, "Less 1.4Z:", &IMsg }; IMsg.IText = s; return (int)AutoRequest(NULL, &LessMsg, &Ok, &Ok, NULL, NULL, 250, 80); }