/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2005,2007 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GRUB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_CHAR_WIDTH 8 #define DEFAULT_CHAR_HEIGHT 16 #define DEFAULT_FG_COLOR 0xa #define DEFAULT_BG_COLOR 0x0 struct grub_colored_char { /* An Unicode codepoint. */ grub_uint32_t code; /* Color indexes. */ unsigned char fg_color; unsigned char bg_color; /* The width of this character minus one. */ unsigned char width; /* The column index of this character. */ unsigned char index; }; struct grub_virtual_screen { /* Dimensions of the virtual screen. */ grub_uint32_t width; grub_uint32_t height; /* Offset in the display. */ grub_uint32_t offset_x; grub_uint32_t offset_y; /* TTY Character sizes. */ grub_uint32_t char_width; grub_uint32_t char_height; /* Virtual screen TTY size. */ grub_uint32_t columns; grub_uint32_t rows; /* Current cursor details. */ grub_uint32_t cursor_x; grub_uint32_t cursor_y; grub_uint8_t cursor_state; grub_uint8_t fg_color; grub_uint8_t bg_color; /* Text buffer for virtual screen. Contains (columns * rows) number of entries. */ struct grub_colored_char *text_buffer; }; /* Make seure text buffer is not marked as allocated. */ static struct grub_virtual_screen virtual_screen = { .text_buffer = 0 }; static grub_dl_t my_mod; static unsigned char *vga_font = 0; static grub_uint32_t old_mode = 0; static struct grub_vbe_mode_info_block mode_info; static grub_uint8_t *framebuffer = 0; static grub_uint32_t bytes_per_scan_line = 0; static void grub_virtual_screen_free (void) { /* If virtual screen has been allocated, free it. */ if (virtual_screen.text_buffer != 0) grub_free (virtual_screen.text_buffer); /* Reset virtual screen data. */ grub_memset (&virtual_screen, 0, sizeof (virtual_screen)); } static grub_err_t grub_virtual_screen_setup (grub_uint32_t width, grub_uint32_t height) { /* Free old virtual screen. */ grub_virtual_screen_free (); /* Initialize with default data. */ virtual_screen.width = width; virtual_screen.height = height; virtual_screen.offset_x = 0; virtual_screen.offset_y = 0; virtual_screen.char_width = DEFAULT_CHAR_WIDTH; virtual_screen.char_height = DEFAULT_CHAR_HEIGHT; virtual_screen.cursor_x = 0; virtual_screen.cursor_y = 0; virtual_screen.cursor_state = 1; virtual_screen.fg_color = DEFAULT_FG_COLOR; virtual_screen.bg_color = DEFAULT_BG_COLOR; /* Calculate size of text buffer. */ virtual_screen.columns = virtual_screen.width / virtual_screen.char_width; virtual_screen.rows = virtual_screen.height / virtual_screen.char_height; /* Allocate memory for text buffer. */ virtual_screen.text_buffer = (struct grub_colored_char *) grub_malloc (virtual_screen.columns * virtual_screen.rows * sizeof (*virtual_screen.text_buffer)); return grub_errno; } static grub_err_t grub_vesafb_mod_init (void) { grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE; struct grub_vbe_info_block controller_info; char *modevar; /* Use fonts from VGA bios. */ vga_font = grub_vga_get_font (); /* Check if we have VESA BIOS installed. */ if (grub_vbe_probe (&controller_info) != GRUB_ERR_NONE) return grub_errno; /* Check existence of vbe_mode environment variable. */ modevar = grub_env_get ("vbe_mode"); if (modevar != 0) { unsigned long value; value = grub_strtoul (modevar, 0, 0); if (grub_errno == GRUB_ERR_NONE) use_mode = value; } /* Store initial video mode. */ if (grub_vbe_get_video_mode (&old_mode) != GRUB_ERR_NONE) return grub_errno; /* Setup desired graphics mode. */ if (grub_vbe_set_video_mode (use_mode, &mode_info) != GRUB_ERR_NONE) return grub_errno; /* Determine framebuffer and bytes per scan line. */ framebuffer = (grub_uint8_t *) mode_info.phys_base_addr; if (controller_info.version >= 0x300) bytes_per_scan_line = mode_info.lin_bytes_per_scan_line; else bytes_per_scan_line = mode_info.bytes_per_scan_line; /* Create virtual screen. */ if (grub_virtual_screen_setup (mode_info.x_resolution, mode_info.y_resolution) != GRUB_ERR_NONE) { grub_vbe_set_video_mode (old_mode, 0); return grub_errno; } /* Make sure frame buffer is black. */ grub_memset (framebuffer, 0, bytes_per_scan_line * mode_info.y_resolution); return GRUB_ERR_NONE; } static grub_err_t grub_vesafb_mod_fini (void) { grub_virtual_screen_free (); grub_vbe_set_video_mode (old_mode, 0); return GRUB_ERR_NONE; } static int grub_virtual_screen_get_glyph (grub_uint32_t code, unsigned char bitmap[32], unsigned *width) { if (code > 0x7f) { /* Map some unicode characters to the VGA font, if possible. */ switch (code) { case 0x2190: /* left arrow */ code = 0x1b; break; case 0x2191: /* up arrow */ code = 0x18; break; case 0x2192: /* right arrow */ code = 0x1a; break; case 0x2193: /* down arrow */ code = 0x19; break; case 0x2501: /* horizontal line */ code = 0xc4; break; case 0x2503: /* vertical line */ code = 0xb3; break; case 0x250F: /* upper-left corner */ code = 0xda; break; case 0x2513: /* upper-right corner */ code = 0xbf; break; case 0x2517: /* lower-left corner */ code = 0xc0; break; case 0x251B: /* lower-right corner */ code = 0xd9; break; default: return grub_font_get_glyph (code, bitmap, width); } } if (bitmap) grub_memcpy (bitmap, vga_font + code * virtual_screen.char_height, virtual_screen.char_height); *width = 1; return 1; } static void grub_virtual_screen_invalidate_char (struct grub_colored_char *p) { p->code = 0xFFFF; if (p->width) { struct grub_colored_char *q; for (q = p + 1; q <= p + p->width; q++) { q->code = 0xFFFF; q->width = 0; q->index = 0; } } p->width = 0; } static void write_char (void) { struct grub_colored_char *p; unsigned char bitmap[32]; unsigned width; unsigned y; unsigned offset; p = (virtual_screen.text_buffer + virtual_screen.cursor_x + (virtual_screen.cursor_y * virtual_screen.columns)); p -= p->index; if (! grub_virtual_screen_get_glyph (p->code, bitmap, &width)) { grub_virtual_screen_invalidate_char (p); width = 0; } for (y = 0, offset = 0; y < virtual_screen.char_height; y++, offset++) { unsigned i; for (i = 0; (i < width * virtual_screen.char_width) && (offset < 32); i++) { unsigned char color; if (bitmap[offset] & (1 << (8-i))) { color = p->fg_color; } else { color = p->bg_color; } grub_vbe_set_pixel_index(i + (virtual_screen.cursor_x * virtual_screen.char_width), y + (virtual_screen.cursor_y * virtual_screen.char_height), color); } } } static void write_cursor (void) { grub_uint32_t x; grub_uint32_t y; for (y = ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 3; y < ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 1; y++) { for (x = virtual_screen.cursor_x * virtual_screen.char_width; x < (virtual_screen.cursor_x + 1) * virtual_screen.char_width; x++) { grub_vbe_set_pixel_index(x, y, 10); } } } static void scroll_up (void) { grub_uint32_t i; /* Scroll text buffer with one line to up. */ grub_memmove (virtual_screen.text_buffer, virtual_screen.text_buffer + virtual_screen.columns, sizeof (*virtual_screen.text_buffer) * virtual_screen.columns * (virtual_screen.rows - 1)); /* Clear last line in text buffer. */ for (i = virtual_screen.columns * (virtual_screen.rows - 1); i < virtual_screen.columns * virtual_screen.rows; i++) { virtual_screen.text_buffer[i].code = ' '; virtual_screen.text_buffer[i].fg_color = 0; virtual_screen.text_buffer[i].bg_color = 0; virtual_screen.text_buffer[i].width = 0; virtual_screen.text_buffer[i].index = 0; } /* Scroll framebuffer with one line to up. */ grub_memmove (framebuffer, framebuffer + bytes_per_scan_line * virtual_screen.char_height, bytes_per_scan_line * (mode_info.y_resolution - virtual_screen.char_height)); /* Clear last line in framebuffer. */ grub_memset (framebuffer + (bytes_per_scan_line * (mode_info.y_resolution - virtual_screen.char_height)), 0, bytes_per_scan_line * virtual_screen.char_height); } static void grub_vesafb_putchar (grub_uint32_t c) { if (c == '\a') /* FIXME */ return; if (c == '\b' || c == '\n' || c == '\r') { /* Erase current cursor, if any. */ if (virtual_screen.cursor_state) write_char (); switch (c) { case '\b': if (virtual_screen.cursor_x > 0) virtual_screen.cursor_x--; break; case '\n': if (virtual_screen.cursor_y >= virtual_screen.rows - 1) scroll_up (); else virtual_screen.cursor_y++; break; case '\r': virtual_screen.cursor_x = 0; break; } if (virtual_screen.cursor_state) write_cursor (); } else { unsigned width; struct grub_colored_char *p; grub_virtual_screen_get_glyph (c, 0, &width); if (virtual_screen.cursor_x + width > virtual_screen.columns) grub_putchar ('\n'); p = (virtual_screen.text_buffer + virtual_screen.cursor_x + virtual_screen.cursor_y * virtual_screen.columns); p->code = c; p->fg_color = virtual_screen.fg_color; p->bg_color = virtual_screen.bg_color; p->width = width - 1; p->index = 0; if (width > 1) { unsigned i; for (i = 1; i < width; i++) { p[i].code = ' '; p[i].width = width - 1; p[i].index = i; } } write_char (); virtual_screen.cursor_x += width; if (virtual_screen.cursor_x >= virtual_screen.columns) { virtual_screen.cursor_x = 0; if (virtual_screen.cursor_y >= virtual_screen.rows - 1) scroll_up (); else virtual_screen.cursor_y++; } if (virtual_screen.cursor_state) write_cursor (); } } static grub_ssize_t grub_vesafb_getcharwidth (grub_uint32_t c) { unsigned width; if (! grub_virtual_screen_get_glyph (c, 0, &width)) return 0; return width; } static grub_uint16_t grub_virtual_screen_getwh (void) { return (virtual_screen.columns << 8) | virtual_screen.rows; } static grub_uint16_t grub_virtual_screen_getxy (void) { return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y); } static void grub_vesafb_gotoxy (grub_uint8_t x, grub_uint8_t y) { if (x >= virtual_screen.columns || y >= virtual_screen.rows) { grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", (unsigned) x, (unsigned) y); return; } if (virtual_screen.cursor_state) write_char (); virtual_screen.cursor_x = x; virtual_screen.cursor_y = y; if (virtual_screen.cursor_state) write_cursor (); } static void grub_virtual_screen_cls (void) { grub_uint32_t i; for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) { virtual_screen.text_buffer[i].code = ' '; virtual_screen.text_buffer[i].fg_color = 0; virtual_screen.text_buffer[i].bg_color = 0; virtual_screen.text_buffer[i].width = 0; virtual_screen.text_buffer[i].index = 0; } virtual_screen.cursor_x = virtual_screen.cursor_y = 0; } static void grub_vesafb_cls (void) { grub_virtual_screen_cls (); grub_memset (framebuffer, 0, mode_info.y_resolution * bytes_per_scan_line); } static void grub_virtual_screen_setcolorstate (grub_term_color_state state) { switch (state) { case GRUB_TERM_COLOR_STANDARD: case GRUB_TERM_COLOR_NORMAL: virtual_screen.fg_color = DEFAULT_FG_COLOR; virtual_screen.bg_color = DEFAULT_BG_COLOR; break; case GRUB_TERM_COLOR_HIGHLIGHT: virtual_screen.fg_color = DEFAULT_BG_COLOR; virtual_screen.bg_color = DEFAULT_FG_COLOR; break; default: break; } } static void grub_vesafb_setcursor (int on) { if (virtual_screen.cursor_state != on) { if (virtual_screen.cursor_state) write_char (); else write_cursor (); virtual_screen.cursor_state = on; } } static struct grub_term_output grub_vesafb_term = { .name = "vesafb", .init = grub_vesafb_mod_init, .fini = grub_vesafb_mod_fini, .putchar = grub_vesafb_putchar, .getcharwidth = grub_vesafb_getcharwidth, .getwh = grub_virtual_screen_getwh, .getxy = grub_virtual_screen_getxy, .gotoxy = grub_vesafb_gotoxy, .cls = grub_vesafb_cls, .setcolorstate = grub_virtual_screen_setcolorstate, .setcursor = grub_vesafb_setcursor, .flags = 0, }; GRUB_MOD_INIT(vesafb) { my_mod = mod; grub_term_register_output (&grub_vesafb_term); } GRUB_MOD_FINI(vesafb) { grub_term_unregister_output (&grub_vesafb_term); }