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

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