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

Last change on this file since 2995 was 2995, checked in by sam, 6 years ago

Port neercs to the unified libcaca 0.99.beta15 API.

  • Property svn:keywords set to Id
File size: 29.6 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id$
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_DEFAULT;
106        sc->dbg = CACA_DEFAULT;
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 = 10;
138            sc->bell = 10;
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    return i;
641}
642
643/* Coding Method Delimiter (CMD), ECMA-48 (1991), ISO/IEC 6429:1992 (ISO IR 189) */
644
645static void reset_conv_state(struct screen *sc)
646{
647    sc->conv_state.cs = '@'; /* ISO-2022 coding system */
648    sc->conv_state.cn[0] = '@'; /* ISO 646 C0 control charset */
649    sc->conv_state.cn[1] = 'C'; /* ISO 6429-1983 C1 control charset */
650    sc->conv_state.glr[0] = 0; /* G0 in GL */
651    sc->conv_state.glr[1] = 2; /* G2 in GR */
652    sc->conv_state.gn[0] = 'B'; /* US-ASCII G0 charset */
653    sc->conv_state.gn[1] = '0'; /* DEC ACS G1 charset */
654    sc->conv_state.gn[2] = LITERAL2CHAR('.', 'A'); /* ISO 8859-1 G2 charset */
655    sc->conv_state.gn[3] = LITERAL2CHAR('.', 'A'); /* ISO 8859-1 G3 charset */
656    sc->conv_state.ss = 0; /* no single-shift (GL) */
657    sc->conv_state.ctrl8bit = 1;
658}
659
660/* XXX : ANSI loader helper */
661
662static void ansi_parse_grcm(struct screen *sc,
663                            unsigned int argc, unsigned int const *argv)
664{
665    static uint8_t const ansi2caca[] =
666    {
667        CACA_BLACK, CACA_RED, CACA_GREEN, CACA_BROWN,
668        CACA_BLUE, CACA_MAGENTA, CACA_CYAN, CACA_LIGHTGRAY
669    };
670
671    unsigned int j;
672    uint8_t efg, ebg; /* Effective (libcaca) fg/bg */
673
674    for(j = 0; j < argc; j++)
675    {
676        /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
677        if(argv[j] >= 30 && argv[j] <= 37)
678            sc->fg = ansi2caca[argv[j] - 30];
679        else if(argv[j] >= 40 && argv[j] <= 47)
680            sc->bg = ansi2caca[argv[j] - 40];
681        else if(argv[j] >= 90 && argv[j] <= 97)
682            sc->fg = ansi2caca[argv[j] - 90] + 8;
683        else if(argv[j] >= 100 && argv[j] <= 107)
684            sc->bg = ansi2caca[argv[j] - 100] + 8;
685        else switch(argv[j])
686        {
687        case 0: /* default rendition */
688            sc->fg = sc->dfg;
689            sc->bg = sc->dbg;
690            sc->bold = sc->blink = sc->italics = sc->negative
691             = sc->concealed = sc->underline = sc->faint = sc->strike
692             = sc->proportional = 0;
693            break;
694        case 1: /* bold or increased intensity */
695            sc->bold = 1;
696            break;
697        case 2: /* faint, decreased intensity or second colour */
698            sc->faint = 1;
699            break;
700        case 3: /* italicized */
701            sc->italics = 1;
702            break;
703        case 4: /* singly underlined */
704            sc->underline = 1;
705            break;
706        case 5: /* slowly blinking (less then 150 per minute) */
707        case 6: /* rapidly blinking (150 per minute or more) */
708            sc->blink = 1;
709            break;
710        case 7: /* negative image */
711            sc->negative = 1;
712            break;
713        case 8: /* concealed characters */
714            sc->concealed = 1;
715            break;
716        case 9: /* crossed-out (characters still legible but marked as to be
717                 * deleted */
718            sc->strike = 1;
719            break;
720        case 21: /* doubly underlined */
721            sc->underline = 1;
722            break;
723        case 22: /* normal colour or normal intensity (neither bold nor
724                  * faint) */
725            sc->bold = sc->faint = 0;
726            break;
727        case 23: /* not italicized, not fraktur */
728            sc->italics = 0;
729            break;
730        case 24: /* not underlined (neither singly nor doubly) */
731            sc->underline = 0;
732            break;
733        case 25: /* steady (not blinking) */
734            sc->blink = 0;
735            break;
736        case 26: /* (reserved for proportional spacing as specified in CCITT
737                  * Recommendation T.61) */
738            sc->proportional = 1;
739            break;
740        case 27: /* positive image */
741            sc->negative = 0;
742            break;
743        case 28: /* revealed characters */
744            sc->concealed = 0;
745            break;
746        case 29: /* not crossed out */
747            sc->strike = 0;
748            break;
749        case 38: /* (reserved for future standardization, intended for setting
750                  * character foreground colour as specified in ISO 8613-6
751                  * [CCITT Recommendation T.416]) */
752            break;
753        case 39: /* default display colour (implementation-defined) */
754            sc->fg = sc->dfg;
755            break;
756        case 48: /* (reserved for future standardization, intended for setting
757                  * character background colour as specified in ISO 8613-6
758                  * [CCITT Recommendation T.416]) */
759            break;
760        case 49: /* default background colour (implementation-defined) */
761            sc->bg = sc->dbg;
762            break;
763        case 50: /* (reserved for cancelling the effect of the rendering
764                  * aspect established by parameter value 26) */
765            sc->proportional = 0;
766            break;
767        default:
768            debug("ansi import: unknown sgr %i", argv[j]);
769            break;
770        }
771    }
772
773    if(sc->concealed)
774    {
775        efg = ebg = CACA_TRANSPARENT;
776    }
777    else
778    {
779        efg = sc->negative ? sc->bg : sc->fg;
780        ebg = sc->negative ? sc->fg : sc->bg;
781
782        if(sc->bold)
783        {
784            if(efg < 8)
785                efg += 8;
786            else if(efg == CACA_DEFAULT)
787                efg = CACA_WHITE;
788        }
789    }
790
791    caca_set_color_ansi(sc->cv, efg, ebg);
792}
793
794int create_pty(char *cmd, unsigned int w, unsigned int h, int *cpid)
795{
796    char **argv;
797    int fd;
798    pid_t pid;
799
800    pid = forkpty(&fd, NULL, NULL, NULL);
801    if(pid < 0)
802    {
803        fprintf(stderr, "forkpty() error\n");
804        return -1;
805    }
806    else if(pid == 0)
807    {
808        set_tty_size(0, w, h);
809        putenv("CACA_DRIVER=slang");
810        putenv("TERM=xterm");
811        argv = malloc(2 * sizeof(char *));
812        if(!argv)
813        {
814            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
815            return -1;
816        }
817        argv[0] = cmd;
818        argv[1] = NULL;
819        execvp(cmd, argv);
820        fprintf(stderr, "execvp() error\n");
821        return -1;
822    }
823
824    *cpid = pid;
825
826    fcntl(fd, F_SETFL, O_NDELAY);
827    return fd;
828}
829
830int create_pty_grab(long pid, unsigned int w, unsigned int h)
831{
832    int fdm, fds;
833
834    int ret = openpty(&fdm, &fds, NULL, NULL, NULL);
835
836    if(ret < 0)
837    {
838        fprintf(stderr, "open() error\n");
839        return -1;
840    }
841
842    set_tty_size(0, w, h);
843    grab_process(pid, ptsname(fdm), fds);
844
845    fcntl(fdm, F_SETFL, O_NDELAY);
846    return fdm;
847}
848
849int set_tty_size(int fd, unsigned int w, unsigned int h)
850{
851    struct winsize ws;
852
853    memset(&ws, 0, sizeof(ws));
854    ws.ws_row = h;
855    ws.ws_col = w;
856    ioctl(fd, TIOCSWINSZ, (char *)&ws);
857
858    return 0;
859}
860
861
862
863int update_terms(struct screen_list* screen_list)
864{
865    int i, refresh = 0;
866    for(i = 0; i < screen_list->count; i++)
867    {
868        if(screen_list->screen[i]->total && !screen_list->dont_update_coords)
869        {
870            unsigned long int bytes;
871
872            bytes = import_term(screen_list,
873                                screen_list->screen[i],
874                                screen_list->screen[i]->buf,
875                                screen_list->screen[i]->total);
876
877            if(bytes > 0)
878            {
879                screen_list->screen[i]->total -= bytes;
880                memmove(screen_list->screen[i]->buf,
881                        screen_list->screen[i]->buf + bytes,
882                        screen_list->screen[i]->total);
883                refresh = 1;
884            }
885        }
886    }
887    return refresh;
888}
889
Note: See TracBrowser for help on using the repository browser.