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