source: neercs/trunk/src/term.c @ 3977

Last change on this file since 3977 was 3977, checked in by Pascal Terjan, 11 years ago
  • Fix Erase In Line (2) and add missing -1 for the beauty of the code
  • Property svn:keywords set to Id
File size: 33.6 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: term.c 3977 2009-11-20 13:21:15Z pterjan $
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15#include "config.h"
16
17#define _XOPEN_SOURCE
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21#if defined HAVE_PTY_H
22#   include <pty.h>             /* for openpty and forkpty */
23#else
24#   include <util.h>            /* for OS X */
25#endif
26#include <unistd.h>
27#include <fcntl.h>
28
29#include <caca.h>
30#include <caca.h>
31
32#include "neercs.h"
33
34/* DEC ACS with common extensions */
35static uint32_t dec_acs(uint32_t uc)
36{
37    switch (uc)
38    {
39    case '+':
40        return 0x2192;          /* RIGHTWARDS ARROW */
41    case ',':
42        return 0x2190;          /* LEFTWARDS ARROW */
43    case '-':
44        return 0x2191;          /* UPWARDS ARROW */
45    case '.':
46        return 0x2193;          /* DOWNWARDS ARROW */
47    case '0':
48        return 0x25AE;          /* BLACK VERTICAL RECTANGLE */
49    case '_':
50        return 0x25AE;          /* BLACK VERTICAL RECTANGLE */
51    case '`':
52        return 0x25C6;          /* BLACK DIAMOND */
53    case 'a':
54        return 0x2592;          /* MEDIUM SHADE */
55    case 'b':
56        return 0x2409;          /* SYMBOL FOR HORIZONTAL TABULATION */
57    case 'c':
58        return 0x240C;          /* SYMBOL FOR FORM FEED */
59    case 'd':
60        return 0x240D;          /* SYMBOL FOR CARRIAGE RETURN */
61    case 'e':
62        return 0x240A;          /* SYMBOL FOR LINE FEED */
63    case 'f':
64        return 0x00B0;          /* DEGREE SIGN */
65    case 'g':
66        return 0x00B1;          /* PLUS-MINUS SIGN */
67    case 'h':
68        return 0x2424;          /* SYMBOL FOR NEWLINE */
69    case 'i':
70        return 0x240B;          /* SYMBOL FOR VERTICAL TABULATION */
71    case 'j':
72        return 0x2518;          /* BOX DRAWINGS LIGHT UP AND LEFT */
73    case 'k':
74        return 0x2510;          /* BOX DRAWINGS LIGHT DOWN AND LEFT */
75    case 'l':
76        return 0x250C;          /* BOX DRAWINGS LIGHT DOWN AND RIGHT */
77    case 'm':
78        return 0x2514;          /* BOX DRAWINGS LIGHT UP AND RIGHT */
79    case 'n':
80        return 0x253C;          /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
81    case 'o':
82        return 0x23BA;          /* HORIZONTAL SCAN LINE-1 */
83    case 'p':
84        return 0x23BB;          /* HORIZONTAL SCAN LINE-3 */
85    case 'q':
86        return 0x2500;          /* BOX DRAWINGS LIGHT HORIZONTAL */
87    case 'r':
88        return 0x23BC;          /* HORIZONTAL SCAN LINE-7 */
89    case 's':
90        return 0x23BD;          /* HORIZONTAL SCAN LINE-9 */
91    case 't':
92        return 0x251C;          /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
93    case 'u':
94        return 0x2524;          /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
95    case 'v':
96        return 0x2534;          /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
97    case 'w':
98        return 0x252C;          /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
99    case 'x':
100        return 0x2502;          /* BOX DRAWINGS LIGHT VERTICAL */
101    case 'y':
102        return 0x2264;          /* LESS-THAN OR EQUAL TO */
103    case 'z':
104        return 0x2265;          /* GREATER-THAN OR EQUAL TO */
105    case '{':
106        return 0x03C0;          /* GREEK SMALL LETTER PI */
107    case '|':
108        return 0x2260;          /* NOT EQUAL TO */
109    case '}':
110        return 0x00A3;          /* POUND SIGN */
111    case '~':
112        return 0x00B7;          /* MIDDLE DOT */
113    default:
114        return uc;
115    }
116};
117
118static void reset_conv_state(struct screen *);
119
120#define LITERAL2CHAR(i0,i1) (((i0) << 8) | (i1))
121
122#define LITERAL3CHAR(i0,i1,i2) LITERAL2CHAR(LITERAL2CHAR(i0, i1), i2)
123
124static void ansi_parse_grcm(struct screen *,
125                            unsigned int, unsigned int const *);
126
127long int import_term(struct screen_list *screen_list, struct screen *sc,
128                     void const *data, unsigned int size)
129{
130    unsigned char const *buffer = (unsigned char const *)data;
131    unsigned int i, j, k, skip, dummy = 0;
132    unsigned int width, height, top, bottom;
133    uint32_t savedattr;
134    int x = 0, y = 0, save_x = 0, save_y = 0;
135
136    width = caca_get_canvas_width(sc->cv);
137    height = caca_get_canvas_height(sc->cv);
138    x = caca_get_cursor_x(sc->cv);
139    y = caca_get_cursor_y(sc->cv);
140    top = 1;
141    bottom = height;
142
143    if (!sc->init)
144    {
145        sc->dfg = CACA_LIGHTGRAY;
146        sc->dbg = CACA_BLACK;
147
148        caca_set_color_ansi(sc->cv, sc->dfg, sc->dbg);
149        sc->clearattr = caca_get_attr(sc->cv, -1, -1);
150
151        ansi_parse_grcm(sc, 1, &dummy);
152
153        reset_conv_state(sc);
154
155        sc->init = 1;
156    }
157
158    for (i = 0; i < size; i += skip)
159    {
160        uint32_t ch = 0;
161        int wch = 0;
162
163        skip = 1;
164
165        if (buffer[i] == '\r')
166        {
167            x = 0;
168        }
169
170        else if (buffer[i] == '\n')
171        {
172            x = 0;
173            y++;
174        }
175        else if (buffer[i] == '\a')
176        {
177            if (!sc->bell)
178                screen_list->in_bell++;
179            sc->bell = 1;
180        }
181
182        else if (buffer[i] == '\t')
183        {
184            x = (x + 7) & ~7;
185        }
186
187        else if (buffer[i] == '\x08')
188        {
189            if (x > 0)
190                x--;
191        }
192
193        else if (buffer[i] == '\x0e')
194        {
195            /* Shift Out (Ctrl-N) -> Switch to Alternate Character Set:
196               invokes the G1 character set. */
197            sc->conv_state.glr[0] = 1;
198        }
199
200        else if (buffer[i] == '\x0f')
201        {
202            /* Shift In (Ctrl-O) -> Switch to Standard Character Set: invokes
203               the G0 character set. */
204            sc->conv_state.glr[0] = 0;
205        }
206
207        /* If there are not enough characters to parse the escape sequence,
208           wait until the next try. We require 3. */
209        else if (buffer[i] == '\033' && i + 2 >= size)
210            break;
211
212        /* Single Shift Select of G2 Character Set (SS2: 0x8e): affects next
213           character only */
214        else if (buffer[i] == '\033' && buffer[i + 1] == 'N')
215        {
216            sc->conv_state.ss = 2;
217            skip += 1;
218        }
219        /* Reverse Index (RI) go up one line, reverse scroll if necessary */
220        else if (buffer[i] == '\033' && buffer[i + 1] == 'M')
221        {
222            /* FIXME : not sure about the meaning of 'go up one line' and 'if
223               necessary' words. Implemented as a scroller only. */
224            for (j = height; j > 0; j--)
225            {
226                for (k = 0; k < width; k++)
227                {
228                    caca_put_char(sc->cv, k, j,
229                                  caca_get_char(sc->cv, k, j - 1));
230                    caca_put_attr(sc->cv, k, j,
231                                  caca_get_attr(sc->cv, k, j - 1));
232                }
233            }
234            caca_draw_line(sc->cv, 0, 0, width, 0, ' ');
235            skip += 1;
236        }
237
238        /* Single Shift Select of G3 Character Set (SS2: 0x8f): affects next
239           character only */
240        else if (buffer[i] == '\033' && buffer[i + 1] == 'O')
241        {
242            sc->conv_state.ss = 3;
243            skip += 1;
244        }
245
246        /* LOCKING-SHIFT TWO (LS2), ISO 2022, ECMA-48 (1986), ISO 6429 : 1988 */
247        else if (buffer[i] == '\033' && buffer[i + 1] == 'n')
248        {
249            sc->conv_state.glr[0] = 2;
250            skip += 1;
251        }
252
253        /* LOCKING-SHIFT THREE (LS3) ISO 2022, ECMA-48 (1986), ISO 6429 : 1988
254         */
255        else if (buffer[i] == '\033' && buffer[i + 1] == 'o')
256        {
257            sc->conv_state.glr[0] = 3;
258            skip += 1;
259        }
260
261        /* RESET TO INITIAL STATE (RIS), ECMA-48 (1986), ISO 6429 : 1988 */
262        else if (buffer[i] == '\033' && buffer[i + 1] == 'c')
263        {
264            sc->dfg = CACA_DEFAULT;
265            sc->dbg = CACA_DEFAULT;
266
267            caca_set_color_ansi(sc->cv, sc->dfg, sc->dbg);
268            sc->clearattr = caca_get_attr(sc->cv, -1, -1);
269            ansi_parse_grcm(sc, 1, &dummy);
270
271            reset_conv_state(sc);
272            skip += 1;
273        }
274
275        /* Coding Method Delimiter (CMD), ECMA-48 (1991), ISO/IEC 6429:1992
276           (ISO IR 189) */
277        else if (buffer[i] == '\033' && buffer[i + 1] == 'd')
278        {
279            reset_conv_state(sc);
280            skip += 1;
281        }
282
283        /* GZDM4, G0-Designators, multi, 94^n chars [grandfathered short form
284           from ISO 2022:1986] */
285        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
286                 && (buffer[i + 2] >= '@') && (buffer[i + 2] <= 'C'))
287        {
288            sc->conv_state.gn[0] = LITERAL2CHAR('$', buffer[i + 2]);
289            skip += 2;
290        }
291
292        /* GnDMx Gn-Designators, 9x^n chars; need one more char to distinguish
293           these */
294        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
295                 && (i + 3 >= size))
296            break;
297
298        /* GZD4 G0-Designator, 94 chars */
299        else if (buffer[i] == '\033' && buffer[i + 1] == '(')
300        {
301            sc->conv_state.gn[0] = buffer[i + 2];
302            skip += 2;
303        }
304
305        /* G1D4 G1-Designator, 94 chars */
306        else if (buffer[i] == '\033' && buffer[i + 1] == ')')
307        {
308            sc->conv_state.gn[1] = buffer[i + 2];
309            skip += 2;
310        }
311
312        /* G2D4 G2-Designator, 94 chars */
313        else if (buffer[i] == '\033' && buffer[i + 1] == '*')
314        {
315            sc->conv_state.gn[2] = buffer[i + 2];
316            skip += 2;
317        }
318
319        /* G3D4 G3-Designator, 94 chars */
320        else if (buffer[i] == '\033' && buffer[i + 1] == '+')
321        {
322            sc->conv_state.gn[3] = buffer[i + 2];
323            skip += 2;
324        }
325
326        /* G2D6 G2-Designator, 96 chars */
327        else if (buffer[i] == '\033' && buffer[i + 1] == '.')
328        {
329            sc->conv_state.gn[2] = LITERAL2CHAR('.', buffer[i + 2]);
330            skip += 2;
331        }
332
333        /* G3D6 G3-Designator, 96 chars */
334        else if (buffer[i] == '\033' && buffer[i + 1] == '/')
335        {
336            sc->conv_state.gn[3] = LITERAL2CHAR('.', buffer[i + 2]);
337            skip += 2;
338        }
339
340        /* GZDM4 G0-Designator, 94^n chars */
341        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
342                 && buffer[i + 2] == '(')
343        {
344            sc->conv_state.gn[0] = LITERAL2CHAR('$', buffer[i + 3]);
345            skip += 3;
346        }
347
348        /* G1DM4 G1-Designator, 94^n chars */
349        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
350                 && buffer[i + 2] == ')')
351        {
352            sc->conv_state.gn[1] = LITERAL2CHAR('$', buffer[i + 3]);
353            skip += 3;
354        }
355
356        /* G2DM4 G2-Designator, 94^n chars */
357        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
358                 && buffer[i + 2] == '*')
359        {
360            sc->conv_state.gn[2] = LITERAL2CHAR('$', buffer[i + 3]);
361            skip += 3;
362        }
363
364        /* G3DM4 G3-Designator, 94^n chars */
365        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
366                 && buffer[i + 2] == '+')
367        {
368            sc->conv_state.gn[3] = LITERAL2CHAR('$', buffer[i + 3]);
369            skip += 3;
370        }
371
372        /* G2DM6 G2-Designator, 96^n chars */
373        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
374                 && buffer[i + 2] == '.')
375        {
376            sc->conv_state.gn[2] = LITERAL3CHAR('$', '.', buffer[i + 3]);
377            skip += 3;
378        }
379
380        /* G3DM6 G3-Designator, 96^n chars */
381        else if (buffer[i] == '\033' && buffer[i + 1] == '$'
382                 && buffer[i + 2] == '/')
383        {
384            sc->conv_state.gn[3] = LITERAL3CHAR('$', '.', buffer[i + 3]);
385            skip += 3;
386        }
387
388        /* Interpret escape commands, as per Standard ECMA-48 "Control
389           Functions for Coded Character Sets", 5.4. Control sequences. */
390        else if (buffer[i] == '\033' && buffer[i + 1] == '[')
391        {
392            unsigned int argc = 0, argv[101];
393            unsigned int param, inter, final;
394
395
396            /* Compute offsets to parameter bytes, intermediate bytes and to
397               the final byte. Only the final byte is mandatory, there can be
398               zero of the others. 0 param=2 inter final final+1
399               +-----+------------------+---------------------+-----------------+
400               | CSI | parameter bytes | intermediate bytes | final byte | | |
401               0x30 - 0x3f | 0x20 - 0x2f | 0x40 - 0x7e | | ^[[ | 0123456789:;<=>?
402               | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
403               +-----+------------------+---------------------+-----------------+ */
404            param = 2;
405
406            for (inter = param; i + inter < size; inter++)
407                if (buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
408                    break;
409
410            for (final = inter; i + final < size; final++)
411                if (buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
412                    break;
413
414            if (i + final >= size
415                || buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
416                break;          /* Invalid Final Byte */
417
418            skip += final;
419
420            /* Sanity checks */
421            if (param < inter && buffer[i + param] >= 0x3c)
422            {
423                /* Private sequence, only parse what we know */
424                debug("ansi import: private sequence \"^[[%.*s\"",
425                      final - param + 1, buffer + i + param);
426                continue;       /* Private sequence, skip it entirely */
427            }
428
429            if (final - param > 100)
430                continue;       /* Suspiciously long sequence, skip it */
431
432            /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
433               format */
434            if (param < inter)
435            {
436                argv[0] = 0;
437                for (j = param; j < inter; j++)
438                {
439                    if (buffer[i + j] == ';')
440                        argv[++argc] = 0;
441                    else if (buffer[i + j] >= '0' && buffer[i + j] <= '9')
442                        argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
443                }
444                argc++;
445            }
446
447            /* Interpret final byte. The code representations are given in
448               ECMA-48 5.4: Control sequences, and the code definitions are
449               given in ECMA-48 8.3: Definition of control functions. */
450            debug("ansi import: command '%c'", buffer[i + final]);
451            switch (buffer[i + final])
452            {
453            case 'A':          /* CUU (0x41) - Cursor Up */
454                y -= argc ? argv[0] : 1;
455                if (y < 0)
456                    y = 0;
457                break;
458            case 'B':          /* CUD (0x42) - Cursor Down */
459                y += argc ? argv[0] : 1;
460                break;
461            case 'C':          /* CUF (0x43) - Cursor Right */
462                x += argc ? argv[0] : 1;
463                break;
464            case 'D':          /* CUB (0x44) - Cursor Left */
465                x -= argc ? argv[0] : 1;
466                if (x < 0)
467                    x = 0;
468                break;
469            case 'G':          /* CHA (0x47) - Cursor Character Absolute */
470                x = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
471                break;
472            case 'H':          /* CUP (0x48) - Cursor Position */
473                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
474                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
475                break;
476            case 'J':          /* ED (0x4a) - Erase In Page */
477                savedattr = caca_get_attr(sc->cv, -1, -1);
478                caca_set_attr(sc->cv, sc->clearattr);
479                if (!argc || argv[0] == 0)
480                {
481                    caca_draw_line(sc->cv, x, y, width - 1, y, ' ');
482                    caca_fill_box(sc->cv, 0, y + 1, width - 1, height - 1,
483                                  ' ');
484                }
485                else if (argv[0] == 1)
486                {
487                    caca_fill_box(sc->cv, 0, 0, width - 1, y - 1, ' ');
488                    caca_draw_line(sc->cv, 0, y, x, y, ' ');
489                }
490                else if (argv[0] == 2)
491                {
492                    // x = y = 0;
493                    caca_fill_box(sc->cv, 0, 0, width, height, ' ');
494                }
495                caca_set_attr(sc->cv, savedattr);
496                break;
497            case 'K':          /* EL (0x4b) - Erase In Line */
498                if (!argc || argv[0] == 0)
499                {
500                    caca_draw_line(sc->cv, x, y, width - 1, y, ' ');
501                }
502                else if (argv[0] == 1)
503                {
504                    caca_draw_line(sc->cv, 0, y, x, y, ' ');
505                }
506                else if (argv[0] == 2)
507                {
508                    caca_draw_line(sc->cv, 0, y, width - 1, y, ' ');
509                }
510                break;
511            case 'L':          /* IL - Insert line */
512                {
513                    unsigned int nb_lines = argc ? argv[0] : 1;
514                    debug("IL %d %d", argc, nb_lines);
515                    for (j = bottom - 1; j >= (unsigned int)y + nb_lines; j--)
516                    {
517                        for (k = 0; k < width; k++)
518                        {
519                            caca_put_char(sc->cv, k, j,
520                                          caca_get_char(sc->cv, k,
521                                                        j - nb_lines));
522                            caca_put_attr(sc->cv, k, j,
523                                          caca_get_attr(sc->cv, k,
524                                                        j - nb_lines));
525                        }
526                        caca_draw_line(sc->cv, 0, j - nb_lines, width,
527                                       j - nb_lines, ' ');
528                    }
529                }
530                break;
531            case 'P':          /* DCH (0x50) - Delete Character */
532                if (!argc || argv[0] == 0)
533                    argv[0] = 1;        /* echo -ne 'foobar\r\e[0P\n' */
534                /* Jylam : Start from x, not 0 */
535                for (j = x; (unsigned int)(j + argv[0]) < width; j++)
536                {
537                    caca_put_char(sc->cv, j, y,
538                                  caca_get_char(sc->cv, j + argv[0], y));
539                    caca_put_attr(sc->cv, j, y,
540                                  caca_get_attr(sc->cv, j + argv[0], y));
541                }
542                break;          /* Jylam: this one was missing I guess */
543#if 0
544                savedattr = caca_get_attr(sc->cv, -1, -1);
545                caca_set_attr(sc->cv, sc->clearattr);
546                for (; (unsigned int)j < width; j++)
547                    caca_put_char(sc->cv, j, y, ' ');
548                caca_set_attr(sc->cv, savedattr);
549#endif
550            case 'X':          /* ECH (0x58) - Erase Character */
551                if (argc && argv[0])
552                {
553                    savedattr = caca_get_attr(sc->cv, -1, -1);
554                    caca_set_attr(sc->cv, sc->clearattr);
555                    caca_draw_line(sc->cv, x, y, x + argv[0] - 1, y, ' ');
556                    caca_set_attr(sc->cv, savedattr);
557                }
558            case 'd':          /* VPA (0x64) - Line Position Absolute */
559                y = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
560                break;
561            case 'f':          /* HVP (0x66) - Character And Line Position */
562                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
563                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
564                break;
565            case 'r':          /* FIXME */
566                if (argc == 2)  /* DCSTBM - Set top and bottom margin */
567                {
568                    debug("DCSTBM %d %d", argv[0], argv[1]);
569                    top = argv[0];
570                    bottom = argv[1];
571                }
572                else
573                    debug("ansi import: command r with %d params", argc);
574                break;
575            case 'h':          /* SM (0x68) - FIXME */
576                debug("ansi import: set mode %i", argc ? (int)argv[0] : -1);
577                break;
578            case 'l':          /* RM (0x6c) - FIXME */
579                debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1);
580                break;
581            case 'm':          /* SGR (0x6d) - Select Graphic Rendition */
582                if (argc)
583                    ansi_parse_grcm(sc, argc, argv);
584                else
585                    ansi_parse_grcm(sc, 1, &dummy);
586                break;
587            case 's':          /* Private (save cursor position) */
588                save_x = x;
589                save_y = y;
590                break;
591            case 'u':          /* Private (reload cursor position) */
592                x = save_x;
593                y = save_y;
594                break;
595            default:
596                debug("ansi import: unknown command \"^[[%.*s\"",
597                      final - param + 1, buffer + i + param);
598                break;
599            }
600        }
601
602        /* Parse OSC stuff. */
603        else if (buffer[i] == '\033' && buffer[i + 1] == ']')
604        {
605            char *string;
606            unsigned int command = 0;
607            unsigned int mode = 2, semicolon, final;
608
609            for (semicolon = mode; i + semicolon < size; semicolon++)
610            {
611                if (buffer[i + semicolon] < '0' || buffer[i + semicolon] > '9')
612                    break;
613                command = 10 * command + (buffer[i + semicolon] - '0');
614            }
615
616            if (i + semicolon >= size || buffer[i + semicolon] != ';')
617                break;          /* Invalid Mode */
618
619            for (final = semicolon + 1; i + final < size; final++)
620                if (buffer[i + final] < 0x20)
621                    break;
622
623            if (i + final >= size || buffer[i + final] != '\a')
624                break;          /* Not enough data or no bell found */
625            /* FIXME: XTerm also reacts to <ESC><backslash> and <ST> */
626            /* FIXME: differenciate between not enough data (try again) and
627               invalid data (print shit) */
628
629            skip += final;
630
631            string = malloc(final - (semicolon + 1) + 1);
632            memcpy(string, buffer + i + (semicolon + 1),
633                   final - (semicolon + 1));
634            string[final - (semicolon + 1)] = '\0';
635            debug("ansi import: got OSC command %i string '%s'", command,
636                  string);
637            if (command == 0 || command == 2)
638            {
639                if (sc->title)
640                    free(sc->title);
641                sc->title = string;
642            }
643            else
644                free(string);
645        }
646
647        /* Get the character we’re going to paste */
648        else
649        {
650            size_t bytes;
651
652            if (i + 6 < size)
653            {
654                ch = caca_utf8_to_utf32((char const *)(buffer + i), &bytes);
655            }
656            else
657            {
658                /* Add a trailing zero to what we're going to read */
659                char tmp[7];
660                memcpy(tmp, buffer + i, size - i);
661                tmp[size - i] = '\0';
662                ch = caca_utf8_to_utf32(tmp, &bytes);
663            }
664
665            if (!bytes)
666            {
667                /* If the Unicode is invalid, assume it was latin1. */
668                ch = buffer[i];
669                bytes = 1;
670            }
671
672            /* very incomplete ISO-2022 implementation tailored to DEC ACS */
673            if (sc->conv_state.cs == '@')
674            {
675                if (((ch > ' ') && (ch <= '~'))
676                    &&
677                    (sc->conv_state.
678                     gn[sc->conv_state.ss ? sc->conv_state.
679                        gn[sc->conv_state.ss] : sc->conv_state.glr[0]] == '0'))
680                {
681                    ch = dec_acs(ch);
682                }
683                else if (((ch > 0x80) && (ch < 0xff))
684                         && (sc->conv_state.gn[sc->conv_state.glr[1]] == '0'))
685                {
686                    ch = dec_acs(ch + ' ' - 0x80);
687                }
688            }
689            sc->conv_state.ss = 0;      /* no single-shift (GL) */
690
691            wch = caca_utf32_is_fullwidth(ch) ? 2 : 1;
692
693            skip += bytes - 1;
694        }
695
696        /* Wrap long lines or grow horizontally */
697        while ((unsigned int)x + wch > width)
698        {
699            x -= width;
700            y++;
701        }
702
703        /* Scroll or grow vertically */
704        if ((unsigned int)y >= bottom)
705        {
706            int lines = (y - bottom) + 1;
707
708            savedattr = caca_get_attr(sc->cv, -1, -1);
709
710            for (j = top - 1; j + lines < bottom; j++)
711            {
712                for (k = 0; k < width; k++)
713                {
714                    caca_put_char(sc->cv, k, j,
715                                  caca_get_char(sc->cv, k, j + lines));
716                    caca_put_attr(sc->cv, k, j,
717                                  caca_get_attr(sc->cv, k, j + lines));
718                }
719            }
720            caca_set_attr(sc->cv, sc->clearattr);
721            caca_fill_box(sc->cv, 0, bottom - lines, width, bottom - 1, ' ');
722            y -= lines;
723            caca_set_attr(sc->cv, savedattr);
724        }
725
726        /* Now paste our character, if any */
727        if (wch)
728        {
729            caca_put_char(sc->cv, x, y, ch);
730            x += wch;
731        }
732    }
733
734    caca_gotoxy(sc->cv, x, y);
735
736    if (i)
737        sc->changed = 1;
738    return i;
739}
740
741/* Coding Method Delimiter (CMD), ECMA-48 (1991), ISO/IEC 6429:1992 (ISO IR
742   189) */
743
744static void reset_conv_state(struct screen *sc)
745{
746    sc->conv_state.cs = '@';    /* ISO-2022 coding system */
747    sc->conv_state.cn[0] = '@'; /* ISO 646 C0 control charset */
748    sc->conv_state.cn[1] = 'C'; /* ISO 6429-1983 C1 control charset */
749    sc->conv_state.glr[0] = 0;  /* G0 in GL */
750    sc->conv_state.glr[1] = 2;  /* G2 in GR */
751    sc->conv_state.gn[0] = 'B'; /* US-ASCII G0 charset */
752    sc->conv_state.gn[1] = '0'; /* DEC ACS G1 charset */
753    sc->conv_state.gn[2] = LITERAL2CHAR('.', 'A');      /* ISO 8859-1 G2
754                                                           charset */
755    sc->conv_state.gn[3] = LITERAL2CHAR('.', 'A');      /* ISO 8859-1 G3
756                                                           charset */
757    sc->conv_state.ss = 0;      /* no single-shift (GL) */
758    sc->conv_state.ctrl8bit = 1;
759}
760
761/* XXX : ANSI loader helper */
762
763static void ansi_parse_grcm(struct screen *sc,
764                            unsigned int argc, unsigned int const *argv)
765{
766    static uint8_t const ansi2caca[] = {
767        CACA_BLACK, CACA_RED, CACA_GREEN, CACA_BROWN,
768        CACA_BLUE, CACA_MAGENTA, CACA_CYAN, CACA_LIGHTGRAY
769    };
770
771    unsigned int j;
772    uint8_t efg, ebg;           /* Effective (libcaca) fg/bg */
773
774    for (j = 0; j < argc; j++)
775    {
776        /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
777        if (argv[j] >= 30 && argv[j] <= 37)
778            sc->fg = ansi2caca[argv[j] - 30];
779        else if (argv[j] >= 40 && argv[j] <= 47)
780            sc->bg = ansi2caca[argv[j] - 40];
781        else if (argv[j] >= 90 && argv[j] <= 97)
782            sc->fg = ansi2caca[argv[j] - 90] + 8;
783        else if (argv[j] >= 100 && argv[j] <= 107)
784            sc->bg = ansi2caca[argv[j] - 100] + 8;
785        else
786            switch (argv[j])
787            {
788            case 0:            /* default rendition */
789                sc->fg = sc->dfg;
790                sc->bg = sc->dbg;
791                sc->bold = sc->blink = sc->italics = sc->negative
792                    = sc->concealed = sc->underline = sc->faint = sc->strike
793                    = sc->proportional = 0;
794                break;
795            case 1:            /* bold or increased intensity */
796                sc->bold = 1;
797                break;
798            case 2:            /* faint, decreased intensity or second colour
799                                 */
800                sc->faint = 1;
801                break;
802            case 3:            /* italicized */
803                sc->italics = 1;
804                break;
805            case 4:            /* singly underlined */
806                sc->underline = 1;
807                break;
808            case 5:            /* slowly blinking (less then 150 per minute) */
809            case 6:            /* rapidly blinking (150 per minute or more) */
810                sc->blink = 1;
811                break;
812            case 7:            /* negative image */
813                sc->negative = 1;
814                break;
815            case 8:            /* concealed characters */
816                sc->concealed = 1;
817                break;
818            case 9:            /* crossed-out (characters still legible but
819                                   marked as to be deleted */
820                sc->strike = 1;
821                break;
822            case 21:           /* doubly underlined */
823                sc->underline = 1;
824                break;
825            case 22:           /* normal colour or normal intensity (neither
826                                   bold nor faint) */
827                sc->bold = sc->faint = 0;
828                break;
829            case 23:           /* not italicized, not fraktur */
830                sc->italics = 0;
831                break;
832            case 24:           /* not underlined (neither singly nor doubly) */
833                sc->underline = 0;
834                break;
835            case 25:           /* steady (not blinking) */
836                sc->blink = 0;
837                break;
838            case 26:           /* (reserved for proportional spacing as
839                                   specified in CCITT Recommendation T.61) */
840                sc->proportional = 1;
841                break;
842            case 27:           /* positive image */
843                sc->negative = 0;
844                break;
845            case 28:           /* revealed characters */
846                sc->concealed = 0;
847                break;
848            case 29:           /* not crossed out */
849                sc->strike = 0;
850                break;
851            case 38:           /* (reserved for future standardization,
852                                   intended for setting character foreground
853                                   colour as specified in ISO 8613-6 [CCITT
854                                   Recommendation T.416]) */
855                break;
856            case 39:           /* default display colour
857                                   (implementation-defined) */
858                sc->fg = sc->dfg;
859                break;
860            case 48:           /* (reserved for future standardization,
861                                   intended for setting character background
862                                   colour as specified in ISO 8613-6 [CCITT
863                                   Recommendation T.416]) */
864                break;
865            case 49:           /* default background colour
866                                   (implementation-defined) */
867                sc->bg = sc->dbg;
868                break;
869            case 50:           /* (reserved for cancelling the effect of the
870                                   rendering aspect established by parameter
871                                   value 26) */
872                sc->proportional = 0;
873                break;
874            default:
875                debug("ansi import: unknown sgr %i", argv[j]);
876                break;
877            }
878    }
879
880    if (sc->concealed)
881    {
882        efg = ebg = CACA_TRANSPARENT;
883    }
884    else
885    {
886        efg = sc->negative ? sc->bg : sc->fg;
887        ebg = sc->negative ? sc->fg : sc->bg;
888
889        if (sc->bold)
890        {
891            if (efg < 8)
892                efg += 8;
893            else if (efg == CACA_DEFAULT)
894                efg = CACA_WHITE;
895        }
896    }
897
898    caca_set_color_ansi(sc->cv, efg, ebg);
899}
900
901int create_pty(char *cmd, unsigned int w, unsigned int h, int *cpid)
902{
903    char **argv;
904    int fd;
905    pid_t pid;
906
907    pid = forkpty(&fd, NULL, NULL, NULL);
908    if (pid < 0)
909    {
910        fprintf(stderr, "forkpty() error\n");
911        return -1;
912    }
913    else if (pid == 0)
914    {
915        set_tty_size(0, w, h);
916        putenv("CACA_DRIVER=slang");
917        putenv("TERM=xterm");
918        argv = malloc(2 * sizeof(char *));
919        if (!argv)
920        {
921            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__,
922                    __LINE__);
923            return -1;
924        }
925        argv[0] = cmd;
926        argv[1] = NULL;
927        execvp(cmd, argv);
928        fprintf(stderr, "execvp() error\n");
929        return -1;
930    }
931
932    *cpid = pid;
933
934    fcntl(fd, F_SETFL, O_NDELAY);
935    return fd;
936}
937
938int create_pty_grab(long pid, unsigned int w, unsigned int h, int *newpid)
939{
940    int fdm, fds;
941
942    int ret = openpty(&fdm, &fds, NULL, NULL, NULL);
943
944    if (ret < 0)
945    {
946        fprintf(stderr, "open() error\n");
947        return -1;
948    }
949
950    set_tty_size(0, w, h);
951    grab_process(pid, ptsname(fdm), fds, newpid);
952
953    fcntl(fdm, F_SETFL, O_NDELAY);
954    return fdm;
955}
956
957int set_tty_size(int fd, unsigned int w, unsigned int h)
958{
959    struct winsize ws;
960
961    memset(&ws, 0, sizeof(ws));
962    ws.ws_row = h;
963    ws.ws_col = w;
964    ioctl(fd, TIOCSWINSZ, (char *)&ws);
965
966    return 0;
967}
968
969
970
971int update_terms(struct screen_list *screen_list)
972{
973    int i, refresh = 0;
974    for (i = 0; i < screen_list->count; i++)
975    {
976        if (screen_list->screen[i]->total && !screen_list->dont_update_coords)
977        {
978            unsigned long int bytes;
979
980            bytes = import_term(screen_list,
981                                screen_list->screen[i],
982                                screen_list->screen[i]->buf,
983                                screen_list->screen[i]->total);
984
985            if (bytes > 0)
986            {
987                screen_list->screen[i]->total -= bytes;
988                memmove(screen_list->screen[i]->buf,
989                        screen_list->screen[i]->buf + bytes,
990                        screen_list->screen[i]->total);
991                if (screen_list->screen[i]->visible || screen_list->mini)
992                    refresh = 1;
993            }
994        }
995    }
996    return refresh;
997}
Note: See TracBrowser for help on using the repository browser.