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

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