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