/* By Frank on June 20 - Add parity to all outbound chars using software */ /* Also, ignore DEL (0177) characters on input. */ /* By Bill on May 29 - Add Key set translation */ /* By WBC3 on Apr 24 - Add ^^, ^@ and ^_. Also use Pascal strings for */ /* output in the terminal emulator */ /* By WBC3 on Apr 23 - Add query terminal and be more fastidious about */ /* ignoring sequences we don't know about */ /* By WBC3 on Apr 22 - Fix tab stops to conform to the rest of the world! */ /* By Bill on Apr 21 - Fix immediate echo problems. */ /* do less cursor_erase, cursor_draw stuff */ /* * FILE ckmcon.c * * Module of mackermit: contains code for the terminal simulation * routine. */ #include "ckmdef.h" #include "mac/quickdraw.h" /* Macintosh C interface */ #include "mac/osintf.h" #include "mac/toolintf.h" #include "ckmkkc.h" /* common key configure defs */ #define MAXLIN 24 #define MAXCOL 80 #define LINEHEIGHT 12 #define CHARWIDTH 6 #define TOPMARGIN 3 /* Terminal display constants */ #define BOTTOMMARGIN (LINEHEIGHT * MAXLIN + TOPMARGIN) #define LEFTMARGIN 3 #define RIGHTMARGIN (CHARWIDTH * MAXCOL + LEFTMARGIN) #define LINEADJ 3 /* Amount of char below base line */ /* output character handling */ unsigned char obuf[2] = {1,0}; /* single char output buffer */ /* Tab settings */ #define NUMTABS 9 short tabstops[NUMTABS] = {8,16,24,32,40,48,56,64,72}; int invert=FALSE, /* Flag for inverted terminal mode */ insert=FALSE, topmargin=TOPMARGIN, /* Edges of adjustable window */ bottommargin=BOTTOMMARGIN, textstyle=0, autowrap=TRUE; /* Autowrap on by default */ char *querystring="\033[?6c"; /* Answer we are a VT102 */ short scrollrect[] = { TOPMARGIN, LEFTMARGIN, BOTTOMMARGIN, RIGHTMARGIN }; RgnHandle dumptr; /* Dummy ptr to satisfy scrollbits */ /* Screen book keeping variables */ char scr[MAXLIN][MAXCOL+1]; /* Characters on the screen */ short nxtlin[MAXLIN], toplin, botlin; /* Linked list of lines */ int curlin, curcol, abslin; /* Cursor position */ int savcol, savlin; /* Cursor save variables */ int scrtop, scrbot; /* Absolute scrolling region bounds */ /* Stuff for escape character processing */ #define CF_OUTC 0 /* Just output the char */ #define CF_SESC 1 /* In a single char escape seq */ #define CF_MESC 2 /* In a multi char '[' escape seq */ #define CF_TOSS 3 /* Toss this char */ char prvchr, numone[6], numtwo[6], *numptr; int num1, num2, charflg=CF_OUTC; extern CSParam controlparam; extern unsigned char dopar(); typedef int (*PFI) (); /* Terminal function declarations. */ int tab(), back_space(), carriage_return(), line_feed(), bell(), escape_seq(), text_mode(), clear_line(), erase_display(), cursor_position(), cursor_up(), cursor_down(), cursor_right(), cursor_left(), cursor_save(), cursor_restore(), set_scroll_region(), reverse_line_feed(), dummy(), delete_char(), insert_mode(), end_insert_mode(), insert_line(), delete_line(), query_terminal(), multi_char(), toss_char(); /* Terminal control character function command table. */ #define MINSINGCMDS 000 #define MAXSINGCMDS 037 PFI controltable[MAXSINGCMDS-MINSINGCMDS+1] = { dummy, /* 0 */ dummy, /* 1 */ dummy, /* 2 */ dummy, /* 3 */ dummy, /* 4 */ dummy, /* 5 */ dummy, /* 6 */ bell, /* 7 */ back_space, /* 10 */ tab, /* 11 */ line_feed, /* 12 */ line_feed, /* 13 (Vertical tab) */ line_feed, /* 14 (Form feed) */ carriage_return, /* 15 */ dummy, /* 16 */ dummy, /* 17 */ dummy, /* 20 */ dummy, /* 21 */ dummy, /* 22 */ dummy, /* 23 */ dummy, /* 24 */ dummy, /* 25 */ dummy, /* 26 */ dummy, /* 27 */ dummy, /* 30 */ dummy, /* 31 */ dummy, /* 32 */ escape_seq, /* 33 (Escape) */ dummy, /* 34 */ dummy, /* 35 */ dummy, /* 36 */ dummy /* 37 */ }; #define MINSINGESCS 0040 #define MAXSINGESCS 0137 PFI singescapetable[MAXSINGESCS-MINSINGESCS+1] = { dummy, /* 40 */ dummy, /* 41 */ dummy, /* 42 */ toss_char, /* 43 '#' */ dummy, /* 44 */ dummy, /* 45 */ dummy, /* 46 */ dummy, /* 47 */ toss_char, /* 50 '(' */ toss_char, /* 51 ')' */ dummy, /* 52 */ dummy, /* 53 */ dummy, /* 54 */ dummy, /* 55 */ dummy, /* 56 */ dummy, /* 57 */ dummy, /* 60 */ dummy, /* 61 */ dummy, /* 62 */ dummy, /* 63 */ dummy, /* 64 */ dummy, /* 65 */ dummy, /* 66 */ cursor_save, /* 67 '7' */ cursor_restore, /* 70 '8' */ dummy, /* 71 */ dummy, /* 72 */ dummy, /* 73 */ dummy, /* 74 '<' */ dummy, /* 75 '=' */ dummy, /* 76 '>' */ dummy, /* 77 */ dummy, /* 100 */ dummy, /* 101 */ dummy, /* 102 */ dummy, /* 103 */ line_feed, /* 104 'D' */ dummy, /* 105 'E' */ dummy, /* 106 */ dummy, /* 107 */ dummy, /* 110 */ dummy, /* 111 */ dummy, /* 112 */ dummy, /* 113 */ dummy, /* 114 */ reverse_line_feed, /* 115 'M' */ dummy, /* 116 */ dummy, /* 117 */ dummy, /* 120 */ dummy, /* 121 */ dummy, /* 122 */ dummy, /* 123 */ dummy, /* 124 */ dummy, /* 125 */ dummy, /* 126 */ dummy, /* 127 */ dummy, /* 130 */ dummy, /* 131 */ query_terminal, /* 132 'Z' */ multi_char, /* 133 '[' */ dummy, /* 134 */ dummy, /* 135 */ dummy, /* 136 */ dummy /* 137 */ }; /* Terminal escape sequence function command table */ #define MINMULTESCS 0100 #define MAXMULTESCS 0177 PFI escapetable[MAXMULTESCS-MINMULTESCS+1] = { dummy, /* 100 */ cursor_up, /* 101 'A' */ cursor_down, /* 102 'B' */ cursor_right, /* 103 'C' */ cursor_left, /* 104 'D' */ dummy, /* 105 */ dummy, /* 106 */ dummy, /* 107 */ cursor_position, /* 110 'H' */ dummy, /* 111 */ erase_display, /* 112 'J' */ clear_line, /* 113 'K' */ insert_line, /* 114 'L' */ delete_line, /* 115 'M' */ dummy, /* 116 */ dummy, /* 117 */ delete_char, /* 120 'P' */ dummy, /* 121 */ dummy, /* 122 */ dummy, /* 123 */ dummy, /* 124 */ dummy, /* 125 */ dummy, /* 126 */ dummy, /* 127 */ dummy, /* 130 */ dummy, /* 131 */ dummy, /* 132 */ dummy, /* 133 */ dummy, /* 134 */ dummy, /* 135 */ dummy, /* 136 */ dummy, /* 137 */ dummy, /* 140 */ dummy, /* 141 */ dummy, /* 142 */ query_terminal, /* 143 'c' */ dummy, /* 144 */ dummy, /* 145 */ cursor_position, /* 146 'f' */ dummy, /* 147 */ insert_mode, /* 150 'h' */ dummy, /* 151 */ dummy, /* 152 */ dummy, /* 153 */ end_insert_mode, /* 154 'l' */ text_mode, /* 155 'm' */ dummy, /* 156 */ dummy, /* 157 */ dummy, /* 160 */ dummy, /* 161 */ set_scroll_region, /* 162 'r'*/ dummy, /* 163 */ dummy, /* 164 */ dummy, /* 165 */ dummy, /* 166 */ dummy, /* 167 */ dummy, /* 170 */ dummy, /* 171 */ dummy, /* 172 */ dummy, /* 173 */ dummy, /* 174 */ dummy, /* 175 */ dummy, /* 176 */ dummy /* 177 */ }; /* Connect support routines */ consetup() { dumptr = NewRgn(); PenMode(patXor); flushio(); /* Get rid of pending characters */ init_term(); /* Set up some terminal variables */ clear_screen(); /* Clear the screen */ home_cursor(); /* Go to the upper left */ cursor_save(); /* Save this position */ } /* Input and process all the characters pending on the tty line */ inpchars() { int rdcnt; if ((rdcnt = ttchk()) == 0) return; /* How many chars there? Ret if 0 */ cursor_erase(); /* remove cursor from screen */ while (rdcnt-- > 0) printit(ttinc(0)); /* Output all those characters */ flushbuf(); /* Flush any remaining characters */ cursor_draw(); /* put it back */ } /* writeps - write a pascal form string to the serial port. * */ writeps(s) char *s; { PLONG wcnt, w2; int err; char *s2; w2 = wcnt = *s++; /* get count */ for (s2 = s; w2 > 0; w2--,s2++) /* add parity */ *s2 = dopar(*s2); err = FSWrite(outnum,&wcnt,s); /* write the characters */ if (err != noErr) printerr("Bad FSWrite in writeps: ",err); return; SYM(WRITEPS); } /* getindstr - given an indirect (or is it indexed) string and integer * n, return the pointer to the Nth substring. * * Indirect strings have the count of substrings in the first byte and * each string follows with a length byte and a body. * * Substrings are referenced by 1..N * */ char *getindstr(indstr,n) char *indstr; { register char *ip = indstr; int i; if (n > *ip++) /* too large? */ return(""); /* yes, return empty pascal string */ for (i=1; i < n; i++) /* scan until we hit the Nth */ ip += (*ip)+1; /* move to next substring */ return(ip); /* return ptr to it */ SYM(GETINDSTR); } /* Process a character received from the keyboard */ handle_char(evt) EventRecord *evt; { int code,mods,cidx; register KSET *ks; unsigned char c; code = (evt->message & keyCodeMask) >> 8; mods = (evt->modifiers & MOD_MASK); cidx = (mods & shiftKey) ? /* decide if shifted or */ UC_IDX : LC_IDX; /* unshifted map */ ks = *kshdl; /* de-reference KSET handle */ if (mods & ks->ctrlmods) /* control? */ c = ks->ctrlmap[cidx][code]; /* yes... use control map */ else if (mods & ks->caplmods) /* else caps lock? */ c = ks->caplmap[cidx][code]; /* yes, use that map */ else c = ks->normmap[cidx][code]; /* otherwise use normal map */ if (c & FKEYBIT) { /* is this a function key? */ c &= ~FKEYBIT; /* clear function bit */ switch (c) { /* handle special functions */ case SPFLBRK: /* do long break */ sendbreak(70); /* 70*50MS = 3.5 seconds */ break; case SPFSBRK: /* do short break */ sendbreak(5); /* 5*50MS = 0.25 seconds */ break; default: /* do user defined function */ writeps(getindstr(*(ks->fcnshdl),(int) c+1)); break; } return; /* all done */ } if (mods & ks->metamods) /* want to do meta? */ if (ks->meta8bit) /* yes, want 8 bit on? */ c |= METABIT; /* so, turn it on */ else writeps(*(ks->metahdl)); /* else send prefix */ obuf[1] = c; /* store character */ writeps(obuf); /* and write it out */ if (duplex != 0) { cursor_erase(); /* remove from screen */ printit((char) c); /* Echo the char to the screen */ flushbuf(); /* flush the character */ cursor_draw(); /* put it back */ } return; SYM(HANDLE_CHAR); } char outbuf[MAXCOL+1]; int outcnt=0, outcol; flushbuf() { Rect r; if (outcnt == 0) return; /* Nothing to flush */ /* Erase a hole large enough for outcnt chars */ makerect(&r,abslin,outcol,1,outcnt); if (invert) FillRect(&r,&QD->black); else EraseRect(&r); outbuf[outcnt] = '\0'; /* Terminate the string */ DrawString(outbuf); /* Output the string */ outcnt = 0; /* Say no more chars to output */ } buf_char(c) char c; { if (outcnt == 0) outcol = curcol; /* No chars in buffer, init column */ outbuf[outcnt++] = c; /* Put in the buffer to output later */ } /* * Printit: * Draws character and updates buffer */ printit(c) char c; { PFI funp, lookup(); c &= 0177; switch (charflg) { case CF_OUTC: /* Just output the char */ MDrawChar(c); break; case CF_SESC: /* In a single char escape seq */ charflg = CF_OUTC; /* Reset flag to simple outputting */ if(funp=lookup(c,singescapetable,MINSINGESCS,MAXSINGESCS)) (*funp)(); /* Do escape sequence function */ break; case CF_MESC: /* Multichar escape sequence */ if (c >= 0x20 && c < 0x40) /* Deal with the modifiers */ { if (c >= '<' && c <= '?') prvchr = c; /* Handle priv char */ else if ((c >= '0' && c <= '9') || c == '-' || c == '+') { *numptr++ = c; /* Add the chat to the num */ *numptr = '\0'; /* Terminate the string */ } else if (c == ';') numptr = numtwo; /* Go to next number */ } else /* End of sequence */ { if (funp=lookup(c,escapetable,MINMULTESCS,MAXMULTESCS)) { StringToNum(numone,&num1); /* Translate the numbers */ StringToNum(numtwo,&num2); (*funp)(); /* Do the escape sequence function */ } charflg = CF_OUTC; /* Back to simple outputting */ } break; case CF_TOSS: /* Ignore this char */ charflg = CF_OUTC; /* Reset flag */ break; } } /* * Routine makerect * * Make a rectangle in r starting on line lin and column col extending * numlin lines and numcol characters. * */ makerect(r,lin,col,numlin,numcol) Rect *r; int lin, col, numlin, numcol; { r->top = lin * LINEHEIGHT + TOPMARGIN; r->left = col * CHARWIDTH + LEFTMARGIN; r->bottom = r->top + numlin * LINEHEIGHT; r->right = r->left + numcol * CHARWIDTH; } /* * Lookup: * Lookup a given character in the apropriate character table, and * return a pointer to the appropriate function, if it exists. */ PFI lookup(index,table,min,max) char index; PFI table[]; int min, max; { if (index > max || index < min) return((PFI) NULL); /* Don't index out of range */ return(table[index-min]); } /* * Flushio: * Initialize some communications constants, and clear screen and * character buffers. */ flushio() { int err; err = KillIO(-6); if (err) printerr("Bad input clear",err); err = KillIO(-7); if (err) printerr("Bad ouput clear",err); } /* sendbreak - sends a break across the communictions line. * * The argument is in units of approximately 0.05 seconds (or 50 * milliseconds). To send a break of duration 250 milliseconds the * argument would be 5; a break of duration 3.5 seconds would be (umm, * lets see now) 70. * */ sendbreak(msunit) { int finalticks; /* delay wants 1/60th units. We have 3/60 (50 ms.) units, convert */ msunit = msunit*3; Control(outnum,12,&controlparam); /* Start marking */ /* SerSetBrk(outnum); /* start breaking */ Delay(msunit,&finalticks); /* delay */ /* SerClrBrk(outnum); /* stop breaking */ Control(outnum,11,&controlparam); /* Stop marking */ } MDrawChar(chr) char chr; { PFI funp; /* If it's a control char, do the apropriate function. */ if (chr < ' ') /* Is it a control character */ { flushbuf(); if (funp=lookup(chr,controltable,MINSINGCMDS,MAXSINGCMDS)) (*funp)(); } else if (chr < 0177) /* Handle graphic characters */ { if (curcol >= MAXCOL) /* Are we about to wrap around? */ { if (autowrap) /* If autowrap indicated wrap */ { flushbuf(); carriage_return(); line_feed(); } else { back_space(); /* Otherwise just overwrite */ outcnt--; /* Overwrite buffered chars too */ } } if (insert) /* Insert mode? */ { insert_char(); /* Open hole for char if requested */ erase_char(); /* Erase the old char */ DrawChar(chr); } else buf_char(chr); /* Otherwise just buffer the char */ scr[curlin][curcol++] = chr; } } /* * Control character functions: * Each of the following allow the mac to simulate * the behavior of a terminal when given the proper * control character. */ back_space() { if (curcol > 0) relmove(-1,0); } erase_char() { Rect r; scr[curlin][curcol] = ' '; /* Erase char for update */ makerect(&r,abslin,curcol,1,1); /* One char by one line */ if (invert) FillRect(&r,&QD->black); else EraseRect(&r); } tab() { int i; for (i=0; i curcol) { absmove(tabstops[i],abslin); return; } } } line_feed() { int tbot, ttop, tlout; if (curlin == scrbot) { ScrollRect((Rect *) scrollrect,0,-LINEHEIGHT,dumptr); zeroline(scrtop); tbot = scrbot; ttop = scrtop; tlout = nxtlin[scrbot]; nxtlin[scrbot] = scrtop; scrbot = scrtop; scrtop = nxtlin[scrtop]; if (ttop == toplin) toplin = scrtop; else nxtlin[fndprv(ttop)] = scrtop; if (tbot == botlin) { botlin = scrbot; nxtlin[botlin] = -1; } else nxtlin[scrbot] = tlout; curlin = scrbot; } else relmove(0,1); } reverse_line_feed() { int tbot, ttop, tlout; if (curlin == scrtop) { ScrollRect((Rect *) scrollrect,0,LINEHEIGHT,dumptr); zeroline(scrbot); tbot = scrbot; ttop = scrtop; tlout = nxtlin[scrbot]; nxtlin[scrbot] = scrtop; scrtop = scrbot; scrbot = fndprv(scrbot); if (ttop == toplin) toplin = scrtop; else nxtlin[fndprv(ttop)] = scrtop; if (tbot == botlin) { botlin = scrbot; nxtlin[botlin] = -1; } else nxtlin[scrbot] = tlout; curlin = scrtop; } else relmove(0,-1); } carriage_return() { absmove(0,abslin); } clear_screen() { register int i; Rect r; makerect(&r,0,0,MAXLIN,MAXCOL); /* The whole screen */ EraseRect(&r); for (i=0; i= boldStyle) { TextFace(textstyle-boldStyle); textstyle -= boldStyle; } break; case 24: if (textstyle >= underlineStyle) { TextFace(textstyle-underlineStyle); textstyle -= underlineStyle; } break; case 27: invert = FALSE; TextMode(srcOr); break; } } /**** NYI */ insert_line() { Rect r; if (num1 == 0) num1 = 1; makerect(&r,abslin,0,0,MAXCOL); r.bottom = bottommargin; /* Just do this for now */ if (num1 > fndabs(scrbot) - abslin) num1 = fndabs(scrbot) - abslin; ScrollRect(&r,0,num1*LINEHEIGHT,dumptr); /* Do the book keeping!!! */ } delete_line() { Rect r; if (num1 == 0) num1 = 1; makerect(&r,abslin,0,0,MAXCOL); r.bottom = bottommargin; /* Just do this for now */ if (num1 > fndabs(scrbot) - abslin) num1 = fndabs(scrbot) - abslin; ScrollRect(&r,0,-num1*LINEHEIGHT,dumptr); /* Do the book keeping!!! */ } delete_char() { int i; Rect r; if (num1 == 0) num1 = 1; makerect(&r,abslin,curcol,1,MAXCOL-curcol); if(num1 > MAXCOL - curcol - 1) num1 = MAXCOL - curcol - 1; ScrollRect(&r,-CHARWIDTH*num1,0,dumptr); /* Scroll em out */ /* Shift em down */ for (i=curcol; icurcol+1; i--) scr[abslin][i-1] = scr[abslin][i]; scr[abslin][curcol] = ' '; } insert_mode() { if (prvchr == '?') return; /* Don't deal with these calls */ if (num1 == 4) insert = TRUE; } end_insert_mode() { if (prvchr == '?') return; /* Don't deal with these calls */ if (num1 == 4) insert = FALSE; } query_terminal() { PLONG wrcnt, w2; int err; char *s2; w2 = wrcnt = strlen(querystring); /* How long is the string? */ for (s2 = querystring; w2 > 0; w2--,s2++) /* add parity */ *s2 = dopar(*s2); err = FSWrite(outnum,&wrcnt,querystring); /* Respond to the query */ if (err) printerr("Bad Writeout:",err); } dummy() { } multi_char() { numone[0] = numtwo[0] = '0'; /* Initialize the numbers to zero */ numone[1] = numtwo[1] = '\0'; numptr = numone; /* Place to put the next number */ prvchr = '\0'; /* No priv char yet */ charflg = CF_MESC; /* Say we are in a ESC [ swequence */ } toss_char() { charflg = CF_TOSS; } /* * Routine zeroline * * Zero (set to space) all the characters in relative line lin. * */ zeroline(lin) int lin; { register int i; for (i=0; i