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