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