home *** CD-ROM | disk | FTP | other *** search
- /*
- console.c -- Screen and keyboard control routines
-
- Poor Man's Packet (PMP)
- Copyright (c) 1991 by Andrew C. Payne All Rights Reserved.
-
- Permission to use, copy, modify, and distribute this software and its
- documentation without fee for NON-COMMERCIAL AMATEUR RADIO USE ONLY is hereby
- granted, provided that the above copyright notice appear in all copies.
- The author makes no representations about the suitability of this software
- for any purpose. It is provided "as is" without express or implied warranty.
-
- July, 1989
- Andrew C. Payne
-
- 07/30/91 Temporary fix to crashing when scrollback runs out of memory
- */
-
- /* ----- Includes ----- */
- #include <stdio.h>
- #include <stdarg.h>
- #include <string.h>
- #include <conio.h>
- #include <dos.h>
- #include <bios.h>
- #include <alloc.h>
- #include <mem.h>
- #include "pmp.h"
- #include "keys.h"
-
- #define VIDEO 0x10 /* video BIOS interrupt */
-
- /* ----- Local Stuff ----- */
-
- static int orgcursor; /* original cursor */
-
- static int cx,cy; /* saved cursor locations */
-
- struct dtext { /* display text structure */
- byte attr; /* display attribute */
- int len; /* length of text to display */
- struct dtext *next; /* next item in linked list */
- byte data[1]; /* data to display */
- };
-
- static struct dtext *dtext_head; /* head of dtext queue */
- static struct dtext *dtext_tail; /* tail of dtext queue */
-
- static int dtext_mode; /* display text mode */
-
- #define DTEXT_WRITE 1 /* write to screen */
- #define DTEXT_QUEUE 2 /* queue up writes */
-
- static byte *screen_save; /* saved screen */
- static int savex, savey; /* saved cursor location */
-
- static byte saveline[160]; /* saved message line */
-
- struct vscrline { /* virtual screen line */
- struct vscrline *next; /* next in link */
- struct vscrline *prev; /* previous in link */
- int len; /* length in bytes */
- char data[0]; /* line data */
- };
-
- static struct vscrline *vscrfirst, *vscrlast; /* linked list */
- static struct vscrline *vscrstart, *vscrend; /* start and end of current screen */
- static struct vscrline *vscrorg; /* original screen */
-
- /* ----- Low Level Screen Control ----- */
-
- /* v_setctype(start,end)
- Set the cursor size: start and end.
- */
- static void cdecl v_setctype(int s,int e)
- {
- _AH = 1;
- _CH = s;
- _CL = e;
- geninterrupt(VIDEO);
- }
-
- /* cursave()
- Returns an integer value representing the current cursor size.
- */
- int cursave(void)
- {
- int s,e;
-
- _AH = 3;
- _BH = 0;
- geninterrupt(VIDEO);
- s = _CH;
- e = _CL;
-
- return (s << 4) | e;
- }
-
- /* currest(i)
- Given the number returned by 'cursave' above, restores the cursor
- size.
- */
- void currest(int i)
- {
- v_setctype(i >> 4, i & 0xF);
- }
-
- /* curoff()
- Turns the cursor off.
- */
- void cdecl curoff(void)
- {
- v_setctype(0x0f,0x0f);
- }
-
- /* curon()
- Turns the cursor on.
- */
- void cdecl curon(void)
- {
- if(monochrome)
- v_setctype(12,13);
- else
- v_setctype(6,7);
- }
-
- /* v_scroll(n,ulrow,ulcol,lrrow,lrcol,attr)
- Scrolls current display page. Blank lines are filled with attr.
- Positive 'n' scrolls up, negative down.
- */
- static void cdecl v_scroll(int n,int ulrow,int ulcol,int lrrow,int lrcol,int attr)
- {
- int mode,lines;
- static int bpsave;
-
- mode = (n > 0) ? 6 : 7; /* use vars so as not to trash regs */
- lines = abs(n);
-
- bpsave = _BP; /* old BIOS trashes BP */
- _AH = mode;
- _AL = lines;
- _CH = ulrow;
- _CL = ulcol;
- _DH = lrrow;
- _DL = lrcol;
- _BH = attr;
- geninterrupt(VIDEO);
- _BP = bpsave;
- }
-
- /* putstring(x,y,len,attr,string)
- Given an absolute screen position, a buffered length, and a string,
- write string to screen. (FAST!!)
- */
- void putstring(int x, int y, int len, byte attr, char *s)
- {
- byte buf[80*2]; /* buffer for string */
- byte *p;
- int i;
-
- /* fill buffer with screen data */
- p = buf;
- for(i=0; i<len; i++) {
- if(*s)
- *p++ = *s++;
- else
- *p++ = ' '; /* buffer with spaces */
-
- *p++ = attr; /* character attribute */
- }
-
- /* write string to screen */
- puttext(x,y,x+len-1,y,buf);
- }
-
- /* clear_area(line,start,end)
- Clears part of a line.
- */
- void clear_area(int line, int start, int end)
- {
- putstring(start,line,end-start+1,0,"");
- }
-
- /* ----- CRT Entry and Exit ----- */
- /* CRTInit()
- Initialize the screen.
- */
- void CRTInit()
- {
- struct text_info r;
-
- /* Are we monochrome or CGA? */
- gettextinfo(&r);
- monochrome = (r.currmode == MONO);
-
- orgcursor = cursave(); /* save original cursor */
- curoff();
-
- /* set the default screen colors */
- if(monochrome) {
- NormalAttr = CYAN;
- MsgAttr = InvAttr = StatusAttr = 0x70;
- BrightAttr = LIGHTCYAN;
- } else {
- NormalAttr = CYAN;
- MsgAttr = InvAttr = StatusAttr = YELLOW;
- BrightAttr = LIGHTCYAN;
- MsgAttr = InvAttr + 0x80; /* blink */
- }
-
- cx = cy = 1;
- dtext_head = dtext_tail = NULL;
- dtext_mode = DTEXT_WRITE;
- screen_save = NULL;
-
- /* initialize the virtual screen stuff */
- vscrfirst = vscrlast = NULL;
- }
-
- /* CRTExit()
- Close up the screen.
- */
- void CRTExit()
- {
- normal();
- window(1,1,80,25);
- clrscr();
- currest(orgcursor); /* restore cursor */
- }
-
- /* ----- Scrollback stuff ----- */
-
- /* TrimScreen()
- Trims lines off the front of the virtual screen buffer until
- coreleft() is greater than 32K.
-
- This not the ideal way to do this. What is really needed is to have
- the scrollback buffer allocated as one big chunk of memory at
- startup. (acp)
- */
- static void TrimScreen(void)
- {
- struct vscrline *p;
-
- while(vscrfirst != NULL && coreleft() < 32000) {
- p = vscrfirst->next;
- free(vscrfirst);
- vscrfirst = p;
- }
- if(vscrfirst != NULL)
- vscrfirst->prev = NULL;
- }
-
- /* AddLine(n)
- Adds the given line number to the scrollback buffer.
-
- Crude version now, doesn't try to save memory by trimming lines.
- */
- static void AddLine(int n)
- {
- struct vscrline *p;
- byte temp[160]; /* temp space */
- byte *q;
- int len;
-
- TrimScreen(); /* make sure we're not overflowing */
-
- /* get line into temp space, and trim off trailing blanks */
- gettext(1,n,80,n,temp);
- q = temp + 158;
- while(q > temp && q[0] == ' ' && q[1] == NormalAttr)
- q -= 2; /* trim trailing spaces */
-
- /* create line record */
- len = q - temp + 2;
- if((p = malloc(sizeof(struct vscrline) + len)) == NULL)
- OutOfMemory();
- memcpy(p->data,temp,p->len = len);
-
- /* add line record to doubly-linked list */
- if(vscrlast != NULL)
- vscrlast->next = p;
- p->prev = vscrlast;
- p->next = NULL;
- vscrlast = p;
- if(vscrfirst == NULL)
- vscrfirst = p;
- }
-
- /* PutLine(vscrline,n)
- Given a pointer to a virtual line record, puts the contents of
- the record on the line specified.
- */
- static void PutLine(struct vscrline *p, int n)
- {
- puttext(1,n,p->len >> 1,n,p->data);
- }
-
- /* StartScrollback()
- Starts the scrollback mode. Adds the contents of the current screen
- to the scrollback buffer so it can be part of any scrolls.
-
- Returns TRUE if in scrollback mode.
- */
- int StartScrollback(void)
- {
- int i;
-
- if(vscrlast == NULL)
- return FALSE;
-
- vscrorg = vscrstart = vscrlast;
- for(i=1; i<24; i++) /* add rest of screen to buffer */
- AddLine(i);
-
- vscrend = vscrlast;
- SaveScreen(TRUE, FALSE);
- putstring(11,25,1,StatusAttr,"\031");
- return TRUE;
- }
-
- /* EndSrollback()
- Ends the scrollback mode. Restores current screen, and removes
- current screen from buffer.
- */
- void EndScrollback(void)
- {
- struct vscrline *p,*q;
-
- /* remove entries representing current screen from list */
- q = vscrorg->next;
- while(q != NULL) {
- p = q;
- q = q->next;
- free(p);
- }
-
- vscrlast = vscrorg; /* fix up end of list */
- vscrlast->next = NULL;
- RestoreScreen();
- putstring(11,25,1,StatusAttr," ");
- }
-
- /* MoveScrollback(n)
- Moves the scrollback the increment specified. (Negative moves
- backward, positive forward).
-
- Returns TRUE if at the end of the virtual screen.
- */
- int MoveScrollback(int n)
- {
- if(n > 0) {
- while(n-- && vscrend->next != NULL) {
- v_scroll(1,0,0,22,79,NormalAttr);
- PutLine(vscrend->next, 23);
- if(vscrstart != NULL)
- vscrstart = vscrstart->next;
- else
- vscrstart = vscrfirst;
- vscrend = vscrend->next;
- }
- } else {
- while(n++ && vscrstart != NULL) {
- v_scroll(-1,0,0,22,79,NormalAttr);
- PutLine(vscrstart, 1);
- vscrstart = vscrstart->prev;
- vscrend = vscrend->prev;
- }
- }
-
- return (vscrend->next == NULL);
- }
-
- /* WriteScrollback(fname)
- Writes the contents of the scrollback buffer to the file specified.
-
- Error checking (on file writes) could be improved.
-
- Also: if no info has scrolled off the top of the screen, no info
- is written to the file (this needs to be fixed).
- */
- void WriteScrollback(char *fname)
- {
- FILE *outfile;
- struct vscrline *p;
- char temp[85]; /* output line */
- char *q,*r;
- int i;
-
- if(StartScrollback()) { /* add current screen to buffer */
-
- outfile = fopen(fname, "w");
- if(outfile == NULL) {
- uprintf(InvAttr," Error: can't open '%s'\n",fname);
- return;
- }
-
- p = vscrfirst; /* write buffer contents */
- while(p != NULL) {
- q = temp;
- r = p->data;
- i = p->len >> 1; /* extract data */
- while(i--) {
- *q++ = *r;
- r += 2;
- }
- *q++ = '\n';
- *q = '\0';
- fputs(temp, outfile); /* error check this! */
-
- p = p->next;
- }
- fclose(outfile);
- EndScrollback(); /* restore buffer */
- }
- }
-
- /* ----- Upper/Lower Screen Control ----- */
-
- /* CheckScroll()
- Check the 'cy' and scroll the screen if necessary.
- */
- static void CheckScroll(void)
- {
- if(cy > 23) {
- AddLine(1); /* save first screen line */
- v_scroll(1,0,0,22,79,NormalAttr);
- cy = 23;
- }
- }
-
- /* GotoLeft()
- If the cursor is not at the left margin, moves it there.
-
- NOTE: Future project: this routine should queue up calls
- as well in the dtext queue.
- */
- void GotoLeft(void)
- {
- if(dtext_mode == DTEXT_WRITE) {
- if(cx != 1) {
- cx = 1;
- cy++; /* advance row */
- Capture("\n", 1); /* pass along to capture too */
- }
- CheckScroll();
- }
- }
-
- /* GotoXY(x,y)
- Moves upper screen cursor to specified location.
- */
- void GotoXY(int x, int y)
- {
- cx = x;
- cy = y;
- }
-
- /* _uputtext(attr,s,l)
- Given an attribute, string and length. Does a fast screen write to
- the upper screen area.
- */
- static void _uputtext(byte attr, byte *s, int l)
- {
- byte buf[80*2]; /* buffer for string */
- byte *p; /* pointer */
- int x;
-
- /* fill buffer until newline, end of string, or end of screen */
- while(l) {
- p = buf;
- x = cx;
- while(x <= 80 && l && *s != '\n') {
- if(*s == '\t') { /* handle tabs */
- do {
- *p++ = ' ';
- *p++ = attr;
- x++;
- } while(x <= 80 && (x % 8));
- } else {
- *p++ = *s;
- *p++ = attr;
- x++;
- }
- s++;
- l--;
- }
-
- /* put buffer to screen */
- puttext(cx,cy,x-1,cy,buf);
-
- /* update cursor */
- cx = x;
- if(cx > 80 || *s == '\n') {
- cx = 1;
- cy++;
- }
- CheckScroll();
- if(l && *s == '\n') {
- s++;
- l--;
- }
- }
- }
-
- /* uputtext(attr,s,l)
- Given an attribute, string and length. Does a fast screen write to
- the upper screen area. If the screen is not in the write mode, the
- screen write is queued.
-
- Also passes data to catpure file.
- */
- void uputtext(byte attr,byte *s, int l)
- {
- struct dtext *q;
-
- Capture(s, l);
-
- if(dtext_mode == DTEXT_WRITE)
- _uputtext(attr,s,l); /* write text */
- else {
-
- /* add entry to display write queue */
- if((q = malloc(sizeof(struct dtext) + l)) == NULL)
- OutOfMemory();
- q->attr = attr;
- q->len = l;
- memcpy(q->data,s,l);
- q->next = NULL;
- if(dtext_tail != NULL)
- dtext_tail->next = q;
- dtext_tail = q;
- if(dtext_head == NULL)
- dtext_head = q;
- }
- }
-
- /* uputs(attr, s)
- Given a string, does a fast put to the upper screen area with the
- attributes given.
- */
- void uputs(byte attr, char *s)
- {
- int count;
- int len;
- char *p,*q;
-
- /* minor hack: extract beeps */
- p = q = s;
- len = 0;
- count = 0;
- while(*q) {
- if(*p != 7) {
- len++;
- *p++ = *q++;
- } else {
- count++;
- q++;
- }
- }
- if(count)
- StartSound(700, 4*count); /* make some noise */
- uputtext(attr, (byte *) s, len);
- }
-
- /* _uputs(attr, s)
- Given a string, does a fast put to the upper screen area with the
- attributes given.
-
- NOTE: This puts routine calls the _uputtext primive, so screen
- writes are never queued.
- */
- void _uputs(byte attr, char *s)
- {
- _uputtext(attr,(byte *)s, strlen(s));
- }
-
- /* uprintf(attr, format,s)
- Does a printf to the upper screen area, with given attribute.
- */
- void uprintf(byte attr, char *format, ...)
- {
- char s[1000];
- va_list argptr;
-
- va_start(argptr, format);
- vsprintf(s, format, argptr);
- va_end(argptr);
- uputs(attr,s);
- }
-
- /* SaveScreen(f,border)
- Prepares the upper screen for an overwrite. Subsequent display writes
- are queued for later processing. If 'f' is TRUE, then the contents
- of the screen are preserved. If border is true, then the screen is
- cleared and bordered.
- */
- void SaveScreen(int f, int border)
- {
- int i;
- byte buf[80*2];
- byte *p;
-
- dtext_mode = DTEXT_QUEUE;
- if(f) {
- if((screen_save = malloc(80*23*2)) == NULL)
- OutOfMemory();
- gettext(1,1,80,23,screen_save);
- savex = cx;
- savey = cy;
- if(border) {
- p = buf;
- for(i=0; i<80; i++) {
- *p++ = '─';
- *p++ = NormalAttr;
- }
- buf[0] = '┌';
- buf[79*2] = '┐';
- puttext(1,1,80,1,buf);
- buf[0] = '└';
- buf[79*2] = '┘';
- puttext(1,23,80,23,buf);
- p = buf;
- *p++ = '│';
- p++;
- for(i=0; i<78; i++) {
- *p++ = ' ';
- p++;
- }
- *p++ = '│';
- for(i=2; i<23; i++)
- puttext(1,i,80,i,buf);
- }
- }
- }
-
- /* RestoreScreen()
- Restores the upper screen after an overwrite. Any queued display
- writes are now processed.
- */
- void RestoreScreen(void)
- {
- struct dtext *n;
-
- dtext_mode = DTEXT_WRITE;
-
- /* restore screen if previously saved */
- if(screen_save != NULL) {
- puttext(1,1,80,23,screen_save);
- free(screen_save);
- cx = savex;
- cy = savey;
- }
-
- /* process all entries in the dtext queue */
- while(dtext_head != NULL) {
- _uputtext(dtext_head->attr,dtext_head->data,dtext_head->len);
- n = dtext_head->next;
- free(dtext_head);
- dtext_head = n;
- }
- dtext_tail = NULL;
- }
-
- /* CenterTitle(line,t)
- Given a title string, centers the title inversed on the line given.
- */
- void CenterTitle(int line, char *t)
- {
- GotoXY(40 - (strlen(t) >> 1),line);
- _uputs(InvAttr,t);
- }
-
- /* ----- High-Level Screen Control ----- */
-
- /* StatusLine()
- Redraws the Status line on the bottom of the screen.
- */
- void StatusLine()
- {
- char buf[82];
- char *p;
-
- /* get user's callsign */
- p = GetAX25Addr(&MyCall);
- CallLength = strlen(p);
-
- sprintf(buf," PMP %-4s│ %-9s │ ",
- VERSION,p);
- putstring(1,25,80,StatusAttr,buf);
- }
-
- /* ShowTXRX(tx,rx)
- Shows the TX/RX state on the status line.
- */
- void ShowTXRX(int tx, int rx)
- {
- static char *txstr[] = { " ","TX" };
- static char *rxstr[] = { " ","RX" };
-
- putstring(75,25,2,StatusAttr,txstr[tx]);
- putstring(78,25,2,StatusAttr,rxstr[rx]);
- }
-
- /* ----- Link Status Change ----- */
-
- /* NotifyStatus(old,new)
- Notifies the user of the new link status.
-
- Also updates status line to changes in link status.
- */
- void NotifyStatus(int old, int new)
- {
- char *p,*q;
- char s[70];
-
- /* get pointer to destination path */
- p = GetAX25Path(&AX25_Control.header);
-
- /* update status line */
- switch(new) {
- case RECOVERY:
- case CONNECTED:
- sprintf(s,"»» %s",p);
- putstring(13+CallLength,25,40,StatusAttr,s);
- break;
- case DISCONNECTED:
- putstring(13+CallLength,25,40,StatusAttr,"");
- break;
- case SETUP:
- sprintf(s,"?? %s",p);
- putstring(13+CallLength,25,40,StatusAttr,s);
- break;
- case DISCONNECTPEND:
- sprintf(s,"-- %s",p);
- putstring(13+CallLength,25,40,StatusAttr,s);
- break;
- }
-
- /* show change in link status */
- *s = '\0';
- if(new == CONNECTED && old != RECOVERY) {
- sprintf(s,"***** CONNECTED to %s ",p);
- StartSound(300,3);
- } else if(new == DISCONNECTED) {
- switch(AX25_Control.dreason) {
- case DISC_LOCAL:
- q = "";
- break;
- case DISC_REMOTE:
- q = "(remote)";
- break;
- case DISC_TIMEOUT:
- q = "(timeout)";
- break;
- case DISC_BUSY:
- q = "(remote busy)";
- break;
- }
- sprintf(s,"***** DISCONNECTED from %s %s ",p,q);
- StartSound(200,3);
- }
-
- if(*s) /* show string */
- uprintf(InvAttr,"%s\n",s);
- }
-
- /* ----- Message Line ------ */
-
- /* Pause(t)
- Waits the number of BIOS ticks specified or until a key is pressed.
- Calls PeriodicHook() while waiting.
- */
- void Pause(int delay)
- {
- long t;
-
- t = BiosTime() + delay;
-
- while(!keypressed() && t >= BiosTime())
- PeriodicHook();
-
- if(keypressed())
- getkey();
- }
-
- /* Notify(s)
- Flashes a message on the message line.
- */
- void Notify(char *s)
- {
- int cursor;
-
- cursor = cursave();
- putstring(1,24,80,MsgAttr,s);
- Pause(BIOSSEC*2); /* wait */
- clear_area(24,1,80);
- currest(cursor);
- }
-
- /* SaveMessage()
- Saves the contents of the message line.
- */
- void SaveMessage(void)
- {
- gettext(1,24,80,24, saveline);
- }
-
- /* RestoreMessage()
- Restores the contents of the message line.
- */
- void RestoreMessage(void)
- {
- puttext(1,24,80,24,saveline);
- }
-
- /* ----- Edit Line Input ----- */
-
- /* GetInput(prompt,s,len)
- Reads input from the editline into s, (max length 'len')
- Returns TRUE if return was pressed, or FALSE if ESC abort.
- */
- int GetInput(char *prompt, char *s, int len)
- {
- int i;
- char c;
- char *p;
- KEY k;
- int cursor;
-
- /* show the prompt on the bottom screen line */
- cursor = cursave();
- curon(); /* make sure the cursor is on */
- clear_area(24,1,80);
- gotoxy(1,24); /* go to start */
- bright();
- cprintf(prompt);
- p = s;
- i = 0;
-
- /* loop, handling keypresses */
- while(TRUE) {
- PeriodicHook(); /* handle everything else */
- while(k = Nextkey()) {
- if(c = asciicode(k)) {
- switch(c) {
- case '\r': /* CR */
- case 27: /* ESC */
- *p = '\0';
- clear_area(24,1,80);
- currest(cursor);
- return c == '\r';
- case '\b': /* backspace */
- if(i > 0) {
- i--;
- p--;
- cprintf("\b \b");
- }
- break;
- default: /* store character */
- if(i < len) {
- *p++ = c;
- i++;
- bright();
- putch(c);
- }
- break;
- }
- }
- }
- }
- }