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 int saved_videomode;
34 unsigned char *font8x16;
35 
36 int graphics_inited = 0;
37 static char splashimage[64];
38 
39 #define VSHADOW VSHADOW1
40 unsigned char VSHADOW1[38400];
41 unsigned char VSHADOW2[38400];
42 unsigned char VSHADOW4[38400];
43 unsigned char VSHADOW8[38400];
44 
45 /* constants to define the viewable area */
46 const int x0 = 0;
47 const int x1 = 80;
48 const int y0 = 0;
49 const int y1 = 30;
50 
51 /* text buffer has to be kept around so that we can write things as we
52  * scroll and the like */
53 unsigned short text[80 * 30];
54 
55 /* why do these have to be kept here? */
56 int foreground = (63 << 16) | (63 << 8) | (63), background = 0, border = 0;
57 
58 /* current position */
59 static int fontx = 0;
60 static int fonty = 0;
61 
62 /* global state so that we don't try to recursively scroll or cursor */
63 static int no_scroll = 0;
64 
65 /* color state */
66 static int graphics_standard_color = A_NORMAL;
67 static int graphics_normal_color = A_NORMAL;
68 static int graphics_highlight_color = A_REVERSE;
69 static int graphics_current_color = A_NORMAL;
70 static color_state graphics_color_state = COLOR_STATE_STANDARD;
71 
72 
73 /* graphics local functions */
74 static void graphics_setxy(int col, int row);
75 static void graphics_scroll();
76 
77 /* FIXME: where do these really belong? */
78 static inline void outb(unsigned short port, unsigned char val)
79 {
80     __asm __volatile ("outb %0,%1"::"a" (val), "d" (port));
81 }
82 
83 static void MapMask(int value) {
84     outb(0x3c4, 2);
85     outb(0x3c5, value);
86 }
87 
88 /* bit mask register */
89 static void BitMask(int value) {
90     outb(0x3ce, 8);
91     outb(0x3cf, value);
92 }
93 
94 
95 
96 /* Set the splash image */
97 void graphics_set_splash(char *splashfile) {
98     grub_strcpy(splashimage, splashfile);
99 }
100 
101 /* Get the current splash image */
102 char *graphics_get_splash(void) {
103     return splashimage;
104 }
105 
106 /* Initialize a vga16 graphics display with the palette based off of
107  * the image in splashimage.  If the image doesn't exist, leave graphics
108  * mode.  */
109 int graphics_init()
110 {
111     if (!graphics_inited) {
112         saved_videomode = set_videomode(0x12);
113     }
114 
115     if (!read_image(splashimage)) {
116         set_videomode(saved_videomode);
117         grub_printf("failed to read image\n");
118         return 0;
119     }
120 
121     font8x16 = (unsigned char*)graphics_get_font();
122 
123     graphics_inited = 1;
124 
125     /* make sure that the highlight color is set correctly */
126     graphics_highlight_color = ((graphics_normal_color >> 4) |
127 				((graphics_normal_color & 0xf) << 4));
128 
129     return 1;
130 }
131 
132 /* Leave graphics mode */
133 void graphics_end(void)
134 {
135     if (graphics_inited) {
136         set_videomode(saved_videomode);
137         graphics_inited = 0;
138     }
139 }
140 
141 /* Print ch on the screen.  Handle any needed scrolling or the like */
142 void graphics_putchar(int ch) {
143     ch &= 0xff;
144 
145     graphics_cursor(0);
146 
147     if (ch == '\n') {
148         if (fonty + 1 < y1)
149             graphics_setxy(fontx, fonty + 1);
150         else
151             graphics_scroll();
152         graphics_cursor(1);
153         return;
154     } else if (ch == '\r') {
155         graphics_setxy(x0, fonty);
156         graphics_cursor(1);
157         return;
158     }
159 
160     graphics_cursor(0);
161 
162     text[fonty * 80 + fontx] = ch;
163     text[fonty * 80 + fontx] &= 0x00ff;
164     if (graphics_current_color & 0xf0)
165         text[fonty * 80 + fontx] |= 0x100;
166 
167     graphics_cursor(0);
168 
169     if ((fontx + 1) >= x1) {
170         graphics_setxy(x0, fonty);
171         if (fonty + 1 < y1)
172             graphics_setxy(x0, fonty + 1);
173         else
174             graphics_scroll();
175     } else {
176         graphics_setxy(fontx + 1, fonty);
177     }
178 
179     graphics_cursor(1);
180 }
181 
182 /* get the current location of the cursor */
183 int graphics_getxy(void) {
184     return (fontx << 8) | fonty;
185 }
186 
187 void graphics_gotoxy(int x, int y) {
188     graphics_cursor(0);
189 
190     graphics_setxy(x, y);
191 
192     graphics_cursor(1);
193 }
194 
195 void graphics_cls(void) {
196     int i;
197     unsigned char *mem, *s1, *s2, *s4, *s8;
198 
199     graphics_cursor(0);
200     graphics_gotoxy(x0, y0);
201 
202     mem = (unsigned char*)VIDEOMEM;
203     s1 = (unsigned char*)VSHADOW1;
204     s2 = (unsigned char*)VSHADOW2;
205     s4 = (unsigned char*)VSHADOW4;
206     s8 = (unsigned char*)VSHADOW8;
207 
208     for (i = 0; i < 80 * 30; i++)
209         text[i] = ' ';
210     graphics_cursor(1);
211 
212     BitMask(0xff);
213 
214     /* plano 1 */
215     MapMask(1);
216     grub_memcpy(mem, s1, 38400);
217 
218     /* plano 2 */
219     MapMask(2);
220     grub_memcpy(mem, s2, 38400);
221 
222     /* plano 3 */
223     MapMask(4);
224     grub_memcpy(mem, s4, 38400);
225 
226     /* plano 4 */
227     MapMask(8);
228     grub_memcpy(mem, s8, 38400);
229 
230     MapMask(15);
231 
232 }
233 
234 void graphics_setcolorstate (color_state state) {
235     switch (state) {
236     case COLOR_STATE_STANDARD:
237         graphics_current_color = graphics_standard_color;
238         break;
239     case COLOR_STATE_NORMAL:
240         graphics_current_color = graphics_normal_color;
241         break;
242     case COLOR_STATE_HIGHLIGHT:
243         graphics_current_color = graphics_highlight_color;
244         break;
245     default:
246         graphics_current_color = graphics_standard_color;
247         break;
248     }
249 
250     graphics_color_state = state;
251 }
252 
253 void graphics_setcolor (int normal_color, int highlight_color) {
254     graphics_normal_color = normal_color;
255     graphics_highlight_color = highlight_color;
256 
257     graphics_setcolorstate (graphics_color_state);
258 }
259 
260 int graphics_setcursor (int on) {
261     /* FIXME: we don't have a cursor in graphics */
262     return 1;
263 }
264 
265 /* Read in the splashscreen image and set the palette up appropriately.
266  * Format of splashscreen is an xpm (can be gzipped) with 16 colors and
267  * 640x480. */
268 int read_image(char *s)
269 {
270     char buf[32], pal[16];
271     unsigned char c, base, mask, *s1, *s2, *s4, *s8;
272     unsigned i, len, idx, colors, x, y, width, height;
273 
274     if (!grub_open(s))
275         return 0;
276 
277     /* read header */
278     if (!grub_read((char*)&buf, 10) || grub_memcmp(buf, "/* XPM */\n", 10)) {
279         grub_close();
280         return 0;
281     }
282 
283     /* parse info */
284     while (grub_read(&c, 1)) {
285         if (c == '"')
286             break;
287     }
288 
289     while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
290         ;
291 
292     i = 0;
293     width = c - '0';
294     while (grub_read(&c, 1)) {
295         if (c >= '0' && c <= '9')
296             width = width * 10 + c - '0';
297         else
298             break;
299     }
300     while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
301         ;
302 
303     height = c - '0';
304     while (grub_read(&c, 1)) {
305         if (c >= '0' && c <= '9')
306             height = height * 10 + c - '0';
307         else
308             break;
309     }
310     while (grub_read(&c, 1) && (c == ' ' || c == '\t'))
311         ;
312 
313     colors = c - '0';
314     while (grub_read(&c, 1)) {
315         if (c >= '0' && c <= '9')
316             colors = colors * 10 + c - '0';
317         else
318             break;
319     }
320 
321     base = 0;
322     while (grub_read(&c, 1) && c != '"')
323         ;
324 
325     /* palette */
326     for (i = 0, idx = 1; i < colors; i++) {
327         len = 0;
328 
329         while (grub_read(&c, 1) && c != '"')
330             ;
331         grub_read(&c, 1);       /* char */
332         base = c;
333         grub_read(buf, 4);      /* \t c # */
334 
335         while (grub_read(&c, 1) && c != '"') {
336             if (len < sizeof(buf))
337                 buf[len++] = c;
338         }
339 
340         if (len == 6 && idx < 15) {
341             int r = ((hex(buf[0]) << 4) | hex(buf[1])) >> 2;
342             int g = ((hex(buf[2]) << 4) | hex(buf[3])) >> 2;
343             int b = ((hex(buf[4]) << 4) | hex(buf[5])) >> 2;
344 
345             pal[idx] = base;
346             graphics_set_palette(idx, r, g, b);
347             ++idx;
348         }
349     }
350 
351     x = y = len = 0;
352 
353     s1 = (unsigned char*)VSHADOW1;
354     s2 = (unsigned char*)VSHADOW2;
355     s4 = (unsigned char*)VSHADOW4;
356     s8 = (unsigned char*)VSHADOW8;
357 
358     for (i = 0; i < 38400; i++)
359         s1[i] = s2[i] = s4[i] = s8[i] = 0;
360 
361     /* parse xpm data */
362     while (y < height) {
363         while (1) {
364             if (!grub_read(&c, 1)) {
365                 grub_close();
366                 return 0;
367             }
368             if (c == '"')
369                 break;
370         }
371 
372         while (grub_read(&c, 1) && c != '"') {
373             for (i = 1; i < 15; i++)
374                 if (pal[i] == c) {
375                     c = i;
376                     break;
377                 }
378 
379             mask = 0x80 >> (x & 7);
380             if (c & 1)
381                 s1[len + (x >> 3)] |= mask;
382             if (c & 2)
383                 s2[len + (x >> 3)] |= mask;
384             if (c & 4)
385                 s4[len + (x >> 3)] |= mask;
386             if (c & 8)
387                 s8[len + (x >> 3)] |= mask;
388 
389             if (++x >= 640) {
390                 x = 0;
391 
392                 if (y < 480)
393                     len += 80;
394                 ++y;
395             }
396         }
397     }
398 
399     grub_close();
400 
401     graphics_set_palette(0, (background >> 16), (background >> 8) & 63,
402                 background & 63);
403     graphics_set_palette(15, (foreground >> 16), (foreground >> 8) & 63,
404                 foreground & 63);
405     graphics_set_palette(0x11, (border >> 16), (border >> 8) & 63,
406                          border & 63);
407 
408     return 1;
409 }
410 
411 
412 /* Convert a character which is a hex digit to the appropriate integer */
413 int hex(int v)
414 {
415     if (v >= 'A' && v <= 'F')
416         return (v - 'A' + 10);
417     if (v >= 'a' && v <= 'f')
418         return (v - 'a' + 10);
419     return (v - '0');
420 }
421 
422 
423 /* move the graphics cursor location to col, row */
424 static void graphics_setxy(int col, int row) {
425     if (col >= x0 && col < x1) {
426         fontx = col;
427         cursorX = col << 3;
428     }
429     if (row >= y0 && row < y1) {
430         fonty = row;
431         cursorY = row << 4;
432     }
433 }
434 
435 /* scroll the screen */
436 static void graphics_scroll() {
437     int i, j;
438 
439     /* we don't want to scroll recursively... that would be bad */
440     if (no_scroll)
441         return;
442     no_scroll = 1;
443 
444     /* move everything up a line */
445     for (j = y0 + 1; j < y1; j++) {
446         graphics_gotoxy(x0, j - 1);
447         for (i = x0; i < x1; i++) {
448             graphics_putchar(text[j * 80 + i]);
449         }
450     }
451 
452     /* last line should be blank */
453     graphics_gotoxy(x0, y1 - 1);
454     for (i = x0; i < x1; i++)
455         graphics_putchar(' ');
456     graphics_setxy(x0, y1 - 1);
457 
458     no_scroll = 0;
459 }
460 
461 
462 void graphics_cursor(int set) {
463     unsigned char *pat, *mem, *ptr, chr[16 << 2];
464     int i, ch, invert, offset;
465 
466     if (set && no_scroll)
467         return;
468 
469     offset = cursorY * 80 + fontx;
470     ch = text[fonty * 80 + fontx] & 0xff;
471     invert = (text[fonty * 80 + fontx] & 0xff00) != 0;
472     pat = font8x16 + (ch << 4);
473 
474     mem = (unsigned char*)VIDEOMEM + offset;
475 
476     if (!set) {
477         for (i = 0; i < 16; i++) {
478             unsigned char mask = pat[i];
479 
480             if (!invert) {
481                 chr[i     ] = ((unsigned char*)VSHADOW1)[offset];
482                 chr[16 + i] = ((unsigned char*)VSHADOW2)[offset];
483                 chr[32 + i] = ((unsigned char*)VSHADOW4)[offset];
484                 chr[48 + i] = ((unsigned char*)VSHADOW8)[offset];
485 
486                 /* FIXME: if (shade) */
487                 if (1) {
488                     if (ch == DISP_VERT || ch == DISP_LL ||
489                         ch == DISP_UR || ch == DISP_LR) {
490                         unsigned char pmask = ~(pat[i] >> 1);
491 
492                         chr[i     ] &= pmask;
493                         chr[16 + i] &= pmask;
494                         chr[32 + i] &= pmask;
495                         chr[48 + i] &= pmask;
496                     }
497                     if (i > 0 && ch != DISP_VERT) {
498                         unsigned char pmask = ~(pat[i - 1] >> 1);
499 
500                         chr[i     ] &= pmask;
501                         chr[16 + i] &= pmask;
502                         chr[32 + i] &= pmask;
503                         chr[48 + i] &= pmask;
504                         if (ch == DISP_HORIZ || ch == DISP_UR || ch == DISP_LR) {
505                             pmask = ~pat[i - 1];
506 
507                             chr[i     ] &= pmask;
508                             chr[16 + i] &= pmask;
509                             chr[32 + i] &= pmask;
510                             chr[48 + i] &= pmask;
511                         }
512                     }
513                 }
514                 chr[i     ] |= mask;
515                 chr[16 + i] |= mask;
516                 chr[32 + i] |= mask;
517                 chr[48 + i] |= mask;
518 
519                 offset += 80;
520             }
521             else {
522                 chr[i     ] = mask;
523                 chr[16 + i] = mask;
524                 chr[32 + i] = mask;
525                 chr[48 + i] = mask;
526             }
527         }
528     }
529     else {
530         MapMask(15);
531         ptr = mem;
532         for (i = 0; i < 16; i++, ptr += 80) {
533             cursorBuf[i] = pat[i];
534             *ptr = ~pat[i];
535         }
536         return;
537     }
538 
539     offset = 0;
540     for (i = 1; i < 16; i <<= 1, offset += 16) {
541         int j;
542 
543         MapMask(i);
544         ptr = mem;
545         for (j = 0; j < 16; j++, ptr += 80)
546             *ptr = chr[j + offset];
547     }
548 
549     MapMask(15);
550 }
551 
552 #endif /* SUPPORT_GRAPHICS */
553