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