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