xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/graphics.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 static char splashimage[64];
40 
41 #define	HPIXELS		640
42 #define	VPIXELS		480
43 #define	HPIXELSPERBYTE	8
44 
45 #define	ROWBYTES	(HPIXELS / HPIXELSPERBYTE)
46 #define	SCREENBYTES	(ROWBYTES * VPIXELS)
47 
48 #define VSHADOW VSHADOW1
49 unsigned char VSHADOW1[SCREENBYTES];
50 unsigned char VSHADOW2[SCREENBYTES];
51 unsigned char VSHADOW4[SCREENBYTES];
52 unsigned char VSHADOW8[SCREENBYTES];
53 
54 static unsigned char *s1 = (unsigned char*)VSHADOW1;
55 static unsigned char *s2 = (unsigned char*)VSHADOW2;
56 static unsigned char *s4 = (unsigned char*)VSHADOW4;
57 static unsigned char *s8 = (unsigned char*)VSHADOW8;
58 
59 /* constants to define the viewable area */
60 const int x0 = 0;
61 const int x1 = ROWBYTES;
62 const int y0 = 0;
63 const int y1 = 30;
64 
65 /* text buffer has to be kept around so that we can write things as we
66  * scroll and the like */
67 unsigned short text[ROWBYTES * 30];
68 
69 /* why do these have to be kept here? */
70 int foreground = (63 << 16) | (63 << 8) | (63), background = 0, border = 0;
71 
72 
73 /* current position */
74 static int fontx = 0;
75 static int fonty = 0;
76 
77 /* global state so that we don't try to recursively scroll or cursor */
78 static int no_scroll = 0;
79 
80 /* color state */
81 static int graphics_standard_color = A_NORMAL;
82 static int graphics_normal_color = A_NORMAL;
83 static int graphics_highlight_color = A_REVERSE;
84 static int graphics_current_color = A_NORMAL;
85 static color_state graphics_color_state = COLOR_STATE_STANDARD;
86 
87 
88 /* graphics local functions */
89 static void graphics_setxy(int col, int row);
90 static void graphics_scroll();
91 
92 /* FIXME: where do these really belong? */
93 static inline void outb(unsigned short port, unsigned char val)
94 {
95     __asm __volatile ("outb %0,%1"::"a" (val), "d" (port));
96 }
97 
98 static int splashscreen_read = 0;
99 
100 static void MapMask(int value) {
101     outb(0x3c4, 2);
102     outb(0x3c5, value);
103 }
104 
105 /* bit mask register */
106 static void BitMask(int value) {
107     outb(0x3ce, 8);
108     outb(0x3cf, value);
109 }
110 
111 
112 /* Set the splash image */
113 void graphics_set_splash(char *splashfile) {
114     grub_strcpy(splashimage, splashfile);
115 }
116 
117 /* Get the current splash image */
118 char *graphics_get_splash(void) {
119     return splashimage;
120 }
121 
122 /* Initialize a vga16 graphics display with the palette based off of
123  * the image in splashimage.  If the image doesn't exist, leave graphics
124  * mode.  */
125 int graphics_init()
126 {
127     if (!graphics_inited) {
128         saved_videomode = set_videomode(0x12);
129     }
130 
131     if (!read_image(splashimage)) {
132         set_videomode(saved_videomode);
133         grub_printf("failed to read image\n");
134         return 0;
135     }
136 
137     font8x16 = (unsigned char*)graphics_get_font();
138 
139     graphics_inited = 1;
140 
141     /* make sure that the highlight color is set correctly */
142     graphics_highlight_color = ((graphics_normal_color >> 4) |
143 				((graphics_normal_color & 0xf) << 4));
144 
145     return 1;
146 }
147 
148 /* Leave graphics mode */
149 void graphics_end(void)
150 {
151     if (graphics_inited) {
152         set_videomode(saved_videomode);
153         graphics_inited = 0;
154     }
155 }
156 
157 /* Print ch on the screen.  Handle any needed scrolling or the like */
158 void graphics_putchar(int ch) {
159     ch &= 0xff;
160 
161     graphics_cursor(0);
162 
163     if (ch == '\n') {
164         if (fonty + 1 < y1)
165             graphics_setxy(fontx, fonty + 1);
166         else
167             graphics_scroll();
168         graphics_cursor(1);
169         return;
170     } else if (ch == '\r') {
171         graphics_setxy(x0, fonty);
172         graphics_cursor(1);
173         return;
174     }
175 
176     graphics_cursor(0);
177 
178     text[fonty * ROWBYTES + fontx] = ch;
179     text[fonty * ROWBYTES + fontx] &= 0x00ff;
180     if (graphics_current_color & 0xf0)
181         text[fonty * ROWBYTES + fontx] |= 0x100;
182 
183     graphics_cursor(0);
184 
185     if ((fontx + 1) >= x1) {
186         graphics_setxy(x0, fonty);
187         if (fonty + 1 < y1)
188             graphics_setxy(x0, fonty + 1);
189         else
190             graphics_scroll();
191     } else {
192         graphics_setxy(fontx + 1, fonty);
193     }
194 
195     graphics_cursor(1);
196 }
197 
198 /* get the current location of the cursor */
199 int graphics_getxy(void) {
200     return (fontx << 8) | fonty;
201 }
202 
203 void graphics_gotoxy(int x, int y) {
204     graphics_cursor(0);
205 
206     graphics_setxy(x, y);
207 
208     graphics_cursor(1);
209 }
210 
211 void graphics_cls(void) {
212     int i;
213     unsigned char *mem;
214 
215     graphics_cursor(0);
216     graphics_gotoxy(x0, y0);
217 
218     mem = (unsigned char*)VIDEOMEM;
219 
220     for (i = 0; i < ROWBYTES * 30; i++)
221         text[i] = ' ';
222     graphics_cursor(1);
223 
224     BitMask(0xff);
225 
226     /* plane 1 */
227     MapMask(1);
228     grub_memcpy(mem, s1, SCREENBYTES);
229 
230     /* plane 2 */
231     MapMask(2);
232     grub_memcpy(mem, s2, SCREENBYTES);
233 
234     /* plane 3 */
235     MapMask(4);
236     grub_memcpy(mem, s4, SCREENBYTES);
237 
238     /* plane 4 */
239     MapMask(8);
240     grub_memcpy(mem, s8, SCREENBYTES);
241 
242     MapMask(15);
243 }
244 
245 void graphics_setcolorstate (color_state state) {
246     switch (state) {
247     case COLOR_STATE_STANDARD:
248         graphics_current_color = graphics_standard_color;
249         break;
250     case COLOR_STATE_NORMAL:
251         graphics_current_color = graphics_normal_color;
252         break;
253     case COLOR_STATE_HIGHLIGHT:
254         graphics_current_color = graphics_highlight_color;
255         break;
256     default:
257         graphics_current_color = graphics_standard_color;
258         break;
259     }
260 
261     graphics_color_state = state;
262 }
263 
264 void graphics_setcolor (int normal_color, int highlight_color) {
265     graphics_normal_color = normal_color;
266     graphics_highlight_color = highlight_color;
267 
268     graphics_setcolorstate (graphics_color_state);
269 }
270 
271 int graphics_setcursor (int on) {
272     /* FIXME: we don't have a cursor in graphics */
273     return 1;
274 }
275 
276 void
277 draw_xbmlogo(void)
278 {
279     unsigned char mask;
280     unsigned xbm_index = 0, xbm_incr;
281     unsigned screenx, logox, logoy, fb_offset, fb_index;
282 
283     /*
284      * Place the logo such that the right hand side will be four pixels from
285      * the right hand edge of the screen and the bottom will be two pixels
286      * from the bottom edge.
287      */
288     fb_offset = ((VPIXELS - 1) - logo_height - 2) * ROWBYTES;
289     xbm_incr = (logo_width / 8) + 1;
290 
291     for (logoy = 0; logoy < logo_height; logoy++) {
292 	for (logox = 0, screenx = (HPIXELS - 1) - logo_width - 4;
293 	  logox < logo_width; logox++, screenx++) {
294 	    mask = 0x80 >> (screenx & 7);
295 	    fb_index = fb_offset + (screenx >> 3);
296 
297 	    /*
298 	     * If a bit is clear in the bitmap, draw it onto the
299 	     * framebuffer in the default foreground color.
300 	     */
301 	    if ((logo_bits[xbm_index + (logox >> 3)] &
302 		(1 << (logox & 7))) == 0) {
303 		    /* system default foreground color */
304 		    s1[fb_index] |= mask;
305 		    s2[fb_index] |= mask;
306 		    s4[fb_index] |= mask;
307 		    s8[fb_index] |= mask;
308 	    }
309 	}
310 
311 	xbm_index += xbm_incr;
312 	fb_offset += ROWBYTES;
313     }
314 }
315 
316 /*
317  * Read in the splashscreen image and set the palette up appropriately.
318  *
319  * Format of splashscreen is an XPM (can be gzipped) with up to 15 colors and
320  * is assumed to be of the proper screen dimensions.
321  */
322 int read_image(char *s)
323 {
324     char buf[32], pal[16];
325     unsigned char c, base, mask;
326     unsigned i, len, idx, colors, x, y, width, height;
327 
328     if (!grub_open(s))
329         return 0;
330 
331     /* read XPM header - must match memcmp string PRECISELY. */
332     if (!grub_read((char*)&buf, 10) || grub_memcmp(buf, "/* XPM */\n", 10)) {
333         grub_close();
334         return 0;
335     }
336 
337     /* skip characters until we reach an initial '"' */
338     while (grub_read(&c, 1)) {
339         if (c == '"')
340             break;
341     }
342 
343     /* skip whitespace */
344     while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
345 	;
346 
347     /*
348      * Format here should be four integers:
349      *
350      *     Width Height NumberOfColors CharactersPerPixel
351      */
352     i = 0;
353     width = c - '0';
354     while (grub_read(&c, 1)) {
355         if (c >= '0' && c <= '9')
356             width = width * 10 + c - '0';
357         else
358             break;
359     }
360 
361     /* skip whitespace to advance to next digit */
362     while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
363 	;
364 
365     height = c - '0';
366     while (grub_read(&c, 1)) {
367         if (c >= '0' && c <= '9')
368             height = height * 10 + c - '0';
369         else
370             break;
371     }
372 
373     /* skip whitespace to advance to next digit */
374     while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ;
375 
376     colors = c - '0';
377     while (grub_read(&c, 1)) {
378         if (c >= '0' && c <= '9')
379             colors = colors * 10 + c - '0';
380         else
381             break;
382     }
383 
384     /* allow 14 specified palette colors (indices 1 - 14) at most */
385     if (colors > 14) {
386 	grub_close();
387 	return (0);
388     }
389 
390     /* eat rest of line - assumes chars per pixel is one */
391     while (grub_read(&c, 1) && c != '"')
392         ;
393 
394     /*
395      * Parse the XPM palette - the format is:
396      *
397      *    identifier colorspace #RRGGBB
398      *
399      * The identifier is simply a single character; the colorspace identifier
400      * is skipped as it's assumed to be "c" denoting RGB color.
401      *
402      * The six digits after the "#" are assumed to be a six digit RGB color
403      * identifier as defined in X11's rgb.txt file.
404      */
405     for (i = 0, idx = 1; i < colors; i++) {
406         len = 0;
407 
408         while (grub_read(&c, 1) && c != '"')
409             ;
410 
411         grub_read(&c, 1);       /* char */
412         base = c;
413         grub_read(buf, 4);      /* \t c # */
414 
415         while (grub_read(&c, 1) && c != '"') {
416             if (len < sizeof(buf))
417                 buf[len++] = c;
418         }
419 
420         if (len == 6) {
421             int r = ((hex(buf[0]) << 4) | hex(buf[1])) >> 2;
422             int g = ((hex(buf[2]) << 4) | hex(buf[3])) >> 2;
423             int b = ((hex(buf[4]) << 4) | hex(buf[5])) >> 2;
424 
425             pal[idx] = base;
426             graphics_set_palette(idx, r, g, b);
427             ++idx;
428         }
429     }
430 
431     x = y = len = 0;
432 
433     /* clear (zero out) all four planes of the framebuffer */
434     for (i = 0; i < SCREENBYTES; i++)
435         s1[i] = s2[i] = s4[i] = s8[i] = 0;
436 
437     /* parse the XPM data */
438     while (y < height) {
439 	/* exit on EOF, otherwise skip characters until an initial '"' */
440         while (1) {
441             if (!grub_read(&c, 1)) {
442                 grub_close();
443                 return 0;
444             }
445             if (c == '"')
446                 break;
447         }
448 
449 	/* read characters until we hit an EOF or a terminating '"' */
450         while (grub_read(&c, 1) && c != '"') {
451 
452 	    /* look up specified pixel color in palette */
453             for (i = 1; i < 15; i++)
454                 if (pal[i] == c) {
455                     c = i;
456                     break;
457                 }
458 
459 	    /*
460 	     * A bit is set in each of the "planes" of the frame buffer to
461 	     * denote a pixel drawn in each color of the palette.
462 	     *
463 	     * The planes are a binary representation of the palette, so a
464 	     * pixel in color "1" of the palette would be denoted by setting a
465 	     * bit in plane "s1"; a pixel in color "15" of the palette would
466 	     * set the same bit in each of the four planes.
467 	     *
468 	     * Pixels are represented by set bits in a byte, in the order
469 	     * left-to-right (e.g. pixel 0 is 0x80, pixel 7 is 1.)
470 	     */
471             mask = 0x80 >> (x & 7);
472             if (c & 1)
473                 s1[len + (x >> 3)] |= mask;
474             if (c & 2)
475                 s2[len + (x >> 3)] |= mask;
476             if (c & 4)
477                 s4[len + (x >> 3)] |= mask;
478             if (c & 8)
479                 s8[len + (x >> 3)] |= mask;
480 
481 	    /*
482 	     * Increment "x"; if we hit pixel HPIXELS, wrap to the start of the
483 	     * next horizontal line if we haven't yet reached the bottom of
484 	     * the screen.
485 	     */
486             if (++x >= HPIXELS) {
487                 x = 0;
488 
489                 if (y++ < VPIXELS)
490                     len += ROWBYTES;
491 		else
492 		    break;
493             }
494         }
495     }
496 
497     grub_close();
498 
499     /*
500      * Set BIOS palette color 0 to be the system background color, 15 to be the
501      * system foreground color, and 17 to be the system border color.
502      */
503     graphics_set_palette(0, (background >> 16), (background >> 8) & 63,
504                 background & 63);
505     graphics_set_palette(15, (foreground >> 16), (foreground >> 8) & 63,
506                 foreground & 63);
507     graphics_set_palette(0x11, (border >> 16), (border >> 8) & 63,
508                          border & 63);
509 
510     draw_xbmlogo();
511 
512     return 1;
513 }
514 
515 /* Convert a character which is a hex digit to the appropriate integer */
516 int hex(int v)
517 {
518     if (v >= 'A' && v <= 'F')
519         return (v - 'A' + 10);
520     if (v >= 'a' && v <= 'f')
521         return (v - 'a' + 10);
522     return (v - '0');
523 }
524 
525 
526 /* move the graphics cursor location to col, row */
527 static void graphics_setxy(int col, int row) {
528     if (col >= x0 && col < x1) {
529         fontx = col;
530         cursorX = col << 3;
531     }
532     if (row >= y0 && row < y1) {
533         fonty = row;
534         cursorY = row << 4;
535     }
536 }
537 
538 /* scroll the screen */
539 static void graphics_scroll() {
540     int i, j;
541 
542     /* we don't want to scroll recursively... that would be bad */
543     if (no_scroll)
544         return;
545     no_scroll = 1;
546 
547     /* move everything up a line */
548     for (j = y0 + 1; j < y1; j++) {
549         graphics_gotoxy(x0, j - 1);
550         for (i = x0; i < x1; i++) {
551             graphics_putchar(text[j * ROWBYTES + i]);
552         }
553     }
554 
555     /* last line should be blank */
556     graphics_gotoxy(x0, y1 - 1);
557     for (i = x0; i < x1; i++)
558         graphics_putchar(' ');
559     graphics_setxy(x0, y1 - 1);
560 
561     no_scroll = 0;
562 }
563 
564 void graphics_cursor(int set) {
565     unsigned char *pat, *mem, *ptr, chr[16 << 2];
566     int i, ch, invert, offset;
567 
568     if (set && no_scroll)
569         return;
570 
571     offset = cursorY * ROWBYTES + fontx;
572     ch = text[fonty * ROWBYTES + fontx] & 0xff;
573     invert = (text[fonty * ROWBYTES + fontx] & 0xff00) != 0;
574     pat = font8x16 + (ch << 4);
575 
576     mem = (unsigned char*)VIDEOMEM + offset;
577 
578     if (!set) {
579         for (i = 0; i < 16; i++) {
580             unsigned char mask = pat[i];
581 
582             if (!invert) {
583                 chr[i     ] = ((unsigned char*)VSHADOW1)[offset];
584                 chr[16 + i] = ((unsigned char*)VSHADOW2)[offset];
585                 chr[32 + i] = ((unsigned char*)VSHADOW4)[offset];
586                 chr[48 + i] = ((unsigned char*)VSHADOW8)[offset];
587 
588                 /* FIXME: if (shade) */
589                 if (1) {
590                     if (ch == DISP_VERT || ch == DISP_LL ||
591                         ch == DISP_UR || ch == DISP_LR) {
592                         unsigned char pmask = ~(pat[i] >> 1);
593 
594                         chr[i     ] &= pmask;
595                         chr[16 + i] &= pmask;
596                         chr[32 + i] &= pmask;
597                         chr[48 + i] &= pmask;
598                     }
599                     if (i > 0 && ch != DISP_VERT) {
600                         unsigned char pmask = ~(pat[i - 1] >> 1);
601 
602                         chr[i     ] &= pmask;
603                         chr[16 + i] &= pmask;
604                         chr[32 + i] &= pmask;
605                         chr[48 + i] &= pmask;
606                         if (ch == DISP_HORIZ || ch == DISP_UR || ch == DISP_LR) {
607                             pmask = ~pat[i - 1];
608 
609                             chr[i     ] &= pmask;
610                             chr[16 + i] &= pmask;
611                             chr[32 + i] &= pmask;
612                             chr[48 + i] &= pmask;
613                         }
614                     }
615                 }
616                 chr[i     ] |= mask;
617                 chr[16 + i] |= mask;
618                 chr[32 + i] |= mask;
619                 chr[48 + i] |= mask;
620 
621                 offset += ROWBYTES;
622             }
623             else {
624                 chr[i     ] = mask;
625                 chr[16 + i] = mask;
626                 chr[32 + i] = mask;
627                 chr[48 + i] = mask;
628             }
629         }
630     }
631     else {
632         MapMask(15);
633         ptr = mem;
634         for (i = 0; i < 16; i++, ptr += ROWBYTES) {
635             cursorBuf[i] = pat[i];
636             *ptr = ~pat[i];
637         }
638         return;
639     }
640 
641     offset = 0;
642     for (i = 1; i < 16; i <<= 1, offset += 16) {
643         int j;
644 
645         MapMask(i);
646         ptr = mem;
647         for (j = 0; j < 16; j++, ptr += ROWBYTES)
648             *ptr = chr[j + offset];
649     }
650 
651     MapMask(15);
652 }
653 
654 #endif /* SUPPORT_GRAPHICS */
655