aboutsummaryrefslogtreecommitdiff
path: root/miscutils
diff options
context:
space:
mode:
authorPascal Bellard <pascal.bellard@ads-lu.com>2011-09-13 18:39:04 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2011-09-13 18:39:04 +0200
commitf9e07e70bb086697190ac1dcfb75d99b59a374be (patch)
treea5aff5f5232d5d5a96af562a0eca171be8294ecd /miscutils
parent74b871febbaf8b363c27879bea398cf11da3f48c (diff)
downloadbusybox-w32-f9e07e70bb086697190ac1dcfb75d99b59a374be.tar.gz
busybox-w32-f9e07e70bb086697190ac1dcfb75d99b59a374be.tar.bz2
busybox-w32-f9e07e70bb086697190ac1dcfb75d99b59a374be.zip
conspy: add framebuffer support, -Q option.
Signed-off-by: Pascal Bellard <pascal.bellard@ads-lu.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'miscutils')
-rw-r--r--miscutils/conspy.c114
1 files changed, 71 insertions, 43 deletions
diff --git a/miscutils/conspy.c b/miscutils/conspy.c
index c6ed91832..1a46a4340 100644
--- a/miscutils/conspy.c
+++ b/miscutils/conspy.c
@@ -25,25 +25,31 @@
25//config: or conspy -cs NUM poor man's GNU screen like 25//config: or conspy -cs NUM poor man's GNU screen like
26 26
27//usage:#define conspy_trivial_usage 27//usage:#define conspy_trivial_usage
28//usage: "[-vcsndf] [-x COL] [-y LINE] [CONSOLE_NO]" 28//usage: "[-vcsndfFQ] [-x COL] [-y LINE] [CONSOLE_NO]"
29//usage:#define conspy_full_usage "\n\n" 29//usage:#define conspy_full_usage "\n\n"
30//usage: "A text-mode VNC like program for Linux virtual consoles." 30//usage: "A text-mode VNC like program for Linux virtual consoles."
31//usage: "\nTo exit, quickly press ESC 3 times." 31//usage: "\nTo exit, quickly press ESC 3 times."
32//usage: "\n" 32//usage: "\n"
33//usage: "\n -v Don't send keystrokes to the console" 33//usage: "\n -v Don't send keystrokes to the console"
34//usage: "\n -c Create missing devices in /dev" 34//usage: "\n -c Create missing /dev/{tty,vcsa}N"
35//usage: "\n -s Open a SHELL session" 35//usage: "\n -s Open a SHELL session"
36//usage: "\n -n Black & white" 36//usage: "\n -n Black & white"
37//usage: "\n -d Dump console to stdout" 37//usage: "\n -d Dump console to stdout"
38//usage: "\n -f Follow cursor" 38//usage: "\n -f Follow cursor"
39//usage: "\n -F Assume console is on a framebuffer device"
40//usage: "\n -Q Disable exit on ESC-ESC-ESC"
39//usage: "\n -x COL Starting column" 41//usage: "\n -x COL Starting column"
40//usage: "\n -y LINE Starting line" 42//usage: "\n -y LINE Starting line"
41 43
42#include "libbb.h" 44#include "libbb.h"
43#include <sys/kd.h> 45#include <sys/kd.h>
44 46
45
46#define ESC "\033" 47#define ESC "\033"
48#define CURSOR_ON -1
49#define CURSOR_OFF 1
50
51#define DEV_TTY "/dev/tty"
52#define DEV_VCSA "/dev/vcsa"
47 53
48struct screen_info { 54struct screen_info {
49 unsigned char lines, cols, cursor_x, cursor_y; 55 unsigned char lines, cols, cursor_x, cursor_y;
@@ -72,19 +78,17 @@ struct globals {
72 unsigned col; 78 unsigned col;
73 unsigned line; 79 unsigned line;
74 smallint curoff; // unknown:0 cursor on:-1 cursor off:1 80 smallint curoff; // unknown:0 cursor on:-1 cursor off:1
75 char attrbuf[sizeof(ESC"[0;1;5;30;40m")]; 81 char attrbuf[sizeof("0;1;5;30;40m")];
76 // remote console 82 // remote console
77 struct screen_info remote; 83 struct screen_info remote;
78 // saved local tty terminfo 84 // saved local tty terminfo
79 struct termios term_orig; 85 struct termios term_orig;
80 char vcsa_name[sizeof("/dev/vcsaNN")]; 86 char vcsa_name[sizeof(DEV_VCSA "NN")];
81}; 87};
82 88
83#define G (*ptr_to_globals) 89#define G (*ptr_to_globals)
84#define INIT_G() do { \ 90#define INIT_G() do { \
85 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 91 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
86 G.attrbuf[0] = '\033'; \
87 G.attrbuf[1] = '['; \
88 G.width = G.height = UINT_MAX; \ 92 G.width = G.height = UINT_MAX; \
89 G.last_attr--; \ 93 G.last_attr--; \
90} while (0) 94} while (0)
@@ -92,18 +96,26 @@ struct globals {
92enum { 96enum {
93 FLAG_v, // view only 97 FLAG_v, // view only
94 FLAG_c, // create device if need 98 FLAG_c, // create device if need
99 FLAG_Q, // never exit
95 FLAG_s, // session 100 FLAG_s, // session
96 FLAG_n, // no colors 101 FLAG_n, // no colors
97 FLAG_d, // dump screen 102 FLAG_d, // dump screen
98 FLAG_f, // follow cursor 103 FLAG_f, // follow cursor
104 FLAG_F, // framebuffer
99}; 105};
100#define FLAG(x) (1 << FLAG_##x) 106#define FLAG(x) (1 << FLAG_##x)
101#define BW (option_mask32 & FLAG(n)) 107#define BW (option_mask32 & FLAG(n))
102 108
109static void putcsi(const char *s)
110{
111 fputs(ESC"[", stdout);
112 fputs(s, stdout);
113}
114
103static void clrscr(void) 115static void clrscr(void)
104{ 116{
105 // Home, clear till end of screen 117 // Home, clear till end of screen
106 fputs(ESC"[1;1H" ESC"[J", stdout); 118 putcsi("1;1H" ESC"[J");
107 G.col = G.line = 0; 119 G.col = G.line = 0;
108} 120}
109 121
@@ -111,7 +123,7 @@ static void set_cursor(int state)
111{ 123{
112 if (G.curoff != state) { 124 if (G.curoff != state) {
113 G.curoff = state; 125 G.curoff = state;
114 fputs(ESC"[?25", stdout); 126 putcsi("?25");
115 bb_putchar("h?l"[1 + state]); 127 bb_putchar("h?l"[1 + state]);
116 } 128 }
117} 129}
@@ -125,18 +137,19 @@ static void gotoxy(int col, int line)
125 } 137 }
126} 138}
127 139
140static void cleanup(int code) NORETURN;
128static void cleanup(int code) 141static void cleanup(int code)
129{ 142{
130 set_cursor(-1); // cursor on 143 set_cursor(CURSOR_ON);
131 tcsetattr(G.kbd_fd, TCSANOW, &G.term_orig); 144 tcsetattr(G.kbd_fd, TCSANOW, &G.term_orig);
132 if (ENABLE_FEATURE_CLEAN_UP) { 145 if (ENABLE_FEATURE_CLEAN_UP) {
133 close(G.kbd_fd); 146 close(G.kbd_fd);
134 } 147 }
135 // Reset attributes 148 // Reset attributes
136 if (!BW) 149 if (!BW)
137 fputs(ESC"[0m", stdout); 150 putcsi("0m");
138 bb_putchar('\n'); 151 bb_putchar('\n');
139 if (code > 1) 152 if (code > EXIT_FAILURE)
140 kill_myself_with_sig(code); 153 kill_myself_with_sig(code);
141 exit(code); 154 exit(code);
142} 155}
@@ -157,8 +170,8 @@ static void screen_read_close(void)
157 G.size = i; 170 G.size = i;
158 G.data = xzalloc(2 * i); 171 G.data = xzalloc(2 * i);
159 } 172 }
160 else if (G.size != i) { 173 if (G.size != i) {
161 cleanup(1); 174 cleanup(EXIT_FAILURE);
162 } 175 }
163 data = G.data + G.current; 176 data = G.data + G.current;
164 xread(vcsa_fd, data, G.size); 177 xread(vcsa_fd, data, G.size);
@@ -168,10 +181,15 @@ static void screen_read_close(void)
168 unsigned x = j - G.x; // if will catch j < G.x too 181 unsigned x = j - G.x; // if will catch j < G.x too
169 unsigned y = i - G.y; // if will catch i < G.y too 182 unsigned y = i - G.y; // if will catch i < G.y too
170 183
171 if (CHAR(data) < ' ')
172 CHAR(data) = ' ';
173 if (y >= G.height || x >= G.width) 184 if (y >= G.height || x >= G.width)
174 DATA(data) = 0; 185 DATA(data) = 0;
186 else {
187 uint8_t ch = CHAR(data);
188 if (ch < ' ')
189 CHAR(data) = ch | 0x40;
190 else if (ch > 0x7e)
191 CHAR(data) = '?';
192 }
175 } 193 }
176 } 194 }
177} 195}
@@ -179,10 +197,13 @@ static void screen_read_close(void)
179static void screen_char(char *data) 197static void screen_char(char *data)
180{ 198{
181 if (!BW) { 199 if (!BW) {
200 uint8_t attr_diff;
182 uint8_t attr = ATTR(data); 201 uint8_t attr = ATTR(data);
183 //uint8_t attr = ATTR(data) >> 1; // for framebuffer console
184 uint8_t attr_diff = G.last_attr ^ attr;
185 202
203 if (option_mask32 & FLAG(F)) {
204 attr >>= 1;
205 }
206 attr_diff = G.last_attr ^ attr;
186 if (attr_diff) { 207 if (attr_diff) {
187// Attribute layout for VGA compatible text videobuffer: 208// Attribute layout for VGA compatible text videobuffer:
188// blinking text 209// blinking text
@@ -213,7 +234,7 @@ static void screen_char(char *data)
213 const uint8_t bg_mask = 0x70, blink_mask = 0x80; 234 const uint8_t bg_mask = 0x70, blink_mask = 0x80;
214 char *ptr; 235 char *ptr;
215 236
216 ptr = G.attrbuf + 2; // skip "ESC [" 237 ptr = G.attrbuf;
217 238
218 // (G.last_attr & ~attr) has 1 only where 239 // (G.last_attr & ~attr) has 1 only where
219 // G.last_attr has 1 but attr has 0. 240 // G.last_attr has 1 but attr has 0.
@@ -244,12 +265,12 @@ static void screen_char(char *data)
244 if (attr_diff & bg_mask) { 265 if (attr_diff & bg_mask) {
245 *ptr++ = '4'; 266 *ptr++ = '4';
246 *ptr++ = color[(attr & bg_mask) >> 4]; 267 *ptr++ = color[(attr & bg_mask) >> 4];
247 *ptr++ = ';'; 268 ptr++; // last attribute
248 } 269 }
249 if (ptr != G.attrbuf + 2) { 270 if (ptr != G.attrbuf) {
250 ptr[-1] = 'm'; 271 ptr[-1] = 'm';
251 *ptr = '\0'; 272 *ptr = '\0';
252 fputs(G.attrbuf, stdout); 273 putcsi(G.attrbuf);
253 } 274 }
254 } 275 }
255 } 276 }
@@ -292,11 +313,11 @@ static void curmove(void)
292{ 313{
293 unsigned cx = G.remote.cursor_x - G.x; 314 unsigned cx = G.remote.cursor_x - G.x;
294 unsigned cy = G.remote.cursor_y - G.y; 315 unsigned cy = G.remote.cursor_y - G.y;
295 int cursor = 1; 316 int cursor = CURSOR_OFF;
296 317
297 if (cx < G.width && cy < G.height) { 318 if (cx < G.width && cy < G.height) {
298 gotoxy(cx, cy); 319 gotoxy(cx, cy);
299 cursor = -1; 320 cursor = CURSOR_ON;
300 } 321 }
301 set_cursor(cursor); 322 set_cursor(cursor);
302} 323}
@@ -341,7 +362,7 @@ static NOINLINE void start_shell_in_child(const char* tty_name)
341int conspy_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 362int conspy_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
342int conspy_main(int argc UNUSED_PARAM, char **argv) 363int conspy_main(int argc UNUSED_PARAM, char **argv)
343{ 364{
344 char tty_name[sizeof("/dev/ttyNN")]; 365 char tty_name[sizeof(DEV_TTY "NN")];
345#define keybuf bb_common_bufsiz1 366#define keybuf bb_common_bufsiz1
346 struct termios termbuf; 367 struct termios termbuf;
347 unsigned opts; 368 unsigned opts;
@@ -351,26 +372,28 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
351 static const char getopt_longopts[] ALIGN1 = 372 static const char getopt_longopts[] ALIGN1 =
352 "viewonly\0" No_argument "v" 373 "viewonly\0" No_argument "v"
353 "createdevice\0" No_argument "c" 374 "createdevice\0" No_argument "c"
375 "neverquit\0" No_argument "Q"
354 "session\0" No_argument "s" 376 "session\0" No_argument "s"
355 "nocolors\0" No_argument "n" 377 "nocolors\0" No_argument "n"
356 "dump\0" No_argument "d" 378 "dump\0" No_argument "d"
357 "follow\0" No_argument "f" 379 "follow\0" No_argument "f"
380 "framebuffer\0" No_argument "F"
358 ; 381 ;
359 382
360 applet_long_options = getopt_longopts; 383 applet_long_options = getopt_longopts;
361#endif 384#endif
362 INIT_G(); 385 INIT_G();
363 strcpy(G.vcsa_name, "/dev/vcsa"); 386 strcpy(G.vcsa_name, DEV_VCSA);
364 387
365 opt_complementary = "x+:y+"; // numeric params 388 opt_complementary = "x+:y+"; // numeric params
366 opts = getopt32(argv, "vcsndfx:y:", &G.x, &G.y); 389 opts = getopt32(argv, "vcQsndfFx:y:", &G.x, &G.y);
367 argv += optind; 390 argv += optind;
368 ttynum = 0; 391 ttynum = 0;
369 if (argv[0]) { 392 if (argv[0]) {
370 ttynum = xatou_range(argv[0], 0, 63); 393 ttynum = xatou_range(argv[0], 0, 63);
371 sprintf(G.vcsa_name + sizeof("/dev/vcsa")-1, "%u", ttynum); 394 sprintf(G.vcsa_name + sizeof(DEV_VCSA)-1, "%u", ttynum);
372 } 395 }
373 sprintf(tty_name, "%s%u", "/dev/tty", ttynum); 396 sprintf(tty_name, "%s%u", DEV_TTY, ttynum);
374 if (opts & FLAG(c)) { 397 if (opts & FLAG(c)) {
375 if ((opts & (FLAG(s)|FLAG(v))) != FLAG(v)) 398 if ((opts & (FLAG(s)|FLAG(v))) != FLAG(v))
376 create_cdev_if_doesnt_exist(tty_name, makedev(4, ttynum)); 399 create_cdev_if_doesnt_exist(tty_name, makedev(4, ttynum));
@@ -481,7 +504,7 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
481 char *k; 504 char *k;
482 case -1: 505 case -1:
483 if (errno != EINTR) 506 if (errno != EINTR)
484 cleanup(1); 507 goto abort;
485 break; 508 break;
486 case 0: 509 case 0:
487 if (++G.nokeys >= 4) 510 if (++G.nokeys >= 4)
@@ -492,14 +515,16 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
492 k = keybuf + G.key_count; 515 k = keybuf + G.key_count;
493 bytes_read = read(G.kbd_fd, k, sizeof(keybuf) - G.key_count); 516 bytes_read = read(G.kbd_fd, k, sizeof(keybuf) - G.key_count);
494 if (bytes_read < 0) 517 if (bytes_read < 0)
495 cleanup(1); 518 goto abort;
496 519
497 // Do exit processing 520 // Do exit processing
498 for (i = 0; i < bytes_read; i++) { 521 if (!(option_mask32 & FLAG(Q))) {
499 if (k[i] != '\033') 522 for (i = 0; i < bytes_read; i++) {
500 G.escape_count = 0; 523 if (k[i] != '\033')
501 else if (++G.escape_count >= 3) 524 G.escape_count = -1;
502 cleanup(0); 525 if (++G.escape_count >= 3)
526 cleanup(EXIT_SUCCESS);
527 }
503 } 528 }
504 } 529 }
505 poll_timeout_ms = 250; 530 poll_timeout_ms = 250;
@@ -519,6 +544,7 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
519 if (result >= 0) { 544 if (result >= 0) {
520 char *p = keybuf; 545 char *p = keybuf;
521 546
547 G.ioerror_count = 0;
522 if (kbd_mode != K_XLATE && kbd_mode != K_UNICODE) { 548 if (kbd_mode != K_XLATE && kbd_mode != K_UNICODE) {
523 G.key_count = 0; // scan code mode 549 G.key_count = 0; // scan code mode
524 } 550 }
@@ -534,16 +560,18 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
534 poll_timeout_ms = 20; 560 poll_timeout_ms = 20;
535 } 561 }
536 } 562 }
563 // We sometimes get spurious IO errors on the TTY
564 // as programs close and re-open it
565 else if (errno != EIO || ++G.ioerror_count > 4) {
566 if (ENABLE_FEATURE_CLEAN_UP)
567 close(handle);
568 goto abort;
569 }
537 // Close & re-open tty in case they have 570 // Close & re-open tty in case they have
538 // swapped virtual consoles 571 // swapped virtual consoles
539 close(handle); 572 close(handle);
540
541 // We sometimes get spurious IO errors on the TTY
542 // as programs close and re-open it
543 if (result >= 0)
544 G.ioerror_count = 0;
545 else if (errno != EIO || ++G.ioerror_count > 4)
546 cleanup(1);
547 } 573 }
548 } /* while (1) */ 574 } /* while (1) */
575 abort:
576 cleanup(EXIT_FAILURE);
549} 577}