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