xref: /freebsd/usr.sbin/vidcontrol/vidcontrol.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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, _IO('V', cur_mode), 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