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