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