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