1 /*
2 LICENSE INFORMATION:
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Lesser General Public
5 License (LGPL) as published by the Free Software Foundation.
6 
7 Please refer to the COPYING file for more information.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 General Public License for more details.
13 
14 You should have received a copy of the GNU Lesser General Public
15 License along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 
18 Copyright (c) 2004 Bruno T. C. de Oliveira
19 */
20 
21 
22 #include "rote.h"
23 #include "roteprivate.h"
24 #include <sys/types.h>
25 #include <sys/ioctl.h>
26 #include <termios.h>
27 #include <libutil.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdbool.h>
32 #include <sys/time.h>
33 
34 #define ROTE_VT_UPDATE_ITERATIONS 5
35 
rote_vt_create(int rows,int cols)36 RoteTerm *rote_vt_create(int rows, int cols) {
37    RoteTerm *rt;
38    int i, j;
39 
40    if (rows <= 0 || cols <= 0) return NULL;
41 
42    if (! (rt = (RoteTerm*) malloc(sizeof(RoteTerm))) ) return NULL;
43    memset(rt, 0, sizeof(RoteTerm));
44 
45    /* record dimensions */
46    rt->rows = rows;
47    rt->cols = cols;
48 
49    /* create the cell matrix */
50    rt->cells = (RoteCell**) malloc(sizeof(RoteCell*) * rt->rows);
51    for (i = 0; i < rt->rows; i++) {
52       /* create row */
53       rt->cells[i] = (RoteCell*) malloc(sizeof(RoteCell) * rt->cols);
54 
55       /* fill row with spaces */
56       for (j = 0; j < rt->cols; j++) {
57          rt->cells[i][j].ch = 0x20;    /* a space */
58          rt->cells[i][j].attr = 0x70;  /* white text, black background */
59       }
60    }
61 
62    /* allocate dirtiness array */
63    rt->line_dirty = malloc(sizeof(bool) * rt->rows);
64 
65    /* initialization of other public fields */
66    rt->crow = rt->ccol = 0;
67    rt->curattr = 0x70;  /* white text over black background */
68 
69    /* allocate private data */
70    rt->pd = (RoteTermPrivate*) malloc(sizeof(RoteTermPrivate));
71    memset(rt->pd, 0, sizeof(RoteTermPrivate));
72 
73    rt->pd->pty = -1;  /* no pty for now */
74 
75    /* initial scrolling area is the whole window */
76    rt->pd->scrolltop = 0;
77    rt->pd->scrollbottom = rt->rows - 1;
78 
79    #ifdef DEBUG
80    fprintf(stderr, "Created a %d x %d terminal.\n", rt->rows, rt->cols);
81    #endif
82 
83    return rt;
84 }
85 
rote_vt_destroy(RoteTerm * rt)86 void rote_vt_destroy(RoteTerm *rt) {
87    int i;
88    if (!rt) return;
89 
90    free(rt->pd);
91    free(rt->line_dirty);
92    for (i = 0; i < rt->rows; i++) free(rt->cells[i]);
93    free(rt->cells);
94    free(rt);
95 }
96 
default_cur_set_attr(WINDOW * win,unsigned char attr)97 static void default_cur_set_attr(WINDOW *win, unsigned char attr) {
98    int cp = ROTE_ATTR_BG(attr) * 8 + 7 - ROTE_ATTR_FG(attr);
99    if (!cp) wattrset(win, A_NORMAL);
100    else     wattrset(win, COLOR_PAIR(cp));
101 
102    if (ROTE_ATTR_BOLD(attr))     wattron(win, A_BOLD);
103    if (ROTE_ATTR_BLINK(attr))    wattron(win, A_BLINK);
104 }
105 
ensure_printable(unsigned char ch)106 static inline unsigned char ensure_printable(unsigned char ch)
107                                         { return ch >= 32 ? ch : 32; }
108 
rote_vt_draw(RoteTerm * rt,WINDOW * win,int srow,int scol,void (* cur_set_attr)(WINDOW *,unsigned char))109 void rote_vt_draw(RoteTerm *rt, WINDOW *win, int srow, int scol,
110                                 void (*cur_set_attr)(WINDOW*,unsigned char)) {
111 
112    int i, j;
113    rote_vt_update(rt);
114 
115    if (!cur_set_attr) cur_set_attr = default_cur_set_attr;
116    for (i = 0; i < rt->rows; i++) {
117       wmove(win, srow + i, scol);
118       for (j = 0; j < rt->cols; j++) {
119          (*cur_set_attr)(win, rt->cells[i][j].attr);
120          waddch(win, ensure_printable(rt->cells[i][j].ch));
121       }
122    }
123 
124    wmove(win, srow + rt->crow, scol + rt->ccol);
125 }
126 
rote_vt_forkpty(RoteTerm * rt,const char * command)127 pid_t rote_vt_forkpty(RoteTerm *rt, const char *command) {
128    struct winsize ws;
129    pid_t childpid;
130 
131    ws.ws_row = rt->rows;
132    ws.ws_col = rt->cols;
133    ws.ws_xpixel = ws.ws_ypixel = 0;
134 
135    childpid = forkpty(&rt->pd->pty, NULL, NULL, &ws);
136    if (childpid < 0) return -1;
137 
138    if (childpid == 0) {
139       /* we are the child, running under the slave side of the pty. */
140 
141       /* Cajole application into using linux-console-compatible escape
142        * sequences (which is what we are prepared to interpret) */
143       setenv("TERM", "linux", 1);
144 
145       /* Now we will exec /bin/sh -c command. */
146       execl("/bin/sh", "/bin/sh", "-c", command, NULL);
147 
148       fprintf(stderr, "\nexecl() failed.\nCommand: '%s'\n", command);
149       exit(127);  /* error exec'ing */
150    }
151 
152    /* if we got here we are the parent process */
153    rt->childpid = childpid;
154    return childpid;
155 }
156 
rote_vt_forsake_child(RoteTerm * rt)157 void rote_vt_forsake_child(RoteTerm *rt) {
158    if (rt->pd->pty >= 0) close(rt->pd->pty);
159    rt->pd->pty = -1;
160    rt->childpid = 0;
161 }
162 
rote_vt_update(RoteTerm * rt)163 void rote_vt_update(RoteTerm *rt) {
164    fd_set ifs;
165    struct timeval tvzero;
166    char buf[512];
167    int bytesread;
168    int n = ROTE_VT_UPDATE_ITERATIONS;
169    if (rt->pd->pty < 0) return;  /* nothing to pump */
170 
171    while (n--) { /* iterate at most ROVE_VT_UPDATE_ITERATIONS times.
172                   * As Phil Endecott pointed out, if we don't restrict this,
173                   * a program that floods the terminal with output
174                   * could cause this loop to iterate forever, never
175                   * being able to catch up. So we'll rely on the client
176                   * calling rote_vt_update often, as the documentation
177                   * recommends :-) */
178 
179       /* check if pty has something to say */
180       FD_ZERO(&ifs); FD_SET(rt->pd->pty, &ifs);
181       tvzero.tv_sec = 0; tvzero.tv_usec = 0;
182 
183       if (select(rt->pd->pty + 1, &ifs, NULL, NULL, &tvzero) <= 0)
184          return; /* nothing to read, or select() failed */
185 
186       /* read what we can. This is guaranteed not to block, since
187        * select() told us there was something to read. */
188       bytesread = read(rt->pd->pty, buf, 512);
189       if (bytesread <= 0) return;
190 
191       /* inject the data into the terminal */
192       rote_vt_inject(rt, buf, bytesread);
193    }
194 }
195 
rote_vt_write(RoteTerm * rt,const char * data,int len)196 void rote_vt_write(RoteTerm *rt, const char *data, int len) {
197    if (rt->pd->pty < 0) {
198       /* no pty, so just inject the data plain and simple */
199       rote_vt_inject(rt, data, len);
200       return;
201    }
202 
203    /* write data to pty. Keep calling write() until we have written
204     * everything. */
205    while (len > 0) {
206       int byteswritten = write(rt->pd->pty, data, len);
207       if (byteswritten < 0) {
208          /* very ugly way to inform the error. Improvements welcome! */
209          static char errormsg[] = "\n(ROTE: pty write() error)\n";
210          rote_vt_inject(rt, errormsg, strlen(errormsg));
211          return;
212       }
213 
214       data += byteswritten;
215       len  -= byteswritten;
216    }
217 }
218 
rote_vt_install_handler(RoteTerm * rt,rote_es_handler_t handler)219 void rote_vt_install_handler(RoteTerm *rt, rote_es_handler_t handler) {
220    rt->pd->handler = handler;
221 }
222 
rote_vt_take_snapshot(RoteTerm * rt)223 void *rote_vt_take_snapshot(RoteTerm *rt) {
224    int i;
225    int bytes_per_row = sizeof(RoteCell) * rt->cols;
226    void *buf = malloc(bytes_per_row * rt->rows);
227    void *ptr = buf;
228 
229    for (i = 0; i < rt->rows; i++, ptr += bytes_per_row)
230       memcpy(ptr, rt->cells[i], bytes_per_row);
231 
232    return buf;
233 }
234 
rote_vt_restore_snapshot(RoteTerm * rt,void * snapbuf)235 void rote_vt_restore_snapshot(RoteTerm *rt, void *snapbuf) {
236    int i;
237    int bytes_per_row = sizeof(RoteCell) * rt->cols;
238 
239    for (i = 0; i < rt->rows; i++, snapbuf += bytes_per_row) {
240       rt->line_dirty[i] = true;
241       memcpy(rt->cells[i], snapbuf, bytes_per_row);
242    }
243 }
244 
rote_vt_get_pty_fd(RoteTerm * rt)245 int rote_vt_get_pty_fd(RoteTerm *rt) {
246    return rt->pd->pty;
247 }
248 
249