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