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 -+-