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