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