vidcontrol.c revision 75344
1/*-
2 * Copyright (c) 1994-1996 S�ren Schmidt
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software withough specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef lint
30static const char rcsid[] =
31  "$FreeBSD: head/usr.sbin/vidcontrol/vidcontrol.c 75344 2001-04-09 17:24:29Z sobomax $";
32#endif /* not lint */
33
34#include <ctype.h>
35#include <err.h>
36#include <limits.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41#include <sys/fbio.h>
42#include <sys/consio.h>
43#include <sys/errno.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include "path.h"
47#include "decode.h"
48
49#define _VESA_800x600_DFL_COLS 80
50#define _VESA_800x600_DFL_ROWS 25
51#define _VESA_800x600_DFL_FNSZ 16
52
53char 	legal_colors[16][16] = {
54	"black", "blue", "green", "cyan",
55	"red", "magenta", "brown", "white",
56	"grey", "lightblue", "lightgreen", "lightcyan",
57	"lightred", "lightmagenta", "yellow", "lightwhite"
58};
59int 	hex = 0;
60int 	number;
61int	vesa_cols = _VESA_800x600_DFL_COLS;
62int	vesa_rows = _VESA_800x600_DFL_ROWS;
63char 	letter;
64struct 	vid_info info;
65
66
67static void
68usage()
69{
70	fprintf(stderr, "%s\n%s\n%s\n%s\n",
71"usage: vidcontrol [-r fg bg] [-b color] [-c appearance] [-d] [-l scrmap]",
72"                  [-i adapter | mode] [-L] [-M char] [-m on|off]",
73"                  [-f size file] [-s number] [-t N|off] [-x] [-g geometry]",
74"                  [mode] [fgcol [bgcol]] [show]");
75	exit(1);
76}
77
78char *
79nextarg(int ac, char **av, int *indp, int oc, int strict)
80{
81	if (*indp < ac)
82		return(av[(*indp)++]);
83	if (strict != 0)
84		errx(1, "option requires two arguments -- %c", oc);
85	return(NULL);
86}
87
88char *
89mkfullname(const char *s1, const char *s2, const char *s3)
90{
91	static char *buf = NULL;
92	static int bufl = 0;
93	int f;
94
95	f = strlen(s1) + strlen(s2) + strlen(s3) + 1;
96	if (f > bufl) {
97		if (buf)
98			buf = (char *)realloc(buf, f);
99		else
100			buf = (char *)malloc(f);
101	}
102	if (!buf) {
103		bufl = 0;
104		return(NULL);
105	}
106
107	bufl = f;
108	strcpy(buf, s1);
109	strcat(buf, s2);
110	strcat(buf, s3);
111	return(buf);
112}
113
114void
115load_scrnmap(char *filename)
116{
117	FILE *fd = 0;
118	int i, size;
119	char *name;
120	scrmap_t scrnmap;
121	char *prefix[]  = {"", "", SCRNMAP_PATH, SCRNMAP_PATH, NULL};
122	char *postfix[] = {"", ".scm", "", ".scm"};
123
124	for (i=0; prefix[i]; i++) {
125		name = mkfullname(prefix[i], filename, postfix[i]);
126		fd = fopen(name, "r");
127		if (fd)
128			break;
129	}
130	if (fd == NULL) {
131		warn("screenmap file not found");
132		return;
133	}
134	size = sizeof(scrnmap);
135	if (decode(fd, (char *)&scrnmap, size) != size) {
136		rewind(fd);
137		if (fread(&scrnmap, 1, size, fd) != size) {
138			warnx("bad screenmap file");
139			fclose(fd);
140			return;
141		}
142	}
143	if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
144		warn("can't load screenmap");
145	fclose(fd);
146}
147
148void
149load_default_scrnmap()
150{
151	scrmap_t scrnmap;
152	int i;
153
154	for (i=0; i<256; i++)
155		*((char*)&scrnmap + i) = i;
156	if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
157		warn("can't load default screenmap");
158}
159
160void
161print_scrnmap()
162{
163	unsigned char map[256];
164	int i;
165
166	if (ioctl(0, GIO_SCRNMAP, &map) < 0) {
167		warn("getting screenmap");
168		return;
169	}
170	for (i=0; i<sizeof(map); i++) {
171		if (i > 0 && i % 16 == 0)
172			fprintf(stdout, "\n");
173		if (hex)
174			fprintf(stdout, " %02x", map[i]);
175		else
176			fprintf(stdout, " %03d", map[i]);
177	}
178	fprintf(stdout, "\n");
179
180}
181
182int
183fsize(FILE *file)
184{
185	struct stat sb;
186
187	if (fstat(fileno(file), &sb) == 0)
188		return sb.st_size;
189	else
190		return -1;
191}
192
193#define DATASIZE(x) ((x).w * (x).h * 256 / 8)
194
195void
196load_font(char *type, char *filename)
197{
198	FILE	*fd = NULL;
199	int	h, i, size, w;
200	unsigned long io = 0;	/* silence stupid gcc(1) in the Wall mode */
201	char	*name, *fontmap;
202	char	*prefix[]  = {"", "", FONT_PATH, FONT_PATH, NULL};
203	char	*postfix[] = {"", ".fnt", "", ".fnt"};
204
205	struct sizeinfo {
206		int w;
207		int h;
208		unsigned long io;
209	} sizes[] = {{8, 16, PIO_FONT8x16},
210		     {8, 14, PIO_FONT8x14},
211		     {8,  8,  PIO_FONT8x8},
212		     {0,  0,            0}};
213
214	for (i=0; prefix[i]; i++) {
215		name = mkfullname(prefix[i], filename, postfix[i]);
216		fd = fopen(name, "r");
217		if (fd)
218			break;
219	}
220	if (fd == NULL) {
221		warn("%s: can't load font file", filename);
222		return;
223	}
224	if (type != NULL) {
225		size = 0;
226		if (sscanf(type, "%dx%d", &w, &h) == 2)
227			for (i = 0; sizes[i].w != 0; i++)
228				if (sizes[i].w == w && sizes[i].h == h) {
229					size = DATASIZE(sizes[i]);
230					io = sizes[i].io;
231				}
232
233		if (size == 0) {
234			warnx("%s: bad font size specification", type);
235			fclose(fd);
236			return;
237		}
238	} else {
239		/* Apply heuristics */
240		int j;
241		int dsize[2];
242
243		size = DATASIZE(sizes[0]);
244		fontmap = (char*) malloc(size);
245		dsize[0] = decode(fd, fontmap, size);
246		dsize[1] = fsize(fd);
247		free(fontmap);
248
249		size = 0;
250		for (j = 0; j < 2; j++)
251			for (i = 0; sizes[i].w != 0; i++)
252				if (DATASIZE(sizes[i]) == dsize[j]) {
253					size = dsize[j];
254					io = sizes[i].io;
255					j = 2;	/* XXX */
256					break;
257				}
258
259		if (size == 0) {
260			warnx("%s: can't guess font size", filename);
261			fclose(fd);
262			return;
263		}
264		rewind(fd);
265	}
266
267	fontmap = (char*) malloc(size);
268	if (decode(fd, fontmap, size) != size) {
269		rewind(fd);
270		if (fsize(fd) != size || fread(fontmap, 1, size, fd) != size) {
271			warnx("%s: bad font file", filename);
272			fclose(fd);
273			free(fontmap);
274			return;
275		}
276	}
277	if (ioctl(0, io, fontmap) < 0)
278		warn("can't load font");
279	fclose(fd);
280	free(fontmap);
281}
282
283void
284set_screensaver_timeout(char *arg)
285{
286	int nsec;
287
288	if (!strcmp(arg, "off"))
289		nsec = 0;
290	else {
291		nsec = atoi(arg);
292		if ((*arg == '\0') || (nsec < 1)) {
293			warnx("argument must be a positive number");
294			return;
295		}
296	}
297	if (ioctl(0, CONS_BLANKTIME, &nsec) == -1)
298		warn("setting screensaver period");
299}
300
301void
302set_cursor_type(char *appearence)
303{
304	int type;
305
306	if (!strcmp(appearence, "normal"))
307		type = 0;
308	else if (!strcmp(appearence, "blink"))
309		type = 1;
310	else if (!strcmp(appearence, "destructive"))
311		type = 3;
312	else {
313		warnx("argument to -c must be normal, blink or destructive");
314		return;
315	}
316	ioctl(0, CONS_CURSORTYPE, &type);
317}
318
319void
320video_mode(int argc, char **argv, int *index)
321{
322	static struct {
323		char *name;
324		unsigned long mode;
325	} modes[] = {
326		{ "80x25",		SW_TEXT_80x25 },
327		{ "80x30",		SW_TEXT_80x30 },
328		{ "80x43",		SW_TEXT_80x43 },
329		{ "80x50",		SW_TEXT_80x50 },
330		{ "80x60",		SW_TEXT_80x60 },
331		{ "132x25",		SW_TEXT_132x25 },
332		{ "132x30",		SW_TEXT_132x30 },
333		{ "132x43",		SW_TEXT_132x43 },
334		{ "132x50",		SW_TEXT_132x50 },
335		{ "132x60",		SW_TEXT_132x60 },
336		{ "VGA_40x25",		SW_VGA_C40x25 },
337		{ "VGA_80x25",		SW_VGA_C80x25 },
338		{ "VGA_80x30",		SW_VGA_C80x30 },
339		{ "VGA_80x50",		SW_VGA_C80x50 },
340		{ "VGA_80x60",		SW_VGA_C80x60 },
341#ifdef SW_VGA_C90x25
342		{ "VGA_90x25",		SW_VGA_C90x25 },
343		{ "VGA_90x30",		SW_VGA_C90x30 },
344		{ "VGA_90x43",		SW_VGA_C90x43 },
345		{ "VGA_90x50",		SW_VGA_C90x50 },
346		{ "VGA_90x60",		SW_VGA_C90x60 },
347#endif
348		{ "VGA_320x200",	SW_VGA_CG320 },
349		{ "EGA_80x25",		SW_ENH_C80x25 },
350		{ "EGA_80x43",		SW_ENH_C80x43 },
351		{ "VESA_132x25",	SW_VESA_C132x25 },
352		{ "VESA_132x43",	SW_VESA_C132x43 },
353		{ "VESA_132x50",	SW_VESA_C132x50 },
354		{ "VESA_132x60",	SW_VESA_C132x60 },
355		{ "VESA_800x600",	SW_VESA_800x600 },
356		{ NULL },
357	};
358	unsigned long mode = 0;
359	int cur_mode;
360	int ioerr;
361	int size[3];
362	int i;
363
364	if (ioctl(0, CONS_GET, &cur_mode) < 0)
365		err(1, "cannot get the current video mode");
366	if (*index < argc) {
367		for (i = 0; modes[i].name != NULL; ++i) {
368			if (!strcmp(argv[*index], modes[i].name)) {
369				mode = modes[i].mode;
370				break;
371			}
372		}
373		if (modes[i].name == NULL)
374			return;
375		if (ioctl(0, mode, NULL) < 0)
376			warn("cannot set videomode");
377		if (mode == SW_VESA_800x600) {
378			/* columns */
379			if ((vesa_cols * 8 > 800) || (vesa_cols <= 0)) {
380				warnx("incorrect number of columns: %d",
381				      vesa_cols);
382				size[0] = _VESA_800x600_DFL_COLS;
383			} else {
384				size[0] = vesa_cols;
385			}
386			/* rows */
387			if ((vesa_rows * _VESA_800x600_DFL_FNSZ > 600) ||
388			    (vesa_rows <=0)) {
389				warnx("incorrect number of rows: %d",
390				      vesa_rows);
391				size[1] = _VESA_800x600_DFL_ROWS;
392			} else {
393				size[1] = vesa_rows;
394			}
395			/* font size */
396			size[2] = _VESA_800x600_DFL_FNSZ;
397			if (ioctl(0, KDRASTER, size)) {
398				ioerr = errno;
399				if (cur_mode >= M_VESA_BASE)
400					ioctl(0, _IO('V', cur_mode), NULL);
401				else
402					ioctl(0, _IO('S', cur_mode), NULL);
403				warnc(ioerr, "cannot activate raster display");
404			}
405		}
406		(*index)++;
407	}
408	return;
409}
410
411int
412get_color_number(char *color)
413{
414	int i;
415
416	for (i=0; i<16; i++)
417		if (!strcmp(color, legal_colors[i]))
418			return i;
419	return -1;
420}
421
422void
423set_normal_colors(int argc, char **argv, int *index)
424{
425	int color;
426
427	if (*index < argc && (color = get_color_number(argv[*index])) != -1) {
428		(*index)++;
429		fprintf(stderr, "[=%dF", color);
430		if (*index < argc
431		    && (color = get_color_number(argv[*index])) != -1
432		    && color < 8) {
433			(*index)++;
434			fprintf(stderr, "[=%dG", color);
435		}
436	}
437}
438
439void
440set_reverse_colors(int argc, char **argv, int *index)
441{
442	int color;
443
444	if ((color = get_color_number(argv[*(index)-1])) != -1) {
445		fprintf(stderr, "[=%dH", color);
446		if (*index < argc
447		    && (color = get_color_number(argv[*index])) != -1
448		    && color < 8) {
449			(*index)++;
450			fprintf(stderr, "[=%dI", color);
451		}
452	}
453}
454
455void
456set_console(char *arg)
457{
458	int n;
459
460	if( !arg || strspn(arg,"0123456789") != strlen(arg)) {
461		warnx("bad console number");
462		return;
463	}
464
465	n = atoi(arg);
466	if (n < 1 || n > 16) {
467		warnx("console number out of range");
468	} else if (ioctl(0, VT_ACTIVATE, (caddr_t) (long) n) == -1)
469		warn("ioctl(VT_ACTIVATE)");
470}
471
472void
473set_border_color(char *arg)
474{
475	int color;
476
477	if ((color = get_color_number(arg)) != -1) {
478		fprintf(stderr, "[=%dA", color);
479	}
480	else
481		usage();
482}
483
484void
485set_mouse_char(char *arg)
486{
487	struct mouse_info mouse;
488	long l;
489
490	l = strtol(arg, NULL, 0);
491	if ((l < 0) || (l > UCHAR_MAX)) {
492		warnx("argument to -M must be 0 through %d", UCHAR_MAX);
493		return;
494	}
495	mouse.operation = MOUSE_MOUSECHAR;
496	mouse.u.mouse_char = (int)l;
497	ioctl(0, CONS_MOUSECTL, &mouse);
498}
499
500void
501set_mouse(char *arg)
502{
503	struct mouse_info mouse;
504
505	if (!strcmp(arg, "on"))
506		mouse.operation = MOUSE_SHOW;
507	else if (!strcmp(arg, "off"))
508		mouse.operation = MOUSE_HIDE;
509	else {
510		warnx("argument to -m must either on or off");
511		return;
512	}
513	ioctl(0, CONS_MOUSECTL, &mouse);
514}
515
516static char
517*adapter_name(int type)
518{
519    static struct {
520	int type;
521	char *name;
522    } names[] = {
523	{ KD_MONO,	"MDA" },
524	{ KD_HERCULES,	"Hercules" },
525	{ KD_CGA,	"CGA" },
526	{ KD_EGA,	"EGA" },
527	{ KD_VGA,	"VGA" },
528	{ KD_PC98,	"PC-98xx" },
529	{ KD_TGA,	"TGA" },
530	{ -1,		"Unknown" },
531    };
532    int i;
533
534    for (i = 0; names[i].type != -1; ++i)
535	if (names[i].type == type)
536	    break;
537    return names[i].name;
538}
539
540void
541show_adapter_info(void)
542{
543	struct video_adapter_info ad;
544
545	ad.va_index = 0;
546	if (ioctl(0, CONS_ADPINFO, &ad)) {
547		warn("failed to obtain adapter information");
548		return;
549	}
550
551	printf("fb%d:\n", ad.va_index);
552	printf("    %.*s%d, type:%s%s (%d), flags:0x%x\n",
553	       (int)sizeof(ad.va_name), ad.va_name, ad.va_unit,
554	       (ad.va_flags & V_ADP_VESA) ? "VESA " : "",
555	       adapter_name(ad.va_type), ad.va_type, ad.va_flags);
556	printf("    initial mode:%d, current mode:%d, BIOS mode:%d\n",
557	       ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode);
558	printf("    frame buffer window:0x%x, buffer size:0x%x\n",
559	       ad.va_window, ad.va_buffer_size);
560	printf("    window size:0x%x, origin:0x%x\n",
561	       ad.va_window_size, ad.va_window_orig);
562	printf("    display start address (%d, %d), scan line width:%d\n",
563	       ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width);
564	printf("    reserved:0x%x\n", ad.va_unused0);
565}
566
567void
568show_mode_info(void)
569{
570	struct video_info info;
571	char buf[80];
572	int mode;
573	int c;
574
575	printf("    mode#     flags   type    size       "
576	       "font      window      linear buffer\n");
577	printf("---------------------------------------"
578	       "---------------------------------------\n");
579	for (mode = 0; mode < M_VESA_MODE_MAX; ++mode) {
580		info.vi_mode = mode;
581		if (ioctl(0, CONS_MODEINFO, &info))
582			continue;
583		if (info.vi_mode != mode)
584			continue;
585
586		printf("%3d (0x%03x)", mode, mode);
587    		printf(" 0x%08x", info.vi_flags);
588		if (info.vi_flags & V_INFO_GRAPHICS) {
589			c = 'G';
590			snprintf(buf, sizeof(buf), "%dx%dx%d %d",
591				 info.vi_width, info.vi_height,
592				 info.vi_depth, info.vi_planes);
593		} else {
594			c = 'T';
595			snprintf(buf, sizeof(buf), "%dx%d",
596				 info.vi_width, info.vi_height);
597		}
598		printf(" %c %-15s", c, buf);
599		snprintf(buf, sizeof(buf), "%dx%d",
600			 info.vi_cwidth, info.vi_cheight);
601		printf(" %-5s", buf);
602    		printf(" 0x%05x %2dk %2dk",
603		       info.vi_window, (int)info.vi_window_size/1024,
604		       (int)info.vi_window_gran/1024);
605    		printf(" 0x%08x %dk\n",
606		       info.vi_buffer, (int)info.vi_buffer_size/1024);
607	}
608}
609
610void
611show_info(char *arg)
612{
613	if (!strcmp(arg, "adapter"))
614		show_adapter_info();
615	else if (!strcmp(arg, "mode"))
616		show_mode_info();
617	else {
618		warnx("argument to -i must either adapter or mode");
619		return;
620	}
621}
622
623void
624test_frame()
625{
626	int i;
627
628	fprintf(stdout, "[=0G\n\n");
629	for (i=0; i<8; i++) {
630		fprintf(stdout, "[=15F[=0G        %2d [=%dF%-16s"
631				"[=15F[=0G        %2d [=%dF%-16s        "
632				"[=15F %2d [=%dGBACKGROUND[=0G\n",
633			i, i, legal_colors[i], i+8, i+8,
634			legal_colors[i+8], i, i);
635	}
636	fprintf(stdout, "[=%dF[=%dG[=%dH[=%dI\n",
637		info.mv_norm.fore, info.mv_norm.back,
638		info.mv_rev.fore, info.mv_rev.back);
639}
640
641int
642main(int argc, char **argv)
643{
644	char	*font, *type;
645	int	opt;
646
647
648	info.size = sizeof(info);
649	if (ioctl(0, CONS_GETINFO, &info) < 0)
650		err(1, "must be on a virtual console");
651	while((opt = getopt(argc, argv, "b:c:df:g:i:l:LM:m:r:s:t:x")) != -1)
652		switch(opt) {
653			case 'b':
654				set_border_color(optarg);
655				break;
656			case 'c':
657				set_cursor_type(optarg);
658				break;
659			case 'd':
660				print_scrnmap();
661				break;
662			case 'f':
663				type = optarg;
664				font = nextarg(argc, argv, &optind, 'f', 0);
665				if (font == NULL) {
666					type = NULL;
667					font = optarg;
668				}
669				load_font(type, font);
670				break;
671			case 'g':
672				if (sscanf(optarg, "%dx%d", &vesa_cols,
673					   &vesa_rows) != 2) {
674					warnx("incorrect geometry: %s", optarg);
675					usage();
676				}
677				break;
678			case 'i':
679				show_info(optarg);
680				break;
681			case 'l':
682				load_scrnmap(optarg);
683				break;
684			case 'L':
685				load_default_scrnmap();
686				break;
687			case 'M':
688				set_mouse_char(optarg);
689				break;
690			case 'm':
691				set_mouse(optarg);
692				break;
693			case 'r':
694				set_reverse_colors(argc, argv, &optind);
695				break;
696			case 's':
697				set_console(optarg);
698				break;
699			case 't':
700				set_screensaver_timeout(optarg);
701				break;
702			case 'x':
703				hex = 1;
704				break;
705			default:
706				usage();
707		}
708	video_mode(argc, argv, &optind);
709	set_normal_colors(argc, argv, &optind);
710	if (optind < argc && !strcmp(argv[optind], "show")) {
711		test_frame();
712		optind++;
713	}
714	if ((optind != argc) || (argc == 1))
715		usage();
716	return 0;
717}
718
719