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