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

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