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

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