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