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

Last change on this file since 2451 was 2451, checked in by Pascal Terjan, 12 years ago
  • Remove termios stuff, does not do anything currently
  • Property svn:keywords set to Id
File size: 21.0 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 2451 2008-06-18 21:08:44Z pterjan $
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15#include "config.h"
16
17#define _XOPEN_SOURCE
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21#include <pty.h>
22#include <unistd.h>
23#include <fcntl.h>
24
25#include <cucul.h>
26#include <caca.h>
27
28#include "neercs.h"
29
30static void ansi_parse_grcm(struct screen *,
31                            unsigned int, unsigned int const *);
32
33long int import_term(struct screen_list *screen_list, struct screen *sc, void const *data, unsigned int size)
34{
35    unsigned char const *buffer = (unsigned char const*)data;
36    unsigned int i, j, k,skip, dummy = 0;
37    unsigned int width, height;
38    uint32_t savedattr, scrolled = 0;
39    int x = 0, y = 0, save_x = 0, save_y = 0;
40
41    width = cucul_get_canvas_width(sc->cv);
42    height = cucul_get_canvas_height(sc->cv);
43    x = cucul_get_cursor_x(sc->cv);
44    y = cucul_get_cursor_y(sc->cv);
45
46    if(!sc->init)
47    {
48        sc->dfg = CUCUL_DEFAULT;
49        sc->dbg = CUCUL_DEFAULT;
50
51        cucul_set_color_ansi(sc->cv, sc->dfg, sc->dbg);
52        sc->clearattr = cucul_get_attr(sc->cv, -1, -1);
53
54        ansi_parse_grcm(sc, 1, &dummy);
55
56        sc->init = 1;
57    }
58
59    for(i = 0; i < size; i += skip)
60    {
61        uint32_t ch = 0;
62        int wch = 0;
63
64        skip = 1;
65
66        if(buffer[i] == '\r')
67        {
68            x = 0;
69        }
70
71        else if(buffer[i] == '\n')
72        {
73            x = 0;
74            y++;
75        }
76        else if(buffer[i] == '\a')
77        {
78            screen_list->in_bell = 10;
79            sc->bell = 10;
80        }
81
82        else if(buffer[i] == '\t')
83        {
84            x = (x + 7) & ~7;
85        }
86
87        else if(buffer[i] == '\x08')
88        {
89            if(x > 0)
90                x--;
91        }
92
93        /* If there are not enough characters to parse the escape sequence,
94         * wait until the next try. We require 3. */
95        else if(buffer[i] == '\x1b' && i + 2 >= size)
96            break;
97
98        /* XXX: What the fuck is this shit? */
99        else if(buffer[i] == '\x1b' && buffer[i + 1] == '('
100                 && buffer[i + 2] == 'B')
101        {
102            skip += 2;
103        }
104
105        /* Interpret escape commands, as per Standard ECMA-48 "Control
106         * Functions for Coded Character Sets", 5.4. Control sequences. */
107        else if(buffer[i] == '\x1b' && buffer[i + 1] == '[')
108        {
109            unsigned int argc = 0, argv[101];
110            unsigned int param, inter, final;
111
112
113        /* Compute offsets to parameter bytes, intermediate bytes and
114         * to the final byte. Only the final byte is mandatory, there
115         * can be zero of the others.
116         * 0  param=2             inter                 final           final+1
117         * +-----+------------------+---------------------+-----------------+
118         * | CSI | parameter bytes  | intermediate bytes  |   final byte    |
119         * |     |   0x30 - 0x3f    |    0x20 - 0x2f      |   0x40 - 0x7e   |
120         * | ^[[ | 0123456789:;<=>? | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
121         * +-----+------------------+---------------------+-----------------+
122         */
123            param = 2;
124
125            for(inter = param; i + inter < size; inter++)
126                if(buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
127                    break;
128
129            for(final = inter; i + final < size; final++)
130                if(buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
131                    break;
132
133            if(i + final >= size
134                || buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
135                break; /* Invalid Final Byte */
136
137            skip += final;
138
139            /* Sanity checks */
140            if(param < inter && buffer[i + param] >= 0x3c)
141            {
142                /* Private sequence, only parse what we know */
143                debug("ansi import: private sequence \"^[[%.*s\"",
144                      final - param + 1, buffer + i + param);
145                continue; /* Private sequence, skip it entirely */
146            }
147
148            if(final - param > 100)
149                continue; /* Suspiciously long sequence, skip it */
150
151            /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
152             * format */
153            if(param < inter)
154            {
155                argv[0] = 0;
156                for(j = param; j < inter; j++)
157                {
158                    if(buffer[i + j] == ';')
159                        argv[++argc] = 0;
160                    else if(buffer[i + j] >= '0' && buffer[i + j] <= '9')
161                        argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
162                }
163                argc++;
164            }
165
166            /* Interpret final byte. The code representations are given in
167             * ECMA-48 5.4: Control sequences, and the code definitions are
168             * given in ECMA-48 8.3: Definition of control functions. */
169            debug("Got command '%c'\n", buffer[i + final]);
170            switch(buffer[i + final])
171            {
172            case 'H': /* CUP (0x48) - Cursor Position */
173                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
174                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
175                break;
176            case 'A': /* CUU (0x41) - Cursor Up */
177                y -= argc ? argv[0] : 1;
178                if(y < 0)
179                    y = 0;
180                break;
181            case 'B': /* CUD (0x42) - Cursor Down */
182                y += argc ? argv[0] : 1;
183                break;
184            case 'C': /* CUF (0x43) - Cursor Right */
185                x += argc ? argv[0] : 1;
186                break;
187            case 'D': /* CUB (0x44) - Cursor Left */
188                x -= argc ? argv[0] : 1;
189                if(x < 0)
190                    x = 0;
191                break;
192            case 'G': /* CHA (0x47) - Cursor Character Absolute */
193                x = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
194                break;
195            case 'J': /* ED (0x4a) - Erase In Page */
196                savedattr = cucul_get_attr(sc->cv, -1, -1);
197                cucul_set_attr(sc->cv, sc->clearattr);
198                if(!argc || argv[0] == 0)
199                {
200                    cucul_draw_line(sc->cv, x, y, width, y, ' ');
201                    cucul_fill_box(sc->cv, 0, y + 1, width - 1, height - 1, ' ');
202                }
203                else if(argv[0] == 1)
204                {
205                    cucul_fill_box(sc->cv, 0, 0, width - 1, y - 1, ' ');
206                    cucul_draw_line(sc->cv, 0, y, x, y, ' ');
207                }
208                else if(argv[0] == 2)
209                {
210                    //x = y = 0;
211                    cucul_fill_box(sc->cv, 0, 0, width, height - 1, ' ');
212                }
213                cucul_set_attr(sc->cv, savedattr);
214                break;
215            case 'K': /* EL (0x4b) - Erase In Line */
216                if(!argc || argv[0] == 0)
217                    cucul_draw_line(sc->cv, x, y, width, y, ' ');
218                else if(argv[0] == 1)
219                    cucul_draw_line(sc->cv, 0, y, x, y, ' ');
220                else if(argv[0] == 2)
221                    if((unsigned int)x < width)
222                        cucul_draw_line(sc->cv, x, y, width - 1, y, ' ');
223                //x = width;
224                break;
225            case 'P': /* DCH (0x50) - Delete Character */
226                if(!argc || argv[0] == 0)
227                    argv[0] = 1; /* echo -ne 'foobar\r\e[0P\n' */
228                for(j = 0; (unsigned int)(j + argv[0]) < width; j++)
229                {
230                    cucul_put_char(sc->cv, j, y,
231                                   cucul_get_char(sc->cv, j + argv[0], y));
232                    cucul_put_attr(sc->cv, j, y,
233                                   cucul_get_attr(sc->cv, j + argv[0], y));
234                }
235#if 0
236                savedattr = cucul_get_attr(sc->cv, -1, -1);
237                cucul_set_attr(sc->cv, sc->clearattr);
238                for( ; (unsigned int)j < width; j++)
239                    cucul_put_char(sc->cv, j, y, ' ');
240                cucul_set_attr(sc->cv, savedattr);
241#endif
242            case 'X': /* ECH (0x58) - Erase Character */
243                if(argc && argv[0])
244                {
245                    savedattr = cucul_get_attr(sc->cv, -1, -1);
246                    cucul_set_attr(sc->cv, sc->clearattr);
247                    cucul_draw_line(sc->cv, x, y, x + argv[0] - 1, y, ' ');
248                    cucul_set_attr(sc->cv, savedattr);
249                }
250            case 'd': /* VPA (0x64) - Line Position Absolute */
251                y = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
252                break;
253            case 'f': /* HVP (0x66) - Character And Line Position */
254                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
255                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
256                break;
257            case 'r': /* SS  (0x72) - Scroll Screen FIXME */
258                if(scrolled) break;
259                if((argv[0]==0 && argv[1]==0)  || scrolled) break;
260                for(j = argv[0]-1; j < argv[1]-1; j++)
261                {
262                    for(k = 0; k < width; k++)
263                    {
264                        cucul_put_char(sc->cv, k, j, cucul_get_char(sc->cv, k, j+1));
265                        cucul_put_attr(sc->cv, k, j, cucul_get_attr(sc->cv, k, j+1));
266                    }
267                }
268                cucul_draw_line(sc->cv, 0, argv[1]-1, width, argv[1]-1, ' ');
269                scrolled = 1;
270                break;
271            case 'h': /* SM (0x68) - FIXME */
272                debug("ansi import: set mode %i", argc ? (int)argv[0] : -1);
273                break;
274            case 'l': /* RM (0x6c) - FIXME */
275                debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1);
276                break;
277            case 'm': /* SGR (0x6d) - Select Graphic Rendition */
278                if(argc)
279                    ansi_parse_grcm(sc, argc, argv);
280                else
281                    ansi_parse_grcm(sc, 1, &dummy);
282                break;
283            case 's': /* Private (save cursor position) */
284                save_x = x;
285                save_y = y;
286                break;
287            case 'u': /* Private (reload cursor position) */
288                x = save_x;
289                y = save_y;
290                break;
291            default:
292                debug("ansi import: unknown command \"^[[%.*s\"",
293                      final - param + 1, buffer + i + param);
294                break;
295            }
296        }
297
298        /* Parse OSC stuff. */
299        else if(buffer[i] == '\x1b' && buffer[i + 1] == ']')
300        {
301            char *string;
302            unsigned int command = 0;
303            unsigned int mode = 2, semicolon, final;
304
305            for(semicolon = mode; i + semicolon < size; semicolon++)
306            {
307                if(buffer[i + semicolon] < '0' || buffer[i + semicolon] > '9')
308                    break;
309                command = 10 * command + (buffer[i + semicolon] - '0');
310            }
311
312            if(i + semicolon >= size || buffer[i + semicolon] != ';')
313                break; /* Invalid Mode */
314
315            for(final = semicolon + 1; i + final < size; final++)
316                if(buffer[i + final] < 0x20)
317                    break;
318
319            if(i + final >= size || buffer[i + final] != '\a')
320                break; /* Not enough data or no bell found */
321                /* FIXME: XTerm also reacts to <ESC><backslash> and <ST> */
322                /* FIXME: differenciate between not enough data (try again)
323                 *        and invalid data (print shit) */
324
325            skip += final;
326
327            string = malloc(final - (semicolon + 1) + 1);
328            memcpy(string, buffer + i + (semicolon + 1), final - (semicolon + 1));
329            string[final - (semicolon + 1)] = '\0';
330            debug("ansi import: got OSC command %i string '%s'", command,
331                  string);
332            if(command == 0 || command == 2)
333            {
334                if(sc->title)
335                    free(sc->title);
336                sc->title = string;
337            }
338            else
339                free(string);
340        }
341
342        /* Get the character we’re going to paste */
343        else
344        {
345            unsigned int bytes;
346            char tmp[7];
347
348            if(i + 6 < size)
349                ch = cucul_utf8_to_utf32((char const *)(buffer + i), &bytes);
350            else
351            {
352                /* Add a trailing zero to what we're going to read */
353                memcpy(tmp, buffer + i, size - i);
354                tmp[size - i] = '\0';
355                ch = cucul_utf8_to_utf32(tmp, &bytes);
356            }
357            /* FIXME awful for perfs
358               should get a better way to know if utf8 was valid */
359            memset(tmp, 0, 7);
360            cucul_utf32_to_utf8(tmp, ch);
361            if(strncmp(tmp, (char const *)(buffer + i), strlen(tmp)))
362                bytes = 0;
363            if(!bytes)
364            {
365                /* If the Unicode is invalid, assume it was latin1. */
366                ch = buffer[i];
367                debug("Invalid UTF-8 : %c", ch);
368                bytes = 1;
369            }
370            wch = cucul_utf32_is_fullwidth(ch) ? 2 : 1;
371            skip += bytes - 1;
372        }
373
374        /* Wrap long lines or grow horizontally */
375        while((unsigned int)x + wch > width)
376        {
377            x -= width;
378            y++;
379        }
380
381        /* Scroll or grow vertically */
382        if((unsigned int)y >= height)
383        {
384            int lines = (y - height) + 1;
385
386            savedattr = cucul_get_attr(sc->cv, -1, -1);
387
388            for(j = 0; j + lines < height; j++)
389            {
390                for(k = 0; k < width; k++)
391                {
392                    cucul_put_char(sc->cv, k, j, cucul_get_char(sc->cv, k, j + lines));
393                    cucul_put_attr(sc->cv, k, j, cucul_get_attr(sc->cv, k, j + lines));
394                }
395            }
396            cucul_set_attr(sc->cv, sc->clearattr);
397            cucul_fill_box(sc->cv, 0, height - lines,
398                                   width - 1, height - 1, ' ');
399            y -= lines;
400            cucul_set_attr(sc->cv, savedattr);
401        }
402
403        /* Now paste our character, if any */
404        if(wch)
405        {
406            cucul_put_char(sc->cv, x, y, ch);
407            x += wch;
408        }
409    }
410
411    cucul_gotoxy(sc->cv, x, y);
412
413    return i;
414}
415
416/* XXX : ANSI loader helper */
417
418static void ansi_parse_grcm(struct screen *sc,
419                            unsigned int argc, unsigned int const *argv)
420{
421    static uint8_t const ansi2cucul[] =
422    {
423        CUCUL_BLACK, CUCUL_RED, CUCUL_GREEN, CUCUL_BROWN,
424        CUCUL_BLUE, CUCUL_MAGENTA, CUCUL_CYAN, CUCUL_LIGHTGRAY
425    };
426
427    unsigned int j;
428    uint8_t efg, ebg; /* Effective (libcucul) fg/bg */
429
430    for(j = 0; j < argc; j++)
431    {
432        /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
433        if(argv[j] >= 30 && argv[j] <= 37)
434            sc->fg = ansi2cucul[argv[j] - 30];
435        else if(argv[j] >= 40 && argv[j] <= 47)
436            sc->bg = ansi2cucul[argv[j] - 40];
437        else if(argv[j] >= 90 && argv[j] <= 97)
438            sc->fg = ansi2cucul[argv[j] - 90] + 8;
439        else if(argv[j] >= 100 && argv[j] <= 107)
440            sc->bg = ansi2cucul[argv[j] - 100] + 8;
441        else switch(argv[j])
442        {
443        case 0: /* default rendition */
444            sc->fg = sc->dfg;
445            sc->bg = sc->dbg;
446            sc->bold = sc->blink = sc->italics = sc->negative
447             = sc->concealed = sc->underline = sc->faint = sc->strike
448             = sc->proportional = 0;
449            break;
450        case 1: /* bold or increased intensity */
451            sc->bold = 1;
452            break;
453        case 2: /* faint, decreased intensity or second colour */
454            sc->faint = 1;
455            break;
456        case 3: /* italicized */
457            sc->italics = 1;
458            break;
459        case 4: /* singly underlined */
460            sc->underline = 1;
461            break;
462        case 5: /* slowly blinking (less then 150 per minute) */
463        case 6: /* rapidly blinking (150 per minute or more) */
464            sc->blink = 1;
465            break;
466        case 7: /* negative image */
467            sc->negative = 1;
468            break;
469        case 8: /* concealed characters */
470            sc->concealed = 1;
471            break;
472        case 9: /* crossed-out (characters still legible but marked as to be
473                 * deleted */
474            sc->strike = 1;
475            break;
476        case 21: /* doubly underlined */
477            sc->underline = 1;
478            break;
479        case 22: /* normal colour or normal intensity (neither bold nor
480                  * faint) */
481            sc->bold = sc->faint = 0;
482            break;
483        case 23: /* not italicized, not fraktur */
484            sc->italics = 0;
485            break;
486        case 24: /* not underlined (neither singly nor doubly) */
487            sc->underline = 0;
488            break;
489        case 25: /* steady (not blinking) */
490            sc->blink = 0;
491            break;
492        case 26: /* (reserved for proportional spacing as specified in CCITT
493                  * Recommendation T.61) */
494            sc->proportional = 1;
495            break;
496        case 27: /* positive image */
497            sc->negative = 0;
498            break;
499        case 28: /* revealed characters */
500            sc->concealed = 0;
501            break;
502        case 29: /* not crossed out */
503            sc->strike = 0;
504            break;
505        case 38: /* (reserved for future standardization, intended for setting
506                  * character foreground colour as specified in ISO 8613-6
507                  * [CCITT Recommendation T.416]) */
508            break;
509        case 39: /* default display colour (implementation-defined) */
510            sc->fg = sc->dfg;
511            break;
512        case 48: /* (reserved for future standardization, intended for setting
513                  * character background colour as specified in ISO 8613-6
514                  * [CCITT Recommendation T.416]) */
515            break;
516        case 49: /* default background colour (implementation-defined) */
517            sc->bg = sc->dbg;
518            break;
519        case 50: /* (reserved for cancelling the effect of the rendering
520                  * aspect established by parameter value 26) */
521            sc->proportional = 0;
522            break;
523        default:
524            debug("ansi import: unknown sgr %i", argv[j]);
525            break;
526        }
527    }
528
529    if(sc->concealed)
530    {
531        efg = ebg = CUCUL_TRANSPARENT;
532    }
533    else
534    {
535        efg = sc->negative ? sc->bg : sc->fg;
536        ebg = sc->negative ? sc->fg : sc->bg;
537
538        if(sc->bold)
539        {
540            if(efg < 8)
541                efg += 8;
542            else if(efg == CUCUL_DEFAULT)
543                efg = CUCUL_WHITE;
544        }
545    }
546
547    cucul_set_color_ansi(sc->cv, efg, ebg);
548}
549
550int create_pty(char *cmd, unsigned int w, unsigned int h, int *cpid)
551{
552    char **argv;
553    int fd;
554    pid_t pid;
555
556    pid = forkpty(&fd, NULL, NULL, NULL);
557    if(pid < 0)
558    {
559        fprintf(stderr, "forkpty() error\n");
560        return -1;
561    }
562    else if(pid == 0)
563    {
564        set_tty_size(0, w, h);
565        putenv("CACA_DRIVER=slang");
566        putenv("TERM=xterm");
567        argv = malloc(2 * sizeof(char *));
568        if(!argv)
569        {
570            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
571            return -1;
572        }
573        argv[0] = cmd;
574        argv[1] = NULL;
575        execvp(cmd, argv);
576        fprintf(stderr, "execvp() error\n");
577        return -1;
578    }
579
580    *cpid = pid;
581
582    fcntl(fd, F_SETFL, O_NDELAY);
583    return fd;
584}
585
586#ifdef USE_GRAB
587int create_pty_grab(pid_t pid, unsigned int w, unsigned int h)
588{
589    int fdm, fds;
590
591    int ret = openpty(&fdm, &fds, NULL, NULL, NULL);
592
593    if(ret < 0)
594    {
595        fprintf(stderr, "open() error\n");
596        return -1;
597    }
598
599    set_tty_size(0, w, h);
600    grab_process(pid, ptsname(fdm), fds);
601
602    fcntl(fdm, F_SETFL, O_NDELAY);
603    return fdm;
604}
605#endif
606
607int set_tty_size(int fd, unsigned int w, unsigned int h)
608{
609    struct winsize ws;
610
611    memset(&ws, 0, sizeof(ws));
612    ws.ws_row = h;
613    ws.ws_col = w;
614    ioctl(fd, TIOCSWINSZ, (char *)&ws);
615
616    return 0;
617}
618
619
620
621int update_terms(struct screen_list* screen_list)
622{
623    int i, refresh = 0;
624    for(i = 0; i < screen_list->count; i++)
625    {
626        if(screen_list->screen[i]->total && !screen_list->dont_update_coords)
627        {
628            unsigned long int bytes;
629
630            bytes = import_term(screen_list,
631                                screen_list->screen[i],
632                                screen_list->screen[i]->buf,
633                                screen_list->screen[i]->total);
634
635            if(bytes > 0)
636            {
637                screen_list->screen[i]->total -= bytes;
638                memmove(screen_list->screen[i]->buf,
639                        screen_list->screen[i]->buf + bytes,
640                        screen_list->screen[i]->total);
641                refresh = 1;
642            }
643        }
644    }
645    return refresh;
646}
647
Note: See TracBrowser for help on using the repository browser.