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