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 int saved_videomode; 34 unsigned char *font8x16; 35 36 int graphics_inited = 0; 37 static char splashimage[64]; 38 39 #define VSHADOW VSHADOW1 40 unsigned char VSHADOW1[38400]; 41 unsigned char VSHADOW2[38400]; 42 unsigned char VSHADOW4[38400]; 43 unsigned char VSHADOW8[38400]; 44 45 /* constants to define the viewable area */ 46 const int x0 = 0; 47 const int x1 = 80; 48 const int y0 = 0; 49 const int y1 = 30; 50 51 /* text buffer has to be kept around so that we can write things as we 52 * scroll and the like */ 53 unsigned short text[80 * 30]; 54 55 /* why do these have to be kept here? */ 56 int foreground = (63 << 16) | (63 << 8) | (63), background = 0, border = 0; 57 58 /* current position */ 59 static int fontx = 0; 60 static int fonty = 0; 61 62 /* global state so that we don't try to recursively scroll or cursor */ 63 static int no_scroll = 0; 64 65 /* color state */ 66 static int graphics_standard_color = A_NORMAL; 67 static int graphics_normal_color = A_NORMAL; 68 static int graphics_highlight_color = A_REVERSE; 69 static int graphics_current_color = A_NORMAL; 70 static color_state graphics_color_state = COLOR_STATE_STANDARD; 71 72 73 /* graphics local functions */ 74 static void graphics_setxy(int col, int row); 75 static void graphics_scroll(); 76 77 /* FIXME: where do these really belong? */ 78 static inline void outb(unsigned short port, unsigned char val) 79 { 80 __asm __volatile ("outb %0,%1"::"a" (val), "d" (port)); 81 } 82 83 static void MapMask(int value) { 84 outb(0x3c4, 2); 85 outb(0x3c5, value); 86 } 87 88 /* bit mask register */ 89 static void BitMask(int value) { 90 outb(0x3ce, 8); 91 outb(0x3cf, value); 92 } 93 94 95 96 /* Set the splash image */ 97 void graphics_set_splash(char *splashfile) { 98 grub_strcpy(splashimage, splashfile); 99 } 100 101 /* Get the current splash image */ 102 char *graphics_get_splash(void) { 103 return splashimage; 104 } 105 106 /* Initialize a vga16 graphics display with the palette based off of 107 * the image in splashimage. If the image doesn't exist, leave graphics 108 * mode. */ 109 int graphics_init() 110 { 111 if (!graphics_inited) { 112 saved_videomode = set_videomode(0x12); 113 } 114 115 if (!read_image(splashimage)) { 116 set_videomode(saved_videomode); 117 grub_printf("failed to read image\n"); 118 return 0; 119 } 120 121 font8x16 = (unsigned char*)graphics_get_font(); 122 123 graphics_inited = 1; 124 125 /* make sure that the highlight color is set correctly */ 126 graphics_highlight_color = ((graphics_normal_color >> 4) | 127 ((graphics_normal_color & 0xf) << 4)); 128 129 return 1; 130 } 131 132 /* Leave graphics mode */ 133 void graphics_end(void) 134 { 135 if (graphics_inited) { 136 set_videomode(saved_videomode); 137 graphics_inited = 0; 138 } 139 } 140 141 /* Print ch on the screen. Handle any needed scrolling or the like */ 142 void graphics_putchar(int ch) { 143 ch &= 0xff; 144 145 graphics_cursor(0); 146 147 if (ch == '\n') { 148 if (fonty + 1 < y1) 149 graphics_setxy(fontx, fonty + 1); 150 else 151 graphics_scroll(); 152 graphics_cursor(1); 153 return; 154 } else if (ch == '\r') { 155 graphics_setxy(x0, fonty); 156 graphics_cursor(1); 157 return; 158 } 159 160 graphics_cursor(0); 161 162 text[fonty * 80 + fontx] = ch; 163 text[fonty * 80 + fontx] &= 0x00ff; 164 if (graphics_current_color & 0xf0) 165 text[fonty * 80 + fontx] |= 0x100; 166 167 graphics_cursor(0); 168 169 if ((fontx + 1) >= x1) { 170 graphics_setxy(x0, fonty); 171 if (fonty + 1 < y1) 172 graphics_setxy(x0, fonty + 1); 173 else 174 graphics_scroll(); 175 } else { 176 graphics_setxy(fontx + 1, fonty); 177 } 178 179 graphics_cursor(1); 180 } 181 182 /* get the current location of the cursor */ 183 int graphics_getxy(void) { 184 return (fontx << 8) | fonty; 185 } 186 187 void graphics_gotoxy(int x, int y) { 188 graphics_cursor(0); 189 190 graphics_setxy(x, y); 191 192 graphics_cursor(1); 193 } 194 195 void graphics_cls(void) { 196 int i; 197 unsigned char *mem, *s1, *s2, *s4, *s8; 198 199 graphics_cursor(0); 200 graphics_gotoxy(x0, y0); 201 202 mem = (unsigned char*)VIDEOMEM; 203 s1 = (unsigned char*)VSHADOW1; 204 s2 = (unsigned char*)VSHADOW2; 205 s4 = (unsigned char*)VSHADOW4; 206 s8 = (unsigned char*)VSHADOW8; 207 208 for (i = 0; i < 80 * 30; i++) 209 text[i] = ' '; 210 graphics_cursor(1); 211 212 BitMask(0xff); 213 214 /* plano 1 */ 215 MapMask(1); 216 grub_memcpy(mem, s1, 38400); 217 218 /* plano 2 */ 219 MapMask(2); 220 grub_memcpy(mem, s2, 38400); 221 222 /* plano 3 */ 223 MapMask(4); 224 grub_memcpy(mem, s4, 38400); 225 226 /* plano 4 */ 227 MapMask(8); 228 grub_memcpy(mem, s8, 38400); 229 230 MapMask(15); 231 232 } 233 234 void graphics_setcolorstate (color_state state) { 235 switch (state) { 236 case COLOR_STATE_STANDARD: 237 graphics_current_color = graphics_standard_color; 238 break; 239 case COLOR_STATE_NORMAL: 240 graphics_current_color = graphics_normal_color; 241 break; 242 case COLOR_STATE_HIGHLIGHT: 243 graphics_current_color = graphics_highlight_color; 244 break; 245 default: 246 graphics_current_color = graphics_standard_color; 247 break; 248 } 249 250 graphics_color_state = state; 251 } 252 253 void graphics_setcolor (int normal_color, int highlight_color) { 254 graphics_normal_color = normal_color; 255 graphics_highlight_color = highlight_color; 256 257 graphics_setcolorstate (graphics_color_state); 258 } 259 260 int graphics_setcursor (int on) { 261 /* FIXME: we don't have a cursor in graphics */ 262 return 1; 263 } 264 265 /* Read in the splashscreen image and set the palette up appropriately. 266 * Format of splashscreen is an xpm (can be gzipped) with 16 colors and 267 * 640x480. */ 268 int read_image(char *s) 269 { 270 char buf[32], pal[16]; 271 unsigned char c, base, mask, *s1, *s2, *s4, *s8; 272 unsigned i, len, idx, colors, x, y, width, height; 273 274 if (!grub_open(s)) 275 return 0; 276 277 /* read header */ 278 if (!grub_read((char*)&buf, 10) || grub_memcmp(buf, "/* XPM */\n", 10)) { 279 grub_close(); 280 return 0; 281 } 282 283 /* parse info */ 284 while (grub_read(&c, 1)) { 285 if (c == '"') 286 break; 287 } 288 289 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) 290 ; 291 292 i = 0; 293 width = c - '0'; 294 while (grub_read(&c, 1)) { 295 if (c >= '0' && c <= '9') 296 width = width * 10 + c - '0'; 297 else 298 break; 299 } 300 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) 301 ; 302 303 height = c - '0'; 304 while (grub_read(&c, 1)) { 305 if (c >= '0' && c <= '9') 306 height = height * 10 + c - '0'; 307 else 308 break; 309 } 310 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) 311 ; 312 313 colors = c - '0'; 314 while (grub_read(&c, 1)) { 315 if (c >= '0' && c <= '9') 316 colors = colors * 10 + c - '0'; 317 else 318 break; 319 } 320 321 base = 0; 322 while (grub_read(&c, 1) && c != '"') 323 ; 324 325 /* palette */ 326 for (i = 0, idx = 1; i < colors; i++) { 327 len = 0; 328 329 while (grub_read(&c, 1) && c != '"') 330 ; 331 grub_read(&c, 1); /* char */ 332 base = c; 333 grub_read(buf, 4); /* \t c # */ 334 335 while (grub_read(&c, 1) && c != '"') { 336 if (len < sizeof(buf)) 337 buf[len++] = c; 338 } 339 340 if (len == 6 && idx < 15) { 341 int r = ((hex(buf[0]) << 4) | hex(buf[1])) >> 2; 342 int g = ((hex(buf[2]) << 4) | hex(buf[3])) >> 2; 343 int b = ((hex(buf[4]) << 4) | hex(buf[5])) >> 2; 344 345 pal[idx] = base; 346 graphics_set_palette(idx, r, g, b); 347 ++idx; 348 } 349 } 350 351 x = y = len = 0; 352 353 s1 = (unsigned char*)VSHADOW1; 354 s2 = (unsigned char*)VSHADOW2; 355 s4 = (unsigned char*)VSHADOW4; 356 s8 = (unsigned char*)VSHADOW8; 357 358 for (i = 0; i < 38400; i++) 359 s1[i] = s2[i] = s4[i] = s8[i] = 0; 360 361 /* parse xpm data */ 362 while (y < height) { 363 while (1) { 364 if (!grub_read(&c, 1)) { 365 grub_close(); 366 return 0; 367 } 368 if (c == '"') 369 break; 370 } 371 372 while (grub_read(&c, 1) && c != '"') { 373 for (i = 1; i < 15; i++) 374 if (pal[i] == c) { 375 c = i; 376 break; 377 } 378 379 mask = 0x80 >> (x & 7); 380 if (c & 1) 381 s1[len + (x >> 3)] |= mask; 382 if (c & 2) 383 s2[len + (x >> 3)] |= mask; 384 if (c & 4) 385 s4[len + (x >> 3)] |= mask; 386 if (c & 8) 387 s8[len + (x >> 3)] |= mask; 388 389 if (++x >= 640) { 390 x = 0; 391 392 if (y < 480) 393 len += 80; 394 ++y; 395 } 396 } 397 } 398 399 grub_close(); 400 401 graphics_set_palette(0, (background >> 16), (background >> 8) & 63, 402 background & 63); 403 graphics_set_palette(15, (foreground >> 16), (foreground >> 8) & 63, 404 foreground & 63); 405 graphics_set_palette(0x11, (border >> 16), (border >> 8) & 63, 406 border & 63); 407 408 return 1; 409 } 410 411 412 /* Convert a character which is a hex digit to the appropriate integer */ 413 int hex(int v) 414 { 415 if (v >= 'A' && v <= 'F') 416 return (v - 'A' + 10); 417 if (v >= 'a' && v <= 'f') 418 return (v - 'a' + 10); 419 return (v - '0'); 420 } 421 422 423 /* move the graphics cursor location to col, row */ 424 static void graphics_setxy(int col, int row) { 425 if (col >= x0 && col < x1) { 426 fontx = col; 427 cursorX = col << 3; 428 } 429 if (row >= y0 && row < y1) { 430 fonty = row; 431 cursorY = row << 4; 432 } 433 } 434 435 /* scroll the screen */ 436 static void graphics_scroll() { 437 int i, j; 438 439 /* we don't want to scroll recursively... that would be bad */ 440 if (no_scroll) 441 return; 442 no_scroll = 1; 443 444 /* move everything up a line */ 445 for (j = y0 + 1; j < y1; j++) { 446 graphics_gotoxy(x0, j - 1); 447 for (i = x0; i < x1; i++) { 448 graphics_putchar(text[j * 80 + i]); 449 } 450 } 451 452 /* last line should be blank */ 453 graphics_gotoxy(x0, y1 - 1); 454 for (i = x0; i < x1; i++) 455 graphics_putchar(' '); 456 graphics_setxy(x0, y1 - 1); 457 458 no_scroll = 0; 459 } 460 461 462 void graphics_cursor(int set) { 463 unsigned char *pat, *mem, *ptr, chr[16 << 2]; 464 int i, ch, invert, offset; 465 466 if (set && no_scroll) 467 return; 468 469 offset = cursorY * 80 + fontx; 470 ch = text[fonty * 80 + fontx] & 0xff; 471 invert = (text[fonty * 80 + fontx] & 0xff00) != 0; 472 pat = font8x16 + (ch << 4); 473 474 mem = (unsigned char*)VIDEOMEM + offset; 475 476 if (!set) { 477 for (i = 0; i < 16; i++) { 478 unsigned char mask = pat[i]; 479 480 if (!invert) { 481 chr[i ] = ((unsigned char*)VSHADOW1)[offset]; 482 chr[16 + i] = ((unsigned char*)VSHADOW2)[offset]; 483 chr[32 + i] = ((unsigned char*)VSHADOW4)[offset]; 484 chr[48 + i] = ((unsigned char*)VSHADOW8)[offset]; 485 486 /* FIXME: if (shade) */ 487 if (1) { 488 if (ch == DISP_VERT || ch == DISP_LL || 489 ch == DISP_UR || ch == DISP_LR) { 490 unsigned char pmask = ~(pat[i] >> 1); 491 492 chr[i ] &= pmask; 493 chr[16 + i] &= pmask; 494 chr[32 + i] &= pmask; 495 chr[48 + i] &= pmask; 496 } 497 if (i > 0 && ch != DISP_VERT) { 498 unsigned char pmask = ~(pat[i - 1] >> 1); 499 500 chr[i ] &= pmask; 501 chr[16 + i] &= pmask; 502 chr[32 + i] &= pmask; 503 chr[48 + i] &= pmask; 504 if (ch == DISP_HORIZ || ch == DISP_UR || ch == DISP_LR) { 505 pmask = ~pat[i - 1]; 506 507 chr[i ] &= pmask; 508 chr[16 + i] &= pmask; 509 chr[32 + i] &= pmask; 510 chr[48 + i] &= pmask; 511 } 512 } 513 } 514 chr[i ] |= mask; 515 chr[16 + i] |= mask; 516 chr[32 + i] |= mask; 517 chr[48 + i] |= mask; 518 519 offset += 80; 520 } 521 else { 522 chr[i ] = mask; 523 chr[16 + i] = mask; 524 chr[32 + i] = mask; 525 chr[48 + i] = mask; 526 } 527 } 528 } 529 else { 530 MapMask(15); 531 ptr = mem; 532 for (i = 0; i < 16; i++, ptr += 80) { 533 cursorBuf[i] = pat[i]; 534 *ptr = ~pat[i]; 535 } 536 return; 537 } 538 539 offset = 0; 540 for (i = 1; i < 16; i <<= 1, offset += 16) { 541 int j; 542 543 MapMask(i); 544 ptr = mem; 545 for (j = 0; j < 16; j++, ptr += 80) 546 *ptr = chr[j + offset]; 547 } 548 549 MapMask(15); 550 } 551 552 #endif /* SUPPORT_GRAPHICS */ 553