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