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