chiark / gitweb /
c501ab9ed160b845584cd9ac60e468952f1423f2
[sympathy.git] / src / ansi.c
1 /*
2  * ansi.c:
3  *
4  * Copyright (c) 2008 James McKenzie <james@fishsoup.dhs.org>,
5  * All rights reserved.
6  *
7  */
8
9 static char rcsid[] = "$Id$";
10
11 /*
12  * $Log$
13  * Revision 1.16  2008/02/07 13:22:51  james
14  * *** empty log message ***
15  *
16  * Revision 1.15  2008/02/07 13:19:48  james
17  * *** empty log message ***
18  *
19  * Revision 1.14  2008/02/07 12:21:16  james
20  * *** empty log message ***
21  *
22  * Revision 1.13  2008/02/07 12:16:04  james
23  * *** empty log message ***
24  *
25  * Revision 1.12  2008/02/07 11:32:41  james
26  * *** empty log message ***
27  *
28  * Revision 1.11  2008/02/07 11:11:14  staffcvs
29  * *** empty log message ***
30  *
31  * Revision 1.10  2008/02/07 01:02:52  james
32  * *** empty log message ***
33  *
34  * Revision 1.9  2008/02/07 00:43:27  james
35  * *** empty log message ***
36  *
37  * Revision 1.8  2008/02/07 00:39:13  james
38  * *** empty log message ***
39  *
40  * Revision 1.7  2008/02/06 20:26:57  james
41  * *** empty log message ***
42  *
43  * Revision 1.6  2008/02/06 17:53:28  james
44  * *** empty log message ***
45  *
46  * Revision 1.5  2008/02/06 15:53:22  james
47  * *** empty log message ***
48  *
49  * Revision 1.4  2008/02/04 20:23:55  james
50  * *** empty log message ***
51  *
52  * Revision 1.3  2008/02/04 05:45:55  james
53  * ::
54  *
55  * Revision 1.2  2008/02/04 02:05:06  james
56  * *** empty log message ***
57  *
58  * Revision 1.1  2008/02/03 23:31:25  james
59  * *** empty log message ***
60  *
61  */
62 #include "project.h"
63
64 static void
65 set_nonblocking (int fd)
66 {
67   long arg;
68   arg = fcntl (fd, F_GETFL, arg);
69   arg |= O_NONBLOCK;
70   fcntl (fd, F_SETFL, arg);
71 }
72
73 static void
74 set_blocking (int fd)
75 {
76   long arg;
77   arg = fcntl (fd, F_GETFL, arg);
78   arg &= ~O_NONBLOCK;
79   fcntl (fd, F_SETFL, arg);
80 }
81
82
83 int
84 ansi_read (ANSI * a, void *buf, int n)
85 {
86   int red;
87
88   set_nonblocking (a->fd);
89   red = read (a->fd, buf, n);
90
91   if (!red)
92     return -1;
93
94   if ((red == -1) && (errno == EAGAIN))
95     {
96       return 0;
97     }
98
99   return red;
100 }
101
102 void
103 ansi_write (ANSI * a, char *buf, int n)
104 {
105   set_blocking (a->fd);
106   write (a->fd, buf, n);
107 }
108
109 void
110 ansi_getsize (ANSI * a)
111 {
112   struct winsize sz = { 0 };
113   if (ioctl (a->fd, TIOCGWINSZ, &sz))
114     {
115       a->size.x = CRT_COLS;
116       a->size.y = CRT_ROWS;
117     }
118   else
119     {
120       a->size.x = sz.ws_col;
121       a->size.y = sz.ws_row;
122     }
123
124 }
125
126
127 void
128 ansi_move (ANSI * a, CRT_Pos p)
129 {
130   char buf[16];
131   int n;
132   int dx = p.x - a->pos.x;
133   int dy = p.y - a->pos.y;
134
135 //  a->pos.x = ANSI_INVAL;
136
137   if (a->pos.x != ANSI_INVAL)
138     {
139
140       if ((!dx) && (!dy))
141         return;
142
143       if (!dy)
144         {
145           if (dx == 1)
146             {
147               ansi_write (a, "\033[C", 3);
148             }
149           else if (dx == -1)
150             {
151               ansi_write (a, "\033[D", 3);
152             }
153           else
154             {
155               n = snprintf (buf, sizeof (buf), "\033[%dG", p.x + 1);
156               ansi_write (a, buf, n);
157             }
158         }
159       else if (!dx)
160         {
161           if (dy == -1)
162             {
163               ansi_write (a, "\033[A", 3);
164             }
165           else if (dy == 1)
166             {
167               ansi_write (a, "\033[B", 3);
168             }
169           else if (dy < 0)
170             {
171               n = snprintf (buf, sizeof (buf), "\033[%dA", -dy);
172               ansi_write (a, buf, n);
173             }
174           else
175             {
176               n = snprintf (buf, sizeof (buf), "\033[%dB", dy);
177               ansi_write (a, buf, n);
178             }
179         }
180       else if (!p.x)
181         {
182           if (dy == 1)
183             {
184               ansi_write (a, "\033[E", 3);
185             }
186           else if (dy == -1)
187             {
188               ansi_write (a, "\033[F", 3);
189             }
190           else if (dy > 0)
191             {
192               n = snprintf (buf, sizeof (buf), "\033[%dE", dy);
193               ansi_write (a, buf, n);
194             }
195           else
196             {
197               n = snprintf (buf, sizeof (buf), "\033[%dF", -dy);
198               ansi_write (a, buf, n);
199             }
200         }
201       else
202         {
203           n = snprintf (buf, sizeof (buf), "\033[%d;%dH", p.y + 1, p.x + 1);
204           ansi_write (a, buf, n);
205         }
206     }
207   else
208     {
209       n = snprintf (buf, sizeof (buf), "\033[%d;%dH", p.y + 1, p.x + 1);
210       ansi_write (a, buf, n);
211     }
212
213   a->pos = p;
214 }
215
216
217 void
218 ansi_showhide_cursor (ANSI * a, int hide)
219 {
220   if (a->hide_cursor == hide)
221     return;
222
223   if (hide)
224     {
225       ansi_write (a, "\033[?25l", 6);
226     }
227   else
228     {
229       ansi_write (a, "\033[?25h", 6);
230     }
231
232   a->hide_cursor = hide;
233 }
234
235
236 void
237 ansi_force_attr_normal (ANSI * a)
238 {
239   ansi_write (a, "\033[0m", 4);
240   a->attr = CRT_ATTR_NORMAL;
241   a->color = ANSI_INVAL;
242 }
243
244 void
245 ansi_set_color (ANSI * a, int color)
246 {
247   int dif;
248   char buf[16];
249   int i;
250   int fg, bg;
251
252   if ((a->color == ANSI_INVAL) || (color != a->color))
253     {
254       fg = CRT_COLOR_FG (color);
255       bg = CRT_COLOR_BG (color);
256
257       if (fg & CRT_COLOR_INTENSITY)
258         {
259           fg += 90;
260         }
261       else
262         {
263           fg += 30;
264         }
265
266       if (bg & CRT_COLOR_INTENSITY)
267         {
268           bg += 100;
269         }
270       else
271         {
272           bg += 40;
273         }
274
275       i = sprintf (buf, "\033[%d;%dm", fg, bg);
276 #if 0
277       fprintf (stderr, "Color set to %d %d %x\n", fg, bg, color);
278 #endif
279
280       ansi_write (a, buf, i);
281       a->color = color;
282     }
283 }
284
285 void
286 ansi_set_attr (ANSI * a, int attr)
287 {
288   int dif;
289
290   dif = attr ^ a->attr;
291
292   if (!dif)
293     return;
294
295   a->attr = attr;
296
297 #if 0
298   if (attr == CRT_ATTR_NORMAL)
299     {
300       ansi_force_attr_normal (a);
301       return;
302     }
303 #endif
304
305   if (dif & CRT_ATTR_UNDERLINE)
306     {
307       if (attr & CRT_ATTR_UNDERLINE)
308         {
309           ansi_write (a, "\033[4m", 4);
310         }
311       else
312         {
313           ansi_write (a, "\033[24m", 5);
314         }
315     }
316   if (dif & CRT_ATTR_REVERSE)
317     {
318       if (attr & CRT_ATTR_REVERSE)
319         {
320           ansi_write (a, "\033[7m", 4);
321         }
322       else
323         {
324           ansi_write (a, "\033[27m", 5);
325         }
326     }
327   if (dif & CRT_ATTR_BOLD)
328     {
329       if (attr & CRT_ATTR_BOLD)
330         {
331           ansi_write (a, "\033[1m", 4);
332         }
333       else
334         {
335           ansi_write (a, "\033[21m", 5);
336           ansi_write (a, "\033[22m", 5);
337         }
338     }
339
340 }
341
342
343 void
344 ansi_render (ANSI * a, CRT_CA ca)
345 {
346   int dif;
347
348   if (ca.chr < 32)
349     ca.chr = ' ';
350   if (ca.chr > 126)
351     ca.chr = ' ';
352
353   ansi_set_attr (a, ca.attr);
354   ansi_set_color (a, ca.color);
355
356   ansi_write (a, &ca.chr, 1);
357
358   a->pos.x++;
359
360 /*Can't easily wrap round here as don't know size of destination screen*/
361 /*so invalidate the cached cursor position*/
362
363   if (a->pos.x >= a->size.x)
364     a->pos.x = ANSI_INVAL;
365
366 }
367
368 void
369 ansi_cls (ANSI * a)
370 {
371   CRT_Pos p = { 0 };
372
373   crt_cls (&a->crt);
374
375   ansi_force_attr_normal (a);
376   ansi_set_color (a, CRT_COLOR_NORMAL);
377   ansi_move (a, p);
378   ansi_write (a, "\033[2J", 4);
379 /*different emulators leave cursor in different places after cls differently*/
380   a->pos.x = ANSI_INVAL;
381 }
382
383 int
384 ansi_scroll_up (ANSI * a, CRT_Pos s, CRT_Pos e)
385 {
386   char buf[16];
387   int i;
388   if (s.x)
389     return -1;
390   if (e.x != (CRT_COLS - 1))
391     return -1;
392   if (s.y)
393     return -1;
394   if (s.y >= a->size.y)
395     return -1;
396
397   ansi_showhide_cursor (a, 1);
398   i = sprintf (buf, "\033[%d;%dr", s.y + 1, e.y + 1);
399   ansi_write (a, buf, i);
400   i = sprintf (buf, "\033[%d;%dH", e.y + 1, 0);
401   ansi_write (a, buf, i);
402   ansi_write (a, "\033D", 2);
403   ansi_write (a, "\033[r", 3);
404
405   s.y = e.y;
406   crt_erase (&a->crt, s, e, 1);
407
408   a->pos.x = ANSI_INVAL;
409
410   return 0;
411 }
412
413
414 void
415 ansi_spot_scroll_up (ANSI * a, CRT * c)
416 {
417   int l, n, p;
418
419   l = c->sh.e.x - c->sh.s.x;
420   l++;
421   l *= sizeof (CRT_CA);
422
423   n = c->sh.e.y - c->sh.s.y;
424   p = CRT_ADDR_POS (&c->sh.s);
425
426   while (n--)
427     {
428       if (memcmp (&c->screen[p], &a->crt.screen[p + CRT_COLS], l))
429         return;
430       p += CRT_COLS;
431     }
432
433   if (ansi_scroll_up (a, c->sh.s, c->sh.e))
434     return;
435
436   n = c->sh.e.y - c->sh.s.y;
437   p = CRT_ADDR_POS (&c->sh.s);
438
439   while (n--)
440     {
441       memcpy (&a->crt.screen[p], &a->crt.screen[p + CRT_COLS], l);
442       p += CRT_COLS;
443     }
444
445   c->sh.dir = 0;                //FIXME: horrid hack
446
447 }
448
449 void
450 ansi_spot_scroll (ANSI * a, CRT * c)
451 {
452
453   switch (c->sh.dir)
454     {
455     case -1:
456 /*we only care about up for the moment */
457       ansi_spot_scroll_up (a, c);
458       break;
459     }
460
461   return;
462 }
463
464
465
466
467 void
468 ansi_draw (ANSI * a, CRT * c)
469 {
470   CRT_Pos p;
471   int o;
472   int hidden_cursor = 0;
473
474   ansi_spot_scroll (a, c);
475
476   for (p.y = 0; p.y < CRT_ROWS; ++p.y)
477     {
478       if (p.y >= a->size.y)
479         continue;
480       o = CRT_ADDR (p.y, 0);
481       for (p.x = 0; p.x < CRT_COLS; ++p.x, ++o)
482         {
483           if (p.x >= a->size.x)
484             continue;
485           if (crt_ca_cmp (a->crt.screen[o], c->screen[o]))
486             {
487               ansi_showhide_cursor (a, 1);
488               a->crt.screen[o] = c->screen[o];
489
490               ansi_move (a, p);
491               ansi_render (a, a->crt.screen[o]);
492             }
493         }
494     }
495
496
497   if ((CRT_COLS > a->size.x) || (CRT_ROWS > a->size.y))
498     {
499       char msg[] = "Window is too small";
500       p.x = 0;
501       p.y = 0;
502
503       ansi_showhide_cursor (a, 1);
504       ansi_set_attr (a, CRT_ATTR_REVERSE);
505       ansi_set_color (a, CRT_MAKE_COLOR (CRT_COLOR_WHITE, CRT_COLOR_RED));
506       ansi_move (a, p);
507
508       ansi_write (a, msg, sizeof (msg));
509       a->pos.x = ANSI_INVAL;
510     }
511
512
513   if ((c->pos.x >= a->size.x) || (c->pos.y >= a->size.y))
514     {
515       ansi_showhide_cursor (a, 1);
516       return;
517     }
518
519   a->crt.pos = c->pos;
520   ansi_move (a, a->crt.pos);
521
522   a->crt.hide_cursor = c->hide_cursor;
523   ansi_showhide_cursor (a, a->crt.hide_cursor);
524 }
525
526 void
527 ansi_reset (ANSI * a)
528 {
529 // FIXME: -- echos back crap?
530 //  ansi_write (a, "\033[c", 3);
531   ansi_getsize (a);
532
533   a->pos.x = ANSI_INVAL;
534   a->hide_cursor = ANSI_INVAL;
535
536   crt_reset (&a->crt);
537
538   ansi_cls (a);
539   ansi_write (a, "\033=", 2);
540   ansi_write (a, "\033[?6l", 5);
541   ansi_write (a, "\033[r", 3);
542   ansi_draw (a, &a->crt);
543 }
544
545 void
546 ansi_flush_escape (ANSI * a, VT102 * v, TTY * t)
547 {
548   ANSI_Parser *p = &a->parser;
549   int i;
550
551   for (i = 0; i < p->escape_ptr; ++i)
552     {
553       vt102_send (v, p->escape_buf[i], t);
554     }
555
556   p->escape_ptr = 0;
557   p->in_escape = 0;
558 }
559
560 void
561 ansi_parse_deckey (ANSI * a, VT102 * v, TTY * t)
562 {
563   ANSI_Parser *p = &a->parser;
564   if ((p->escape_buf[1] != '[') && (p->escape_buf[1] != 'O'))
565     {
566       ansi_flush_escape (a, v, t);
567       return;
568     }
569
570   if ((p->escape_buf[2] >= 'A') || (p->escape_buf[2] <= 'Z'))
571     {
572       vt102_send (v, KEY_UP + (p->escape_buf[2] - 'A'), t);
573     }
574   else if ((p->escape_buf[2] >= 'a') || (p->escape_buf[2] <= 'z'))
575     {
576       vt102_send (v, KEY_154 + (p->escape_buf[2] - 'a'), t);
577     }
578   else
579     {
580       ansi_flush_escape (a, v, t);
581       return;
582     }
583   p->in_escape = 0;
584   p->escape_ptr = 0;
585 }
586
587 void
588 ansi_parse_ansikey (ANSI * a, VT102 * v, TTY * t)
589 {
590   ANSI_Parser *p = &a->parser;
591
592   if ((p->escape_buf[1] != '[') || (p->escape_buf[3] != '~'))
593     {
594       ansi_flush_escape (a, v, t);
595       return;
596     }
597   if ((p->escape_buf[2] >= '0') || (p->escape_buf[2] <= '9'))
598     {
599       vt102_send (v, KEY_180 + (p->escape_buf[2] - '0'), t);
600     }
601   else
602     {
603       ansi_flush_escape (a, v, t);
604       return;
605     }
606
607   p->in_escape = 0;
608   p->escape_ptr = 0;
609 }
610
611
612
613 void
614 ansi_parse_escape (ANSI * a, VT102 * v, TTY * t)
615 {
616   ANSI_Parser *p = &a->parser;
617   switch (p->escape_ptr)
618     {
619     case 0:
620     case 1:
621       return;
622     case 2:
623       switch (p->escape_buf[1])
624         {
625         case '[':
626         case 'O':
627           break;
628         default:
629           ansi_flush_escape (a, v, t);
630         }
631       break;
632     case 3:
633       switch (p->escape_buf[1])
634         {
635         case 'O':
636           ansi_parse_deckey (a, v, t);
637           break;
638         case '[':
639           if ((p->escape_buf[2] >= 'A') && (p->escape_buf[2] <= 'Z'))
640             ansi_parse_deckey (a, v, t);
641           break;
642         default:
643           ansi_flush_escape (a, v, t);
644         }
645       break;
646     case 4:
647       switch (p->escape_buf[1])
648         {
649         case '[':
650           ansi_parse_ansikey (a, v, t);
651           break;
652         default:
653           ansi_flush_escape (a, v, t);
654         }
655       break;
656     case 5:
657       ansi_flush_escape (a, v, t);
658     }
659 }
660
661
662 void
663 ansi_check_escape (ANSI * a, VT102 * v, TTY * t)
664 {
665   ANSI_Parser *p = &a->parser;
666   struct timeval now, diff;
667   gettimeofday (&now, NULL);
668   timersub (&now, &p->last_escape, &diff);
669
670 #if 0
671   fprintf (stderr, "ie %d tl %d.%06d eb %d\n",
672            p->in_escape, diff.tv_sec, diff.tv_usec, p->escape_ptr);
673 #endif
674
675   if (!p->in_escape)
676     return;
677
678
679   /*Time up? */
680   if (diff.tv_sec || (diff.tv_usec > ANSI_ESCAPE_TIMEOUT))
681     ansi_flush_escape (a, v, t);
682
683 }
684
685
686 void
687 ansi_parse_char (ANSI * a, int c, VT102 * v, TTY * t)
688 {
689   ANSI_Parser *p = &a->parser;
690
691
692 /*See if it's time to flush the escape*/
693   ansi_check_escape (a, v, t);
694
695   if (c == 033)
696     {
697       if (p->in_escape)
698         ansi_flush_escape (a, v, t);
699
700       p->in_escape++;
701       p->escape_ptr = 0;
702       gettimeofday (&p->last_escape, NULL);
703     }
704
705   if (p->in_escape)
706     {
707       p->escape_buf[p->escape_ptr++] = c;
708       ansi_parse_escape (a, v, t);
709     }
710   else
711     {
712       vt102_send (v, c, t);
713     }
714
715 }
716
717 void
718 ansi_parse (ANSI * a, char *buf, int len, VT102 * v, TTY * t)
719 {
720   while (len--)
721     ansi_parse_char (a, *(buf++), v, t);
722 }
723
724 int
725 ansi_dispatch (ANSI * a, VT102 * v, TTY * t)
726 {
727   char buf[1024];
728   int red;
729
730   ansi_check_escape (a, v, t);
731
732
733   red = ansi_read (a, buf, sizeof (buf));
734   if (red <= 0)
735     return red;
736
737 #if 0
738   if (*buf == 3)
739     return -1;
740 #endif
741
742 #if 0
743   if (*buf == 1)
744     return 1;
745 #endif
746
747
748   ansi_parse (a, buf, red, v, t);
749
750   return 0;
751 }