xref: /freebsd/usr.sbin/vidcontrol/vidcontrol.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
1 /*-
2  * Copyright (c) 1994-1996 S�ren Schmidt
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software withough specific prior written permission
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef lint
30 static const char rcsid[] =
31   "$FreeBSD$";
32 #endif /* not lint */
33 
34 #include <ctype.h>
35 #include <err.h>
36 #include <limits.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sys/fbio.h>
42 #include <sys/consio.h>
43 #include <sys/errno.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include "path.h"
47 #include "decode.h"
48 
49 #define _VESA_800x600_DFL_COLS 80
50 #define _VESA_800x600_DFL_ROWS 25
51 #define _VESA_800x600_DFL_FNSZ 16
52 
53 #define DUMP_RAW	0
54 #define DUMP_TXT	1
55 
56 #define DUMP_FMT_REV	1
57 
58 char 	legal_colors[16][16] = {
59 	"black", "blue", "green", "cyan",
60 	"red", "magenta", "brown", "white",
61 	"grey", "lightblue", "lightgreen", "lightcyan",
62 	"lightred", "lightmagenta", "yellow", "lightwhite"
63 };
64 int 	hex = 0;
65 int 	number;
66 int	vesa_cols = _VESA_800x600_DFL_COLS;
67 int	vesa_rows = _VESA_800x600_DFL_ROWS;
68 char 	letter;
69 struct 	vid_info info;
70 
71 
72 static void
73 usage()
74 {
75 	fprintf(stderr, "%s\n%s\n%s\n%s\n",
76 "usage: vidcontrol [-CdLPpx] [-b color] [-c appearance] [-f [size] file]",
77 "                  [-g geometry] [-h size] [-i adapter | mode] [-l screen_map]",
78 "                  [-m on | off] [-M char] [-r foreground background] [-s num]",
79 "                  [-t N | off] [mode] [foreground [background]] [show]");
80 	exit(1);
81 }
82 
83 char *
84 nextarg(int ac, char **av, int *indp, int oc, int strict)
85 {
86 	if (*indp < ac)
87 		return(av[(*indp)++]);
88 	if (strict != 0)
89 		errx(1, "option requires two arguments -- %c", oc);
90 	return(NULL);
91 }
92 
93 char *
94 mkfullname(const char *s1, const char *s2, const char *s3)
95 {
96 	static char *buf = NULL;
97 	static int bufl = 0;
98 	int f;
99 
100 	f = strlen(s1) + strlen(s2) + strlen(s3) + 1;
101 	if (f > bufl) {
102 		if (buf)
103 			buf = (char *)realloc(buf, f);
104 		else
105 			buf = (char *)malloc(f);
106 	}
107 	if (!buf) {
108 		bufl = 0;
109 		return(NULL);
110 	}
111 
112 	bufl = f;
113 	strcpy(buf, s1);
114 	strcat(buf, s2);
115 	strcat(buf, s3);
116 	return(buf);
117 }
118 
119 void
120 load_scrnmap(char *filename)
121 {
122 	FILE *fd = 0;
123 	int i, size;
124 	char *name;
125 	scrmap_t scrnmap;
126 	char *prefix[]  = {"", "", SCRNMAP_PATH, SCRNMAP_PATH, NULL};
127 	char *postfix[] = {"", ".scm", "", ".scm"};
128 
129 	for (i=0; prefix[i]; i++) {
130 		name = mkfullname(prefix[i], filename, postfix[i]);
131 		fd = fopen(name, "r");
132 		if (fd)
133 			break;
134 	}
135 	if (fd == NULL) {
136 		warn("screenmap file not found");
137 		return;
138 	}
139 	size = sizeof(scrnmap);
140 	if (decode(fd, (char *)&scrnmap, size) != size) {
141 		rewind(fd);
142 		if (fread(&scrnmap, 1, size, fd) != size) {
143 			warnx("bad screenmap file");
144 			fclose(fd);
145 			return;
146 		}
147 	}
148 	if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
149 		warn("can't load screenmap");
150 	fclose(fd);
151 }
152 
153 void
154 load_default_scrnmap()
155 {
156 	scrmap_t scrnmap;
157 	int i;
158 
159 	for (i=0; i<256; i++)
160 		*((char*)&scrnmap + i) = i;
161 	if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
162 		warn("can't load default screenmap");
163 }
164 
165 void
166 print_scrnmap()
167 {
168 	unsigned char map[256];
169 	int i;
170 
171 	if (ioctl(0, GIO_SCRNMAP, &map) < 0) {
172 		warn("getting screenmap");
173 		return;
174 	}
175 	for (i=0; i<sizeof(map); i++) {
176 		if (i > 0 && i % 16 == 0)
177 			fprintf(stdout, "\n");
178 		if (hex)
179 			fprintf(stdout, " %02x", map[i]);
180 		else
181 			fprintf(stdout, " %03d", map[i]);
182 	}
183 	fprintf(stdout, "\n");
184 
185 }
186 
187 int
188 fsize(FILE *file)
189 {
190 	struct stat sb;
191 
192 	if (fstat(fileno(file), &sb) == 0)
193 		return sb.st_size;
194 	else
195 		return -1;
196 }
197 
198 #define DATASIZE(x) ((x).w * (x).h * 256 / 8)
199 
200 void
201 load_font(char *type, char *filename)
202 {
203 	FILE	*fd = NULL;
204 	int	h, i, size, w;
205 	unsigned long io = 0;	/* silence stupid gcc(1) in the Wall mode */
206 	char	*name, *fontmap;
207 	char	*prefix[]  = {"", "", FONT_PATH, FONT_PATH, NULL};
208 	char	*postfix[] = {"", ".fnt", "", ".fnt"};
209 
210 	struct sizeinfo {
211 		int w;
212 		int h;
213 		unsigned long io;
214 	} sizes[] = {{8, 16, PIO_FONT8x16},
215 		     {8, 14, PIO_FONT8x14},
216 		     {8,  8,  PIO_FONT8x8},
217 		     {0,  0,            0}};
218 
219 	for (i=0; prefix[i]; i++) {
220 		name = mkfullname(prefix[i], filename, postfix[i]);
221 		fd = fopen(name, "r");
222 		if (fd)
223 			break;
224 	}
225 	if (fd == NULL) {
226 		warn("%s: can't load font file", filename);
227 		return;
228 	}
229 	if (type != NULL) {
230 		size = 0;
231 		if (sscanf(type, "%dx%d", &w, &h) == 2)
232 			for (i = 0; sizes[i].w != 0; i++)
233 				if (sizes[i].w == w && sizes[i].h == h) {
234 					size = DATASIZE(sizes[i]);
235 					io = sizes[i].io;
236 				}
237 
238 		if (size == 0) {
239 			warnx("%s: bad font size specification", type);
240 			fclose(fd);
241 			return;
242 		}
243 	} else {
244 		/* Apply heuristics */
245 		int j;
246 		int dsize[2];
247 
248 		size = DATASIZE(sizes[0]);
249 		fontmap = (char*) malloc(size);
250 		dsize[0] = decode(fd, fontmap, size);
251 		dsize[1] = fsize(fd);
252 		free(fontmap);
253 
254 		size = 0;
255 		for (j = 0; j < 2; j++)
256 			for (i = 0; sizes[i].w != 0; i++)
257 				if (DATASIZE(sizes[i]) == dsize[j]) {
258 					size = dsize[j];
259 					io = sizes[i].io;
260 					j = 2;	/* XXX */
261 					break;
262 				}
263 
264 		if (size == 0) {
265 			warnx("%s: can't guess font size", filename);
266 			fclose(fd);
267 			return;
268 		}
269 		rewind(fd);
270 	}
271 
272 	fontmap = (char*) malloc(size);
273 	if (decode(fd, fontmap, size) != size) {
274 		rewind(fd);
275 		if (fsize(fd) != size || fread(fontmap, 1, size, fd) != size) {
276 			warnx("%s: bad font file", filename);
277 			fclose(fd);
278 			free(fontmap);
279 			return;
280 		}
281 	}
282 	if (ioctl(0, io, fontmap) < 0)
283 		warn("can't load font");
284 	fclose(fd);
285 	free(fontmap);
286 }
287 
288 void
289 set_screensaver_timeout(char *arg)
290 {
291 	int nsec;
292 
293 	if (!strcmp(arg, "off"))
294 		nsec = 0;
295 	else {
296 		nsec = atoi(arg);
297 		if ((*arg == '\0') || (nsec < 1)) {
298 			warnx("argument must be a positive number");
299 			return;
300 		}
301 	}
302 	if (ioctl(0, CONS_BLANKTIME, &nsec) == -1)
303 		warn("setting screensaver period");
304 }
305 
306 void
307 set_cursor_type(char *appearence)
308 {
309 	int type;
310 
311 	if (!strcmp(appearence, "normal"))
312 		type = 0;
313 	else if (!strcmp(appearence, "blink"))
314 		type = 1;
315 	else if (!strcmp(appearence, "destructive"))
316 		type = 3;
317 	else {
318 		warnx("argument to -c must be normal, blink or destructive");
319 		return;
320 	}
321 	ioctl(0, CONS_CURSORTYPE, &type);
322 }
323 
324 void
325 video_mode(int argc, char **argv, int *index)
326 {
327 	static struct {
328 		char *name;
329 		unsigned long mode;
330 	} modes[] = {
331 		{ "80x25",		SW_TEXT_80x25 },
332 		{ "80x30",		SW_TEXT_80x30 },
333 		{ "80x43",		SW_TEXT_80x43 },
334 		{ "80x50",		SW_TEXT_80x50 },
335 		{ "80x60",		SW_TEXT_80x60 },
336 		{ "132x25",		SW_TEXT_132x25 },
337 		{ "132x30",		SW_TEXT_132x30 },
338 		{ "132x43",		SW_TEXT_132x43 },
339 		{ "132x50",		SW_TEXT_132x50 },
340 		{ "132x60",		SW_TEXT_132x60 },
341 		{ "VGA_40x25",		SW_VGA_C40x25 },
342 		{ "VGA_80x25",		SW_VGA_C80x25 },
343 		{ "VGA_80x30",		SW_VGA_C80x30 },
344 		{ "VGA_80x50",		SW_VGA_C80x50 },
345 		{ "VGA_80x60",		SW_VGA_C80x60 },
346 #ifdef SW_VGA_C90x25
347 		{ "VGA_90x25",		SW_VGA_C90x25 },
348 		{ "VGA_90x30",		SW_VGA_C90x30 },
349 		{ "VGA_90x43",		SW_VGA_C90x43 },
350 		{ "VGA_90x50",		SW_VGA_C90x50 },
351 		{ "VGA_90x60",		SW_VGA_C90x60 },
352 #endif
353 		{ "VGA_320x200",	SW_VGA_CG320 },
354 		{ "EGA_80x25",		SW_ENH_C80x25 },
355 		{ "EGA_80x43",		SW_ENH_C80x43 },
356 		{ "VESA_132x25",	SW_VESA_C132x25 },
357 		{ "VESA_132x43",	SW_VESA_C132x43 },
358 		{ "VESA_132x50",	SW_VESA_C132x50 },
359 		{ "VESA_132x60",	SW_VESA_C132x60 },
360 		{ "VESA_800x600",	SW_VESA_800x600 },
361 		{ NULL },
362 	};
363 	unsigned long mode = 0;
364 	int cur_mode;
365 	int ioerr;
366 	int size[3];
367 	int i;
368 
369 	if (ioctl(0, CONS_GET, &cur_mode) < 0)
370 		err(1, "cannot get the current video mode");
371 	if (*index < argc) {
372 		for (i = 0; modes[i].name != NULL; ++i) {
373 			if (!strcmp(argv[*index], modes[i].name)) {
374 				mode = modes[i].mode;
375 				break;
376 			}
377 		}
378 		if (modes[i].name == NULL)
379 			return;
380 		if (ioctl(0, mode, NULL) < 0)
381 			warn("cannot set videomode");
382 		if (mode == SW_VESA_800x600) {
383 			/* columns */
384 			if ((vesa_cols * 8 > 800) || (vesa_cols <= 0)) {
385 				warnx("incorrect number of columns: %d",
386 				      vesa_cols);
387 				size[0] = _VESA_800x600_DFL_COLS;
388 			} else {
389 				size[0] = vesa_cols;
390 			}
391 			/* rows */
392 			if ((vesa_rows * _VESA_800x600_DFL_FNSZ > 600) ||
393 			    (vesa_rows <=0)) {
394 				warnx("incorrect number of rows: %d",
395 				      vesa_rows);
396 				size[1] = _VESA_800x600_DFL_ROWS;
397 			} else {
398 				size[1] = vesa_rows;
399 			}
400 			/* font size */
401 			size[2] = _VESA_800x600_DFL_FNSZ;
402 			if (ioctl(0, KDRASTER, size)) {
403 				ioerr = errno;
404 				if (cur_mode >= M_VESA_BASE)
405 					ioctl(0,
406 					    _IO('V', cur_mode - M_VESA_BASE),
407 					    NULL);
408 				else
409 					ioctl(0, _IO('S', cur_mode), NULL);
410 				warnc(ioerr, "cannot activate raster display");
411 			}
412 		}
413 		(*index)++;
414 	}
415 	return;
416 }
417 
418 int
419 get_color_number(char *color)
420 {
421 	int i;
422 
423 	for (i=0; i<16; i++)
424 		if (!strcmp(color, legal_colors[i]))
425 			return i;
426 	return -1;
427 }
428 
429 void
430 set_normal_colors(int argc, char **argv, int *index)
431 {
432 	int color;
433 
434 	if (*index < argc && (color = get_color_number(argv[*index])) != -1) {
435 		(*index)++;
436 		fprintf(stderr, "[=%dF", color);
437 		if (*index < argc
438 		    && (color = get_color_number(argv[*index])) != -1
439 		    && color < 8) {
440 			(*index)++;
441 			fprintf(stderr, "[=%dG", color);
442 		}
443 	}
444 }
445 
446 void
447 set_reverse_colors(int argc, char **argv, int *index)
448 {
449 	int color;
450 
451 	if ((color = get_color_number(argv[*(index)-1])) != -1) {
452 		fprintf(stderr, "[=%dH", color);
453 		if (*index < argc
454 		    && (color = get_color_number(argv[*index])) != -1
455 		    && color < 8) {
456 			(*index)++;
457 			fprintf(stderr, "[=%dI", color);
458 		}
459 	}
460 }
461 
462 void
463 set_console(char *arg)
464 {
465 	int n;
466 
467 	if( !arg || strspn(arg,"0123456789") != strlen(arg)) {
468 		warnx("bad console number");
469 		return;
470 	}
471 
472 	n = atoi(arg);
473 	if (n < 1 || n > 16) {
474 		warnx("console number out of range");
475 	} else if (ioctl(0, VT_ACTIVATE, (caddr_t) (long) n) == -1)
476 		warn("ioctl(VT_ACTIVATE)");
477 }
478 
479 void
480 set_border_color(char *arg)
481 {
482 	int color;
483 
484 	if ((color = get_color_number(arg)) != -1) {
485 		fprintf(stderr, "[=%dA", color);
486 	}
487 	else
488 		usage();
489 }
490 
491 void
492 set_mouse_char(char *arg)
493 {
494 	struct mouse_info mouse;
495 	long l;
496 
497 	l = strtol(arg, NULL, 0);
498 	if ((l < 0) || (l > UCHAR_MAX - 3)) {
499 		warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3);
500 		return;
501 	}
502 	mouse.operation = MOUSE_MOUSECHAR;
503 	mouse.u.mouse_char = (int)l;
504 	ioctl(0, CONS_MOUSECTL, &mouse);
505 }
506 
507 void
508 set_mouse(char *arg)
509 {
510 	struct mouse_info mouse;
511 
512 	if (!strcmp(arg, "on"))
513 		mouse.operation = MOUSE_SHOW;
514 	else if (!strcmp(arg, "off"))
515 		mouse.operation = MOUSE_HIDE;
516 	else {
517 		warnx("argument to -m must either on or off");
518 		return;
519 	}
520 	ioctl(0, CONS_MOUSECTL, &mouse);
521 }
522 
523 static char
524 *adapter_name(int type)
525 {
526     static struct {
527 	int type;
528 	char *name;
529     } names[] = {
530 	{ KD_MONO,	"MDA" },
531 	{ KD_HERCULES,	"Hercules" },
532 	{ KD_CGA,	"CGA" },
533 	{ KD_EGA,	"EGA" },
534 	{ KD_VGA,	"VGA" },
535 	{ KD_PC98,	"PC-98xx" },
536 	{ KD_TGA,	"TGA" },
537 	{ -1,		"Unknown" },
538     };
539     int i;
540 
541     for (i = 0; names[i].type != -1; ++i)
542 	if (names[i].type == type)
543 	    break;
544     return names[i].name;
545 }
546 
547 void
548 show_adapter_info(void)
549 {
550 	struct video_adapter_info ad;
551 
552 	ad.va_index = 0;
553 	if (ioctl(0, CONS_ADPINFO, &ad)) {
554 		warn("failed to obtain adapter information");
555 		return;
556 	}
557 
558 	printf("fb%d:\n", ad.va_index);
559 	printf("    %.*s%d, type:%s%s (%d), flags:0x%x\n",
560 	       (int)sizeof(ad.va_name), ad.va_name, ad.va_unit,
561 	       (ad.va_flags & V_ADP_VESA) ? "VESA " : "",
562 	       adapter_name(ad.va_type), ad.va_type, ad.va_flags);
563 	printf("    initial mode:%d, current mode:%d, BIOS mode:%d\n",
564 	       ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode);
565 	printf("    frame buffer window:0x%x, buffer size:0x%x\n",
566 	       ad.va_window, ad.va_buffer_size);
567 	printf("    window size:0x%x, origin:0x%x\n",
568 	       ad.va_window_size, ad.va_window_orig);
569 	printf("    display start address (%d, %d), scan line width:%d\n",
570 	       ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width);
571 	printf("    reserved:0x%x\n", ad.va_unused0);
572 }
573 
574 void
575 show_mode_info(void)
576 {
577 	struct video_info info;
578 	char buf[80];
579 	int mode;
580 	int c;
581 
582 	printf("    mode#     flags   type    size       "
583 	       "font      window      linear buffer\n");
584 	printf("---------------------------------------"
585 	       "---------------------------------------\n");
586 	for (mode = 0; mode < M_VESA_MODE_MAX; ++mode) {
587 		info.vi_mode = mode;
588 		if (ioctl(0, CONS_MODEINFO, &info))
589 			continue;
590 		if (info.vi_mode != mode)
591 			continue;
592 
593 		printf("%3d (0x%03x)", mode, mode);
594     		printf(" 0x%08x", info.vi_flags);
595 		if (info.vi_flags & V_INFO_GRAPHICS) {
596 			c = 'G';
597 			snprintf(buf, sizeof(buf), "%dx%dx%d %d",
598 				 info.vi_width, info.vi_height,
599 				 info.vi_depth, info.vi_planes);
600 		} else {
601 			c = 'T';
602 			snprintf(buf, sizeof(buf), "%dx%d",
603 				 info.vi_width, info.vi_height);
604 		}
605 		printf(" %c %-15s", c, buf);
606 		snprintf(buf, sizeof(buf), "%dx%d",
607 			 info.vi_cwidth, info.vi_cheight);
608 		printf(" %-5s", buf);
609     		printf(" 0x%05x %2dk %2dk",
610 		       info.vi_window, (int)info.vi_window_size/1024,
611 		       (int)info.vi_window_gran/1024);
612     		printf(" 0x%08x %dk\n",
613 		       info.vi_buffer, (int)info.vi_buffer_size/1024);
614 	}
615 }
616 
617 void
618 show_info(char *arg)
619 {
620 	if (!strcmp(arg, "adapter"))
621 		show_adapter_info();
622 	else if (!strcmp(arg, "mode"))
623 		show_mode_info();
624 	else {
625 		warnx("argument to -i must either adapter or mode");
626 		return;
627 	}
628 }
629 
630 void
631 test_frame()
632 {
633 	int i;
634 
635 	fprintf(stdout, "[=0G\n\n");
636 	for (i=0; i<8; i++) {
637 		fprintf(stdout, "[=15F[=0G        %2d [=%dF%-16s"
638 				"[=15F[=0G        %2d [=%dF%-16s        "
639 				"[=15F %2d [=%dGBACKGROUND[=0G\n",
640 			i, i, legal_colors[i], i+8, i+8,
641 			legal_colors[i+8], i, i);
642 	}
643 	fprintf(stdout, "[=%dF[=%dG[=%dH[=%dI\n",
644 		info.mv_norm.fore, info.mv_norm.back,
645 		info.mv_rev.fore, info.mv_rev.back);
646 }
647 
648 /*
649  * Snapshot the video memory of that terminal, using the CONS_SCRSHOT
650  * ioctl, and writes the results to stdout either in the special
651  * binary format (see manual page for details), or in the plain
652  * text format.
653  */
654 void
655 dump_screen(int mode)
656 {
657 	scrshot_t shot;
658 	vid_info_t info;
659 
660 	info.size = sizeof(info);
661 	if (ioctl(0, CONS_GETINFO, &info) == -1) {
662 		warn("failed to obtain current video mode parameters");
663 		return;
664 	}
665 
666 	shot.buf = alloca(info.mv_csz * info.mv_rsz * sizeof(u_int16_t));
667 	if (shot.buf == NULL) {
668 		warn("failed to allocate memory for dump");
669 		return;
670 	}
671 
672 	shot.xsize = info.mv_csz;
673 	shot.ysize = info.mv_rsz;
674 	if (ioctl(0, CONS_SCRSHOT, &shot) == -1) {
675 		warn("failed to get dump of the screen");
676 		return;
677 	}
678 
679 	if (mode == DUMP_RAW) {
680 		printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2,
681 		       shot.xsize, shot.ysize);
682 		fflush(stdout);
683 
684 		(void)write(STDOUT_FILENO, shot.buf,
685 			    shot.xsize * shot.ysize * sizeof(u_int16_t));
686 	} else {
687 		char *line;
688 		int x, y;
689 		u_int16_t ch;
690 
691 		line = alloca(shot.xsize + 1);
692 		if (line == NULL) {
693 			warn("failed to allocate memory for line buffer");
694 			return;
695 		}
696 
697 		for (y = 0; y < shot.ysize; y++) {
698 			for (x = 0; x < shot.xsize; x++) {
699 				ch = shot.buf[x + (y * shot.xsize)];
700 				ch &= 0xff;
701 				if (isprint(ch) == 0)
702 					ch = ' ';
703 				line[x] = (char)ch;
704 			}
705 
706 			/* Trim trailing spaces */
707 			do {
708 				line[x--] = '\0';
709 			} while (line[x] == ' ' && x != 0);
710 
711 			puts(line);
712 		}
713 		fflush(stdout);
714 	}
715 
716 	return;
717 }
718 
719 void
720 set_history(char *opt)
721 {
722 	int size;
723 
724 	size = atoi(opt);
725 	if ((*opt == '\0') || size < 0) {
726 		warnx("argument must be a positive number");
727 		return;
728 	}
729 	if (ioctl(0, CONS_HISTORY, &size) == -1)
730 		warn("setting history buffer size");
731 }
732 
733 void
734 clear_history()
735 {
736 
737 	if (ioctl(0, CONS_CLRHIST) == -1)
738 		warn("clear history buffer");
739 }
740 
741 int
742 main(int argc, char **argv)
743 {
744 	char	*font, *type;
745 	int	opt;
746 
747 
748 	info.size = sizeof(info);
749 	if (argc == 1)
750 		usage();
751 		/* Not reached */
752 	if (ioctl(0, CONS_GETINFO, &info) < 0)
753 		err(1, "must be on a virtual console");
754 	while((opt = getopt(argc, argv, "b:Cc:df:g:h:i:l:LM:m:pPr:s:t:x")) != -1)
755 		switch(opt) {
756 		case 'b':
757 			set_border_color(optarg);
758 			break;
759 		case 'C':
760 			clear_history();
761 			break;
762 		case 'c':
763 			set_cursor_type(optarg);
764 			break;
765 		case 'd':
766 			print_scrnmap();
767 			break;
768 		case 'f':
769 			type = optarg;
770 			font = nextarg(argc, argv, &optind, 'f', 0);
771 			if (font == NULL) {
772 				type = NULL;
773 				font = optarg;
774 			}
775 			load_font(type, font);
776 			break;
777 		case 'g':
778 			if (sscanf(optarg, "%dx%d", &vesa_cols,
779 			    &vesa_rows) != 2) {
780 				warnx("incorrect geometry: %s", optarg);
781 				usage();
782 			}
783 			break;
784 		case 'h':
785 			set_history(optarg);
786 			break;
787 		case 'i':
788 			show_info(optarg);
789 			break;
790 		case 'l':
791 			load_scrnmap(optarg);
792 			break;
793 		case 'L':
794 			load_default_scrnmap();
795 			break;
796 		case 'M':
797 			set_mouse_char(optarg);
798 			break;
799 		case 'm':
800 			set_mouse(optarg);
801 			break;
802 		case 'p':
803 			dump_screen(DUMP_RAW);
804 			break;
805 		case 'P':
806 			dump_screen(DUMP_TXT);
807 			break;
808 		case 'r':
809 			set_reverse_colors(argc, argv, &optind);
810 			break;
811 		case 's':
812 			set_console(optarg);
813 			break;
814 		case 't':
815 			set_screensaver_timeout(optarg);
816 			break;
817 		case 'x':
818 			hex = 1;
819 			break;
820 		default:
821 			usage();
822 		}
823 	video_mode(argc, argv, &optind);
824 	set_normal_colors(argc, argv, &optind);
825 	if (optind < argc && !strcmp(argv[optind], "show")) {
826 		test_frame();
827 		optind++;
828 	}
829 	if ((optind != argc) || (argc == 1))
830 		usage();
831 	return 0;
832 }
833 
834