1 /* graphics.c - graphics mode support for GRUB */ 2 /* Implemented as a terminal type by Jeremy Katz <katzj@redhat.com> based 3 * on a patch by Paulo C�sar Pereira de Andrade <pcpa@conectiva.com.br> 4 */ 5 /* 6 * GRUB -- GRand Unified Bootloader 7 * Copyright (C) 2001,2002 Red Hat, Inc. 8 * Portions copyright (C) 2000 Conectiva, Inc. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 */ 24 25 26 27 #ifdef SUPPORT_GRAPHICS 28 29 #include <term.h> 30 #include <shared.h> 31 #include <graphics.h> 32 33 #ifdef OVERLAY_LOGO 34 #include <logo.xbm> 35 #endif /* OVERLAY_LOGO */ 36 37 int saved_videomode; 38 unsigned char *font8x16; 39 40 int graphics_inited = 0; 41 42 #define PALETTE_REDSHIFT 16 43 #define PALETTE_GREENSHIFT 8 44 #define PALETTE_COLORMASK 63 45 46 #define PALETTE_NCOLORS 16 47 48 #define PALETTE_RED(entry) ((entry) >> PALETTE_REDSHIFT) 49 #define PALETTE_GREEN(entry) (((entry) >> PALETTE_GREENSHIFT) & \ 50 PALETTE_COLORMASK) 51 #define PALETTE_BLUE(entry) ((entry) & PALETTE_COLORMASK) 52 53 static char splashimage[64]; 54 static int splash_palette[PALETTE_NCOLORS]; 55 56 #define HPIXELS 640 57 #define VPIXELS 480 58 #define HPIXELSPERBYTE 8 59 60 #define ROWBYTES (HPIXELS / HPIXELSPERBYTE) 61 #define SCREENBYTES (ROWBYTES * VPIXELS) 62 63 #define VSHADOW VSHADOW1 64 unsigned char VSHADOW1[SCREENBYTES]; 65 unsigned char VSHADOW2[SCREENBYTES]; 66 unsigned char VSHADOW4[SCREENBYTES]; 67 unsigned char VSHADOW8[SCREENBYTES]; 68 69 static unsigned char *s1 = (unsigned char*)VSHADOW1; 70 static unsigned char *s2 = (unsigned char*)VSHADOW2; 71 static unsigned char *s4 = (unsigned char*)VSHADOW4; 72 static unsigned char *s8 = (unsigned char*)VSHADOW8; 73 74 /* constants to define the viewable area */ 75 const int x0 = 0; 76 const int x1 = ROWBYTES; 77 const int y0 = 0; 78 const int y1 = 30; 79 80 /* text buffer has to be kept around so that we can write things as we 81 * scroll and the like */ 82 unsigned short text[ROWBYTES * 30]; 83 84 /* why do these have to be kept here? */ 85 int foreground = (63 << 16) | (63 << 8) | (63), background = 0, border = 0; 86 87 88 /* current position */ 89 static int fontx = 0; 90 static int fonty = 0; 91 92 /* global state so that we don't try to recursively scroll or cursor */ 93 static int no_scroll = 0; 94 95 /* color state */ 96 static int graphics_standard_color = A_NORMAL; 97 static int graphics_normal_color = A_NORMAL; 98 static int graphics_highlight_color = A_REVERSE; 99 static int graphics_current_color = A_NORMAL; 100 static color_state graphics_color_state = COLOR_STATE_STANDARD; 101 102 103 /* graphics local functions */ 104 static void graphics_setxy(int col, int row); 105 static void graphics_scroll(void); 106 static int read_image(char *); 107 108 #ifdef OVERLAY_LOGO 109 static void draw_xbmlogo(void); 110 #endif /* OVERLAY_LOGO */ 111 112 /* FIXME: where do these really belong? */ 113 static inline void outb(unsigned short port, unsigned char val) 114 { 115 __asm __volatile ("outb %0,%1"::"a" (val), "d" (port)); 116 } 117 118 static void MapMask(int value) { 119 outb(0x3c4, 2); 120 outb(0x3c5, value); 121 } 122 123 /* bit mask register */ 124 static void BitMask(int value) { 125 outb(0x3ce, 8); 126 outb(0x3cf, value); 127 } 128 129 130 /* Set the splash image */ 131 void graphics_set_splash(char *splashfile) { 132 grub_strcpy(splashimage, splashfile); 133 } 134 135 /* Get the current splash image */ 136 char *graphics_get_splash(void) { 137 return splashimage; 138 } 139 140 /* Initialize a vga16 graphics display with the palette based off of 141 * the image in splashimage. If the image doesn't exist, leave graphics 142 * mode. */ 143 int graphics_init() 144 { 145 int image_read, index, color; 146 147 if (!graphics_inited) { 148 saved_videomode = set_videomode(0x12); 149 } 150 151 font8x16 = (unsigned char*)graphics_get_font(); 152 153 image_read = read_image(splashimage); 154 155 /* 156 * Set VGA palette color 0 to be the system background color, 15 to be the 157 * system foreground color, and 17 to be the system border color. 158 * 159 * If the splashimage was read successfully, program the palette with 160 * its new colors; if not, set them to the background color. 161 */ 162 163 graphics_set_palette(0, PALETTE_RED(background), PALETTE_GREEN(background), 164 PALETTE_BLUE(background)); 165 166 for (index = 1; index < 15; index++) { 167 color = (image_read ? splash_palette[index] : background); 168 graphics_set_palette(index, PALETTE_RED(color), 169 PALETTE_GREEN(color), PALETTE_BLUE(color)); 170 } 171 172 graphics_set_palette(15, PALETTE_RED(foreground), 173 PALETTE_GREEN(foreground), PALETTE_BLUE(foreground)); 174 175 graphics_set_palette(0x11, PALETTE_RED(border), PALETTE_GREEN(border), 176 PALETTE_BLUE(border)); 177 178 #ifdef OVERLAY_LOGO 179 draw_xbmlogo(); 180 #endif /* OVERLAY_LOGO */ 181 182 graphics_inited = 1; 183 184 /* make sure that the highlight color is set correctly */ 185 graphics_highlight_color = ((graphics_normal_color >> 4) | 186 ((graphics_normal_color & 0xf) << 4)); 187 188 return 1; 189 } 190 191 /* Leave graphics mode */ 192 void graphics_end(void) 193 { 194 if (graphics_inited) { 195 set_videomode(saved_videomode); 196 graphics_inited = 0; 197 } 198 } 199 200 /* Print ch on the screen. Handle any needed scrolling or the like */ 201 void graphics_putchar(int ch) { 202 ch &= 0xff; 203 204 graphics_cursor(0); 205 206 if (ch == '\n') { 207 if (fonty + 1 < y1) 208 graphics_setxy(fontx, fonty + 1); 209 else 210 graphics_scroll(); 211 graphics_cursor(1); 212 return; 213 } else if (ch == '\r') { 214 graphics_setxy(x0, fonty); 215 graphics_cursor(1); 216 return; 217 } 218 219 graphics_cursor(0); 220 221 text[fonty * ROWBYTES + fontx] = ch; 222 text[fonty * ROWBYTES + fontx] &= 0x00ff; 223 if (graphics_current_color & 0xf0) 224 text[fonty * ROWBYTES + fontx] |= 0x100; 225 226 graphics_cursor(0); 227 228 if ((fontx + 1) >= x1) { 229 graphics_setxy(x0, fonty); 230 if (fonty + 1 < y1) 231 graphics_setxy(x0, fonty + 1); 232 else 233 graphics_scroll(); 234 } else { 235 graphics_setxy(fontx + 1, fonty); 236 } 237 238 graphics_cursor(1); 239 } 240 241 /* get the current location of the cursor */ 242 int graphics_getxy(void) { 243 return (fontx << 8) | fonty; 244 } 245 246 void graphics_gotoxy(int x, int y) { 247 graphics_cursor(0); 248 249 graphics_setxy(x, y); 250 251 graphics_cursor(1); 252 } 253 254 void graphics_cls(void) { 255 int i; 256 unsigned char *mem; 257 258 graphics_cursor(0); 259 graphics_gotoxy(x0, y0); 260 261 mem = (unsigned char*)VIDEOMEM; 262 263 for (i = 0; i < ROWBYTES * 30; i++) 264 text[i] = ' '; 265 graphics_cursor(1); 266 267 BitMask(0xff); 268 269 /* plane 1 */ 270 MapMask(1); 271 grub_memcpy(mem, s1, SCREENBYTES); 272 273 /* plane 2 */ 274 MapMask(2); 275 grub_memcpy(mem, s2, SCREENBYTES); 276 277 /* plane 3 */ 278 MapMask(4); 279 grub_memcpy(mem, s4, SCREENBYTES); 280 281 /* plane 4 */ 282 MapMask(8); 283 grub_memcpy(mem, s8, SCREENBYTES); 284 285 MapMask(15); 286 } 287 288 void graphics_setcolorstate (color_state state) { 289 switch (state) { 290 case COLOR_STATE_STANDARD: 291 graphics_current_color = graphics_standard_color; 292 break; 293 case COLOR_STATE_NORMAL: 294 graphics_current_color = graphics_normal_color; 295 break; 296 case COLOR_STATE_HIGHLIGHT: 297 graphics_current_color = graphics_highlight_color; 298 break; 299 default: 300 graphics_current_color = graphics_standard_color; 301 break; 302 } 303 304 graphics_color_state = state; 305 } 306 307 void graphics_setcolor (int normal_color, int highlight_color) { 308 graphics_normal_color = normal_color; 309 graphics_highlight_color = highlight_color; 310 311 graphics_setcolorstate (graphics_color_state); 312 } 313 314 int graphics_setcursor (int on) { 315 /* FIXME: we don't have a cursor in graphics */ 316 return 1; 317 } 318 319 #ifdef OVERLAY_LOGO 320 static void draw_xbmlogo(void) 321 { 322 unsigned char mask; 323 unsigned xbm_index = 0, xbm_incr; 324 unsigned screenx, logox, logoy, fb_offset, fb_index; 325 326 /* 327 * Place the logo such that the right hand side will be four pixels from 328 * the right hand edge of the screen and the bottom will be two pixels 329 * from the bottom edge. 330 */ 331 fb_offset = ((VPIXELS - 1) - logo_height - 2) * ROWBYTES; 332 xbm_incr = (logo_width / 8) + 1; 333 334 for (logoy = 0; logoy < logo_height; logoy++) { 335 for (logox = 0, screenx = (HPIXELS - 1) - logo_width - 4; 336 logox < logo_width; logox++, screenx++) { 337 mask = 0x80 >> (screenx & 7); 338 fb_index = fb_offset + (screenx >> 3); 339 340 /* 341 * If a bit is clear in the bitmap, draw it onto the 342 * framebuffer in the default foreground color. 343 */ 344 if ((logo_bits[xbm_index + (logox >> 3)] & 345 (1 << (logox & 7))) == 0) { 346 /* system default foreground color */ 347 s1[fb_index] |= mask; 348 s2[fb_index] |= mask; 349 s4[fb_index] |= mask; 350 s8[fb_index] |= mask; 351 } 352 } 353 354 xbm_index += xbm_incr; 355 fb_offset += ROWBYTES; 356 } 357 } 358 #endif /* OVERLAY_LOGO */ 359 360 /* 361 * Read in the splashscreen image and set the palette up appropriately. 362 * 363 * Format of splashscreen is an XPM (can be gzipped) with up to 15 colors and 364 * is assumed to be of the proper screen dimensions. 365 */ 366 static int read_image(char *s) 367 { 368 char buf[32], pal[16]; 369 unsigned char c, base, mask; 370 unsigned i, len, idx, colors, x, y, width, height; 371 372 if (!grub_open(s)) 373 return 0; 374 375 /* read XPM header - must match memcmp string PRECISELY. */ 376 if (!grub_read((char*)&buf, 10) || grub_memcmp(buf, "/* XPM */\n", 10)) { 377 errnum = ERR_NOTXPM; 378 grub_close(); 379 return 0; 380 } 381 382 /* skip characters until we reach an initial '"' */ 383 while (grub_read(&c, 1)) { 384 if (c == '"') 385 break; 386 } 387 388 /* skip whitespace */ 389 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) 390 ; 391 392 /* 393 * Format here should be four integers: 394 * 395 * Width Height NumberOfColors CharactersPerPixel 396 */ 397 i = 0; 398 width = c - '0'; 399 while (grub_read(&c, 1)) { 400 if (c >= '0' && c <= '9') 401 width = width * 10 + c - '0'; 402 else 403 break; 404 } 405 406 /* skip whitespace to advance to next digit */ 407 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) 408 ; 409 410 height = c - '0'; 411 while (grub_read(&c, 1)) { 412 if (c >= '0' && c <= '9') 413 height = height * 10 + c - '0'; 414 else 415 break; 416 } 417 418 /* skip whitespace to advance to next digit */ 419 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ; 420 421 colors = c - '0'; 422 while (grub_read(&c, 1)) { 423 if (c >= '0' && c <= '9') 424 colors = colors * 10 + c - '0'; 425 else 426 break; 427 } 428 429 /* eat rest of line - assumes chars per pixel is one */ 430 while (grub_read(&c, 1) && c != '"') 431 ; 432 433 /* 434 * Parse the XPM palette - the format is: 435 * 436 * identifier colorspace #RRGGBB 437 * 438 * The identifier is simply a single character; the colorspace identifier 439 * is skipped as it's assumed to be "c" denoting RGB color. 440 * 441 * The six digits after the "#" are assumed to be a six digit RGB color 442 * identifier as defined in X11's rgb.txt file. 443 */ 444 for (i = 0, idx = 1; i < colors; i++) { 445 len = 0; 446 447 while (grub_read(&c, 1) && c != '"') 448 ; 449 450 grub_read(&c, 1); /* char */ 451 base = c; 452 grub_read(buf, 4); /* \t c # */ 453 454 while (grub_read(&c, 1) && c != '"') { 455 if (len < sizeof(buf)) 456 buf[len++] = c; 457 } 458 459 /* 460 * The RGB hex digits should be six characters in length. 461 * 462 * If the color field contains anything other than six 463 * characters, such as "None" to denote a transparent color, 464 * ignore it. 465 */ 466 if (len == 6) { 467 int r = ((hex(buf[0]) << 4) | hex(buf[1])) >> 2; 468 int g = ((hex(buf[2]) << 4) | hex(buf[3])) >> 2; 469 int b = ((hex(buf[4]) << 4) | hex(buf[5])) >> 2; 470 471 if (idx > 14) { 472 errnum = ERR_TOOMANYCOLORS; 473 grub_close(); 474 return 0; 475 } 476 477 pal[idx] = base; 478 splash_palette[idx++] = 479 ((r & PALETTE_COLORMASK) << PALETTE_REDSHIFT) | 480 ((g & PALETTE_COLORMASK) << PALETTE_GREENSHIFT) | 481 (b & PALETTE_COLORMASK); 482 } 483 } 484 485 colors = idx - 1; /* actual number of colors used in XPM image */ 486 x = y = len = 0; 487 488 /* clear (zero out) all four planes of the framebuffer */ 489 for (i = 0; i < SCREENBYTES; i++) 490 s1[i] = s2[i] = s4[i] = s8[i] = 0; 491 492 /* parse the XPM data */ 493 while (y < height) { 494 /* exit on EOF, otherwise skip characters until an initial '"' */ 495 while (1) { 496 if (!grub_read(&c, 1)) { 497 errnum = ERR_CORRUPTXPM; 498 grub_close(); 499 return 0; 500 } 501 if (c == '"') 502 break; 503 } 504 505 /* read characters until we hit an EOF or a terminating '"' */ 506 while (grub_read(&c, 1) && c != '"') { 507 int pixel = 0; 508 509 /* 510 * Look up the specified pixel color in the palette; the 511 * pixel will not be drawn if its color cannot be found or 512 * if no colors were specified in the XPM image itself. 513 */ 514 for (i = 1; i <= colors; i++) 515 if (pal[i] == c) { 516 pixel = i; 517 break; 518 } 519 520 /* 521 * A bit is set in each of the "planes" of the frame buffer to 522 * denote a pixel drawn in each color of the palette. 523 * 524 * The planes are a binary representation of the palette, so a 525 * pixel in color "1" of the palette would be denoted by setting a 526 * bit in plane "s1"; a pixel in color "15" of the palette would 527 * set the same bit in each of the four planes. 528 * 529 * Pixels are represented by set bits in a byte, in the order 530 * left-to-right (e.g. pixel 0 is 0x80, pixel 7 is 1.) 531 */ 532 if (pixel != 0) { 533 mask = 0x80 >> (x & 7); 534 535 if (pixel & 1) 536 s1[len + (x >> 3)] |= mask; 537 if (pixel & 2) 538 s2[len + (x >> 3)] |= mask; 539 if (pixel & 4) 540 s4[len + (x >> 3)] |= mask; 541 if (pixel & 8) 542 s8[len + (x >> 3)] |= mask; 543 } 544 545 /* 546 * Increment "x"; if we hit pixel HPIXELS, wrap to the start of the 547 * next horizontal line if we haven't yet reached the bottom of 548 * the screen. 549 */ 550 if (++x >= HPIXELS) { 551 x = 0; 552 553 if (y++ < VPIXELS) 554 len += ROWBYTES; 555 else 556 break; 557 } 558 } 559 } 560 561 grub_close(); 562 563 return 1; 564 } 565 566 /* Convert a character which is a hex digit to the appropriate integer */ 567 int hex(int v) 568 { 569 if (v >= 'A' && v <= 'F') 570 return (v - 'A' + 10); 571 if (v >= 'a' && v <= 'f') 572 return (v - 'a' + 10); 573 return (v - '0'); 574 } 575 576 577 /* move the graphics cursor location to col, row */ 578 static void graphics_setxy(int col, int row) { 579 if (col >= x0 && col < x1) { 580 fontx = col; 581 cursorX = col << 3; 582 } 583 if (row >= y0 && row < y1) { 584 fonty = row; 585 cursorY = row << 4; 586 } 587 } 588 589 /* scroll the screen */ 590 static void graphics_scroll() { 591 int i, j; 592 593 /* we don't want to scroll recursively... that would be bad */ 594 if (no_scroll) 595 return; 596 no_scroll = 1; 597 598 /* move everything up a line */ 599 for (j = y0 + 1; j < y1; j++) { 600 graphics_gotoxy(x0, j - 1); 601 for (i = x0; i < x1; i++) { 602 graphics_putchar(text[j * ROWBYTES + i]); 603 } 604 } 605 606 /* last line should be blank */ 607 graphics_gotoxy(x0, y1 - 1); 608 for (i = x0; i < x1; i++) 609 graphics_putchar(' '); 610 graphics_setxy(x0, y1 - 1); 611 612 no_scroll = 0; 613 } 614 615 void graphics_cursor(int set) { 616 unsigned char *pat, *mem, *ptr, chr[16 << 2]; 617 int i, ch, invert, offset; 618 619 if (set && no_scroll) 620 return; 621 622 offset = cursorY * ROWBYTES + fontx; 623 ch = text[fonty * ROWBYTES + fontx] & 0xff; 624 invert = (text[fonty * ROWBYTES + fontx] & 0xff00) != 0; 625 pat = font8x16 + (ch << 4); 626 627 mem = (unsigned char*)VIDEOMEM + offset; 628 629 if (!set) { 630 for (i = 0; i < 16; i++) { 631 unsigned char mask = pat[i]; 632 633 if (!invert) { 634 chr[i ] = ((unsigned char*)VSHADOW1)[offset]; 635 chr[16 + i] = ((unsigned char*)VSHADOW2)[offset]; 636 chr[32 + i] = ((unsigned char*)VSHADOW4)[offset]; 637 chr[48 + i] = ((unsigned char*)VSHADOW8)[offset]; 638 639 /* FIXME: if (shade) */ 640 if (1) { 641 if (ch == DISP_VERT || ch == DISP_LL || 642 ch == DISP_UR || ch == DISP_LR) { 643 unsigned char pmask = ~(pat[i] >> 1); 644 645 chr[i ] &= pmask; 646 chr[16 + i] &= pmask; 647 chr[32 + i] &= pmask; 648 chr[48 + i] &= pmask; 649 } 650 if (i > 0 && ch != DISP_VERT) { 651 unsigned char pmask = ~(pat[i - 1] >> 1); 652 653 chr[i ] &= pmask; 654 chr[16 + i] &= pmask; 655 chr[32 + i] &= pmask; 656 chr[48 + i] &= pmask; 657 if (ch == DISP_HORIZ || ch == DISP_UR || ch == DISP_LR) { 658 pmask = ~pat[i - 1]; 659 660 chr[i ] &= pmask; 661 chr[16 + i] &= pmask; 662 chr[32 + i] &= pmask; 663 chr[48 + i] &= pmask; 664 } 665 } 666 } 667 chr[i ] |= mask; 668 chr[16 + i] |= mask; 669 chr[32 + i] |= mask; 670 chr[48 + i] |= mask; 671 672 offset += ROWBYTES; 673 } 674 else { 675 chr[i ] = mask; 676 chr[16 + i] = mask; 677 chr[32 + i] = mask; 678 chr[48 + i] = mask; 679 } 680 } 681 } 682 else { 683 MapMask(15); 684 ptr = mem; 685 for (i = 0; i < 16; i++, ptr += ROWBYTES) { 686 cursorBuf[i] = pat[i]; 687 *ptr = ~pat[i]; 688 } 689 return; 690 } 691 692 offset = 0; 693 for (i = 1; i < 16; i <<= 1, offset += 16) { 694 int j; 695 696 MapMask(i); 697 ptr = mem; 698 for (j = 0; j < 16; j++, ptr += ROWBYTES) 699 *ptr = chr[j + offset]; 700 } 701 702 MapMask(15); 703 } 704 705 #endif /* SUPPORT_GRAPHICS */ 706