1 /* s_vcsa.cpp -- Linux /dev/vcsa screen driver
2 
3    This file is part of the UPX executable compressor.
4 
5    Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
6    Copyright (C) 1996-2020 Laszlo Molnar
7    All Rights Reserved.
8 
9    UPX and the UCL library are free software; you can redistribute them
10    and/or modify them under the terms of the GNU General Public License as
11    published by the Free Software Foundation; either version 2 of
12    the License, or (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; see the file COPYING.
21    If not, write to the Free Software Foundation, Inc.,
22    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24    Markus F.X.J. Oberhumer              Laszlo Molnar
25    <markus@oberhumer.com>               <ezerotven+github@gmail.com>
26  */
27 
28 #include "conf.h"
29 
30 #if (USE_SCREEN) && (USE_SCREEN_VCSA)
31 
32 #include "screen.h"
33 
34 #define this self
35 
36 #define mask_fg 0x0f
37 #define mask_bg 0xf0
38 
39 /* #define USE_SCROLLBACK 1 */
40 
41 /*************************************************************************
42 // direct screen access ( /dev/vcsaNN )
43 **************************************************************************/
44 
45 #include <sys/ioctl.h>
46 #include <sys/select.h>
47 #include <termios.h>
48 #if defined(__linux__)
49 #include <linux/kd.h>
50 #include <linux/kdev_t.h>
51 #include <linux/major.h>
52 #endif
53 
54 #define Cell upx_uint16_t
55 
56 struct screen_data_t {
57     int fd;
58     int mode;
59     int page;
60     int cols;
61     int rows;
62     int cursor_x;
63     int cursor_y;
64     int scroll_counter;
65     unsigned char attr;
66     unsigned char init_attr;
67     unsigned char map[256];
68     Cell empty_line[256];
69 #if USE_SCROLLBACK
70     /* scrollback buffer */
71     Cell sb_buf[32][256];
72     int sb_size;
73     int sb_base;
74     int sb_sp;
75 #endif /* USE_SCROLLBACK */
76 };
77 
78 #if USE_SCROLLBACK
sb_add(screen_t * this,int * val,int inc)79 static __inline__ void sb_add(screen_t *this, int *val, int inc) {
80     *val = (*val + inc) & (this->data->sb_size - 1);
81 }
82 
sb_push(screen_t * this,const Cell * line,int len)83 static void sb_push(screen_t *this, const Cell *line, int len) {
84     memcpy(this->data->sb_buf[this->data->sb_sp], line, len);
85     sb_add(this, &this->data->sb_sp, 1);
86     if (this->data->sb_sp == this->data->sb_base)
87         sb_add(this, &this->data->sb_base, 1);
88 }
89 
sb_pop(screen_t * this)90 static const Cell *sb_pop(screen_t *this) {
91     if (this->data->sb_sp == this->data->sb_base)
92         return NULL;
93     sb_add(this, &this->data->sb_sp, -1);
94     return this->data->sb_buf[this->data->sb_sp];
95 }
96 #endif /* USE_SCROLLBACK */
97 
refresh(screen_t * this)98 static void refresh(screen_t *this) { UNUSED(this); }
99 
make_cell(screen_t * this,int ch,int attr)100 static __inline__ Cell make_cell(screen_t *this, int ch, int attr) {
101     return ((attr & 0xff) << 8) | (this->data->map[ch & 0xff] & 0xff);
102 }
103 
getMode(const screen_t * this)104 static int getMode(const screen_t *this) { return this->data->mode; }
105 
getPage(const screen_t * this)106 static int getPage(const screen_t *this) { return this->data->page; }
107 
getRows(const screen_t * this)108 static int getRows(const screen_t *this) { return this->data->rows; }
109 
getCols(const screen_t * this)110 static int getCols(const screen_t *this) { return this->data->cols; }
111 
isMono(const screen_t * this)112 static int isMono(const screen_t *this) {
113     /* FIXME */
114     UNUSED(this);
115     return 0;
116 }
117 
getFg(const screen_t * this)118 static int getFg(const screen_t *this) { return this->data->attr & mask_fg; }
119 
getBg(const screen_t * this)120 static int getBg(const screen_t *this) { return this->data->attr & mask_bg; }
121 
setFg(screen_t * this,int fg)122 static void setFg(screen_t *this, int fg) {
123     this->data->attr = (this->data->attr & mask_bg) | (fg & mask_fg);
124 }
125 
setBg(screen_t * this,int bg)126 static void setBg(screen_t *this, int bg) {
127     this->data->attr = (this->data->attr & mask_fg) | (bg & mask_bg);
128 }
129 
130 /* private */
gotoxy(screen_t * this,int x,int y)131 static int gotoxy(screen_t *this, int x, int y) {
132     if (x >= 0 && y >= 0 && x < this->data->cols && y < this->data->rows) {
133         if (lseek(this->data->fd, 4 + (x + y * this->data->cols) * 2, SEEK_SET) != -1) {
134             return 0;
135         }
136     }
137     return -1;
138 }
139 
setCursor(screen_t * this,int x,int y)140 static void setCursor(screen_t *this, int x, int y) {
141     if (gotoxy(this, x, y) == 0) {
142         unsigned char b[2] = {x, y};
143         if (lseek(this->data->fd, 2, SEEK_SET) != -1)
144             write(this->data->fd, b, 2);
145         this->data->cursor_x = x;
146         this->data->cursor_y = y;
147     }
148 }
149 
getCursor(const screen_t * this,int * x,int * y)150 static void getCursor(const screen_t *this, int *x, int *y) {
151     int cx = this->data->cursor_x;
152     int cy = this->data->cursor_y;
153 #if 1
154     if (lseek(this->data->fd, 2, SEEK_SET) != -1) {
155         unsigned char b[2];
156         if (read(this->data->fd, b, 2) == 2) {
157             if (b[0] < this->data->cols && b[1] < this->data->rows) {
158                 cx = b[0];
159                 cy = b[1];
160             }
161         }
162     }
163 #endif
164     if (x)
165         *x = cx;
166     if (y)
167         *y = cy;
168 }
169 
putCharAttr(screen_t * this,int ch,int attr,int x,int y)170 static void putCharAttr(screen_t *this, int ch, int attr, int x, int y) {
171     Cell a = make_cell(this, ch, attr);
172 
173     if (gotoxy(this, x, y) == 0)
174         write(this->data->fd, &a, 2);
175 }
176 
putChar(screen_t * this,int ch,int x,int y)177 static void putChar(screen_t *this, int ch, int x, int y) {
178     putCharAttr(this, ch, this->data->attr, x, y);
179 }
180 
putStringAttr(screen_t * this,const char * s,int attr,int x,int y)181 static void putStringAttr(screen_t *this, const char *s, int attr, int x, int y) {
182     assert((int) strlen(s) <= 256);
183     assert(x + (int) strlen(s) <= this->data->cols);
184     while (*s)
185         putCharAttr(this, *s++, attr, x++, y);
186 }
187 
putString(screen_t * this,const char * s,int x,int y)188 static void putString(screen_t *this, const char *s, int x, int y) {
189     putStringAttr(this, s, this->data->attr, x, y);
190 }
191 
192 /* private */
getChar(screen_t * this,int * ch,int * attr,int x,int y)193 static void getChar(screen_t *this, int *ch, int *attr, int x, int y) {
194     upx_uint16_t a;
195 
196     if (gotoxy(this, x, y) == 0 && read(this->data->fd, &a, 2) == 2) {
197         if (ch)
198             *ch = a & 0xff;
199         if (attr)
200             *attr = (a >> 8) & 0xff;
201     }
202 }
203 
204 /* private */
init_scrnmap(screen_t * this,int fd)205 static int init_scrnmap(screen_t *this, int fd) {
206     int scrnmap_done = 0;
207     int i;
208 
209 #if 1 && defined(GIO_UNISCRNMAP) && defined(E_TABSZ)
210     if (!scrnmap_done) {
211         upx_uint16_t scrnmap[E_TABSZ];
212         if (ioctl(fd, GIO_UNISCRNMAP, scrnmap) == 0) {
213             for (i = 0; i < E_TABSZ; i++)
214                 this->data->map[scrnmap[i] & 0xff] = i;
215             scrnmap_done = 1;
216         }
217     }
218 #endif
219 #if 1 && defined(GIO_SCRNMAP) && defined(E_TABSZ)
220     if (!scrnmap_done) {
221         unsigned char scrnmap[E_TABSZ];
222         if (ioctl(fd, GIO_SCRNMAP, scrnmap) == 0) {
223             for (i = 0; i < E_TABSZ; i++)
224                 this->data->map[scrnmap[i] & 0xff] = i;
225             scrnmap_done = 1;
226         }
227     }
228 #endif
229 
230     return scrnmap_done;
231 }
232 
init(screen_t * this,int fd)233 static int init(screen_t *this, int fd) {
234     struct stat st;
235 
236     if (!this || !this->data)
237         return -1;
238 
239     this->data->fd = -1;
240     this->data->mode = -1;
241     this->data->page = 0;
242 #if USE_SCROLLBACK
243     this->data->sb_size = 32;
244     this->data->sb_base = 0;
245     this->data->sb_sp = 0;
246 #endif
247     if (fd < 0 || !acc_isatty(fd))
248         return -1;
249     if (fstat(fd, &st) != 0)
250         return -1;
251 
252 /* check if we are running in a virtual console */
253 #if defined(MINOR) && defined(MAJOR) && defined(TTY_MAJOR)
254     if (MAJOR(st.st_rdev) == TTY_MAJOR) {
255         char vc_name[64];
256         unsigned char vc_data[4];
257         int i;
258         int attr;
259         Cell a;
260 
261         upx_snprintf(vc_name, sizeof(vc_name), "/dev/vcsa%d", (int) MINOR(st.st_rdev));
262         this->data->fd = open(vc_name, O_RDWR);
263         if (this->data->fd == -1) {
264             upx_snprintf(vc_name, sizeof(vc_name), "/dev/vcc/a%d", (int) MINOR(st.st_rdev));
265             this->data->fd = open(vc_name, O_RDWR);
266         }
267         if (this->data->fd != -1) {
268             if (read(this->data->fd, vc_data, 4) == 4) {
269                 this->data->mode = 3;
270                 this->data->rows = vc_data[0];
271                 this->data->cols = vc_data[1];
272                 this->data->cursor_x = vc_data[2];
273                 this->data->cursor_y = vc_data[3];
274 
275                 for (i = 0; i < 256; i++)
276                     this->data->map[i] = i;
277                 i = init_scrnmap(this, this->data->fd) || init_scrnmap(this, STDIN_FILENO);
278 
279                 getChar(this, NULL, &attr, this->data->cursor_x, this->data->cursor_y);
280                 this->data->init_attr = attr;
281                 this->data->attr = attr;
282                 a = make_cell(this, ' ', attr);
283                 for (i = 0; i < 256; i++)
284                     this->data->empty_line[i] = a;
285             } else {
286                 close(this->data->fd);
287                 this->data->fd = -1;
288             }
289         }
290     }
291 #endif
292 
293     if (this->data->mode < 0)
294         return -1;
295 
296     return 0;
297 }
298 
finalize(screen_t * this)299 static void finalize(screen_t *this) {
300     if (this->data->fd != -1)
301         (void) close(this->data->fd);
302 }
303 
updateLineN(screen_t * this,const void * line,int y,int len)304 static void updateLineN(screen_t *this, const void *line, int y, int len) {
305     if (len > 0 && len <= 2 * this->data->cols && gotoxy(this, 0, y) == 0) {
306         int i;
307         unsigned char new_line[len];
308         unsigned char *l1 = new_line;
309         const unsigned char *l2 = (const unsigned char *) line;
310 
311         for (i = 0; i < len; i += 2) {
312             *l1++ = *l2++;
313             *l1++ = this->data->map[*l2++];
314         }
315         write(this->data->fd, new_line, len);
316     }
317 }
318 
clearLine(screen_t * this,int y)319 static void clearLine(screen_t *this, int y) {
320     if (gotoxy(this, 0, y) == 0)
321         write(this->data->fd, this->data->empty_line, 2 * this->data->cols);
322 }
323 
clear(screen_t * this)324 static void clear(screen_t *this) {
325     int y;
326 
327     for (y = 0; y < this->data->rows; y++)
328         clearLine(this, y);
329 }
330 
scrollUp(screen_t * this,int lines)331 static int scrollUp(screen_t *this, int lines) {
332     int sr = this->data->rows;
333     int sc = this->data->cols;
334     int y;
335 
336     if (lines <= 0 || lines > sr)
337         return 0;
338 
339 #if USE_SCROLLBACK
340     /* copy to scrollback buffer */
341     for (y = 0; y < lines; y++) {
342         Cell buf[sc];
343         gotoxy(this, 0, y);
344         read(this->data->fd, buf, sizeof(buf));
345         sb_push(this, buf, sizeof(buf));
346     }
347 #endif
348 
349     /* move screen up */
350     if (lines < sr) {
351         Cell buf[(sr - lines) * sc];
352         gotoxy(this, 0, lines);
353         read(this->data->fd, buf, sizeof(buf));
354         gotoxy(this, 0, 0);
355         write(this->data->fd, buf, sizeof(buf));
356     }
357 
358     /* fill in blank lines at bottom */
359     for (y = sr - lines; y < sr; y++)
360         clearLine(this, y);
361 
362     this->data->scroll_counter += lines;
363     return lines;
364 }
365 
scrollDown(screen_t * this,int lines)366 static int scrollDown(screen_t *this, int lines) {
367     int sr = this->data->rows;
368     int sc = this->data->cols;
369     int y;
370 
371     if (lines <= 0 || lines > sr)
372         return 0;
373 
374     /* move screen down */
375     if (lines < sr) {
376         Cell buf[(sr - lines) * sc];
377         gotoxy(this, 0, 0);
378         read(this->data->fd, buf, sizeof(buf));
379         gotoxy(this, 0, lines);
380         write(this->data->fd, buf, sizeof(buf));
381     }
382 
383     /* copy top lines from scrollback buffer */
384     for (y = lines; --y >= 0;) {
385 #if USE_SCROLLBACK
386         const Cell *buf = sb_pop(this);
387         if (buf == NULL)
388             clearLine(this, y);
389         else
390             updateLineN(this, buf, y, sc * 2);
391 #else
392         clearLine(this, y);
393 #endif
394     }
395 
396     this->data->scroll_counter -= lines;
397     return lines;
398 }
399 
getScrollCounter(const screen_t * this)400 static int getScrollCounter(const screen_t *this) { return this->data->scroll_counter; }
401 
getCursorShape(const screen_t * this)402 static int getCursorShape(const screen_t *this) {
403     UNUSED(this);
404     return 0;
405 }
406 
setCursorShape(screen_t * this,int shape)407 static void setCursorShape(screen_t *this, int shape) {
408     UNUSED(this);
409     UNUSED(shape);
410 }
411 
kbhit(screen_t * this)412 static int kbhit(screen_t *this) {
413     const int fd = STDIN_FILENO;
414     const unsigned usec = 0;
415     struct timeval tv;
416     fd_set fds;
417 
418     UNUSED(this);
419     FD_ZERO(&fds);
420     FD_SET(fd, &fds);
421     tv.tv_sec = usec / 1000000;
422     tv.tv_usec = usec % 1000000;
423     return (select(fd + 1, &fds, NULL, NULL, &tv) > 0);
424 }
425 
intro(screen_t * this,void (* show_frames)(screen_t *))426 static int intro(screen_t *this, void (*show_frames)(screen_t *)) {
427     int shape;
428     struct termios term_old, term_new;
429     int term_r;
430 
431     if ((this->data->init_attr & mask_bg) != BG_BLACK)
432         return 0;
433 
434     term_r = tcgetattr(STDIN_FILENO, &term_old);
435     if (term_r == 0) {
436         term_new = term_old;
437         term_new.c_lflag &= ~(ISIG | ICANON | ECHO);
438         tcsetattr(STDIN_FILENO, TCSANOW, &term_new);
439     }
440 
441     shape = getCursorShape(this);
442     setCursorShape(this, 0x2000);
443     show_frames(this);
444     if (this->data->rows > 24)
445         setCursor(this, this->data->cursor_x, this->data->cursor_y + 1);
446     setCursorShape(this, shape);
447 
448     while (kbhit(this))
449         (void) fgetc(stdin);
450     if (term_r == 0)
451         tcsetattr(STDIN_FILENO, TCSANOW, &term_old);
452 
453     return 1;
454 }
455 
456 static const screen_t driver = {sobject_destroy,
457                                 finalize,
458                                 0, /* atExit */
459                                 init,
460                                 refresh,
461                                 getMode,
462                                 getPage,
463                                 getRows,
464                                 getCols,
465                                 isMono,
466                                 getFg,
467                                 getBg,
468                                 getCursor,
469                                 getCursorShape,
470                                 setFg,
471                                 setBg,
472                                 setCursor,
473                                 setCursorShape,
474                                 0, /* hideCursor */
475                                 putChar,
476                                 putCharAttr,
477                                 putString,
478                                 putStringAttr,
479                                 clear,
480                                 clearLine,
481                                 updateLineN,
482                                 scrollUp,
483                                 scrollDown,
484                                 getScrollCounter,
485                                 kbhit,
486                                 intro,
487                                 (struct screen_data_t *) 0};
488 
489 /* public constructor */
screen_vcsa_construct(void)490 screen_t *screen_vcsa_construct(void) { return sobject_construct(&driver, sizeof(*driver.data)); }
491 
492 #endif /* (USE_SCREEN) && (USE_SCREEN_VCSA) */
493 
494 /* vim:set ts=4 sw=4 et: */
495