chiark / gitweb /
*** empty log message ***
[sympathy.git] / src / vt102.c
1 /*
2  * vt102.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/06 11:49:47  james
14  * *** empty log message ***
15  *
16  * Revision 1.7  2008/02/06 11:30:37  james
17  * *** empty log message ***
18  *
19  * Revision 1.6  2008/02/05 01:11:46  james
20  * *** empty log message ***
21  *
22  * Revision 1.5  2008/02/04 20:23:55  james
23  * *** empty log message ***
24  *
25  * Revision 1.4  2008/02/04 05:45:55  james
26  * ::
27  *
28  * Revision 1.3  2008/02/04 02:05:06  james
29  * *** empty log message ***
30  *
31  * Revision 1.2  2008/02/04 01:32:39  james
32  * *** empty log message ***
33  *
34  * Revision 1.1  2008/02/03 23:36:41  james
35  * *** empty log message ***
36  *
37  */
38
39
40 /* Termcap he say:
41
42 vt102|dec vt102:\
43         :mi:\
44         :al=\E[L:dc=\E[P:dl=\E[M:ei=\E[4l:im=\E[4h:tc=vt100:
45
46 vt100|vt100-am|dec vt100 (w/advanced video):\
47         :am:bs:ms:xn:xo:\
48         :co#80:it#8:li#24:vt#3:\
49         :DO=\E[%dB:LE=\E[%dD:RA=\E[?7l:RI=\E[%dC:SA=\E[?7h:\
50         :UP=\E[%dA:\
51         :ac=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~:\
52         :ae=^O:as=^N:bl=^G:cb=\E[1K:cd=\E[J:ce=\E[K:cl=\E[H\E[J:\
53         :cm=\E[%i%d;%dH:cr=^M:cs=\E[%i%d;%dr:ct=\E[3g:do=^J:\
54         :eA=\E(B\E)0:ho=\E[H:kb=^H:kd=\EOB:ke=\E[?1l\E>:kl=\EOD:\
55         :kr=\EOC:ks=\E[?1h\E=:ku=\EOA:le=^H:mb=\E[5m:md=\E[1m:\
56         :me=\E[m\017:mr=\E[7m:nd=\E[C:rc=\E8:\
57         :rs=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:sc=\E7:se=\E[m:\
58         :sf=^J:so=\E[7m:sr=\EM:st=\EH:ta=^I:ue=\E[m:up=\E[A:\
59         :us=\E[4m:tc=vt100+fnkeys:
60
61 vt100+fnkeys|dec vt100 numeric keypad:\
62         :k0=\EOy:k5=\EOt:k6=\EOu:k7=\EOv:k8=\EOl:k9=\EOw:k;=\EOx:\
63         :tc=vt100+pfkeys:
64
65 vt100+pfkeys|dec vt100 numeric keypad:\
66         :@8=\EOM:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:tc=vt100+keypad:
67
68 vt100+keypad|dec vt100 numeric keypad no fkeys:\
69         :K1=\EOq:K2=\EOr:K3=\EOs:K4=\EOp:K5=\EOn:
70
71 */
72
73 /*
74 so the parser needs to be able to at least do
75 CTRL-G
76 CTRL-H
77 CTRL-I
78 CTRL-J
79 CTRL-M
80 CTRL-N
81 CTRL-O
82
83 ESC7
84 ESC8
85 ESCH
86 ESCM
87 ESC> 
88
89 ESC[%dA
90 ESC[%dB
91 ESC[%dC
92 ESC[%dD
93 ESC[H
94 ESC[%d;%dH
95 ESC[J
96 ESC[K
97 ESC[1K
98 ESC[L
99 ESC[M
100 ESC[P
101
102 ESC[3g
103 ESC[4h
104 ESC[4l
105 ESC[m 
106 ESC[1m
107 ESC[4m
108 ESC[5m
109 ESC[7m
110 ESC[%d;%dr
111
112 ESC(B
113 ESC)0
114
115 ESC[?3l 
116 ESC[?4l 
117 ESC[?5l
118 ESC[?7h
119 ESC[?7h
120 ESC[?7l
121 ESC[?8h
122
123 */
124
125 #include "project.h"
126
127
128 static inline int
129 csi_ender (int c)
130 {
131   if ((c >= 'a') && (c <= 'z'))
132     return 1;
133   if ((c >= 'A') && (c <= 'Z'))
134     return 1;
135   return 0;
136 }
137
138 static inline int
139 csi_starter (int c)
140 {
141   switch (c)
142     {
143     case '[':
144     case '(':
145     case ')':
146       return 1;
147     }
148   return 0;
149 }
150
151
152 void
153 vt102_cursor_normalize (VT102 * v, int do_wrapscroll, int use_margins)
154 {
155   int wrap = do_wrapscroll ? 1 : 0;
156   int scroll = do_wrapscroll ? 1 : 0;
157   CRT_Pos *top, *bottom;
158
159   if (use_margins)
160     {
161       top = &v->top_margin;
162       bottom = &v->bottom_margin;
163     }
164   else
165     {
166       top = &v->screen_start;
167       bottom = &v->screen_end;
168     }
169
170   if (v->pos.x < top->x)        /*don't wrap backwards */
171     v->pos.x = top->x;
172
173   if (v->pos.x > bottom->x)
174     {
175       if (wrap)
176         {
177           v->pos.x = top->x;
178           v->pos.y++;
179         }
180       else
181         {
182           v->pos.x = bottom->x;
183         }
184     }
185
186   if (v->pos.y < top->y)
187     v->pos.y = top->y;
188
189   if (v->pos.y > bottom->y)
190     {
191       if (scroll)
192         crt_scroll_up (&v->crt, *top, *bottom, 1);
193       v->pos.y = bottom->y;
194     }
195 }
196
197
198 void
199 vt102_cursor_motion (VT102 * v, int x, int y, int wrapscroll)
200 {
201   while (x > 0)
202     {
203       x--;
204       v->pos.x++;
205       vt102_cursor_normalize (v, wrapscroll, 1);
206     }
207
208   while (x < 0)
209     {
210       x++;
211       v->pos.x--;
212       vt102_cursor_normalize (v, wrapscroll, 1);
213     }
214
215   while (y > 0)
216     {
217       y--;
218       v->pos.y++;
219       vt102_cursor_normalize (v, wrapscroll, 1);
220     }
221
222   while (y < 0)
223     {
224       y++;
225       v->pos.y--;
226       vt102_cursor_normalize (v, wrapscroll, 1);
227     }
228 }
229
230 void
231 vt102_delete_from_line (VT102 * v, CRT_Pos p)
232 {
233   int n = v->bottom_margin.x - p.x;
234
235   if (n < 0)
236     return;
237
238   if (n)
239     {
240
241       memmove (&v->crt.screen[CRT_ADDR_POS (&p)],
242                &v->crt.screen[CRT_ADDR_POS (&p) + 1], sizeof (CRT_CA) * n);
243     }
244
245   v->crt.screen[CRT_ADDR (p.y, v->bottom_margin.x)].chr = ' ';
246 /*But not attr due to vt102 bug*/
247 }
248
249 void
250 vt102_insert_into_line (VT102 * v, CRT_Pos p)
251 {
252   int n = v->bottom_margin.x - p.x;
253
254   if (n < 0)
255     return;
256
257   if (n)
258     {
259
260       memmove (&v->crt.screen[CRT_ADDR_POS (&p) + 1],
261                &v->crt.screen[CRT_ADDR_POS (&p) ], sizeof (CRT_CA) * n);
262     }
263
264   v->crt.screen[CRT_ADDR (p.y, v->bottom_margin.x)].chr = ' ';
265   v->crt.screen[CRT_ADDR (p.y, v->bottom_margin.x)].attr = CRT_ATTR_NORMAL;
266 }
267
268 void
269 vt102_change_mode (VT102 * v, int private, char *ns, int set)
270 {
271   int m;
272
273
274   if (*ns)
275     {
276       m = atoi (ns);
277     }
278   else
279     {
280       m = 1;
281     }
282
283   if (m < 0)
284     return;
285   if (m >= VT102_NMODES)
286     return;
287
288   if (private)
289     v->private_modes[m] = set;
290   else
291     v->modes[m] = set;
292
293   fprintf (stderr, "mode set=%d private=%d num=%d\n", set, private, m);
294 }
295
296 void
297 vt102_parse_mode_string (VT102 * v, char *buf, int len)
298 {
299   int private = 0;
300   char last = buf[len - 1];
301   char num[4];
302   int o;
303
304   memset (num, 0, sizeof (num));
305   o = sizeof (num) - 1;
306
307   len--;
308
309   if (*buf == '?')
310     {
311       private++;
312       buf++;
313       len--;
314     }
315
316   if (len < 0)
317     return;
318
319   while (len--)
320     {
321       if (*buf == ';')
322         {
323           vt102_change_mode (v, private, &num[o], last == 'h');
324           memset (num, 0, sizeof (num));
325           o = sizeof (num) - 1;
326           buf++;
327           continue;
328         }
329
330       num[0] = num[1];
331       num[1] = num[2];
332       num[2] = *buf;
333
334       if (o)
335         o--;
336
337       buf++;
338     }
339
340   vt102_change_mode (v, private, &num[o], last == 'h');
341
342 }
343
344 void
345 vt102_parse_esc (VT102 * v, int c)
346 {
347   fprintf (stderr, "ESC 0%o(%c)\n", c, c);
348 }
349
350 void
351 vt102_parse_csi (VT102 * v, char *buf, int len)
352 {
353   char last;
354   char *ptr;
355   char *arg = buf + 1;
356   int narg;
357
358   buf[len] = 0;
359
360   last = buf[len - 1];
361 #if 0
362   buf[len - 1] = 0;
363 #endif
364
365   if (len > 2)
366     {
367       narg = atoi (arg);
368     }
369   else
370     {
371       narg = 1;
372     }
373
374   switch (buf[0])
375     {
376     case '[':
377       switch (last)
378         {
379         case 'A':
380           vt102_cursor_motion (v, 0, -narg, 0);
381           break;
382         case 'B':
383           vt102_cursor_motion (v, 0, narg, 0);
384           break;
385         case 'C':
386           vt102_cursor_motion (v, narg, 0, 0);
387           break;
388         case 'D':
389           vt102_cursor_motion (v, -narg, 0, 0);
390           break;
391         case 'H':
392           v->pos.y = narg - 1;
393
394           ptr = index (arg, ';');
395           if (ptr)
396             v->pos.x = atoi (ptr + 1) - 1;
397           else
398             v->pos.x = 0;
399
400           vt102_cursor_normalize (v, 0, 0);
401           break;
402         case 'J':
403           fprintf (stderr, "OCTOPUS %d\n", narg);
404           switch (narg)
405             {
406             case 1:
407               crt_erase (&v->crt, v->pos, v->screen_end, 1);
408               break;
409             case 2:
410               crt_erase (&v->crt, v->screen_start, v->screen_end, 1);
411               break;
412             }
413           break;
414         case 'K':
415           {
416             CRT_Pos ls = { 0, v->pos.y };
417             CRT_Pos le = { VT102_COLS - 1, v->pos.y };
418             if (len == 2)
419               narg = 0;         /*Different default */
420
421             switch (narg)
422               {
423               case 0:
424                 fprintf (stderr, "FISH %d %d -> %d %d\n",
425                          v->pos.x, v->pos.y, le.x, le.y);
426                 crt_erase (&v->crt, v->pos, le, 1);
427                 break;
428               case 1:
429                 fprintf (stderr, "SOUP %d %d -> %d %d\n",
430                          ls.x, ls.y, v->pos.x, v->pos.y);
431                 crt_erase (&v->crt, ls, v->pos, 1);
432                 break;
433               case 2:
434                 fprintf (stderr, "TREE %d %d -> %d %d\n",
435                          ls.x, ls.y, le.x, le.y);
436                 crt_erase (&v->crt, ls, le, 1);
437                 break;
438               }
439           }
440           break;
441
442         case 'P':
443           while (narg--)
444             vt102_delete_from_line (v, v->pos);
445           break;
446         case 'L':
447           if ((v->pos.y >= v->top_margin.y)
448               && (v->pos.y <= v->bottom_margin.y))
449             {
450               while (narg--)
451                 crt_scroll_down (&v->crt, v->pos, v->bottom_margin, 1);
452             }
453           break;
454
455         case 'M':
456           if ((v->pos.y >= v->top_margin.y)
457               && (v->pos.y <= v->bottom_margin.y))
458             {
459               while (narg--)
460                 crt_scroll_up (&v->crt, v->pos, v->bottom_margin, 0);
461             }
462           break;
463
464         case 'g':
465           fprintf (stderr, "C: CSI %s buf[0]=%c\n", buf, buf[0]);
466           break;
467
468         case 'h':
469         case 'l':
470           fprintf (stderr, "D: CSI %s buf[0]=%c\n", buf, buf[0]);
471           vt102_parse_mode_string (v, &buf[1], len - 1);
472           break;
473
474         case 'm':
475           //horror of horrors parsing the ;
476           break;
477         case 'r':
478           v->top_margin = v->screen_start;
479           v->bottom_margin = v->screen_end;
480
481           if ((len > 2) && (ptr = index (arg, ';')))
482             {
483               ptr++;
484               v->top_margin.y = narg - 1;
485               v->bottom_margin.y = atoi (ptr) - 1;
486             }
487
488           if (v->top_margin.y < v->screen_start.y)
489             v->top_margin.y = v->screen_start.y;
490           if (v->top_margin.y > v->screen_end.y)
491             v->top_margin.y = v->screen_end.y;
492           if (v->bottom_margin.y < v->screen_start.y)
493             v->bottom_margin.y = v->screen_start.y;
494           if (v->bottom_margin.y > v->screen_end.y)
495             v->bottom_margin.y = v->screen_end.y;
496
497           fprintf (stderr, "D: %d %d\n", v->top_margin.y, v->bottom_margin.y);
498
499           v->pos = v->top_margin;
500           break;
501
502         default:
503           fprintf (stderr, "A: CSI %s buf[0]=%c\n", buf, buf[0]);
504         }
505       break;
506     default:
507       fprintf (stderr, "B: CSI %s buf[0]=%c\n", buf, buf[0]);
508     }
509
510
511
512 }
513
514 void
515 vt102_status_line (VT102 * v, char *str)
516 {
517   int i = VT102_COLS;
518   CRT_CA *ca = &v->crt.screen[CRT_ADDR (VT102_STATUS_ROW, 0)];
519
520   while (i--)
521     {
522       ca->attr = CRT_ATTR_REVERSE;
523       ca->chr = *str;
524       if (*str)
525         str++;
526       ca++;
527     }
528
529 }
530
531
532 void
533 vt102_parse_char (VT102 * v, int c)
534 {
535   VT102_parser *p = &v->parser;
536
537 #if 0
538   fprintf (stderr, "%c pc %d %d %d   %d %d\n", (c > 31) ? 32 : c, c,
539            p->in_csi, p->in_escape, v->pos.x, v->pos.y);
540 #endif
541   if (p->in_csi)
542     {
543       p->csi_buf[p->csi_ptr++] = c;
544       if (csi_ender (c) || (p->csi_ptr == VT102_CSI_LEN))
545         {
546           vt102_parse_csi (v, p->csi_buf, p->csi_ptr);
547           p->in_csi = 0;
548         }
549     }
550   else if (p->in_escape)
551     {
552       if (csi_starter (c))
553         {
554           p->csi_ptr = 0;
555           p->csi_buf[p->csi_ptr++] = c;
556           p->in_csi++;
557           p->in_escape = 0;
558         }
559       else
560         {
561           p->in_escape = 0;
562           vt102_parse_esc (v, c);
563         }
564     }
565   else
566     {
567
568       switch (c)
569         {
570          /*NUL*/ case 0:
571          /*SOH*/ case 1:
572          /*STX*/ case 2:
573          /*ETX*/ case 3:
574          /*EOT*/ case 4:
575          /*ENQ*/ case 5:
576          /*ACK*/ case 6:
577          /*BEL*/ case 7:
578           break;
579          /*BS*/ case 8:
580           vt102_cursor_motion (v, -1, 0, 1);
581           break;
582          /*HT*/ case 9:
583           v->pos.x += 8;
584           v->pos.x &= ~7;
585           vt102_cursor_normalize (v, 1, 1);
586           break;
587          /*LF*/ case 10:
588          /*VT*/ case 11:
589          /*FF*/ case 12:
590           vt102_cursor_motion (v, 0, 1, 1);
591           if (v->modes[VT102_MODE_NEWLINE_MODE]) 
592                 v->pos.x = v->top_margin.x;
593           break;
594          /*CR*/ case 13:
595           v->pos.x = v->top_margin.x;
596           break;
597          /*SO*/ case 14:
598          /*SI*/ case 15:
599          /*DLE*/ case 16:
600         /*DC1 */ case 17:
601         /*DC2 */ case 18:
602         /*DC3 */ case 19:
603         /*DC4 */ case 20:
604          /*NAK*/ case 21:
605          /*SYN*/ case 22:
606          /*ETB*/ case 23:
607          /*CAN*/ case 24:
608          /*EM*/ case 25:
609          /*SUB*/ case 26:
610           break;
611          /*ESC*/ case 27:
612           p->in_escape++;
613           return;
614          /*FS*/ case 28:
615          /*GS*/ case 29:
616          /*RS*/ case 30:
617          /*US*/ case 31:
618          /*DEL*/ case 127:
619           break;
620         /*regular character */ default:
621
622           if (v->modes[VT102_MODE_INSERT]) 
623                 vt102_insert_into_line(v,v->pos);
624
625           v->crt.screen[CRT_ADDR_POS (&v->pos)].chr = c;
626           v->crt.screen[CRT_ADDR_POS (&v->pos)].attr = v->attr;
627           vt102_cursor_motion (v, 1, 0, 1);
628         }
629     }
630
631   v->crt.pos = v->pos;
632
633   vt102_status_line (v, "VT102 foo bar baz I'm the urban spaceman baby");
634 }
635
636 void
637 vt102_parser_reset (VT102_parser * p)
638 {
639   p->in_csi = 0;
640   p->in_escape = 0;
641   p->csi_ptr = 0;
642 }
643
644 void
645 vt102_reset (VT102 * v)
646 {
647   VT102_parser *p = &v->parser;
648
649   vt102_parser_reset (p);
650   crt_cls (&v->crt);
651
652
653   v->screen_start.x = 0;
654   v->screen_start.y = 0;
655   v->screen_end.x = VT102_COLS - 1;
656   v->screen_end.y = VT102_ROWS - 1;
657
658   v->top_margin = v->screen_start;
659   v->bottom_margin = v->screen_end;
660
661   v->pos = v->screen_start;
662
663   memset(v->modes,0,VT102_NMODES);
664   memset(v->private_modes,0,VT102_NMODES);
665
666   v->modes[VT102_PRIVATE_MODE_AUTO_WRAP]=1;
667   v->modes[VT102_PRIVATE_MODE_AUTO_REPEAT]=1;
668
669
670 }