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