xref: /freebsd/usr.sbin/vidcontrol/vidcontrol.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*-
2  * Copyright (c) 1994-1996 S�ren Schmidt
3  * All rights reserved.
4  *
5  * Portions of this software are based in part on the work of
6  * Sascha Wildner <saw@online.de> contributed to The DragonFly Project
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer,
13  *    in this position and unchanged.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $DragonFly: src/usr.sbin/vidcontrol/vidcontrol.c,v 1.10 2005/03/02 06:08:29 joerg Exp $
32  */
33 
34 #ifndef lint
35 static const char rcsid[] =
36   "$FreeBSD$";
37 #endif /* not lint */
38 
39 #include <ctype.h>
40 #include <err.h>
41 #include <limits.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <sys/fbio.h>
47 #include <sys/consio.h>
48 #include <sys/errno.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include "path.h"
52 #include "decode.h"
53 
54 
55 #define	DATASIZE(x)	((x).w * (x).h * 256 / 8)
56 
57 /* Screen dump modes */
58 #define DUMP_FMT_RAW	1
59 #define DUMP_FMT_TXT	2
60 /* Screen dump options */
61 #define DUMP_FBF	0
62 #define DUMP_ALL	1
63 /* Screen dump file format revision */
64 #define DUMP_FMT_REV	1
65 
66 char 	legal_colors[16][16] = {
67 	"black", "blue", "green", "cyan",
68 	"red", "magenta", "brown", "white",
69 	"grey", "lightblue", "lightgreen", "lightcyan",
70 	"lightred", "lightmagenta", "yellow", "lightwhite"
71 };
72 
73 struct {
74 	int			active_vty;
75 	vid_info_t		console_info;
76 	unsigned char		screen_map[256];
77 	int			video_mode_number;
78 	struct video_info	video_mode_info;
79 } cur_info;
80 
81 int	hex = 0;
82 int	number;
83 int	vesa_cols;
84 int	vesa_rows;
85 int	font_height;
86 int	colors_changed;
87 int	video_mode_changed;
88 int	normal_fore_color, normal_back_color;
89 int	revers_fore_color, revers_back_color;
90 char	letter;
91 struct	vid_info info;
92 struct	video_info new_mode_info;
93 
94 
95 /*
96  * Initialize revert data.
97  *
98  * NOTE: the following parameters are not yet saved/restored:
99  *
100  *   screen saver timeout
101  *   cursor type
102  *   mouse character and mouse show/hide state
103  *   vty switching on/off state
104  *   history buffer size
105  *   history contents
106  *   font maps
107  */
108 
109 static void
110 init(void)
111 {
112 	if (ioctl(0, VT_GETACTIVE, &cur_info.active_vty) == -1)
113 		errc(1, errno, "getting active vty");
114 
115 	cur_info.console_info.size = sizeof(cur_info.console_info);
116 
117 	if (ioctl(0, CONS_GETINFO, &cur_info.console_info) == -1)
118 		errc(1, errno, "getting console information");
119 
120 	if (ioctl(0, GIO_SCRNMAP, &cur_info.screen_map) == -1)
121 		errc(1, errno, "getting screen map");
122 
123 	if (ioctl(0, CONS_GET, &cur_info.video_mode_number) == -1)
124 		errc(1, errno, "getting video mode number");
125 
126 	cur_info.video_mode_info.vi_mode = cur_info.video_mode_number;
127 
128 	if (ioctl(0, CONS_MODEINFO, &cur_info.video_mode_info) == -1)
129 		errc(1, errno, "getting video mode parameters");
130 
131 	normal_fore_color = cur_info.console_info.mv_norm.fore;
132 	normal_back_color = cur_info.console_info.mv_norm.back;
133 	revers_fore_color = cur_info.console_info.mv_rev.fore;
134 	revers_back_color = cur_info.console_info.mv_rev.back;
135 }
136 
137 
138 /*
139  * If something goes wrong along the way we call revert() to go back to the
140  * console state we came from (which is assumed to be working).
141  *
142  * NOTE: please also read the comments of init().
143  */
144 
145 static void
146 revert(void)
147 {
148 	int size[3];
149 
150 	ioctl(0, VT_ACTIVATE, cur_info.active_vty);
151 
152 	fprintf(stderr, "\033[=%dA", cur_info.console_info.mv_ovscan);
153 	fprintf(stderr, "\033[=%dF", cur_info.console_info.mv_norm.fore);
154 	fprintf(stderr, "\033[=%dG", cur_info.console_info.mv_norm.back);
155 	fprintf(stderr, "\033[=%dH", cur_info.console_info.mv_rev.fore);
156 	fprintf(stderr, "\033[=%dI", cur_info.console_info.mv_rev.back);
157 
158 	ioctl(0, PIO_SCRNMAP, &cur_info.screen_map);
159 
160 	if (cur_info.video_mode_number >= M_VESA_BASE)
161 		ioctl(0, _IO('V', cur_info.video_mode_number - M_VESA_BASE),
162 		      NULL);
163 	else
164 		ioctl(0, _IO('S', cur_info.video_mode_number), NULL);
165 
166 	if (cur_info.video_mode_info.vi_flags & V_INFO_GRAPHICS) {
167 		size[0] = cur_info.video_mode_info.vi_width / 8;
168 		size[1] = cur_info.video_mode_info.vi_height /
169 			  cur_info.console_info.font_size;
170 		size[2] = cur_info.console_info.font_size;
171 
172 		ioctl(0, KDRASTER, size);
173 	}
174 }
175 
176 
177 /*
178  * Print a short usage string describing all options, then exit.
179  */
180 
181 static void
182 usage(void)
183 {
184 	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
185 "usage: vidcontrol [-CdHLPpx] [-b color] [-c appearance] [-f [size] file]",
186 "                  [-g geometry] [-h size] [-i adapter | mode] [-l screen_map]",
187 "                  [-M char] [-m on | off] [-r foreground background]",
188 "                  [-S on | off] [-s number] [-t N | off] [mode]",
189 "                  [foreground [background]] [show]");
190 	exit(1);
191 }
192 
193 
194 /*
195  * Retrieve the next argument from the command line (for options that require
196  * more than one argument).
197  */
198 
199 static char *
200 nextarg(int ac, char **av, int *indp, int oc, int strict)
201 {
202 	if (*indp < ac)
203 		return(av[(*indp)++]);
204 
205 	if (strict != 0) {
206 		revert();
207 		errx(1, "option requires two arguments -- %c", oc);
208 	}
209 
210 	return(NULL);
211 }
212 
213 
214 /*
215  * Guess which file to open. Try to open each combination of a specified set
216  * of file name components.
217  */
218 
219 static FILE *
220 openguess(const char *a[], const char *b[], const char *c[], const char *d[], char **name)
221 {
222 	FILE *f;
223 	int i, j, k, l;
224 
225 	for (i = 0; a[i] != NULL; i++) {
226 		for (j = 0; b[j] != NULL; j++) {
227 			for (k = 0; c[k] != NULL; k++) {
228 				for (l = 0; d[l] != NULL; l++) {
229 					asprintf(name, "%s%s%s%s",
230 						 a[i], b[j], c[k], d[l]);
231 
232 					f = fopen(*name, "r");
233 
234 					if (f != NULL)
235 						return (f);
236 
237 					free(*name);
238 				}
239 			}
240 		}
241 	}
242 	return (NULL);
243 }
244 
245 
246 /*
247  * Load a screenmap from a file and set it.
248  */
249 
250 static void
251 load_scrnmap(const char *filename)
252 {
253 	FILE *fd;
254 	int size;
255 	char *name;
256 	scrmap_t scrnmap;
257 	const char *a[] = {"", SCRNMAP_PATH, NULL};
258 	const char *b[] = {filename, NULL};
259 	const char *c[] = {"", ".scm", NULL};
260 	const char *d[] = {"", NULL};
261 
262 	fd = openguess(a, b, c, d, &name);
263 
264 	if (fd == NULL) {
265 		revert();
266 		errx(1, "screenmap file not found");
267 	}
268 
269 	size = sizeof(scrnmap);
270 
271 	if (decode(fd, (char *)&scrnmap, size) != size) {
272 		rewind(fd);
273 
274 		if (fread(&scrnmap, 1, size, fd) != (size_t)size) {
275 			warnx("bad screenmap file");
276 			fclose(fd);
277 			revert();
278 			errx(1, "bad screenmap file");
279 		}
280 	}
281 
282 	if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) {
283 		revert();
284 		errc(1, errno, "loading screenmap");
285 	}
286 
287 	fclose(fd);
288 }
289 
290 
291 /*
292  * Set the default screenmap.
293  */
294 
295 static void
296 load_default_scrnmap(void)
297 {
298 	scrmap_t scrnmap;
299 	int i;
300 
301 	for (i=0; i<256; i++)
302 		*((char*)&scrnmap + i) = i;
303 
304 	if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) {
305 		revert();
306 		errc(1, errno, "loading default screenmap");
307 	}
308 }
309 
310 
311 /*
312  * Print the current screenmap to stdout.
313  */
314 
315 static void
316 print_scrnmap(void)
317 {
318 	unsigned char map[256];
319 	size_t i;
320 
321 	if (ioctl(0, GIO_SCRNMAP, &map) == -1) {
322 		revert();
323 		errc(1, errno, "getting screenmap");
324 	}
325 	for (i=0; i<sizeof(map); i++) {
326 		if (i != 0 && i % 16 == 0)
327 			fprintf(stdout, "\n");
328 
329 		if (hex != 0)
330 			fprintf(stdout, " %02x", map[i]);
331 		else
332 			fprintf(stdout, " %03d", map[i]);
333 	}
334 	fprintf(stdout, "\n");
335 
336 }
337 
338 
339 /*
340  * Determine a file's size.
341  */
342 
343 static int
344 fsize(FILE *file)
345 {
346 	struct stat sb;
347 
348 	if (fstat(fileno(file), &sb) == 0)
349 		return sb.st_size;
350 	else
351 		return -1;
352 }
353 
354 
355 /*
356  * Load a font from file and set it.
357  */
358 
359 static void
360 load_font(const char *type, const char *filename)
361 {
362 	FILE	*fd;
363 	int	h, i, size, w;
364 	unsigned long io = 0;	/* silence stupid gcc(1) in the Wall mode */
365 	char	*name, *fontmap, size_sufx[6];
366 	const char	*a[] = {"", FONT_PATH, NULL};
367 	const char	*b[] = {filename, NULL};
368 	const char	*c[] = {"", size_sufx, NULL};
369 	const char	*d[] = {"", ".fnt", NULL};
370 	vid_info_t _info;
371 
372 	struct sizeinfo {
373 		int w;
374 		int h;
375 		unsigned long io;
376 	} sizes[] = {{8, 16, PIO_FONT8x16},
377 		     {8, 14, PIO_FONT8x14},
378 		     {8,  8,  PIO_FONT8x8},
379 		     {0,  0,            0}};
380 
381 	_info.size = sizeof(_info);
382 	if (ioctl(0, CONS_GETINFO, &_info) == -1) {
383 		revert();
384 		warn("failed to obtain current video mode parameters");
385 		return;
386 	}
387 
388 	snprintf(size_sufx, sizeof(size_sufx), "-8x%d", _info.font_size);
389 	fd = openguess(a, b, c, d, &name);
390 
391 	if (fd == NULL) {
392 		revert();
393 		errx(1, "%s: can't load font file", filename);
394 	}
395 
396 	if (type != NULL) {
397 		size = 0;
398 		if (sscanf(type, "%dx%d", &w, &h) == 2) {
399 			for (i = 0; sizes[i].w != 0; i++) {
400 				if (sizes[i].w == w && sizes[i].h == h) {
401 					size = DATASIZE(sizes[i]);
402 					io = sizes[i].io;
403 					font_height = sizes[i].h;
404 				}
405 			}
406 		}
407 		if (size == 0) {
408 			fclose(fd);
409 			revert();
410 			errx(1, "%s: bad font size specification", type);
411 		}
412 	} else {
413 		/* Apply heuristics */
414 
415 		int j;
416 		int dsize[2];
417 
418 		size = DATASIZE(sizes[0]);
419 		fontmap = (char*) malloc(size);
420 		dsize[0] = decode(fd, fontmap, size);
421 		dsize[1] = fsize(fd);
422 		free(fontmap);
423 
424 		size = 0;
425 		for (j = 0; j < 2; j++) {
426 			for (i = 0; sizes[i].w != 0; i++) {
427 				if (DATASIZE(sizes[i]) == dsize[j]) {
428 					size = dsize[j];
429 					io = sizes[i].io;
430 					font_height = sizes[i].h;
431 					j = 2;	/* XXX */
432 					break;
433 				}
434 			}
435 		}
436 
437 		if (size == 0) {
438 			fclose(fd);
439 			revert();
440 			errx(1, "%s: can't guess font size", filename);
441 		}
442 
443 		rewind(fd);
444 	}
445 
446 	fontmap = (char*) malloc(size);
447 
448 	if (decode(fd, fontmap, size) != size) {
449 		rewind(fd);
450 		if (fsize(fd) != size ||
451 		    fread(fontmap, 1, size, fd) != (size_t)size) {
452 			warnx("%s: bad font file", filename);
453 			fclose(fd);
454 			free(fontmap);
455 			revert();
456 			errx(1, "%s: bad font file", filename);
457 		}
458 	}
459 
460 	if (ioctl(0, io, fontmap) == -1) {
461 		revert();
462 		errc(1, errno, "loading font");
463 	}
464 
465 	fclose(fd);
466 	free(fontmap);
467 }
468 
469 
470 /*
471  * Set the timeout for the screensaver.
472  */
473 
474 static void
475 set_screensaver_timeout(char *arg)
476 {
477 	int nsec;
478 
479 	if (!strcmp(arg, "off")) {
480 		nsec = 0;
481 	} else {
482 		nsec = atoi(arg);
483 
484 		if ((*arg == '\0') || (nsec < 1)) {
485 			revert();
486 			errx(1, "argument must be a positive number");
487 		}
488 	}
489 
490 	if (ioctl(0, CONS_BLANKTIME, &nsec) == -1) {
491 		revert();
492 		errc(1, errno, "setting screensaver period");
493 	}
494 }
495 
496 
497 /*
498  * Set the cursor's shape/type.
499  */
500 
501 static void
502 set_cursor_type(char *appearence)
503 {
504 	int type;
505 
506 	if (!strcmp(appearence, "normal"))
507 		type = 0;
508 	else if (!strcmp(appearence, "blink"))
509 		type = 1;
510 	else if (!strcmp(appearence, "destructive"))
511 		type = 3;
512 	else {
513 		revert();
514 		errx(1, "argument to -c must be normal, blink or destructive");
515 	}
516 
517 	if (ioctl(0, CONS_CURSORTYPE, &type) == -1) {
518 		revert();
519 		errc(1, errno, "setting cursor type");
520 	}
521 }
522 
523 
524 /*
525  * Set the video mode.
526  */
527 
528 static int
529 video_mode(int argc, char **argv, int *mode_index)
530 {
531 	static struct {
532 		const char *name;
533 		unsigned long mode;
534 		unsigned long mode_num;
535 	} modes[] = {
536 		{ "80x25",        SW_TEXT_80x25,   M_TEXT_80x25 },
537 		{ "80x30",        SW_TEXT_80x30,   M_TEXT_80x30 },
538 		{ "80x43",        SW_TEXT_80x43,   M_TEXT_80x43 },
539 		{ "80x50",        SW_TEXT_80x50,   M_TEXT_80x50 },
540 		{ "80x60",        SW_TEXT_80x60,   M_TEXT_80x60 },
541 		{ "132x25",       SW_TEXT_132x25,  M_TEXT_132x25 },
542 		{ "132x30",       SW_TEXT_132x30,  M_TEXT_132x30 },
543 		{ "132x43",       SW_TEXT_132x43,  M_TEXT_132x43 },
544 		{ "132x50",       SW_TEXT_132x50,  M_TEXT_132x50 },
545 		{ "132x60",       SW_TEXT_132x60,  M_TEXT_132x60 },
546 		{ "VGA_40x25",    SW_VGA_C40x25,   M_VGA_C40x25 },
547 		{ "VGA_80x25",    SW_VGA_C80x25,   M_VGA_C80x25 },
548 		{ "VGA_80x30",    SW_VGA_C80x30,   M_VGA_C80x30 },
549 		{ "VGA_80x50",    SW_VGA_C80x50,   M_VGA_C80x50 },
550 		{ "VGA_80x60",    SW_VGA_C80x60,   M_VGA_C80x60 },
551 #ifdef SW_VGA_C90x25
552 		{ "VGA_90x25",    SW_VGA_C90x25,   M_VGA_C90x25 },
553 		{ "VGA_90x30",    SW_VGA_C90x30,   M_VGA_C90x30 },
554 		{ "VGA_90x43",    SW_VGA_C90x43,   M_VGA_C90x43 },
555 		{ "VGA_90x50",    SW_VGA_C90x50,   M_VGA_C90x50 },
556 		{ "VGA_90x60",    SW_VGA_C90x60,   M_VGA_C90x60 },
557 #endif
558 		{ "VGA_320x200",	SW_VGA_CG320,	M_CG320 },
559 		{ "EGA_80x25",		SW_ENH_C80x25,	M_ENH_C80x25 },
560 		{ "EGA_80x43",		SW_ENH_C80x43,	M_ENH_C80x43 },
561 		{ "VESA_132x25",	SW_VESA_C132x25,M_VESA_C132x25 },
562 		{ "VESA_132x43",	SW_VESA_C132x43,M_VESA_C132x43 },
563 		{ "VESA_132x50",	SW_VESA_C132x50,M_VESA_C132x50 },
564 		{ "VESA_132x60",	SW_VESA_C132x60,M_VESA_C132x60 },
565 		{ "VESA_800x600",	SW_VESA_800x600,M_VESA_800x600 },
566 		{ NULL, 0, 0 },
567 	};
568 
569 	int new_mode_num = 0;
570 	unsigned long mode = 0;
571 	int cur_mode;
572 	int ioerr;
573 	int size[3];
574 	int i;
575 
576 	if (ioctl(0, CONS_GET, &cur_mode) < 0)
577 		err(1, "cannot get the current video mode");
578 
579 	/*
580 	 * Parse the video mode argument...
581 	 */
582 
583 	if (*mode_index < argc) {
584 		if (!strncmp(argv[*mode_index], "MODE_", 5)) {
585 			if (!isdigit(argv[*mode_index][5]))
586 				errx(1, "invalid video mode number");
587 
588 			new_mode_num = atoi(&argv[*mode_index][5]);
589 		} else {
590 			for (i = 0; modes[i].name != NULL; ++i) {
591 				if (!strcmp(argv[*mode_index], modes[i].name)) {
592 					mode = modes[i].mode;
593 					new_mode_num = modes[i].mode_num;
594 					break;
595 				}
596 			}
597 
598 			if (modes[i].name == NULL)
599 				return EXIT_FAILURE;
600 			if (ioctl(0, mode, NULL) < 0) {
601 				warn("cannot set videomode");
602 				return EXIT_FAILURE;
603 			}
604 		}
605 
606 		/*
607 		 * Collect enough information about the new video mode...
608 		 */
609 
610 		new_mode_info.vi_mode = new_mode_num;
611 
612 		if (ioctl(0, CONS_MODEINFO, &new_mode_info) == -1) {
613 			revert();
614 			errc(1, errno, "obtaining new video mode parameters");
615 		}
616 
617 		if (mode == 0) {
618 			if (new_mode_num >= M_VESA_BASE)
619 				mode = _IO('V', new_mode_num - M_VESA_BASE);
620 			else
621 				mode = _IO('S', new_mode_num);
622 		}
623 
624 		/*
625 		 * Try setting the new mode.
626 		 */
627 
628 		if (ioctl(0, mode, NULL) == -1) {
629 			revert();
630 			errc(1, errno, "setting video mode");
631 		}
632 
633 		/*
634 		 * For raster modes it's not enough to just set the mode.
635 		 * We also need to explicitly set the raster mode.
636 		 */
637 
638 		if (new_mode_info.vi_flags & V_INFO_GRAPHICS) {
639 			/* font size */
640 
641 			if (font_height == 0)
642 				font_height = cur_info.console_info.font_size;
643 
644 			size[2] = font_height;
645 
646 			/* adjust columns */
647 
648 			if ((vesa_cols * 8 > new_mode_info.vi_width) ||
649 			    (vesa_cols <= 0)) {
650 				size[0] = new_mode_info.vi_width / 8;
651 			} else {
652 				size[0] = vesa_cols;
653 			}
654 
655 			/* adjust rows */
656 
657 			if ((vesa_rows * font_height > new_mode_info.vi_height) ||
658 			    (vesa_rows <= 0)) {
659 				size[1] = new_mode_info.vi_height /
660 					  font_height;
661 			} else {
662 				size[1] = vesa_rows;
663 			}
664 
665 			/* set raster mode */
666 
667 			if (ioctl(0, KDRASTER, size)) {
668 				ioerr = errno;
669 				if (cur_mode >= M_VESA_BASE)
670 					ioctl(0,
671 					    _IO('V', cur_mode - M_VESA_BASE),
672 					    NULL);
673 				else
674 					ioctl(0, _IO('S', cur_mode), NULL);
675 				revert();
676 				warnc(ioerr, "cannot activate raster display");
677 				return EXIT_FAILURE;
678 			}
679 		}
680 
681 		video_mode_changed = 1;
682 
683 		(*mode_index)++;
684 	}
685 	return EXIT_SUCCESS;
686 }
687 
688 
689 /*
690  * Return the number for a specified color name.
691  */
692 
693 static int
694 get_color_number(char *color)
695 {
696 	int i;
697 
698 	for (i=0; i<16; i++) {
699 		if (!strcmp(color, legal_colors[i]))
700 			return i;
701 	}
702 	return -1;
703 }
704 
705 
706 /*
707  * Get normal text and background colors.
708  */
709 
710 static void
711 get_normal_colors(int argc, char **argv, int *_index)
712 {
713 	int color;
714 
715 	if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) {
716 		(*_index)++;
717 		fprintf(stderr, "\033[=%dF", color);
718 		normal_fore_color=color;
719 		colors_changed = 1;
720 		if (*_index < argc
721 		    && (color = get_color_number(argv[*_index])) != -1
722 		    && color < 8) {
723 			(*_index)++;
724 			fprintf(stderr, "\033[=%dG", color);
725 			normal_back_color=color;
726 		}
727 	}
728 }
729 
730 
731 /*
732  * Get reverse text and background colors.
733  */
734 
735 static void
736 get_reverse_colors(int argc, char **argv, int *_index)
737 {
738 	int color;
739 
740 	if ((color = get_color_number(argv[*(_index)-1])) != -1) {
741 		fprintf(stderr, "\033[=%dH", color);
742 		revers_fore_color=color;
743 		colors_changed = 1;
744 		if (*_index < argc
745 		    && (color = get_color_number(argv[*_index])) != -1
746 		    && color < 8) {
747 			(*_index)++;
748 			fprintf(stderr, "\033[=%dI", color);
749 			revers_back_color=color;
750 		}
751 	}
752 }
753 
754 
755 /*
756  * Set normal and reverse foreground and background colors.
757  */
758 
759 static void
760 set_colors(void)
761 {
762 	fprintf(stderr, "\033[=%dF", normal_fore_color);
763 	fprintf(stderr, "\033[=%dG", normal_back_color);
764 	fprintf(stderr, "\033[=%dH", revers_fore_color);
765 	fprintf(stderr, "\033[=%dI", revers_back_color);
766 }
767 
768 
769 /*
770  * Switch to virtual terminal #arg.
771  */
772 
773 static void
774 set_console(char *arg)
775 {
776 	int n;
777 
778 	if(!arg || strspn(arg,"0123456789") != strlen(arg)) {
779 		revert();
780 		errx(1, "bad console number");
781 	}
782 
783 	n = atoi(arg);
784 
785 	if (n < 1 || n > 16) {
786 		revert();
787 		errx(1, "console number out of range");
788 	} else if (ioctl(0, VT_ACTIVATE, n) == -1) {
789 		revert();
790 		errc(1, errno, "switching vty");
791 	}
792 }
793 
794 
795 /*
796  * Sets the border color.
797  */
798 
799 static void
800 set_border_color(char *arg)
801 {
802 	int color;
803 
804 	if ((color = get_color_number(arg)) != -1) {
805 		fprintf(stderr, "\033[=%dA", color);
806 	}
807 	else
808 		usage();
809 }
810 
811 static void
812 set_mouse_char(char *arg)
813 {
814 	struct mouse_info mouse;
815 	long l;
816 
817 	l = strtol(arg, NULL, 0);
818 
819 	if ((l < 0) || (l > UCHAR_MAX - 3)) {
820 		revert();
821 		warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3);
822 		return;
823 	}
824 
825 	mouse.operation = MOUSE_MOUSECHAR;
826 	mouse.u.mouse_char = (int)l;
827 
828 	if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) {
829 		revert();
830 		errc(1, errno, "setting mouse character");
831 	}
832 }
833 
834 
835 /*
836  * Show/hide the mouse.
837  */
838 
839 static void
840 set_mouse(char *arg)
841 {
842 	struct mouse_info mouse;
843 
844 	if (!strcmp(arg, "on")) {
845 		mouse.operation = MOUSE_SHOW;
846 	} else if (!strcmp(arg, "off")) {
847 		mouse.operation = MOUSE_HIDE;
848 	} else {
849 		revert();
850 		errx(1, "argument to -m must be either on or off");
851 	}
852 
853 	if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) {
854 		revert();
855 		errc(1, errno, "%sing the mouse",
856 		     mouse.operation == MOUSE_SHOW ? "show" : "hid");
857 	}
858 }
859 
860 
861 static void
862 set_lockswitch(char *arg)
863 {
864 	int data;
865 
866 	if (!strcmp(arg, "off")) {
867 		data = 0x01;
868 	} else if (!strcmp(arg, "on")) {
869 		data = 0x02;
870 	} else {
871 		revert();
872 		errx(1, "argument to -S must be either on or off");
873 	}
874 
875 	if (ioctl(0, VT_LOCKSWITCH, &data) == -1) {
876 		revert();
877 		errc(1, errno, "turning %s vty switching",
878 		     data == 0x01 ? "off" : "on");
879 	}
880 }
881 
882 
883 /*
884  * Return the adapter name for a specified type.
885  */
886 
887 static const char
888 *adapter_name(int type)
889 {
890     static struct {
891 	int type;
892 	const char *name;
893     } names[] = {
894 	{ KD_MONO,	"MDA" },
895 	{ KD_HERCULES,	"Hercules" },
896 	{ KD_CGA,	"CGA" },
897 	{ KD_EGA,	"EGA" },
898 	{ KD_VGA,	"VGA" },
899 	{ KD_PC98,	"PC-98xx" },
900 	{ KD_TGA,	"TGA" },
901 	{ -1,		"Unknown" },
902     };
903 
904     int i;
905 
906     for (i = 0; names[i].type != -1; ++i)
907 	if (names[i].type == type)
908 	    break;
909     return names[i].name;
910 }
911 
912 
913 /*
914  * Show graphics adapter information.
915  */
916 
917 static void
918 show_adapter_info(void)
919 {
920 	struct video_adapter_info ad;
921 
922 	ad.va_index = 0;
923 
924 	if (ioctl(0, CONS_ADPINFO, &ad) == -1) {
925 		revert();
926 		errc(1, errno, "obtaining adapter information");
927 	}
928 
929 	printf("fb%d:\n", ad.va_index);
930 	printf("    %.*s%d, type:%s%s (%d), flags:0x%x\n",
931 	       (int)sizeof(ad.va_name), ad.va_name, ad.va_unit,
932 	       (ad.va_flags & V_ADP_VESA) ? "VESA " : "",
933 	       adapter_name(ad.va_type), ad.va_type, ad.va_flags);
934 	printf("    initial mode:%d, current mode:%d, BIOS mode:%d\n",
935 	       ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode);
936 	printf("    frame buffer window:0x%zx, buffer size:0x%zx\n",
937 	       ad.va_window, ad.va_buffer_size);
938 	printf("    window size:0x%zx, origin:0x%x\n",
939 	       ad.va_window_size, ad.va_window_orig);
940 	printf("    display start address (%d, %d), scan line width:%d\n",
941 	       ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width);
942 	printf("    reserved:0x%zx\n", ad.va_unused0);
943 }
944 
945 
946 /*
947  * Show video mode information.
948  */
949 
950 static void
951 show_mode_info(void)
952 {
953 	struct video_info _info;
954 	char buf[80];
955 	int mode;
956 	int c;
957 
958 	printf("    mode#     flags   type    size       "
959 	       "font      window      linear buffer\n");
960 	printf("---------------------------------------"
961 	       "---------------------------------------\n");
962 
963 	for (mode = 0; mode < M_VESA_MODE_MAX; ++mode) {
964 		_info.vi_mode = mode;
965 		if (ioctl(0, CONS_MODEINFO, &_info))
966 			continue;
967 		if (_info.vi_mode != mode)
968 			continue;
969 
970 		printf("%3d (0x%03x)", mode, mode);
971     		printf(" 0x%08x", _info.vi_flags);
972 		if (_info.vi_flags & V_INFO_GRAPHICS) {
973 			c = 'G';
974 
975 			snprintf(buf, sizeof(buf), "%dx%dx%d %d",
976 				 _info.vi_width, _info.vi_height,
977 				 _info.vi_depth, _info.vi_planes);
978 		} else {
979 			c = 'T';
980 
981 			snprintf(buf, sizeof(buf), "%dx%d",
982 				 _info.vi_width, _info.vi_height);
983 		}
984 
985 		printf(" %c %-15s", c, buf);
986 		snprintf(buf, sizeof(buf), "%dx%d",
987 			 _info.vi_cwidth, _info.vi_cheight);
988 		printf(" %-5s", buf);
989     		printf(" 0x%05zx %2dk %2dk",
990 		       _info.vi_window, (int)_info.vi_window_size/1024,
991 		       (int)_info.vi_window_gran/1024);
992     		printf(" 0x%08zx %dk\n",
993 		       _info.vi_buffer, (int)_info.vi_buffer_size/1024);
994 	}
995 }
996 
997 
998 static void
999 show_info(char *arg)
1000 {
1001 	if (!strcmp(arg, "adapter")) {
1002 		show_adapter_info();
1003 	} else if (!strcmp(arg, "mode")) {
1004 		show_mode_info();
1005 	} else {
1006 		revert();
1007 		errx(1, "argument to -i must be either adapter or mode");
1008 	}
1009 }
1010 
1011 
1012 static void
1013 test_frame(void)
1014 {
1015 	int i, cur_mode, fore;
1016 
1017 	fore = 15;
1018 
1019 	if (ioctl(0, CONS_GET, &cur_mode) < 0)
1020 		err(1, "must be on a virtual console");
1021 	switch (cur_mode) {
1022 	case M_PC98_80x25:
1023 	case M_PC98_80x30:
1024 		fore = 7;
1025 		break;
1026 	}
1027 
1028 	fprintf(stdout, "\033[=0G\n\n");
1029 	for (i=0; i<8; i++) {
1030 		fprintf(stdout, "\033[=%dF\033[=0G        %2d \033[=%dF%-16s"
1031 				"\033[=%dF\033[=0G        %2d \033[=%dF%-16s        "
1032 				"\033[=%dF %2d \033[=%dGBACKGROUND\033[=0G\n",
1033 			fore, i, i, legal_colors[i],
1034 			fore, i+8, i+8, legal_colors[i+8],
1035 			fore, i, i);
1036 	}
1037 	fprintf(stdout, "\033[=%dF\033[=%dG\033[=%dH\033[=%dI\n",
1038 		info.mv_norm.fore, info.mv_norm.back,
1039 		info.mv_rev.fore, info.mv_rev.back);
1040 }
1041 
1042 
1043 /*
1044  * Snapshot the video memory of that terminal, using the CONS_SCRSHOT
1045  * ioctl, and writes the results to stdout either in the special
1046  * binary format (see manual page for details), or in the plain
1047  * text format.
1048  */
1049 
1050 static void
1051 dump_screen(int mode, int opt)
1052 {
1053 	scrshot_t shot;
1054 	vid_info_t _info;
1055 
1056 	_info.size = sizeof(_info);
1057 
1058 	if (ioctl(0, CONS_GETINFO, &_info) == -1) {
1059 		revert();
1060 		errc(1, errno, "obtaining current video mode parameters");
1061 		return;
1062 	}
1063 
1064 	shot.x = shot.y = 0;
1065 	shot.xsize = _info.mv_csz;
1066 	shot.ysize = _info.mv_rsz;
1067 	if (opt == DUMP_ALL)
1068 		shot.ysize += _info.mv_hsz;
1069 
1070 	shot.buf = alloca(shot.xsize * shot.ysize * sizeof(u_int16_t));
1071 	if (shot.buf == NULL) {
1072 		revert();
1073 		errx(1, "failed to allocate memory for dump");
1074 	}
1075 
1076 	if (ioctl(0, CONS_SCRSHOT, &shot) == -1) {
1077 		revert();
1078 		errc(1, errno, "dumping screen");
1079 	}
1080 
1081 	if (mode == DUMP_FMT_RAW) {
1082 		printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2,
1083 		       shot.xsize, shot.ysize);
1084 
1085 		fflush(stdout);
1086 
1087 		write(STDOUT_FILENO, shot.buf,
1088 		      shot.xsize * shot.ysize * sizeof(u_int16_t));
1089 	} else {
1090 		char *line;
1091 		int x, y;
1092 		u_int16_t ch;
1093 
1094 		line = alloca(shot.xsize + 1);
1095 
1096 		if (line == NULL) {
1097 			revert();
1098 			errx(1, "failed to allocate memory for line buffer");
1099 		}
1100 
1101 		for (y = 0; y < shot.ysize; y++) {
1102 			for (x = 0; x < shot.xsize; x++) {
1103 				ch = shot.buf[x + (y * shot.xsize)];
1104 				ch &= 0xff;
1105 
1106 				if (isprint(ch) == 0)
1107 					ch = ' ';
1108 
1109 				line[x] = (char)ch;
1110 			}
1111 
1112 			/* Trim trailing spaces */
1113 
1114 			do {
1115 				line[x--] = '\0';
1116 			} while (line[x] == ' ' && x != 0);
1117 
1118 			puts(line);
1119 		}
1120 
1121 		fflush(stdout);
1122 	}
1123 }
1124 
1125 
1126 /*
1127  * Set the console history buffer size.
1128  */
1129 
1130 static void
1131 set_history(char *opt)
1132 {
1133 	int size;
1134 
1135 	size = atoi(opt);
1136 
1137 	if ((*opt == '\0') || size < 0) {
1138 		revert();
1139 		errx(1, "argument must be a positive number");
1140 	}
1141 
1142 	if (ioctl(0, CONS_HISTORY, &size) == -1) {
1143 		revert();
1144 		errc(1, errno, "setting history buffer size");
1145 	}
1146 }
1147 
1148 
1149 /*
1150  * Clear the console history buffer.
1151  */
1152 
1153 static void
1154 clear_history(void)
1155 {
1156 	if (ioctl(0, CONS_CLRHIST) == -1) {
1157 		revert();
1158 		errc(1, errno, "clearing history buffer");
1159 	}
1160 }
1161 
1162 
1163 int
1164 main(int argc, char **argv)
1165 {
1166 	char	*font, *type;
1167 	int	dumpmod, dumpopt, opt;
1168 	int	reterr;
1169 
1170 	init();
1171 
1172 	info.size = sizeof(info);
1173 
1174 	if (ioctl(0, CONS_GETINFO, &info) == -1)
1175 		err(1, "must be on a virtual console");
1176 	dumpmod = 0;
1177 	dumpopt = DUMP_FBF;
1178 	while((opt = getopt(argc, argv, "b:Cc:df:g:h:Hi:l:LM:m:pPr:S:s:t:x")) != -1)
1179 		switch(opt) {
1180 		case 'b':
1181 			set_border_color(optarg);
1182 			break;
1183 		case 'C':
1184 			clear_history();
1185 			break;
1186 		case 'c':
1187 			set_cursor_type(optarg);
1188 			break;
1189 		case 'd':
1190 			print_scrnmap();
1191 			break;
1192 		case 'f':
1193 			type = optarg;
1194 			font = nextarg(argc, argv, &optind, 'f', 0);
1195 
1196 			if (font == NULL) {
1197 				type = NULL;
1198 				font = optarg;
1199 			}
1200 
1201 			load_font(type, font);
1202 			break;
1203 		case 'g':
1204 			if (sscanf(optarg, "%dx%d",
1205 			    &vesa_cols, &vesa_rows) != 2) {
1206 				revert();
1207 				warnx("incorrect geometry: %s", optarg);
1208 				usage();
1209 			}
1210                 	break;
1211 		case 'h':
1212 			set_history(optarg);
1213 			break;
1214 		case 'H':
1215 			dumpopt = DUMP_ALL;
1216 			break;
1217 		case 'i':
1218 			show_info(optarg);
1219 			break;
1220 		case 'l':
1221 			load_scrnmap(optarg);
1222 			break;
1223 		case 'L':
1224 			load_default_scrnmap();
1225 			break;
1226 		case 'M':
1227 			set_mouse_char(optarg);
1228 			break;
1229 		case 'm':
1230 			set_mouse(optarg);
1231 			break;
1232 		case 'p':
1233 			dumpmod = DUMP_FMT_RAW;
1234 			break;
1235 		case 'P':
1236 			dumpmod = DUMP_FMT_TXT;
1237 			break;
1238 		case 'r':
1239 			get_reverse_colors(argc, argv, &optind);
1240 			break;
1241 		case 'S':
1242 			set_lockswitch(optarg);
1243 			break;
1244 		case 's':
1245 			set_console(optarg);
1246 			break;
1247 		case 't':
1248 			set_screensaver_timeout(optarg);
1249 			break;
1250 		case 'x':
1251 			hex = 1;
1252 			break;
1253 		default:
1254 			usage();
1255 		}
1256 
1257 	if (dumpmod != 0)
1258 		dump_screen(dumpmod, dumpopt);
1259 	reterr = video_mode(argc, argv, &optind);
1260 	get_normal_colors(argc, argv, &optind);
1261 
1262 	if (optind < argc && !strcmp(argv[optind], "show")) {
1263 		test_frame();
1264 		optind++;
1265 	}
1266 
1267 	video_mode(argc, argv, &optind);
1268 
1269 	get_normal_colors(argc, argv, &optind);
1270 
1271 	if (colors_changed || video_mode_changed) {
1272 		if (!(new_mode_info.vi_flags & V_INFO_GRAPHICS)) {
1273 			if ((normal_back_color < 8) && (revers_back_color < 8)) {
1274 				set_colors();
1275 			} else {
1276 				revert();
1277 				errx(1, "bg color for text modes must be < 8");
1278 			}
1279 		} else {
1280 			set_colors();
1281 		}
1282 	}
1283 
1284 	if ((optind != argc) || (argc == 1))
1285 		usage();
1286 	return reterr;
1287 }
1288 
1289