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