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