Page MenuHomeFreeBSD

D27420.diff
No OneTemporary

D27420.diff

diff --git a/stand/Makefile b/stand/Makefile
--- a/stand/Makefile
+++ b/stand/Makefile
@@ -25,6 +25,8 @@
S.${MK_LOADER_LUA}+= liblua
S.${MK_LOADER_LUA}+= lua
S.yes+= defaults
+S.yes+= fonts
+S.yes+= images
S.yes+= man
.if ${MK_FORTH} != "no"
diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h
--- a/stand/common/bootstrap.h
+++ b/stand/common/bootstrap.h
@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/linker_set.h>
+#include <stdbool.h>
#include "readin.h"
@@ -119,6 +120,8 @@
};
extern struct console *consoles[];
void cons_probe(void);
+bool cons_update_mode(bool);
+void autoload_font(bool);
/*
* Plug-and-play enumerator/configurator interface.
@@ -258,6 +261,8 @@
struct kernel_module **);
void file_removemetadata(struct preloaded_file *fp);
+vm_offset_t build_font_module(vm_offset_t);
+
/* MI module loaders */
#ifdef __elfN
/* Relocation types. */
diff --git a/stand/common/gfx_fb.h b/stand/common/gfx_fb.h
new file mode 100644
--- /dev/null
+++ b/stand/common/gfx_fb.h
@@ -0,0 +1,274 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2020 Toomas Soome
+ * Copyright 2020 RackTop Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GFX_FB_H
+#define _GFX_FB_H
+
+#include <sys/font.h>
+#include <teken.h>
+#include <stdbool.h>
+#include <machine/metadata.h>
+#include <pnglite.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EDID_MAGIC { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }
+
+struct edid_header {
+ uint8_t header[8]; /* fixed header pattern */
+ uint16_t manufacturer_id;
+ uint16_t product_code;
+ uint32_t serial_number;
+ uint8_t week_of_manufacture;
+ uint8_t year_of_manufacture;
+ uint8_t version;
+ uint8_t revision;
+};
+
+struct edid_basic_display_parameters {
+ uint8_t video_input_parameters;
+ uint8_t max_horizontal_image_size;
+ uint8_t max_vertical_image_size;
+ uint8_t display_gamma;
+ uint8_t supported_features;
+};
+
+struct edid_chromaticity_coordinates {
+ uint8_t red_green_lo;
+ uint8_t blue_white_lo;
+ uint8_t red_x_hi;
+ uint8_t red_y_hi;
+ uint8_t green_x_hi;
+ uint8_t green_y_hi;
+ uint8_t blue_x_hi;
+ uint8_t blue_y_hi;
+ uint8_t white_x_hi;
+ uint8_t white_y_hi;
+};
+
+struct edid_detailed_timings {
+ uint16_t pixel_clock;
+ uint8_t horizontal_active_lo;
+ uint8_t horizontal_blanking_lo;
+ uint8_t horizontal_hi;
+ uint8_t vertical_active_lo;
+ uint8_t vertical_blanking_lo;
+ uint8_t vertical_hi;
+ uint8_t horizontal_sync_offset_lo;
+ uint8_t horizontal_sync_pulse_width_lo;
+ uint8_t vertical_sync_lo;
+ uint8_t sync_hi;
+ uint8_t horizontal_image_size_lo;
+ uint8_t vertical_image_size_lo;
+ uint8_t image_size_hi;
+ uint8_t horizontal_border;
+ uint8_t vertical_border;
+ uint8_t features;
+};
+
+struct vesa_edid_info {
+ struct edid_header header;
+ struct edid_basic_display_parameters display;
+#define EDID_FEATURE_PREFERRED_TIMING_MODE (1 << 1)
+ struct edid_chromaticity_coordinates chromaticity;
+ uint8_t established_timings_1;
+ uint8_t established_timings_2;
+ uint8_t manufacturer_reserved_timings;
+ uint16_t standard_timings[8];
+ struct edid_detailed_timings detailed_timings[4];
+ uint8_t number_of_extensions;
+ uint8_t checksum;
+} __packed;
+
+#define STD_TIMINGS 8
+#define DET_TIMINGS 4
+
+#define HSIZE(x) (((x & 0xff) + 31) * 8)
+#define RATIO(x) ((x & 0xC000) >> 14)
+#define RATIO1_1 0
+/* EDID Ver. 1.3 redefined this */
+#define RATIO16_10 RATIO1_1
+#define RATIO4_3 1
+#define RATIO5_4 2
+#define RATIO16_9 3
+
+/*
+ * Number of pixels and lines is 12-bit int, valid values 0-4095.
+ */
+#define EDID_MAX_PIXELS 4095
+#define EDID_MAX_LINES 4095
+
+#define GET_EDID_INFO_WIDTH(edid_info, timings_num) \
+ ((edid_info)->detailed_timings[(timings_num)].horizontal_active_lo | \
+ (((uint32_t)(edid_info)->detailed_timings[(timings_num)].horizontal_hi & \
+ 0xf0) << 4))
+
+#define GET_EDID_INFO_HEIGHT(edid_info, timings_num) \
+ ((edid_info)->detailed_timings[(timings_num)].vertical_active_lo | \
+ (((uint32_t)(edid_info)->detailed_timings[(timings_num)].vertical_hi & \
+ 0xf0) << 4))
+
+struct resolution {
+ uint32_t width;
+ uint32_t height;
+ TAILQ_ENTRY(resolution) next;
+};
+
+typedef TAILQ_HEAD(edid_resolution, resolution) edid_res_list_t;
+
+struct vesa_flat_panel_info {
+ uint16_t HSize; /* Horizontal Size in Pixels */
+ uint16_t VSize; /* Vertical Size in Lines */
+ uint16_t FPType; /* Flat Panel Type */
+ uint8_t RedBPP; /* Red Bits Per Primary */
+ uint8_t GreenBPP; /* Green Bits Per Primary */
+ uint8_t BlueBPP; /* Blue Bits Per Primary */
+ uint8_t ReservedBPP; /* Reserved Bits Per Primary */
+ uint32_t RsvdOffScrnMemSize; /* Size in KB of Offscreen Memory */
+ uint32_t RsvdOffScrnMemPtr; /* Pointer to reserved offscreen memory */
+ uint8_t Reserved[14]; /* remainder of FPInfo */
+} __packed;
+
+#define COLOR_FORMAT_VGA 0
+#define COLOR_FORMAT_RGB 1
+#define NCOLORS 16
+#define NCMAP 256
+extern uint32_t cmap[NCMAP];
+
+enum FB_TYPE {
+ FB_TEXT = -1,
+ FB_GOP,
+ FB_UGA,
+ FB_VBE
+};
+
+enum COLOR_TYPE {
+ CT_INDEXED,
+ CT_RGB
+};
+
+struct gen_fb {
+ uint64_t fb_addr;
+ uint64_t fb_size;
+ uint32_t fb_height;
+ uint32_t fb_width;
+ uint32_t fb_stride;
+ uint32_t fb_mask_red;
+ uint32_t fb_mask_green;
+ uint32_t fb_mask_blue;
+ uint32_t fb_mask_reserved;
+ uint32_t fb_bpp;
+};
+
+typedef struct teken_gfx {
+ enum FB_TYPE tg_fb_type;
+ enum COLOR_TYPE tg_ctype;
+ unsigned tg_mode;
+ teken_t tg_teken; /* Teken core */
+ teken_pos_t tg_cursor; /* Where cursor was drawn */
+ bool tg_cursor_visible;
+ teken_pos_t tg_tp; /* Terminal dimensions */
+ teken_pos_t tg_origin; /* Point of origin in pixels */
+ uint8_t *tg_glyph; /* Memory for glyph */
+ size_t tg_glyph_size;
+ struct vt_font tg_font;
+ struct gen_fb tg_fb;
+ teken_funcs_t *tg_functions;
+ void *tg_private;
+} teken_gfx_t;
+
+extern font_list_t fonts;
+extern teken_gfx_t gfx_state;
+
+typedef enum {
+ GfxFbBltVideoFill,
+ GfxFbBltVideoToBltBuffer,
+ GfxFbBltBufferToVideo,
+ GfxFbBltVideoToVideo,
+ GfxFbBltOperationMax,
+} GFXFB_BLT_OPERATION;
+
+int gfxfb_blt(void *, GFXFB_BLT_OPERATION, uint32_t, uint32_t,
+ uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+
+int generate_cons_palette(uint32_t *, int, uint32_t, int, uint32_t, int,
+ uint32_t, int);
+bool console_update_mode(bool);
+void setup_font(teken_gfx_t *, teken_unit_t, teken_unit_t);
+uint8_t *font_lookup(const struct vt_font *, teken_char_t,
+ const teken_attr_t *);
+void bios_text_font(bool);
+
+/* teken callbacks. */
+tf_cursor_t gfx_fb_cursor;
+tf_putchar_t gfx_fb_putchar;
+tf_fill_t gfx_fb_fill;
+tf_copy_t gfx_fb_copy;
+tf_param_t gfx_fb_param;
+
+/* Screen buffer element */
+struct text_pixel {
+ teken_char_t c;
+ teken_attr_t a;
+};
+
+extern const int cons_to_vga_colors[NCOLORS];
+
+/* Screen buffer to track changes on the terminal screen. */
+extern struct text_pixel *screen_buffer;
+bool is_same_pixel(struct text_pixel *, struct text_pixel *);
+
+bool gfx_get_edid_resolution(struct vesa_edid_info *, edid_res_list_t *);
+void gfx_framework_init(void);
+void gfx_fb_cons_display(uint32_t, uint32_t, uint32_t, uint32_t, void *);
+void gfx_fb_setpixel(uint32_t, uint32_t);
+void gfx_fb_drawrect(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_term_drawrect(uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_fb_line(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_fb_bezier(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
+ uint32_t);
+
+#define FL_PUTIMAGE_BORDER 0x1
+#define FL_PUTIMAGE_NOSCROLL 0x2
+#define FL_PUTIMAGE_DEBUG 0x80
+
+int gfx_fb_putimage(png_t *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+bool gfx_parse_mode_str(char *, int *, int *, int *);
+void term_image_display(teken_gfx_t *, const teken_rect_t *);
+
+void reset_font_flags(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GFX_FB_H */
diff --git a/stand/common/gfx_fb.c b/stand/common/gfx_fb.c
new file mode 100644
--- /dev/null
+++ b/stand/common/gfx_fb.c
@@ -0,0 +1,2641 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2020 Toomas Soome
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2020 RackTop Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <stand.h>
+#include <teken.h>
+#include <gfx_fb.h>
+#include <sys/font.h>
+#include <sys/stdint.h>
+#include <sys/endian.h>
+#include <pnglite.h>
+#include <bootstrap.h>
+#include <lz4.h>
+#if defined(EFI)
+#include <efi.h>
+#include <efilib.h>
+#else
+#include <vbe.h>
+#endif
+
+/* VGA text mode does use bold font. */
+#if !defined(VGA_8X16_FONT)
+#define VGA_8X16_FONT "/boot/fonts/8x16v.fnt"
+#endif
+#if !defined(DEFAULT_8X16_FONT)
+#define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt"
+#endif
+
+/*
+ * Must be sorted by font size in descending order
+ */
+font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
+
+#define DEFAULT_FONT_DATA font_data_8x16
+extern vt_font_bitmap_data_t font_data_8x16;
+teken_gfx_t gfx_state = { 0 };
+
+static struct {
+ unsigned char r; /* Red percentage value. */
+ unsigned char g; /* Green percentage value. */
+ unsigned char b; /* Blue percentage value. */
+} color_def[NCOLORS] = {
+ {0, 0, 0}, /* black */
+ {50, 0, 0}, /* dark red */
+ {0, 50, 0}, /* dark green */
+ {77, 63, 0}, /* dark yellow */
+ {20, 40, 64}, /* dark blue */
+ {50, 0, 50}, /* dark magenta */
+ {0, 50, 50}, /* dark cyan */
+ {75, 75, 75}, /* light gray */
+
+ {18, 20, 21}, /* dark gray */
+ {100, 0, 0}, /* light red */
+ {0, 100, 0}, /* light green */
+ {100, 100, 0}, /* light yellow */
+ {45, 62, 81}, /* light blue */
+ {100, 0, 100}, /* light magenta */
+ {0, 100, 100}, /* light cyan */
+ {100, 100, 100}, /* white */
+};
+uint32_t cmap[NCMAP];
+
+/*
+ * Between console's palette and VGA's one:
+ * - blue and red are swapped (1 <-> 4)
+ * - yellow and cyan are swapped (3 <-> 6)
+ */
+const int cons_to_vga_colors[NCOLORS] = {
+ 0, 4, 2, 6, 1, 5, 3, 7,
+ 8, 12, 10, 14, 9, 13, 11, 15
+};
+
+static const int vga_to_cons_colors[NCOLORS] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+struct text_pixel *screen_buffer;
+#if defined(EFI)
+static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
+#else
+static struct paletteentry *GlyphBuffer;
+#endif
+static size_t GlyphBufferSize;
+
+static bool insert_font(char *, FONT_FLAGS);
+static int font_set(struct env_var *, int, const void *);
+static void * allocate_glyphbuffer(uint32_t, uint32_t);
+static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool);
+
+/*
+ * Initialize gfx framework.
+ */
+void
+gfx_framework_init(void)
+{
+ /*
+ * Setup font list to have builtin font.
+ */
+ (void) insert_font(NULL, FONT_BUILTIN);
+}
+
+static uint8_t *
+gfx_get_fb_address(void)
+{
+ return (ptov((uint32_t)gfx_state.tg_fb.fb_addr));
+}
+
+/*
+ * Utility function to parse gfx mode line strings.
+ */
+bool
+gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
+{
+ char *p, *end;
+
+ errno = 0;
+ p = str;
+ *x = strtoul(p, &end, 0);
+ if (*x == 0 || errno != 0)
+ return (false);
+ if (*end != 'x')
+ return (false);
+ p = end + 1;
+ *y = strtoul(p, &end, 0);
+ if (*y == 0 || errno != 0)
+ return (false);
+ if (*end != 'x') {
+ *depth = -1; /* auto select */
+ } else {
+ p = end + 1;
+ *depth = strtoul(p, &end, 0);
+ if (*depth == 0 || errno != 0 || *end != '\0')
+ return (false);
+ }
+
+ return (true);
+}
+
+static uint32_t
+rgb_color_map(uint8_t index, uint32_t rmax, int roffset,
+ uint32_t gmax, int goffset, uint32_t bmax, int boffset)
+{
+ uint32_t color, code, gray, level;
+
+ if (index < NCOLORS) {
+#define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
+ return (CF(r, index) | CF(g, index) | CF(b, index));
+#undef CF
+ }
+
+#define CF(_f, _c) ((_f ## max & _c) << _f ## offset)
+ /* 6x6x6 color cube */
+ if (index > 15 && index < 232) {
+ uint32_t red, green, blue;
+
+ for (red = 0; red < 6; red++) {
+ for (green = 0; green < 6; green++) {
+ for (blue = 0; blue < 6; blue++) {
+ code = 16 + (red * 36) +
+ (green * 6) + blue;
+ if (code != index)
+ continue;
+ red = red ? (red * 40 + 55) : 0;
+ green = green ? (green * 40 + 55) : 0;
+ blue = blue ? (blue * 40 + 55) : 0;
+ color = CF(r, red);
+ color |= CF(g, green);
+ color |= CF(b, blue);
+ return (color);
+ }
+ }
+ }
+ }
+
+ /* colors 232-255 are a grayscale ramp */
+ for (gray = 0; gray < 24; gray++) {
+ level = (gray * 10) + 8;
+ code = 232 + gray;
+ if (code == index)
+ break;
+ }
+ return (CF(r, level) | CF(g, level) | CF(b, level));
+#undef CF
+}
+
+/*
+ * Support for color mapping.
+ * For 8, 24 and 32 bit depth, use mask size 8.
+ * 15/16 bit depth needs to use mask size from mode,
+ * or we will lose color information from 32-bit to 15/16 bit translation.
+ */
+uint32_t
+gfx_fb_color_map(uint8_t index)
+{
+ int rmask, gmask, bmask;
+ int roff, goff, boff, bpp;
+
+ roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+ bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
+
+ if (bpp == 2)
+ rmask = gfx_state.tg_fb.fb_mask_red >> roff;
+ else
+ rmask = 0xff;
+
+ if (bpp == 2)
+ gmask = gfx_state.tg_fb.fb_mask_green >> goff;
+ else
+ gmask = 0xff;
+
+ if (bpp == 2)
+ bmask = gfx_state.tg_fb.fb_mask_blue >> boff;
+ else
+ bmask = 0xff;
+
+ return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0));
+}
+
+/* Get indexed color */
+static uint8_t
+rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
+{
+#if !defined(EFI)
+ uint32_t color, best, dist, k;
+ int diff;
+
+ color = 0;
+ best = NCMAP * NCMAP * NCMAP;
+ for (k = 0; k < NCMAP; k++) {
+ diff = r - pe8[k].Red;
+ dist = diff * diff;
+ diff = g - pe8[k].Green;
+ dist += diff * diff;
+ diff = b - pe8[k].Blue;
+ dist += diff * diff;
+
+ if (dist == 0)
+ break;
+ if (dist < best) {
+ color = k;
+ best = dist;
+ }
+ }
+ if (k == NCMAP)
+ k = color;
+ return (k);
+#else
+ (void) r;
+ (void) g;
+ (void) b;
+ return (0);
+#endif
+}
+
+int
+generate_cons_palette(uint32_t *palette, int format,
+ uint32_t rmax, int roffset, uint32_t gmax, int goffset,
+ uint32_t bmax, int boffset)
+{
+ int i;
+
+ switch (format) {
+ case COLOR_FORMAT_VGA:
+ for (i = 0; i < NCOLORS; i++)
+ palette[i] = cons_to_vga_colors[i];
+ for (; i < NCMAP; i++)
+ palette[i] = i;
+ break;
+ case COLOR_FORMAT_RGB:
+ for (i = 0; i < NCMAP; i++)
+ palette[i] = rgb_color_map(i, rmax, roffset,
+ gmax, goffset, bmax, boffset);
+ break;
+ default:
+ return (ENODEV);
+ }
+
+ return (0);
+}
+
+static void
+gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
+{
+
+ if (o >= size)
+ return;
+ *(uint8_t *)(base + o) = v;
+}
+
+static void
+gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
+{
+
+ if (o >= size)
+ return;
+ *(uint16_t *)(base + o) = v;
+}
+
+static void
+gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
+{
+
+ if (o >= size)
+ return;
+ *(uint32_t *)(base + o) = v;
+}
+
+/* Our GFX Block transfer toolkit. */
+static int gfxfb_blt_fill(void *BltBuffer,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height)
+{
+#if defined(EFI)
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
+#else
+ struct paletteentry *p;
+#endif
+ uint32_t data, bpp, pitch, y, x;
+ int roff, goff, boff;
+ size_t size;
+ off_t off;
+ uint8_t *destination;
+
+ if (BltBuffer == NULL)
+ return (EINVAL);
+
+ if (DestinationY + Height > gfx_state.tg_fb.fb_height)
+ return (EINVAL);
+
+ if (DestinationX + Width > gfx_state.tg_fb.fb_width)
+ return (EINVAL);
+
+ if (Width == 0 || Height == 0)
+ return (EINVAL);
+
+ p = BltBuffer;
+ roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+
+ if (gfx_state.tg_fb.fb_bpp == 8) {
+ data = rgb_to_color_index(p->Red, p->Green, p->Blue);
+ } else {
+ data = (p->Red &
+ (gfx_state.tg_fb.fb_mask_red >> roff)) << roff;
+ data |= (p->Green &
+ (gfx_state.tg_fb.fb_mask_green >> goff)) << goff;
+ data |= (p->Blue &
+ (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff;
+ }
+
+ bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
+ pitch = gfx_state.tg_fb.fb_stride * bpp;
+ destination = gfx_get_fb_address();
+ size = gfx_state.tg_fb.fb_size;
+
+ for (y = DestinationY; y < Height + DestinationY; y++) {
+ off = y * pitch + DestinationX * bpp;
+ for (x = 0; x < Width; x++) {
+ switch (bpp) {
+ case 1:
+ gfx_mem_wr1(destination, size, off,
+ (data < NCOLORS) ?
+ cons_to_vga_colors[data] : data);
+ break;
+ case 2:
+ gfx_mem_wr2(destination, size, off, data);
+ break;
+ case 3:
+ gfx_mem_wr1(destination, size, off,
+ (data >> 16) & 0xff);
+ gfx_mem_wr1(destination, size, off + 1,
+ (data >> 8) & 0xff);
+ gfx_mem_wr1(destination, size, off + 2,
+ data & 0xff);
+ break;
+ case 4:
+ gfx_mem_wr4(destination, size, off, data);
+ break;
+ }
+ off += bpp;
+ }
+ }
+
+ return (0);
+}
+
+static int
+gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height, uint32_t Delta)
+{
+#if defined(EFI)
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
+#else
+ struct paletteentry *p;
+#endif
+ uint32_t x, sy, dy;
+ uint32_t bpp, pitch, copybytes;
+ off_t off;
+ uint8_t *source, *destination, *buffer, *sb;
+ uint8_t rm, rp, gm, gp, bm, bp;
+ bool bgra;
+
+ if (BltBuffer == NULL)
+ return (EINVAL);
+
+ if (SourceY + Height >
+ gfx_state.tg_fb.fb_height)
+ return (EINVAL);
+
+ if (SourceX + Width > gfx_state.tg_fb.fb_width)
+ return (EINVAL);
+
+ if (Width == 0 || Height == 0)
+ return (EINVAL);
+
+ if (Delta == 0)
+ Delta = Width * sizeof (*p);
+
+ bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
+ pitch = gfx_state.tg_fb.fb_stride * bpp;
+
+ copybytes = Width * bpp;
+
+ rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+ rm = gfx_state.tg_fb.fb_mask_red >> rp;
+ gm = gfx_state.tg_fb.fb_mask_green >> gp;
+ bm = gfx_state.tg_fb.fb_mask_blue >> bp;
+
+ /* If FB pixel format is BGRA, we can use direct copy. */
+ bgra = bpp == 4 &&
+ ffs(rm) - 1 == 8 && rp == 16 &&
+ ffs(gm) - 1 == 8 && gp == 8 &&
+ ffs(bm) - 1 == 8 && bp == 0;
+
+ if (bgra) {
+ buffer = NULL;
+ } else {
+ buffer = malloc(copybytes);
+ if (buffer == NULL)
+ return (ENOMEM);
+ }
+
+ for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
+ sy++, dy++) {
+ off = sy * pitch + SourceX * bpp;
+ source = gfx_get_fb_address() + off;
+
+ if (bgra) {
+ destination = (uint8_t *)BltBuffer + dy * Delta +
+ DestinationX * sizeof (*p);
+ } else {
+ destination = buffer;
+ }
+
+ bcopy(source, destination, copybytes);
+
+ if (!bgra) {
+ for (x = 0; x < Width; x++) {
+ uint32_t c = 0;
+
+ p = (void *)((uint8_t *)BltBuffer +
+ dy * Delta +
+ (DestinationX + x) * sizeof (*p));
+ sb = buffer + x * bpp;
+ switch (bpp) {
+ case 1:
+ c = *sb;
+ break;
+ case 2:
+ c = *(uint16_t *)sb;
+ break;
+ case 3:
+ c = sb[0] << 16 | sb[1] << 8 | sb[2];
+ break;
+ case 4:
+ c = *(uint32_t *)sb;
+ break;
+ }
+
+ if (bpp == 1) {
+ *(uint32_t *)p = gfx_fb_color_map(
+ (c < 16) ?
+ vga_to_cons_colors[c] : c);
+ } else {
+ p->Red = (c >> rp) & rm;
+ p->Green = (c >> gp) & gm;
+ p->Blue = (c >> bp) & bm;
+ p->Reserved = 0;
+ }
+ }
+ }
+ }
+
+ free(buffer);
+ return (0);
+}
+
+static int
+gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height, uint32_t Delta)
+{
+#if defined(EFI)
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
+#else
+ struct paletteentry *p;
+#endif
+ uint32_t x, sy, dy;
+ uint32_t bpp, pitch, copybytes;
+ off_t off;
+ uint8_t *source, *destination, *buffer;
+ uint8_t rm, rp, gm, gp, bm, bp;
+ bool bgra;
+
+ if (BltBuffer == NULL)
+ return (EINVAL);
+
+ if (DestinationY + Height >
+ gfx_state.tg_fb.fb_height)
+ return (EINVAL);
+
+ if (DestinationX + Width > gfx_state.tg_fb.fb_width)
+ return (EINVAL);
+
+ if (Width == 0 || Height == 0)
+ return (EINVAL);
+
+ if (Delta == 0)
+ Delta = Width * sizeof (*p);
+
+ bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
+ pitch = gfx_state.tg_fb.fb_stride * bpp;
+
+ copybytes = Width * bpp;
+
+ rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+ rm = gfx_state.tg_fb.fb_mask_red >> rp;
+ gm = gfx_state.tg_fb.fb_mask_green >> gp;
+ bm = gfx_state.tg_fb.fb_mask_blue >> bp;
+
+ /* If FB pixel format is BGRA, we can use direct copy. */
+ bgra = bpp == 4 &&
+ ffs(rm) - 1 == 8 && rp == 16 &&
+ ffs(gm) - 1 == 8 && gp == 8 &&
+ ffs(bm) - 1 == 8 && bp == 0;
+
+ if (bgra) {
+ buffer = NULL;
+ } else {
+ buffer = malloc(copybytes);
+ if (buffer == NULL)
+ return (ENOMEM);
+ }
+ for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
+ sy++, dy++) {
+ off = dy * pitch + DestinationX * bpp;
+ destination = gfx_get_fb_address() + off;
+
+ if (bgra) {
+ source = (uint8_t *)BltBuffer + sy * Delta +
+ SourceX * sizeof (*p);
+ } else {
+ for (x = 0; x < Width; x++) {
+ uint32_t c;
+
+ p = (void *)((uint8_t *)BltBuffer +
+ sy * Delta +
+ (SourceX + x) * sizeof (*p));
+ if (bpp == 1) {
+ c = rgb_to_color_index(p->Red,
+ p->Green, p->Blue);
+ } else {
+ c = (p->Red & rm) << rp |
+ (p->Green & gm) << gp |
+ (p->Blue & bm) << bp;
+ }
+ off = x * bpp;
+ switch (bpp) {
+ case 1:
+ gfx_mem_wr1(buffer, copybytes,
+ off, (c < 16) ?
+ cons_to_vga_colors[c] : c);
+ break;
+ case 2:
+ gfx_mem_wr2(buffer, copybytes,
+ off, c);
+ break;
+ case 3:
+ gfx_mem_wr1(buffer, copybytes,
+ off, (c >> 16) & 0xff);
+ gfx_mem_wr1(buffer, copybytes,
+ off + 1, (c >> 8) & 0xff);
+ gfx_mem_wr1(buffer, copybytes,
+ off + 2, c & 0xff);
+ break;
+ case 4:
+ gfx_mem_wr4(buffer, copybytes,
+ x * bpp, c);
+ break;
+ }
+ }
+ source = buffer;
+ }
+
+ bcopy(source, destination, copybytes);
+ }
+
+ free(buffer);
+ return (0);
+}
+
+static int
+gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height)
+{
+ uint32_t bpp, copybytes;
+ int pitch;
+ uint8_t *source, *destination;
+ off_t off;
+
+ if (SourceY + Height >
+ gfx_state.tg_fb.fb_height)
+ return (EINVAL);
+
+ if (SourceX + Width > gfx_state.tg_fb.fb_width)
+ return (EINVAL);
+
+ if (DestinationY + Height >
+ gfx_state.tg_fb.fb_height)
+ return (EINVAL);
+
+ if (DestinationX + Width > gfx_state.tg_fb.fb_width)
+ return (EINVAL);
+
+ if (Width == 0 || Height == 0)
+ return (EINVAL);
+
+ bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
+ pitch = gfx_state.tg_fb.fb_stride * bpp;
+
+ copybytes = Width * bpp;
+
+ off = SourceY * pitch + SourceX * bpp;
+ source = gfx_get_fb_address() + off;
+ off = DestinationY * pitch + DestinationX * bpp;
+ destination = gfx_get_fb_address() + off;
+
+ if ((uintptr_t)destination > (uintptr_t)source) {
+ source += Height * pitch;
+ destination += Height * pitch;
+ pitch = -pitch;
+ }
+
+ while (Height-- > 0) {
+ bcopy(source, destination, copybytes);
+ source += pitch;
+ destination += pitch;
+ }
+
+ return (0);
+}
+
+int
+gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
+ uint32_t SourceX, uint32_t SourceY,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height, uint32_t Delta)
+{
+ int rv;
+#if defined(EFI)
+ EFI_STATUS status;
+ EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private;
+
+ if (gop != NULL && (gop->Mode->Info->PixelFormat == PixelBltOnly ||
+ gfx_state.tg_fb.fb_addr == 0)) {
+ switch (BltOperation) {
+ case GfxFbBltVideoFill:
+ status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
+ SourceX, SourceY, DestinationX, DestinationY,
+ Width, Height, Delta);
+ break;
+
+ case GfxFbBltVideoToBltBuffer:
+ status = gop->Blt(gop, BltBuffer,
+ EfiBltVideoToBltBuffer,
+ SourceX, SourceY, DestinationX, DestinationY,
+ Width, Height, Delta);
+ break;
+
+ case GfxFbBltBufferToVideo:
+ status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
+ SourceX, SourceY, DestinationX, DestinationY,
+ Width, Height, Delta);
+ break;
+
+ case GfxFbBltVideoToVideo:
+ status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
+ SourceX, SourceY, DestinationX, DestinationY,
+ Width, Height, Delta);
+ break;
+
+ default:
+ status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ switch (status) {
+ case EFI_SUCCESS:
+ rv = 0;
+ break;
+
+ case EFI_INVALID_PARAMETER:
+ rv = EINVAL;
+ break;
+
+ case EFI_DEVICE_ERROR:
+ default:
+ rv = EIO;
+ break;
+ }
+
+ return (rv);
+ }
+#endif
+
+ switch (BltOperation) {
+ case GfxFbBltVideoFill:
+ rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
+ Width, Height);
+ break;
+
+ case GfxFbBltVideoToBltBuffer:
+ rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
+ DestinationX, DestinationY, Width, Height, Delta);
+ break;
+
+ case GfxFbBltBufferToVideo:
+ rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
+ DestinationX, DestinationY, Width, Height, Delta);
+ break;
+
+ case GfxFbBltVideoToVideo:
+ rv = gfxfb_blt_video_to_video(SourceX, SourceY,
+ DestinationX, DestinationY, Width, Height);
+ break;
+
+ default:
+ rv = EINVAL;
+ break;
+ }
+ return (rv);
+}
+
+void
+gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
+ const teken_attr_t *a, uint32_t alpha, bool cursor)
+{
+ uint32_t width, height;
+ uint32_t fgc, bgc, bpl, cc, o;
+ int bpp, bit, byte;
+ bool invert = false;
+
+ bpp = 4; /* We only generate BGRA */
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+ bpl = (width + 7) / 8; /* Bytes per source line. */
+
+ fgc = a->ta_fgcolor;
+ bgc = a->ta_bgcolor;
+ if (a->ta_format & TF_BOLD)
+ fgc |= TC_LIGHT;
+ if (a->ta_format & TF_BLINK)
+ bgc |= TC_LIGHT;
+
+ fgc = gfx_fb_color_map(fgc);
+ bgc = gfx_fb_color_map(bgc);
+
+ if (a->ta_format & TF_REVERSE)
+ invert = !invert;
+ if (cursor)
+ invert = !invert;
+ if (invert) {
+ uint32_t tmp;
+
+ tmp = fgc;
+ fgc = bgc;
+ bgc = tmp;
+ }
+
+ alpha = alpha << 24;
+ fgc |= alpha;
+ bgc |= alpha;
+
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ byte = y * bpl + x / 8;
+ bit = 0x80 >> (x % 8);
+ o = y * width * bpp + x * bpp;
+ cc = glyph[byte] & bit ? fgc : bgc;
+
+ gfx_mem_wr4(state->tg_glyph,
+ state->tg_glyph_size, o, cc);
+ }
+ }
+}
+
+/*
+ * Draw prepared glyph on terminal point p.
+ */
+static void
+gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
+{
+ unsigned x, y, width, height;
+
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+ x = state->tg_origin.tp_col + p->tp_col * width;
+ y = state->tg_origin.tp_row + p->tp_row * height;
+
+ gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
+}
+
+/*
+ * Store char with its attribute to buffer and put it on screen.
+ */
+void
+gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
+ const teken_attr_t *a)
+{
+ teken_gfx_t *state = arg;
+ const uint8_t *glyph;
+ int idx;
+
+ idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ gfx_fb_cursor_draw(state, &state->tg_cursor, false);
+
+ screen_buffer[idx].c = c;
+ screen_buffer[idx].a = *a;
+
+ glyph = font_lookup(&state->tg_font, c, a);
+ gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
+ gfx_fb_printchar(state, p);
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ gfx_fb_cursor_draw(state, c, true);
+ }
+}
+
+void
+gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
+ const teken_attr_t *a)
+{
+ teken_gfx_t *state = arg;
+ const uint8_t *glyph;
+ teken_pos_t p;
+ struct text_pixel *row;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ gfx_fb_cursor_draw(state, &state->tg_cursor, false);
+
+ glyph = font_lookup(&state->tg_font, c, a);
+ gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
+
+ for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
+ p.tp_row++) {
+ row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
+ for (p.tp_col = r->tr_begin.tp_col;
+ p.tp_col < r->tr_end.tp_col; p.tp_col++) {
+ row[p.tp_col].c = c;
+ row[p.tp_col].a = *a;
+ gfx_fb_printchar(state, &p);
+ }
+ }
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ gfx_fb_cursor_draw(state, c, true);
+ }
+}
+
+static void
+gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *p, bool on)
+{
+ const uint8_t *glyph;
+ int idx;
+
+ idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+
+ glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
+ &screen_buffer[idx].a);
+ gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
+ gfx_fb_printchar(state, p);
+ state->tg_cursor = *p;
+}
+
+void
+gfx_fb_cursor(void *arg, const teken_pos_t *p)
+{
+ teken_gfx_t *state = arg;
+#if defined(EFI)
+ EFI_TPL tpl;
+
+ tpl = BS->RaiseTPL(TPL_NOTIFY);
+#endif
+
+ /* Switch cursor off in old location and back on in new. */
+ if (state->tg_cursor_visible) {
+ gfx_fb_cursor_draw(state, &state->tg_cursor, false);
+ gfx_fb_cursor_draw(state, p, true);
+ }
+#if defined(EFI)
+ BS->RestoreTPL(tpl);
+#endif
+}
+
+void
+gfx_fb_param(void *arg, int cmd, unsigned int value)
+{
+ teken_gfx_t *state = arg;
+ const teken_pos_t *c;
+
+ switch (cmd) {
+ case TP_SETLOCALCURSOR:
+ /*
+ * 0 means normal (usually block), 1 means hidden, and
+ * 2 means blinking (always block) for compatibility with
+ * syscons. We don't support any changes except hiding,
+ * so must map 2 to 0.
+ */
+ value = (value == 1) ? 0 : 1;
+ /* FALLTHROUGH */
+ case TP_SHOWCURSOR:
+ c = teken_get_cursor(&state->tg_teken);
+ gfx_fb_cursor_draw(state, c, true);
+ if (value != 0)
+ state->tg_cursor_visible = true;
+ else
+ state->tg_cursor_visible = false;
+ break;
+ default:
+ /* Not yet implemented */
+ break;
+ }
+}
+
+bool
+is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
+{
+ if (px1->c != px2->c)
+ return (false);
+
+ /* Is there image stored? */
+ if ((px1->a.ta_format & TF_IMAGE) ||
+ (px2->a.ta_format & TF_IMAGE))
+ return (false);
+
+ if (px1->a.ta_format != px2->a.ta_format)
+ return (false);
+ if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
+ return (false);
+ if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
+ return (false);
+
+ return (true);
+}
+
+static void
+gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
+ const teken_pos_t *d)
+{
+ uint32_t sx, sy, dx, dy, width, height;
+
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+
+ sx = state->tg_origin.tp_col + s->tr_begin.tp_col * width;
+ sy = state->tg_origin.tp_row + s->tr_begin.tp_row * height;
+ dx = state->tg_origin.tp_col + d->tp_col * width;
+ dy = state->tg_origin.tp_row + d->tp_row * height;
+
+ width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
+
+ (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, sx, sy, dx, dy,
+ width, height, 0);
+}
+
+static void
+gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
+{
+ teken_rect_t sr;
+ teken_pos_t dp;
+ unsigned soffset, doffset;
+ bool mark = false;
+ int x;
+
+ soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
+ doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
+
+ for (x = 0; x < ncol; x++) {
+ if (is_same_pixel(&screen_buffer[soffset + x],
+ &screen_buffer[doffset + x])) {
+ if (mark) {
+ gfx_fb_copy_area(state, &sr, &dp);
+ mark = false;
+ }
+ } else {
+ screen_buffer[doffset + x] = screen_buffer[soffset + x];
+ if (mark) {
+ /* update end point */
+ sr.tr_end.tp_col = s->tp_col + x;;
+ } else {
+ /* set up new rectangle */
+ mark = true;
+ sr.tr_begin.tp_col = s->tp_col + x;
+ sr.tr_begin.tp_row = s->tp_row;
+ sr.tr_end.tp_col = s->tp_col + x;
+ sr.tr_end.tp_row = s->tp_row;
+ dp.tp_col = d->tp_col + x;
+ dp.tp_row = d->tp_row;
+ }
+ }
+ }
+ if (mark) {
+ gfx_fb_copy_area(state, &sr, &dp);
+ }
+}
+
+void
+gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
+{
+ teken_gfx_t *state = arg;
+ unsigned doffset, soffset;
+ teken_pos_t d, s;
+ int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
+
+ /*
+ * Copying is a little tricky. We must make sure we do it in
+ * correct order, to make sure we don't overwrite our own data.
+ */
+
+ nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
+ ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
+
+ if (p->tp_row + nrow > state->tg_tp.tp_row ||
+ p->tp_col + ncol > state->tg_tp.tp_col)
+ return;
+
+ soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
+ doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ gfx_fb_cursor_draw(state, &state->tg_cursor, false);
+
+ /*
+ * Copy line by line.
+ */
+ if (doffset <= soffset) {
+ s = r->tr_begin;
+ d = *p;
+ for (y = 0; y < nrow; y++) {
+ s.tp_row = r->tr_begin.tp_row + y;
+ d.tp_row = p->tp_row + y;
+
+ gfx_fb_copy_line(state, ncol, &s, &d);
+ }
+ } else {
+ for (y = nrow - 1; y >= 0; y--) {
+ s.tp_row = r->tr_begin.tp_row + y;
+ d.tp_row = p->tp_row + y;
+
+ gfx_fb_copy_line(state, ncol, &s, &d);
+ }
+ }
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ gfx_fb_cursor_draw(state, c, true);
+ }
+}
+
+/*
+ * Implements alpha blending for RGBA data, could use pixels for arguments,
+ * but byte stream seems more generic.
+ * The generic alpha blending is:
+ * blend = alpha * fg + (1.0 - alpha) * bg.
+ * Since our alpha is not from range [0..1], we scale appropriately.
+ */
+static uint8_t
+alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
+{
+ uint16_t blend, h, l;
+
+ /* trivial corner cases */
+ if (alpha == 0)
+ return (bg);
+ if (alpha == 0xFF)
+ return (fg);
+ blend = (alpha * fg + (0xFF - alpha) * bg);
+ /* Division by 0xFF */
+ h = blend >> 8;
+ l = blend & 0xFF;
+ if (h + l >= 0xFF)
+ h++;
+ return (h);
+}
+
+/*
+ * Implements alpha blending for RGBA data, could use pixels for arguments,
+ * but byte stream seems more generic.
+ * The generic alpha blending is:
+ * blend = alpha * fg + (1.0 - alpha) * bg.
+ * Since our alpha is not from range [0..1], we scale appropriately.
+ */
+static void
+bitmap_cpy(void *dst, void *src, uint32_t size)
+{
+#if defined(EFI)
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
+#else
+ struct paletteentry *ps, *pd;
+#endif
+ uint32_t i;
+ uint8_t a;
+
+ ps = src;
+ pd = dst;
+
+ /*
+ * we only implement alpha blending for depth 32.
+ */
+ for (i = 0; i < size; i ++) {
+ a = ps[i].Reserved;
+ pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
+ pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
+ pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
+ pd[i].Reserved = a;
+ }
+}
+
+static void *
+allocate_glyphbuffer(uint32_t width, uint32_t height)
+{
+ size_t size;
+
+ size = sizeof (*GlyphBuffer) * width * height;
+ if (size != GlyphBufferSize) {
+ free(GlyphBuffer);
+ GlyphBuffer = malloc(size);
+ if (GlyphBuffer == NULL)
+ return (NULL);
+ GlyphBufferSize = size;
+ }
+ return (GlyphBuffer);
+}
+
+void
+gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ void *data)
+{
+#if defined(EFI)
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf;
+#else
+ struct paletteentry *buf;
+#endif
+ size_t size;
+
+ size = width * height * sizeof(*buf);
+
+ /*
+ * Common data to display is glyph, use preallocated
+ * glyph buffer.
+ */
+ if (gfx_state.tg_glyph_size != GlyphBufferSize)
+ (void) allocate_glyphbuffer(width, height);
+
+ if (size == GlyphBufferSize)
+ buf = GlyphBuffer;
+ else
+ buf = malloc(size);
+ if (buf == NULL)
+ return;
+
+ if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
+ width, height, 0) == 0) {
+ bitmap_cpy(buf, data, width * height);
+ (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
+ width, height, 0);
+ }
+ if (buf != GlyphBuffer)
+ free(buf);
+}
+
+/*
+ * Public graphics primitives.
+ */
+
+static int
+isqrt(int num)
+{
+ int res = 0;
+ int bit = 1 << 30;
+
+ /* "bit" starts at the highest power of four <= the argument. */
+ while (bit > num)
+ bit >>= 2;
+
+ while (bit != 0) {
+ if (num >= res + bit) {
+ num -= res + bit;
+ res = (res >> 1) + bit;
+ } else {
+ res >>= 1;
+ }
+ bit >>= 2;
+ }
+ return (res);
+}
+
+/* set pixel in framebuffer using gfx coordinates */
+void
+gfx_fb_setpixel(uint32_t x, uint32_t y)
+{
+ uint32_t c;
+ const teken_attr_t *ap;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ ap = teken_get_curattr(&gfx_state.tg_teken);
+ if (ap->ta_format & TF_REVERSE) {
+ c = ap->ta_bgcolor;
+ if (ap->ta_format & TF_BLINK)
+ c |= TC_LIGHT;
+ } else {
+ c = ap->ta_fgcolor;
+ if (ap->ta_format & TF_BOLD)
+ c |= TC_LIGHT;
+ }
+
+ c = gfx_fb_color_map(c);
+
+ if (x >= gfx_state.tg_fb.fb_width ||
+ y >= gfx_state.tg_fb.fb_height)
+ return;
+
+ gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
+}
+
+/*
+ * draw rectangle in framebuffer using gfx coordinates.
+ * The function is borrowed from vt_fb.c
+ */
+void
+gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
+ uint32_t fill)
+{
+ uint32_t x, y;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ for (y = y1; y <= y2; y++) {
+ if (fill || (y == y1) || (y == y2)) {
+ for (x = x1; x <= x2; x++)
+ gfx_fb_setpixel(x, y);
+ } else {
+ gfx_fb_setpixel(x1, y);
+ gfx_fb_setpixel(x2, y);
+ }
+ }
+}
+
+void
+gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
+{
+ int dx, sx, dy, sy;
+ int err, e2, x2, y2, ed, width;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ width = wd;
+ sx = x0 < x1? 1 : -1;
+ sy = y0 < y1? 1 : -1;
+ dx = x1 > x0? x1 - x0 : x0 - x1;
+ dy = y1 > y0? y1 - y0 : y0 - y1;
+ err = dx + dy;
+ ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
+
+ for (;;) {
+ gfx_fb_setpixel(x0, y0);
+ e2 = err;
+ x2 = x0;
+ if ((e2 << 1) >= -dx) { /* x step */
+ e2 += dy;
+ y2 = y0;
+ while (e2 < ed * width &&
+ (y1 != (uint32_t)y2 || dx > dy)) {
+ y2 += sy;
+ gfx_fb_setpixel(x0, y2);
+ e2 += dx;
+ }
+ if (x0 == x1)
+ break;
+ e2 = err;
+ err -= dy;
+ x0 += sx;
+ }
+ if ((e2 << 1) <= dy) { /* y step */
+ e2 = dx-e2;
+ while (e2 < ed * width &&
+ (x1 != (uint32_t)x2 || dx < dy)) {
+ x2 += sx;
+ gfx_fb_setpixel(x2, y0);
+ e2 += dy;
+ }
+ if (y0 == y1)
+ break;
+ err += dx;
+ y0 += sy;
+ }
+ }
+}
+
+/*
+ * quadratic Bézier curve limited to gradients without sign change.
+ */
+void
+gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
+ uint32_t y2, uint32_t wd)
+{
+ int sx, sy, xx, yy, xy, width;
+ int dx, dy, err, curvature;
+ int i;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ width = wd;
+ sx = x2 - x1;
+ sy = y2 - y1;
+ xx = x0 - x1;
+ yy = y0 - y1;
+ curvature = xx*sy - yy*sx;
+
+ if (sx*sx + sy*sy > xx*xx+yy*yy) {
+ x2 = x0;
+ x0 = sx + x1;
+ y2 = y0;
+ y0 = sy + y1;
+ curvature = -curvature;
+ }
+ if (curvature != 0) {
+ xx += sx;
+ sx = x0 < x2? 1 : -1;
+ xx *= sx;
+ yy += sy;
+ sy = y0 < y2? 1 : -1;
+ yy *= sy;
+ xy = (xx*yy) << 1;
+ xx *= xx;
+ yy *= yy;
+ if (curvature * sx * sy < 0) {
+ xx = -xx;
+ yy = -yy;
+ xy = -xy;
+ curvature = -curvature;
+ }
+ dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
+ dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
+ xx += xx;
+ yy += yy;
+ err = dx + dy + xy;
+ do {
+ for (i = 0; i <= width; i++)
+ gfx_fb_setpixel(x0 + i, y0);
+ if (x0 == x2 && y0 == y2)
+ return; /* last pixel -> curve finished */
+ y1 = 2 * err < dx;
+ if (2 * err > dy) {
+ x0 += sx;
+ dx -= xy;
+ dy += yy;
+ err += dy;
+ }
+ if (y1 != 0) {
+ y0 += sy;
+ dy -= xy;
+ dx += xx;
+ err += dx;
+ }
+ } while (dy < dx); /* gradient negates -> algorithm fails */
+ }
+ gfx_fb_line(x0, y0, x2, y2, width);
+}
+
+/*
+ * draw rectangle using terminal coordinates and current foreground color.
+ */
+void
+gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
+{
+ int x1, y1, x2, y2;
+ int xshift, yshift;
+ int width, i;
+ uint32_t vf_width, vf_height;
+ teken_rect_t r;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ vf_width = gfx_state.tg_font.vf_width;
+ vf_height = gfx_state.tg_font.vf_height;
+ width = vf_width / 4; /* line width */
+ xshift = (vf_width - width) / 2;
+ yshift = (vf_height - width) / 2;
+
+ /* Shift coordinates */
+ if (ux1 != 0)
+ ux1--;
+ if (uy1 != 0)
+ uy1--;
+ ux2--;
+ uy2--;
+
+ /* mark area used in terminal */
+ r.tr_begin.tp_col = ux1;
+ r.tr_begin.tp_row = uy1;
+ r.tr_end.tp_col = ux2 + 1;
+ r.tr_end.tp_row = uy2 + 1;
+
+ term_image_display(&gfx_state, &r);
+
+ /*
+ * Draw horizontal lines width points thick, shifted from outer edge.
+ */
+ x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
+ x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ y2 += vf_height - yshift - width;
+ gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
+
+ /*
+ * Draw vertical lines width points thick, shifted from outer edge.
+ */
+ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height;
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
+ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ x1 += vf_width - xshift - width;
+ gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
+
+ /* Draw upper left corner. */
+ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height;
+
+ x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
+ x2 += vf_width;
+ y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
+
+ /* Draw lower left corner. */
+ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
+ x1 += vf_width;
+ y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height - yshift;
+ x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
+
+ /* Draw upper right corner. */
+ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
+ x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ x2 += vf_width - xshift - width;
+ y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
+ y2 += vf_height;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
+
+ /* Draw lower right corner. */
+ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height - yshift;
+ x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ x2 += vf_width - xshift - width;
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
+}
+
+int
+gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
+ uint32_t uy2, uint32_t flags)
+{
+#if defined(EFI)
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
+#else
+ struct paletteentry *p;
+#endif
+ uint8_t *data;
+ uint32_t i, j, x, y, fheight, fwidth;
+ int rs, gs, bs;
+ uint8_t r, g, b, a;
+ bool scale = false;
+ bool trace = false;
+ teken_rect_t rect;
+
+ trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
+
+ if (gfx_state.tg_fb_type == FB_TEXT) {
+ if (trace)
+ printf("Framebuffer not active.\n");
+ return (1);
+ }
+
+ if (png->color_type != PNG_TRUECOLOR_ALPHA) {
+ if (trace)
+ printf("Not truecolor image.\n");
+ return (1);
+ }
+
+ if (ux1 > gfx_state.tg_fb.fb_width ||
+ uy1 > gfx_state.tg_fb.fb_height) {
+ if (trace)
+ printf("Top left coordinate off screen.\n");
+ return (1);
+ }
+
+ if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
+ if (trace)
+ printf("Image too large.\n");
+ return (1);
+ }
+
+ if (png->width < 1 || png->height < 1) {
+ if (trace)
+ printf("Image too small.\n");
+ return (1);
+ }
+
+ /*
+ * If 0 was passed for either ux2 or uy2, then calculate the missing
+ * part of the bottom right coordinate.
+ */
+ scale = true;
+ if (ux2 == 0 && uy2 == 0) {
+ /* Both 0, use the native resolution of the image */
+ ux2 = ux1 + png->width;
+ uy2 = uy1 + png->height;
+ scale = false;
+ } else if (ux2 == 0) {
+ /* Set ux2 from uy2/uy1 to maintain aspect ratio */
+ ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
+ } else if (uy2 == 0) {
+ /* Set uy2 from ux2/ux1 to maintain aspect ratio */
+ uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
+ }
+
+ if (ux2 > gfx_state.tg_fb.fb_width ||
+ uy2 > gfx_state.tg_fb.fb_height) {
+ if (trace)
+ printf("Bottom right coordinate off screen.\n");
+ return (1);
+ }
+
+ fwidth = ux2 - ux1;
+ fheight = uy2 - uy1;
+
+ /*
+ * If the original image dimensions have been passed explicitly,
+ * disable scaling.
+ */
+ if (fwidth == png->width && fheight == png->height)
+ scale = false;
+
+ if (ux1 == 0) {
+ /*
+ * No top left X co-ordinate (real coordinates start at 1),
+ * place as far right as it will fit.
+ */
+ ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
+ ux1 = ux2 - fwidth;
+ }
+
+ if (uy1 == 0) {
+ /*
+ * No top left Y co-ordinate (real coordinates start at 1),
+ * place as far down as it will fit.
+ */
+ uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
+ uy1 = uy2 - fheight;
+ }
+
+ if (ux1 >= ux2 || uy1 >= uy2) {
+ if (trace)
+ printf("Image dimensions reversed.\n");
+ return (1);
+ }
+
+ if (fwidth < 2 || fheight < 2) {
+ if (trace)
+ printf("Target area too small\n");
+ return (1);
+ }
+
+ if (trace)
+ printf("Image %ux%u -> %ux%u @%ux%u\n",
+ png->width, png->height, fwidth, fheight, ux1, uy1);
+
+ rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
+ rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
+ rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
+ rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
+
+ /*
+ * mark area used in terminal
+ */
+ if (!(flags & FL_PUTIMAGE_NOSCROLL))
+ term_image_display(&gfx_state, &rect);
+
+ if ((flags & FL_PUTIMAGE_BORDER))
+ gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
+
+ data = malloc(fwidth * fheight * sizeof(*p));
+ p = (void *)data;
+ if (data == NULL) {
+ if (trace)
+ printf("Out of memory.\n");
+ return (1);
+ }
+
+ /*
+ * Build image for our framebuffer.
+ */
+
+ /* Helper to calculate the pixel index from the source png */
+#define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
+
+ /*
+ * For each of the x and y directions, calculate the number of pixels
+ * in the source image that correspond to a single pixel in the target.
+ * Use fixed-point arithmetic with 16-bits for each of the integer and
+ * fractional parts.
+ */
+ const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
+ const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
+
+ rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
+ ffs(gfx_state.tg_fb.fb_mask_red) + 1);
+ gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
+ ffs(gfx_state.tg_fb.fb_mask_green) + 1);
+ bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
+ ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
+
+ uint32_t hc = 0;
+ for (y = 0; y < fheight; y++) {
+ uint32_t hc2 = (hc >> 9) & 0x7f;
+ uint32_t hc1 = 0x80 - hc2;
+
+ uint32_t offset_y = hc >> 16;
+ uint32_t offset_y1 = offset_y + 1;
+
+ uint32_t wc = 0;
+ for (x = 0; x < fwidth; x++) {
+ uint32_t wc2 = (wc >> 9) & 0x7f;
+ uint32_t wc1 = 0x80 - wc2;
+
+ uint32_t offset_x = wc >> 16;
+ uint32_t offset_x1 = offset_x + 1;
+
+ /* Target pixel index */
+ j = y * fwidth + x;
+
+ if (!scale) {
+ i = GETPIXEL(x, y);
+ r = png->image[i];
+ g = png->image[i + 1];
+ b = png->image[i + 2];
+ a = png->image[i + 3];
+ } else {
+ uint8_t pixel[4];
+
+ uint32_t p00 = GETPIXEL(offset_x, offset_y);
+ uint32_t p01 = GETPIXEL(offset_x, offset_y1);
+ uint32_t p10 = GETPIXEL(offset_x1, offset_y);
+ uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
+
+ /*
+ * Given a 2x2 array of pixels in the source
+ * image, combine them to produce a single
+ * value for the pixel in the target image.
+ * Each column of pixels is combined using
+ * a weighted average where the top and bottom
+ * pixels contribute hc1 and hc2 respectively.
+ * The calculation for bottom pixel pB and
+ * top pixel pT is:
+ * (pT * hc1 + pB * hc2) / (hc1 + hc2)
+ * Once the values are determined for the two
+ * columns of pixels, then the columns are
+ * averaged together in the same way but using
+ * wc1 and wc2 for the weightings.
+ *
+ * Since hc1 and hc2 are chosen so that
+ * hc1 + hc2 == 128 (and same for wc1 + wc2),
+ * the >> 14 below is a quick way to divide by
+ * (hc1 + hc2) * (wc1 + wc2)
+ */
+ for (i = 0; i < 4; i++)
+ pixel[i] = (
+ (png->image[p00 + i] * hc1 +
+ png->image[p01 + i] * hc2) * wc1 +
+ (png->image[p10 + i] * hc1 +
+ png->image[p11 + i] * hc2) * wc2)
+ >> 14;
+
+ r = pixel[0];
+ g = pixel[1];
+ b = pixel[2];
+ a = pixel[3];
+ }
+
+ if (trace)
+ printf("r/g/b: %x/%x/%x\n", r, g, b);
+ /*
+ * Rough colorspace reduction for 15/16 bit colors.
+ */
+ p[j].Red = r >> rs;
+ p[j].Green = g >> gs;
+ p[j].Blue = b >> bs;
+ p[j].Reserved = a;
+
+ wc += wcstep;
+ }
+ hc += hcstep;
+ }
+
+ gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
+ free(data);
+ return (0);
+}
+
+/*
+ * Reset font flags to FONT_AUTO.
+ */
+void
+reset_font_flags(void)
+{
+ struct fontlist *fl;
+
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ fl->font_flags = FONT_AUTO;
+ }
+}
+
+static vt_font_bitmap_data_t *
+set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
+{
+ vt_font_bitmap_data_t *font = NULL;
+ struct fontlist *fl;
+ unsigned height = h;
+ unsigned width = w;
+
+ /*
+ * First check for manually loaded font.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_flags == FONT_MANUAL) {
+ font = fl->font_data;
+ if (font->vfbd_font == NULL && fl->font_load != NULL &&
+ fl->font_name != NULL) {
+ font = fl->font_load(fl->font_name);
+ }
+ if (font == NULL || font->vfbd_font == NULL)
+ font = NULL;
+ break;
+ }
+ }
+
+ if (font != NULL) {
+ *rows = (height - BORDER_PIXELS) / font->vfbd_height;
+ *cols = (width - BORDER_PIXELS) / font->vfbd_width;
+ return (font);
+ }
+
+ /*
+ * Find best font for these dimensions, or use default
+ *
+ * A 1 pixel border is the absolute minimum we could have
+ * as a border around the text window (BORDER_PIXELS = 2),
+ * however a slightly larger border not only looks better
+ * but for the fonts currently statically built into the
+ * emulator causes much better font selection for the
+ * normal range of screen resolutions.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ font = fl->font_data;
+ if ((((*rows * font->vfbd_height) + BORDER_PIXELS) <= height) &&
+ (((*cols * font->vfbd_width) + BORDER_PIXELS) <= width)) {
+ if (font->vfbd_font == NULL ||
+ fl->font_flags == FONT_RELOAD) {
+ if (fl->font_load != NULL &&
+ fl->font_name != NULL) {
+ font = fl->font_load(fl->font_name);
+ }
+ if (font == NULL)
+ continue;
+ }
+ *rows = (height - BORDER_PIXELS) / font->vfbd_height;
+ *cols = (width - BORDER_PIXELS) / font->vfbd_width;
+ break;
+ }
+ font = NULL;
+ }
+
+ if (font == NULL) {
+ /*
+ * We have fonts sorted smallest last, try it before
+ * falling back to builtin.
+ */
+ fl = STAILQ_LAST(&fonts, fontlist, font_next);
+ if (fl != NULL && fl->font_load != NULL &&
+ fl->font_name != NULL) {
+ font = fl->font_load(fl->font_name);
+ }
+ if (font == NULL)
+ font = &DEFAULT_FONT_DATA;
+
+ *rows = (height - BORDER_PIXELS) / font->vfbd_height;
+ *cols = (width - BORDER_PIXELS) / font->vfbd_width;
+ }
+
+ return (font);
+}
+
+static void
+cons_clear(void)
+{
+ char clear[] = { '\033', 'c' };
+
+ /* Reset terminal */
+ teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
+ gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
+}
+
+void
+setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
+{
+ vt_font_bitmap_data_t *font_data;
+ teken_pos_t *tp = &state->tg_tp;
+ char env[8];
+ int i;
+
+ /*
+ * set_font() will select a appropriate sized font for
+ * the number of rows and columns selected. If we don't
+ * have a font that will fit, then it will use the
+ * default builtin font and adjust the rows and columns
+ * to fit on the screen.
+ */
+ font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
+
+ if (font_data == NULL)
+ panic("out of memory");
+
+ for (i = 0; i < VFNT_MAPS; i++) {
+ state->tg_font.vf_map[i] =
+ font_data->vfbd_font->vf_map[i];
+ state->tg_font.vf_map_count[i] =
+ font_data->vfbd_font->vf_map_count[i];
+ }
+
+ state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
+ state->tg_font.vf_height = font_data->vfbd_font->vf_height;
+ state->tg_font.vf_width = font_data->vfbd_font->vf_width;
+
+ snprintf(env, sizeof (env), "%ux%u",
+ state->tg_font.vf_width, state->tg_font.vf_height);
+ env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
+ env, font_set, env_nounset);
+}
+
+/* Binary search for the glyph. Return 0 if not found. */
+static uint16_t
+font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
+{
+ unsigned min, mid, max;
+
+ min = 0;
+ max = len - 1;
+
+ /* Empty font map. */
+ if (len == 0)
+ return (0);
+ /* Character below minimal entry. */
+ if (src < map[0].vfm_src)
+ return (0);
+ /* Optimization: ASCII characters occur very often. */
+ if (src <= map[0].vfm_src + map[0].vfm_len)
+ return (src - map[0].vfm_src + map[0].vfm_dst);
+ /* Character above maximum entry. */
+ if (src > map[max].vfm_src + map[max].vfm_len)
+ return (0);
+
+ /* Binary search. */
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (src < map[mid].vfm_src)
+ max = mid - 1;
+ else if (src > map[mid].vfm_src + map[mid].vfm_len)
+ min = mid + 1;
+ else
+ return (src - map[mid].vfm_src + map[mid].vfm_dst);
+ }
+
+ return (0);
+}
+
+/*
+ * Return glyph bitmap. If glyph is not found, we will return bitmap
+ * for the first (offset 0) glyph.
+ */
+uint8_t *
+font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
+{
+ uint16_t dst;
+ size_t stride;
+
+ /* Substitute bold with normal if not found. */
+ if (a->ta_format & TF_BOLD) {
+ dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
+ vf->vf_map_count[VFNT_MAP_BOLD], c);
+ if (dst != 0)
+ goto found;
+ }
+ dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
+ vf->vf_map_count[VFNT_MAP_NORMAL], c);
+
+found:
+ stride = howmany(vf->vf_width, 8) * vf->vf_height;
+ return (&vf->vf_bytes[dst * stride]);
+}
+
+static int
+load_mapping(int fd, struct vt_font *fp, int n)
+{
+ size_t i, size;
+ ssize_t rv;
+ vfnt_map_t *mp;
+
+ if (fp->vf_map_count[n] == 0)
+ return (0);
+
+ size = fp->vf_map_count[n] * sizeof(*mp);
+ mp = malloc(size);
+ if (mp == NULL)
+ return (ENOMEM);
+ fp->vf_map[n] = mp;
+
+ rv = read(fd, mp, size);
+ if (rv < 0 || (size_t)rv != size) {
+ free(fp->vf_map[n]);
+ fp->vf_map[n] = NULL;
+ return (EIO);
+ }
+
+ for (i = 0; i < fp->vf_map_count[n]; i++) {
+ mp[i].vfm_src = be32toh(mp[i].vfm_src);
+ mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
+ mp[i].vfm_len = be16toh(mp[i].vfm_len);
+ }
+ return (0);
+}
+
+static int
+builtin_mapping(struct vt_font *fp, int n)
+{
+ size_t size;
+ struct vfnt_map *mp;
+
+ if (n >= VFNT_MAPS)
+ return (EINVAL);
+
+ if (fp->vf_map_count[n] == 0)
+ return (0);
+
+ size = fp->vf_map_count[n] * sizeof(*mp);
+ mp = malloc(size);
+ if (mp == NULL)
+ return (ENOMEM);
+ fp->vf_map[n] = mp;
+
+ memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
+ return (0);
+}
+
+/*
+ * Load font from builtin or from file.
+ * We do need special case for builtin because the builtin font glyphs
+ * are compressed and we do need to uncompress them.
+ * Having single load_font() for both cases will help us to simplify
+ * font switch handling.
+ */
+static vt_font_bitmap_data_t *
+load_font(char *path)
+{
+ int fd, i;
+ uint32_t glyphs;
+ struct font_header fh;
+ struct fontlist *fl;
+ vt_font_bitmap_data_t *bp;
+ struct vt_font *fp;
+ size_t size;
+ ssize_t rv;
+
+ /* Get our entry from the font list. */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (strcmp(fl->font_name, path) == 0)
+ break;
+ }
+ if (fl == NULL)
+ return (NULL); /* Should not happen. */
+
+ bp = fl->font_data;
+ if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
+ return (bp);
+
+ fd = -1;
+ /*
+ * Special case for builtin font.
+ * Builtin font is the very first font we load, we do not have
+ * previous loads to be released.
+ */
+ if (fl->font_flags == FONT_BUILTIN) {
+ if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
+ return (NULL);
+
+ fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
+ fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
+
+ fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
+ if (fp->vf_bytes == NULL) {
+ free(fp);
+ return (NULL);
+ }
+
+ bp->vfbd_uncompressed_size =
+ DEFAULT_FONT_DATA.vfbd_uncompressed_size;
+ bp->vfbd_compressed_size =
+ DEFAULT_FONT_DATA.vfbd_compressed_size;
+
+ if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
+ fp->vf_bytes,
+ DEFAULT_FONT_DATA.vfbd_compressed_size,
+ DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
+ free(fp->vf_bytes);
+ free(fp);
+ return (NULL);
+ }
+
+ for (i = 0; i < VFNT_MAPS; i++) {
+ fp->vf_map_count[i] =
+ DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
+ if (builtin_mapping(fp, i) != 0)
+ goto free_done;
+ }
+
+ bp->vfbd_font = fp;
+ return (bp);
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+
+ size = sizeof(fh);
+ rv = read(fd, &fh, size);
+ if (rv < 0 || (size_t)rv != size) {
+ bp = NULL;
+ goto done;
+ }
+ if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
+ bp = NULL;
+ goto done;
+ }
+ if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
+ bp = NULL;
+ goto done;
+ }
+ for (i = 0; i < VFNT_MAPS; i++)
+ fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
+
+ glyphs = be32toh(fh.fh_glyph_count);
+ fp->vf_width = fh.fh_width;
+ fp->vf_height = fh.fh_height;
+
+ size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
+ bp->vfbd_uncompressed_size = size;
+ if ((fp->vf_bytes = malloc(size)) == NULL)
+ goto free_done;
+
+ rv = read(fd, fp->vf_bytes, size);
+ if (rv < 0 || (size_t)rv != size)
+ goto free_done;
+ for (i = 0; i < VFNT_MAPS; i++) {
+ if (load_mapping(fd, fp, i) != 0)
+ goto free_done;
+ }
+
+ /*
+ * Reset builtin flag now as we have full font loaded.
+ */
+ if (fl->font_flags == FONT_BUILTIN)
+ fl->font_flags = FONT_AUTO;
+
+ /*
+ * Release previously loaded entries. We can do this now, as
+ * the new font is loaded. Note, there can be no console
+ * output till the new font is in place and teken is notified.
+ * We do need to keep fl->font_data for glyph dimensions.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_data->vfbd_font == NULL)
+ continue;
+
+ for (i = 0; i < VFNT_MAPS; i++)
+ free(fl->font_data->vfbd_font->vf_map[i]);
+ free(fl->font_data->vfbd_font->vf_bytes);
+ free(fl->font_data->vfbd_font);
+ fl->font_data->vfbd_font = NULL;
+ }
+
+ bp->vfbd_font = fp;
+ bp->vfbd_compressed_size = 0;
+
+done:
+ if (fd != -1)
+ close(fd);
+ return (bp);
+
+free_done:
+ for (i = 0; i < VFNT_MAPS; i++)
+ free(fp->vf_map[i]);
+ free(fp->vf_bytes);
+ free(fp);
+ bp = NULL;
+ goto done;
+}
+
+struct name_entry {
+ char *n_name;
+ SLIST_ENTRY(name_entry) n_entry;
+};
+
+SLIST_HEAD(name_list, name_entry);
+
+/* Read font names from index file. */
+static struct name_list *
+read_list(char *fonts)
+{
+ struct name_list *nl;
+ struct name_entry *np;
+ char *dir, *ptr;
+ char buf[PATH_MAX];
+ int fd, len;
+
+ dir = strdup(fonts);
+ if (dir == NULL)
+ return (NULL);
+
+ ptr = strrchr(dir, '/');
+ *ptr = '\0';
+
+ fd = open(fonts, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+
+ nl = malloc(sizeof(*nl));
+ if (nl == NULL) {
+ close(fd);
+ return (nl);
+ }
+
+ SLIST_INIT(nl);
+ while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
+ if (*buf == '#' || *buf == '\0')
+ continue;
+
+ if (bcmp(buf, "MENU", 4) == 0)
+ continue;
+
+ if (bcmp(buf, "FONT", 4) == 0)
+ continue;
+
+ ptr = strchr(buf, ':');
+ if (ptr == NULL)
+ continue;
+ else
+ *ptr = '\0';
+
+ np = malloc(sizeof(*np));
+ if (np == NULL) {
+ close(fd);
+ return (nl); /* return what we have */
+ }
+ if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
+ free(np);
+ close(fd);
+ return (nl); /* return what we have */
+ }
+ SLIST_INSERT_HEAD(nl, np, n_entry);
+ }
+ close(fd);
+ return (nl);
+}
+
+/*
+ * Read the font properties and insert new entry into the list.
+ * The font list is built in descending order.
+ */
+static bool
+insert_font(char *name, FONT_FLAGS flags)
+{
+ struct font_header fh;
+ struct fontlist *fp, *previous, *entry, *next;
+ size_t size;
+ ssize_t rv;
+ int fd;
+ char *font_name;
+
+ font_name = NULL;
+ if (flags == FONT_BUILTIN) {
+ /*
+ * We only install builtin font once, while setting up
+ * initial console. Since this will happen very early,
+ * we assume asprintf will not fail. Once we have access to
+ * files, the builtin font will be replaced by font loaded
+ * from file.
+ */
+ if (!STAILQ_EMPTY(&fonts))
+ return (false);
+
+ fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
+ fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
+
+ (void) asprintf(&font_name, "%dx%d",
+ DEFAULT_FONT_DATA.vfbd_width,
+ DEFAULT_FONT_DATA.vfbd_height);
+ } else {
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return (false);
+ rv = read(fd, &fh, sizeof(fh));
+ close(fd);
+ if (rv < 0 || (size_t)rv != sizeof(fh))
+ return (false);
+
+ if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
+ sizeof(fh.fh_magic)) != 0)
+ return (false);
+ font_name = strdup(name);
+ }
+
+ if (font_name == NULL)
+ return (false);
+
+ /*
+ * If we have an entry with the same glyph dimensions, replace
+ * the file name and mark us. We only support unique dimensions.
+ */
+ STAILQ_FOREACH(entry, &fonts, font_next) {
+ if (fh.fh_width == entry->font_data->vfbd_width &&
+ fh.fh_height == entry->font_data->vfbd_height) {
+ free(entry->font_name);
+ entry->font_name = font_name;
+ entry->font_flags = FONT_RELOAD;
+ return (true);
+ }
+ }
+
+ fp = calloc(sizeof(*fp), 1);
+ if (fp == NULL) {
+ free(font_name);
+ return (false);
+ }
+ fp->font_data = calloc(sizeof(*fp->font_data), 1);
+ if (fp->font_data == NULL) {
+ free(font_name);
+ free(fp);
+ return (false);
+ }
+ fp->font_name = font_name;
+ fp->font_flags = flags;
+ fp->font_load = load_font;
+ fp->font_data->vfbd_width = fh.fh_width;
+ fp->font_data->vfbd_height = fh.fh_height;
+
+ if (STAILQ_EMPTY(&fonts)) {
+ STAILQ_INSERT_HEAD(&fonts, fp, font_next);
+ return (true);
+ }
+
+ previous = NULL;
+ size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
+
+ STAILQ_FOREACH(entry, &fonts, font_next) {
+ vt_font_bitmap_data_t *bd;
+
+ bd = entry->font_data;
+ /* Should fp be inserted before the entry? */
+ if (size > bd->vfbd_width * bd->vfbd_height) {
+ if (previous == NULL) {
+ STAILQ_INSERT_HEAD(&fonts, fp, font_next);
+ } else {
+ STAILQ_INSERT_AFTER(&fonts, previous, fp,
+ font_next);
+ }
+ return (true);
+ }
+ next = STAILQ_NEXT(entry, font_next);
+ if (next == NULL ||
+ size > next->font_data->vfbd_width *
+ next->font_data->vfbd_height) {
+ STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
+ return (true);
+ }
+ previous = entry;
+ }
+ return (true);
+}
+
+static int
+font_set(struct env_var *ev __unused, int flags __unused, const void *value)
+{
+ struct fontlist *fl;
+ char *eptr;
+ unsigned long x = 0, y = 0;
+
+ /*
+ * Attempt to extract values from "XxY" string. In case of error,
+ * we have unmaching glyph dimensions and will just output the
+ * available values.
+ */
+ if (value != NULL) {
+ x = strtoul(value, &eptr, 10);
+ if (*eptr == 'x')
+ y = strtoul(eptr + 1, &eptr, 10);
+ }
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_data->vfbd_width == x &&
+ fl->font_data->vfbd_height == y)
+ break;
+ }
+ if (fl != NULL) {
+ /* Reset any FONT_MANUAL flag. */
+ reset_font_flags();
+
+ /* Mark this font manually loaded */
+ fl->font_flags = FONT_MANUAL;
+ cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+ return (CMD_OK);
+ }
+
+ printf("Available fonts:\n");
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ printf(" %dx%d\n", fl->font_data->vfbd_width,
+ fl->font_data->vfbd_height);
+ }
+ return (CMD_OK);
+}
+
+void
+bios_text_font(bool use_vga_font)
+{
+ if (use_vga_font)
+ (void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
+ else
+ (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
+}
+
+void
+autoload_font(bool bios)
+{
+ struct name_list *nl;
+ struct name_entry *np;
+
+ nl = read_list("/boot/fonts/INDEX.fonts");
+ if (nl == NULL)
+ return;
+
+ while (!SLIST_EMPTY(nl)) {
+ np = SLIST_FIRST(nl);
+ SLIST_REMOVE_HEAD(nl, n_entry);
+ if (insert_font(np->n_name, FONT_AUTO) == false)
+ printf("failed to add font: %s\n", np->n_name);
+ free(np->n_name);
+ free(np);
+ }
+
+ /*
+ * If vga text mode was requested, load vga.font (8x16 bold) font.
+ */
+ if (bios) {
+ bios_text_font(true);
+ }
+
+ (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+}
+
+COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
+
+static int
+command_font(int argc, char *argv[])
+{
+ int i, c, rc;
+ struct fontlist *fl;
+ vt_font_bitmap_data_t *bd;
+ bool list;
+
+ list = false;
+ optind = 1;
+ optreset = 1;
+ rc = CMD_OK;
+
+ while ((c = getopt(argc, argv, "l")) != -1) {
+ switch (c) {
+ case 'l':
+ list = true;
+ break;
+ case '?':
+ default:
+ return (CMD_ERROR);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1 || (list && argc != 0)) {
+ printf("Usage: loadfont [-l] | [file.fnt]\n");
+ return (CMD_ERROR);
+ }
+
+ if (list) {
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ printf("font %s: %dx%d%s\n", fl->font_name,
+ fl->font_data->vfbd_width,
+ fl->font_data->vfbd_height,
+ fl->font_data->vfbd_font == NULL? "" : " loaded");
+ }
+ return (CMD_OK);
+ }
+
+ /* Clear scren */
+ cons_clear();
+
+ if (argc == 1) {
+ char *name = argv[0];
+
+ if (insert_font(name, FONT_MANUAL) == false) {
+ printf("loadfont error: failed to load: %s\n", name);
+ return (CMD_ERROR);
+ }
+
+ (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+ return (CMD_OK);
+ }
+
+ if (argc == 0) {
+ /*
+ * Walk entire font list, release any loaded font, and set
+ * autoload flag. The font list does have at least the builtin
+ * default font.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_data->vfbd_font != NULL) {
+
+ bd = fl->font_data;
+ /*
+ * Note the setup_font() is releasing
+ * font bytes.
+ */
+ for (i = 0; i < VFNT_MAPS; i++)
+ free(bd->vfbd_font->vf_map[i]);
+ free(fl->font_data->vfbd_font);
+ fl->font_data->vfbd_font = NULL;
+ fl->font_data->vfbd_uncompressed_size = 0;
+ fl->font_flags = FONT_AUTO;
+ }
+ }
+ (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+ }
+ return (rc);
+}
+
+bool
+gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
+{
+ struct resolution *rp, *p;
+
+ /*
+ * Walk detailed timings tables (4).
+ */
+ if ((edid->display.supported_features
+ & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
+ /* Walk detailed timing descriptors (4) */
+ for (int i = 0; i < DET_TIMINGS; i++) {
+ /*
+ * Reserved value 0 is not used for display decriptor.
+ */
+ if (edid->detailed_timings[i].pixel_clock == 0)
+ continue;
+ if ((rp = malloc(sizeof(*rp))) == NULL)
+ continue;
+ rp->width = GET_EDID_INFO_WIDTH(edid, i);
+ rp->height = GET_EDID_INFO_HEIGHT(edid, i);
+ if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
+ rp->height > 0 && rp->height <= EDID_MAX_LINES)
+ TAILQ_INSERT_TAIL(res, rp, next);
+ else
+ free(rp);
+ }
+ }
+
+ /*
+ * Walk standard timings list (8).
+ */
+ for (int i = 0; i < STD_TIMINGS; i++) {
+ /* Is this field unused? */
+ if (edid->standard_timings[i] == 0x0101)
+ continue;
+
+ if ((rp = malloc(sizeof(*rp))) == NULL)
+ continue;
+
+ rp->width = HSIZE(edid->standard_timings[i]);
+ switch (RATIO(edid->standard_timings[i])) {
+ case RATIO1_1:
+ rp->height = HSIZE(edid->standard_timings[i]);
+ if (edid->header.version > 1 ||
+ edid->header.revision > 2) {
+ rp->height = rp->height * 10 / 16;
+ }
+ break;
+ case RATIO4_3:
+ rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
+ break;
+ case RATIO5_4:
+ rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
+ break;
+ case RATIO16_9:
+ rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
+ break;
+ }
+
+ /*
+ * Create resolution list in decreasing order, except keep
+ * first entry (preferred timing mode).
+ */
+ TAILQ_FOREACH(p, res, next) {
+ if (p->width * p->height < rp->width * rp->height) {
+ /* Keep preferred mode first */
+ if (TAILQ_FIRST(res) == p)
+ TAILQ_INSERT_AFTER(res, p, rp, next);
+ else
+ TAILQ_INSERT_BEFORE(p, rp, next);
+ break;
+ }
+ if (TAILQ_NEXT(p, next) == NULL) {
+ TAILQ_INSERT_TAIL(res, rp, next);
+ break;
+ }
+ }
+ }
+ return (!TAILQ_EMPTY(res));
+}
diff --git a/stand/common/module.c b/stand/common/module.c
--- a/stand/common/module.c
+++ b/stand/common/module.c
@@ -38,6 +38,8 @@
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/stdint.h>
+#include <sys/font.h>
+#include <gfx_fb.h>
#if defined(LOADER_FDT_SUPPORT)
#include <fdt_platform.h>
@@ -91,7 +93,6 @@
NULL
};
-
/*
* load an object, either a disk file or code module.
*
@@ -616,6 +617,92 @@
return (error);
}
+vm_offset_t
+build_font_module(vm_offset_t addr)
+{
+ vt_font_bitmap_data_t *bd;
+ struct vt_font *fd;
+ struct preloaded_file *fp;
+ size_t size;
+ uint32_t checksum;
+ int i;
+ struct font_info fi;
+ struct fontlist *fl;
+ uint64_t fontp;
+
+ if (STAILQ_EMPTY(&fonts))
+ return (addr);
+
+ /* We can't load first */
+ if ((file_findfile(NULL, NULL)) == NULL) {
+ printf("Can not load font module: %s\n",
+ "the kernel is not loaded");
+ return (addr);
+ }
+
+ /* helper pointers */
+ bd = NULL;
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
+ gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
+ /*
+ * Kernel does have better built in font.
+ */
+ if (fl->font_flags == FONT_BUILTIN)
+ return (addr);
+
+ bd = fl->font_data;
+ break;
+ }
+ }
+ if (bd == NULL)
+ return (addr);
+ fd = bd->vfbd_font;
+
+ fi.fi_width = fd->vf_width;
+ checksum = fi.fi_width;
+ fi.fi_height = fd->vf_height;
+ checksum += fi.fi_height;
+ fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
+ checksum += fi.fi_bitmap_size;
+
+ size = roundup2(sizeof (struct font_info), 8);
+ for (i = 0; i < VFNT_MAPS; i++) {
+ fi.fi_map_count[i] = fd->vf_map_count[i];
+ checksum += fi.fi_map_count[i];
+ size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
+ size += roundup2(size, 8);
+ }
+ size += bd->vfbd_uncompressed_size;
+
+ fi.fi_checksum = -checksum;
+
+ fp = file_findfile(NULL, "elf kernel");
+ if (fp == NULL)
+ fp = file_findfile(NULL, "elf64 kernel");
+ if (fp == NULL)
+ panic("can't find kernel file");
+
+ fontp = addr;
+ addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
+ addr = roundup2(addr, 8);
+
+ /* Copy maps. */
+ for (i = 0; i < VFNT_MAPS; i++) {
+ if (fd->vf_map_count[i] != 0) {
+ addr += archsw.arch_copyin(fd->vf_map[i], addr,
+ fd->vf_map_count[i] * sizeof (struct vfnt_map));
+ addr = roundup2(addr, 8);
+ }
+ }
+
+ /* Copy the bitmap. */
+ addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
+
+ /* Looks OK so far; populate control structure */
+ file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
+ return (addr);
+}
#ifdef LOADER_VERIEXEC_VECTX
#define VECTX_HANDLE(fd) vctx
diff --git a/stand/defaults/loader.conf.5 b/stand/defaults/loader.conf.5
--- a/stand/defaults/loader.conf.5
+++ b/stand/defaults/loader.conf.5
@@ -238,7 +238,9 @@
.Dq spinning
character (useful for embedded products and such).
.It Va efi_max_resolution
-Specify the maximum desired resolution for the EFI console.
+.It Va vbe_max_resolution
+Specify the maximum desired resolution for the EFI or VESA BIOS Extension (VBE)
+framebuffer console.
The following values are accepted:
.Bl -column "WidthxHeight"
.It Sy Value Ta Sy Resolution
diff --git a/stand/efi/include/efilib.h b/stand/efi/include/efilib.h
--- a/stand/efi/include/efilib.h
+++ b/stand/efi/include/efilib.h
@@ -108,7 +108,6 @@
void efi_time_fini(void);
int parse_uefi_con_out(void);
-bool efi_cons_update_mode(void);
EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
EFI_STATUS main(int argc, CHAR16 *argv[]);
diff --git a/stand/efi/libefi/Makefile b/stand/efi/libefi/Makefile
--- a/stand/efi/libefi/Makefile
+++ b/stand/efi/libefi/Makefile
@@ -49,7 +49,7 @@
.endif
CFLAGS+= -I${EFIINC}
CFLAGS+= -I${EFIINCMD}
-CFLAGS.efi_console.c+= -I${SRCTOP}/sys/teken
+CFLAGS.efi_console.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
CFLAGS.teken.c+= -I${SRCTOP}/sys/teken
.if ${MK_LOADER_ZFS} != "no"
CFLAGS+= -I${ZFSSRC}
diff --git a/stand/efi/libefi/efi_console.c b/stand/efi/libefi/efi_console.c
--- a/stand/efi/libefi/efi_console.c
+++ b/stand/efi/libefi/efi_console.c
@@ -27,17 +27,22 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/param.h>
#include <efi.h>
#include <efilib.h>
#include <teken.h>
#include <sys/reboot.h>
-
+#include <machine/metadata.h>
+#include <gfx_fb.h>
#include "bootstrap.h"
+extern EFI_GUID gop_guid;
+extern int efi_find_framebuffer(struct efi_fb *efifb);
static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
static SIMPLE_INPUT_INTERFACE *conin;
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
+static bool efi_started;
static int mode; /* Does ConOut have serial console? */
@@ -59,6 +64,9 @@
void end_term(void);
#endif
+#define TEXT_ROWS 24
+#define TEXT_COLS 80
+
static tf_bell_t efi_cons_bell;
static tf_cursor_t efi_text_cursor;
static tf_putchar_t efi_text_putchar;
@@ -77,16 +85,16 @@
.tf_respond = efi_cons_respond,
};
-teken_t teken;
-teken_pos_t tp;
-
-struct text_pixel {
- teken_char_t c;
- teken_attr_t a;
+static teken_funcs_t tfx = {
+ .tf_bell = efi_cons_bell,
+ .tf_cursor = gfx_fb_cursor,
+ .tf_putchar = gfx_fb_putchar,
+ .tf_fill = gfx_fb_fill,
+ .tf_copy = gfx_fb_copy,
+ .tf_param = gfx_fb_param,
+ .tf_respond = efi_cons_respond,
};
-static struct text_pixel *buffer;
-
#define KEYBUFSZ 10
static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */
static int key_pending;
@@ -116,6 +124,7 @@
int efi_cons_getchar(void);
void efi_cons_efiputchar(int);
int efi_cons_poll(void);
+static void cons_draw_frame(teken_attr_t *);
struct console efi_console = {
"efi",
@@ -128,6 +137,28 @@
efi_cons_poll
};
+/*
+ * This function is used to mark a rectangular image area so the scrolling
+ * will know we need to copy the data from there.
+ */
+void
+term_image_display(teken_gfx_t *state, const teken_rect_t *r)
+{
+ teken_pos_t p;
+ int idx;
+
+ for (p.tp_row = r->tr_begin.tp_row;
+ p.tp_row < r->tr_end.tp_row; p.tp_row++) {
+ for (p.tp_col = r->tr_begin.tp_col;
+ p.tp_col < r->tr_end.tp_col; p.tp_col++) {
+ idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+ screen_buffer[idx].a.ta_format |= TF_IMAGE;
+ }
+ }
+}
+
/*
* Not implemented.
*/
@@ -137,33 +168,30 @@
}
static void
-efi_text_cursor(void *s __unused, const teken_pos_t *p)
+efi_text_cursor(void *arg, const teken_pos_t *p)
{
- UINTN row, col;
+ teken_gfx_t *state = arg;
+ UINTN col, row;
- (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
+ row = p->tp_row;
+ if (p->tp_row >= state->tg_tp.tp_row)
+ row = state->tg_tp.tp_row - 1;
- if (p->tp_col == col)
- col = p->tp_col - 1;
- else
- col = p->tp_col;
-
- if (p->tp_row == row)
- row = p->tp_row - 1;
- else
- row = p->tp_row;
+ col = p->tp_col;
+ if (p->tp_col >= state->tg_tp.tp_col)
+ col = state->tg_tp.tp_col - 1;
conout->SetCursorPosition(conout, col, row);
}
static void
-efi_text_printchar(const teken_pos_t *p, bool autoscroll)
+efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll)
{
UINTN a, attr;
struct text_pixel *px;
teken_color_t fg, bg, tmp;
- px = buffer + p->tp_col + p->tp_row * tp.tp_col;
+ px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
a = conout->Mode->Attribute;
fg = teken_256to16(px->a.ta_fgcolor);
@@ -184,10 +212,10 @@
conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
- /* to prvent autoscroll, skip print of lower right char */
+ /* to prevent autoscroll, skip print of lower right char */
if (!autoscroll &&
- p->tp_row == tp.tp_row - 1 &&
- p->tp_col == tp.tp_col - 1)
+ p->tp_row == state->tg_tp.tp_row - 1 &&
+ p->tp_col == state->tg_tp.tp_col - 1)
return;
(void) conout->SetAttribute(conout, attr);
@@ -196,58 +224,80 @@
}
static void
-efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
+efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
const teken_attr_t *a)
{
+ teken_gfx_t *state = s;
EFI_STATUS status;
int idx;
- idx = p->tp_col + p->tp_row * tp.tp_col;
- buffer[idx].c = c;
- buffer[idx].a = *a;
- efi_text_printchar(p, false);
+ idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+
+ screen_buffer[idx].c = c;
+ screen_buffer[idx].a = *a;
+
+ efi_text_printchar(s, p, false);
}
static void
-efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
+efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
const teken_attr_t *a)
{
+ teken_gfx_t *state = arg;
teken_pos_t p;
- UINTN row, col;
-
- (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
- conout->EnableCursor(conout, FALSE);
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, FALSE);
for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
p.tp_row++)
for (p.tp_col = r->tr_begin.tp_col;
p.tp_col < r->tr_end.tp_col; p.tp_col++)
- efi_text_putchar(s, &p, c, a);
- conout->EnableCursor(conout, TRUE);
+ efi_text_putchar(state, &p, c, a);
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, TRUE);
}
-static bool
-efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
+static void
+efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s,
+ teken_pos_t *d, bool scroll)
{
- if (px1->c != px2->c)
- return (false);
-
- if (px1->a.ta_format != px2->a.ta_format)
- return (false);
- if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
- return (false);
- if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
- return (false);
-
- return (true);
+ unsigned soffset, doffset;
+ teken_pos_t sp, dp;
+ int x;
+
+ soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
+ doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
+
+ sp = *s;
+ dp = *d;
+ for (x = 0; x < ncol; x++) {
+ sp.tp_col = s->tp_col + x;
+ dp.tp_col = d->tp_col + x;
+ if (!is_same_pixel(&screen_buffer[soffset + x],
+ &screen_buffer[doffset + x])) {
+ screen_buffer[doffset + x] =
+ screen_buffer[soffset + x];
+ if (!scroll)
+ efi_text_printchar(state, &dp, false);
+ } else if (scroll) {
+ /* Draw last char and trigger scroll. */
+ if (dp.tp_col + 1 == state->tg_tp.tp_col &&
+ dp.tp_row + 1 == state->tg_tp.tp_row) {
+ efi_text_printchar(state, &dp, true);
+ }
+ }
+ }
}
static void
-efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
+efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
{
- int srow, drow;
- int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
+ teken_gfx_t *state = arg;
+ unsigned doffset, soffset;
teken_pos_t d, s;
+ int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
bool scroll = false;
/*
@@ -262,90 +312,47 @@
* Check if we do copy whole screen.
*/
if (p->tp_row == 0 && p->tp_col == 0 &&
- nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2)
+ nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2)
scroll = true;
- conout->EnableCursor(conout, FALSE);
- if (p->tp_row < r->tr_begin.tp_row) {
- /* Copy from bottom to top. */
+ soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
+ doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, FALSE);
+
+ /*
+ * Copy line by line.
+ */
+ if (doffset <= soffset) {
+ s = r->tr_begin;
+ d = *p;
for (y = 0; y < nrow; y++) {
- d.tp_row = p->tp_row + y;
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
- for (x = 0; x < ncol; x++) {
- d.tp_col = p->tp_col + x;
- s.tp_col = r->tr_begin.tp_col + x;
-
- if (!efi_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- if (!scroll)
- efi_text_printchar(&d, false);
- } else if (scroll) {
- /*
- * Draw last char and trigger
- * scroll.
- */
- if (y == nrow - 1 &&
- x == ncol - 1) {
- efi_text_printchar(&d, true);
- }
- }
- }
+ d.tp_row = p->tp_row + y;
+
+ efi_text_copy_line(state, ncol, &s, &d, scroll);
}
} else {
- /* Copy from top to bottom. */
- if (p->tp_col < r->tr_begin.tp_col) {
- /* Copy from right to left. */
- for (y = nrow - 1; y >= 0; y--) {
- d.tp_row = p->tp_row + y;
- s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
- for (x = 0; x < ncol; x++) {
- d.tp_col = p->tp_col + x;
- s.tp_col = r->tr_begin.tp_col + x;
-
- if (!efi_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- efi_text_printchar(&d, false);
- }
- }
- }
- } else {
- /* Copy from left to right. */
- for (y = nrow - 1; y >= 0; y--) {
- d.tp_row = p->tp_row + y;
- s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
- for (x = ncol - 1; x >= 0; x--) {
- d.tp_col = p->tp_col + x;
- s.tp_col = r->tr_begin.tp_col + x;
-
- if (!efi_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- efi_text_printchar(&d, false);
- }
- }
- }
+ for (y = nrow - 1; y >= 0; y--) {
+ s.tp_row = r->tr_begin.tp_row + y;
+ d.tp_row = p->tp_row + y;
+
+ efi_text_copy_line(state, ncol, &s, &d, false);
}
}
- conout->EnableCursor(conout, TRUE);
+
+ /* display the cursor */
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, TRUE);
}
static void
-efi_text_param(void *s __unused, int cmd, unsigned int value)
+efi_text_param(void *arg, int cmd, unsigned int value)
{
+ teken_gfx_t *state = arg;
+
switch (cmd) {
case TP_SETLOCALCURSOR:
/*
@@ -357,10 +364,13 @@
value = (value == 1) ? 0 : 1;
/* FALLTHROUGH */
case TP_SHOWCURSOR:
- if (value == 1)
+ if (value != 0) {
conout->EnableCursor(conout, TRUE);
- else
+ state->tg_cursor_visible = true;
+ } else {
conout->EnableCursor(conout, FALSE);
+ state->tg_cursor_visible = false;
+ }
break;
default:
/* Not yet implemented */
@@ -470,7 +480,7 @@
evalue = value;
}
- ap = teken_get_defattr(&teken);
+ ap = teken_get_defattr(&gfx_state.tg_teken);
a = *ap;
if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
/* is it already set? */
@@ -484,8 +494,15 @@
return (CMD_OK);
a.ta_bgcolor = val;
}
+
+ /* Improve visibility */
+ if (a.ta_bgcolor == TC_WHITE)
+ a.ta_bgcolor |= TC_LIGHT;
+
+ teken_set_defattr(&gfx_state.tg_teken, &a);
+ cons_draw_frame(&a);
env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
- teken_set_defattr(&teken, &a);
+ teken_input(&gfx_state.tg_teken, "\e[2J", 4);
return (CMD_OK);
}
@@ -823,19 +840,100 @@
#endif
}
+static int
+env_screen_nounset(struct env_var *ev __unused)
+{
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return (0);
+ return (EPERM);
+}
+
+static void
+cons_draw_frame(teken_attr_t *a)
+{
+ teken_attr_t attr = *a;
+ teken_color_t fg = a->ta_fgcolor;
+
+ attr.ta_fgcolor = attr.ta_bgcolor;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+
+ gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
+ gfx_state.tg_origin.tp_row, 1);
+ gfx_fb_drawrect(0,
+ gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
+ gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
+ gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
+ gfx_state.tg_origin.tp_col,
+ gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
+ gfx_fb_drawrect(
+ gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
+ gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
+ gfx_state.tg_fb.fb_height, 1);
+
+ attr.ta_fgcolor = fg;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+}
+
bool
-efi_cons_update_mode(void)
+cons_update_mode(bool use_gfx_mode)
{
UINTN cols, rows;
const teken_attr_t *a;
teken_attr_t attr;
EFI_STATUS status;
- char env[8], *ptr;
+ EFI_GRAPHICS_OUTPUT *gop = NULL;
+ struct efi_fb efifb;
+ char env[10], *ptr;
+
+ if (use_gfx_mode == true) {
+ gfx_state.tg_fb_type = FB_GOP;
+ if (gfx_state.tg_private == NULL) {
+ (void) BS->LocateProtocol(&gop_guid, NULL,
+ (void **)&gop);
+ gfx_state.tg_private = gop;
+ } else {
+ gop = gfx_state.tg_private;
+ }
+
+ /*
+ * We have FB but no GOP - it must be UGA.
+ */
+ if (gop == NULL)
+ gfx_state.tg_fb_type = FB_UGA;
+
+ if (efi_find_framebuffer(&efifb) == 0) {
+ int roff, goff, boff;
+
+ gfx_state.tg_fb.fb_addr = efifb.fb_addr;
+ gfx_state.tg_fb.fb_size = efifb.fb_size;
+ gfx_state.tg_fb.fb_height = efifb.fb_height;
+ gfx_state.tg_fb.fb_width = efifb.fb_width;
+ gfx_state.tg_fb.fb_stride = efifb.fb_stride;
+ gfx_state.tg_fb.fb_mask_red = efifb.fb_mask_red;
+ gfx_state.tg_fb.fb_mask_green = efifb.fb_mask_green;
+ gfx_state.tg_fb.fb_mask_blue = efifb.fb_mask_blue;
+ gfx_state.tg_fb.fb_mask_reserved =
+ efifb.fb_mask_reserved;
+ roff = ffs(efifb.fb_mask_red) - 1;
+ goff = ffs(efifb.fb_mask_green) - 1;
+ boff = ffs(efifb.fb_mask_blue) - 1;
+
+ (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB,
+ efifb.fb_mask_red >> roff, roff,
+ efifb.fb_mask_green >> goff, goff,
+ efifb.fb_mask_blue >> boff, boff);
+ gfx_state.tg_fb.fb_bpp = fls(
+ efifb.fb_mask_red | efifb.fb_mask_green |
+ efifb.fb_mask_blue | efifb.fb_mask_reserved);
+ }
+ } else {
+ gfx_state.tg_fb_type = FB_TEXT;
+ }
status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
if (EFI_ERROR(status) || cols * rows == 0) {
- cols = 80;
- rows = 24;
+ cols = TEXT_COLS;
+ rows = TEXT_ROWS;
}
/*
@@ -853,20 +951,74 @@
*/
mode = parse_uefi_con_out();
if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
- if (buffer != NULL) {
- if (tp.tp_row == rows && tp.tp_col == cols)
- return (true);
- free(buffer);
+ conout->EnableCursor(conout, FALSE);
+ gfx_state.tg_cursor_visible = false;
+
+ if (gfx_state.tg_fb_type == FB_TEXT) {
+
+ gfx_state.tg_functions = &tf;
+ /* ensure the following are not set for text mode */
+ unsetenv("screen.height");
+ unsetenv("screen.width");
+ unsetenv("screen.depth");
} else {
- teken_init(&teken, &tf, NULL);
+ uint32_t fb_height, fb_width;
+
+ fb_height = gfx_state.tg_fb.fb_height;
+ fb_width = gfx_state.tg_fb.fb_width;
+
+ /*
+ * setup_font() can adjust terminal size.
+ * Note, we assume 80x24 terminal first, this is
+ * because the font selection will attempt to achieve
+ * at least this terminal dimension and we do not
+ * end up with too small font.
+ */
+ gfx_state.tg_tp.tp_row = TEXT_ROWS;
+ gfx_state.tg_tp.tp_col = TEXT_COLS;
+ setup_font(&gfx_state, fb_height, fb_width);
+ rows = gfx_state.tg_tp.tp_row;
+ cols = gfx_state.tg_tp.tp_col;
+ /* Point of origin in pixels. */
+ gfx_state.tg_origin.tp_row = (fb_height -
+ (rows * gfx_state.tg_font.vf_height)) / 2;
+ gfx_state.tg_origin.tp_col = (fb_width -
+ (cols * gfx_state.tg_font.vf_width)) / 2;
+
+ /* UEFI gop has depth 32. */
+ gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
+ gfx_state.tg_font.vf_width * 4;
+ free(gfx_state.tg_glyph);
+ gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
+ if (gfx_state.tg_glyph == NULL)
+ return (false);
+
+ gfx_state.tg_functions = &tfx;
+ snprintf(env, sizeof (env), "%d", fb_height);
+ env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK,
+ env, env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", fb_width);
+ env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK,
+ env, env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d",
+ gfx_state.tg_fb.fb_bpp);
+ env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK,
+ env, env_noset, env_screen_nounset);
}
- tp.tp_row = rows;
- tp.tp_col = cols;
- buffer = malloc(rows * cols * sizeof(*buffer));
- if (buffer != NULL) {
- teken_set_winsize(&teken, &tp);
- a = teken_get_defattr(&teken);
+ /* Record our terminal screen size. */
+ gfx_state.tg_tp.tp_row = rows;
+ gfx_state.tg_tp.tp_col = cols;
+
+ teken_init(&gfx_state.tg_teken, gfx_state.tg_functions,
+ &gfx_state);
+
+ free(screen_buffer);
+ screen_buffer = malloc(rows * cols * sizeof(*screen_buffer));
+ if (screen_buffer != NULL) {
+ teken_set_winsize(&gfx_state.tg_teken,
+ &gfx_state.tg_tp);
+ a = teken_get_defattr(&gfx_state.tg_teken);
attr = *a;
/*
@@ -880,7 +1032,7 @@
ptr = getenv("teken.bg_color");
attr.ta_bgcolor = strtol(ptr, NULL, 10);
- teken_set_defattr(&teken, &attr);
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
} else {
snprintf(env, sizeof(env), "%d",
attr.ta_fgcolor);
@@ -891,18 +1043,12 @@
env_setenv("teken.bg_color", EV_VOLATILE, env,
efi_set_colors, env_nounset);
}
-
- for (int row = 0; row < rows; row++) {
- for (int col = 0; col < cols; col++) {
- buffer[col + row * tp.tp_col].c = ' ';
- buffer[col + row * tp.tp_col].a = attr;
- }
- }
}
}
+ if (screen_buffer == NULL) {
+ conout->EnableCursor(conout, TRUE);
#ifdef TERM_EMU
- if (buffer == NULL) {
conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
DEFAULT_BGCOLOR));
end_term();
@@ -910,8 +1056,23 @@
curs_move(&curx, &cury, curx, cury);
fg_c = DEFAULT_FGCOLOR;
bg_c = DEFAULT_BGCOLOR;
- }
#endif
+ } else {
+ /* Improve visibility */
+ if (attr.ta_bgcolor == TC_WHITE)
+ attr.ta_bgcolor |= TC_LIGHT;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+
+ /* Draw frame around terminal area. */
+ cons_draw_frame(&attr);
+ /*
+ * Erase display, this will also fill our screen
+ * buffer.
+ */
+ teken_input(&gfx_state.tg_teken, "\e[2J", 4);
+ gfx_state.tg_functions->tf_param(&gfx_state,
+ TP_SHOWCURSOR, 1);
+ }
snprintf(env, sizeof (env), "%u", (unsigned)rows);
setenv("LINES", env, 1);
@@ -926,8 +1087,13 @@
{
EFI_STATUS status;
- conout->EnableCursor(conout, TRUE);
- if (efi_cons_update_mode())
+ if (efi_started)
+ return (0);
+
+ efi_started = true;
+
+ gfx_framework_init();
+ if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
return (0);
return (1);
@@ -1035,12 +1201,12 @@
* Don't use Teken when we're doing pure serial, or a multiple console
* with video "primary" because that's also serial.
*/
- if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || buffer == NULL) {
+ if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) {
input_byte(ch);
return;
}
- teken_input(&teken, &ch, sizeof (ch));
+ teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
}
static int
diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -22,7 +22,9 @@
framebuffer.c \
main.c \
self_reloc.c \
- vers.c
+ vers.c \
+ gfx_fb.c \
+ 8x16.c
CFLAGS+= -I${.CURDIR}/../loader
.if ${MK_LOADER_ZFS} != "no"
@@ -33,6 +35,11 @@
HAVE_ZFS= yes
.endif
+CFLAGS.gfx_fb.c += -I$(SRCTOP)/sys/teken
+CFLAGS.gfx_fb.c += -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
+CFLAGS.gfx_fb.c += -I${SRCTOP}/contrib/pnglite
+CFLAGS.gfx_fb.c += -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib
+
# We implement a slightly non-standard %S in that it always takes a
# CHAR16 that's common in UEFI-land instead of a wchar_t. This only
# seems to matter on arm64 where wchar_t defaults to an int instead
@@ -74,6 +81,11 @@
# Always add MI sources
.include "${BOOTSRC}/loader.mk"
+CLEANFILES+= 8x16.c
+
+8x16.c: ${SRCTOP}/contrib/terminus/ter-u16v.bdf
+ vtfontcvt -f compressed-source -o ${.TARGET} ${.ALLSRC}
+
FILES+= ${LOADER}.efi
FILESMODE_${LOADER}.efi= ${BINMODE}
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
--- a/stand/efi/loader/bootinfo.c
+++ b/stand/efi/loader/bootinfo.c
@@ -447,7 +447,7 @@
*/
uint32_t mdt[] = {
MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
- MODINFOMD_ENVP,
+ MODINFOMD_ENVP, MODINFOMD_FONT,
#if defined(LOADER_FDT_SUPPORT)
MODINFOMD_DTBP
#endif
@@ -480,6 +480,11 @@
/* Pad to a page boundary. */
addr = roundup(addr, PAGE_SIZE);
+ addr = build_font_module(addr);
+
+ /* Pad to a page boundary. */
+ addr = roundup(addr, PAGE_SIZE);
+
/* Copy our environment. */
envp = addr;
addr = bi_copyenv(addr);
@@ -503,17 +508,17 @@
if (kfp == NULL)
panic("can't find kernel file");
kernend = 0; /* fill it in later */
- file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
- file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof(howto), &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof(envp), &envp);
#if defined(LOADER_FDT_SUPPORT)
if (dtb_size)
- file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof(dtbp), &dtbp);
else
printf("WARNING! Trying to fire up the kernel, but no "
"device tree blob found!\n");
#endif
- file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
- file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof(kernend), &kernend);
+ file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(ST), &ST);
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
diff --git a/stand/efi/loader/framebuffer.c b/stand/efi/loader/framebuffer.c
--- a/stand/efi/loader/framebuffer.c
+++ b/stand/efi/loader/framebuffer.c
@@ -40,9 +40,10 @@
#include <efipciio.h>
#include <machine/metadata.h>
+#include "bootstrap.h"
#include "framebuffer.h"
-static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID;
@@ -586,7 +587,7 @@
mode, EFI_ERROR_CODE(status));
return (CMD_ERROR);
}
- (void) efi_cons_update_mode();
+ (void) cons_update_mode(true);
}
return (CMD_OK);
}
@@ -611,7 +612,7 @@
}
if (max_dim > 0)
conout->SetMode(conout, best_mode);
- (void) efi_cons_update_mode();
+ (void) cons_update_mode(true);
return (CMD_OK);
}
@@ -699,8 +700,10 @@
argv[0], mode, EFI_ERROR_CODE(status));
return (CMD_ERROR);
}
- (void) efi_cons_update_mode();
- } else if (!strcmp(argv[1], "get")) {
+ (void) cons_update_mode(true);
+ } else if (strcmp(argv[1], "off") == 0) {
+ (void) cons_update_mode(false);
+ } else if (strcmp(argv[1], "get") == 0) {
if (argc != 2)
goto usage;
efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
@@ -728,7 +731,7 @@
usage:
snprintf(command_errbuf, sizeof(command_errbuf),
- "usage: %s [list | get | set <mode>]", argv[0]);
+ "usage: %s [list | get | set <mode> | off]", argv[0]);
return (CMD_ERROR);
}
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -1155,6 +1155,7 @@
!interactive_interrupt("Failed to find bootable partition"))
return (EFI_NOT_FOUND);
+ autoload_font(false); /* Set up the font list for console. */
efi_init_environment();
#if !defined(__arm__)
@@ -1354,7 +1355,7 @@
printf("couldn't set mode %d\n", mode);
return (CMD_ERROR);
}
- (void) efi_cons_update_mode();
+ (void) cons_update_mode(true);
return (CMD_OK);
}
diff --git a/stand/ficl.mk b/stand/ficl.mk
--- a/stand/ficl.mk
+++ b/stand/ficl.mk
@@ -15,7 +15,7 @@
.endif
CFLAGS+= -I${FICLSRC} -I${FICLSRC}/${FICL_CPUARCH} -I${LDRSRC}
-CFLAGS+= -DBF_DICTSIZE=15000
+CFLAGS+= -DBF_DICTSIZE=30000
.if ${MK_LOADER_VERIEXEC} != "no"
CFLAGS+= -DLOADER_VERIEXEC -I${SRCTOP}/lib/libsecureboot/h
diff --git a/stand/ficl/Makefile b/stand/ficl/Makefile
--- a/stand/ficl/Makefile
+++ b/stand/ficl/Makefile
@@ -12,6 +12,8 @@
SRCS= ${BASE_SRCS} sysdep.c softcore.c
CLEANFILES+= softcore.c testmain testmain.o
+CFLAGS.loader.c += -I${SRCTOP}/sys/teken
+CFLAGS.loader.c += -I${SRCTOP}/contrib/pnglite
.ifmake testmain
CFLAGS= -DTESTMAIN -D_TESTMAIN
CFLAGS+= -I${FICLSRC} -I${FICLSRC}/${FICL_CPUARCH} -I${LDRSRC}
diff --git a/stand/ficl/loader.c b/stand/ficl/loader.c
--- a/stand/ficl/loader.c
+++ b/stand/ficl/loader.c
@@ -46,6 +46,8 @@
#include "bootstrap.h"
#include <string.h>
#include <uuid.h>
+#include <gfx_fb.h>
+#include <pnglite.h>
#include "ficl.h"
/* FreeBSD's loader interaction words and extras
@@ -65,6 +67,182 @@
* .# ( value -- )
*/
+#ifndef TESTMAIN
+/* ( flags x1 y1 x2 y2 -- flag ) */
+void
+ficl_term_putimage(FICL_VM *pVM)
+{
+ char *namep, *name;
+ int names;
+ unsigned long ret = FICL_FALSE;
+ uint32_t x1, y1, x2, y2, f;
+ png_t png;
+ int error;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 7, 1);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char *) stackPopPtr(pVM->pStack);
+ y2 = stackPopINT(pVM->pStack);
+ x2 = stackPopINT(pVM->pStack);
+ y1 = stackPopINT(pVM->pStack);
+ x1 = stackPopINT(pVM->pStack);
+ f = stackPopINT(pVM->pStack);
+
+ x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width;
+ y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height;
+ if (x2 != 0) {
+ x2 = gfx_state.tg_origin.tp_col +
+ x2 * gfx_state.tg_font.vf_width;
+ }
+ if (y2 != 0) {
+ y2 = gfx_state.tg_origin.tp_row +
+ y2 * gfx_state.tg_font.vf_height;
+ }
+
+ name = ficlMalloc(names + 1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ (void) strncpy(name, namep, names);
+ name[names] = '\0';
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = FICL_TRUE; /* success */
+ (void) png_close(&png);
+ }
+ ficlFree(name);
+ stackPushUNS(pVM->pStack, ret);
+}
+
+/* ( flags x1 y1 x2 y2 -- flag ) */
+void
+ficl_fb_putimage(FICL_VM *pVM)
+{
+ char *namep, *name;
+ int names;
+ unsigned long ret = FICL_FALSE;
+ uint32_t x1, y1, x2, y2, f;
+ png_t png;
+ int error;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 7, 1);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char *) stackPopPtr(pVM->pStack);
+ y2 = stackPopINT(pVM->pStack);
+ x2 = stackPopINT(pVM->pStack);
+ y1 = stackPopINT(pVM->pStack);
+ x1 = stackPopINT(pVM->pStack);
+ f = stackPopINT(pVM->pStack);
+
+ name = ficlMalloc(names + 1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ (void) strncpy(name, namep, names);
+ name[names] = '\0';
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = FICL_TRUE; /* success */
+ (void) png_close(&png);
+ }
+ ficlFree(name);
+ stackPushUNS(pVM->pStack, ret);
+}
+
+void
+ficl_fb_setpixel(FICL_VM *pVM)
+{
+ FICL_UNS x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ y = stackPopUNS(pVM->pStack);
+ x = stackPopUNS(pVM->pStack);
+ gfx_fb_setpixel(x, y);
+}
+
+void
+ficl_fb_line(FICL_VM *pVM)
+{
+ FICL_UNS x0, y0, x1, y1, wd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 5, 0);
+#endif
+
+ wd = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ y0 = stackPopUNS(pVM->pStack);
+ x0 = stackPopUNS(pVM->pStack);
+ gfx_fb_line(x0, y0, x1, y1, wd);
+}
+
+void
+ficl_fb_bezier(FICL_VM *pVM)
+{
+ FICL_UNS x0, y0, x1, y1, x2, y2, width;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 7, 0);
+#endif
+
+ width = stackPopUNS(pVM->pStack);
+ y2 = stackPopUNS(pVM->pStack);
+ x2 = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ y0 = stackPopUNS(pVM->pStack);
+ x0 = stackPopUNS(pVM->pStack);
+ gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width);
+}
+
+void
+ficl_fb_drawrect(FICL_VM *pVM)
+{
+ FICL_UNS x1, x2, y1, y2, fill;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 5, 0);
+#endif
+
+ fill = stackPopUNS(pVM->pStack);
+ y2 = stackPopUNS(pVM->pStack);
+ x2 = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ gfx_fb_drawrect(x1, y1, x2, y2, fill);
+}
+
+void
+ficl_term_drawrect(FICL_VM *pVM)
+{
+ FICL_UNS x1, x2, y1, y2;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 0);
+#endif
+
+ y2 = stackPopUNS(pVM->pStack);
+ x2 = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ gfx_term_drawrect(x1, y1, x2, y2);
+}
+#endif /* TESTMAIN */
+
void
ficlSetenv(FICL_VM *pVM)
{
@@ -867,6 +1045,13 @@
dictAppendWord(dp, "uuid-from-string", ficlUuidFromString, FW_DEFAULT);
dictAppendWord(dp, "uuid-to-string", ficlUuidToString, FW_DEFAULT);
#ifndef TESTMAIN
+ dictAppendWord(dp, "fb-setpixel", ficl_fb_setpixel, FW_DEFAULT);
+ dictAppendWord(dp, "fb-line", ficl_fb_line, FW_DEFAULT);
+ dictAppendWord(dp, "fb-bezier", ficl_fb_bezier, FW_DEFAULT);
+ dictAppendWord(dp, "fb-drawrect", ficl_fb_drawrect, FW_DEFAULT);
+ dictAppendWord(dp, "fb-putimage", ficl_fb_putimage, FW_DEFAULT);
+ dictAppendWord(dp, "term-drawrect", ficl_term_drawrect, FW_DEFAULT);
+ dictAppendWord(dp, "term-putimage", ficl_term_putimage, FW_DEFAULT);
dictAppendWord(dp, "isvirtualized?",ficlIsvirtualizedQ, FW_DEFAULT);
#endif
diff --git a/stand/fonts/INDEX.fonts b/stand/fonts/INDEX.fonts
new file mode 100644
--- /dev/null
+++ b/stand/fonts/INDEX.fonts
@@ -0,0 +1,66 @@
+#
+# $FreeBSD$
+#
+# database for vidfont(8)
+#
+# Format <file>:<lang>:<description>
+#
+# lang: ar bg cs da de el en es fi fr hr hu hy is it iw ja ko nl no pl
+# pt ro ru sh sk sl sv tr uk zh
+# lang: lang,lang
+#
+# Example:
+# terminus-b32.fnt:de:Terminus Schriftart
+# terminus-b32.fnt:en:Terminus font
+#
+# If lang is empty use 'en' (us-english) as default.
+#
+# See also setlocale(3),
+# /usr/share/locale, /usr/X11/lib/X11/locale/locale.alias
+#
+################################
+# Language support: MENU, FONT
+#
+MENU:en:Choose your terminal font
+MENU:da:Vælg skrifttypen til din terminal
+MENU:de:Wählen Sie Ihre Schrift
+MENU:fr:Choisissez votre fonte écran
+
+#
+# The font definition for "en" is the fall-back font for
+# all languages.
+# Add language specific font definitions only where required!
+#
+FONT:en:8x16v.fnt
+#
+
+6x12.fnt:en:Terminus BSD Console, size 12
+6x12.fnt:da:Terminus BSD-konsol, størrelse 12
+6x12.fnt:de:Terminus BSD Console, Größe 12
+8x14.fnt:en:Terminus BSD Console, size 14
+8x14.fnt:da:Terminus BSD-konsol, størrelse 14
+8x14.fnt:de:Terminus BSD Console, Größe 14
+8x16.fnt:en:Terminus BSD Console, size 16
+8x16.fnt:da:Terminus BSD-konsol, størrelse 16
+8x16.fnt:de:Terminus BSD Console, Größe 16
+10x18.fnt:en:Terminus BSD Console, size 18
+10x18.fnt:da:Terminus BSD-konsol, størrelse 18
+10x18.fnt:de:Terminus BSD Console, Größe 18
+10x20.fnt:en:Terminus BSD Console, size 20
+10x20.fnt:da:Terminus BSD-konsol, størrelse 20
+10x20.fnt:de:Terminus BSD Console, Größe 20
+11x22.fnt:en:Terminus BSD Console, size 22
+11x22.fnt:da:Terminus BSD-konsol, størrelse 22
+11x22.fnt:de:Terminus BSD Console, Größe 22
+12x24.fnt:en:Terminus BSD Console, size 24
+12x24.fnt:da:Terminus BSD-konsol, størrelse 24
+12x24.fnt:de:Terminus BSD Console, Größe 24
+14x28.fnt:en:Terminus BSD Console, size 28
+14x28.fnt:da:Terminus BSD-konsol, størrelse 28
+14x28.fnt:de:Terminus BSD Console, Größe 28
+16x32.fnt:en:Terminus BSD Console, size 32
+16x32.fnt:da:Terminus BSD-konsol, størrelse 32
+16x32.fnt:de:Terminus BSD Console, Größe 32
+
+# (fset 'langnew
+# "\M-}\C-p\C-k\C-y\C-m\C-y\M-}")
diff --git a/stand/fonts/Makefile b/stand/fonts/Makefile
new file mode 100644
--- /dev/null
+++ b/stand/fonts/Makefile
@@ -0,0 +1,81 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+.PATH: ${SRCTOP}/contrib/terminus
+
+FONTS= \
+ 6x12.fnt.gz \
+ 8x14.fnt.gz \
+ 8x14v.fnt.gz \
+ 8x16.fnt.gz \
+ 8x16v.fnt.gz \
+ 10x18.fnt.gz \
+ 10x20.fnt.gz \
+ 11x22.fnt.gz \
+ 12x24.fnt.gz \
+ 14x28.fnt.gz \
+ 16x32.fnt.gz \
+
+FILES= ${FONTS} INDEX.fonts
+FILESDIR= /boot/fonts
+
+CLEANFILES+= ${FONTS} ${FONTS:T:S/${COMPRESS_EXT}//g}
+
+6x12.fnt.gz: 6x12.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+8x14.fnt.gz: 8x14.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+8x14v.fnt.gz: 8x14v.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+8x16.fnt.gz: 8x16.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+8x16v.fnt.gz: 8x16v.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+10x18.fnt.gz: 10x18.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+10x20.fnt.gz: 10x20.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+11x22.fnt.gz: 11x22.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+12x24.fnt.gz: 12x24.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+14x28.fnt.gz: 14x28.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+16x32.fnt.gz: 16x32.fnt
+ ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+
+6x12.fnt: ter-u12n.bdf ter-u12b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+8x14.fnt: ter-u14n.bdf ter-u14b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+8x14v.fnt: ter-u14v.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+8x16.fnt: ter-u16n.bdf ter-u16b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+8x16v.fnt: ter-u16v.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+10x18.fnt: ter-u18n.bdf ter-u18b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+10x20.fnt: ter-u20n.bdf ter-u20b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+11x22.fnt: ter-u22n.bdf ter-u22b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+12x24.fnt: ter-u24n.bdf ter-u24b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+14x28.fnt: ter-u28n.bdf ter-u28b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+16x32.fnt: ter-u32n.bdf ter-u32b.bdf
+ vtfontcvt -o ${.TARGET} ${.ALLSRC}
+
+.include <bsd.prog.mk>
diff --git a/stand/forth/beastie.4th b/stand/forth/beastie.4th
--- a/stand/forth/beastie.4th
+++ b/stand/forth/beastie.4th
@@ -82,6 +82,10 @@
then
;
+: draw-beastie
+ ['] draw-beastie console-iterate
+;
+
also support-functions
: beastie-start ( -- ) \ starts the menu
diff --git a/stand/forth/brand-fbsd.4th b/stand/forth/brand-fbsd.4th
--- a/stand/forth/brand-fbsd.4th
+++ b/stand/forth/brand-fbsd.4th
@@ -34,6 +34,18 @@
: brand ( x y -- ) \ "FreeBSD" [wide] logo in B/W (7 rows x 42 columns)
+ framebuffer? if
+ s" term-putimage" sfind if
+ \ note, we use 0, 0 for image upper left as origin,
+ \ and 0, 7 for lower right to preserve aspect ratio
+ >r 0 0 0 0 7
+ s" /boot/images/freebsd-brand-rev.png"
+ r> execute if 2drop exit then
+ else
+ drop
+ then
+ then
+
s" ______ ____ _____ _____ " brand+
s" | ____| | _ \ / ____| __ \ " brand+
s" | |___ _ __ ___ ___ | |_) | (___ | | | |" brand+
diff --git a/stand/forth/brand.4th b/stand/forth/brand.4th
--- a/stand/forth/brand.4th
+++ b/stand/forth/brand.4th
@@ -72,3 +72,7 @@
else drop then
then
;
+
+: draw-brand
+ ['] draw-brand console-iterate
+;
diff --git a/stand/forth/color.4th b/stand/forth/color.4th
--- a/stand/forth/color.4th
+++ b/stand/forth/color.4th
@@ -28,7 +28,7 @@
\ This function returns FALSE if the `loader_color' environment variable is set
\ to NO, no, or 0. It returns TRUE if `loader_color' is set to any other value.
-\ If `loader_color' is unset, TRUE is returned (unless booting serial).
+\ If `loader_color' is unset, TRUE is returned.
\
: loader_color? ( -- t )
@@ -44,12 +44,8 @@
FALSE exit
then
drop
- \ It is enabled.
- TRUE
- else
- \ `loader_color' is unset.
- \ Default to using color unless serial boot is active.
- drop
- boot_serial? 0=
then
+ drop
+ \ It is enabled.
+ TRUE
;
diff --git a/stand/forth/frames.4th b/stand/forth/frames.4th
--- a/stand/forth/frames.4th
+++ b/stand/forth/frames.4th
@@ -121,6 +121,20 @@
;
: box ( w h x y -- ) \ Draw a box
+ framebuffer? if
+ s" term-drawrect" sfind if
+ >R
+ rot ( w x y h )
+ over + >R ( w x y -- R: y+h )
+ swap rot ( y x w -- R: y+h )
+ over + >R ( y x -- R: y+h x+w )
+ swap R> R> R> execute
+ exit
+ else
+ drop
+ then
+ then
+ \ Non-framebuffer version
2dup 1+ 4 pick 1- -rot
vline \ Draw left vert line
2dup 1+ swap 5 pick + swap 4 pick 1- -rot
diff --git a/stand/forth/logo-orb.4th b/stand/forth/logo-orb.4th
--- a/stand/forth/logo-orb.4th
+++ b/stand/forth/logo-orb.4th
@@ -35,6 +35,18 @@
: logo ( x y -- ) \ color Orb mascot (15 rows x 30 columns)
+ framebuffer? if
+ s" term-putimage" sfind if
+ >r 2dup ( x y x y )
+ >r 0 swap r> ( x y 0 x y )
+ dup 0 swap 15 + ( x y 0 x y 0 y+15 )
+ s" /boot/images/freebsd-logo-rev.png"
+ r> execute if 2drop exit then
+ else
+ drop
+ then
+ then
+
s" @[31m``` @[31;1m`@[31m" logo+
s" s` `.....---...@[31;1m....--.``` -/@[31m" logo+
s" +o .--` @[31;1m/y:` +.@[31m" logo+
diff --git a/stand/forth/menu.4th b/stand/forth/menu.4th
--- a/stand/forth/menu.4th
+++ b/stand/forth/menu.4th
@@ -991,6 +991,24 @@
menu-create
;
+: menu-box
+ f_double ( default frame type )
+ \ Interpret a custom frame type for the menu
+ TRUE ( draw a box? default yes, but might be altered below )
+ s" loader_menu_frame" getenv dup -1 = if ( 1 )
+ drop \ no custom frame type
+ else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 )
+ f_single ( see frames.4th )
+ else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 )
+ f_double ( see frames.4th )
+ else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
+ drop FALSE \ don't draw a box
+ ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then
+ if
+ 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
+ then
+;
+
\ This function initializes the menu. Call this from your `loader.rc' file
\ before calling any other menu-related functions.
\
@@ -1021,21 +1039,7 @@
then
menuX !
- \ Interpret a custom frame type for the menu
- TRUE ( draw a box? default yes, but might be altered below )
- s" loader_menu_frame" getenv dup -1 = if ( 1 )
- drop \ no custom frame type
- else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 )
- f_single ( see frames.4th )
- else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 )
- f_double ( see frames.4th )
- else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
- drop FALSE \ don't draw a box
- ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then
- if
- 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
- then
-
+ ['] menu-box console-iterate
0 25 at-xy \ Move cursor to the bottom for output
;
diff --git a/stand/forth/support.4th b/stand/forth/support.4th
--- a/stand/forth/support.4th
+++ b/stand/forth/support.4th
@@ -190,6 +190,25 @@
0 0
;
+: strspn { addr len addr1 len1 | paddr plen -- addr' len' }
+ begin
+ len
+ while
+ addr1 to paddr
+ len1 to plen
+ begin
+ plen
+ while
+ addr c@ paddr c@ = if addr len exit then
+ paddr 1+ to paddr
+ plen 1- to plen
+ repeat
+ addr 1 + to addr
+ len 1 - to len
+ repeat
+ 0 0
+;
+
: s' \ same as s", allows " in the string
[char] ' parse
state @ if postpone sliteral then
@@ -201,6 +220,53 @@
: getenv? getenv -1 = if false else drop true then ;
+\ execute xt for each device listed in console variable.
+\ this allows us to have device specific output for logos, menu frames etc
+: console-iterate { xt | caddr clen taddr tlen -- }
+ \ get current console and save it
+ s" console" getenv
+ ['] strdup catch if 2drop exit then
+ to clen to caddr
+
+ clen to tlen
+ caddr to taddr
+ begin
+ tlen
+ while
+ taddr tlen s" , " strspn
+ \ we need to handle 3 cases for addr len pairs on stack:
+ \ addr len are 0 0 - there was no comma nor space
+ \ addr len are x 0 - the first char is either comma or space
+ \ addr len are x y.
+ 2dup + 0= if
+ \ there was no comma nor space.
+ 2drop
+ taddr tlen s" console" setenv
+ xt execute
+ 0 to tlen
+ else dup 0= if
+ 2drop
+ else
+ dup ( taddr' tlen' tlen' )
+ tlen swap - dup
+ 0= if \ sequence of comma and space?
+ drop
+ else
+ taddr swap s" console" setenv
+ xt execute
+ then
+ to tlen
+ to taddr
+ then then
+ tlen 0> if \ step over separator
+ tlen 1- to tlen
+ taddr 1+ to taddr
+ then
+ repeat
+ caddr clen s" console" setenv \ restore console setup
+ caddr free drop
+;
+
\ determine if a word appears in a string, case-insensitive
: contains? ( addr1 len1 addr2 len2 -- 0 | -1 )
2 pick 0= if 2drop 2drop true exit then
@@ -231,14 +297,23 @@
s" console" getenv dup -1 <> if
s" comconsole" 2swap contains?
else drop false then
- s" boot_serial" getenv dup -1 <> if
- swap drop 0>
- else drop false then
- or \ console contains comconsole ( or ) boot_serial
- s" boot_multicons" getenv dup -1 <> if
- swap drop 0>
- else drop false then
- or \ previous boolean ( or ) boot_multicons
+\ s" boot_serial" getenv dup -1 <> if
+\ swap drop 0>
+\ else drop false then
+\ or \ console contains comconsole ( or ) boot_serial
+\ s" boot_multicons" getenv dup -1 <> if
+\ swap drop 0>
+\ else drop false then
+\ or \ previous boolean ( or ) boot_multicons
+;
+
+: framebuffer? ( -- t )
+ s" console" getenv
+ 2dup s" efi" compare 0<> >r
+ s" vidconsole" compare 0<> r> and if
+ FALSE exit
+ then
+ s" screen.depth" getenv?
;
\ Private definitions
diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile
--- a/stand/i386/libi386/Makefile
+++ b/stand/i386/libi386/Makefile
@@ -9,7 +9,7 @@
comconsole.c devicename.c elf32_freebsd.c \
elf64_freebsd.c multiboot.c multiboot_tramp.S relocater_tramp.S \
i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.S \
- time.c vidconsole.c amd64_tramp.S spinconsole.c
+ time.c vidconsole.c vbe.c amd64_tramp.S spinconsole.c
.PATH: ${ZFSSRC}
SRCS+= devicename_stubs.c
CFLAGS+= -I${ZFSSRC}
@@ -29,8 +29,13 @@
.endif
# terminal emulation
-CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken
+.if ${BOOT_FRAMEBUFFER_MODE:Uno} == "yes"
+CFLAGS.vidconsole.c+= -DFRAMEBUFFER_MODE
+.endif
+CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
CFLAGS.teken.c+= -I${SRCTOP}/sys/teken
+CFLAGS.bootinfo.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
+CFLAGS.vbe.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
# XXX: make alloca() useable
CFLAGS+= -Dalloca=__builtin_alloca
diff --git a/stand/i386/libi386/bootinfo.c b/stand/i386/libi386/bootinfo.c
--- a/stand/i386/libi386/bootinfo.c
+++ b/stand/i386/libi386/bootinfo.c
@@ -32,10 +32,21 @@
#include <sys/reboot.h>
#include <sys/boot.h>
#include <sys/linker.h>
+#include <gfx_fb.h>
#include "bootstrap.h"
#include "libi386.h"
+#include "vbe.h"
#include "btxv86.h"
+void
+bi_load_vbe_data(struct preloaded_file *kfp)
+{
+ if (vbe_available()) {
+ file_addmetadata(kfp, MODINFOMD_VBE_FB,
+ sizeof(gfx_state.tg_fb), &gfx_state.tg_fb);
+ }
+}
+
int
bi_getboothowto(char *kargs)
{
diff --git a/stand/i386/libi386/bootinfo32.c b/stand/i386/libi386/bootinfo32.c
--- a/stand/i386/libi386/bootinfo32.c
+++ b/stand/i386/libi386/bootinfo32.c
@@ -205,6 +205,8 @@
/* pad to a page boundary */
addr = roundup(addr, PAGE_SIZE);
+ addr = build_font_module(addr);
+
/* copy our environment */
envp = addr;
addr = bi_copyenv(addr);
@@ -225,6 +227,7 @@
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
+ bi_load_vbe_data(kfp);
/* Figure out the size and location of the metadata */
*modulep = addr;
diff --git a/stand/i386/libi386/bootinfo64.c b/stand/i386/libi386/bootinfo64.c
--- a/stand/i386/libi386/bootinfo64.c
+++ b/stand/i386/libi386/bootinfo64.c
@@ -227,6 +227,8 @@
/* pad to a page boundary */
addr = roundup(addr, PAGE_SIZE);
+ addr = build_font_module(addr);
+
/* place the metadata before anything */
module = *modulep = addr;
@@ -245,6 +247,7 @@
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
+ bi_load_vbe_data(kfp);
size = bi_copymodules64(0);
diff --git a/stand/i386/libi386/libi386.h b/stand/i386/libi386/libi386.h
--- a/stand/i386/libi386/libi386.h
+++ b/stand/i386/libi386/libi386.h
@@ -145,6 +145,7 @@
int i386_autoload(void);
+void bi_load_vbe_data(struct preloaded_file *kfp);
int bi_getboothowto(char *kargs);
void bi_setboothowto(int howto);
vm_offset_t bi_copyenv(vm_offset_t addr);
diff --git a/stand/i386/libi386/vbe.h b/stand/i386/libi386/vbe.h
new file mode 100644
--- /dev/null
+++ b/stand/i386/libi386/vbe.h
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Default mode for VESA frame buffer.
+ * This mode is selected when there is no EDID inormation and
+ * mode is not provided by user.
+ * To provide consistent look with UEFI GOP, we use 800x600 here,
+ * and if this mode is not available, we fall back to text mode and
+ * VESA disabled.
+ */
+
+#define VBE_DEFAULT_MODE "800x600"
+
+struct vbeinfoblock {
+ char VbeSignature[4];
+ uint16_t VbeVersion;
+ uint32_t OemStringPtr;
+ uint32_t Capabilities;
+#define VBE_CAP_DAC8 (1 << 0) /* Can switch DAC */
+#define VBE_CAP_NONVGA (1 << 1) /* Controller is not VGA comp. */
+#define VBE_CAP_SNOW (1 << 2) /* Set data during Vertical Reterace */
+ uint32_t VideoModePtr;
+ uint16_t TotalMemory;
+ uint16_t OemSoftwareRev;
+ uint32_t OemVendorNamePtr, OemProductNamePtr, OemProductRevPtr;
+ /* data area, in total max 512 bytes for VBE 2.0 */
+ uint8_t Reserved[222];
+ uint8_t OemData[256];
+} __packed;
+
+struct modeinfoblock {
+ /* Mandatory information for all VBE revisions */
+ uint16_t ModeAttributes;
+ uint8_t WinAAttributes, WinBAttributes;
+ uint16_t WinGranularity, WinSize, WinASegment, WinBSegment;
+ uint32_t WinFuncPtr;
+ uint16_t BytesPerScanLine;
+ /* Mandatory information for VBE 1.2 and above */
+ uint16_t XResolution, YResolution;
+ uint8_t XCharSize, YCharSize, NumberOfPlanes, BitsPerPixel;
+ uint8_t NumberOfBanks, MemoryModel, BankSize, NumberOfImagePages;
+ uint8_t Reserved1;
+ /* Direct Color fields
+ (required for direct/6 and YUV/7 memory models) */
+ uint8_t RedMaskSize, RedFieldPosition;
+ uint8_t GreenMaskSize, GreenFieldPosition;
+ uint8_t BlueMaskSize, BlueFieldPosition;
+ uint8_t RsvdMaskSize, RsvdFieldPosition;
+ uint8_t DirectColorModeInfo;
+ /* Mandatory information for VBE 2.0 and above */
+ uint32_t PhysBasePtr;
+ uint32_t OffScreenMemOffset; /* reserved in VBE 3.0 and above */
+ uint16_t OffScreenMemSize; /* reserved in VBE 3.0 and above */
+
+ /* Mandatory information for VBE 3.0 and above */
+ uint16_t LinBytesPerScanLine;
+ uint8_t BnkNumberOfImagePages;
+ uint8_t LinNumberOfImagePages;
+ uint8_t LinRedMaskSize, LinRedFieldPosition;
+ uint8_t LinGreenMaskSize, LinGreenFieldPosition;
+ uint8_t LinBlueMaskSize, LinBlueFieldPosition;
+ uint8_t LinRsvdMaskSize, LinRsvdFieldPosition;
+ uint32_t MaxPixelClock;
+ /* + 1 will fix the size to 256 bytes */
+ uint8_t Reserved4[189 + 1];
+} __packed;
+
+struct crtciinfoblock {
+ uint16_t HorizontalTotal;
+ uint16_t HorizontalSyncStart;
+ uint16_t HorizontalSyncEnd;
+ uint16_t VerticalTotal;
+ uint16_t VerticalSyncStart;
+ uint16_t VerticalSyncEnd;
+ uint8_t Flags;
+ uint32_t PixelClock;
+ uint16_t RefreshRate;
+ uint8_t Reserved[40];
+} __packed;
+
+struct paletteentry {
+ uint8_t Blue;
+ uint8_t Green;
+ uint8_t Red;
+ uint8_t Reserved;
+} __packed;
+
+struct flatpanelinfo
+{
+ uint16_t HorizontalSize;
+ uint16_t VerticalSize;
+ uint16_t PanelType;
+ uint8_t RedBPP;
+ uint8_t GreenBPP;
+ uint8_t BlueBPP;
+ uint8_t ReservedBPP;
+ uint32_t ReservedOffScreenMemSize;
+ uint32_t ReservedOffScreenMemPtr;
+
+ uint8_t Reserved[14];
+} __packed;
+
+#define VBE_BASE_MODE (0x100) /* VBE 3.0 page 18 */
+#define VBE_VALID_MODE(a) ((a) >= VBE_BASE_MODE)
+#define VBE_ERROR(a) (((a) & 0xFF) != 0x4F || ((a) & 0xFF00) != 0)
+#define VBE_SUCCESS (0x004F)
+#define VBE_FAILED (0x014F)
+#define VBE_NOTSUP (0x024F)
+#define VBE_INVALID (0x034F)
+
+#define VGA_TEXT_MODE (3) /* 80x25 text mode */
+#define TEXT_ROWS (25) /* VGATEXT rows */
+#define TEXT_COLS (80) /* VGATEXT columns */
+
+extern struct paletteentry *pe8;
+extern int palette_format;
+
+int vga_get_reg(int, int);
+int vga_get_atr(int, int);
+void vga_set_atr(int, int, int);
+void vga_set_indexed(int, int, int, uint8_t, uint8_t);
+int vga_get_indexed(int, int, int, uint8_t);
+int vga_get_crtc(int, int);
+void vga_set_crtc(int, int, int);
+int vga_get_seq(int, int);
+void vga_set_seq(int, int, int);
+int vga_get_grc(int, int);
+void vga_set_grc(int, int, int);
+
+/* high-level VBE helpers, from vbe.c */
+void bios_set_text_mode(int);
+int biosvbe_palette_format(int *);
+void vbe_init(void);
+bool vbe_available(void);
+int vbe_default_mode(void);
+int vbe_set_mode(int);
+int vbe_get_mode(void);
+int vbe_set_palette(const struct paletteentry *, size_t);
+void vbe_modelist(int);
diff --git a/stand/i386/libi386/vbe.c b/stand/i386/libi386/vbe.c
new file mode 100644
--- /dev/null
+++ b/stand/i386/libi386/vbe.c
@@ -0,0 +1,1226 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ * Copyright 2020 Toomas Soome
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <stand.h>
+#include <sys/param.h>
+#include <machine/psl.h>
+#include <machine/cpufunc.h>
+#include <stdbool.h>
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <gfx_fb.h>
+#include <dev/vt/hw/vga/vt_vga_reg.h>
+#include "libi386.h"
+#include "vbe.h"
+
+/*
+ * VESA BIOS Extensions routines
+ */
+
+static struct vbeinfoblock *vbe;
+static struct modeinfoblock *vbe_mode;
+/* The default VGA color palette format is 6 bits per primary color. */
+int palette_format = 6;
+
+#define VESA_MODE_BASE 0x100
+#define VESA_MODE_MAX 0x1ff
+#define VESA_MODE_COUNT (VESA_MODE_MAX - VESA_MODE_BASE + 1)
+
+/*
+ * palette array for 8-bit indexed colors. In this case, cmap does store
+ * index and pe8 does store actual RGB. This is needed because we may
+ * not be able to read palette data from hardware.
+ */
+struct paletteentry *pe8 = NULL;
+
+static struct named_resolution {
+ const char *name;
+ const char *alias;
+ unsigned int width;
+ unsigned int height;
+} resolutions[] = {
+ {
+ .name = "480p",
+ .width = 640,
+ .height = 480,
+ },
+ {
+ .name = "720p",
+ .width = 1280,
+ .height = 720,
+ },
+ {
+ .name = "1080p",
+ .width = 1920,
+ .height = 1080,
+ },
+ {
+ .name = "2160p",
+ .alias = "4k",
+ .width = 3840,
+ .height = 2160,
+ },
+ {
+ .name = "5k",
+ .width = 5120,
+ .height = 2880,
+ }
+};
+
+static bool
+vbe_resolution_compare(struct named_resolution *res, const char *cmp)
+{
+
+ if (strcasecmp(res->name, cmp) == 0)
+ return (true);
+ if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
+ return (true);
+ return (false);
+}
+
+static void
+vbe_get_max_resolution(int *width, int *height)
+{
+ struct named_resolution *res;
+ char *maxres;
+ char *height_start, *width_start;
+ int idx;
+
+ *width = *height = 0;
+ maxres = getenv("vbe_max_resolution");
+ /* No max_resolution set? Bail out; choose highest resolution */
+ if (maxres == NULL)
+ return;
+ /* See if it matches one of our known resolutions */
+ for (idx = 0; idx < nitems(resolutions); ++idx) {
+ res = &resolutions[idx];
+ if (vbe_resolution_compare(res, maxres)) {
+ *width = res->width;
+ *height = res->height;
+ return;
+ }
+ }
+ /* Not a known resolution, try to parse it; make a copy we can modify */
+ maxres = strdup(maxres);
+ if (maxres == NULL)
+ return;
+ height_start = strchr(maxres, 'x');
+ if (height_start == NULL) {
+ free(maxres);
+ return;
+ }
+ width_start = maxres;
+ *height_start++ = 0;
+ /* Errors from this will effectively mean "no max" */
+ *width = (int)strtol(width_start, NULL, 0);
+ *height = (int)strtol(height_start, NULL, 0);
+ free(maxres);
+}
+
+int
+vga_get_reg(int reg, int index)
+{
+ return (inb(reg + index));
+}
+
+int
+vga_get_atr(int reg, int i)
+{
+ int ret;
+
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+ outb(reg + VGA_AC_WRITE, i);
+ ret = inb(reg + VGA_AC_READ);
+
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+
+ return (ret);
+}
+
+void
+vga_set_atr(int reg, int i, int v)
+{
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+ outb(reg + VGA_AC_WRITE, i);
+ outb(reg + VGA_AC_WRITE, v);
+
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+}
+
+void
+vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val)
+{
+ outb(reg + indexreg, index);
+ outb(reg + datareg, val);
+}
+
+int
+vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index)
+{
+ outb(reg + indexreg, index);
+ return (inb(reg + datareg));
+}
+
+int
+vga_get_crtc(int reg, int i)
+{
+ return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i));
+}
+
+void
+vga_set_crtc(int reg, int i, int v)
+{
+ vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
+}
+
+int
+vga_get_seq(int reg, int i)
+{
+ return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i));
+}
+
+void
+vga_set_seq(int reg, int i, int v)
+{
+ vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v);
+}
+
+int
+vga_get_grc(int reg, int i)
+{
+ return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i));
+}
+
+void
+vga_set_grc(int reg, int i, int v)
+{
+ vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v);
+}
+
+/* Actually assuming mode 3. */
+void
+bios_set_text_mode(int mode)
+{
+ int atr;
+
+ if (vbe->Capabilities & VBE_CAP_DAC8) {
+ int m;
+
+ /*
+ * The mode change should reset the palette format to
+ * 6 bits, but apparently some systems do fail with 8-bit
+ * palette, so we switch to 6-bit here.
+ */
+ m = 0x0600;
+ (void) biosvbe_palette_format(&m);
+ palette_format = m;
+ }
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = mode; /* set VGA text mode */
+ v86int();
+ atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL);
+ atr &= ~VGA_AC_MC_BI;
+ atr &= ~VGA_AC_MC_ELG;
+ vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr);
+
+ gfx_state.tg_mode = mode;
+ gfx_state.tg_fb_type = FB_TEXT;
+ gfx_state.tg_fb.fb_height = TEXT_ROWS;
+ gfx_state.tg_fb.fb_width = TEXT_COLS;
+
+ gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16;
+ gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8;
+ gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0;
+ gfx_state.tg_ctype = CT_INDEXED;
+ env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL);
+}
+
+/* Function 00h - Return VBE Controller Information */
+static int
+biosvbe_info(struct vbeinfoblock *vbep)
+{
+ struct vbeinfoblock *rvbe;
+ int ret;
+
+ if (vbep == NULL)
+ return (VBE_FAILED);
+
+ rvbe = bio_alloc(sizeof(*rvbe));
+ if (rvbe == NULL)
+ return (VBE_FAILED);
+
+ /* Now check if we have vesa. */
+ memset(rvbe, 0, sizeof (*vbe));
+ memcpy(rvbe->VbeSignature, "VBE2", 4);
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f00;
+ v86.es = VTOPSEG(rvbe);
+ v86.edi = VTOPOFF(rvbe);
+ v86int();
+ ret = v86.eax & 0xffff;
+
+ if (ret != VBE_SUCCESS)
+ goto done;
+
+ if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) {
+ ret = VBE_NOTSUP;
+ goto done;
+ }
+ bcopy(rvbe, vbep, sizeof(*vbep));
+done:
+ bio_free(rvbe, sizeof(*rvbe));
+ return (ret);
+}
+
+/* Function 01h - Return VBE Mode Information */
+static int
+biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
+{
+ struct modeinfoblock *rmi;
+ int ret;
+
+ rmi = bio_alloc(sizeof(*rmi));
+ if (rmi == NULL)
+ return (VBE_FAILED);
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f01;
+ v86.ecx = mode;
+ v86.es = VTOPSEG(rmi);
+ v86.edi = VTOPOFF(rmi);
+ v86int();
+
+ ret = v86.eax & 0xffff;
+ if (ret != VBE_SUCCESS)
+ goto done;
+ bcopy(rmi, mi, sizeof(*rmi));
+done:
+ bio_free(rmi, sizeof(*rmi));
+ return (ret);
+}
+
+/* Function 02h - Set VBE Mode */
+static int
+biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
+{
+ int rv;
+
+ if (vbe->Capabilities & VBE_CAP_DAC8) {
+ int m;
+
+ /*
+ * The mode change should reset the palette format to
+ * 6 bits, but apparently some systems do fail with 8-bit
+ * palette, so we switch to 6-bit here.
+ */
+ m = 0x0600;
+ if (biosvbe_palette_format(&m) == VBE_SUCCESS)
+ palette_format = m;
+ }
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f02;
+ v86.ebx = mode | 0x4000; /* set linear FB bit */
+ v86.es = VTOPSEG(ci);
+ v86.edi = VTOPOFF(ci);
+ v86int();
+ rv = v86.eax & 0xffff;
+ if (vbe->Capabilities & VBE_CAP_DAC8) {
+ int m;
+
+ /* Switch to 8-bits per primary color. */
+ m = 0x0800;
+ if (biosvbe_palette_format(&m) == VBE_SUCCESS)
+ palette_format = m;
+ }
+ env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL);
+ return (rv);
+}
+
+/* Function 03h - Get VBE Mode */
+static int
+biosvbe_get_mode(int *mode)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f03;
+ v86int();
+ *mode = v86.ebx & 0x3fff; /* Bits 0-13 */
+ return (v86.eax & 0xffff);
+}
+
+/* Function 08h - Set/Get DAC Palette Format */
+int
+biosvbe_palette_format(int *format)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f08;
+ v86.ebx = *format;
+ v86int();
+ *format = (v86.ebx >> 8) & 0xff;
+ return (v86.eax & 0xffff);
+}
+
+/* Function 09h - Set/Get Palette Data */
+static int
+biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f09;
+ v86.ebx = mode;
+ v86.edx = reg;
+ v86.ecx = 1;
+ v86.es = VTOPSEG(pe);
+ v86.edi = VTOPOFF(pe);
+ v86int();
+ return (v86.eax & 0xffff);
+}
+
+/*
+ * Function 15h BL=00h - Report VBE/DDC Capabilities
+ *
+ * int biosvbe_ddc_caps(void)
+ * return: VBE/DDC capabilities
+ */
+static int
+biosvbe_ddc_caps(void)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f15; /* display identification extensions */
+ v86.ebx = 0; /* report DDC capabilities */
+ v86.ecx = 0; /* controller unit number (00h = primary) */
+ v86.es = 0;
+ v86.edi = 0;
+ v86int();
+ if (VBE_ERROR(v86.eax & 0xffff))
+ return (0);
+ return (v86.ebx & 0xffff);
+}
+
+/* Function 11h BL=01h - Flat Panel status */
+static int
+biosvbe_ddc_read_flat_panel_info(void *buf)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f11; /* Flat Panel Interface extensions */
+ v86.ebx = 1; /* Return Flat Panel Information */
+ v86.es = VTOPSEG(buf);
+ v86.edi = VTOPOFF(buf);
+ v86int();
+ return (v86.eax & 0xffff);
+}
+
+/* Function 15h BL=01h - Read EDID */
+static int
+biosvbe_ddc_read_edid(int blockno, void *buf)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f15; /* display identification extensions */
+ v86.ebx = 1; /* read EDID */
+ v86.ecx = 0; /* controller unit number (00h = primary) */
+ v86.edx = blockno;
+ v86.es = VTOPSEG(buf);
+ v86.edi = VTOPOFF(buf);
+ v86int();
+ return (v86.eax & 0xffff);
+}
+
+static int
+vbe_mode_is_supported(struct modeinfoblock *mi)
+{
+ if ((mi->ModeAttributes & 0x01) == 0)
+ return (0); /* mode not supported by hardware */
+ if ((mi->ModeAttributes & 0x08) == 0)
+ return (0); /* linear fb not available */
+ if ((mi->ModeAttributes & 0x10) == 0)
+ return (0); /* text mode */
+ if (mi->NumberOfPlanes != 1)
+ return (0); /* planar mode not supported */
+ if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
+ mi->MemoryModel != 0x06 /* Direct Color */)
+ return (0); /* unsupported pixel format */
+ return (1);
+}
+
+static bool
+vbe_check(void)
+{
+
+ if (vbe == NULL) {
+ printf("VBE not available\n");
+ return (false);
+ }
+ return (true);
+}
+
+static int
+mode_set(struct env_var *ev, int flags __unused, const void *value)
+{
+ int mode;
+
+ if (strcmp(ev->ev_name, "hw.vga.textmode") == 0) {
+ unsigned long v;
+ char *end;
+
+ if (value == NULL)
+ return (0);
+ /* VT(4) describes hw.vga.textmode values 0 or 1. */
+ errno = 0;
+ v = strtoul(value, &end, 0);
+ if (errno != 0 || *(char *)value == '\0' || *end != '\0' ||
+ (v != 0 && v != 1))
+ return (EINVAL);
+ env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK,
+ value, NULL, NULL);
+ if (v == 1) {
+ reset_font_flags();
+ bios_text_font(true);
+ bios_set_text_mode(VGA_TEXT_MODE);
+ (void) cons_update_mode(false);
+ return (0);
+ }
+ } else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) {
+ env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK,
+ value, NULL, NULL);
+ } else {
+ return (EINVAL);
+ }
+
+ mode = vbe_default_mode();
+ if (gfx_state.tg_mode != mode) {
+ reset_font_flags();
+ bios_text_font(false);
+ vbe_set_mode(mode);
+ cons_update_mode(true);
+ }
+ return (0);
+}
+
+void
+vbe_init(void)
+{
+ /* First set FB for text mode. */
+ gfx_state.tg_fb_type = FB_TEXT;
+ gfx_state.tg_fb.fb_height = TEXT_ROWS;
+ gfx_state.tg_fb.fb_width = TEXT_COLS;
+ gfx_state.tg_ctype = CT_INDEXED;
+ gfx_state.tg_mode = 3;
+
+ if (vbe == NULL)
+ vbe = malloc(sizeof(*vbe));
+
+ if (vbe_mode == NULL) {
+ vbe_mode = malloc(sizeof(*vbe_mode));
+ if (vbe_mode == NULL) {
+ free(vbe);
+ vbe = NULL;
+ }
+ }
+
+ if (biosvbe_info(vbe) != VBE_SUCCESS) {
+ free(vbe);
+ vbe = NULL;
+ free(vbe_mode);
+ vbe_mode = NULL;
+ }
+
+ env_setenv("hw.vga.textmode", EV_VOLATILE, "1", mode_set,
+ env_nounset);
+ env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set,
+ env_nounset);
+ /* vbe_set_mode() will set up the rest. */
+}
+
+bool
+vbe_available(void)
+{
+ return (gfx_state.tg_fb_type == FB_VBE);
+}
+
+int
+vbe_set_palette(const struct paletteentry *entry, size_t slot)
+{
+ struct paletteentry pe;
+ int mode, ret;
+
+ if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0)
+ return (1);
+
+ if (gfx_state.tg_ctype != CT_INDEXED) {
+ return (1);
+ }
+
+ pe.Blue = entry->Blue;
+ pe.Green = entry->Green;
+ pe.Red = entry->Red;
+ pe.Reserved = entry->Reserved;
+
+ if (vbe->Capabilities & VBE_CAP_SNOW)
+ mode = 0x80;
+ else
+ mode = 0;
+
+ ret = biosvbe_palette_data(mode, slot, &pe);
+
+ return (ret == VBE_SUCCESS ? 0 : 1);
+}
+
+int
+vbe_get_mode(void)
+{
+ return (gfx_state.tg_mode);
+}
+
+int
+vbe_set_mode(int modenum)
+{
+ struct modeinfoblock mi;
+ int bpp, ret;
+
+ if (!vbe_check())
+ return (1);
+
+ ret = biosvbe_get_mode_info(modenum, &mi);
+ if (VBE_ERROR(ret)) {
+ printf("mode 0x%x invalid\n", modenum);
+ return (1);
+ }
+
+ if (!vbe_mode_is_supported(&mi)) {
+ printf("mode 0x%x not supported\n", modenum);
+ return (1);
+ }
+
+ /* calculate bytes per pixel */
+ switch (mi.BitsPerPixel) {
+ case 32:
+ case 24:
+ case 16:
+ case 15:
+ case 8:
+ break;
+ default:
+ printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
+ return (1);
+ }
+
+ ret = biosvbe_set_mode(modenum, NULL);
+ if (VBE_ERROR(ret)) {
+ printf("mode 0x%x could not be set\n", modenum);
+ return (1);
+ }
+
+ gfx_state.tg_mode = modenum;
+ gfx_state.tg_fb_type = FB_VBE;
+ /* make sure we have current MI in vbestate */
+ memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
+
+ gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff;
+ gfx_state.tg_fb.fb_height = mi.YResolution;
+ gfx_state.tg_fb.fb_width = mi.XResolution;
+ gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel;
+
+ /* Bytes per pixel */
+ bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY;
+
+ /* vbe_mode_is_supported() excludes the rest */
+ switch (mi.MemoryModel) {
+ case 0x4:
+ gfx_state.tg_ctype = CT_INDEXED;
+ break;
+ case 0x6:
+ gfx_state.tg_ctype = CT_RGB;
+ break;
+ }
+
+#define COLOR_MASK(size, pos) (((1 << size) - 1) << pos)
+ if (gfx_state.tg_ctype == CT_INDEXED) {
+ gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16);
+ gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8);
+ gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0);
+ } else if (vbe->VbeVersion >= 0x300) {
+ gfx_state.tg_fb.fb_mask_red =
+ COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition);
+ gfx_state.tg_fb.fb_mask_green =
+ COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition);
+ gfx_state.tg_fb.fb_mask_blue =
+ COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition);
+ } else {
+ gfx_state.tg_fb.fb_mask_red =
+ COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition);
+ gfx_state.tg_fb.fb_mask_green =
+ COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition);
+ gfx_state.tg_fb.fb_mask_blue =
+ COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition);
+ }
+ gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red |
+ gfx_state.tg_fb.fb_mask_green |
+ gfx_state.tg_fb.fb_mask_blue);
+
+ if (vbe->VbeVersion >= 0x300)
+ gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp;
+ else
+ gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp;
+
+ gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride *
+ bpp;
+
+ return (0);
+}
+
+static void *
+vbe_farptr(uint32_t farptr)
+{
+ return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
+}
+
+/*
+ * Verify existance of mode number or find mode by
+ * dimensions. If depth is not given, walk values 32, 24, 16, 8.
+ */
+static int
+vbe_find_mode_xydm(int x, int y, int depth, int m)
+{
+ struct modeinfoblock mi;
+ uint32_t farptr;
+ uint16_t mode;
+ int safety, i;
+
+ memset(vbe, 0, sizeof (*vbe));
+ if (biosvbe_info(vbe) != VBE_SUCCESS)
+ return (0);
+ if (vbe->VideoModePtr == 0)
+ return (0);
+
+ if (m != -1)
+ i = 8;
+ else if (depth == -1)
+ i = 32;
+ else
+ i = depth;
+
+ while (i > 0) {
+ farptr = vbe->VideoModePtr;
+ safety = 0;
+ while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
+ safety++;
+ farptr += 2;
+ if (safety == VESA_MODE_COUNT)
+ break;
+ if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) {
+ continue;
+ }
+ /* we only care about linear modes here */
+ if (vbe_mode_is_supported(&mi) == 0)
+ continue;
+
+ if (m != -1) {
+ if (m == mode)
+ return (mode);
+ else
+ continue;
+ }
+
+ if (mi.XResolution == x &&
+ mi.YResolution == y &&
+ mi.BitsPerPixel == i)
+ return (mode);
+ }
+ if (depth != -1)
+ break;
+
+ i -= 8;
+ }
+
+ return (0);
+}
+
+static int
+vbe_find_mode(char *str)
+{
+ int x, y, depth;
+
+ if (!gfx_parse_mode_str(str, &x, &y, &depth))
+ return (0);
+
+ return (vbe_find_mode_xydm(x, y, depth, -1));
+}
+
+static void
+vbe_dump_mode(int modenum, struct modeinfoblock *mi)
+{
+ printf("0x%x=%dx%dx%d", modenum,
+ mi->XResolution, mi->YResolution, mi->BitsPerPixel);
+}
+
+static bool
+vbe_get_edid(edid_res_list_t *res)
+{
+ struct vesa_edid_info *edid_info;
+ const uint8_t magic[] = EDID_MAGIC;
+ int ddc_caps;
+ bool ret = false;
+
+ ddc_caps = biosvbe_ddc_caps();
+ if (ddc_caps == 0) {
+ return (ret);
+ }
+
+ edid_info = bio_alloc(sizeof (*edid_info));
+ if (edid_info == NULL)
+ return (ret);
+ memset(edid_info, 0, sizeof (*edid_info));
+
+ if (VBE_ERROR(biosvbe_ddc_read_edid(0, edid_info)))
+ goto done;
+
+ if (memcmp(edid_info, magic, sizeof (magic)) != 0)
+ goto done;
+
+ /* Unknown EDID version. */
+ if (edid_info->header.version != 1)
+ goto done;
+
+ ret = gfx_get_edid_resolution(edid_info, res);
+done:
+ bio_free(edid_info, sizeof (*edid_info));
+ return (ret);
+}
+
+static bool
+vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight)
+{
+ struct vesa_flat_panel_info *fp_info;
+ bool ret = false;
+
+ fp_info = bio_alloc(sizeof (*fp_info));
+ if (fp_info == NULL)
+ return (ret);
+ memset(fp_info, 0, sizeof (*fp_info));
+
+ if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info)))
+ goto done;
+
+ *pwidth = fp_info->HSize;
+ *pheight = fp_info->VSize;
+ ret = true;
+
+done:
+ bio_free(fp_info, sizeof (*fp_info));
+ return (ret);
+}
+
+static void
+vbe_print_memory(unsigned vmem)
+{
+ char unit = 'K';
+
+ vmem /= 1024;
+ if (vmem >= 10240000) {
+ vmem /= 1048576;
+ unit = 'G';
+ } else if (vmem >= 10000) {
+ vmem /= 1024;
+ unit = 'M';
+ }
+ printf("Total memory: %u%cB\n", vmem, unit);
+}
+
+static void
+vbe_print_vbe_info(struct vbeinfoblock *vbep)
+{
+ char *oemstring = "";
+ char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
+
+ if (vbep->OemStringPtr != 0)
+ oemstring = vbe_farptr(vbep->OemStringPtr);
+
+ if (vbep->OemVendorNamePtr != 0)
+ oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
+
+ if (vbep->OemProductNamePtr != 0)
+ oemproductname = vbe_farptr(vbep->OemProductNamePtr);
+
+ if (vbep->OemProductRevPtr != 0)
+ oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
+
+ printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
+ vbep->VbeVersion & 0xF, oemstring);
+
+ if (vbep->OemSoftwareRev != 0) {
+ printf("OEM Version %d.%d, %s (%s, %s)\n",
+ vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
+ oemvendor, oemproductname, oemproductrev);
+ }
+ vbe_print_memory(vbep->TotalMemory << 16);
+ printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages);
+}
+
+/* List available modes, filter by depth. If depth is -1, list all. */
+void
+vbe_modelist(int depth)
+{
+ struct modeinfoblock mi;
+ uint32_t farptr;
+ uint16_t mode;
+ int nmodes = 0, safety = 0;
+ int ddc_caps;
+ uint32_t width, height;
+ bool edid = false;
+ edid_res_list_t res;
+ struct resolution *rp;
+
+ if (!vbe_check())
+ return;
+
+ ddc_caps = biosvbe_ddc_caps();
+ if (ddc_caps & 3) {
+ printf("DDC");
+ if (ddc_caps & 1)
+ printf(" [DDC1]");
+ if (ddc_caps & 2)
+ printf(" [DDC2]");
+
+ TAILQ_INIT(&res);
+ edid = vbe_get_edid(&res);
+ if (edid) {
+ printf(": EDID");
+ while ((rp = TAILQ_FIRST(&res)) != NULL) {
+ printf(" %dx%d", rp->width, rp->height);
+ TAILQ_REMOVE(&res, rp, next);
+ free(rp);
+ }
+ printf("\n");
+ } else {
+ printf(": no EDID information\n");
+ }
+ }
+ if (!edid)
+ if (vbe_get_flatpanel(&width, &height))
+ printf(": Panel %dx%d\n", width, height);
+
+ memset(vbe, 0, sizeof (*vbe));
+ memcpy(vbe->VbeSignature, "VBE2", 4);
+ if (biosvbe_info(vbe) != VBE_SUCCESS)
+ goto done;
+ if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
+ goto done;
+
+ vbe_print_vbe_info(vbe);
+ printf("Modes: ");
+
+ farptr = vbe->VideoModePtr;
+ if (farptr == 0)
+ goto done;
+
+ while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
+ safety++;
+ farptr += 2;
+ if (safety == VESA_MODE_COUNT) {
+ printf("[?] ");
+ break;
+ }
+ if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
+ continue;
+ /* we only care about linear modes here */
+ if (vbe_mode_is_supported(&mi) == 0)
+ continue;
+
+ /* we found some mode so reset safety counter */
+ safety = 0;
+
+ /* apply requested filter */
+ if (depth != -1 && mi.BitsPerPixel != depth)
+ continue;
+
+ if (nmodes % 4 == 0)
+ printf("\n");
+ else
+ printf(" ");
+
+ vbe_dump_mode(mode, &mi);
+ nmodes++;
+ }
+
+done:
+ if (nmodes == 0)
+ printf("none found");
+ printf("\n");
+}
+
+static void
+vbe_print_mode(bool verbose __unused)
+{
+ int nc, mode, i, rc;
+
+ nc = NCOLORS;
+
+ memset(vbe, 0, sizeof (*vbe));
+ if (biosvbe_info(vbe) != VBE_SUCCESS)
+ return;
+
+ vbe_print_vbe_info(vbe);
+
+ if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
+ printf("Error getting current VBE mode\n");
+ return;
+ }
+
+ if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
+ vbe_mode_is_supported(vbe_mode) == 0) {
+ printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
+ return;
+ }
+
+ printf("\nCurrent VBE mode: ");
+ vbe_dump_mode(mode, vbe_mode);
+ printf("\n");
+
+ printf("%ux%ux%u, stride=%u\n",
+ gfx_state.tg_fb.fb_width,
+ gfx_state.tg_fb.fb_height,
+ gfx_state.tg_fb.fb_bpp,
+ gfx_state.tg_fb.fb_stride *
+ (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY));
+ printf(" frame buffer: address=%jx, size=%jx\n",
+ (uintmax_t)gfx_state.tg_fb.fb_addr,
+ (uintmax_t)gfx_state.tg_fb.fb_size);
+
+ if (vbe_mode->MemoryModel == 0x6) {
+ printf(" color mask: R=%08x, G=%08x, B=%08x\n",
+ gfx_state.tg_fb.fb_mask_red,
+ gfx_state.tg_fb.fb_mask_green,
+ gfx_state.tg_fb.fb_mask_blue);
+ pager_open();
+ for (i = 0; i < nc; i++) {
+ printf("%d: R=%02x, G=%02x, B=%02x %08x", i,
+ (cmap[i] & gfx_state.tg_fb.fb_mask_red) >>
+ ffs(gfx_state.tg_fb.fb_mask_red) - 1,
+ (cmap[i] & gfx_state.tg_fb.fb_mask_green) >>
+ ffs(gfx_state.tg_fb.fb_mask_green) - 1,
+ (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >>
+ ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]);
+ if (pager_output("\n") != 0)
+ break;
+ }
+ pager_close();
+ return;
+ }
+
+ mode = 1; /* get DAC palette width */
+ rc = biosvbe_palette_format(&mode);
+ if (rc != VBE_SUCCESS)
+ return;
+
+ printf(" palette format: %x bits per primary\n", mode);
+ if (pe8 == NULL)
+ return;
+
+ pager_open();
+ for (i = 0; i < nc; i++) {
+ printf("%d: R=%02x, G=%02x, B=%02x", i,
+ pe8[i].Red, pe8[i].Green, pe8[i].Blue);
+ if (pager_output("\n") != 0)
+ break;
+ }
+ pager_close();
+}
+
+/*
+ * Try EDID preferred mode, if EDID or the suggested mode is not available,
+ * then try flat panel information.
+ * Fall back to VBE_DEFAULT_MODE.
+ */
+int
+vbe_default_mode(void)
+{
+ edid_res_list_t res;
+ struct resolution *rp;
+ int modenum;
+ uint32_t width, height;
+
+ modenum = 0;
+ vbe_get_max_resolution(&width, &height);
+ if (width != 0 && height != 0)
+ modenum = vbe_find_mode_xydm(width, height, -1, -1);
+
+ TAILQ_INIT(&res);
+ if (vbe_get_edid(&res)) {
+ while ((rp = TAILQ_FIRST(&res)) != NULL) {
+ if (modenum == 0) {
+ modenum = vbe_find_mode_xydm(
+ rp->width, rp->height, -1, -1);
+ }
+ TAILQ_REMOVE(&res, rp, next);
+ free(rp);
+ }
+ }
+
+ if (modenum == 0 &&
+ vbe_get_flatpanel(&width, &height)) {
+ modenum = vbe_find_mode_xydm(width, height, -1, -1);
+ }
+
+ /* Still no mode? Fall back to default. */
+ if (modenum == 0)
+ modenum = vbe_find_mode(VBE_DEFAULT_MODE);
+ return (modenum);
+}
+
+COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa);
+
+int
+command_vesa(int argc, char *argv[])
+{
+ char *arg, *cp;
+ int modenum = -1, n;
+
+ if (!vbe_check())
+ return (CMD_OK);
+
+ if (argc < 2)
+ goto usage;
+
+ if (strcmp(argv[1], "list") == 0) {
+ n = -1;
+ if (argc != 2 && argc != 3)
+ goto usage;
+
+ if (argc == 3) {
+ arg = argv[2];
+ errno = 0;
+ n = strtoul(arg, &cp, 0);
+ if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
+ snprintf(command_errbuf,
+ sizeof (command_errbuf),
+ "depth should be an integer");
+ return (CMD_ERROR);
+ }
+ }
+ vbe_modelist(n);
+ return (CMD_OK);
+ }
+
+ if (strcmp(argv[1], "get") == 0) {
+ bool verbose = false;
+
+ if (argc != 2) {
+ if (argc > 3 || strcmp(argv[2], "-v") != 0)
+ goto usage;
+ verbose = true;
+ }
+ vbe_print_mode(verbose);
+ return (CMD_OK);
+ }
+
+ if (strcmp(argv[1], "off") == 0) {
+ if (argc != 2)
+ goto usage;
+
+ if (gfx_state.tg_mode == VGA_TEXT_MODE)
+ return (CMD_OK);
+
+ reset_font_flags();
+ bios_text_font(true);
+ bios_set_text_mode(VGA_TEXT_MODE);
+ cons_update_mode(false);
+ return (CMD_OK);
+ }
+
+ if (strcmp(argv[1], "on") == 0) {
+ if (argc != 2)
+ goto usage;
+
+ modenum = vbe_default_mode();
+ if (modenum == 0) {
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "%s: no suitable VBE mode number found", argv[0]);
+ return (CMD_ERROR);
+ }
+ } else if (strcmp(argv[1], "set") == 0) {
+ if (argc != 3)
+ goto usage;
+
+ if (strncmp(argv[2], "0x", 2) == 0) {
+ arg = argv[2];
+ errno = 0;
+ n = strtoul(arg, &cp, 0);
+ if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
+ snprintf(command_errbuf,
+ sizeof (command_errbuf),
+ "mode should be an integer");
+ return (CMD_ERROR);
+ }
+ modenum = vbe_find_mode_xydm(0, 0, 0, n);
+ } else if (strchr(argv[2], 'x') != NULL) {
+ modenum = vbe_find_mode(argv[2]);
+ }
+ } else {
+ goto usage;
+ }
+
+ if (modenum == 0) {
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "%s: mode %s not supported by firmware\n",
+ argv[0], argv[2]);
+ return (CMD_ERROR);
+ }
+
+ if (modenum >= VESA_MODE_BASE) {
+ if (gfx_state.tg_mode != modenum) {
+ reset_font_flags();
+ bios_text_font(false);
+ vbe_set_mode(modenum);
+ cons_update_mode(true);
+ }
+ return (CMD_OK);
+ } else {
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]);
+ return (CMD_ERROR);
+ }
+
+usage:
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "usage: %s on | off | get | list [depth] | "
+ "set <display or VBE mode number>", argv[0]);
+ return (CMD_ERROR);
+}
diff --git a/stand/i386/libi386/vidconsole.c b/stand/i386/libi386/vidconsole.c
--- a/stand/i386/libi386/vidconsole.c
+++ b/stand/i386/libi386/vidconsole.c
@@ -31,12 +31,13 @@
__FBSDID("$FreeBSD$");
#include <stand.h>
+#include <sys/param.h>
#include <bootstrap.h>
#include <btxv86.h>
-#include <machine/psl.h>
-#include <machine/cpufunc.h>
+#include <gfx_fb.h>
#include <teken.h>
#include <stdbool.h>
+#include "vbe.h"
#include <dev/vt/hw/vga/vt_vga_reg.h>
@@ -50,6 +51,7 @@
static void vidc_putchar(int c);
static int vidc_getchar(void);
static int vidc_ischar(void);
+static void cons_draw_frame(teken_attr_t *);
static int vidc_started;
static uint16_t *vgatext;
@@ -72,30 +74,16 @@
.tf_respond = vidc_cons_respond,
};
-teken_t teken;
-teken_pos_t tp;
-
-struct text_pixel {
- teken_char_t c;
- teken_attr_t a;
-};
-
-static struct text_pixel *buffer;
-
-#define NCOLORS 16
-
-/*
- * Between console's palette and VGA's one:
- * - blue and red are swapped (1 <-> 4)
- * - yellow and cyan are swapped (3 <-> 6)
- */
-static const int cons_to_vga_colors[NCOLORS] = {
- 0, 4, 2, 6, 1, 5, 3, 7,
- 8, 12, 10, 14, 9, 13, 11, 15
+static teken_funcs_t tfx = {
+ .tf_bell = vidc_cons_bell,
+ .tf_cursor = gfx_fb_cursor,
+ .tf_putchar = gfx_fb_putchar,
+ .tf_fill = gfx_fb_fill,
+ .tf_copy = gfx_fb_copy,
+ .tf_param = gfx_fb_param,
+ .tf_respond = vidc_cons_respond,
};
-#define TEXT_COLS 80
-#define TEXT_ROWS 25
#define KEYBUFSZ 10
static uint8_t keybuf[KEYBUFSZ]; /* keybuf for extended codes */
@@ -111,60 +99,26 @@
.c_ready = vidc_ischar
};
-static int
-vga_get_reg(int reg, int index)
-{
- return (inb(reg + index));
-}
-
-static int
-vga_get_atr(int reg, int i)
-{
- int ret;
-
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
- outb(reg + VGA_AC_WRITE, i);
- ret = inb(reg + VGA_AC_READ);
-
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
-
- return (ret);
-}
-
-static void
-vga_set_atr(int reg, int i, int v)
-{
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
- outb(reg + VGA_AC_WRITE, i);
- outb(reg + VGA_AC_WRITE, v);
-
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
-}
-
-static void
-vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val)
-{
- outb(reg + indexreg, index);
- outb(reg + datareg, val);
-}
-
-static int
-vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index)
-{
- outb(reg + indexreg, index);
- return (inb(reg + datareg));
-}
-
-static int
-vga_get_crtc(int reg, int i)
-{
- return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i));
-}
-
-static void
-vga_set_crtc(int reg, int i, int v)
+/*
+ * This function is used to mark a rectangular image area so the scrolling
+ * will know we need to copy the data from there.
+ */
+void
+term_image_display(teken_gfx_t *state, const teken_rect_t *r)
{
- vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
+ teken_pos_t p;
+ int idx;
+
+ for (p.tp_row = r->tr_begin.tp_row;
+ p.tp_row < r->tr_end.tp_row; p.tp_row++) {
+ for (p.tp_col = r->tr_begin.tp_col;
+ p.tp_col < r->tr_end.tp_col; p.tp_col++) {
+ idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+ screen_buffer[idx].a.ta_format |= TF_IMAGE;
+ }
+ }
}
static void
@@ -353,9 +307,9 @@
}
static void
-vidc_text_printchar(const teken_pos_t *p)
+vidc_text_printchar(teken_gfx_t *state, const teken_pos_t *p)
{
- int i;
+ int idx;
uint8_t attr;
struct text_pixel *px;
teken_color_t fg, bg, tmp;
@@ -364,7 +318,8 @@
uint8_t attr;
} *addr;
- px = buffer + p->tp_col + p->tp_row * tp.tp_col;
+ idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ px = &screen_buffer[idx];
fg = teken_256to16(px->a.ta_fgcolor);
bg = teken_256to16(px->a.ta_bgcolor);
if (px->a.ta_format & TF_BOLD)
@@ -378,29 +333,34 @@
bg = tmp;
}
- attr = (cons_to_vga_colors[bg & 0xf] << 4) |
- cons_to_vga_colors[fg & 0xf];
- addr = (struct cgatext *)vgatext + p->tp_col + p->tp_row * tp.tp_col;
- addr->ch = vga_get_cp437(px->c);
- addr->attr = attr;
+ attr = (cmap[bg & 0xf] << 4) | cmap[fg & 0xf];
+ addr = (struct cgatext *)vgatext;
+ addr[idx].ch = vga_get_cp437(px->c);
+ addr[idx].attr = attr;
}
static void
-vidc_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
+vidc_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
const teken_attr_t *a)
{
- int attr, idx;
+ teken_gfx_t *state = s;
+ int attr, idx;
- idx = p->tp_col + p->tp_row * tp.tp_col;
- buffer[idx].c = c;
- buffer[idx].a = *a;
- vidc_text_printchar(p);
+ idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+
+ screen_buffer[idx].c = c;
+ screen_buffer[idx].a = *a;
+
+ vidc_text_printchar(state, p);
}
static void
-vidc_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
+vidc_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
const teken_attr_t *a)
{
+ teken_gfx_t *state = arg;
teken_pos_t p;
teken_unit_t row, col;
@@ -410,29 +370,14 @@
p.tp_row++)
for (p.tp_col = r->tr_begin.tp_col;
p.tp_col < r->tr_end.tp_col; p.tp_col++)
- vidc_text_putchar(s, &p, c, a);
+ vidc_text_putchar(state, &p, c, a);
vidc_text_set_cursor(row, col, true);
}
-static bool
-vidc_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
-{
- if (px1->c != px2->c)
- return (false);
-
- if (px1->a.ta_format != px2->a.ta_format)
- return (false);
- if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
- return (false);
- if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
- return (false);
-
- return (true);
-}
-
static void
-vidc_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
+vidc_text_copy(void *ptr, const teken_rect_t *r, const teken_pos_t *p)
{
+ teken_gfx_t *state = ptr;
int srow, drow;
int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
teken_pos_t d, s;
@@ -453,18 +398,18 @@
for (y = 0; y < nrow; y++) {
d.tp_row = p->tp_row + y;
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
+ drow = d.tp_row * state->tg_tp.tp_col;
+ srow = s.tp_row * state->tg_tp.tp_col;
for (x = 0; x < ncol; x++) {
d.tp_col = p->tp_col + x;
s.tp_col = r->tr_begin.tp_col + x;
- if (!vidc_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ if (!is_same_pixel(
+ &screen_buffer[d.tp_col + drow],
+ &screen_buffer[s.tp_col + srow])) {
+ screen_buffer[d.tp_col + drow] =
+ screen_buffer[s.tp_col + srow];
+ vidc_text_printchar(state, &d);
}
}
}
@@ -475,18 +420,18 @@
for (y = nrow - 1; y >= 0; y--) {
d.tp_row = p->tp_row + y;
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
+ drow = d.tp_row * state->tg_tp.tp_col;
+ srow = s.tp_row * state->tg_tp.tp_col;
for (x = 0; x < ncol; x++) {
d.tp_col = p->tp_col + x;
s.tp_col = r->tr_begin.tp_col + x;
- if (!vidc_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ if (!is_same_pixel(
+ &screen_buffer[d.tp_col + drow],
+ &screen_buffer[s.tp_col + srow])) {
+ screen_buffer[d.tp_col + drow] =
+ screen_buffer[s.tp_col + srow];
+ vidc_text_printchar(state, &d);
}
}
}
@@ -495,18 +440,18 @@
for (y = nrow - 1; y >= 0; y--) {
d.tp_row = p->tp_row + y;
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
+ drow = d.tp_row * state->tg_tp.tp_col;
+ srow = s.tp_row * state->tg_tp.tp_col;
for (x = ncol - 1; x >= 0; x--) {
d.tp_col = p->tp_col + x;
s.tp_col = r->tr_begin.tp_col + x;
- if (!vidc_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ if (!is_same_pixel(
+ &screen_buffer[d.tp_col + drow],
+ &screen_buffer[s.tp_col + srow])) {
+ screen_buffer[d.tp_col + drow] =
+ screen_buffer[s.tp_col + srow];
+ vidc_text_printchar(state, &d);
}
}
}
@@ -516,8 +461,9 @@
}
static void
-vidc_text_param(void *s __unused, int cmd, unsigned int value)
+vidc_text_param(void *arg, int cmd, unsigned int value)
{
+ teken_gfx_t *state = arg;
teken_unit_t row, col;
switch (cmd) {
@@ -532,10 +478,13 @@
/* FALLTHROUGH */
case TP_SHOWCURSOR:
vidc_text_get_cursor(&row, &col);
- if (value == 1)
+ if (value != 0) {
vidc_text_set_cursor(row, col, true);
- else
+ state->tg_cursor_visible = true;
+ } else {
vidc_text_set_cursor(row, col, false);
+ state->tg_cursor_visible = false;
+ }
break;
default:
/* Not yet implemented */
@@ -635,7 +584,7 @@
evalue = value;
}
- ap = teken_get_defattr(&teken);
+ ap = teken_get_defattr(&gfx_state.tg_teken);
a = *ap;
if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
/* is it already set? */
@@ -654,11 +603,381 @@
if (a.ta_bgcolor == TC_WHITE)
a.ta_bgcolor |= TC_LIGHT;
+ teken_set_defattr(&gfx_state.tg_teken, &a);
+ cons_draw_frame(&a);
env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
- teken_set_defattr(&teken, &a);
+ teken_input(&gfx_state.tg_teken, "\e[2J", 4);
+
return (CMD_OK);
}
+static int
+env_screen_nounset(struct env_var *ev __unused)
+{
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return (0);
+ return (EPERM);
+}
+
+static int
+vidc_load_palette(uint32_t *cmap)
+{
+ int i, roff, goff, boff, rc;
+
+ if (pe8 == NULL)
+ pe8 = calloc(sizeof(*pe8), NCMAP);
+ if (pe8 == NULL)
+ return (ENOMEM);
+
+ /* Generate VGA colors */
+ roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+ rc = generate_cons_palette((uint32_t *)pe8, COLOR_FORMAT_RGB,
+ gfx_state.tg_fb.fb_mask_red >> roff, roff,
+ gfx_state.tg_fb.fb_mask_green >> goff, goff,
+ gfx_state.tg_fb.fb_mask_blue >> boff, boff);
+
+ if (rc == 0) {
+ for (i = 0; i < NCMAP; i++) {
+ int idx;
+
+ if (i < NCOLORS)
+ idx = cons_to_vga_colors[i];
+ else
+ idx = i;
+
+ rc = vbe_set_palette(&pe8[i], idx);
+ if (rc != 0)
+ break;
+ }
+ }
+ return (rc);
+}
+
+static void
+cons_draw_frame(teken_attr_t *a)
+{
+ teken_attr_t attr = *a;
+ teken_color_t fg = a->ta_fgcolor;
+
+ attr.ta_fgcolor = attr.ta_bgcolor;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+
+ gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
+ gfx_state.tg_origin.tp_row, 1);
+ gfx_fb_drawrect(0,
+ gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
+ gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
+ gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
+ gfx_state.tg_origin.tp_col,
+ gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
+ gfx_fb_drawrect(
+ gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
+ gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
+ gfx_state.tg_fb.fb_height, 1);
+
+ attr.ta_fgcolor = fg;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+}
+
+/*
+ * Binary searchable table for CP437 to Unicode conversion.
+ */
+struct cp437uni {
+ uint8_t cp437_base;
+ uint16_t unicode_base;
+ uint8_t length;
+};
+
+static const struct cp437uni cp437unitable[] = {
+ { 0, 0x0000, 0 }, { 1, 0x263A, 1 }, { 3, 0x2665, 1 },
+ { 5, 0x2663, 0 }, { 6, 0x2660, 0 }, { 7, 0x2022, 0 },
+ { 8, 0x25D8, 0 }, { 9, 0x25CB, 0 }, { 10, 0x25D9, 0 },
+ { 11, 0x2642, 0 }, { 12, 0x2640, 0 }, { 13, 0x266A, 1 },
+ { 15, 0x263C, 0 }, { 16, 0x25BA, 0 }, { 17, 0x25C4, 0 },
+ { 18, 0x2195, 0 }, { 19, 0x203C, 0 }, { 20, 0x00B6, 0 },
+ { 21, 0x00A7, 0 }, { 22, 0x25AC, 0 }, { 23, 0x21A8, 0 },
+ { 24, 0x2191, 0 }, { 25, 0x2193, 0 }, { 26, 0x2192, 0 },
+ { 27, 0x2190, 0 }, { 28, 0x221F, 0 }, { 29, 0x2194, 0 },
+ { 30, 0x25B2, 0 }, { 31, 0x25BC, 0 }, { 32, 0x0020, 0x5e },
+ { 127, 0x2302, 0 }, { 128, 0x00C7, 0 }, { 129, 0x00FC, 0 },
+ { 130, 0x00E9, 0 }, { 131, 0x00E2, 0 }, { 132, 0x00E4, 0 },
+ { 133, 0x00E0, 0 }, { 134, 0x00E5, 0 }, { 135, 0x00E7, 0 },
+ { 136, 0x00EA, 1 }, { 138, 0x00E8, 0 }, { 139, 0x00EF, 0 },
+ { 140, 0x00EE, 0 }, { 141, 0x00EC, 0 }, { 142, 0x00C4, 1 },
+ { 144, 0x00C9, 0 }, { 145, 0x00E6, 0 }, { 146, 0x00C6, 0 },
+ { 147, 0x00F4, 0 }, { 148, 0x00F6, 0 }, { 149, 0x00F2, 0 },
+ { 150, 0x00FB, 0 }, { 151, 0x00F9, 0 }, { 152, 0x00FF, 0 },
+ { 153, 0x00D6, 0 }, { 154, 0x00DC, 0 }, { 155, 0x00A2, 1 },
+ { 157, 0x00A5, 0 }, { 158, 0x20A7, 0 }, { 159, 0x0192, 0 },
+ { 160, 0x00E1, 0 }, { 161, 0x00ED, 0 }, { 162, 0x00F3, 0 },
+ { 163, 0x00FA, 0 }, { 164, 0x00F1, 0 }, { 165, 0x00D1, 0 },
+ { 166, 0x00AA, 0 }, { 167, 0x00BA, 0 }, { 168, 0x00BF, 0 },
+ { 169, 0x2310, 0 }, { 170, 0x00AC, 0 }, { 171, 0x00BD, 0 },
+ { 172, 0x00BC, 0 }, { 173, 0x00A1, 0 }, { 174, 0x00AB, 0 },
+ { 175, 0x00BB, 0 }, { 176, 0x2591, 2 }, { 179, 0x2502, 0 },
+ { 180, 0x2524, 0 }, { 181, 0x2561, 1 }, { 183, 0x2556, 0 },
+ { 184, 0x2555, 0 }, { 185, 0x2563, 0 }, { 186, 0x2551, 0 },
+ { 187, 0x2557, 0 }, { 188, 0x255D, 0 }, { 189, 0x255C, 0 },
+ { 190, 0x255B, 0 }, { 191, 0x2510, 0 }, { 192, 0x2514, 0 },
+ { 193, 0x2534, 0 }, { 194, 0x252C, 0 }, { 195, 0x251C, 0 },
+ { 196, 0x2500, 0 }, { 197, 0x253C, 0 }, { 198, 0x255E, 1 },
+ { 200, 0x255A, 0 }, { 201, 0x2554, 0 }, { 202, 0x2569, 0 },
+ { 203, 0x2566, 0 }, { 204, 0x2560, 0 }, { 205, 0x2550, 0 },
+ { 206, 0x256C, 0 }, { 207, 0x2567, 1 }, { 209, 0x2564, 1 },
+ { 211, 0x2559, 0 }, { 212, 0x2558, 0 }, { 213, 0x2552, 1 },
+ { 215, 0x256B, 0 }, { 216, 0x256A, 0 }, { 217, 0x2518, 0 },
+ { 218, 0x250C, 0 }, { 219, 0x2588, 0 }, { 220, 0x2584, 0 },
+ { 221, 0x258C, 0 }, { 222, 0x2590, 0 }, { 223, 0x2580, 0 },
+ { 224, 0x03B1, 0 }, { 225, 0x00DF, 0 }, { 226, 0x0393, 0 },
+ { 227, 0x03C0, 0 }, { 228, 0x03A3, 0 }, { 229, 0x03C3, 0 },
+ { 230, 0x00B5, 0 }, { 231, 0x03C4, 0 }, { 232, 0x03A6, 0 },
+ { 233, 0x0398, 0 }, { 234, 0x03A9, 0 }, { 235, 0x03B4, 0 },
+ { 236, 0x221E, 0 }, { 237, 0x03C6, 0 }, { 238, 0x03B5, 0 },
+ { 239, 0x2229, 0 }, { 240, 0x2261, 0 }, { 241, 0x00B1, 0 },
+ { 242, 0x2265, 0 }, { 243, 0x2264, 0 }, { 244, 0x2320, 1 },
+ { 246, 0x00F7, 0 }, { 247, 0x2248, 0 }, { 248, 0x00B0, 0 },
+ { 249, 0x2219, 0 }, { 250, 0x00B7, 0 }, { 251, 0x221A, 0 },
+ { 252, 0x207F, 0 }, { 253, 0x00B2, 0 }, { 254, 0x25A0, 0 },
+ { 255, 0x00A0, 0 }
+};
+
+static uint16_t
+vga_cp437_to_uni(uint8_t c)
+{
+ int min, mid, max;
+
+ min = 0;
+ max = (sizeof(cp437unitable) / sizeof(struct cp437uni)) - 1;
+
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (c < cp437unitable[mid].cp437_base)
+ max = mid - 1;
+ else if (c > cp437unitable[mid].cp437_base +
+ cp437unitable[mid].length)
+ min = mid + 1;
+ else
+ return (c - cp437unitable[mid].cp437_base +
+ cp437unitable[mid].unicode_base);
+ }
+
+ return ('?');
+}
+
+/*
+ * install font for text mode
+ */
+static void
+vidc_install_font(void)
+{
+ static uint8_t fsreg[8] = {0x0, 0x30, 0x5, 0x35, 0xa, 0x3a, 0xf, 0x3f};
+ const uint8_t *from;
+ uint8_t volatile *to;
+ uint16_t c;
+ int i, j, s;
+ int bpc, f_offset;
+ teken_attr_t a = { 0 };
+
+ if (gfx_state.tg_fb_type != FB_TEXT)
+ return;
+
+ /* Sync-reset the sequencer registers */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x01);
+ /*
+ * enable write to plane2, since fonts
+ * could only be loaded into plane2
+ */
+ vga_set_seq(VGA_REG_BASE, 0x02, 0x04);
+ /*
+ * sequentially access data in the bit map being
+ * selected by MapMask register (index 0x02)
+ */
+ vga_set_seq(VGA_REG_BASE, 0x04, 0x07);
+ /* Sync-reset ended, and allow the sequencer to operate */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x03);
+
+ /*
+ * select plane 2 on Read Mode 0
+ */
+ vga_set_grc(VGA_REG_BASE, 0x04, 0x02);
+ /*
+ * system addresses sequentially access data, follow
+ * Memory Mode register bit 2 in the sequencer
+ */
+ vga_set_grc(VGA_REG_BASE, 0x05, 0x00);
+ /*
+ * set range of host memory addresses decoded by VGA
+ * hardware -- A0000h-BFFFFh (128K region)
+ */
+ vga_set_grc(VGA_REG_BASE, 0x06, 0x00);
+
+ /*
+ * This assumes 8x16 characters, which yield the traditional 80x25
+ * screen.
+ */
+ bpc = 16;
+ s = 0; /* font slot, maybe should use tunable there. */
+ f_offset = s * 8 * 1024;
+ for (i = 0; i < 256; i++) {
+ c = vga_cp437_to_uni(i);
+ from = font_lookup(&gfx_state.tg_font, c, &a);
+ to = (unsigned char *)ptov(VGA_MEM_BASE) + f_offset +
+ i * 0x20;
+ for (j = 0; j < bpc; j++)
+ *to++ = *from++;
+ }
+
+ /* Sync-reset the sequencer registers */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x01);
+ /* enable write to plane 0 and 1 */
+ vga_set_seq(VGA_REG_BASE, 0x02, 0x03);
+ /*
+ * enable character map selection
+ * and odd/even addressing
+ */
+ vga_set_seq(VGA_REG_BASE, 0x04, 0x03);
+ /*
+ * select font map
+ */
+ vga_set_seq(VGA_REG_BASE, 0x03, fsreg[s]);
+ /* Sync-reset ended, and allow the sequencer to operate */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x03);
+
+ /* restore graphic registers */
+
+ /* select plane 0 */
+ vga_set_grc(VGA_REG_BASE, 0x04, 0x00);
+ /* enable odd/even addressing mode */
+ vga_set_grc(VGA_REG_BASE, 0x05, 0x10);
+ /*
+ * range of host memory addresses decoded by VGA
+ * hardware -- B8000h-BFFFFh (32K region)
+ */
+ vga_set_grc(VGA_REG_BASE, 0x06, 0x0e);
+ /* enable all color plane */
+ vga_set_atr(VGA_REG_BASE, 0x12, 0x0f);
+}
+
+bool
+cons_update_mode(bool use_gfx_mode)
+{
+ const teken_attr_t *a;
+ teken_attr_t attr;
+ char env[10], *ptr;
+ int format, roff, goff, boff;
+
+ gfx_state.tg_tp.tp_row = TEXT_ROWS;
+ gfx_state.tg_tp.tp_col = TEXT_COLS;
+
+ if (use_gfx_mode) {
+ setup_font(&gfx_state, gfx_state.tg_fb.fb_height,
+ gfx_state.tg_fb.fb_width);
+ /* Point of origin in pixels. */
+ gfx_state.tg_origin.tp_row = (gfx_state.tg_fb.fb_height -
+ (gfx_state.tg_tp.tp_row * gfx_state.tg_font.vf_height)) / 2;
+ gfx_state.tg_origin.tp_col = (gfx_state.tg_fb.fb_width -
+ (gfx_state.tg_tp.tp_col * gfx_state.tg_font.vf_width)) / 2;
+
+ gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
+ gfx_state.tg_font.vf_width * 4;
+ free(gfx_state.tg_glyph);
+ gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
+ if (gfx_state.tg_glyph == NULL)
+ return (false);
+ gfx_state.tg_functions = &tfx;
+ snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_height);
+ env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_width);
+ env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_bpp);
+ env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+ } else {
+ /* Trigger loading of 8x16 font. */
+ setup_font(&gfx_state,
+ 16 * gfx_state.tg_fb.fb_height + BORDER_PIXELS,
+ 8 * gfx_state.tg_fb.fb_width + BORDER_PIXELS);
+ gfx_state.tg_functions = &tf;
+ /* ensure the following are not set for text mode */
+ unsetenv("screen.height");
+ unsetenv("screen.width");
+ unsetenv("screen.depth");
+ unsetenv("kern.vt.fb.default_mode");
+ vidc_install_font();
+ }
+
+ free(screen_buffer);
+ screen_buffer = malloc(gfx_state.tg_tp.tp_row * gfx_state.tg_tp.tp_col *
+ sizeof(*screen_buffer));
+ if (screen_buffer == NULL)
+ return (false);
+
+ teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, &gfx_state);
+
+ if (gfx_state.tg_ctype == CT_INDEXED)
+ format = COLOR_FORMAT_VGA;
+ else
+ format = COLOR_FORMAT_RGB;
+
+ roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+ (void) generate_cons_palette(cmap, format,
+ gfx_state.tg_fb.fb_mask_red >> roff, roff,
+ gfx_state.tg_fb.fb_mask_green >> goff, goff,
+ gfx_state.tg_fb.fb_mask_blue >> boff, boff);
+
+ if (gfx_state.tg_ctype == CT_INDEXED)
+ vidc_load_palette(cmap);
+
+ teken_set_winsize(&gfx_state.tg_teken, &gfx_state.tg_tp);
+ a = teken_get_defattr(&gfx_state.tg_teken);
+ attr = *a;
+
+ /*
+ * On first run, we set up the vidc_set_colors()
+ * callback. If the env is already set, we
+ * pick up fg and bg color values from the environment.
+ */
+ ptr = getenv("teken.fg_color");
+ if (ptr != NULL) {
+ attr.ta_fgcolor = strtol(ptr, NULL, 10);
+ ptr = getenv("teken.bg_color");
+ attr.ta_bgcolor = strtol(ptr, NULL, 10);
+
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+ } else {
+ snprintf(env, sizeof(env), "%d", attr.ta_fgcolor);
+ env_setenv("teken.fg_color", EV_VOLATILE, env,
+ vidc_set_colors, env_nounset);
+ snprintf(env, sizeof(env), "%d", attr.ta_bgcolor);
+ env_setenv("teken.bg_color", EV_VOLATILE, env,
+ vidc_set_colors, env_nounset);
+ }
+
+ /* Improve visibility */
+ if (attr.ta_bgcolor == TC_WHITE)
+ attr.ta_bgcolor |= TC_LIGHT;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+
+ snprintf(env, sizeof (env), "%u", (unsigned)gfx_state.tg_tp.tp_row);
+ setenv("LINES", env, 1);
+ snprintf(env, sizeof (env), "%u", (unsigned)gfx_state.tg_tp.tp_col);
+ setenv("COLUMNS", env, 1);
+
+ /* Draw frame around terminal area. */
+ cons_draw_frame(&attr);
+ /* Erase display, this will also fill our screen buffer. */
+ teken_input(&gfx_state.tg_teken, "\e[2J", 4);
+ gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 1);
+
+ return (true);
+}
+
static int
vidc_init(int arg)
{
@@ -670,6 +989,7 @@
return (0);
vidc_started = 1;
+ vbe_init();
/*
* Check Miscellaneous Output Register (Read at 3CCh, Write at 3C2h)
@@ -687,30 +1007,19 @@
val &= ~VGA_AC_MC_ELG;
vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, val);
- tp.tp_row = TEXT_ROWS;
- tp.tp_col = TEXT_COLS;
- buffer = malloc(tp.tp_row * tp.tp_col * sizeof(*buffer));
- if (buffer == NULL)
- return (1);
-
- snprintf(env, sizeof (env), "%u", tp.tp_row);
- setenv("LINES", env, 1);
- snprintf(env, sizeof (env), "%u", tp.tp_col);
- setenv("COLUMNS", env, 1);
-
- teken_init(&teken, &tf, NULL);
- teken_set_winsize(&teken, &tp);
- a = teken_get_defattr(&teken);
+#if defined(FRAMEBUFFER_MODE)
+ val = vbe_default_mode();
+ /* if val is not legal VBE mode, use text mode */
+ if (VBE_VALID_MODE(val)) {
+ if (vbe_set_mode(val) != 0)
+ bios_set_text_mode(VGA_TEXT_MODE);
+ }
+#endif
- snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
- env_setenv("teken.fg_color", EV_VOLATILE, env, vidc_set_colors,
- env_nounset);
- snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
- env_setenv("teken.bg_color", EV_VOLATILE, env, vidc_set_colors,
- env_nounset);
+ gfx_framework_init();
- /* Erase display, this will also fill our screen buffer. */
- teken_input(&teken, "\e[J", 3);
+ if (!cons_update_mode(VBE_VALID_MODE(vbe_get_mode())))
+ return (1);
for (int i = 0; i < 10 && vidc_ischar(); i++)
(void) vidc_getchar();
@@ -734,8 +1043,8 @@
{
unsigned char ch = c;
- if (buffer != NULL)
- teken_input(&teken, &ch, sizeof (ch));
+ if (screen_buffer != NULL)
+ teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
else
vidc_biosputchar(c);
}
@@ -808,27 +1117,27 @@
#if KEYBOARD_PROBE
-#define PROBE_MAXRETRY 5
-#define PROBE_MAXWAIT 400
-#define IO_DUMMY 0x84
-#define IO_KBD 0x060 /* 8042 Keyboard */
+#define PROBE_MAXRETRY 5
+#define PROBE_MAXWAIT 400
+#define IO_DUMMY 0x84
+#define IO_KBD 0x060 /* 8042 Keyboard */
/* selected defines from kbdio.h */
-#define KBD_STATUS_PORT 4 /* status port, read */
-#define KBD_DATA_PORT 0 /* data port, read/write
+#define KBD_STATUS_PORT 4 /* status port, read */
+#define KBD_DATA_PORT 0 /* data port, read/write
* also used as keyboard command
* and mouse command port
*/
-#define KBDC_ECHO 0x00ee
-#define KBDS_ANY_BUFFER_FULL 0x0001
-#define KBDS_INPUT_BUFFER_FULL 0x0002
-#define KBD_ECHO 0x00ee
+#define KBDC_ECHO 0x00ee
+#define KBDS_ANY_BUFFER_FULL 0x0001
+#define KBDS_INPUT_BUFFER_FULL 0x0002
+#define KBD_ECHO 0x00ee
/* 7 microsec delay necessary for some keyboard controllers */
static void
delay7(void)
{
- /*
+ /*
* I know this is broken, but no timer is available yet at this stage...
* See also comments in `delay1ms()'.
*/
@@ -854,7 +1163,7 @@
(void) inb(0x84);
}
-/*
+/*
* We use the presence/absence of a keyboard to determine whether the internal
* console can be used for input.
*
diff --git a/stand/i386/loader/Makefile b/stand/i386/loader/Makefile
--- a/stand/i386/loader/Makefile
+++ b/stand/i386/loader/Makefile
@@ -23,7 +23,13 @@
.PATH: ${BOOTSRC}/i386/loader
# architecture-specific loader code
-SRCS= main.c conf.c vers.c chain.c
+SRCS= main.c conf.c vers.c chain.c gfx_fb.c 8x16.c
+
+CFLAGS.gfx_fb.c += -I${.CURDIR}/../libi386
+CFLAGS.gfx_fb.c += -I$(SRCTOP)/sys/teken
+CFLAGS.gfx_fb.c += -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
+CFLAGS.gfx_fb.c += -I${SRCTOP}/contrib/pnglite
+CFLAGS.gfx_fb.c += -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib
# Include bcache code.
HAVE_BCACHE= yes
@@ -49,7 +55,7 @@
# Always add MI sources
.include "${BOOTSRC}/loader.mk"
-CLEANFILES+= ${LOADER} ${LOADER}.bin
+CLEANFILES+= ${LOADER} ${LOADER}.bin 8x16.c
ORG= 0x0
@@ -64,6 +70,9 @@
#CFLAGS+= -g
#LDFLAGS+= -g
+8x16.c: ${SRCTOP}/contrib/terminus/ter-u16v.bdf
+ vtfontcvt -f compressed-source -o ${.TARGET} ${.ALLSRC}
+
${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN}
btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
-b ${BTXKERN} ${LOADER}.bin
diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c
--- a/stand/i386/loader/main.c
+++ b/stand/i386/loader/main.c
@@ -130,6 +130,12 @@
}
setheap(heap_bottom, heap_top);
+ /*
+ * detect ACPI for future reference. This may set console to comconsole
+ * if we do have ACPI SPCR table.
+ */
+ biosacpi_detect();
+
/*
* XXX Chicken-and-egg problem; we want to have console output early,
* but some console attributes may depend on reading from eg. the boot
@@ -242,9 +248,6 @@
initial_bootinfo->bi_extmem = bios_extmem / 1024;
}
- /* detect ACPI for future reference */
- biosacpi_detect();
-
/* detect SMBIOS for future reference */
smbios_detect(NULL);
@@ -254,6 +257,7 @@
printf("\n%s", bootprog_info);
extract_currdev(); /* set $currdev and $loaddev */
+ autoload_font(true);
bios_getsmap();
diff --git a/stand/images/Makefile b/stand/images/Makefile
new file mode 100644
--- /dev/null
+++ b/stand/images/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+FILES+= freebsd-brand-rev.png freebsd-brand.png freebsd-logo-rev.png
+
+FILESDIR= /boot/images
+
+.include <bsd.prog.mk>
diff --git a/stand/images/freebsd-brand-rev.png b/stand/images/freebsd-brand-rev.png
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
diff --git a/stand/images/freebsd-brand.png b/stand/images/freebsd-brand.png
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
diff --git a/stand/images/freebsd-logo-rev.png b/stand/images/freebsd-logo-rev.png
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
diff --git a/stand/liblua/Makefile b/stand/liblua/Makefile
--- a/stand/liblua/Makefile
+++ b/stand/liblua/Makefile
@@ -35,6 +35,8 @@
CFLAGS+= -fno-stack-protector -D__BSD_VISIBLE
CFLAGS+= -I${BOOTSRC}/include -I${LIBLUASRC} -I${LUASRC} -I${LDRSRC}
+CFLAGS.lutils.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
+
.if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 0
CFLAGS+= -fPIC
.endif
diff --git a/stand/liblua/lutils.c b/stand/liblua/lutils.c
--- a/stand/liblua/lutils.c
+++ b/stand/liblua/lutils.c
@@ -35,6 +35,8 @@
#include "lstd.h"
#include "lutils.h"
#include "bootstrap.h"
+#include <gfx_fb.h>
+#include <pnglite.h>
/*
* Like loader.perform, except args are passed already parsed
@@ -346,6 +348,189 @@
return 1;
}
+/*
+ * put image using terminal coordinates.
+ */
+static int
+lua_term_putimage(lua_State *L)
+{
+ const char *name;
+ png_t png;
+ uint32_t x1, y1, x2, y2, f;
+ int nargs, ret = 0, error;
+
+ nargs = lua_gettop(L);
+ if (nargs != 6) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+
+ name = luaL_checkstring(L, 1);
+ x1 = luaL_checknumber(L, 2);
+ y1 = luaL_checknumber(L, 3);
+ x2 = luaL_checknumber(L, 4);
+ y2 = luaL_checknumber(L, 5);
+ f = luaL_checknumber(L, 6);
+
+ x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width;
+ y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height;
+ if (x2 != 0) {
+ x2 = gfx_state.tg_origin.tp_col +
+ x2 * gfx_state.tg_font.vf_width;
+ }
+ if (y2 != 0) {
+ y2 = gfx_state.tg_origin.tp_row +
+ y2 * gfx_state.tg_font.vf_height;
+ }
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = 1;
+ (void) png_close(&png);
+ }
+ lua_pushboolean(L, ret);
+ return 1;
+}
+
+static int
+lua_fb_putimage(lua_State *L)
+{
+ const char *name;
+ png_t png;
+ uint32_t x1, y1, x2, y2, f;
+ int nargs, ret = 0, error;
+
+ nargs = lua_gettop(L);
+ if (nargs != 6) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+
+ name = luaL_checkstring(L, 1);
+ x1 = luaL_checknumber(L, 2);
+ y1 = luaL_checknumber(L, 3);
+ x2 = luaL_checknumber(L, 4);
+ y2 = luaL_checknumber(L, 5);
+ f = luaL_checknumber(L, 6);
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = 1;
+ (void) png_close(&png);
+ }
+ lua_pushboolean(L, ret);
+ return 1;
+}
+
+static int
+lua_fb_setpixel(lua_State *L)
+{
+ uint32_t x, y;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 2) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x = luaL_checknumber(L, 1);
+ y = luaL_checknumber(L, 2);
+ gfx_fb_setpixel(x, y);
+ return 0;
+}
+
+static int
+lua_fb_line(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1, wd;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 5) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ wd = luaL_checknumber(L, 5);
+ gfx_fb_line(x0, y0, x1, y1, wd);
+ return 0;
+}
+
+static int
+lua_fb_bezier(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1, x2, y2, width;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 7) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ x2 = luaL_checknumber(L, 5);
+ y2 = luaL_checknumber(L, 6);
+ width = luaL_checknumber(L, 7);
+ gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width);
+ return 0;
+}
+
+static int
+lua_fb_drawrect(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1, fill;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 5) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ fill = luaL_checknumber(L, 5);
+ gfx_fb_drawrect(x0, y0, x1, y1, fill);
+ return 0;
+}
+
+static int
+lua_term_drawrect(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 4) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ gfx_term_drawrect(x0, y0, x1, y1);
+ return 0;
+}
+
#define REG_SIMPLE(n) { #n, lua_ ## n }
static const struct luaL_Reg loaderlib[] = {
REG_SIMPLE(delay),
@@ -360,6 +545,13 @@
REG_SIMPLE(setenv),
REG_SIMPLE(time),
REG_SIMPLE(unsetenv),
+ REG_SIMPLE(fb_bezier),
+ REG_SIMPLE(fb_drawrect),
+ REG_SIMPLE(fb_line),
+ REG_SIMPLE(fb_putimage),
+ REG_SIMPLE(fb_setpixel),
+ REG_SIMPLE(term_drawrect),
+ REG_SIMPLE(term_putimage),
{ NULL, NULL },
};
diff --git a/stand/loader.mk b/stand/loader.mk
--- a/stand/loader.mk
+++ b/stand/loader.mk
@@ -8,9 +8,15 @@
SRCS+= interp_backslash.c interp_parse.c ls.c misc.c
SRCS+= module.c nvstore.c
+CFLAGS.module.c += -I$(SRCTOP)/sys/teken -I${SRCTOP}/contrib/pnglite
+
.if ${MACHINE} == "i386" || ${MACHINE_CPUARCH} == "amd64"
SRCS+= load_elf32.c load_elf32_obj.c reloc_elf32.c
SRCS+= load_elf64.c load_elf64_obj.c reloc_elf64.c
+SRCS+= pnglite.c
+.PATH: ${SRCTOP}/contrib/pnglite
+CFLAGS.pnglite.c+= -I${SRCTOP}/contrib/pnglite
+CFLAGS.pnglite.c+= -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib
.elif ${MACHINE_CPUARCH} == "aarch64"
SRCS+= load_elf64.c reloc_elf64.c
.elif ${MACHINE_CPUARCH} == "arm"
diff --git a/stand/lua/color.lua b/stand/lua/color.lua
--- a/stand/lua/color.lua
+++ b/stand/lua/color.lua
@@ -56,7 +56,7 @@
if c ~= nil then
return c:lower() ~= "no" and c ~= "0"
end
- return not core.isSerialBoot()
+ return true
end
function color.escapefg(color_value)
diff --git a/stand/lua/core.lua b/stand/lua/core.lua
--- a/stand/lua/core.lua
+++ b/stand/lua/core.lua
@@ -383,6 +383,19 @@
return false
end
+function core.isFramebufferConsole()
+ local c = loader.getenv("console")
+ if c ~= nil then
+ if c:find("efi") == nil and c:find("vidconsole") == nil then
+ return false
+ end
+ if loader.getenv("screen.depth") ~= nil then
+ return true
+ end
+ end
+ return false
+end
+
function core.isSerialConsole()
local c = loader.getenv("console")
if c ~= nil then
diff --git a/stand/lua/drawer.lua b/stand/lua/drawer.lua
--- a/stand/lua/drawer.lua
+++ b/stand/lua/drawer.lua
@@ -202,7 +202,7 @@
return "double"
end
-local function drawbox()
+local function drawframe()
local x = menu_position.x - 3
local y = menu_position.y - 1
local w = frame_size.w
@@ -213,7 +213,7 @@
-- If we don't have a framespec for the current frame style, just don't
-- draw a box.
if framespec == nil then
- return
+ return false
end
local hl = framespec.horizontal
@@ -227,6 +227,11 @@
x = x + shift.x
y = y + shift.y
+ if core.isFramebufferConsole() and loader.term_drawrect ~= nil then
+ loader.term_drawrect(x, y, x + w, y + h)
+ return true
+ end
+
screen.setcursor(x, y); printc(tl)
screen.setcursor(x, y + h); printc(bl)
screen.setcursor(x + w, y); printc(tr)
@@ -248,12 +253,25 @@
screen.setcursor(x + w, y + i)
printc(vl)
end
+ return true
+end
+local function drawbox()
+ local x = menu_position.x - 3
+ local y = menu_position.y - 1
+ local w = frame_size.w
local menu_header = loader.getenv("loader_menu_title") or
"Welcome to FreeBSD"
local menu_header_align = loader.getenv("loader_menu_title_align")
local menu_header_x
+ x = x + shift.x
+ y = y + shift.y
+
+ if drawframe(x, y, w) == false then
+ return
+ end
+
if menu_header_align ~= nil then
menu_header_align = menu_header_align:lower()
if menu_header_align == "left" then
@@ -287,6 +305,14 @@
x = x + shift.x
y = y + shift.y
+ if core.isFramebufferConsole() and
+ loader.term_putimage ~= nil and
+ branddef.image ~= nil then
+ if loader.term_putimage(branddef.image, 0, 0, 0, 7, 0)
+ then
+ return true
+ end
+ end
draw(x, y, graphic)
end
@@ -330,9 +356,33 @@
y = y + logodef.shift.y
end
+ if core.isFramebufferConsole() and
+ loader.term_putimage ~= nil and
+ logodef.image ~= nil then
+ local y1 = 15
+
+ if logodef.image_rl ~= nil then
+ y1 = logodef.image_rl
+ end
+ if loader.term_putimage(logodef.image, x, y, 0, y + y1, 0)
+ then
+ return true
+ end
+ end
draw(x, y, logodef.graphic)
end
+local function drawitem(func)
+ local console = loader.getenv("console")
+ local c
+
+ for c in string.gmatch(console, "%w+") do
+ loader.setenv("console", c)
+ func()
+ end
+ loader.setenv("console", console)
+end
+
fbsd_brand = {
" ______ ____ _____ _____ ",
" | ____| | _ \\ / ____| __ \\ ",
@@ -378,6 +428,7 @@
-- keys are: graphic (table depicting graphic)
["fbsd"] = {
graphic = fbsd_brand,
+ image = "/boot/images/freebsd-brand-rev.png",
},
["none"] = {
graphic = none,
@@ -458,9 +509,9 @@
function drawer.drawscreen(menudef)
-- drawlogo() must go first.
-- it determines the positions of other elements
- drawlogo()
- drawbrand()
- drawbox()
+ drawitem(drawlogo)
+ drawitem(drawbrand)
+ drawitem(drawbox)
return drawmenu(menudef)
end
diff --git a/stand/lua/gfx-orb.lua b/stand/lua/gfx-orb.lua
--- a/stand/lua/gfx-orb.lua
+++ b/stand/lua/gfx-orb.lua
@@ -47,6 +47,8 @@
" .---.....----.\027[m",
},
requires_color = true,
- shift = {x = 2, y = 4},
+ shift = {x = 2, y = 3},
+ image = "/boot/images/freebsd-logo-rev.png",
+ image_rl = 15
}
}
diff --git a/stand/userboot/userboot/Makefile b/stand/userboot/userboot/Makefile
--- a/stand/userboot/userboot/Makefile
+++ b/stand/userboot/userboot/Makefile
@@ -35,6 +35,7 @@
CFLAGS.main.c+= -I${BOOTSRC}/libsa/zfs
CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include
CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs
+CFLAGS.userboot_cons.c+= -I$(SRCTOP)/sys/teken -I${SRCTOP}/contrib/pnglite
CWARNFLAGS.main.c += -Wno-implicit-function-declaration
LDFLAGS+= -nostdlib -Wl,-Bsymbolic
diff --git a/stand/userboot/userboot/userboot_cons.c b/stand/userboot/userboot/userboot_cons.c
--- a/stand/userboot/userboot/userboot_cons.c
+++ b/stand/userboot/userboot/userboot_cons.c
@@ -28,9 +28,14 @@
__FBSDID("$FreeBSD$");
#include <stand.h>
+#include <sys/font.h>
+#include "gfx_fb.h"
#include "bootstrap.h"
#include "libuserboot.h"
+font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
+teken_gfx_t gfx_state = { 0 };
+
int console;
static struct console *userboot_comconsp;
diff --git a/sys/sys/font.h b/sys/sys/font.h
--- a/sys/sys/font.h
+++ b/sys/sys/font.h
@@ -93,9 +93,10 @@
} vt_font_bitmap_data_t;
typedef enum {
- FONT_AUTO,
- FONT_MANUAL,
- FONT_BOOT
+ FONT_AUTO, /* This font is loaded by software */
+ FONT_MANUAL, /* This font is loaded manually by user */
+ FONT_BUILTIN, /* This font was built in at compile time */
+ FONT_RELOAD /* This font is marked to be re-read from file */
} FONT_FLAGS;
struct fontlist {
diff --git a/sys/teken/teken.h b/sys/teken/teken.h
--- a/sys/teken/teken.h
+++ b/sys/teken/teken.h
@@ -48,6 +48,7 @@
#define TF_BLINK 0x04 /* Blinking character. */
#define TF_REVERSE 0x08 /* Reverse rendered character. */
#define TF_CJK_RIGHT 0x10 /* Right-hand side of CJK character. */
+#define TF_IMAGE 0x20 /* This character space has image. */
typedef unsigned char teken_color_t;
#define TC_BLACK 0
#define TC_RED 1

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 17, 10:05 PM (16 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15843707
Default Alt Text
D27420.diff (192 KB)

Event Timeline