Applied, thanks! Damien Zammit, le lun. 28 oct. 2024 08:09:13 +0000, a ecrit: > When bootloader sets a linear framebuffer mode and passes > the required info to Hurd via multiboot info table, we > can use this framebuffer as is. > Otherwise, fall back to EGA text mode as before. > > This is just the new framebuffer code as a separate commit. > > --- > console-client/fb.c | 638 ++++++++++++++++++++++++++++++++++++++++++++ > console-client/fb.h | 136 ++++++++++ > 2 files changed, 774 insertions(+) > create mode 100644 console-client/fb.c > create mode 100644 console-client/fb.h > > diff --git a/console-client/fb.c b/console-client/fb.c > new file mode 100644 > index 00000000..382b949b > --- /dev/null > +++ b/console-client/fb.c > @@ -0,0 +1,638 @@ > +/* > + Copyright (C) 2024 Free Software Foundation, Inc. > + > + This file is part of the GNU Hurd. > + > + The GNU Hurd 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 2, or (at > + your option) any later version. > + > + The GNU Hurd 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 this program; if not, write to the Free Software > + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ > + > +#include <assert-backtrace.h> > +#include <errno.h> > +#include <error.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <iconv.h> > +#include <argp.h> > +#include <string.h> > +#include <stdint.h> > + > +#include <sys/io.h> > +#include <sys/mman.h> > +#include <sys/fcntl.h> > +#include <pthread.h> > +#include <hurd/console.h> > +#include <device/device.h> > +#include <hurd.h> > +#include <mach.h> > + > +#include "driver.h" > + > +#include "fb.h" > +#include "vga-hw.h" > +#include "vga-support.h" > +#include "bdf.h" > +#include "unicode.h" > + > +/* The font file. */ > +#define DEFAULT_VGA_FONT DEFAULT_VGA_FONT_DIR "vga-system.bdf" > +static char *fb_display_font; > + > +off_t vga_fb; > +int fb_type; > +int fb_width; > +int fb_height; > +int fb_bpp; > +int fb_wc; > +int fb_hc; > + > +static unsigned char question_mark[32] = { > + 0x7E, /* ****** */ > + 0xC3, /* ** ** */ > + 0x99, /* * ** * */ > + 0x99, /* * ** * */ > + 0xF9, /* ***** * */ > + 0xF3, /* **** ** */ > + 0xF3, /* *** *** */ > + 0xE7, /* *** *** */ > + 0xFF, /* ******** */ > + 0xE7, /* *** *** */ > + 0xE7, /* *** *** */ > + 0x7E, /* ****** */ > + 0 > +}; > + > +static struct bdf_glyph qmark = { > + .name = "missing", > + .encoding = 0, > + .internal_encoding = 0, > + .bbox = { 8, 12, 0, 0 }, > + .bitmap = &question_mark[0], > +}; > + > +/* Is set to 1 if the cursor state should be hidden. */ > +static int cursor_hidden; > +static int cursor_state; > +static int cursor_pos_x = 0; > +static int cursor_pos_y = 0; > + > +static int current_width; > +static int current_height; > + > +/* FIXME: inherit previous char colours */ > +static int current_fg = 7; > +static int current_bg = 0; > + > +#define fb_pos(_col, _row) (vga_videomem + fb_bpp/8 * ( (_row) * fb_hc * > disp->width + (_col) * fb_wc )) > +#define CURSOR_GLYPH 0x2581 > +#define CURSOR_COLOUR 7 > + > + > + > +error_t > +fb_get_multiboot_params (void) > +{ > + error_t ret = 0; > + mach_port_t master_device, mbinfo_dev; > + struct multiboot_raw_info mbi; > + char buf[sizeof(struct multiboot_raw_info)]; > + char *bufptr = &buf[0]; > + uint32_t bytes = sizeof(struct multiboot_raw_info); > + uint32_t bytes_read = 0; > + > + ret = get_privileged_ports (NULL, &master_device); > + if (ret) > + goto fail; > + > + ret = device_open (master_device, D_READ, "mbinfo", &mbinfo_dev); > + mach_port_deallocate (mach_task_self (), master_device); > + if (ret) > + goto fail; > + > + ret = device_read (mbinfo_dev, D_READ, 0, bytes, &bufptr, &bytes_read); > + mach_port_deallocate (mach_task_self (), mbinfo_dev); > + if (ret) > + goto fail; > + > + if (bytes_read != bytes) > + goto fail; > + > + memcpy((void *)&mbi, (void *)bufptr, sizeof(struct multiboot_raw_info)); > + > + vga_fb = mbi.fb_info.framebuffer_addr; > + fb_type = mbi.fb_info.framebuffer_type; > + fb_width = mbi.fb_info.framebuffer_width; > + fb_height = mbi.fb_info.framebuffer_height; > + fb_bpp = mbi.fb_info.framebuffer_bpp; > + fb_wc = FONT_PIXELS_W; > + fb_hc = FONT_PIXELS_H; > + return ret; > + > +fail: > + /* Fall back to EGA text mode */ > + fb_type = MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT; > + return ret; > +} > + > +static struct bdf_glyph * > +always_find_glyph(bdf_font_t f, int ch) > +{ > + struct bdf_glyph *g; > + > + g = bdf_find_glyph (f, ch, 0); > + if (!g) > + g = bdf_find_glyph (f, -1, ch); > + if (!g) > + g = &qmark; > + > + return g; > +} > + > +static void > +blit_glyph(bdf_font_t f, char *mem, int ch, uint32_t fg, uint32_t bg, int > width, int bpp) > +{ > + int w, h; > + > + struct bdf_glyph *gl = always_find_glyph(f, ch); > + > + if (bg == -1) > + bg = current_bg; > + if (fg == -1) > + fg = current_fg; > + > + for (h = 0; h < fb_hc; h++) > + { > + for (w = 0; w < fb_wc; w++) > + { > + char *pixel = mem + bpp/8 * (w + width * h); > + uint32_t colour = (gl->bitmap[h] & (1 << (7-w))) ? fg : bg; > + pixel[0] = (colour >> 16) & 0xff; > + pixel[1] = (colour >> 8) & 0xff; > + pixel[2] = colour & 0xff; > + } > + } > +} > + > +static void > +blit_glyph_xor(bdf_font_t f, char *mem, int ch, uint32_t fg, uint32_t bg, > int width, int bpp) > +{ > + int w, h; > + > + struct bdf_glyph *gl = always_find_glyph(f, ch); > + > + if (bg == -1) > + bg = current_bg; > + if (fg == -1) > + fg = current_fg; > + > + for (h = 0; h < fb_hc; h++) > + { > + for (w = 0; w < fb_wc; w++) > + { > + char *pixel = mem + bpp/8 * (w + width * h); > + uint32_t colour = (gl->bitmap[h] & (1 << (7-w))) ? fg : bg; > + pixel[0] ^= (colour >> 16) & 0xff; > + pixel[1] ^= (colour >> 8) & 0xff; > + pixel[2] ^= colour & 0xff; > + } > + } > +} > + > +static error_t > +fb_init(void) > +{ > + error_t err; > + int fd; > + > + fd = open ("/dev/mem", O_RDWR); > + if (fd < 0) > + return errno; > + > + vga_videomem = mmap (0, fb_width * fb_height * fb_bpp/8, PROT_READ | > PROT_WRITE, > + MAP_SHARED, fd, vga_fb); > + err = errno; > + close (fd); > + if (vga_videomem == MAP_FAILED) > + return err; > + > + /* Clear screen */ > + memset (vga_videomem, 0, fb_width * fb_height * fb_bpp/8); > + return 0; > +} > + > +static void > +fb_fini(void) > +{ > + munmap (vga_videomem, fb_width * fb_height * fb_bpp/8); > +} > + > +/* Start the driver. */ > +error_t > +fb_display_start (void *handle) > +{ > + error_t err; > + struct fb_display *disp = handle; > + FILE *font_file; > + > + err = fb_init (); > + if (err) > + return err; > + > +#define LOAD_FONT(x,y,z) \ > + do { > \ > + font_file = fopen (fb_display_##x ?: DEFAULT_VGA_##y, "r"); > \ > + if (font_file) \ > + { > \ > + bdf_error_t bdferr = bdf_read (font_file, &z, NULL); \ > + if (bdferr) \ > + { \ > + z = NULL; \ > + err = ENOSYS; \ > + } \ > + else \ > + bdf_sort_glyphs (z); \ > + fclose (font_file); \ > + } > \ > + else > \ > + err = ENOSYS; \ > + } while (0) > + > + LOAD_FONT (font, FONT, disp->font); > + if (err) > + { > + fb_fini (); > + free (disp); > + return err; > + } > + > + err = driver_add_display (&fb_display_ops, disp); > + if (err) > + { > + fb_fini (); > + free (disp); > + } > + return err; > +} > + > +/* Destroy the display HANDLE. */ > +error_t > +fb_display_fini (void *handle, int force) > +{ > + struct fb_display *disp = handle; > + > + driver_remove_display (&fb_display_ops, disp); > + bdf_destroy (disp->font); > + free (disp); > + fb_fini (); > + free (fb_display_font); > + > + return 0; > +} > + > +uint32_t ansi_colour[8] = { > + 0x000000, /* black */ > + 0xaa0000, /* red */ > + 0x00aa00, /* green */ > + 0xaa5500, /* yellow */ > + 0x0000aa, /* blue */ > + 0xaa00aa, /* magenta */ > + 0x00aaaa, /* cyan */ > + 0xaaaaaa /* white */ > +}; > + > +uint32_t ansi_colour_bold[8] = { > + 0x555555, /* bright black */ > + 0xff5555, /* bright red */ > + 0x55ff55, /* bright green */ > + 0xffff55, /* bright yellow */ > + 0x5555ff, /* bright blue */ > + 0xff55ff, /* bright magenta */ > + 0x55ffff, /* bright cyan */ > + 0xffffff, /* bright white */ > +}; > + > +static void > +hide_mousecursor (struct fb_display *disp) > +{ > + char *oldpos = fb_pos((int)disp->mousecursor.posx, > (int)disp->mousecursor.posy); > + > + if (!disp->mousecursor.visible) > + return; > + > + /* First remove the old cursor. */ > + blit_glyph_xor (disp->font, oldpos, 'X', 0x00ff00, -1, disp->width, > fb_bpp); > + disp->mousecursor.visible = 0; > +} > + > + > +static void > +draw_mousecursor (struct fb_display *disp) > +{ > + char *newpos = fb_pos((int)disp->mousecursor.posx, > (int)disp->mousecursor.posy); > + > + if (disp->mousecursor.visible) > + return; > + > + /* Draw the new cursor. */ > + blit_glyph_xor (disp->font, newpos, 'X', 0x00ff00, -1, disp->width, > fb_bpp); > + > + disp->mousecursor.visible = 1; > +} > + > + > +static void > +hide_cursor(struct fb_display *disp) > +{ > + char *curpos; > + > + if (cursor_hidden) > + return; > + > + /* Remove old cursor */ > + curpos = fb_pos(cursor_pos_x, cursor_pos_y); > + blit_glyph_xor (disp->font, curpos, CURSOR_GLYPH, > ansi_colour[CURSOR_COLOUR], -1, disp->width, fb_bpp); > + cursor_hidden = 1; > +} > + > +static void > +draw_cursor(struct fb_display *disp) > +{ > + char *curpos; > + > + if (!cursor_hidden) > + return; > + > + /* Add new cursor */ > + curpos = fb_pos(cursor_pos_x, cursor_pos_y); > + blit_glyph_xor (disp->font, curpos, CURSOR_GLYPH, > ansi_colour[CURSOR_COLOUR], -1, disp->width, fb_bpp); > + cursor_hidden = 0; > +} > + > +/* Set the cursor's state to STATE on display HANDLE. */ > +static error_t > +fb_display_set_cursor_status (void *handle, uint32_t state) > +{ > + struct fb_display *disp = handle; > + > + cursor_state = state; > + > + if (!state) > + hide_cursor (disp); > + else > + draw_cursor (disp); > + > + return 0; > +} > + > + > +/* Set the cursor's position on display HANDLE to column COL and row > + ROW. */ > +static error_t > +fb_display_set_cursor_pos (void *handle, uint32_t col, uint32_t row) > +{ > + struct fb_display *disp = handle; > + > + /* If the cursor did not move from the character position, don't > + bother about updating the cursor position. */ > + if (cursor_state && (col == cursor_pos_x) && (row == (cursor_pos_y))) > + return 0; > + > + if (cursor_state) > + hide_cursor (disp); > + > + cursor_pos_x = col; > + cursor_pos_y = row; > + > + if (cursor_state) > + draw_cursor (disp); > + > + return 0; > +} > + > +/* Deallocate any scarce resources occupied by the LENGTH characters > + from column COL and row ROW. */ > +static error_t > +fb_display_clear (void *handle, size_t length, uint32_t col, uint32_t row) > +{ > + return 0; > +} > + > +/* Scroll the display by the desired number of lines. The area that becomes > + free will be filled in a subsequent write call. */ > +static error_t > +fb_display_scroll (void *handle, int delta) > +{ > + struct fb_display *disp = handle; > + int pixels, chars; > + uint32_t r; > + > + if (abs(delta) > disp->height/fb_hc) > + return ENOTSUP; > + > + pixels = abs(delta)*fb_hc * disp->width; > + chars = abs(delta) * disp->width/fb_wc; > + > + hide_mousecursor (disp); > + hide_cursor (disp); > + > + /* XXX: If the virtual console is bigger than the physical console it is > + impossible to scroll because the data to scroll is not in memory. */ > + if (current_height > disp->height/fb_hc) > + return ENOTSUP; > + > + if (delta > 0) > + { > + memmove (vga_videomem, vga_videomem + fb_bpp/8 * pixels, > + fb_bpp/8 * disp->width * (disp->height - delta*fb_hc)); > + } > + else > + { > + memmove (vga_videomem + fb_bpp/8 * pixels, vga_videomem, > + fb_bpp/8 * disp->width * (disp->height + delta*fb_hc)); > + } > + > + if (delta > 0) > + { > + r = disp->height/fb_hc - delta; > + memmove (&disp->refmatrix[0][0], &disp->refmatrix[0][0] + chars, > + sizeof (struct fbchr) * disp->width/fb_wc * r); > + } > + else > + { > + r = 0; > + memmove (&disp->refmatrix[0][0] + chars, &disp->refmatrix[0][0], > + sizeof (struct fbchr) * disp->width/fb_wc * (disp->height/fb_hc > + delta)); > + } > + > + return 0; > +} > + > + > + > +/* Write the text STR with LENGTH characters to column COL and row > + ROW. */ > +static error_t > +fb_display_write (void *handle, conchar_t *str, size_t length, > + uint32_t col, uint32_t row) > +{ > + struct fb_display *disp = handle; > + char *pos; > + struct fbchr *refpos = &disp->refmatrix[row][col]; > + char *mouse_cursor_pos; > + > + hide_mousecursor (disp); > + hide_cursor (disp); > + > + /* The starting column is outside the physical screen. */ > + if (disp->width/fb_wc < current_width && col >= disp->width/fb_wc) > + { > + size_t skip = current_width - disp->width/fb_wc; > + str += skip; > + length -= skip; > + col = 0; > + row += 1; > + } > + > + mouse_cursor_pos = fb_pos((int)disp->mousecursor.posx, > (int)disp->mousecursor.posy); > + > + while (length--) > + { > + int charval = str->chr; > + int fg, bg; > + > + /* The virtual console is smaller than the physical screen. */ > + if (col >= current_width) > + { > + size_t skip = disp->width/fb_wc - current_width; > + refpos += skip; > + col = 0; > + row += 1; > + } > + /* The virtual console is bigger than the physical console. */ > + else if (disp->width/fb_wc < current_width && col == disp->width/fb_wc) > + { > + size_t skip = current_width - disp->width/fb_wc; > + str += skip; > + length -= skip; > + col = 0; > + row += 1; > + } > + > + /* The screen is filled until the bottom of the screen. */ > + if (row >= disp->height/fb_hc) > + return 0; > + > + pos = fb_pos(col, row); > + > + /* blit glyph to screen */ > + fg = (str->attr.intensity == CONS_ATTR_INTENSITY_BOLD) > + ? ansi_colour_bold[str->attr.fgcol] > + : ansi_colour[str->attr.fgcol]; > + bg = ansi_colour[str->attr.bgcol]; > + blit_glyph(disp->font, pos, charval, fg, bg, disp->width, fb_bpp); > + > + if (pos == mouse_cursor_pos) > + disp->mousecursor.visible = 0; > + > + refpos->used = 1; > + refpos->chr = charval; > + refpos->fgcol = fg; > + refpos->bgcol = bg; > + refpos++; > + col++; > + > + /* Go to next character. */ > + str++; > + } > + return 0; > +} > + > +static error_t > +fb_set_dimension (void *handle, unsigned int width, unsigned int height) > +{ > + current_width = width; > + current_height = height; > + > + return 0; > +} > + > + > +static error_t > +fb_display_update (void *handle) > +{ > + struct fb_display *disp = handle; > + > + if (disp->mousecursor.enabled) > + draw_mousecursor (disp); > + > + if (cursor_state) > + draw_cursor (disp); > + > + return 0; > +} > + > + > +static error_t > +fb_set_mousecursor_pos (void *handle, float x, float y) > +{ > + struct fb_display *disp = handle; > + > + /* If the mouse did not move from the character position, don't > + bother about updating the cursor position. */ > + if (disp->mousecursor.visible && x == (int) disp->mousecursor.posx > + && y == (int) disp->mousecursor.posy) > + return 0; > + > + if (disp->mousecursor.enabled) > + hide_mousecursor (disp); > + > + disp->mousecursor.posx = x; > + disp->mousecursor.posy = y; > + > + if (disp->mousecursor.enabled) > + draw_mousecursor (disp); > + > + return 0; > +} > + > + > +static error_t > +fb_set_mousecursor_status (void *handle, int status) > +{ > + struct fb_display *disp = handle; > + > + disp->mousecursor.enabled = status; > + if (!status) > + hide_mousecursor (disp); > + else > + draw_mousecursor (disp); > + > + return 0; > +} > + > + > + > +struct display_ops fb_display_ops = > + { > + fb_display_set_cursor_pos, > + fb_display_set_cursor_status, > + fb_display_scroll, > + fb_display_clear, > + fb_display_write, > + fb_display_update, > + NULL, > + NULL, > + fb_set_dimension, > + fb_set_mousecursor_pos, > + fb_set_mousecursor_status > + }; > diff --git a/console-client/fb.h b/console-client/fb.h > new file mode 100644 > index 00000000..3b100d50 > --- /dev/null > +++ b/console-client/fb.h > @@ -0,0 +1,136 @@ > +/* > + Copyright (C) 2024 Free Software Foundation, Inc. > + > + This file is part of the GNU Hurd. > + > + The GNU Hurd 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 2, or (at > + your option) any later version. > + > + The GNU Hurd 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 this program; if not, write to the Free Software > + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ > + > +#ifndef _FB_H_ > +#define _FB_H_ 1 > + > +#include <stdint.h> > +#include "bdf.h" > +#include "display.h" > +#include "vga-hw.h" > + > +#define FB_VIDEO_MEM_MAX_W 1920 > +#define FB_VIDEO_MEM_MAX_H 1080 > +#define FB_VIDEO_MEM_MAX_BPP 32 > + > +#define FONT_PIXELS_W 8 > +#define FONT_PIXELS_H 16 > + > +extern struct display_ops fb_display_ops; > + > +extern off_t vga_fb; > +extern int fb_type; > +extern int fb_width; > +extern int fb_height; > +extern int fb_bpp; > +extern int fb_wc; > +extern int fb_hc; > + > +error_t fb_get_multiboot_params (void); > +error_t fb_display_start (void *handle); > +error_t fb_display_fini (void *handle, int force); > + > +struct multiboot_framebuffer_info { > + uint64_t framebuffer_addr; > + uint32_t framebuffer_pitch; > + uint32_t framebuffer_width; > + uint32_t framebuffer_height; > + uint8_t framebuffer_bpp; > +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 > +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 > +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 > + uint8_t framebuffer_type; > + union > + { > + struct > + { > + uint32_t framebuffer_palette_addr; > + uint16_t framebuffer_palette_num_colors; > + }; > + struct > + { > + uint8_t framebuffer_red_field_position; > + uint8_t framebuffer_red_mask_size; > + uint8_t framebuffer_green_field_position; > + uint8_t framebuffer_green_mask_size; > + uint8_t framebuffer_blue_field_position; > + uint8_t framebuffer_blue_mask_size; > + }; > + }; > +} __attribute__((packed)); > + > +/* > + * Multiboot information structure as passed by the boot loader. > + */ > +struct multiboot_raw_info { > + uint32_t flags; > + uint32_t mem_lower; > + uint32_t mem_upper; > + uint32_t unused0; > + uint32_t cmdline; > + uint32_t mods_count; > + uint32_t mods_addr; > + uint32_t shdr_num; > + uint32_t shdr_size; > + uint32_t shdr_addr; > + uint32_t shdr_strndx; > + uint32_t mmap_length; > + uint32_t mmap_addr; > + uint32_t unused1[9]; > + struct multiboot_framebuffer_info fb_info; > +} __attribute__((packed)); > + > +struct fbchr > +{ > + unsigned int used : 1; > + wchar_t chr; > + unsigned int fgcol: 3; > + unsigned int bgcol: 3; > +}; > + > +typedef struct fb_mousecursor > +{ > + float posx; > + float posy; > + int visible; > + int enabled; > +} fb_mousecursor_t; > + > +struct fb_display > +{ > + /* The font for this display. */ > + bdf_font_t font; > + > + int width; > + int height; > + > + /* The state of the mouse cursor. */ > + fb_mousecursor_t mousecursor; > + > + /* The position of the cursor (in characters) */ > + int cursor_pos_x; > + int cursor_pos_y; > + > + /* Remember for each cell on the display the glyph written to it and > + the colours assigned. 0 means unassigned. */ > + > + struct fbchr refmatrix[FB_VIDEO_MEM_MAX_H / > FONT_PIXELS_H][FB_VIDEO_MEM_MAX_W / FONT_PIXELS_W]; > +}; > + > +#endif > -- > 2.45.2 > > >
-- Samuel <O> Ça peut être une madeleine à sous munitions (avec des composants, par exemple) -+- #runtime -+-