xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/graphics.c (revision 9b4e3ac25d882519cad3fc11f0c53b07f4e60536)
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