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 #ifdef	OVERLAY_LOGO
34 #include <logo.xbm>
35 #endif	/* OVERLAY_LOGO */
36 
37 int saved_videomode;
38 unsigned char *font8x16;
39 
40 int graphics_inited = 0;
41 
42 #define	PALETTE_REDSHIFT	16
43 #define	PALETTE_GREENSHIFT	8
44 #define	PALETTE_COLORMASK	63
45 
46 #define	PALETTE_NCOLORS		16
47 
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)
52 
53 static char splashimage[64];
54 static int splash_palette[PALETTE_NCOLORS];
55 
56 #define	HPIXELS		640
57 #define	VPIXELS		480
58 #define	HPIXELSPERBYTE	8
59 
60 #define	ROWBYTES	(HPIXELS / HPIXELSPERBYTE)
61 #define	SCREENBYTES	(ROWBYTES * VPIXELS)
62 
63 #define VSHADOW VSHADOW1
64 unsigned char VSHADOW1[SCREENBYTES];
65 unsigned char VSHADOW2[SCREENBYTES];
66 unsigned char VSHADOW4[SCREENBYTES];
67 unsigned char VSHADOW8[SCREENBYTES];
68 
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;
73 
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;
79 
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];
83 
84 /* why do these have to be kept here? */
85 int foreground = (63 << 16) | (63 << 8) | (63), background = 0, border = 0;
86 
87 
88 /* current position */
89 static int fontx = 0;
90 static int fonty = 0;
91 
92 /* global state so that we don't try to recursively scroll or cursor */
93 static int no_scroll = 0;
94 
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;
101 
102 
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 *);
107 
108 #ifdef	OVERLAY_LOGO
109 static void draw_xbmlogo(void);
110 #endif	/* OVERLAY_LOGO */
111 
112 /* FIXME: where do these really belong? */
outb(unsigned short port,unsigned char val)113 static inline void outb(unsigned short port, unsigned char val)
114 {
115     __asm __volatile ("outb %0,%1"::"a" (val), "d" (port));
116 }
117 
MapMask(int value)118 static void MapMask(int value) {
119     outb(0x3c4, 2);
120     outb(0x3c5, value);
121 }
122 
123 /* bit mask register */
BitMask(int value)124 static void BitMask(int value) {
125     outb(0x3ce, 8);
126     outb(0x3cf, value);
127 }
128 
129 
130 /* Set the splash image */
graphics_set_splash(char * splashfile)131 void graphics_set_splash(char *splashfile) {
132     grub_strcpy(splashimage, splashfile);
133 }
134 
135 /* Get the current splash image */
graphics_get_splash(void)136 char *graphics_get_splash(void) {
137     return splashimage;
138 }
139 
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.  */
graphics_init()143 int graphics_init()
144 {
145     int image_read, index, color;
146 
147     if (!graphics_inited) {
148         saved_videomode = set_videomode(0x12);
149     }
150 
151     font8x16 = (unsigned char*)graphics_get_font();
152 
153     image_read = read_image(splashimage);
154 
155     /*
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.
158      *
159      * If the splashimage was read successfully, program the palette with
160      * its new colors; if not, set them to the background color.
161      */
162 
163     graphics_set_palette(0, PALETTE_RED(background), PALETTE_GREEN(background),
164 	PALETTE_BLUE(background));
165 
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));
170     }
171 
172     graphics_set_palette(15, PALETTE_RED(foreground),
173 	 PALETTE_GREEN(foreground), PALETTE_BLUE(foreground));
174 
175     graphics_set_palette(0x11, PALETTE_RED(border), PALETTE_GREEN(border),
176 	PALETTE_BLUE(border));
177 
178 #ifdef	OVERLAY_LOGO
179     draw_xbmlogo();
180 #endif	/* OVERLAY_LOGO */
181 
182     graphics_inited = 1;
183 
184     /* make sure that the highlight color is set correctly */
185     graphics_highlight_color = ((graphics_normal_color >> 4) |
186 				((graphics_normal_color & 0xf) << 4));
187 
188     return 1;
189 }
190 
191 /* Leave graphics mode */
graphics_end(void)192 void graphics_end(void)
193 {
194     if (graphics_inited) {
195         set_videomode(saved_videomode);
196         graphics_inited = 0;
197     }
198 }
199 
200 /* Print ch on the screen.  Handle any needed scrolling or the like */
graphics_putchar(int ch)201 void graphics_putchar(int ch) {
202     ch &= 0xff;
203 
204     graphics_cursor(0);
205 
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;
217     }
218 
219     graphics_cursor(0);
220 
221     text[fonty * ROWBYTES + fontx] = ch;
222     text[fonty * ROWBYTES + fontx] &= 0x00ff;
223     if (graphics_current_color & 0xf0)
224         text[fonty * ROWBYTES + fontx] |= 0x100;
225 
226     graphics_cursor(0);
227 
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);
236     }
237 
238     graphics_cursor(1);
239 }
240 
241 /* get the current location of the cursor */
graphics_getxy(void)242 int graphics_getxy(void) {
243     return (fontx << 8) | fonty;
244 }
245 
graphics_gotoxy(int x,int y)246 void graphics_gotoxy(int x, int y) {
247     graphics_cursor(0);
248 
249     graphics_setxy(x, y);
250 
251     graphics_cursor(1);
252 }
253 
graphics_cls(void)254 void graphics_cls(void) {
255     int i;
256     unsigned char *mem;
257 
258     graphics_cursor(0);
259     graphics_gotoxy(x0, y0);
260 
261     mem = (unsigned char*)VIDEOMEM;
262 
263     for (i = 0; i < ROWBYTES * 30; i++)
264         text[i] = ' ';
265     graphics_cursor(1);
266 
267     BitMask(0xff);
268 
269     /* plane 1 */
270     MapMask(1);
271     grub_memcpy(mem, s1, SCREENBYTES);
272 
273     /* plane 2 */
274     MapMask(2);
275     grub_memcpy(mem, s2, SCREENBYTES);
276 
277     /* plane 3 */
278     MapMask(4);
279     grub_memcpy(mem, s4, SCREENBYTES);
280 
281     /* plane 4 */
282     MapMask(8);
283     grub_memcpy(mem, s8, SCREENBYTES);
284 
285     MapMask(15);
286 }
287 
graphics_setcolorstate(color_state state)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;
302     }
303 
304     graphics_color_state = state;
305 }
306 
graphics_setcolor(int normal_color,int highlight_color)307 void graphics_setcolor (int normal_color, int highlight_color) {
308     graphics_normal_color = normal_color;
309     graphics_highlight_color = highlight_color;
310 
311     graphics_setcolorstate (graphics_color_state);
312 }
313 
graphics_setcursor(int on)314 int graphics_setcursor (int on) {
315     /* FIXME: we don't have a cursor in graphics */
316     return 1;
317 }
318 
319 #ifdef	OVERLAY_LOGO
draw_xbmlogo(void)320 static void draw_xbmlogo(void)
321 {
322     unsigned char mask;
323     unsigned xbm_index = 0, xbm_incr;
324     unsigned screenx, logox, logoy, fb_offset, fb_index;
325 
326     /*
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.
330      */
331     fb_offset = ((VPIXELS - 1) - logo_height - 2) * ROWBYTES;
332     xbm_incr = (logo_width / 8) + 1;
333 
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);
339 
340 	    /*
341 	     * If a bit is clear in the bitmap, draw it onto the
342 	     * framebuffer in the default foreground color.
343 	     */
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;
351 	    }
352 	}
353 
354 	xbm_index += xbm_incr;
355 	fb_offset += ROWBYTES;
356     }
357 }
358 #endif	/* OVERLAY_LOGO */
359 
360 /*
361  * Read in the splashscreen image and set the palette up appropriately.
362  *
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.
365  */
read_image(char * s)366 static int read_image(char *s)
367 {
368     char buf[32], pal[16];
369     unsigned char c, base, mask;
370     unsigned i, len, idx, colors, x, y, width, height;
371 
372     if (!grub_open(s))
373         return 0;
374 
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;
380     }
381 
382     /* skip characters until we reach an initial '"' */
383     while (grub_read(&c, 1)) {
384         if (c == '"')
385             break;
386     }
387 
388     /* skip whitespace */
389     while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
390 	;
391 
392     /*
393      * Format here should be four integers:
394      *
395      *     Width Height NumberOfColors CharactersPerPixel
396      */
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;
404     }
405 
406     /* skip whitespace to advance to next digit */
407     while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
408 	;
409 
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;
416     }
417 
418     /* skip whitespace to advance to next digit */
419     while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ;
420 
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;
427     }
428 
429     /* eat rest of line - assumes chars per pixel is one */
430     while (grub_read(&c, 1) && c != '"')
431         ;
432 
433     /*
434      * Parse the XPM palette - the format is:
435      *
436      *    identifier colorspace #RRGGBB
437      *
438      * The identifier is simply a single character; the colorspace identifier
439      * is skipped as it's assumed to be "c" denoting RGB color.
440      *
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.
443      */
444     for (i = 0, idx = 1; i < colors; i++) {
445         len = 0;
446 
447         while (grub_read(&c, 1) && c != '"')
448             ;
449 
450         grub_read(&c, 1);       /* char */
451         base = c;
452         grub_read(buf, 4);      /* \t c # */
453 
454         while (grub_read(&c, 1) && c != '"') {
455             if (len < sizeof(buf))
456                 buf[len++] = c;
457         }
458 
459 	/*
460 	 * The RGB hex digits should be six characters in length.
461 	 *
462 	 * If the color field contains anything other than six
463 	 * characters, such as "None" to denote a transparent color,
464 	 * ignore it.
465 	 */
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;
470 
471 	    if (idx > 14) {
472 		errnum = ERR_TOOMANYCOLORS;
473 		grub_close();
474 		return 0;
475 	    }
476 
477             pal[idx] = base;
478 	    splash_palette[idx++] =
479 		((r & PALETTE_COLORMASK) << PALETTE_REDSHIFT) |
480 		((g & PALETTE_COLORMASK) << PALETTE_GREENSHIFT) |
481 		(b & PALETTE_COLORMASK);
482         }
483     }
484 
485     colors = idx - 1;	/* actual number of colors used in XPM image */
486     x = y = len = 0;
487 
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;
491 
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;
500             }
501             if (c == '"')
502                 break;
503         }
504 
505 	/* read characters until we hit an EOF or a terminating '"' */
506         while (grub_read(&c, 1) && c != '"') {
507 	    int pixel = 0;
508 
509 	    /*
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.
513 	     */
514             for (i = 1; i <= colors; i++)
515                 if (pal[i] == c) {
516                     pixel = i;
517                     break;
518                 }
519 
520 	    /*
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.
523 	     *
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.
528 	     *
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.)
531 	     */
532 	    if (pixel != 0) {
533 		mask = 0x80 >> (x & 7);
534 
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;
543 	    }
544 
545 	    /*
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.
549 	     */
550             if (++x >= HPIXELS) {
551                 x = 0;
552 
553                 if (y++ < VPIXELS)
554                     len += ROWBYTES;
555 		else
556 		    break;
557             }
558         }
559     }
560 
561     grub_close();
562 
563     return 1;
564 }
565 
566 /* Convert a character which is a hex digit to the appropriate integer */
hex(int v)567 int hex(int v)
568 {
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');
574 }
575 
576 
577 /* move the graphics cursor location to col, row */
graphics_setxy(int col,int row)578 static void graphics_setxy(int col, int row) {
579     if (col >= x0 && col < x1) {
580         fontx = col;
581         cursorX = col << 3;
582     }
583     if (row >= y0 && row < y1) {
584         fonty = row;
585         cursorY = row << 4;
586     }
587 }
588 
589 /* scroll the screen */
graphics_scroll()590 static void graphics_scroll() {
591     int i, j;
592 
593     /* we don't want to scroll recursively... that would be bad */
594     if (no_scroll)
595         return;
596     no_scroll = 1;
597 
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]);
603         }
604     }
605 
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);
611 
612     no_scroll = 0;
613 }
614 
graphics_cursor(int set)615 void graphics_cursor(int set) {
616     unsigned char *pat, *mem, *ptr, chr[16 << 2];
617     int i, ch, invert, offset;
618 
619     if (set && no_scroll)
620         return;
621 
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);
626 
627     mem = (unsigned char*)VIDEOMEM + offset;
628 
629     if (!set) {
630         for (i = 0; i < 16; i++) {
631             unsigned char mask = pat[i];
632 
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];
638 
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);
644 
645                         chr[i     ] &= pmask;
646                         chr[16 + i] &= pmask;
647                         chr[32 + i] &= pmask;
648                         chr[48 + i] &= pmask;
649                     }
650                     if (i > 0 && ch != DISP_VERT) {
651                         unsigned char pmask = ~(pat[i - 1] >> 1);
652 
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];
659 
660                             chr[i     ] &= pmask;
661                             chr[16 + i] &= pmask;
662                             chr[32 + i] &= pmask;
663                             chr[48 + i] &= pmask;
664                         }
665                     }
666                 }
667                 chr[i     ] |= mask;
668                 chr[16 + i] |= mask;
669                 chr[32 + i] |= mask;
670                 chr[48 + i] |= mask;
671 
672                 offset += ROWBYTES;
673             }
674             else {
675                 chr[i     ] = mask;
676                 chr[16 + i] = mask;
677                 chr[32 + i] = mask;
678                 chr[48 + i] = mask;
679             }
680         }
681     }
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];
688         }
689         return;
690     }
691 
692     offset = 0;
693     for (i = 1; i < 16; i <<= 1, offset += 16) {
694         int j;
695 
696         MapMask(i);
697         ptr = mem;
698         for (j = 0; j < 16; j++, ptr += ROWBYTES)
699             *ptr = chr[j + offset];
700     }
701 
702     MapMask(15);
703 }
704 
705 #endif /* SUPPORT_GRAPHICS */
706