1 /* $NetBSD: wsdisplay_vcons.c,v 1.8 2006/10/12 01:32:06 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2006 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the NetBSD 18 * Foundation, Inc. and its contributors. 19 * 4. Neither the name of The NetBSD Foundation nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.8 2006/10/12 01:32:06 christos Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/buf.h> 43 #include <sys/device.h> 44 #include <sys/ioctl.h> 45 #include <sys/malloc.h> 46 #include <sys/mman.h> 47 #include <sys/tty.h> 48 #include <sys/conf.h> 49 #include <sys/proc.h> 50 #include <sys/kthread.h> 51 #include <sys/tprintf.h> 52 53 #include <dev/wscons/wsdisplayvar.h> 54 #include <dev/wscons/wsconsio.h> 55 #include <dev/wsfont/wsfont.h> 56 #include <dev/rasops/rasops.h> 57 58 #include <dev/wscons/wsdisplay_vconsvar.h> 59 60 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int, 61 long *); 62 63 static int vcons_ioctl(void *, void *, u_long, caddr_t, int, struct lwp *); 64 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 65 int *, int *, long *); 66 static void vcons_free_screen(void *, void *); 67 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int), 68 void *); 69 70 static void vcons_do_switch(struct vcons_data *); 71 72 /* methods that work only on text buffers */ 73 static void vcons_copycols_buffer(void *, int, int, int, int); 74 static void vcons_erasecols_buffer(void *, int, int, int, long); 75 static void vcons_copyrows_buffer(void *, int, int, int); 76 static void vcons_eraserows_buffer(void *, int, int, long); 77 static void vcons_putchar_buffer(void *, int, int, u_int, long); 78 79 /* 80 * actual wrapper methods which call both the _buffer ones above and the 81 * driver supplied ones to do the drawing 82 */ 83 static void vcons_copycols(void *, int, int, int, int); 84 static void vcons_erasecols(void *, int, int, int, long); 85 static void vcons_copyrows(void *, int, int, int); 86 static void vcons_eraserows(void *, int, int, long); 87 static void vcons_putchar(void *, int, int, u_int, long); 88 static void vcons_cursor(void *, int, int, int); 89 90 /* support for readin/writing text buffers. For wsmoused */ 91 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *); 92 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *); 93 94 static void vcons_lock(struct vcons_screen *); 95 static void vcons_unlock(struct vcons_screen *); 96 97 98 int 99 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def, 100 struct wsdisplay_accessops *ao) 101 { 102 103 /* zero out everything so we can rely on untouched fields being 0 */ 104 memset(vd, 0, sizeof(struct vcons_data)); 105 106 vd->cookie = cookie; 107 108 vd->init_screen = vcons_dummy_init_screen; 109 vd->show_screen_cb = NULL; 110 111 /* keep a copy of the accessops that we replace below with our 112 * own wrappers */ 113 vd->ioctl = ao->ioctl; 114 115 /* configure the accessops */ 116 ao->ioctl = vcons_ioctl; 117 ao->alloc_screen = vcons_alloc_screen; 118 ao->free_screen = vcons_free_screen; 119 ao->show_screen = vcons_show_screen; 120 121 LIST_INIT(&vd->screens); 122 vd->active = NULL; 123 vd->wanted = NULL; 124 vd->currenttype = def; 125 callout_init(&vd->switch_callout); 126 127 /* 128 * a lock to serialize access to the framebuffer. 129 * when switching screens we need to make sure there's no rasops 130 * operation in progress 131 */ 132 #ifdef DIAGNOSTIC 133 vd->switch_poll_count = 0; 134 #endif 135 return 0; 136 } 137 138 static void 139 vcons_lock(struct vcons_screen *scr) 140 { 141 #ifdef VCONS_PARANOIA 142 int s; 143 144 s = splhigh(); 145 #endif 146 SCREEN_BUSY(scr); 147 #ifdef VCONS_PARANOIA 148 splx(s); 149 #endif 150 } 151 152 static void 153 vcons_unlock(struct vcons_screen *scr) 154 { 155 #ifdef VCONS_PARANOIA 156 int s; 157 158 s = splhigh(); 159 #endif 160 SCREEN_IDLE(scr); 161 #ifdef VCONS_PARANOIA 162 splx(s); 163 #endif 164 } 165 166 static void 167 vcons_dummy_init_screen(void *cookie __unused, 168 struct vcons_screen *scr __unused, int exists __unused, 169 long *defattr __unused) 170 { 171 172 /* 173 * default init_screen() method. 174 * Needs to be overwritten so we bitch and whine in case anyone ends 175 * up in here. 176 */ 177 printf("vcons_init_screen: dummy function called. Your driver is " 178 "supposed to supply a replacement for proper operation\n"); 179 } 180 181 int 182 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr, 183 int existing, long *defattr) 184 { 185 struct rasops_info *ri = &scr->scr_ri; 186 int cnt, i; 187 188 scr->scr_cookie = vd->cookie; 189 scr->scr_vd = scr->scr_origvd = vd; 190 SCREEN_IDLE(scr); 191 192 /* 193 * call the driver-supplied init_screen function which is expected 194 * to set up rasops_info, override cursor() and probably others 195 */ 196 vd->init_screen(vd->cookie, scr, existing, defattr); 197 198 /* 199 * save the non virtual console aware rasops and replace them with 200 * our wrappers 201 */ 202 vd->eraserows = ri->ri_ops.eraserows; 203 vd->copyrows = ri->ri_ops.copyrows; 204 vd->erasecols = ri->ri_ops.erasecols; 205 vd->copycols = ri->ri_ops.copycols; 206 vd->putchar = ri->ri_ops.putchar; 207 vd->cursor = ri->ri_ops.cursor; 208 209 ri->ri_ops.eraserows = vcons_eraserows; 210 ri->ri_ops.copyrows = vcons_copyrows; 211 ri->ri_ops.erasecols = vcons_erasecols; 212 ri->ri_ops.copycols = vcons_copycols; 213 ri->ri_ops.putchar = vcons_putchar; 214 ri->ri_ops.cursor = vcons_cursor; 215 ri->ri_hw = scr; 216 217 /* 218 * we allocate both chars and attributes in one chunk, attributes first 219 * because they have the (potentially) bigger alignment 220 */ 221 cnt = ri->ri_rows * ri->ri_cols; 222 scr->scr_attrs = (long *)malloc(cnt * (sizeof(long) + 223 sizeof(uint16_t)), M_DEVBUF, M_WAITOK); 224 if (scr->scr_attrs == NULL) 225 return ENOMEM; 226 227 scr->scr_chars = (uint16_t *)&scr->scr_attrs[cnt]; 228 229 ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr); 230 scr->scr_defattr = *defattr; 231 232 /* 233 * fill the attribute buffer with *defattr, chars with 0x20 234 * since we don't know if the driver tries to mimic firmware output or 235 * reset everything we do nothing to VRAM here, any driver that feels 236 * the need to clear screen or something will have to do it on its own 237 * Additional screens will start out in the background anyway so 238 * cleaning or not only really affects the initial console screen 239 */ 240 for (i = 0; i < cnt; i++) { 241 scr->scr_attrs[i] = *defattr; 242 scr->scr_chars[i] = 0x20; 243 } 244 245 if(vd->active == NULL) { 246 vd->active = scr; 247 SCREEN_VISIBLE(scr); 248 } 249 250 if (existing) { 251 SCREEN_VISIBLE(scr); 252 vd->active = scr; 253 } else { 254 SCREEN_INVISIBLE(scr); 255 } 256 257 LIST_INSERT_HEAD(&vd->screens, scr, next); 258 return 0; 259 } 260 261 static void 262 vcons_do_switch(struct vcons_data *vd) 263 { 264 struct vcons_screen *scr, *oldscr; 265 266 scr = vd->wanted; 267 if (!scr) { 268 printf("vcons_switch_screen: disappeared\n"); 269 vd->switch_cb(vd->switch_cb_arg, EIO, 0); 270 return; 271 } 272 oldscr = vd->active; /* can be NULL! */ 273 274 /* 275 * if there's an old, visible screen we mark it invisible and wait 276 * until it's not busy so we can safely switch 277 */ 278 if (oldscr != NULL) { 279 SCREEN_INVISIBLE(oldscr); 280 if (SCREEN_IS_BUSY(oldscr)) { 281 callout_reset(&vd->switch_callout, 1, 282 (void(*)(void *))vcons_do_switch, vd); 283 #ifdef DIAGNOSTIC 284 /* bitch if we wait too long */ 285 vd->switch_poll_count++; 286 if (vd->switch_poll_count > 100) { 287 panic("vcons: screen still busy"); 288 } 289 #endif 290 return; 291 } 292 /* invisible screen -> no visible cursor image */ 293 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 294 #ifdef DIAGNOSTIC 295 vd->switch_poll_count = 0; 296 #endif 297 } 298 299 if (scr == oldscr) 300 return; 301 302 #ifdef DIAGNOSTIC 303 if (SCREEN_IS_VISIBLE(scr)) 304 panic("vcons_switch_screen: already active"); 305 #endif 306 307 #ifdef notyet 308 if (vd->currenttype != type) { 309 vcons_set_screentype(vd, type); 310 vd->currenttype = type; 311 } 312 #endif 313 314 SCREEN_VISIBLE(scr); 315 vd->active = scr; 316 vd->wanted = NULL; 317 318 if (vd->show_screen_cb != NULL) 319 vd->show_screen_cb(scr); 320 321 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 322 vcons_redraw_screen(scr); 323 324 if (vd->switch_cb) 325 vd->switch_cb(vd->switch_cb_arg, 0, 0); 326 } 327 328 void 329 vcons_redraw_screen(struct vcons_screen *scr) 330 { 331 uint16_t *charptr = scr->scr_chars; 332 long *attrptr = scr->scr_attrs; 333 struct rasops_info *ri = &scr->scr_ri; 334 int i, j, offset; 335 336 vcons_lock(scr); 337 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 338 339 /* 340 * only clear the screen when RI_FULLCLEAR is set since we're 341 * going to overwrite every single character cell anyway 342 */ 343 if (ri->ri_flg & RI_FULLCLEAR) { 344 scr->scr_vd->eraserows(ri, 0, ri->ri_rows, 345 scr->scr_defattr); 346 } 347 348 /* redraw the screen */ 349 offset = 0; 350 for (i = 0; i < ri->ri_rows; i++) { 351 for (j = 0; j < ri->ri_cols; j++) { 352 /* 353 * no need to use the wrapper function - we 354 * don't change any characters or attributes 355 * and we already made sure the screen we're 356 * working on is visible 357 */ 358 scr->scr_vd->putchar(ri, i, j, 359 charptr[offset], attrptr[offset]); 360 offset++; 361 } 362 } 363 ri->ri_flg &= ~RI_CURSOR; 364 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 365 } 366 vcons_unlock(scr); 367 } 368 369 static int 370 vcons_ioctl(void *v, void *vs, u_long cmd, caddr_t data, int flag, 371 struct lwp *l) 372 { 373 struct vcons_data *vd = v; 374 int error; 375 376 switch (cmd) { 377 case WSDISPLAYIO_GETWSCHAR: 378 error = vcons_getwschar((struct vcons_screen *)vs, 379 (struct wsdisplay_char *)data); 380 break; 381 382 case WSDISPLAYIO_PUTWSCHAR: 383 error = vcons_putwschar((struct vcons_screen *)vs, 384 (struct wsdisplay_char *)data); 385 break; 386 387 default: 388 if (vd->ioctl != NULL) 389 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 390 else 391 error = EPASSTHROUGH; 392 } 393 394 return error; 395 } 396 397 static int 398 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 399 int *curxp, int *curyp, long *defattrp) 400 { 401 struct vcons_data *vd = v; 402 struct vcons_screen *scr; 403 int ret; 404 405 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 406 if (scr == NULL) 407 return ENOMEM; 408 409 scr->scr_flags = 0; 410 scr->scr_status = 0; 411 scr->scr_busy = 0; 412 scr->scr_type = type; 413 414 ret = vcons_init_screen(vd, scr, 0, defattrp); 415 if (ret != 0) { 416 free(scr, M_DEVBUF); 417 return ret; 418 } 419 420 if (vd->active == NULL) { 421 SCREEN_VISIBLE(scr); 422 vd->active = scr; 423 vd->currenttype = type; 424 } 425 426 *cookiep = scr; 427 *curxp = scr->scr_ri.ri_ccol; 428 *curyp = scr->scr_ri.ri_crow; 429 return 0; 430 } 431 432 static void 433 vcons_free_screen(void *v, void *cookie) 434 { 435 struct vcons_data *vd = v; 436 struct vcons_screen *scr = cookie; 437 438 vcons_lock(scr); 439 /* there should be no rasops activity here */ 440 441 LIST_REMOVE(scr, next); 442 443 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 444 free(scr->scr_attrs, M_DEVBUF); 445 free(scr, M_DEVBUF); 446 } else { 447 /* 448 * maybe we should just restore the old rasops_info methods 449 * and free the character/attribute buffer here? 450 */ 451 #ifdef VCONS_DEBUG 452 panic("vcons_free_screen: console"); 453 #else 454 printf("vcons_free_screen: console\n"); 455 #endif 456 } 457 458 if (vd->active == scr) 459 vd->active = NULL; 460 } 461 462 static int 463 vcons_show_screen(void *v, void *cookie, int waitok __unused, 464 void (*cb)(void *, int, int), void *cb_arg) 465 { 466 struct vcons_data *vd = v; 467 struct vcons_screen *scr; 468 469 scr = cookie; 470 if (scr == vd->active) 471 return 0; 472 473 vd->wanted = scr; 474 vd->switch_cb = cb; 475 vd->switch_cb_arg = cb_arg; 476 if (cb) { 477 callout_reset(&vd->switch_callout, 0, 478 (void(*)(void *))vcons_do_switch, vd); 479 return EAGAIN; 480 } 481 482 vcons_do_switch(vd); 483 return 0; 484 } 485 486 /* wrappers for rasops_info methods */ 487 488 static void 489 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 490 { 491 struct rasops_info *ri = cookie; 492 struct vcons_screen *scr = ri->ri_hw; 493 int from = srccol + row * ri->ri_cols; 494 int to = dstcol + row * ri->ri_cols; 495 496 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 497 ncols * sizeof(long)); 498 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 499 ncols * sizeof(uint16_t)); 500 } 501 502 static void 503 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 504 { 505 struct rasops_info *ri = cookie; 506 struct vcons_screen *scr = ri->ri_hw; 507 508 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 509 510 vcons_lock(scr); 511 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 512 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); 513 } 514 vcons_unlock(scr); 515 } 516 517 static void 518 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 519 { 520 struct rasops_info *ri = cookie; 521 struct vcons_screen *scr = ri->ri_hw; 522 int start = startcol + row * ri->ri_cols; 523 int end = start + ncols, i; 524 525 for (i = start; i < end; i++) { 526 scr->scr_attrs[i] = fillattr; 527 scr->scr_chars[i] = 0x20; 528 } 529 } 530 531 static void 532 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 533 { 534 struct rasops_info *ri = cookie; 535 struct vcons_screen *scr = ri->ri_hw; 536 537 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 538 539 vcons_lock(scr); 540 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 541 scr->scr_vd->erasecols(cookie, row, startcol, ncols, 542 fillattr); 543 } 544 vcons_unlock(scr); 545 } 546 547 static void 548 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 549 { 550 struct rasops_info *ri = cookie; 551 struct vcons_screen *scr = ri->ri_hw; 552 int from, to, len; 553 554 from = ri->ri_cols * srcrow; 555 to = ri->ri_cols * dstrow; 556 len = ri->ri_cols * nrows; 557 558 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 559 len * sizeof(long)); 560 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 561 len * sizeof(uint16_t)); 562 } 563 564 static void 565 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 566 { 567 struct rasops_info *ri = cookie; 568 struct vcons_screen *scr = ri->ri_hw; 569 570 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 571 572 vcons_lock(scr); 573 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 574 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); 575 } 576 vcons_unlock(scr); 577 } 578 579 static void 580 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 581 { 582 struct rasops_info *ri = cookie; 583 struct vcons_screen *scr = ri->ri_hw; 584 int start, end, i; 585 586 start = ri->ri_cols * row; 587 end = ri->ri_cols * (row + nrows); 588 589 for (i = start; i < end; i++) { 590 scr->scr_attrs[i] = fillattr; 591 scr->scr_chars[i] = 0x20; 592 } 593 } 594 595 static void 596 vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 597 { 598 struct rasops_info *ri = cookie; 599 struct vcons_screen *scr = ri->ri_hw; 600 601 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 602 603 vcons_lock(scr); 604 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 605 scr->scr_vd->eraserows(cookie, row, nrows, fillattr); 606 } 607 vcons_unlock(scr); 608 } 609 610 static void 611 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 612 { 613 struct rasops_info *ri = cookie; 614 struct vcons_screen *scr = ri->ri_hw; 615 int pos; 616 617 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 618 (col < ri->ri_cols)) { 619 pos = col + row * ri->ri_cols; 620 scr->scr_attrs[pos] = attr; 621 scr->scr_chars[pos] = c; 622 } 623 } 624 625 static void 626 vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 627 { 628 struct rasops_info *ri = cookie; 629 struct vcons_screen *scr = ri->ri_hw; 630 631 vcons_putchar_buffer(cookie, row, col, c, attr); 632 633 vcons_lock(scr); 634 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 635 scr->scr_vd->putchar(cookie, row, col, c, attr); 636 } 637 vcons_unlock(scr); 638 } 639 640 static void 641 vcons_cursor(void *cookie, int on, int row, int col) 642 { 643 struct rasops_info *ri = cookie; 644 struct vcons_screen *scr = ri->ri_hw; 645 646 vcons_lock(scr); 647 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 648 scr->scr_vd->cursor(cookie, on, row, col); 649 } else { 650 scr->scr_ri.ri_crow = row; 651 scr->scr_ri.ri_ccol = col; 652 } 653 vcons_unlock(scr); 654 } 655 656 /* methods to read/write characters via ioctl() */ 657 658 static int 659 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 660 { 661 long attr; 662 struct rasops_info *ri; 663 664 KASSERT(scr != NULL && wsc != NULL); 665 666 ri = &scr->scr_ri; 667 668 ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background, 669 wsc->flags, &attr); 670 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 671 return 0; 672 } 673 674 static int 675 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 676 { 677 int offset; 678 long attr; 679 struct rasops_info *ri; 680 681 KASSERT(scr != NULL && wsc != NULL); 682 683 ri = &scr->scr_ri; 684 offset = ri->ri_cols * wsc->row + wsc->col; 685 wsc->letter = scr->scr_chars[offset]; 686 attr = scr->scr_attrs[offset]; 687 688 /* 689 * this is ugly. We need to break up an attribute into colours and 690 * flags but there's no rasops method to do that so we must rely on 691 * the 'canonical' encoding. 692 */ 693 wsc->foreground = (attr & 0xff000000) >> 24; 694 wsc->background = (attr & 0x00ff0000) >> 16; 695 wsc->flags = (attr & 0x0000ff00) >> 8; 696 return 0; 697 } 698