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