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