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