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

Last change on this file since 1459 was 1459, checked in by Sam Hocevar, 14 years ago
  • Bwarf, typo in the no warranty clause.
  • Property svn:keywords set to Id
File size: 17.4 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 1459 2006-12-12 01:49:37Z sam $
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#include <stdlib.h>
18#include <stdio.h>
19#include <string.h>
20#include <cucul.h>
21#include <caca.h>
22
23#include "neercs.h"
24
25static void ansi_parse_grcm(struct screen *,
26                            unsigned int, unsigned int const *);
27
28long int import_term(struct screen *sc, void const *data, unsigned int size)
29{
30    unsigned char const *buffer = (unsigned char const*)data;
31    unsigned int i, j, skip, dummy = 0;
32    unsigned int width, height;
33    uint32_t savedattr;
34    int x = 0, y = 0, save_x = 0, save_y = 0;
35
36    width = cucul_get_canvas_width(sc->cv);
37    height = cucul_get_canvas_height(sc->cv);
38    x = cucul_get_cursor_x(sc->cv);
39    y = cucul_get_cursor_y(sc->cv);
40
41    if(!sc->init)
42    {
43        sc->dfg = CUCUL_DEFAULT;
44        sc->dbg = CUCUL_TRANSPARENT;
45
46        cucul_set_color_ansi(sc->cv, sc->dfg, sc->dbg);
47        sc->clearattr = cucul_get_attr(sc->cv, -1, -1);
48
49        ansi_parse_grcm(sc, 1, &dummy);
50
51        sc->init = 1;
52    }
53
54    for(i = 0; i < size; i += skip)
55    {
56        uint32_t ch = 0;
57        int wch = 0;
58
59        skip = 1;
60
61        if(buffer[i] == '\r')
62        {
63            x = 0;
64        }
65
66        else if(buffer[i] == '\n')
67        {
68            x = 0;
69            y++;
70        }
71
72        else if(buffer[i] == '\t')
73        {
74            x = (x + 7) & ~7;
75        }
76
77        else if(buffer[i] == '\x08')
78        {
79            if(x > 0)
80                x--;
81        }
82
83        /* If there are not enough characters to parse the escape sequence,
84         * wait until the next try. We require 3. */
85        else if(buffer[i] == '\x1b' && i + 2 >= size)
86            break;
87
88        /* XXX: What the fuck is this shit? */
89        else if(buffer[i] == '\x1b' && buffer[i + 1] == '('
90                 && buffer[i + 2] == 'B')
91        {
92            skip += 2;
93        }
94
95        /* Interpret escape commands, as per Standard ECMA-48 "Control
96         * Functions for Coded Character Sets", 5.4. Control sequences. */
97        else if(buffer[i] == '\x1b' && buffer[i + 1] == '[')
98        {
99            unsigned int argc = 0, argv[101];
100            unsigned int param, inter, final;
101
102        /* Compute offsets to parameter bytes, intermediate bytes and
103         * to the final byte. Only the final byte is mandatory, there
104         * can be zero of the others.
105         * 0  param=2             inter                 final           final+1
106         * +-----+------------------+---------------------+-----------------+
107         * | CSI | parameter bytes  | intermediate bytes  |   final byte    |
108         * |     |   0x30 - 0x3f    |    0x20 - 0x2f      |   0x40 - 0x7e   |
109         * | ^[[ | 0123456789:;<=>? | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
110         * +-----+------------------+---------------------+-----------------+
111         */
112            param = 2;
113
114            for(inter = param; i + inter < size; inter++)
115                if(buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
116                    break;
117
118            for(final = inter; i + final < size; final++)
119                if(buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
120                    break;
121
122            if(i + final >= size
123                || buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
124                break; /* Invalid Final Byte */
125
126            skip += final;
127
128            /* Sanity checks */
129            if(param < inter && buffer[i + param] >= 0x3c)
130            {
131                /* Private sequence, only parse what we know */
132                debug("ansi import: private sequence \"^[[%.*s\"",
133                      final - param + 1, buffer + i + param);
134                continue; /* Private sequence, skip it entirely */
135            }
136
137            if(final - param > 100)
138                continue; /* Suspiciously long sequence, skip it */
139
140            /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
141             * format */
142            if(param < inter)
143            {
144                argv[0] = 0;
145                for(j = param; j < inter; j++)
146                {
147                    if(buffer[i + j] == ';')
148                        argv[++argc] = 0;
149                    else if(buffer[i + j] >= '0' && buffer[i + j] <= '9')
150                        argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
151                }
152                argc++;
153            }
154
155            /* Interpret final byte. The code representations are given in
156             * ECMA-48 5.4: Control sequences, and the code definitions are
157             * given in ECMA-48 8.3: Definition of control functions. */
158            switch(buffer[i + final])
159            {
160            case 'H': /* CUP (0x48) - Cursor Position */
161                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
162                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
163                break;
164            case 'A': /* CUU (0x41) - Cursor Up */
165                y -= argc ? argv[0] : 1;
166                if(y < 0)
167                    y = 0;
168                break;
169            case 'B': /* CUD (0x42) - Cursor Down */
170                y += argc ? argv[0] : 1;
171                break;
172            case 'C': /* CUF (0x43) - Cursor Right */
173                x += argc ? argv[0] : 1;
174                break;
175            case 'D': /* CUB (0x44) - Cursor Left */
176                x -= argc ? argv[0] : 1;
177                if(x < 0)
178                    x = 0;
179                break;
180            case 'G': /* CHA (0x47) - Cursor Character Absolute */
181                x = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
182                break;
183            case 'J': /* ED (0x4a) - Erase In Page */
184                savedattr = cucul_get_attr(sc->cv, -1, -1);
185                cucul_set_attr(sc->cv, sc->clearattr);
186                if(!argc || argv[0] == 0)
187                {
188                    cucul_draw_line(sc->cv, x, y, width, y, ' ');
189                    cucul_fill_box(sc->cv, 0, y + 1, width - 1, height - 1, ' ');
190                }
191                else if(argv[0] == 1)
192                {
193                    cucul_fill_box(sc->cv, 0, 0, width - 1, y - 1, ' ');
194                    cucul_draw_line(sc->cv, 0, y, x, y, ' ');
195                }
196                else if(argv[0] == 2)
197                    //x = y = 0;
198                    cucul_fill_box(sc->cv, 0, 0, width - 1, height - 1, ' ');
199                cucul_set_attr(sc->cv, savedattr);
200                break;
201            case 'K': /* EL (0x4b) - Erase In Line */
202                if(!argc || argv[0] == 0)
203                    cucul_draw_line(sc->cv, x, y, width, y, ' ');
204                else if(argv[0] == 1)
205                    cucul_draw_line(sc->cv, 0, y, x, y, ' ');
206                else if(argv[0] == 2)
207                    if((unsigned int)x < width)
208                        cucul_draw_line(sc->cv, x, y, width - 1, y, ' ');
209                //x = width;
210                break;
211            case 'P': /* DCH (0x50) - Delete Character */
212                if(!argc || argv[0] == 0)
213                    argv[0] = 1; /* echo -ne 'foobar\r\e[0P\n' */
214                for(j = 0; (unsigned int)(j + argv[0]) < width; j++)
215                {
216                    cucul_put_char(sc->cv, j, y,
217                                   cucul_get_char(sc->cv, j + argv[0], y));
218                    cucul_put_attr(sc->cv, j, y,
219                                   cucul_get_attr(sc->cv, j + argv[0], y));
220                }
221#if 0
222                savedattr = cucul_get_attr(sc->cv, -1, -1);
223                cucul_set_attr(sc->cv, sc->clearattr);
224                for( ; (unsigned int)j < width; j++)
225                    cucul_put_char(sc->cv, j, y, ' ');
226                cucul_set_attr(sc->cv, savedattr);
227#endif
228            case 'X': /* ECH (0x58) - Erase Character */
229                if(argc && argv[0])
230                {
231                    savedattr = cucul_get_attr(sc->cv, -1, -1);
232                    cucul_set_attr(sc->cv, sc->clearattr);
233                    cucul_draw_line(sc->cv, x, y, x + argv[0] - 1, y, ' ');
234                    cucul_set_attr(sc->cv, savedattr);
235                }
236            case 'd': /* VPA (0x64) - Line Position Absolute */
237                y = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
238                break;
239            case 'f': /* HVP (0x66) - Character And Line Position */
240                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
241                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
242                break;
243            case 'h': /* SM (0x68) - FIXME */
244                debug("ansi import: set mode %i", argc ? (int)argv[0] : -1);
245                break;
246            case 'l': /* RM (0x6c) - FIXME */
247                debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1);
248                break;
249            case 'm': /* SGR (0x6d) - Select Graphic Rendition */
250                if(argc)
251                    ansi_parse_grcm(sc, argc, argv);
252                else
253                    ansi_parse_grcm(sc, 1, &dummy);
254                break;
255            case 's': /* Private (save cursor position) */
256                save_x = x;
257                save_y = y;
258                break;
259            case 'u': /* Private (reload cursor position) */
260                x = save_x;
261                y = save_y;
262                break;
263            default:
264                debug("ansi import: unknown command \"^[[%.*s\"",
265                      final - param + 1, buffer + i + param);
266                break;
267            }
268        }
269
270        /* Parse OSC stuff. */
271        else if(buffer[i] == '\x1b' && buffer[i + 1] == ']')
272        {
273            char *string;
274            unsigned int command = 0;
275            unsigned int mode = 2, semicolon, final;
276
277            for(semicolon = mode; i + semicolon < size; semicolon++)
278            {
279                if(buffer[i + semicolon] < '0' || buffer[i + semicolon] > '9')
280                    break;
281                command = 10 * command + (buffer[i + semicolon] - '0');
282            }
283
284            if(i + semicolon >= size || buffer[i + semicolon] != ';')
285                break; /* Invalid Mode */
286
287            for(final = semicolon + 1; i + final < size; final++)
288                if(buffer[i + final] < 0x20)
289                    break;
290
291            if(i + final >= size || buffer[i + final] != '\a')
292                break; /* Not enough data or no bell found */
293                /* FIXME: XTerm also reacts to <ESC><backslash> and <ST> */
294                /* FIXME: differenciate between not enough data (try again)
295                 *        and invalid data (print shit) */
296
297            skip += final;
298
299            string = malloc(final - (semicolon + 1) + 1);
300            memcpy(string, buffer + (semicolon + 1), final - (semicolon + 1));
301            string[final - (semicolon + 1)] = '\0';
302            debug("ansi import: got OSC command %i string '%s'", command,
303                  string);
304            free(string);
305        }
306
307        /* Get the character we’re going to paste */
308        else
309        {
310            unsigned int bytes;
311
312            if(i + 6 < size)
313                ch = cucul_utf8_to_utf32((char const *)(buffer + i), &bytes);
314            else
315            {
316                /* Add a trailing zero to what we're going to read */
317                char tmp[7];
318                memcpy(tmp, buffer + i, size - i);
319                tmp[size - i] = '\0';
320                ch = cucul_utf8_to_utf32(tmp, &bytes);
321            }
322
323            if(!bytes)
324            {
325                /* If the Unicode is invalid, assume it was latin1. */
326                ch = buffer[i];
327                bytes = 1;
328            }
329            wch = cucul_utf32_is_fullwidth(ch) ? 2 : 1;
330            skip += bytes - 1;
331        }
332
333        /* Wrap long lines or grow horizontally */
334        while((unsigned int)x + wch > width)
335        {
336            x -= width;
337            y++;
338        }
339
340        /* Scroll or grow vertically */
341        if((unsigned int)y >= height)
342        {
343            unsigned int k;
344            int lines = (y - height) + 1;
345
346            savedattr = cucul_get_attr(sc->cv, -1, -1);
347
348            for(j = 0; j + lines < height; j++)
349            {
350                for(k = 0; k < width; k++)
351                {
352                    cucul_put_char(sc->cv, k, j, cucul_get_char(sc->cv, k, j + lines));
353                    cucul_put_attr(sc->cv, k, j, cucul_get_attr(sc->cv, k, j + lines));
354                }
355            }
356            cucul_set_attr(sc->cv, sc->clearattr);
357            cucul_fill_box(sc->cv, 0, height - lines,
358                                   width - 1, height - 1, ' ');
359            y -= lines;
360            cucul_set_attr(sc->cv, savedattr);
361        }
362
363        /* Now paste our character, if any */
364        if(wch)
365        {
366            cucul_put_char(sc->cv, x, y, ch);
367            x += wch;
368        }
369    }
370
371    cucul_gotoxy(sc->cv, x, y);
372
373    return i;
374}
375
376/* XXX : ANSI loader helper */
377
378static void ansi_parse_grcm(struct screen *sc,
379                            unsigned int argc, unsigned int const *argv)
380{
381    static uint8_t const ansi2cucul[] =
382    {
383        CUCUL_BLACK, CUCUL_RED, CUCUL_GREEN, CUCUL_BROWN,
384        CUCUL_BLUE, CUCUL_MAGENTA, CUCUL_CYAN, CUCUL_LIGHTGRAY
385    };
386
387    unsigned int j;
388    uint8_t efg, ebg; /* Effective (libcucul) fg/bg */
389
390    for(j = 0; j < argc; j++)
391    {
392        /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
393        if(argv[j] >= 30 && argv[j] <= 37)
394            sc->fg = ansi2cucul[argv[j] - 30];
395        else if(argv[j] >= 40 && argv[j] <= 47)
396            sc->bg = ansi2cucul[argv[j] - 40];
397        else if(argv[j] >= 90 && argv[j] <= 97)
398            sc->fg = ansi2cucul[argv[j] - 90] + 8;
399        else if(argv[j] >= 100 && argv[j] <= 107)
400            sc->bg = ansi2cucul[argv[j] - 100] + 8;
401        else switch(argv[j])
402        {
403        case 0: /* default rendition */
404            sc->fg = sc->dfg;
405            sc->bg = sc->dbg;
406            sc->bold = sc->blink = sc->italics = sc->negative
407             = sc->concealed = sc->underline = sc->faint = sc->strike
408             = sc->proportional = 0;
409            break;
410        case 1: /* bold or increased intensity */
411            sc->bold = 1;
412            break;
413        case 2: /* faint, decreased intensity or second colour */
414            sc->faint = 1;
415            break;
416        case 3: /* italicized */
417            sc->italics = 1;
418            break;
419        case 4: /* singly underlined */
420            sc->underline = 1;
421            break;
422        case 5: /* slowly blinking (less then 150 per minute) */
423        case 6: /* rapidly blinking (150 per minute or more) */
424            sc->blink = 1;
425            break;
426        case 7: /* negative image */
427            sc->negative = 1;
428            break;
429        case 8: /* concealed characters */
430            sc->concealed = 1;
431            break;
432        case 9: /* crossed-out (characters still legible but marked as to be
433                 * deleted */
434            sc->strike = 1;
435            break;
436        case 21: /* doubly underlined */
437            sc->underline = 1;
438            break;
439        case 22: /* normal colour or normal intensity (neither bold nor
440                  * faint) */
441            sc->bold = sc->faint = 0;
442            break;
443        case 23: /* not italicized, not fraktur */
444            sc->italics = 0;
445            break;
446        case 24: /* not underlined (neither singly nor doubly) */
447            sc->underline = 0;
448            break;
449        case 25: /* steady (not blinking) */
450            sc->blink = 0;
451            break;
452        case 26: /* (reserved for proportional spacing as specified in CCITT
453                  * Recommendation T.61) */
454            sc->proportional = 1;
455            break;
456        case 27: /* positive image */
457            sc->negative = 0;
458            break;
459        case 28: /* revealed characters */
460            sc->concealed = 0;
461            break;
462        case 29: /* not crossed out */
463            sc->strike = 0;
464            break;
465        case 38: /* (reserved for future standardization, intended for setting
466                  * character foreground colour as specified in ISO 8613-6
467                  * [CCITT Recommendation T.416]) */
468            break;
469        case 39: /* default display colour (implementation-defined) */
470            sc->fg = sc->dfg;
471            break;
472        case 48: /* (reserved for future standardization, intended for setting
473                  * character background colour as specified in ISO 8613-6
474                  * [CCITT Recommendation T.416]) */
475            break;
476        case 49: /* default background colour (implementation-defined) */
477            sc->bg = sc->dbg;
478            break;
479        case 50: /* (reserved for cancelling the effect of the rendering
480                  * aspect established by parameter value 26) */
481            sc->proportional = 0;
482            break;
483        default:
484            debug("ansi import: unknown sgr %i", argv[j]);
485            break;
486        }
487    }
488
489    if(sc->concealed)
490    {
491        efg = ebg = CUCUL_TRANSPARENT;
492    }
493    else
494    {
495        efg = sc->negative ? sc->bg : sc->fg;
496        ebg = sc->negative ? sc->fg : sc->bg;
497
498        if(sc->bold)
499        {
500            if(efg < 8)
501                efg += 8;
502            else if(efg == CUCUL_DEFAULT)
503                efg = CUCUL_WHITE;
504        }
505    }
506
507    cucul_set_color_ansi(sc->cv, efg, ebg);
508}
509
Note: See TracBrowser for help on using the repository browser.