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