1 /*- 2 * Copyright (c) 1998 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The DragonFly Project 6 * by Sascha Wildner <saw@online.de> 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 as 13 * the first lines of this file unmodified. 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 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD: src/sys/dev/syscons/scvidctl.c,v 1.19.2.2 2000/05/05 09:16:08 nyan Exp $ 30 * $DragonFly: src/sys/dev/misc/syscons/scvidctl.c,v 1.16 2007/08/19 11:39:11 swildner Exp $ 31 */ 32 33 #include "opt_syscons.h" 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/conf.h> 38 #include <sys/signalvar.h> 39 #include <sys/tty.h> 40 #include <sys/kernel.h> 41 #include <sys/thread2.h> 42 43 #include <machine/console.h> 44 45 #include <dev/video/fb/fbreg.h> 46 #include "syscons.h" 47 48 SET_DECLARE(scrndr_set, const sc_renderer_t); 49 50 int 51 sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, 52 int fontsize) 53 { 54 video_info_t info; 55 u_char *font; 56 int prev_ysize; 57 int new_ysize; 58 int error; 59 60 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) 61 return ENODEV; 62 63 /* adjust argument values */ 64 if (fontsize <= 0) 65 fontsize = info.vi_cheight; 66 if (fontsize < 14) { 67 fontsize = 8; 68 #ifndef SC_NO_FONT_LOADING 69 if (!(scp->sc->fonts_loaded & FONT_8)) 70 return EINVAL; 71 font = scp->sc->font_8; 72 #else 73 font = NULL; 74 #endif 75 } else if (fontsize >= 16) { 76 fontsize = 16; 77 #ifndef SC_NO_FONT_LOADING 78 if (!(scp->sc->fonts_loaded & FONT_16)) 79 return EINVAL; 80 font = scp->sc->font_16; 81 #else 82 font = NULL; 83 #endif 84 } else { 85 fontsize = 14; 86 #ifndef SC_NO_FONT_LOADING 87 if (!(scp->sc->fonts_loaded & FONT_14)) 88 return EINVAL; 89 font = scp->sc->font_14; 90 #else 91 font = NULL; 92 #endif 93 } 94 if ((xsize <= 0) || (xsize > info.vi_width)) 95 xsize = info.vi_width; 96 if ((ysize <= 0) || (ysize > info.vi_height)) 97 ysize = info.vi_height; 98 99 /* stop screen saver, etc */ 100 crit_enter(); 101 if ((error = sc_clean_up(scp))) { 102 crit_exit(); 103 return error; 104 } 105 106 if (sc_render_match(scp, scp->sc->adp->va_name, 0) == NULL) { 107 crit_exit(); 108 return ENODEV; 109 } 110 111 /* set up scp */ 112 new_ysize = 0; 113 #ifndef SC_NO_HISTORY 114 if (scp->history != NULL) { 115 sc_hist_save(scp); 116 new_ysize = sc_vtb_rows(scp->history); 117 } 118 #endif 119 prev_ysize = scp->ysize; 120 /* 121 * This is a kludge to fend off scrn_update() while we 122 * muck around with scp. XXX 123 */ 124 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; 125 scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE); 126 scp->mode = mode; 127 scp->xsize = xsize; 128 scp->ysize = ysize; 129 scp->xoff = 0; 130 scp->yoff = 0; 131 scp->xpixel = scp->xsize*8; 132 scp->ypixel = scp->ysize*fontsize; 133 scp->font = font; 134 scp->font_size = fontsize; 135 136 /* allocate buffers */ 137 sc_alloc_scr_buffer(scp, TRUE, TRUE); 138 sc_init_emulator(scp, NULL); 139 #ifndef SC_NO_CUTPASTE 140 sc_alloc_cut_buffer(scp, FALSE); 141 #endif 142 #ifndef SC_NO_HISTORY 143 sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE); 144 #endif 145 crit_exit(); 146 147 if (scp == scp->sc->cur_scp) 148 set_mode(scp); 149 scp->status &= ~UNKNOWN_MODE; 150 151 if (tp == NULL) 152 return 0; 153 DPRINTF(5, ("ws_*size (%d,%d), size (%d,%d)\n", 154 tp->t_winsize.ws_col, tp->t_winsize.ws_row, scp->xsize, scp->ysize)); 155 if (tp->t_winsize.ws_col != scp->xsize 156 || tp->t_winsize.ws_row != scp->ysize) { 157 tp->t_winsize.ws_col = scp->xsize; 158 tp->t_winsize.ws_row = scp->ysize; 159 pgsignal(tp->t_pgrp, SIGWINCH, 1); 160 } 161 162 return 0; 163 } 164 165 int 166 sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode) 167 { 168 #ifdef SC_NO_MODE_CHANGE 169 return ENODEV; 170 #else 171 video_info_t info; 172 int error; 173 174 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) 175 return ENODEV; 176 177 /* stop screen saver, etc */ 178 crit_enter(); 179 if ((error = sc_clean_up(scp))) { 180 crit_exit(); 181 return error; 182 } 183 184 if (sc_render_match(scp, scp->sc->adp->va_name, GRAPHICS_MODE) == NULL) { 185 crit_exit(); 186 return ENODEV; 187 } 188 189 /* set up scp */ 190 scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE | MOUSE_HIDDEN); 191 scp->status &= ~(PIXEL_MODE | MOUSE_VISIBLE); 192 scp->mode = mode; 193 /* 194 * Don't change xsize and ysize; preserve the previous vty 195 * and history buffers. 196 */ 197 scp->xoff = 0; 198 scp->yoff = 0; 199 scp->xpixel = info.vi_width; 200 scp->ypixel = info.vi_height; 201 scp->font = NULL; 202 scp->font_size = 0; 203 #ifndef SC_NO_SYSMOUSE 204 /* move the mouse cursor at the center of the screen */ 205 sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2); 206 #endif 207 sc_init_emulator(scp, NULL); 208 crit_exit(); 209 210 if (scp == scp->sc->cur_scp) 211 set_mode(scp); 212 /* clear_graphics();*/ 213 refresh_ega_palette(scp); 214 scp->status &= ~UNKNOWN_MODE; 215 216 if (tp == NULL) 217 return 0; 218 if (tp->t_winsize.ws_xpixel != scp->xpixel 219 || tp->t_winsize.ws_ypixel != scp->ypixel) { 220 tp->t_winsize.ws_xpixel = scp->xpixel; 221 tp->t_winsize.ws_ypixel = scp->ypixel; 222 pgsignal(tp->t_pgrp, SIGWINCH, 1); 223 } 224 225 return 0; 226 #endif /* SC_NO_MODE_CHANGE */ 227 } 228 229 int 230 sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, 231 int fontsize) 232 { 233 #ifndef SC_PIXEL_MODE 234 return ENODEV; 235 #else 236 video_info_t info; 237 u_char *font; 238 int prev_ysize; 239 int new_ysize; 240 int error; 241 242 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) 243 return ENODEV; /* this shouldn't happen */ 244 245 /* adjust argument values */ 246 if (fontsize <= 0) 247 fontsize = info.vi_cheight; 248 if (fontsize < 14) { 249 fontsize = 8; 250 #ifndef SC_NO_FONT_LOADING 251 if (!(scp->sc->fonts_loaded & FONT_8)) 252 return EINVAL; 253 font = scp->sc->font_8; 254 #else 255 font = NULL; 256 #endif 257 } else if (fontsize >= 16) { 258 fontsize = 16; 259 #ifndef SC_NO_FONT_LOADING 260 if (!(scp->sc->fonts_loaded & FONT_16)) 261 return EINVAL; 262 font = scp->sc->font_16; 263 #else 264 font = NULL; 265 #endif 266 } else { 267 fontsize = 14; 268 #ifndef SC_NO_FONT_LOADING 269 if (!(scp->sc->fonts_loaded & FONT_14)) 270 return EINVAL; 271 font = scp->sc->font_14; 272 #else 273 font = NULL; 274 #endif 275 } 276 if (xsize <= 0) 277 xsize = info.vi_width/8; 278 if (ysize <= 0) 279 ysize = info.vi_height/fontsize; 280 281 if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize)) 282 return EINVAL; 283 284 /* 285 * We currently support the following graphic modes: 286 * 287 * - 4 bpp planar modes whose memory size does not exceed 64K 288 * - 15, 16, 24 and 32 bpp direct modes with linear frame buffer 289 */ 290 291 if (info.vi_mem_model == V_INFO_MM_PLANAR) { 292 if (info.vi_planes != 4) 293 return ENODEV; 294 295 /* 296 * A memory size >64K requires bank switching to access the entire 297 * screen. XXX 298 */ 299 300 if (info.vi_width * info.vi_height / 8 > info.vi_window_size) 301 return ENODEV; 302 } else if (info.vi_mem_model == V_INFO_MM_DIRECT) { 303 if (!(info.vi_flags & V_INFO_LINEAR) && 304 (info.vi_depth != 15) && (info.vi_depth != 16) && 305 (info.vi_depth != 24) && (info.vi_depth != 32)) 306 return ENODEV; 307 } else 308 return ENODEV; 309 310 /* stop screen saver, etc */ 311 crit_enter(); 312 if ((error = sc_clean_up(scp))) { 313 crit_exit(); 314 return error; 315 } 316 317 if (sc_render_match(scp, scp->sc->adp->va_name, PIXEL_MODE) == NULL) { 318 crit_exit(); 319 return ENODEV; 320 } 321 322 #if 0 323 if (scp->tsw) 324 (*scp->tsw->te_term)(scp, scp->ts); 325 scp->tsw = NULL; 326 scp->ts = NULL; 327 #endif 328 329 /* set up scp */ 330 new_ysize = 0; 331 #ifndef SC_NO_HISTORY 332 if (scp->history != NULL) { 333 sc_hist_save(scp); 334 new_ysize = sc_vtb_rows(scp->history); 335 } 336 #endif 337 prev_ysize = scp->ysize; 338 scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN); 339 scp->status &= ~(GRAPHICS_MODE | MOUSE_VISIBLE); 340 scp->xsize = xsize; 341 scp->ysize = ysize; 342 scp->xoff = (scp->xpixel/8 - xsize)/2; 343 scp->yoff = (scp->ypixel/fontsize - ysize)/2; 344 scp->font = font; 345 scp->font_size = fontsize; 346 347 /* allocate buffers */ 348 sc_alloc_scr_buffer(scp, TRUE, TRUE); 349 sc_init_emulator(scp, NULL); 350 #ifndef SC_NO_CUTPASTE 351 sc_alloc_cut_buffer(scp, FALSE); 352 #endif 353 #ifndef SC_NO_HISTORY 354 sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE); 355 #endif 356 crit_exit(); 357 358 if (scp == scp->sc->cur_scp) { 359 sc_set_border(scp, scp->border); 360 sc_set_cursor_image(scp); 361 } 362 363 scp->status &= ~UNKNOWN_MODE; 364 365 if (tp == NULL) 366 return 0; 367 if (tp->t_winsize.ws_col != scp->xsize 368 || tp->t_winsize.ws_row != scp->ysize) { 369 tp->t_winsize.ws_col = scp->xsize; 370 tp->t_winsize.ws_row = scp->ysize; 371 pgsignal(tp->t_pgrp, SIGWINCH, 1); 372 } 373 374 return 0; 375 #endif /* SC_PIXEL_MODE */ 376 } 377 378 #define fb_ioctl(a, c, d) \ 379 (((a) == NULL) ? ENODEV : \ 380 (*vidsw[(a)->va_index]->ioctl)((a), (c), (caddr_t)(d))) 381 382 int 383 sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag) 384 { 385 scr_stat *scp; 386 video_adapter_t *adp; 387 video_info_t info; 388 int error; 389 390 KKASSERT(tp->t_dev); 391 392 scp = SC_STAT(tp->t_dev); 393 if (scp == NULL) /* tp == SC_MOUSE */ 394 return ENOIOCTL; 395 adp = scp->sc->adp; 396 if (adp == NULL) /* shouldn't happen??? */ 397 return ENODEV; 398 399 switch (cmd) { 400 401 case CONS_CURRENTADP: /* get current adapter index */ 402 case FBIO_ADAPTER: 403 return fb_ioctl(adp, FBIO_ADAPTER, data); 404 405 case CONS_CURRENT: /* get current adapter type */ 406 case FBIO_ADPTYPE: 407 return fb_ioctl(adp, FBIO_ADPTYPE, data); 408 409 case CONS_ADPINFO: /* adapter information */ 410 case FBIO_ADPINFO: 411 if (((video_adapter_info_t *)data)->va_index >= 0) { 412 adp = vid_get_adapter(((video_adapter_info_t *)data)->va_index); 413 if (adp == NULL) 414 return ENODEV; 415 } 416 return fb_ioctl(adp, FBIO_ADPINFO, data); 417 418 case CONS_GET: /* get current video mode */ 419 case FBIO_GETMODE: 420 *(int *)data = scp->mode; 421 return 0; 422 423 #ifndef SC_NO_MODE_CHANGE 424 case CONS_SET: 425 case FBIO_SETMODE: /* set video mode */ 426 if (!(adp->va_flags & V_ADP_MODECHANGE)) 427 return ENODEV; 428 info.vi_mode = *(int *)data; 429 error = fb_ioctl(adp, FBIO_MODEINFO, &info); 430 if (error) 431 return error; 432 if (info.vi_flags & V_INFO_GRAPHICS) 433 return sc_set_graphics_mode(scp, tp, *(int *)data); 434 else 435 return sc_set_text_mode(scp, tp, *(int *)data, 0, 0, 0); 436 #endif /* SC_NO_MODE_CHANGE */ 437 438 case CONS_MODEINFO: /* get mode information */ 439 case FBIO_MODEINFO: 440 return fb_ioctl(adp, FBIO_MODEINFO, data); 441 442 case CONS_FINDMODE: /* find a matching video mode */ 443 case FBIO_FINDMODE: 444 return fb_ioctl(adp, FBIO_FINDMODE, data); 445 446 case CONS_SETWINORG: /* set frame buffer window origin */ 447 case FBIO_SETWINORG: 448 if (scp != scp->sc->cur_scp) 449 return ENODEV; /* XXX */ 450 return fb_ioctl(adp, FBIO_SETWINORG, data); 451 452 case FBIO_GETWINORG: /* get frame buffer window origin */ 453 if (scp != scp->sc->cur_scp) 454 return ENODEV; /* XXX */ 455 return fb_ioctl(adp, FBIO_GETWINORG, data); 456 457 case FBIO_GETDISPSTART: 458 case FBIO_SETDISPSTART: 459 case FBIO_GETLINEWIDTH: 460 case FBIO_SETLINEWIDTH: 461 if (scp != scp->sc->cur_scp) 462 return ENODEV; /* XXX */ 463 return fb_ioctl(adp, cmd, data); 464 465 case FBIO_GETPALETTE: 466 case FBIO_SETPALETTE: 467 case FBIOPUTCMAP: 468 case FBIOGETCMAP: 469 case FBIOGTYPE: 470 case FBIOGATTR: 471 case FBIOSVIDEO: 472 case FBIOGVIDEO: 473 case FBIOSCURSOR: 474 case FBIOGCURSOR: 475 case FBIOSCURPOS: 476 case FBIOGCURPOS: 477 case FBIOGCURMAX: 478 if (scp != scp->sc->cur_scp) 479 return ENODEV; /* XXX */ 480 return fb_ioctl(adp, cmd, data); 481 482 case KDSETMODE: /* set current mode of this (virtual) console */ 483 switch (*(int *)data) { 484 case KD_TEXT: /* switch to TEXT (known) mode */ 485 /* 486 * If scp->mode is of graphics modes, we don't know which 487 * text mode to switch back to... 488 */ 489 if (scp->status & GRAPHICS_MODE) 490 return EINVAL; 491 /* restore fonts & palette ! */ 492 #if 0 493 #ifndef SC_NO_FONT_LOADING 494 if (ISFONTAVAIL(adp->va_flags) 495 && !(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) 496 /* 497 * FONT KLUDGE 498 * Don't load fonts for now... XXX 499 */ 500 if (scp->sc->fonts_loaded & FONT_8) 501 sc_load_font(scp, 0, 8, scp->sc->font_8, 0, 256); 502 if (scp->sc->fonts_loaded & FONT_14) 503 sc_load_font(scp, 0, 14, scp->sc->font_14, 0, 256); 504 if (scp->sc->fonts_loaded & FONT_16) 505 sc_load_font(scp, 0, 16, scp->sc->font_16, 0, 256); 506 } 507 #endif /* SC_NO_FONT_LOADING */ 508 #endif 509 510 #ifndef SC_NO_PALETTE_LOADING 511 load_palette(adp, scp->sc->palette); 512 #endif 513 514 /* move hardware cursor out of the way */ 515 (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1); 516 /* FALL THROUGH */ 517 518 case KD_TEXT1: /* switch to TEXT (known) mode */ 519 /* 520 * If scp->mode is of graphics modes, we don't know which 521 * text/pixel mode to switch back to... 522 */ 523 if (scp->status & GRAPHICS_MODE) 524 return EINVAL; 525 crit_enter(); 526 if ((error = sc_clean_up(scp))) { 527 crit_exit(); 528 return error; 529 } 530 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; 531 crit_exit(); 532 /* no restore fonts & palette */ 533 if (scp == scp->sc->cur_scp) 534 set_mode(scp); 535 sc_clear_screen(scp); 536 scp->status &= ~UNKNOWN_MODE; 537 return 0; 538 539 #ifdef SC_PIXEL_MODE 540 case KD_PIXEL: /* pixel (raster) display */ 541 if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) 542 return EINVAL; 543 if (scp->status & GRAPHICS_MODE) 544 return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize, 545 scp->font_size); 546 crit_enter(); 547 if ((error = sc_clean_up(scp))) { 548 crit_exit(); 549 return error; 550 } 551 scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN); 552 crit_exit(); 553 if (scp == scp->sc->cur_scp) { 554 set_mode(scp); 555 #ifndef SC_NO_PALETTE_LOADING 556 load_palette(adp, scp->sc->palette); 557 #endif 558 } 559 sc_clear_screen(scp); 560 scp->status &= ~UNKNOWN_MODE; 561 return 0; 562 #endif /* SC_PIXEL_MODE */ 563 564 case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ 565 crit_enter(); 566 if ((error = sc_clean_up(scp))) { 567 crit_exit(); 568 return error; 569 } 570 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; 571 crit_exit(); 572 return 0; 573 574 default: 575 return EINVAL; 576 } 577 /* NOT REACHED */ 578 579 #ifdef SC_PIXEL_MODE 580 case KDRASTER: /* set pixel (raster) display mode */ 581 if (ISUNKNOWNSC(scp) || ISTEXTSC(scp)) 582 return ENODEV; 583 return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1], 584 ((int *)data)[2]); 585 #endif /* SC_PIXEL_MODE */ 586 587 case KDGETMODE: /* get current mode of this (virtual) console */ 588 /* 589 * From the user program's point of view, KD_PIXEL is the same 590 * as KD_TEXT... 591 */ 592 *data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT; 593 return 0; 594 595 case KDSBORDER: /* set border color of this (virtual) console */ 596 scp->border = *data; 597 if (scp == scp->sc->cur_scp) 598 sc_set_border(scp, scp->border); 599 return 0; 600 } 601 602 return ENOIOCTL; 603 } 604 605 static LIST_HEAD(, sc_renderer) sc_rndr_list = 606 LIST_HEAD_INITIALIZER(sc_rndr_list); 607 608 int 609 sc_render_add(sc_renderer_t *rndr) 610 { 611 LIST_INSERT_HEAD(&sc_rndr_list, rndr, link); 612 return 0; 613 } 614 615 int 616 sc_render_remove(sc_renderer_t *rndr) 617 { 618 /* 619 LIST_REMOVE(rndr, link); 620 */ 621 return EBUSY; /* XXX */ 622 } 623 624 sc_rndr_sw_t * 625 sc_render_match(scr_stat *scp, char *name, int mode) 626 { 627 const sc_renderer_t **list; 628 const sc_renderer_t *p; 629 630 if (!LIST_EMPTY(&sc_rndr_list)) { 631 LIST_FOREACH(p, &sc_rndr_list, link) { 632 if ((strcmp(p->name, name) == 0) 633 && (mode == p->mode)) { 634 scp->status &= 635 ~(VR_CURSOR_ON | VR_CURSOR_BLINK); 636 return p->rndrsw; 637 } 638 } 639 } else { 640 SET_FOREACH(list, scrndr_set) { 641 p = *list; 642 if ((strcmp(p->name, name) == 0) 643 && (mode == p->mode)) { 644 scp->status &= 645 ~(VR_CURSOR_ON | VR_CURSOR_BLINK); 646 return p->rndrsw; 647 } 648 } 649 } 650 651 return NULL; 652 } 653