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