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>
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
35 #endif /* OVERLAY_LOGO */
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) & \
51 #define PALETTE_BLUE(entry) ((entry) & PALETTE_COLORMASK)
53 static char splashimage
[64];
54 static int splash_palette
[PALETTE_NCOLORS
];
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 */
76 const int x1
= ROWBYTES
;
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 */
92 /* global state so that we don't try to recursively scroll or cursor */
93 static int no_scroll
= 0;
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 *);
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
) {
123 /* bit mask register */
124 static void BitMask(int 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) {
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
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
));
180 #endif /* OVERLAY_LOGO */
184 /* make sure that the highlight color is set correctly */
185 graphics_highlight_color
= ((graphics_normal_color
>> 4) |
186 ((graphics_normal_color
& 0xf) << 4));
191 /* Leave graphics mode */
192 void graphics_end(void)
194 if (graphics_inited
) {
195 set_videomode(saved_videomode
);
200 /* Print ch on the screen. Handle any needed scrolling or the like */
201 void graphics_putchar(int ch
) {
208 graphics_setxy(fontx
, fonty
+ 1);
213 } else if (ch
== '\r') {
214 graphics_setxy(x0
, fonty
);
221 text
[fonty
* ROWBYTES
+ fontx
] = ch
;
222 text
[fonty
* ROWBYTES
+ fontx
] &= 0x00ff;
223 if (graphics_current_color
& 0xf0)
224 text
[fonty
* ROWBYTES
+ fontx
] |= 0x100;
228 if ((fontx
+ 1) >= x1
) {
229 graphics_setxy(x0
, fonty
);
231 graphics_setxy(x0
, fonty
+ 1);
235 graphics_setxy(fontx
+ 1, fonty
);
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
) {
249 graphics_setxy(x
, y
);
254 void graphics_cls(void) {
259 graphics_gotoxy(x0
, y0
);
261 mem
= (unsigned char*)VIDEOMEM
;
263 for (i
= 0; i
< ROWBYTES
* 30; i
++)
271 grub_memcpy(mem
, s1
, SCREENBYTES
);
275 grub_memcpy(mem
, s2
, SCREENBYTES
);
279 grub_memcpy(mem
, s4
, SCREENBYTES
);
283 grub_memcpy(mem
, s8
, SCREENBYTES
);
288 void graphics_setcolorstate (color_state state
) {
290 case COLOR_STATE_STANDARD
:
291 graphics_current_color
= graphics_standard_color
;
293 case COLOR_STATE_NORMAL
:
294 graphics_current_color
= graphics_normal_color
;
296 case COLOR_STATE_HIGHLIGHT
:
297 graphics_current_color
= graphics_highlight_color
;
300 graphics_current_color
= graphics_standard_color
;
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 */
320 static void draw_xbmlogo(void)
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
;
375 /* read XPM header - must match memcmp string PRECISELY. */
376 if (!grub_read((char*)&buf
, 10) || grub_memcmp(buf
, "/* XPM */\n", 10)) {
382 /* skip characters until we reach an initial '"' */
383 while (grub_read(&c
, 1)) {
388 /* skip whitespace */
389 while (grub_read(&c
, 1) && (c
== ' ' || c
== '\t'))
393 * Format here should be four integers:
395 * Width Height NumberOfColors CharactersPerPixel
399 while (grub_read(&c
, 1)) {
400 if (c
>= '0' && c
<= '9')
401 width
= width
* 10 + c
- '0';
406 /* skip whitespace to advance to next digit */
407 while (grub_read(&c
, 1) && (c
== ' ' || c
== '\t'))
411 while (grub_read(&c
, 1)) {
412 if (c
>= '0' && c
<= '9')
413 height
= height
* 10 + c
- '0';
418 /* skip whitespace to advance to next digit */
419 while (grub_read(&c
, 1) && (c
== ' ' || c
== '\t')) ;
422 while (grub_read(&c
, 1)) {
423 if (c
>= '0' && c
<= '9')
424 colors
= colors
* 10 + c
- '0';
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
++) {
447 while (grub_read(&c
, 1) && c
!= '"')
450 grub_read(&c
, 1); /* char */
452 grub_read(buf
, 4); /* \t c # */
454 while (grub_read(&c
, 1) && c
!= '"') {
455 if (len
< sizeof(buf
))
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,
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;
472 errnum
= ERR_TOOMANYCOLORS
;
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 */
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 */
494 /* exit on EOF, otherwise skip characters until an initial '"' */
496 if (!grub_read(&c
, 1)) {
497 errnum
= ERR_CORRUPTXPM
;
505 /* read characters until we hit an EOF or a terminating '"' */
506 while (grub_read(&c
, 1) && c
!= '"') {
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
++)
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.)
533 mask
= 0x80 >> (x
& 7);
536 s1
[len
+ (x
>> 3)] |= mask
;
538 s2
[len
+ (x
>> 3)] |= mask
;
540 s4
[len
+ (x
>> 3)] |= mask
;
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
550 if (++x
>= HPIXELS
) {
566 /* Convert a character which is a hex digit to the appropriate integer */
569 if (v
>= 'A' && v
<= 'F')
570 return (v
- 'A' + 10);
571 if (v
>= 'a' && v
<= 'f')
572 return (v
- 'a' + 10);
577 /* move the graphics cursor location to col, row */
578 static void graphics_setxy(int col
, int row
) {
579 if (col
>= x0
&& col
< x1
) {
583 if (row
>= y0
&& row
< y1
) {
589 /* scroll the screen */
590 static void graphics_scroll() {
593 /* we don't want to scroll recursively... that would be bad */
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);
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
)
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
;
630 for (i
= 0; i
< 16; i
++) {
631 unsigned char mask
= pat
[i
];
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) */
641 if (ch
== DISP_VERT
|| ch
== DISP_LL
||
642 ch
== DISP_UR
|| ch
== DISP_LR
) {
643 unsigned char pmask
= ~(pat
[i
] >> 1);
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);
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
) {
661 chr
[16 + i
] &= pmask
;
662 chr
[32 + i
] &= pmask
;
663 chr
[48 + i
] &= pmask
;
685 for (i
= 0; i
< 16; i
++, ptr
+= ROWBYTES
) {
686 cursorBuf
[i
] = pat
[i
];
693 for (i
= 1; i
< 16; i
<<= 1, offset
+= 16) {
698 for (j
= 0; j
< 16; j
++, ptr
+= ROWBYTES
)
699 *ptr
= chr
[j
+ offset
];
705 #endif /* SUPPORT_GRAPHICS */