Merge branch 'master' of git://github.com/illumos/illumos-gate
[unleashed.git] / usr / src / grub / grub-0.97 / stage2 / graphics.c
blobbc8d48a7729cf16f878eceead4f3db5ccd0698bf
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.
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.
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.
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.
27 #ifdef SUPPORT_GRAPHICS
29 #include <term.h>
30 #include <shared.h>
31 #include <graphics.h>
33 #ifdef OVERLAY_LOGO
34 #include <logo.xbm>
35 #endif /* OVERLAY_LOGO */
37 int saved_videomode;
38 unsigned char *font8x16;
40 int graphics_inited = 0;
42 #define PALETTE_REDSHIFT 16
43 #define PALETTE_GREENSHIFT 8
44 #define PALETTE_COLORMASK 63
46 #define PALETTE_NCOLORS 16
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)
53 static char splashimage[64];
54 static int splash_palette[PALETTE_NCOLORS];
56 #define HPIXELS 640
57 #define VPIXELS 480
58 #define HPIXELSPERBYTE 8
60 #define ROWBYTES (HPIXELS / HPIXELSPERBYTE)
61 #define SCREENBYTES (ROWBYTES * VPIXELS)
63 #define VSHADOW VSHADOW1
64 unsigned char VSHADOW1[SCREENBYTES];
65 unsigned char VSHADOW2[SCREENBYTES];
66 unsigned char VSHADOW4[SCREENBYTES];
67 unsigned char VSHADOW8[SCREENBYTES];
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;
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;
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];
84 /* why do these have to be kept here? */
85 int foreground = (63 << 16) | (63 << 8) | (63), background = 0, border = 0;
88 /* current position */
89 static int fontx = 0;
90 static int fonty = 0;
92 /* global state so that we don't try to recursively scroll or cursor */
93 static int no_scroll = 0;
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;
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 *);
108 #ifdef OVERLAY_LOGO
109 static void draw_xbmlogo(void);
110 #endif /* OVERLAY_LOGO */
112 /* FIXME: where do these really belong? */
113 static inline void outb(unsigned short port, unsigned char val)
115 __asm __volatile ("outb %0,%1"::"a" (val), "d" (port));
118 static void MapMask(int value) {
119 outb(0x3c4, 2);
120 outb(0x3c5, value);
123 /* bit mask register */
124 static void BitMask(int value) {
125 outb(0x3ce, 8);
126 outb(0x3cf, value);
130 /* Set the splash image */
131 void graphics_set_splash(char *splashfile) {
132 grub_strcpy(splashimage, splashfile);
135 /* Get the current splash image */
136 char *graphics_get_splash(void) {
137 return splashimage;
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()
145 int image_read, index, color;
147 if (!graphics_inited) {
148 saved_videomode = set_videomode(0x12);
151 font8x16 = (unsigned char*)graphics_get_font();
153 image_read = read_image(splashimage);
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.
159 * If the splashimage was read successfully, program the palette with
160 * its new colors; if not, set them to the background color.
163 graphics_set_palette(0, PALETTE_RED(background), PALETTE_GREEN(background),
164 PALETTE_BLUE(background));
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));
172 graphics_set_palette(15, PALETTE_RED(foreground),
173 PALETTE_GREEN(foreground), PALETTE_BLUE(foreground));
175 graphics_set_palette(0x11, PALETTE_RED(border), PALETTE_GREEN(border),
176 PALETTE_BLUE(border));
178 #ifdef OVERLAY_LOGO
179 draw_xbmlogo();
180 #endif /* OVERLAY_LOGO */
182 graphics_inited = 1;
184 /* make sure that the highlight color is set correctly */
185 graphics_highlight_color = ((graphics_normal_color >> 4) |
186 ((graphics_normal_color & 0xf) << 4));
188 return 1;
191 /* Leave graphics mode */
192 void graphics_end(void)
194 if (graphics_inited) {
195 set_videomode(saved_videomode);
196 graphics_inited = 0;
200 /* Print ch on the screen. Handle any needed scrolling or the like */
201 void graphics_putchar(int ch) {
202 ch &= 0xff;
204 graphics_cursor(0);
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;
219 graphics_cursor(0);
221 text[fonty * ROWBYTES + fontx] = ch;
222 text[fonty * ROWBYTES + fontx] &= 0x00ff;
223 if (graphics_current_color & 0xf0)
224 text[fonty * ROWBYTES + fontx] |= 0x100;
226 graphics_cursor(0);
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);
238 graphics_cursor(1);
241 /* get the current location of the cursor */
242 int graphics_getxy(void) {
243 return (fontx << 8) | fonty;
246 void graphics_gotoxy(int x, int y) {
247 graphics_cursor(0);
249 graphics_setxy(x, y);
251 graphics_cursor(1);
254 void graphics_cls(void) {
255 int i;
256 unsigned char *mem;
258 graphics_cursor(0);
259 graphics_gotoxy(x0, y0);
261 mem = (unsigned char*)VIDEOMEM;
263 for (i = 0; i < ROWBYTES * 30; i++)
264 text[i] = ' ';
265 graphics_cursor(1);
267 BitMask(0xff);
269 /* plane 1 */
270 MapMask(1);
271 grub_memcpy(mem, s1, SCREENBYTES);
273 /* plane 2 */
274 MapMask(2);
275 grub_memcpy(mem, s2, SCREENBYTES);
277 /* plane 3 */
278 MapMask(4);
279 grub_memcpy(mem, s4, SCREENBYTES);
281 /* plane 4 */
282 MapMask(8);
283 grub_memcpy(mem, s8, SCREENBYTES);
285 MapMask(15);
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;
304 graphics_color_state = state;
307 void graphics_setcolor (int normal_color, int highlight_color) {
308 graphics_normal_color = normal_color;
309 graphics_highlight_color = highlight_color;
311 graphics_setcolorstate (graphics_color_state);
314 int graphics_setcursor (int on) {
315 /* FIXME: we don't have a cursor in graphics */
316 return 1;
319 #ifdef OVERLAY_LOGO
320 static void draw_xbmlogo(void)
322 unsigned char mask;
323 unsigned xbm_index = 0, xbm_incr;
324 unsigned screenx, logox, logoy, fb_offset, fb_index;
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.
331 fb_offset = ((VPIXELS - 1) - logo_height - 2) * ROWBYTES;
332 xbm_incr = (logo_width / 8) + 1;
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);
341 * If a bit is clear in the bitmap, draw it onto the
342 * framebuffer in the default foreground color.
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;
354 xbm_index += xbm_incr;
355 fb_offset += ROWBYTES;
358 #endif /* OVERLAY_LOGO */
361 * Read in the splashscreen image and set the palette up appropriately.
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.
366 static int read_image(char *s)
368 char buf[32], pal[16];
369 unsigned char c, base, mask;
370 unsigned i, len, idx, colors, x, y, width, height;
372 if (!grub_open(s))
373 return 0;
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;
382 /* skip characters until we reach an initial '"' */
383 while (grub_read(&c, 1)) {
384 if (c == '"')
385 break;
388 /* skip whitespace */
389 while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
393 * Format here should be four integers:
395 * Width Height NumberOfColors CharactersPerPixel
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;
406 /* skip whitespace to advance to next digit */
407 while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
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;
418 /* skip whitespace to advance to next digit */
419 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ;
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;
429 /* eat rest of line - assumes chars per pixel is one */
430 while (grub_read(&c, 1) && c != '"')
434 * Parse the XPM palette - the format is:
436 * identifier colorspace #RRGGBB
438 * The identifier is simply a single character; the colorspace identifier
439 * is skipped as it's assumed to be "c" denoting RGB color.
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.
444 for (i = 0, idx = 1; i < colors; i++) {
445 len = 0;
447 while (grub_read(&c, 1) && c != '"')
450 grub_read(&c, 1); /* char */
451 base = c;
452 grub_read(buf, 4); /* \t c # */
454 while (grub_read(&c, 1) && c != '"') {
455 if (len < sizeof(buf))
456 buf[len++] = c;
460 * The RGB hex digits should be six characters in length.
462 * If the color field contains anything other than six
463 * characters, such as "None" to denote a transparent color,
464 * ignore it.
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;
471 if (idx > 14) {
472 errnum = ERR_TOOMANYCOLORS;
473 grub_close();
474 return 0;
477 pal[idx] = base;
478 splash_palette[idx++] =
479 ((r & PALETTE_COLORMASK) << PALETTE_REDSHIFT) |
480 ((g & PALETTE_COLORMASK) << PALETTE_GREENSHIFT) |
481 (b & PALETTE_COLORMASK);
485 colors = idx - 1; /* actual number of colors used in XPM image */
486 x = y = len = 0;
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;
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;
501 if (c == '"')
502 break;
505 /* read characters until we hit an EOF or a terminating '"' */
506 while (grub_read(&c, 1) && c != '"') {
507 int pixel = 0;
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.
514 for (i = 1; i <= colors; i++)
515 if (pal[i] == c) {
516 pixel = i;
517 break;
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.
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.
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.)
532 if (pixel != 0) {
533 mask = 0x80 >> (x & 7);
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;
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.
550 if (++x >= HPIXELS) {
551 x = 0;
553 if (y++ < VPIXELS)
554 len += ROWBYTES;
555 else
556 break;
561 grub_close();
563 return 1;
566 /* Convert a character which is a hex digit to the appropriate integer */
567 int hex(int v)
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');
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;
583 if (row >= y0 && row < y1) {
584 fonty = row;
585 cursorY = row << 4;
589 /* scroll the screen */
590 static void graphics_scroll() {
591 int i, j;
593 /* we don't want to scroll recursively... that would be bad */
594 if (no_scroll)
595 return;
596 no_scroll = 1;
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]);
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);
612 no_scroll = 0;
615 void graphics_cursor(int set) {
616 unsigned char *pat, *mem, *ptr, chr[16 << 2];
617 int i, ch, invert, offset;
619 if (set && no_scroll)
620 return;
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);
627 mem = (unsigned char*)VIDEOMEM + offset;
629 if (!set) {
630 for (i = 0; i < 16; i++) {
631 unsigned char mask = pat[i];
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];
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);
645 chr[i ] &= pmask;
646 chr[16 + i] &= pmask;
647 chr[32 + i] &= pmask;
648 chr[48 + i] &= pmask;
650 if (i > 0 && ch != DISP_VERT) {
651 unsigned char pmask = ~(pat[i - 1] >> 1);
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];
660 chr[i ] &= pmask;
661 chr[16 + i] &= pmask;
662 chr[32 + i] &= pmask;
663 chr[48 + i] &= pmask;
667 chr[i ] |= mask;
668 chr[16 + i] |= mask;
669 chr[32 + i] |= mask;
670 chr[48 + i] |= mask;
672 offset += ROWBYTES;
674 else {
675 chr[i ] = mask;
676 chr[16 + i] = mask;
677 chr[32 + i] = mask;
678 chr[48 + i] = mask;
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];
689 return;
692 offset = 0;
693 for (i = 1; i < 16; i <<= 1, offset += 16) {
694 int j;
696 MapMask(i);
697 ptr = mem;
698 for (j = 0; j < 16; j++, ptr += ROWBYTES)
699 *ptr = chr[j + offset];
702 MapMask(15);
705 #endif /* SUPPORT_GRAPHICS */