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

Last change on this file since 3969 was 3969, checked in by Jean-Yves Lamoureux, 11 years ago
  • Massive indentation pass, could insert odd things, blame pterjan, he doesn't care.
  • Property svn:keywords set to Id
File size: 33.7 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 3969 2009-11-19 16:26:53Z jylam $
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, 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, 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                    if ((unsigned int)x < width)
509                        caca_draw_line(sc->cv, x, y, width - 1, y, ' ');
510                }
511                // x = width;
512                break;
513            case 'L':          /* IL - Insert line */
514                {
515                    unsigned int nb_lines = argc ? argv[0] : 1;
516                    debug("IL %d %d", argc, nb_lines);
517                    for (j = bottom - 1; j >= (unsigned int)y + nb_lines; j--)
518                    {
519                        for (k = 0; k < width; k++)
520                        {
521                            caca_put_char(sc->cv, k, j,
522                                          caca_get_char(sc->cv, k,
523                                                        j - nb_lines));
524                            caca_put_attr(sc->cv, k, j,
525                                          caca_get_attr(sc->cv, k,
526                                                        j - nb_lines));
527                        }
528                        caca_draw_line(sc->cv, 0, j - nb_lines, width,
529                                       j - nb_lines, ' ');
530                    }
531                }
532                break;
533            case 'P':          /* DCH (0x50) - Delete Character */
534                if (!argc || argv[0] == 0)
535                    argv[0] = 1;        /* echo -ne 'foobar\r\e[0P\n' */
536                /* Jylam : Start from x, not 0 */
537                for (j = x; (unsigned int)(j + argv[0]) < width; j++)
538                {
539                    caca_put_char(sc->cv, j, y,
540                                  caca_get_char(sc->cv, j + argv[0], y));
541                    caca_put_attr(sc->cv, j, y,
542                                  caca_get_attr(sc->cv, j + argv[0], y));
543                }
544                break;          /* Jylam: this one was missing I guess */
545#if 0
546                savedattr = caca_get_attr(sc->cv, -1, -1);
547                caca_set_attr(sc->cv, sc->clearattr);
548                for (; (unsigned int)j < width; j++)
549                    caca_put_char(sc->cv, j, y, ' ');
550                caca_set_attr(sc->cv, savedattr);
551#endif
552            case 'X':          /* ECH (0x58) - Erase Character */
553                if (argc && argv[0])
554                {
555                    savedattr = caca_get_attr(sc->cv, -1, -1);
556                    caca_set_attr(sc->cv, sc->clearattr);
557                    caca_draw_line(sc->cv, x, y, x + argv[0] - 1, y, ' ');
558                    caca_set_attr(sc->cv, savedattr);
559                }
560            case 'd':          /* VPA (0x64) - Line Position Absolute */
561                y = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
562                break;
563            case 'f':          /* HVP (0x66) - Character And Line Position */
564                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
565                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
566                break;
567            case 'r':          /* FIXME */
568                if (argc == 2)  /* DCSTBM - Set top and bottom margin */
569                {
570                    debug("DCSTBM %d %d", argv[0], argv[1]);
571                    top = argv[0];
572                    bottom = argv[1];
573                }
574                else
575                    debug("ansi import: command r with %d params", argc);
576                break;
577            case 'h':          /* SM (0x68) - FIXME */
578                debug("ansi import: set mode %i", argc ? (int)argv[0] : -1);
579                break;
580            case 'l':          /* RM (0x6c) - FIXME */
581                debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1);
582                break;
583            case 'm':          /* SGR (0x6d) - Select Graphic Rendition */
584                if (argc)
585                    ansi_parse_grcm(sc, argc, argv);
586                else
587                    ansi_parse_grcm(sc, 1, &dummy);
588                break;
589            case 's':          /* Private (save cursor position) */
590                save_x = x;
591                save_y = y;
592                break;
593            case 'u':          /* Private (reload cursor position) */
594                x = save_x;
595                y = save_y;
596                break;
597            default:
598                debug("ansi import: unknown command \"^[[%.*s\"",
599                      final - param + 1, buffer + i + param);
600                break;
601            }
602        }
603
604        /* Parse OSC stuff. */
605        else if (buffer[i] == '\033' && buffer[i + 1] == ']')
606        {
607            char *string;
608            unsigned int command = 0;
609            unsigned int mode = 2, semicolon, final;
610
611            for (semicolon = mode; i + semicolon < size; semicolon++)
612            {
613                if (buffer[i + semicolon] < '0' || buffer[i + semicolon] > '9')
614                    break;
615                command = 10 * command + (buffer[i + semicolon] - '0');
616            }
617
618            if (i + semicolon >= size || buffer[i + semicolon] != ';')
619                break;          /* Invalid Mode */
620
621            for (final = semicolon + 1; i + final < size; final++)
622                if (buffer[i + final] < 0x20)
623                    break;
624
625            if (i + final >= size || buffer[i + final] != '\a')
626                break;          /* Not enough data or no bell found */
627            /* FIXME: XTerm also reacts to <ESC><backslash> and <ST> */
628            /* FIXME: differenciate between not enough data (try again) and
629               invalid data (print shit) */
630
631            skip += final;
632
633            string = malloc(final - (semicolon + 1) + 1);
634            memcpy(string, buffer + i + (semicolon + 1),
635                   final - (semicolon + 1));
636            string[final - (semicolon + 1)] = '\0';
637            debug("ansi import: got OSC command %i string '%s'", command,
638                  string);
639            if (command == 0 || command == 2)
640            {
641                if (sc->title)
642                    free(sc->title);
643                sc->title = string;
644            }
645            else
646                free(string);
647        }
648
649        /* Get the character we’re going to paste */
650        else
651        {
652            size_t bytes;
653
654            if (i + 6 < size)
655            {
656                ch = caca_utf8_to_utf32((char const *)(buffer + i), &bytes);
657            }
658            else
659            {
660                /* Add a trailing zero to what we're going to read */
661                char tmp[7];
662                memcpy(tmp, buffer + i, size - i);
663                tmp[size - i] = '\0';
664                ch = caca_utf8_to_utf32(tmp, &bytes);
665            }
666
667            if (!bytes)
668            {
669                /* If the Unicode is invalid, assume it was latin1. */
670                ch = buffer[i];
671                bytes = 1;
672            }
673
674            /* very incomplete ISO-2022 implementation tailored to DEC ACS */
675            if (sc->conv_state.cs == '@')
676            {
677                if (((ch > ' ') && (ch <= '~'))
678                    &&
679                    (sc->conv_state.
680                     gn[sc->conv_state.ss ? sc->conv_state.
681                        gn[sc->conv_state.ss] : sc->conv_state.glr[0]] == '0'))
682                {
683                    ch = dec_acs(ch);
684                }
685                else if (((ch > 0x80) && (ch < 0xff))
686                         && (sc->conv_state.gn[sc->conv_state.glr[1]] == '0'))
687                {
688                    ch = dec_acs(ch + ' ' - 0x80);
689                }
690            }
691            sc->conv_state.ss = 0;      /* no single-shift (GL) */
692
693            wch = caca_utf32_is_fullwidth(ch) ? 2 : 1;
694
695            skip += bytes - 1;
696        }
697
698        /* Wrap long lines or grow horizontally */
699        while ((unsigned int)x + wch > width)
700        {
701            x -= width;
702            y++;
703        }
704
705        /* Scroll or grow vertically */
706        if ((unsigned int)y >= bottom)
707        {
708            int lines = (y - bottom) + 1;
709
710            savedattr = caca_get_attr(sc->cv, -1, -1);
711
712            for (j = top - 1; j + lines < bottom; j++)
713            {
714                for (k = 0; k < width; k++)
715                {
716                    caca_put_char(sc->cv, k, j,
717                                  caca_get_char(sc->cv, k, j + lines));
718                    caca_put_attr(sc->cv, k, j,
719                                  caca_get_attr(sc->cv, k, j + lines));
720                }
721            }
722            caca_set_attr(sc->cv, sc->clearattr);
723            caca_fill_box(sc->cv, 0, bottom - lines, width, bottom - 1, ' ');
724            y -= lines;
725            caca_set_attr(sc->cv, savedattr);
726        }
727
728        /* Now paste our character, if any */
729        if (wch)
730        {
731            caca_put_char(sc->cv, x, y, ch);
732            x += wch;
733        }
734    }
735
736    caca_gotoxy(sc->cv, x, y);
737
738    if (i)
739        sc->changed = 1;
740    return i;
741}
742
743/* Coding Method Delimiter (CMD), ECMA-48 (1991), ISO/IEC 6429:1992 (ISO IR
744   189) */
745
746static void reset_conv_state(struct screen *sc)
747{
748    sc->conv_state.cs = '@';    /* ISO-2022 coding system */
749    sc->conv_state.cn[0] = '@'; /* ISO 646 C0 control charset */
750    sc->conv_state.cn[1] = 'C'; /* ISO 6429-1983 C1 control charset */
751    sc->conv_state.glr[0] = 0;  /* G0 in GL */
752    sc->conv_state.glr[1] = 2;  /* G2 in GR */
753    sc->conv_state.gn[0] = 'B'; /* US-ASCII G0 charset */
754    sc->conv_state.gn[1] = '0'; /* DEC ACS G1 charset */
755    sc->conv_state.gn[2] = LITERAL2CHAR('.', 'A');      /* ISO 8859-1 G2
756                                                           charset */
757    sc->conv_state.gn[3] = LITERAL2CHAR('.', 'A');      /* ISO 8859-1 G3
758                                                           charset */
759    sc->conv_state.ss = 0;      /* no single-shift (GL) */
760    sc->conv_state.ctrl8bit = 1;
761}
762
763/* XXX : ANSI loader helper */
764
765static void ansi_parse_grcm(struct screen *sc,
766                            unsigned int argc, unsigned int const *argv)
767{
768    static uint8_t const ansi2caca[] = {
769        CACA_BLACK, CACA_RED, CACA_GREEN, CACA_BROWN,
770        CACA_BLUE, CACA_MAGENTA, CACA_CYAN, CACA_LIGHTGRAY
771    };
772
773    unsigned int j;
774    uint8_t efg, ebg;           /* Effective (libcaca) fg/bg */
775
776    for (j = 0; j < argc; j++)
777    {
778        /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
779        if (argv[j] >= 30 && argv[j] <= 37)
780            sc->fg = ansi2caca[argv[j] - 30];
781        else if (argv[j] >= 40 && argv[j] <= 47)
782            sc->bg = ansi2caca[argv[j] - 40];
783        else if (argv[j] >= 90 && argv[j] <= 97)
784            sc->fg = ansi2caca[argv[j] - 90] + 8;
785        else if (argv[j] >= 100 && argv[j] <= 107)
786            sc->bg = ansi2caca[argv[j] - 100] + 8;
787        else
788            switch (argv[j])
789            {
790            case 0:            /* default rendition */
791                sc->fg = sc->dfg;
792                sc->bg = sc->dbg;
793                sc->bold = sc->blink = sc->italics = sc->negative
794                    = sc->concealed = sc->underline = sc->faint = sc->strike
795                    = sc->proportional = 0;
796                break;
797            case 1:            /* bold or increased intensity */
798                sc->bold = 1;
799                break;
800            case 2:            /* faint, decreased intensity or second colour
801                                 */
802                sc->faint = 1;
803                break;
804            case 3:            /* italicized */
805                sc->italics = 1;
806                break;
807            case 4:            /* singly underlined */
808                sc->underline = 1;
809                break;
810            case 5:            /* slowly blinking (less then 150 per minute) */
811            case 6:            /* rapidly blinking (150 per minute or more) */
812                sc->blink = 1;
813                break;
814            case 7:            /* negative image */
815                sc->negative = 1;
816                break;
817            case 8:            /* concealed characters */
818                sc->concealed = 1;
819                break;
820            case 9:            /* crossed-out (characters still legible but
821                                   marked as to be deleted */
822                sc->strike = 1;
823                break;
824            case 21:           /* doubly underlined */
825                sc->underline = 1;
826                break;
827            case 22:           /* normal colour or normal intensity (neither
828                                   bold nor faint) */
829                sc->bold = sc->faint = 0;
830                break;
831            case 23:           /* not italicized, not fraktur */
832                sc->italics = 0;
833                break;
834            case 24:           /* not underlined (neither singly nor doubly) */
835                sc->underline = 0;
836                break;
837            case 25:           /* steady (not blinking) */
838                sc->blink = 0;
839                break;
840            case 26:           /* (reserved for proportional spacing as
841                                   specified in CCITT Recommendation T.61) */
842                sc->proportional = 1;
843                break;
844            case 27:           /* positive image */
845                sc->negative = 0;
846                break;
847            case 28:           /* revealed characters */
848                sc->concealed = 0;
849                break;
850            case 29:           /* not crossed out */
851                sc->strike = 0;
852                break;
853            case 38:           /* (reserved for future standardization,
854                                   intended for setting character foreground
855                                   colour as specified in ISO 8613-6 [CCITT
856                                   Recommendation T.416]) */
857                break;
858            case 39:           /* default display colour
859                                   (implementation-defined) */
860                sc->fg = sc->dfg;
861                break;
862            case 48:           /* (reserved for future standardization,
863                                   intended for setting character background
864                                   colour as specified in ISO 8613-6 [CCITT
865                                   Recommendation T.416]) */
866                break;
867            case 49:           /* default background colour
868                                   (implementation-defined) */
869                sc->bg = sc->dbg;
870                break;
871            case 50:           /* (reserved for cancelling the effect of the
872                                   rendering aspect established by parameter
873                                   value 26) */
874                sc->proportional = 0;
875                break;
876            default:
877                debug("ansi import: unknown sgr %i", argv[j]);
878                break;
879            }
880    }
881
882    if (sc->concealed)
883    {
884        efg = ebg = CACA_TRANSPARENT;
885    }
886    else
887    {
888        efg = sc->negative ? sc->bg : sc->fg;
889        ebg = sc->negative ? sc->fg : sc->bg;
890
891        if (sc->bold)
892        {
893            if (efg < 8)
894                efg += 8;
895            else if (efg == CACA_DEFAULT)
896                efg = CACA_WHITE;
897        }
898    }
899
900    caca_set_color_ansi(sc->cv, efg, ebg);
901}
902
903int create_pty(char *cmd, unsigned int w, unsigned int h, int *cpid)
904{
905    char **argv;
906    int fd;
907    pid_t pid;
908
909    pid = forkpty(&fd, NULL, NULL, NULL);
910    if (pid < 0)
911    {
912        fprintf(stderr, "forkpty() error\n");
913        return -1;
914    }
915    else if (pid == 0)
916    {
917        set_tty_size(0, w, h);
918        putenv("CACA_DRIVER=slang");
919        putenv("TERM=xterm");
920        argv = malloc(2 * sizeof(char *));
921        if (!argv)
922        {
923            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__,
924                    __LINE__);
925            return -1;
926        }
927        argv[0] = cmd;
928        argv[1] = NULL;
929        execvp(cmd, argv);
930        fprintf(stderr, "execvp() error\n");
931        return -1;
932    }
933
934    *cpid = pid;
935
936    fcntl(fd, F_SETFL, O_NDELAY);
937    return fd;
938}
939
940int create_pty_grab(long pid, unsigned int w, unsigned int h, int *newpid)
941{
942    int fdm, fds;
943
944    int ret = openpty(&fdm, &fds, NULL, NULL, NULL);
945
946    if (ret < 0)
947    {
948        fprintf(stderr, "open() error\n");
949        return -1;
950    }
951
952    set_tty_size(0, w, h);
953    grab_process(pid, ptsname(fdm), fds, newpid);
954
955    fcntl(fdm, F_SETFL, O_NDELAY);
956    return fdm;
957}
958
959int set_tty_size(int fd, unsigned int w, unsigned int h)
960{
961    struct winsize ws;
962
963    memset(&ws, 0, sizeof(ws));
964    ws.ws_row = h;
965    ws.ws_col = w;
966    ioctl(fd, TIOCSWINSZ, (char *)&ws);
967
968    return 0;
969}
970
971
972
973int update_terms(struct screen_list *screen_list)
974{
975    int i, refresh = 0;
976    for (i = 0; i < screen_list->count; i++)
977    {
978        if (screen_list->screen[i]->total && !screen_list->dont_update_coords)
979        {
980            unsigned long int bytes;
981
982            bytes = import_term(screen_list,
983                                screen_list->screen[i],
984                                screen_list->screen[i]->buf,
985                                screen_list->screen[i]->total);
986
987            if (bytes > 0)
988            {
989                screen_list->screen[i]->total -= bytes;
990                memmove(screen_list->screen[i]->buf,
991                        screen_list->screen[i]->buf + bytes,
992                        screen_list->screen[i]->total);
993                if (screen_list->screen[i]->visible || screen_list->mini)
994                    refresh = 1;
995            }
996        }
997    }
998    return refresh;
999}
Note: See TracBrowser for help on using the repository browser.