xref: /freebsd/usr.sbin/vidcontrol/vidcontrol.c (revision 5477372324b92240a96310ef2d45fa44ce8d0a93)
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/endian.h>
49 #include <sys/errno.h>
50 #include <sys/param.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/sysctl.h>
54 #include "path.h"
55 #include "decode.h"
56 
57 
58 #define	DATASIZE(x)	((x).w * (x).h * 256 / 8)
59 
60 /* Screen dump modes */
61 #define DUMP_FMT_RAW	1
62 #define DUMP_FMT_TXT	2
63 /* Screen dump options */
64 #define DUMP_FBF	0
65 #define DUMP_ALL	1
66 /* Screen dump file format revision */
67 #define DUMP_FMT_REV	1
68 
69 static const char *legal_colors[16] = {
70 	"black", "blue", "green", "cyan",
71 	"red", "magenta", "brown", "white",
72 	"grey", "lightblue", "lightgreen", "lightcyan",
73 	"lightred", "lightmagenta", "yellow", "lightwhite"
74 };
75 
76 static struct {
77 	int			active_vty;
78 	vid_info_t		console_info;
79 	unsigned char		screen_map[256];
80 	int			video_mode_number;
81 	struct video_info	video_mode_info;
82 } cur_info;
83 
84 struct vt4font_header {
85 	uint8_t		magic[8];
86 	uint8_t		width;
87 	uint8_t		height;
88 	uint16_t	pad;
89 	uint32_t	glyph_count;
90 	uint32_t	map_count[4];
91 } __packed;
92 
93 static int	hex = 0;
94 static int	vesa_cols;
95 static int	vesa_rows;
96 static int	font_height;
97 static int	vt4_mode = 0;
98 static int	video_mode_changed;
99 static struct	video_info new_mode_info;
100 
101 
102 /*
103  * Initialize revert data.
104  *
105  * NOTE: the following parameters are not yet saved/restored:
106  *
107  *   screen saver timeout
108  *   cursor type
109  *   mouse character and mouse show/hide state
110  *   vty switching on/off state
111  *   history buffer size
112  *   history contents
113  *   font maps
114  */
115 
116 static void
117 init(void)
118 {
119 	if (ioctl(0, VT_GETACTIVE, &cur_info.active_vty) == -1)
120 		err(1, "getting active vty");
121 
122 	cur_info.console_info.size = sizeof(cur_info.console_info);
123 	if (ioctl(0, CONS_GETINFO, &cur_info.console_info) == -1)
124 		err(1, "getting console information");
125 
126 	/* vt(4) use unicode, so no screen mapping required. */
127 	if (vt4_mode == 0 &&
128 	    ioctl(0, GIO_SCRNMAP, &cur_info.screen_map) == -1)
129 		err(1, "getting screen map");
130 
131 	if (ioctl(0, CONS_GET, &cur_info.video_mode_number) == -1)
132 		err(1, "getting video mode number");
133 
134 	cur_info.video_mode_info.vi_mode = cur_info.video_mode_number;
135 
136 	if (ioctl(0, CONS_MODEINFO, &cur_info.video_mode_info) == -1)
137 		err(1, "getting video mode parameters");
138 }
139 
140 
141 /*
142  * If something goes wrong along the way we call revert() to go back to the
143  * console state we came from (which is assumed to be working).
144  *
145  * NOTE: please also read the comments of init().
146  */
147 
148 static void
149 revert(void)
150 {
151 	int save_errno, size[3];
152 
153 	save_errno = errno;
154 
155 	ioctl(0, VT_ACTIVATE, cur_info.active_vty);
156 
157 	fprintf(stderr, "\033[=%dA", cur_info.console_info.mv_ovscan);
158 	fprintf(stderr, "\033[=%dH", cur_info.console_info.mv_rev.fore);
159 	fprintf(stderr, "\033[=%dI", cur_info.console_info.mv_rev.back);
160 
161 	if (vt4_mode == 0)
162 		ioctl(0, PIO_SCRNMAP, &cur_info.screen_map);
163 
164 	if (video_mode_changed) {
165 		if (cur_info.video_mode_number >= M_VESA_BASE)
166 			ioctl(0,
167 			    _IO('V', cur_info.video_mode_number - M_VESA_BASE),
168 			    NULL);
169 		else
170 			ioctl(0, _IO('S', cur_info.video_mode_number), NULL);
171 		if (cur_info.video_mode_info.vi_flags & V_INFO_GRAPHICS) {
172 			size[0] = cur_info.video_mode_info.vi_width / 8;
173 			size[1] = cur_info.video_mode_info.vi_height /
174 			    cur_info.console_info.font_size;
175 			size[2] = cur_info.console_info.font_size;
176 			ioctl(0, KDRASTER, size);
177 		}
178 	}
179 
180 	/* Restore some colors last since mode setting forgets some. */
181 	fprintf(stderr, "\033[=%dF", cur_info.console_info.mv_norm.fore);
182 	fprintf(stderr, "\033[=%dG", cur_info.console_info.mv_norm.back);
183 
184 	errno = save_errno;
185 }
186 
187 
188 /*
189  * Print a short usage string describing all options, then exit.
190  */
191 
192 static void
193 usage(void)
194 {
195 	if (vt4_mode)
196 		fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
197 "usage: vidcontrol [-CHPpx] [-b color] [-c appearance] [-f [[size] file]]",
198 "                  [-g geometry] [-h size] [-i active | adapter | mode]",
199 "                  [-M char] [-m on | off] [-r foreground background]",
200 "                  [-S on | off] [-s number] [-T xterm | cons25] [-t N | off]",
201 "                  [mode] [foreground [background]] [show]");
202 	else
203 		fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
204 "usage: vidcontrol [-CdHLPpx] [-b color] [-c appearance] [-f [size] file]",
205 "                  [-g geometry] [-h size] [-i adapter | mode] [-l screen_map]",
206 "                  [-M char] [-m on | off] [-r foreground background]",
207 "                  [-S on | off] [-s number] [-T xterm | cons25] [-t N | off]",
208 "                  [mode] [foreground [background]] [show]");
209 	exit(1);
210 }
211 
212 /* Detect presence of vt(4). */
213 static int
214 is_vt4(void)
215 {
216 	char vty_name[4] = "";
217 	size_t len = sizeof(vty_name);
218 
219 	if (sysctlbyname("kern.vty", vty_name, &len, NULL, 0) != 0)
220 		return (0);
221 	return (strcmp(vty_name, "vt") == 0);
222 }
223 
224 /*
225  * Retrieve the next argument from the command line (for options that require
226  * more than one argument).
227  */
228 
229 static char *
230 nextarg(int ac, char **av, int *indp, int oc, int strict)
231 {
232 	if (*indp < ac)
233 		return(av[(*indp)++]);
234 
235 	if (strict != 0) {
236 		revert();
237 		errx(1, "option requires two arguments -- %c", oc);
238 	}
239 
240 	return(NULL);
241 }
242 
243 
244 /*
245  * Guess which file to open. Try to open each combination of a specified set
246  * of file name components.
247  */
248 
249 static FILE *
250 openguess(const char *a[], const char *b[], const char *c[], const char *d[], char **name)
251 {
252 	FILE *f;
253 	int i, j, k, l;
254 
255 	for (i = 0; a[i] != NULL; i++) {
256 		for (j = 0; b[j] != NULL; j++) {
257 			for (k = 0; c[k] != NULL; k++) {
258 				for (l = 0; d[l] != NULL; l++) {
259 					asprintf(name, "%s%s%s%s",
260 						 a[i], b[j], c[k], d[l]);
261 
262 					f = fopen(*name, "r");
263 
264 					if (f != NULL)
265 						return (f);
266 
267 					free(*name);
268 				}
269 			}
270 		}
271 	}
272 	return (NULL);
273 }
274 
275 
276 /*
277  * Load a screenmap from a file and set it.
278  */
279 
280 static void
281 load_scrnmap(const char *filename)
282 {
283 	FILE *fd;
284 	int size;
285 	char *name;
286 	scrmap_t scrnmap;
287 	const char *a[] = {"", SCRNMAP_PATH, NULL};
288 	const char *b[] = {filename, NULL};
289 	const char *c[] = {"", ".scm", NULL};
290 	const char *d[] = {"", NULL};
291 
292 	fd = openguess(a, b, c, d, &name);
293 
294 	if (fd == NULL) {
295 		revert();
296 		errx(1, "screenmap file not found");
297 	}
298 
299 	size = sizeof(scrnmap);
300 
301 	if (decode(fd, (char *)&scrnmap, size) != size) {
302 		rewind(fd);
303 
304 		if (fread(&scrnmap, 1, size, fd) != (size_t)size) {
305 			fclose(fd);
306 			revert();
307 			errx(1, "bad screenmap file");
308 		}
309 	}
310 
311 	if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) {
312 		revert();
313 		err(1, "loading screenmap");
314 	}
315 
316 	fclose(fd);
317 }
318 
319 
320 /*
321  * Set the default screenmap.
322  */
323 
324 static void
325 load_default_scrnmap(void)
326 {
327 	scrmap_t scrnmap;
328 	int i;
329 
330 	for (i=0; i<256; i++)
331 		*((char*)&scrnmap + i) = i;
332 
333 	if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) {
334 		revert();
335 		err(1, "loading default screenmap");
336 	}
337 }
338 
339 
340 /*
341  * Print the current screenmap to stdout.
342  */
343 
344 static void
345 print_scrnmap(void)
346 {
347 	unsigned char map[256];
348 	size_t i;
349 
350 	if (ioctl(0, GIO_SCRNMAP, &map) == -1) {
351 		revert();
352 		err(1, "getting screenmap");
353 	}
354 	for (i=0; i<sizeof(map); i++) {
355 		if (i != 0 && i % 16 == 0)
356 			fprintf(stdout, "\n");
357 
358 		if (hex != 0)
359 			fprintf(stdout, " %02x", map[i]);
360 		else
361 			fprintf(stdout, " %03d", map[i]);
362 	}
363 	fprintf(stdout, "\n");
364 
365 }
366 
367 
368 /*
369  * Determine a file's size.
370  */
371 
372 static int
373 fsize(FILE *file)
374 {
375 	struct stat sb;
376 
377 	if (fstat(fileno(file), &sb) == 0)
378 		return sb.st_size;
379 	else
380 		return -1;
381 }
382 
383 static vfnt_map_t *
384 load_vt4mappingtable(unsigned int nmappings, FILE *f)
385 {
386 	vfnt_map_t *t;
387 	unsigned int i;
388 
389 	if (nmappings == 0)
390 		return (NULL);
391 
392 	if ((t = calloc(nmappings, sizeof(*t))) == NULL) {
393 		warn("calloc");
394 		return (NULL);
395 	}
396 
397 	if (fread(t, sizeof *t * nmappings, 1, f) != 1) {
398 		warn("read mappings");
399 		free(t);
400 		return (NULL);
401 	}
402 
403 	for (i = 0; i < nmappings; i++) {
404 		t[i].src = be32toh(t[i].src);
405 		t[i].dst = be16toh(t[i].dst);
406 		t[i].len = be16toh(t[i].len);
407 	}
408 
409 	return (t);
410 }
411 
412 /*
413  * Set the default vt font.
414  */
415 
416 static void
417 load_default_vt4font(void)
418 {
419 	if (ioctl(0, PIO_VFONT_DEFAULT) == -1) {
420 		revert();
421 		err(1, "loading default vt font");
422 	}
423 }
424 
425 static void
426 load_vt4font(FILE *f)
427 {
428 	struct vt4font_header fh;
429 	static vfnt_t vfnt;
430 	size_t glyphsize;
431 	unsigned int i;
432 
433 	if (fread(&fh, sizeof fh, 1, f) != 1) {
434 		warn("read file_header");
435 		return;
436 	}
437 
438 	if (memcmp(fh.magic, "VFNT0002", 8) != 0) {
439 		warnx("bad magic in font file\n");
440 		return;
441 	}
442 
443 	for (i = 0; i < VFNT_MAPS; i++)
444 		vfnt.map_count[i] = be32toh(fh.map_count[i]);
445 	vfnt.glyph_count = be32toh(fh.glyph_count);
446 	vfnt.width = fh.width;
447 	vfnt.height = fh.height;
448 
449 	glyphsize = howmany(vfnt.width, 8) * vfnt.height * vfnt.glyph_count;
450 	if ((vfnt.glyphs = malloc(glyphsize)) == NULL) {
451 		warn("malloc");
452 		return;
453 	}
454 
455 	if (fread(vfnt.glyphs, glyphsize, 1, f) != 1) {
456 		warn("read glyphs");
457 		free(vfnt.glyphs);
458 		return;
459 	}
460 
461 	for (i = 0; i < VFNT_MAPS; i++)
462 		vfnt.map[i] = load_vt4mappingtable(vfnt.map_count[i], f);
463 
464 	if (ioctl(STDIN_FILENO, PIO_VFONT, &vfnt) == -1)
465 		warn("PIO_VFONT");
466 
467 	for (i = 0; i < VFNT_MAPS; i++)
468 		free(vfnt.map[i]);
469 	free(vfnt.glyphs);
470 }
471 
472 /*
473  * Load a font from file and set it.
474  */
475 
476 static void
477 load_font(const char *type, const char *filename)
478 {
479 	FILE	*fd;
480 	int	h, i, size, w;
481 	unsigned long io = 0;	/* silence stupid gcc(1) in the Wall mode */
482 	char	*name, *fontmap, size_sufx[6];
483 	const char	*a[] = {"", FONT_PATH, NULL};
484 	const char	*vt4a[] = {"", VT_FONT_PATH, NULL};
485 	const char	*b[] = {filename, NULL};
486 	const char	*c[] = {"", size_sufx, NULL};
487 	const char	*d[] = {"", ".fnt", NULL};
488 	vid_info_t info;
489 
490 	struct sizeinfo {
491 		int w;
492 		int h;
493 		unsigned long io;
494 	} sizes[] = {{8, 16, PIO_FONT8x16},
495 		     {8, 14, PIO_FONT8x14},
496 		     {8,  8,  PIO_FONT8x8},
497 		     {0,  0,            0}};
498 
499 	if (vt4_mode) {
500 		size_sufx[0] = '\0';
501 	} else {
502 		info.size = sizeof(info);
503 		if (ioctl(0, CONS_GETINFO, &info) == -1) {
504 			revert();
505 			err(1, "getting console information");
506 		}
507 
508 		snprintf(size_sufx, sizeof(size_sufx), "-8x%d", info.font_size);
509 	}
510 	fd = openguess((vt4_mode == 0) ? a : vt4a, b, c, d, &name);
511 
512 	if (fd == NULL) {
513 		revert();
514 		errx(1, "%s: can't load font file", filename);
515 	}
516 
517 	if (vt4_mode) {
518 		load_vt4font(fd);
519 		fclose(fd);
520 		return;
521 	}
522 
523 	if (type != NULL) {
524 		size = 0;
525 		if (sscanf(type, "%dx%d", &w, &h) == 2) {
526 			for (i = 0; sizes[i].w != 0; i++) {
527 				if (sizes[i].w == w && sizes[i].h == h) {
528 					size = DATASIZE(sizes[i]);
529 					io = sizes[i].io;
530 					font_height = sizes[i].h;
531 				}
532 			}
533 		}
534 		if (size == 0) {
535 			fclose(fd);
536 			revert();
537 			errx(1, "%s: bad font size specification", type);
538 		}
539 	} else {
540 		/* Apply heuristics */
541 
542 		int j;
543 		int dsize[2];
544 
545 		size = DATASIZE(sizes[0]);
546 		fontmap = (char*) malloc(size);
547 		dsize[0] = decode(fd, fontmap, size);
548 		dsize[1] = fsize(fd);
549 		free(fontmap);
550 
551 		size = 0;
552 		for (j = 0; j < 2; j++) {
553 			for (i = 0; sizes[i].w != 0; i++) {
554 				if (DATASIZE(sizes[i]) == dsize[j]) {
555 					size = dsize[j];
556 					io = sizes[i].io;
557 					font_height = sizes[i].h;
558 					j = 2;	/* XXX */
559 					break;
560 				}
561 			}
562 		}
563 
564 		if (size == 0) {
565 			fclose(fd);
566 			revert();
567 			errx(1, "%s: can't guess font size", filename);
568 		}
569 
570 		rewind(fd);
571 	}
572 
573 	fontmap = (char*) malloc(size);
574 
575 	if (decode(fd, fontmap, size) != size) {
576 		rewind(fd);
577 		if (fsize(fd) != size ||
578 		    fread(fontmap, 1, size, fd) != (size_t)size) {
579 			fclose(fd);
580 			free(fontmap);
581 			revert();
582 			errx(1, "%s: bad font file", filename);
583 		}
584 	}
585 
586 	if (ioctl(0, io, fontmap) == -1) {
587 		revert();
588 		err(1, "loading font");
589 	}
590 
591 	fclose(fd);
592 	free(fontmap);
593 }
594 
595 
596 /*
597  * Set the timeout for the screensaver.
598  */
599 
600 static void
601 set_screensaver_timeout(char *arg)
602 {
603 	int nsec;
604 
605 	if (!strcmp(arg, "off")) {
606 		nsec = 0;
607 	} else {
608 		nsec = atoi(arg);
609 
610 		if ((*arg == '\0') || (nsec < 1)) {
611 			revert();
612 			errx(1, "argument must be a positive number");
613 		}
614 	}
615 
616 	if (ioctl(0, CONS_BLANKTIME, &nsec) == -1) {
617 		revert();
618 		err(1, "setting screensaver period");
619 	}
620 }
621 
622 
623 /*
624  * Set the cursor's shape/type.
625  */
626 
627 static void
628 set_cursor_type(char *appearance)
629 {
630 	int type;
631 
632 	if (!strcmp(appearance, "normal"))
633 		type = 0;
634 	else if (!strcmp(appearance, "blink"))
635 		type = 1;
636 	else if (!strcmp(appearance, "destructive"))
637 		type = 3;
638 	else {
639 		revert();
640 		errx(1, "argument to -c must be normal, blink or destructive");
641 	}
642 
643 	if (ioctl(0, CONS_CURSORTYPE, &type) == -1) {
644 		revert();
645 		err(1, "setting cursor type");
646 	}
647 }
648 
649 
650 /*
651  * Set the video mode.
652  */
653 
654 static void
655 video_mode(int argc, char **argv, int *mode_index)
656 {
657 	static struct {
658 		const char *name;
659 		unsigned long mode;
660 		unsigned long mode_num;
661 	} modes[] = {
662 		{ "80x25",        SW_TEXT_80x25,   M_TEXT_80x25 },
663 		{ "80x30",        SW_TEXT_80x30,   M_TEXT_80x30 },
664 		{ "80x43",        SW_TEXT_80x43,   M_TEXT_80x43 },
665 		{ "80x50",        SW_TEXT_80x50,   M_TEXT_80x50 },
666 		{ "80x60",        SW_TEXT_80x60,   M_TEXT_80x60 },
667 		{ "132x25",       SW_TEXT_132x25,  M_TEXT_132x25 },
668 		{ "132x30",       SW_TEXT_132x30,  M_TEXT_132x30 },
669 		{ "132x43",       SW_TEXT_132x43,  M_TEXT_132x43 },
670 		{ "132x50",       SW_TEXT_132x50,  M_TEXT_132x50 },
671 		{ "132x60",       SW_TEXT_132x60,  M_TEXT_132x60 },
672 		{ "VGA_40x25",    SW_VGA_C40x25,   M_VGA_C40x25 },
673 		{ "VGA_80x25",    SW_VGA_C80x25,   M_VGA_C80x25 },
674 		{ "VGA_80x30",    SW_VGA_C80x30,   M_VGA_C80x30 },
675 		{ "VGA_80x50",    SW_VGA_C80x50,   M_VGA_C80x50 },
676 		{ "VGA_80x60",    SW_VGA_C80x60,   M_VGA_C80x60 },
677 #ifdef SW_VGA_C90x25
678 		{ "VGA_90x25",    SW_VGA_C90x25,   M_VGA_C90x25 },
679 		{ "VGA_90x30",    SW_VGA_C90x30,   M_VGA_C90x30 },
680 		{ "VGA_90x43",    SW_VGA_C90x43,   M_VGA_C90x43 },
681 		{ "VGA_90x50",    SW_VGA_C90x50,   M_VGA_C90x50 },
682 		{ "VGA_90x60",    SW_VGA_C90x60,   M_VGA_C90x60 },
683 #endif
684 		{ "VGA_320x200",	SW_VGA_CG320,	M_CG320 },
685 		{ "EGA_80x25",		SW_ENH_C80x25,	M_ENH_C80x25 },
686 		{ "EGA_80x43",		SW_ENH_C80x43,	M_ENH_C80x43 },
687 		{ "VESA_132x25",	SW_VESA_C132x25,M_VESA_C132x25 },
688 		{ "VESA_132x43",	SW_VESA_C132x43,M_VESA_C132x43 },
689 		{ "VESA_132x50",	SW_VESA_C132x50,M_VESA_C132x50 },
690 		{ "VESA_132x60",	SW_VESA_C132x60,M_VESA_C132x60 },
691 		{ "VESA_800x600",	SW_VESA_800x600,M_VESA_800x600 },
692 		{ NULL, 0, 0 },
693 	};
694 
695 	int new_mode_num = 0;
696 	unsigned long mode = 0;
697 	int cur_mode;
698 	int save_errno;
699 	int size[3];
700 	int i;
701 
702 	if (ioctl(0, CONS_GET, &cur_mode) < 0)
703 		err(1, "cannot get the current video mode");
704 
705 	/*
706 	 * Parse the video mode argument...
707 	 */
708 
709 	if (*mode_index < argc) {
710 		if (!strncmp(argv[*mode_index], "MODE_", 5)) {
711 			if (!isdigit(argv[*mode_index][5]))
712 				errx(1, "invalid video mode number");
713 
714 			new_mode_num = atoi(&argv[*mode_index][5]);
715 		} else {
716 			for (i = 0; modes[i].name != NULL; ++i) {
717 				if (!strcmp(argv[*mode_index], modes[i].name)) {
718 					mode = modes[i].mode;
719 					new_mode_num = modes[i].mode_num;
720 					break;
721 				}
722 			}
723 
724 			if (modes[i].name == NULL)
725 				return;
726 			if (ioctl(0, mode, NULL) < 0) {
727 				revert();
728 				err(1, "cannot set videomode");
729 			}
730 			video_mode_changed = 1;
731 		}
732 
733 		/*
734 		 * Collect enough information about the new video mode...
735 		 */
736 
737 		new_mode_info.vi_mode = new_mode_num;
738 
739 		if (ioctl(0, CONS_MODEINFO, &new_mode_info) == -1) {
740 			revert();
741 			err(1, "obtaining new video mode parameters");
742 		}
743 
744 		if (mode == 0) {
745 			if (new_mode_num >= M_VESA_BASE)
746 				mode = _IO('V', new_mode_num - M_VESA_BASE);
747 			else
748 				mode = _IO('S', new_mode_num);
749 		}
750 
751 		/*
752 		 * Try setting the new mode.
753 		 */
754 
755 		if (ioctl(0, mode, NULL) == -1) {
756 			revert();
757 			err(1, "setting video mode");
758 		}
759 		video_mode_changed = 1;
760 
761 		/*
762 		 * For raster modes it's not enough to just set the mode.
763 		 * We also need to explicitly set the raster mode.
764 		 */
765 
766 		if (new_mode_info.vi_flags & V_INFO_GRAPHICS) {
767 			/* font size */
768 
769 			if (font_height == 0)
770 				font_height = cur_info.console_info.font_size;
771 
772 			size[2] = font_height;
773 
774 			/* adjust columns */
775 
776 			if ((vesa_cols * 8 > new_mode_info.vi_width) ||
777 			    (vesa_cols <= 0)) {
778 				size[0] = new_mode_info.vi_width / 8;
779 			} else {
780 				size[0] = vesa_cols;
781 			}
782 
783 			/* adjust rows */
784 
785 			if ((vesa_rows * font_height > new_mode_info.vi_height) ||
786 			    (vesa_rows <= 0)) {
787 				size[1] = new_mode_info.vi_height /
788 					  font_height;
789 			} else {
790 				size[1] = vesa_rows;
791 			}
792 
793 			/* set raster mode */
794 
795 			if (ioctl(0, KDRASTER, size)) {
796 				save_errno = errno;
797 				if (cur_mode >= M_VESA_BASE)
798 					ioctl(0,
799 					    _IO('V', cur_mode - M_VESA_BASE),
800 					    NULL);
801 				else
802 					ioctl(0, _IO('S', cur_mode), NULL);
803 				revert();
804 				errno = save_errno;
805 				err(1, "cannot activate raster display");
806 			}
807 		}
808 
809 		/* Recover from mode setting forgetting colors. */
810 		fprintf(stderr, "\033[=%dF",
811 		    cur_info.console_info.mv_norm.fore);
812 		fprintf(stderr, "\033[=%dG",
813 		    cur_info.console_info.mv_norm.back);
814 
815 		(*mode_index)++;
816 	}
817 }
818 
819 
820 /*
821  * Return the number for a specified color name.
822  */
823 
824 static int
825 get_color_number(char *color)
826 {
827 	int i;
828 
829 	for (i=0; i<16; i++) {
830 		if (!strcmp(color, legal_colors[i]))
831 			return i;
832 	}
833 	return -1;
834 }
835 
836 
837 /*
838  * Set normal text and background colors.
839  */
840 
841 static void
842 set_normal_colors(int argc, char **argv, int *_index)
843 {
844 	int color;
845 
846 	if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) {
847 		(*_index)++;
848 		fprintf(stderr, "\033[=%dF", color);
849 		if (*_index < argc
850 		    && (color = get_color_number(argv[*_index])) != -1) {
851 			(*_index)++;
852 			fprintf(stderr, "\033[=%dG", color);
853 		}
854 	}
855 }
856 
857 
858 /*
859  * Set reverse text and background colors.
860  */
861 
862 static void
863 set_reverse_colors(int argc, char **argv, int *_index)
864 {
865 	int color;
866 
867 	if ((color = get_color_number(argv[*(_index)-1])) != -1) {
868 		fprintf(stderr, "\033[=%dH", color);
869 		if (*_index < argc
870 		    && (color = get_color_number(argv[*_index])) != -1) {
871 			(*_index)++;
872 			fprintf(stderr, "\033[=%dI", color);
873 		}
874 	}
875 }
876 
877 
878 /*
879  * Switch to virtual terminal #arg.
880  */
881 
882 static void
883 set_console(char *arg)
884 {
885 	int n;
886 
887 	if(!arg || strspn(arg,"0123456789") != strlen(arg)) {
888 		revert();
889 		errx(1, "bad console number");
890 	}
891 
892 	n = atoi(arg);
893 
894 	if (n < 1 || n > 16) {
895 		revert();
896 		errx(1, "console number out of range");
897 	} else if (ioctl(0, VT_ACTIVATE, n) == -1) {
898 		revert();
899 		err(1, "switching vty");
900 	}
901 }
902 
903 
904 /*
905  * Sets the border color.
906  */
907 
908 static void
909 set_border_color(char *arg)
910 {
911 	int color;
912 
913 	if ((color = get_color_number(arg)) != -1) {
914 		fprintf(stderr, "\033[=%dA", color);
915 	}
916 	else
917 		usage();
918 }
919 
920 static void
921 set_mouse_char(char *arg)
922 {
923 	struct mouse_info mouse;
924 	long l;
925 
926 	l = strtol(arg, NULL, 0);
927 
928 	if ((l < 0) || (l > UCHAR_MAX - 3)) {
929 		revert();
930 		warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3);
931 		return;
932 	}
933 
934 	mouse.operation = MOUSE_MOUSECHAR;
935 	mouse.u.mouse_char = (int)l;
936 
937 	if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) {
938 		revert();
939 		err(1, "setting mouse character");
940 	}
941 }
942 
943 
944 /*
945  * Show/hide the mouse.
946  */
947 
948 static void
949 set_mouse(char *arg)
950 {
951 	struct mouse_info mouse;
952 
953 	if (!strcmp(arg, "on")) {
954 		mouse.operation = MOUSE_SHOW;
955 	} else if (!strcmp(arg, "off")) {
956 		mouse.operation = MOUSE_HIDE;
957 	} else {
958 		revert();
959 		errx(1, "argument to -m must be either on or off");
960 	}
961 
962 	if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) {
963 		revert();
964 		err(1, "%sing the mouse",
965 		     mouse.operation == MOUSE_SHOW ? "show" : "hid");
966 	}
967 }
968 
969 
970 static void
971 set_lockswitch(char *arg)
972 {
973 	int data;
974 
975 	if (!strcmp(arg, "off")) {
976 		data = 0x01;
977 	} else if (!strcmp(arg, "on")) {
978 		data = 0x02;
979 	} else {
980 		revert();
981 		errx(1, "argument to -S must be either on or off");
982 	}
983 
984 	if (ioctl(0, VT_LOCKSWITCH, &data) == -1) {
985 		revert();
986 		err(1, "turning %s vty switching",
987 		     data == 0x01 ? "off" : "on");
988 	}
989 }
990 
991 
992 /*
993  * Return the adapter name for a specified type.
994  */
995 
996 static const char
997 *adapter_name(int type)
998 {
999     static struct {
1000 	int type;
1001 	const char *name;
1002     } names[] = {
1003 	{ KD_MONO,	"MDA" },
1004 	{ KD_HERCULES,	"Hercules" },
1005 	{ KD_CGA,	"CGA" },
1006 	{ KD_EGA,	"EGA" },
1007 	{ KD_VGA,	"VGA" },
1008 	{ KD_TGA,	"TGA" },
1009 	{ -1,		"Unknown" },
1010     };
1011 
1012     int i;
1013 
1014     for (i = 0; names[i].type != -1; ++i)
1015 	if (names[i].type == type)
1016 	    break;
1017     return names[i].name;
1018 }
1019 
1020 
1021 /*
1022  * Show active VTY, ie current console number.
1023  */
1024 
1025 static void
1026 show_active_info(void)
1027 {
1028 
1029 	printf("%d\n", cur_info.active_vty);
1030 }
1031 
1032 
1033 /*
1034  * Show graphics adapter information.
1035  */
1036 
1037 static void
1038 show_adapter_info(void)
1039 {
1040 	struct video_adapter_info ad;
1041 
1042 	ad.va_index = 0;
1043 
1044 	if (ioctl(0, CONS_ADPINFO, &ad) == -1) {
1045 		revert();
1046 		err(1, "obtaining adapter information");
1047 	}
1048 
1049 	printf("fb%d:\n", ad.va_index);
1050 	printf("    %.*s%d, type:%s%s (%d), flags:0x%x\n",
1051 	       (int)sizeof(ad.va_name), ad.va_name, ad.va_unit,
1052 	       (ad.va_flags & V_ADP_VESA) ? "VESA " : "",
1053 	       adapter_name(ad.va_type), ad.va_type, ad.va_flags);
1054 	printf("    initial mode:%d, current mode:%d, BIOS mode:%d\n",
1055 	       ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode);
1056 	printf("    frame buffer window:0x%zx, buffer size:0x%zx\n",
1057 	       ad.va_window, ad.va_buffer_size);
1058 	printf("    window size:0x%zx, origin:0x%x\n",
1059 	       ad.va_window_size, ad.va_window_orig);
1060 	printf("    display start address (%d, %d), scan line width:%d\n",
1061 	       ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width);
1062 	printf("    reserved:0x%zx\n", ad.va_unused0);
1063 }
1064 
1065 
1066 /*
1067  * Show video mode information.
1068  */
1069 
1070 static void
1071 show_mode_info(void)
1072 {
1073 	char buf[80];
1074 	struct video_info info;
1075 	int c;
1076 	int mm;
1077 	int mode;
1078 
1079 	printf("    mode#     flags   type    size       "
1080 	       "font      window      linear buffer\n");
1081 	printf("---------------------------------------"
1082 	       "---------------------------------------\n");
1083 
1084 	memset(&info, 0, sizeof(info));
1085 	for (mode = 0; mode <= M_VESA_MODE_MAX; ++mode) {
1086 		info.vi_mode = mode;
1087 		if (ioctl(0, CONS_MODEINFO, &info))
1088 			continue;
1089 		if (info.vi_mode != mode)
1090 			continue;
1091 		if (info.vi_width == 0 && info.vi_height == 0 &&
1092 		    info.vi_cwidth == 0 && info.vi_cheight == 0)
1093 			continue;
1094 
1095 		printf("%3d (0x%03x)", mode, mode);
1096     		printf(" 0x%08x", info.vi_flags);
1097 		if (info.vi_flags & V_INFO_GRAPHICS) {
1098 			c = 'G';
1099 
1100 			if (info.vi_mem_model == V_INFO_MM_PLANAR)
1101 				snprintf(buf, sizeof(buf), "%dx%dx%d %d",
1102 				    info.vi_width, info.vi_height,
1103 				    info.vi_depth, info.vi_planes);
1104 			else {
1105 				switch (info.vi_mem_model) {
1106 				case V_INFO_MM_PACKED:
1107 					mm = 'P';
1108 					break;
1109 				case V_INFO_MM_DIRECT:
1110 					mm = 'D';
1111 					break;
1112 				case V_INFO_MM_CGA:
1113 					mm = 'C';
1114 					break;
1115 				case V_INFO_MM_HGC:
1116 					mm = 'H';
1117 					break;
1118 				case V_INFO_MM_VGAX:
1119 					mm = 'V';
1120 					break;
1121 				default:
1122 					mm = ' ';
1123 					break;
1124 				}
1125 				snprintf(buf, sizeof(buf), "%dx%dx%d %c",
1126 				    info.vi_width, info.vi_height,
1127 				    info.vi_depth, mm);
1128 			}
1129 		} else {
1130 			c = 'T';
1131 
1132 			snprintf(buf, sizeof(buf), "%dx%d",
1133 				 info.vi_width, info.vi_height);
1134 		}
1135 
1136 		printf(" %c %-15s", c, buf);
1137 		snprintf(buf, sizeof(buf), "%dx%d",
1138 			 info.vi_cwidth, info.vi_cheight);
1139 		printf(" %-5s", buf);
1140     		printf(" 0x%05zx %2dk %2dk",
1141 		       info.vi_window, (int)info.vi_window_size/1024,
1142 		       (int)info.vi_window_gran/1024);
1143     		printf(" 0x%08zx %dk\n",
1144 		       info.vi_buffer, (int)info.vi_buffer_size/1024);
1145 	}
1146 }
1147 
1148 
1149 static void
1150 show_info(char *arg)
1151 {
1152 
1153 	if (!strcmp(arg, "active")) {
1154 		show_active_info();
1155 	} else if (!strcmp(arg, "adapter")) {
1156 		show_adapter_info();
1157 	} else if (!strcmp(arg, "mode")) {
1158 		show_mode_info();
1159 	} else {
1160 		revert();
1161 		errx(1, "argument to -i must be active, adapter, or mode");
1162 	}
1163 }
1164 
1165 
1166 static void
1167 test_frame(void)
1168 {
1169 	vid_info_t info;
1170 	const char *bg, *sep;
1171 	int i, fore;
1172 
1173 	info.size = sizeof(info);
1174 	if (ioctl(0, CONS_GETINFO, &info) == -1)
1175 		err(1, "getting console information");
1176 
1177 	fore = 15;
1178 	if (info.mv_csz < 80) {
1179 		bg = "BG";
1180 		sep = " ";
1181 	} else {
1182 		bg = "BACKGROUND";
1183 		sep = "    ";
1184 	}
1185 
1186 	fprintf(stdout, "\033[=0G\n\n");
1187 	for (i=0; i<8; i++) {
1188 		fprintf(stdout,
1189 		    "\033[=%dF\033[=0G%2d \033[=%dF%-7s%s"
1190 		    "\033[=%dF\033[=0G%2d \033[=%dF%-12s%s"
1191 		    "\033[=%dF%2d \033[=%dG%s\033[=0G%s"
1192 		    "\033[=%dF%2d \033[=%dG%s\033[=0G\n",
1193 		    fore, i, i, legal_colors[i], sep,
1194 		    fore, i + 8, i + 8, legal_colors[i + 8], sep,
1195 		    fore, i, i, bg, sep,
1196 		    fore, i + 8, i + 8, bg);
1197 	}
1198 	fprintf(stdout, "\033[=%dF\033[=%dG\033[=%dH\033[=%dI\n",
1199 		info.mv_norm.fore, info.mv_norm.back,
1200 		info.mv_rev.fore, info.mv_rev.back);
1201 }
1202 
1203 
1204 /*
1205  * Snapshot the video memory of that terminal, using the CONS_SCRSHOT
1206  * ioctl, and writes the results to stdout either in the special
1207  * binary format (see manual page for details), or in the plain
1208  * text format.
1209  */
1210 
1211 static void
1212 dump_screen(int mode, int opt)
1213 {
1214 	scrshot_t shot;
1215 	vid_info_t info;
1216 
1217 	info.size = sizeof(info);
1218 	if (ioctl(0, CONS_GETINFO, &info) == -1) {
1219 		revert();
1220 		err(1, "getting console information");
1221 	}
1222 
1223 	shot.x = shot.y = 0;
1224 	shot.xsize = info.mv_csz;
1225 	shot.ysize = info.mv_rsz;
1226 	if (opt == DUMP_ALL)
1227 		shot.ysize += info.mv_hsz;
1228 
1229 	shot.buf = alloca(shot.xsize * shot.ysize * sizeof(u_int16_t));
1230 	if (shot.buf == NULL) {
1231 		revert();
1232 		errx(1, "failed to allocate memory for dump");
1233 	}
1234 
1235 	if (ioctl(0, CONS_SCRSHOT, &shot) == -1) {
1236 		revert();
1237 		err(1, "dumping screen");
1238 	}
1239 
1240 	if (mode == DUMP_FMT_RAW) {
1241 		printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2,
1242 		       shot.xsize, shot.ysize);
1243 
1244 		fflush(stdout);
1245 
1246 		write(STDOUT_FILENO, shot.buf,
1247 		      shot.xsize * shot.ysize * sizeof(u_int16_t));
1248 	} else {
1249 		char *line;
1250 		int x, y;
1251 		u_int16_t ch;
1252 
1253 		line = alloca(shot.xsize + 1);
1254 
1255 		if (line == NULL) {
1256 			revert();
1257 			errx(1, "failed to allocate memory for line buffer");
1258 		}
1259 
1260 		for (y = 0; y < shot.ysize; y++) {
1261 			for (x = 0; x < shot.xsize; x++) {
1262 				ch = shot.buf[x + (y * shot.xsize)];
1263 				ch &= 0xff;
1264 
1265 				if (isprint(ch) == 0)
1266 					ch = ' ';
1267 
1268 				line[x] = (char)ch;
1269 			}
1270 
1271 			/* Trim trailing spaces */
1272 
1273 			do {
1274 				line[x--] = '\0';
1275 			} while (line[x] == ' ' && x != 0);
1276 
1277 			puts(line);
1278 		}
1279 
1280 		fflush(stdout);
1281 	}
1282 }
1283 
1284 
1285 /*
1286  * Set the console history buffer size.
1287  */
1288 
1289 static void
1290 set_history(char *opt)
1291 {
1292 	int size;
1293 
1294 	size = atoi(opt);
1295 
1296 	if ((*opt == '\0') || size < 0) {
1297 		revert();
1298 		errx(1, "argument must be a positive number");
1299 	}
1300 
1301 	if (ioctl(0, CONS_HISTORY, &size) == -1) {
1302 		revert();
1303 		err(1, "setting history buffer size");
1304 	}
1305 }
1306 
1307 
1308 /*
1309  * Clear the console history buffer.
1310  */
1311 
1312 static void
1313 clear_history(void)
1314 {
1315 	if (ioctl(0, CONS_CLRHIST) == -1) {
1316 		revert();
1317 		err(1, "clearing history buffer");
1318 	}
1319 }
1320 
1321 static void
1322 set_terminal_mode(char *arg)
1323 {
1324 
1325 	if (strcmp(arg, "xterm") == 0)
1326 		fprintf(stderr, "\033[=T");
1327 	else if (strcmp(arg, "cons25") == 0)
1328 		fprintf(stderr, "\033[=1T");
1329 }
1330 
1331 
1332 int
1333 main(int argc, char **argv)
1334 {
1335 	char    *font, *type, *termmode;
1336 	const char *opts;
1337 	int	dumpmod, dumpopt, opt;
1338 
1339 	vt4_mode = is_vt4();
1340 
1341 	init();
1342 
1343 	dumpmod = 0;
1344 	dumpopt = DUMP_FBF;
1345 	termmode = NULL;
1346 	if (vt4_mode)
1347 		opts = "b:Cc:fg:h:Hi:M:m:pPr:S:s:T:t:x";
1348 	else
1349 		opts = "b:Cc:dfg:h:Hi:l:LM:m:pPr:S:s:T:t:x";
1350 
1351 	while ((opt = getopt(argc, argv, opts)) != -1)
1352 		switch(opt) {
1353 		case 'b':
1354 			set_border_color(optarg);
1355 			break;
1356 		case 'C':
1357 			clear_history();
1358 			break;
1359 		case 'c':
1360 			set_cursor_type(optarg);
1361 			break;
1362 		case 'd':
1363 			if (vt4_mode)
1364 				break;
1365 			print_scrnmap();
1366 			break;
1367 		case 'f':
1368 			optarg = nextarg(argc, argv, &optind, 'f', 0);
1369 			if (optarg != NULL) {
1370 				font = nextarg(argc, argv, &optind, 'f', 0);
1371 
1372 				if (font == NULL) {
1373 					type = NULL;
1374 					font = optarg;
1375 				} else
1376 					type = optarg;
1377 
1378 				load_font(type, font);
1379 			} else {
1380 				if (!vt4_mode)
1381 					usage(); /* Switch syscons to ROM? */
1382 
1383 				load_default_vt4font();
1384 			}
1385 			break;
1386 		case 'g':
1387 			if (sscanf(optarg, "%dx%d",
1388 			    &vesa_cols, &vesa_rows) != 2) {
1389 				revert();
1390 				warnx("incorrect geometry: %s", optarg);
1391 				usage();
1392 			}
1393                 	break;
1394 		case 'h':
1395 			set_history(optarg);
1396 			break;
1397 		case 'H':
1398 			dumpopt = DUMP_ALL;
1399 			break;
1400 		case 'i':
1401 			show_info(optarg);
1402 			break;
1403 		case 'l':
1404 			if (vt4_mode)
1405 				break;
1406 			load_scrnmap(optarg);
1407 			break;
1408 		case 'L':
1409 			if (vt4_mode)
1410 				break;
1411 			load_default_scrnmap();
1412 			break;
1413 		case 'M':
1414 			set_mouse_char(optarg);
1415 			break;
1416 		case 'm':
1417 			set_mouse(optarg);
1418 			break;
1419 		case 'p':
1420 			dumpmod = DUMP_FMT_RAW;
1421 			break;
1422 		case 'P':
1423 			dumpmod = DUMP_FMT_TXT;
1424 			break;
1425 		case 'r':
1426 			set_reverse_colors(argc, argv, &optind);
1427 			break;
1428 		case 'S':
1429 			set_lockswitch(optarg);
1430 			break;
1431 		case 's':
1432 			set_console(optarg);
1433 			break;
1434 		case 'T':
1435 			if (strcmp(optarg, "xterm") != 0 &&
1436 			    strcmp(optarg, "cons25") != 0)
1437 				usage();
1438 			termmode = optarg;
1439 			break;
1440 		case 't':
1441 			set_screensaver_timeout(optarg);
1442 			break;
1443 		case 'x':
1444 			hex = 1;
1445 			break;
1446 		default:
1447 			usage();
1448 		}
1449 
1450 	if (dumpmod != 0)
1451 		dump_screen(dumpmod, dumpopt);
1452 	video_mode(argc, argv, &optind);
1453 	set_normal_colors(argc, argv, &optind);
1454 
1455 	if (optind < argc && !strcmp(argv[optind], "show")) {
1456 		test_frame();
1457 		optind++;
1458 	}
1459 
1460 	if (termmode != NULL)
1461 		set_terminal_mode(termmode);
1462 
1463 	if ((optind != argc) || (argc == 1))
1464 		usage();
1465 	return (0);
1466 }
1467 
1468