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