|
|
1.1 root 1: /* $Id: ansi_cio.c,v 1.37 2004/12/22 11:06:46 deuce Exp $ */
2:
3: /****************************************************************************
4: * @format.tab-size 4 (Plain Text/Source Code File Header) *
5: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
6: * *
7: * Copyright 2004 Rob Swindell - http://www.synchro.net/copyright.html *
8: * *
9: * This library is free software; you can redistribute it and/or *
10: * modify it under the terms of the GNU Lesser General Public License *
11: * as published by the Free Software Foundation; either version 2 *
12: * of the License, or (at your option) any later version. *
13: * See the GNU Lesser General Public License for more details: lgpl.txt or *
14: * http://www.fsf.org/copyleft/lesser.html *
15: * *
16: * Anonymous FTP access to the most recent released source is available at *
17: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
18: * *
19: * Anonymous CVS access to the development source and modification history *
20: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
21: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
22: * (just hit return, no password is necessary) *
23: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
24: * *
25: * For Synchronet coding style and modification guidelines, see *
26: * http://www.synchro.net/source.html *
27: * *
28: * You are encouraged to submit any modifications (preferably in Unix diff *
29: * format) via e-mail to [email protected] *
30: * *
31: * Note: If this box doesn't appear square, then you need to fix your tabs. *
32: ****************************************************************************/
33:
34: #include <fcntl.h>
35: #include <stdarg.h>
36: #include <stdlib.h> /* malloc */
37:
38: #include <genwrap.h>
39: #include <threadwrap.h>
40: #include <semwrap.h>
41:
42: #ifdef __unix__
43: #include <termios.h>
44: struct termios tio_default; /* Initial term settings */
45: #endif
46:
47: #include "ciolib.h"
48: #include "ansi_cio.h"
49:
50: #define ANSI_TIMEOUT 500
51:
52: sem_t got_key;
53: sem_t got_input;
54: sem_t used_input;
55: sem_t goahead;
56: sem_t need_key;
57: static BOOL sent_ga=FALSE;
58: WORD ansi_curr_attr=0x07<<8;
59:
60: int ansi_rows=24;
61: int ansi_cols=80;
62: int ansi_got_row=0;
63: int ansi_got_col=0;
64: int ansi_esc_delay=25;
65: int puttext_no_move=0;
66:
67: const int ansi_tabs[10]={9,17,25,33,41,49,57,65,73,80};
68: const int ansi_colours[8]={0,4,2,6,1,5,3,7};
69: static WORD ansi_inch;
70: static unsigned char ansi_raw_inch;
71: WORD *vmem;
72: int ansi_row=0;
73: int ansi_col=0;
74: int force_move=1;
75:
76: /* Control sequence table definitions. */
77: typedef struct
78: {
79: char *pszSequence;
80: int chExtendedKey;
81: } tODKeySequence;
82:
83: #define ANSI_KEY_UP 72<<8
84: #define ANSI_KEY_DOWN 80<<8
85: #define ANSI_KEY_RIGHT 0x4d<<8
86: #define ANSI_KEY_LEFT 0x4b<<8
87: #define ANSI_KEY_HOME 0x47<<8
88: #define ANSI_KEY_END 0x4f<<8
89: #define ANSI_KEY_F1 0x3b<<8
90: #define ANSI_KEY_F2 0x3c<<8
91: #define ANSI_KEY_F3 0x3d<<8
92: #define ANSI_KEY_F4 0x3e<<8
93: #define ANSI_KEY_F5 0x3f<<8
94: #define ANSI_KEY_F6 0x40<<8
95: #define ANSI_KEY_F7 0x41<<8
96: #define ANSI_KEY_F8 0x42<<8
97: #define ANSI_KEY_F9 0x43<<8
98: #define ANSI_KEY_F10 0x44<<8
99: #define ANSI_KEY_PGUP 0x49<<8
100: #define ANSI_KEY_PGDN 0x51<<8
101: #define ANSI_KEY_INSERT 0x52<<8
102: #define ANSI_KEY_DELETE 0x53<<8
103:
104: tODKeySequence aKeySequences[] =
105: {
106: /* VT-52 control sequences. */
107: {"\033A", ANSI_KEY_UP},
108: {"\033B", ANSI_KEY_DOWN},
109: {"\033C", ANSI_KEY_RIGHT},
110: {"\033D", ANSI_KEY_LEFT},
111: {"\033H", ANSI_KEY_HOME},
112: {"\033K", ANSI_KEY_END},
113: {"\033P", ANSI_KEY_F1},
114: {"\033Q", ANSI_KEY_F2},
115: {"\033?w", ANSI_KEY_F3},
116: {"\033?x", ANSI_KEY_F4},
117: {"\033?t", ANSI_KEY_F5},
118: {"\033?u", ANSI_KEY_F6},
119: {"\033?q", ANSI_KEY_F7},
120: {"\033?r", ANSI_KEY_F8},
121: {"\033?p", ANSI_KEY_F9},
122:
123: /* Control sequences common to VT-100/VT-102/VT-220/VT-320/ANSI. */
124: {"\033[A", ANSI_KEY_UP},
125: {"\033[B", ANSI_KEY_DOWN},
126: {"\033[C", ANSI_KEY_RIGHT},
127: {"\033[D", ANSI_KEY_LEFT},
128: {"\033[M", ANSI_KEY_PGUP},
129: {"\033[H\x1b[2J", ANSI_KEY_PGDN},
130: {"\033[H", ANSI_KEY_HOME},
131: {"\033[K", ANSI_KEY_END},
132: {"\033OP", ANSI_KEY_F1},
133: {"\033OQ", ANSI_KEY_F2},
134: {"\033OR", ANSI_KEY_F3},
135: {"\033OS", ANSI_KEY_F4},
136:
137: /* VT-220/VT-320 specific control sequences. */
138: {"\033[17~", ANSI_KEY_F6},
139: {"\033[18~", ANSI_KEY_F7},
140: {"\033[19~", ANSI_KEY_F8},
141: {"\033[20~", ANSI_KEY_F9},
142: {"\033[21~", ANSI_KEY_F10},
143:
144: /* ANSI-specific control sequences. */
145: {"\033[L", ANSI_KEY_HOME},
146: {"\033Ow", ANSI_KEY_F3},
147: {"\033Ox", ANSI_KEY_F4},
148: {"\033Ot", ANSI_KEY_F5},
149: {"\033Ou", ANSI_KEY_F6},
150: {"\033Oq", ANSI_KEY_F7},
151: {"\033Or", ANSI_KEY_F8},
152: {"\033Op", ANSI_KEY_F9},
153:
154: /* PROCOMM-specific control sequences (non-keypad alternatives). */
155: {"\033OA", ANSI_KEY_UP},
156: {"\033OB", ANSI_KEY_DOWN},
157: {"\033OC", ANSI_KEY_RIGHT},
158: {"\033OD", ANSI_KEY_LEFT},
159: {"\033OH", ANSI_KEY_HOME},
160: {"\033OK", ANSI_KEY_END},
161:
162: /* Terminator */
163: {"",0}
164: };
165:
166: void ansi_sendch(char ch)
167: {
168: if(!ch)
169: ch=' ';
170: if(ansi_row<ansi_rows-1 || (ansi_row==ansi_rows-1 && ansi_col<ansi_cols-1)) {
171: ansi_col++;
172: if(ansi_col>=ansi_cols) {
173: ansi_col=0;
174: ansi_row++;
175: if(ansi_row>=ansi_rows) {
176: ansi_col=ansi_cols-1;
177: ansi_row=ansi_rows-1;
178: }
179: }
180: fwrite(&ch,1,1,stdout);
181: if(ch<' ')
182: force_move=1;
183: }
184: }
185:
186: void ansi_sendstr(char *str,int len)
187: {
188: if(len==-1)
189: len=strlen(str);
190: if(len) {
191: fwrite(str,len,1,stdout);
192: }
193: }
194:
195: int ansi_puttext(int sx, int sy, int ex, int ey, void* buf)
196: {
197: int x,y;
198: unsigned char *out;
199: WORD sch;
200: struct text_info ti;
201: int attrib;
202: unsigned char *fill = (unsigned char*)buf;
203:
204: gettextinfo(&ti);
205:
206: if( sx < 1
207: || sy < 1
208: || ex < 1
209: || ey < 1
210: || sx > ti.screenwidth
211: || sy > ti.screenheight
212: || sx > ex
213: || sy > ey
214: || ex > ti.screenwidth
215: || ey > ti.screenheight
216: || fill==NULL)
217: return(0);
218:
219: out=fill;
220: attrib=ti.attribute;
221: for(y=sy-1;y<ey;y++) {
222: for(x=sx-1;x<ex;x++) {
223: sch=*(out++);
224: if(sch==27)
225: sch=' ';
226: if(sch==0)
227: sch=' ';
228: sch |= (*(out++))<<8;
229: if(vmem[y*ansi_cols+x]==sch)
230: continue;
231: vmem[y*ansi_cols+x]=sch;
232: ansi_gotoxy(x+1,y+1);
233: if(attrib!=sch>>8) {
234: textattr(sch>>8);
235: attrib=sch>>8;
236: }
237: ansi_sendch((char)(sch&0xff));
238: }
239: }
240:
241: if(!puttext_no_move)
242: gotoxy(ti.curx,ti.cury);
243: if(attrib!=ti.attribute)
244: textattr(ti.attribute);
245: return(1);
246: }
247:
248: int ansi_gettext(int sx, int sy, int ex, int ey, void* buf)
249: {
250: int x,y;
251: unsigned char *out;
252: WORD sch;
253: struct text_info ti;
254: unsigned char *fill = (unsigned char*)buf;
255:
256: gettextinfo(&ti);
257:
258: if( sx < 1
259: || sy < 1
260: || ex < 1
261: || ey < 1
262: || sx > ti.screenwidth
263: || sy > ti.screenheight
264: || sx > ex
265: || sy > ey
266: || ex > ti.screenwidth
267: || ey > ti.screenheight
268: || fill==NULL)
269: return(0);
270:
271: out=fill;
272: for(y=sy-1;y<ey;y++) {
273: for(x=sx-1;x<ex;x++) {
274: sch=vmem[y*ansi_cols+x];
275: *(out++)=sch & 0xff;
276: *(out++)=sch >> 8;
277: }
278: }
279: return(1);
280: }
281:
282: void ansi_textattr(int attr)
283: {
284: char str[16];
285: int fg,ofg;
286: int bg,obg;
287: int bl,obl;
288: int br,obr;
289: int oa;
290:
291: str[0]=0;
292: if(ansi_curr_attr==attr<<8)
293: return;
294:
295: bl=attr&0x80;
296: bg=(attr>>4)&0x7;
297: fg=attr&0x07;
298: br=attr&0x04;
299:
300: oa=ansi_curr_attr>>8;
301: obl=oa>>7;
302: obg=(oa>>4)&0x7;
303: ofg=oa&0x07;
304: obr=(oa>>3)&0x01;
305:
306: ansi_curr_attr=attr<<8;
307:
308: strcpy(str,"\033[");
309: if(obl!=bl) {
310: if(!bl) {
311: strcat(str,"0;");
312: ofg=7;
313: obg=0;
314: obr=0;
315: }
316: else
317: strcat(str,"5;");
318: }
319: if(br!=obr) {
320: if(br)
321: strcat(str,"1;");
322: else
323: #if 0
324: strcat(str,"2;");
325: #else
326: {
327: strcat(str,"0;");
328: ofg=7;
329: obg=0;
330: }
331: #endif
332: }
333: if(fg!=ofg)
334: sprintf(str+strlen(str),"3%d;",ansi_colours[fg]);
335: if(bg!=obg)
336: sprintf(str+strlen(str),"4%d;",ansi_colours[bg]);
337: str[strlen(str)-1]='m';
338: ansi_sendstr(str,-1);
339: }
340:
341: #if defined(__BORLANDC__)
342: #pragma argsused
343: #endif
344: static void ansi_keyparse(void *par)
345: {
346: int gotesc=0;
347: char seq[64];
348: int ch;
349: int i;
350: char *p;
351: int timeout=0;
352: int timedout=0;
353: int sval;
354: int unknown=0;
355:
356: seq[0]=0;
357: for(;;) {
358: sem_wait(&goahead);
359: if(timedout || unknown) {
360: for(p=seq;*p;p++) {
361: ansi_inch=*p;
362: sem_post(&got_input);
363: sem_wait(&used_input);
364: sem_wait(&goahead);
365: }
366: gotesc=0;
367: timeout=0;
368: seq[0]=0;
369: }
370: if(!timedout)
371: sem_post(&need_key);
372: timedout=0;
373: unknown=0;
374: if(timeout) {
375: if(sem_trywait_block(&got_key,timeout)) {
376: timedout=1;
377: sem_post(&goahead);
378: continue;
379: }
380: }
381: else
382: sem_wait(&got_key);
383:
384: ch=ansi_raw_inch;
385:
386: switch(gotesc) {
387: case 1: /* Escape Sequence */
388: timeout=ANSI_TIMEOUT;
389: seq[strlen(seq)+1]=0;
390: seq[strlen(seq)]=ch;
391: if(strlen(seq)>=sizeof(seq)-2) {
392: gotesc=0;
393: break;
394: }
395: if((ch<'0' || ch>'9') /* End of ESC sequence */
396: && ch!=';'
397: && ch!='?'
398: && (strlen(seq)==2?ch != '[':1)
399: && (strlen(seq)==2?ch != 'O':1)) {
400: unknown=1;
401: gotesc=0;
402: timeout=0;
403: for(i=0;aKeySequences[i].pszSequence[0];i++) {
404: if(!strcmp(seq,aKeySequences[i].pszSequence)) {
405: ansi_inch=aKeySequences[i].chExtendedKey;
406: sem_post(&got_input);
407: /* Two-byte code, need to post twice and wait for one to
408: be received */
409: sem_wait(&used_input);
410: sem_wait(&goahead);
411: sem_post(&got_input);
412: sem_wait(&used_input);
413: unknown=0;
414: seq[0]=0;
415: break;
416: }
417: }
418: if(unknown) {
419: sem_post(&goahead);
420: continue;
421: }
422: }
423: else {
424: /* Need more keys... keep looping */
425: sem_post(&goahead);
426: }
427: break;
428: default:
429: if(ch==27) {
430: seq[0]=27;
431: seq[1]=0;
432: gotesc=1;
433: timeout=ANSI_TIMEOUT;
434: /* Need more keys... keep going... */
435: sem_post(&goahead);
436: break;
437: }
438: if(ch==10) {
439: /* The \n that goes with the prev \r (hopefully) */
440: /* Eat it and keep chuggin' */
441: sem_post(&goahead);
442: break;
443: }
444: ansi_inch=ch;
445: sem_post(&got_input);
446: sem_wait(&used_input);
447: break;
448: }
449: }
450: }
451:
452: #if defined(__BORLANDC__)
453: #pragma argsused
454: #endif
455: static void ansi_keythread(void *params)
456: {
457: int sval=1;
458:
459: _beginthread(ansi_keyparse,1024,NULL);
460:
461: for(;;) {
462: sem_wait(&need_key);
463: /* If you already have a key, don't get another */
464: sem_getvalue(&got_key,&sval);
465: if((!sval) && fread(&ansi_raw_inch,1,1,stdin)==1)
466: sem_post(&got_key);
467: else
468: SLEEP(1);
469: }
470: }
471:
472: int ansi_kbhit(void)
473: {
474: int sval=1;
475:
476: if(!sent_ga) {
477: sem_post(&goahead);
478: sent_ga=TRUE;
479: }
480: sem_getvalue(&got_input,&sval);
481: return(sval);
482: }
483:
484: void ansi_delay(long msec)
485: {
486: SLEEP(msec);
487: }
488:
489: int ansi_wherey(void)
490: {
491: return(ansi_row+1);
492: }
493:
494: int ansi_wherex(void)
495: {
496: return(ansi_col+1);
497: }
498:
499: /* Put the character _c on the screen at the current cursor position.
500: * The special characters return, linefeed, bell, and backspace are handled
501: * properly, as is line wrap and scrolling. The cursor position is updated.
502: */
503: int ansi_putch(int ch)
504: {
505: struct text_info ti;
506: int i;
507: unsigned char buf[2];
508:
509: buf[0]=ch;
510: buf[1]=ansi_curr_attr>>8;
511:
512: gettextinfo(&ti);
513: puttext_no_move=1;
514:
515: switch(ch) {
516: case '\r':
517: gotoxy(1,wherey());
518: break;
519: case '\n':
520: if(wherey()==ti.winbottom-ti.wintop+1)
521: wscroll();
522: else
523: gotoxy(wherex(),wherey()+1);
524: break;
525: case '\b':
526: if(ansi_col>ti.winleft-1) {
527: buf[0]=' ';
528: gotoxy(wherex()-1,wherey());
529: puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
530: }
531: break;
532: case 7: /* Bell */
533: ansi_sendch(7);
534: break;
535: case '\t':
536: for(i=0;i<10;i++) {
537: if(ansi_tabs[i]>ansi_col+1) {
538: while(ansi_col+1<ansi_tabs[i]) {
539: putch(' ');
540: }
541: break;
542: }
543: }
544: if(i==10) {
545: putch('\r');
546: putch('\n');
547: }
548: break;
549: default:
550: if(wherey()==ti.winbottom-ti.wintop+1
551: && wherex()==ti.winright-ti.winleft+1) {
552: gotoxy(1,wherey());
553: puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
554: wscroll();
555: }
556: else {
557: if(wherex()==ti.winright-ti.winleft+1) {
558: gotoxy(1,ti.cury+1);
559: puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
560: }
561: else {
562: puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
563: gotoxy(ti.curx+1,ti.cury);
564: }
565: }
566: break;
567: }
568:
569: puttext_no_move=0;
570: return(ch);
571: }
572:
573: void ansi_gotoxy(int x, int y)
574: {
575: char str[16];
576:
577: if(x < 1
578: || x > ansi_cols
579: || y < 1
580: || y > ansi_rows)
581: return;
582: if(force_move) {
583: force_move=0;
584: sprintf(str,"\033[%d;%dH",y,x);
585: }
586: else {
587: if(x==1 && ansi_col != 0 && ansi_row<ansi_row-1) {
588: ansi_sendch('\r');
589: force_move=0;
590: ansi_col=0;
591: }
592: if(x==ansi_col+1) {
593: if(y==ansi_row+1) {
594: str[0]=0;
595: }
596: else {
597: if(y<ansi_row+1) {
598: if(y==ansi_row)
599: strcpy(str,"\033[A");
600: else
601: sprintf(str,"\033[%dA",ansi_row+1-y);
602: }
603: else {
604: if(y==ansi_row+2)
605: strcpy(str,"\033[B");
606: else
607: sprintf(str,"\033[%dB",y-ansi_row-1);
608: }
609: }
610: }
611: else {
612: if(y==ansi_row+1) {
613: if(x<ansi_col+1) {
614: if(x==ansi_col)
615: strcpy(str,"\033[D");
616: else
617: sprintf(str,"\033[%dD",ansi_col+1-x);
618: }
619: else {
620: if(x==ansi_col+2)
621: strcpy(str,"\033[C");
622: else
623: sprintf(str,"\033[%dC",x-ansi_col-1);
624: }
625: }
626: else {
627: sprintf(str,"\033[%d;%dH",y,x);
628: }
629: }
630: }
631:
632: ansi_sendstr(str,-1);
633: ansi_row=y-1;
634: ansi_col=x-1;
635: }
636:
637: void ansi_gettextinfo(struct text_info *info)
638: {
639: info->currmode=3;
640: info->screenheight=ansi_rows;
641: info->screenwidth=ansi_cols;
642: info->curx=wherex();
643: info->cury=wherey();
644: info->attribute=ansi_curr_attr>>8;
645: }
646:
647: void ansi_setcursortype(int type)
648: {
649: switch(type) {
650: case _NOCURSOR:
651: case _SOLIDCURSOR:
652: default:
653: break;
654: }
655: }
656:
657: int ansi_getch(void)
658: {
659: int ch;
660:
661: if(!sent_ga) {
662: sem_post(&goahead);
663: sent_ga=TRUE;
664: }
665: sem_wait(&got_input);
666: ch=ansi_inch&0xff;
667: ansi_inch=ansi_inch>>8;
668: sem_post(&used_input);
669: sent_ga=FALSE;
670: return(ch);
671: }
672:
673: int ansi_getche(void)
674: {
675: int ch;
676:
677: ch=ansi_getch();
678: if(ch)
679: putch(ch);
680: return(ch);
681: }
682:
683: int ansi_beep(void)
684: {
685: putch(7);
686: return(0);
687: }
688:
689: #if defined(__BORLANDC__)
690: #pragma argsused
691: #endif
692: void ansi_textmode(int mode)
693: {
694: }
695:
696: #ifdef __unix__
697: void ansi_fixterm(void)
698: {
699: tcsetattr(STDIN_FILENO,TCSANOW,&tio_default);
700: }
701: #endif
702:
703: #ifndef ENABLE_EXTENDED_FLAGS
704: #define ENABLE_INSERT_MODE 0x0020
705: #define ENABLE_QUICK_EDIT_MODE 0x0040
706: #define ENABLE_EXTENDED_FLAGS 0x0080
707: #define ENABLE_AUTO_POSITION 0x0100
708: #endif
709:
710: #if defined(__BORLANDC__)
711: #pragma argsused
712: #endif
713: int ansi_initciolib(long inmode)
714: {
715: int i;
716: char *init="\033[0m\033[2J\033[1;1H";
717:
718: #ifdef _WIN32
719: if(isatty(fileno(stdin))) {
720: if(!SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0))
721: return(0);
722:
723: if(!SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), 0))
724: return(0);
725: }
726: setmode(fileno(stdout),_O_BINARY);
727: setmode(fileno(stdin),_O_BINARY);
728: setvbuf(stdout, NULL, _IONBF, 0);
729: #else
730: struct termios tio_raw;
731:
732: if (isatty(STDIN_FILENO)) {
733: tcgetattr(STDIN_FILENO,&tio_default);
734: tio_raw = tio_default;
735: cfmakeraw(&tio_raw);
736: tcsetattr(STDIN_FILENO,TCSANOW,&tio_raw);
737: setvbuf(stdout, NULL, _IONBF, 0);
738: atexit(ansi_fixterm);
739: }
740: #endif
741:
742: sem_init(&got_key,0,0);
743: sem_init(&got_input,0,0);
744: sem_init(&used_input,0,0);
745: sem_init(&goahead,0,0);
746: sem_init(&need_key,0,0);
747:
748: vmem=(WORD *)malloc(ansi_rows*ansi_cols*sizeof(WORD));
749: ansi_sendstr(init,-1);
750: for(i=0;i<ansi_rows*ansi_cols;i++)
751: vmem[i]=0x0720;
752: _beginthread(ansi_keythread,1024,NULL);
753: return(1);
754: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.