1 
2 /*
3  *  curses.c
4  *
5  *  Written by Max Khon <fjoe@iclub.nsu.ru> et al and released to the public
6  *  domain.
7  *
8  *  Screen definitions & routines using [n]curses.
9  */
10 
11 #ifdef USE_CURSES
12 
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <ctype.h>
16 #include <ncurses.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 
22 #include "winsys.h"
23 #include "unused.h"
24 #include "keys.h"
25 #include "readtc.h"
26 #include "specch.h"
27 
28 int color;
29 int vrow, vcol;
30 int cur_start = 0;
31 int cur_end = 0;
32 static unsigned char *allowed_special_characters = NULL;
33 
34 #ifdef UNIX
35 volatile
36 #endif
37 TERM term =
38 {
39     80,
40     24,
41     0
42 };
43 
44 static int tcflags = 0;         /* what we want to extract from termcap */
45 
46 #define EBUFSZ 100
47 static EVT EVent[EBUFSZ];	/* event circular queue */
48 static int ebufin = 0;		/* event in */
49 static int ebufout = 0;		/* event out */
50 
51 static int kbhit(void);
52 
53 static int norefresh = 0;
54 
ttrefresh(void)55 static void ttrefresh(void)
56 {
57     if (!norefresh)
58     {
59         refresh();
60     }
61 }
62 
TTBeginOutput(void)63 void TTBeginOutput(void)
64 {
65     norefresh++;
66 }
67 
TTEndOutput(void)68 void TTEndOutput(void)
69 {
70     if (norefresh)
71     {
72         norefresh--;
73         ttrefresh();
74     }
75 }
76 
77 
78 /* This maps PC colors to monochrome attributes for w/o color support */
79 static int mono_colors[128]=
80 {
81     A_NORMAL,    A_NORMAL,    A_NORMAL,   A_NORMAL,
82     A_NORMAL,    A_NORMAL,    A_NORMAL,   A_NORMAL,
83     A_BOLD,      A_BOLD,      A_BOLD,     A_BOLD,
84     A_BOLD,      A_BOLD,      A_BOLD,     A_BOLD,
85 
86     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
87     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
88     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
89     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
90 
91     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
92     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
93     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
94     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
95 
96     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
97     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
98     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
99     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
100 
101     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
102     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
103     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
104     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
105 
106     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
107     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
108     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
109     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
110 
111     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
112     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
113     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
114     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
115 
116     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
117     A_REVERSE,   A_REVERSE,   A_REVERSE,  A_REVERSE,
118     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
119     A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,  A_BOLD | A_REVERSE,
120 };
121 
TTScolor(unsigned int newcolor)122 int TTScolor(unsigned int newcolor)
123 {
124     int attr = 0;
125 
126     color = newcolor;
127     if (!has_colors())
128     {
129         attr = mono_colors[color & 0x7F];
130     }
131     else
132     {
133 	if (color & 0x08)
134             attr |= A_DIM | A_BOLD;
135 	if (color & 0x80)
136             attr |= A_BLINK;
137 	attr |= COLOR_PAIR(((color & 0x07) | ((color & 0x70) >> 1)));
138     }
139 
140 #ifdef A_ALTCHARSET
141     if (color & F_ALTERNATE)
142     {
143         attr |= A_ALTCHARSET;
144     }
145 #endif
146 
147     attrset(attr);
148     bkgdset(attr & (~A_ALTCHARSET));
149 
150     return 1;
151 }
152 
TTCurSet(int st)153 int TTCurSet(int st)
154 {
155     if (st)
156         curs_set(1);
157     else
158         curs_set(0);
159     return 0;
160 }
161 
TTgotoxy(int row,int col)162 int TTgotoxy(int row, int col)
163 {
164     move(vrow = row, vcol = col);
165     ttrefresh();
166     return 1;
167 }
168 
TTgetxy(int * row,int * col)169 int TTgetxy(int* row, int* col)
170 {
171     *row = vrow;
172     *col = vcol;
173     return 1;
174 }
175 
TTPutChr(unsigned int ch)176 int TTPutChr(unsigned int ch)
177 {
178     move(vrow, vcol);
179     addch(ch);
180     ttrefresh();
181     return 1;
182 }
183 
TTWriteStr(unsigned long * b,int len,int row,int col)184 int TTWriteStr(unsigned long *b, int len, int row, int col)
185 {
186     int oldcol = color;
187     int cut = 0;
188 
189     move(row, col);
190     for (; len; len--, b++)
191     {
192         int ch = (int)  (*b & 0x000000ffUL);
193         int col = (int)((*b & 0xffff0000UL) >> 16);
194         int y, i;
195 
196         /* control chars are written in ^X notation */
197         if (ch < ' ')
198         {
199             switch (ch)
200             {
201             case '\t':
202                 getyx(stdscr, y, i);
203                 i = ((y >> 3) + 1) << 3;
204                 cut += i-y;
205                 len -= i-y;
206                 break;
207 
208             case '\n':
209             case '\r':
210                 len += cut;
211                 cut = 0;
212                 break;
213 
214             case '\b':
215                 if (cut > 0)
216                 {
217                     len++;
218                     cut--;
219                 }
220                 break;
221 
222             default:
223                 len--;
224                 cut++;
225             }
226         }
227 
228         if (color != col)
229             TTScolor(col);
230         addch(ch);
231     }
232 
233     if (color != oldcol)
234         TTScolor(oldcol);
235     move(vrow, vcol);
236 
237     ttrefresh();
238 
239     return 1;
240 }
241 
TTStrWr(unsigned char * s,int row,int col,int len)242 int TTStrWr(unsigned char *s, int row, int col, int len)
243 {
244     int cut = 0;
245     int y,i;
246 
247     if (s == NULL)
248         return 1;
249 
250     if (len < 0)
251         len = strlen((const char *)s);
252 
253     if (len == 0)
254         return 1;
255 
256     move(row, col);
257     for ( ; len; len--, s++)
258     {
259         /* control chars are written in ^X notation */
260         if (*s < ' ')
261         {
262             switch (*s)
263             {
264             case '\t':
265                 getyx(stdscr, y, i);
266                 i = ((y >> 3) + 1) << 3;
267                 cut += i-y;
268                 len -= i-y;
269                 break;
270 
271             case '\n':
272             case '\r':
273                 len += cut;
274                 cut = 0;
275                 break;
276 
277             case '\b':
278                 if (cut > 0)
279                 {
280                     len++;
281                     cut--;
282                 }
283                 break;
284 
285             default:
286                 len--;
287                 cut++;
288             }
289         }
290 
291         addch(*s);
292     }
293 
294     move(vrow, vcol);
295     ttrefresh();
296     return 1;
297 }
298 
TTReadStr(unsigned long * b,int len,int row,int col)299 int TTReadStr(unsigned long *b, int len, int row, int col)
300 {
301     while(len--)
302     {
303         int ch;
304         unsigned long cell;
305 
306         ch = mvinch(row, col);
307         cell = ch & A_CHARTEXT;
308         if (ch & (A_DIM | A_BOLD))
309             cell |= 0x080000;
310         if (ch & A_BLINK)
311             cell |= 0x800000;
312         if (ch & A_ALTCHARSET)
313             cell |= (((unsigned long)F_ALTERNATE) << 16);
314         cell |= (((ch & 0x0700) | ((ch & 0x7000) << 1))) << 8;
315 
316         *b++ = cell;
317         col++;
318     }
319     move(vrow, vcol);
320     ttrefresh();
321     return 1;
322 }
323 
TTScroll(int x1,int y1,int x2,int y2,int lines,int dir)324 int TTScroll(int x1, int y1, int x2, int y2, int lines, int dir)
325 {
326 #if 0
327     /*
328      *  XXX  If you can make this work - mail me
329      *  XXX  (Seems that TTClear doesn't work properly too)
330      */
331     int height = y2-y1+1;
332     WINDOW* win;
333 
334     win = subwin(stdscr, height, x2-x1+1, y1, x1);
335     if (win == NULL)
336         return 0;
337     scrollok(win, TRUE);
338     wscrl(win, dir ? lines : -lines);
339     touchline(stdscr, y1, height);
340     wttrefresh(win);
341     delwin(win);
342 #else
343     int y;
344     int width = x2 - x1 + 1;
345     unsigned long* buf;
346 
347     if (lines <= 0)
348         return 0;
349 
350     buf = malloc(width*sizeof(unsigned long));
351     if (buf == NULL)
352         return 0;
353 
354     if (dir)
355     {
356         for (y = y1+lines; y <= y2; y++)
357         {
358             TTReadStr(buf, width, y, x1);
359             TTWriteStr(buf, width, y-lines, x1);
360         }
361 
362         /* fill new lines with spaces */
363         TTClear(x1, y2-lines+1, x2, y2);
364     }
365     else
366     {
367         for (y = y2-lines; y >= y1; y--)
368         {
369             TTReadStr(buf, width, y, x1);
370             TTWriteStr(buf, width, y+lines, x1);
371         }
372 
373         /* fill new lines with spaces */
374         TTClear(x1, y1, x2, y1+lines-1);
375     }
376     free(buf);
377     move(vrow, vcol);
378     ttrefresh();
379 #endif
380     return 1;
381 }
382 
TTClear(int x1,int y1,int x2,int y2)383 int TTClear(int x1, int y1, int x2, int y2)
384 {
385 #if 0
386     int height = y2 - y1 + 1;
387     WINDOW* win;
388 
389     win = subwin(stdscr, height, x2 - x1 + 1, y1, x1);
390     if (win == NULL)
391         return 0;
392     werase(win);
393     touchline(stdscr, y1, height);
394     wttrefresh(win);
395     delwin(win);
396 #else
397     int y;
398 
399     for (y = y1; y <= y2; y++)
400     {
401         int x;
402         move(y, x1);
403         for (x = x1; x <= x2; x++)
404             addch(' ');
405     }
406     move(vrow, vcol);
407     ttrefresh();
408 #endif
409     return 1;
410 }
411 
TTEeol(void)412 int TTEeol(void)
413 {
414     move(vrow, vcol);
415     clrtoeol();
416     move(vrow, vcol);
417     ttrefresh();
418     return 1;
419 }
420 
TTdelay(int mil)421 int TTdelay(int mil)
422 {
423     unused(mil);
424     return 0;
425 }
426 
427 static unsigned meta_alphas[] =
428 {
429     Key_A_A, Key_A_B, Key_A_C, Key_A_D, Key_A_E, Key_A_F, Key_A_G,
430     Key_A_H, Key_A_I, Key_A_J, Key_A_K, Key_A_L, Key_A_M, Key_A_N,
431     Key_A_O, Key_A_P, Key_A_Q, Key_A_R, Key_A_S, Key_A_T, Key_A_U,
432     Key_A_V, Key_A_W, Key_A_X, Key_A_Y, Key_A_Z
433 };
434 
435 static unsigned meta_digits[] =
436 {
437     Key_A_0, Key_A_1, Key_A_2, Key_A_3, Key_A_4,
438     Key_A_5, Key_A_6, Key_A_7, Key_A_8, Key_A_9
439 };
440 
441 void TTSendMsg(int msg, int x, int y, int msgtype);
442 
TTGetKey(void)443 unsigned int TTGetKey(void)
444 {
445     int ch;
446 
447     ch = getch();
448     switch (ch)
449     {
450     case KEY_RESIZE:
451         term.NRow = getmaxy(stdscr);
452 	term.NCol = getmaxx(stdscr);
453         TTSendMsg(1, 0, 0, WND_WM_RESIZE);
454 	return -1;
455     case KEY_LEFT:
456         return Key_Lft;
457     case KEY_RIGHT:
458         return Key_Rgt;
459     case KEY_UP:
460         return Key_Up;
461     case KEY_DOWN:
462         return Key_Dwn;
463     case KEY_BACKSPACE:
464         return Key_BS;
465     case KEY_NPAGE:
466         return Key_PgDn;
467     case KEY_PPAGE:
468         return Key_PgUp;
469     case KEY_HOME:
470         return Key_Home;
471     case KEY_END:
472         return Key_End;
473     case KEY_F(1):
474         return Key_F1;
475     case KEY_F(2):
476         return Key_F2;
477     case KEY_F(3):
478         return Key_F3;
479     case KEY_F(4):
480         return Key_F4;
481     case KEY_F(5):
482         return Key_F5;
483     case KEY_F(6):
484         return Key_F6;
485     case KEY_F(7):
486         return Key_F7;
487     case KEY_F(8):
488         return Key_F8;
489     case KEY_F(9):
490         return Key_F9;
491     case KEY_F(10):
492         return Key_F10;
493     case KEY_F(11):
494         return 0x1b; /* DEC mode ... <grin> */
495     case 0x1b:
496         halfdelay(1);
497         ch = getch();
498         nocbreak();
499         cbreak();
500         switch (tolower(ch))
501         {
502         case ERR:
503         case 0x1b:
504             return 0x1b;
505         case 'a':
506             return Key_A_A;
507         case 'b':
508             return Key_A_B;
509         case 'c':
510             return Key_A_C;
511         case 'd':
512             return Key_A_D;
513         case 'e':
514             return Key_A_E;
515         case 'f':
516             return Key_A_F;
517         case 'g':
518             return Key_A_G;
519         case 'h':
520             return Key_A_H;
521         case 'i':
522             return Key_A_I;
523         case 'j':
524             return Key_A_J;
525         case 'k':
526             return Key_A_K;
527         case 'l':
528             return Key_A_L;
529         case 'm':
530             return Key_A_M;
531         case 'n':
532             return Key_A_N;
533         case 'o':
534             return Key_A_O;
535         case 'p':
536             return Key_A_P;
537         case 'q':
538             return Key_A_Q;
539         case 'r':
540             return Key_A_R;
541         case 's':
542             return Key_A_S;
543         case 't':
544             return Key_A_T;
545         case 'u':
546             return Key_A_U;
547         case 'v':
548             return Key_A_V;
549         case 'w':
550             return Key_A_W;
551         case 'x':
552             return Key_A_X;
553         case 'y':
554             return Key_A_Y;
555         case 'z':
556             return Key_A_Z;
557         case '1':
558             return Key_F1;
559         case '2':
560             return Key_F2;
561         case '3':
562             return Key_F3;
563         case '4':
564             return Key_F4;
565         case '5':
566             return Key_F5;
567         case '6':
568             return Key_F6;
569         case '7':
570             return Key_F7;
571         case '8':
572             return Key_F8;
573         case '9':
574             return Key_F9;
575         case '0':
576             return Key_F10;
577         }
578 	break;
579     }
580 
581     if (ch >= 127)    /* Treat special characters */
582     {
583         int assume_meta_key = 1;
584 
585                       /* if the character has not been explicitly */
586                       /* enabled by the user, we check if it is a */
587                       /* Meta keystroke.                          */
588 
589         if (allowed_special_characters != NULL)
590         {
591             if (strchr((char*)allowed_special_characters, ch) != NULL)
592             {
593                 assume_meta_key = 0;
594             }
595         }
596 
597         if (assume_meta_key)
598         {
599             if (ch == 127)
600             {
601                 if (wnd_bs_127)
602                 {
603                     ch = Key_BS;
604                 }
605                 else
606                 {
607                     ch = Key_Del;
608                 }
609             }
610             else if (isalpha(ch - 128))
611             {
612                 ch = meta_alphas[tolower(ch - 128) - 'a'];
613             }
614             else if (isdigit(ch - 128))
615             {
616                 ch = meta_digits[ch - 128 - '0'];
617             }
618         }
619     }
620 
621     return ch;
622 }
623 
TTSendMsg(int msg,int x,int y,int msgtype)624 void TTSendMsg(int msg, int x, int y, int msgtype)
625 {
626     if (((ebufin + 1) % EBUFSZ) != ebufout)
627     {
628         EVent[ebufin].msg = msg;
629         EVent[ebufin].x = x;
630         EVent[ebufin].y = y;
631         EVent[ebufin].msgtype = msgtype;
632         ebufin = (ebufin + 1) % EBUFSZ;
633     }
634 }
635 
TTkopen(void)636 int TTkopen(void)
637 {
638     nonl();
639     noecho();
640     cbreak();
641     nodelay(stdscr, FALSE);
642     keypad(stdscr, TRUE);
643     meta(stdscr, TRUE);
644     intrflush(stdscr, FALSE);
645     raw();
646     query_termcap(tcflags);
647     return 0;
648 }
649 
TTkclose(void)650 int TTkclose(void)
651 {
652     return 0;
653 }
654 
MouseOFF(void)655 void MouseOFF(void)
656 {
657 }
658 
MouseON(void)659 void MouseON(void)
660 {
661 }
662 
MouseInit(void)663 void MouseInit(void)
664 {
665 }
666 
GetMouInfo(int * x,int * y)667 int GetMouInfo(int *x, int *y)
668 {
669     unused(x);
670     unused(y);
671     return 0;
672 }
673 
674 static void
collect_events(void)675 collect_events(void)
676 {
677     int ch = TTGetKey();
678     if (ch < 0)
679 	return;
680 
681     TTSendMsg(ch, 0, 0, WND_WM_CHAR);
682 }
683 
TTGetMsg(EVT * e)684 int TTGetMsg(EVT * e)
685 {
686     while (ebufin == ebufout)
687 	collect_events();
688 
689     e->msg = EVent[ebufout].msg;
690     e->x = EVent[ebufout].x;
691     e->y = EVent[ebufout].y;
692     e->msgtype = EVent[ebufout].msgtype;
693     e->id = 0;
694     ebufout = (ebufout + 1) % EBUFSZ;
695     return e->msg;
696 }
697 
TTPeekQue(void)698 int TTPeekQue(void)
699 {
700     if (kbhit())
701 	collect_events();
702     return ebufin != ebufout;
703 }
704 
TTClearQue(void)705 void TTClearQue(void)
706 {
707     ebufin = ebufout;
708 }
709 
TTGetChr(void)710 int TTGetChr(void)
711 {
712     EVT e;
713     TTGetMsg(&e);
714     return e.msg;
715 }
716 
717 static char ansi2curses[8] = {
718     COLOR_BLACK,
719     COLOR_BLUE,
720     COLOR_GREEN,
721     COLOR_CYAN,
722     COLOR_RED,
723     COLOR_MAGENTA,
724     COLOR_YELLOW,
725     COLOR_WHITE
726 };
727 
TTopen(void)728 int TTopen(void)
729 {
730     int i;
731 
732     initscr();
733 
734     color = 0x07;
735     vrow = vcol = 0;
736     term.NRow = getmaxy(stdscr);
737     term.NCol = getmaxx(stdscr);
738 
739     if (has_colors())
740     {
741         start_color();
742 
743         for (i = 0; i < COLOR_PAIRS; i++)
744             init_pair(i, ansi2curses[i & 0x07],
745                       ansi2curses[(i & 0x38) >> 3]);
746         init_color(COLOR_RED, 0, 0, 1000);
747         init_color(COLOR_BLUE, 1000, 0, 0);
748     }
749 
750     TTkopen();
751     return 1;
752 }
753 
754 /*
755  * Configure the terminal. Configuration is retained even after TTclose.
756  *
757  * The ANSI/VT100 terminal accepts the following configuration keywords:
758  *
759  * keyword        possible values
760  *
761  * highascii      A string of high ascii bytes that, if read from the key-
762  *                board shall be reported verbatim to the calling application
763  *                instead of being interpreted as combination of the Meta key
764  *                with a low ASCII key. You will need to enable high ascii
765  *                alphabet characters (like umlauts or cyrillics) with this.
766  *                An empty string is the deafault.
767  *
768  * pseudographics Not processed here - currently to psg support for curses.
769  *
770  */
771 
TTconfigure(const char * keyword,const char * value)772 int TTconfigure(const char *keyword, const char *value)
773 {
774     size_t l;
775 
776     if (!strcmp(keyword,"highascii"))
777     {
778         if (allowed_special_characters != NULL)
779         {
780             free(allowed_special_characters);
781         }
782         allowed_special_characters =
783             (unsigned char *) malloc(l = (strlen(value) + 1));
784         memcpy(allowed_special_characters, value, l);
785     }
786     else if (!strcmp(keyword,"pseudographics"))
787     {
788         if (atoi(value))
789         {
790             tcflags |= QUERY_ALTCHARSET;
791         }
792         else
793         {
794             tcflags &= ~QUERY_ALTCHARSET;
795         }
796         query_termcap(tcflags);
797     }
798     else
799     {
800         return 0;
801     }
802     return 1;
803 }
804 
TTclose(void)805 int TTclose(void)
806 {
807     TTkclose();
808     endwin();
809     return 1;
810 }
811 
kbhit(void)812 static int kbhit(void)
813 {
814     fd_set select_set;
815     struct timeval timeout;
816     FD_ZERO(&select_set);
817     FD_SET(0, &select_set);
818     timeout.tv_sec = 0;
819     timeout.tv_usec = 0;
820     select(FD_SETSIZE, &select_set, 0, 0, &timeout);
821     return FD_ISSET(0, &select_set);
822 }
823 
dv_running(void)824 int dv_running(void)
825 {
826     return 0;
827 }
828 
829 #endif
830