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