term: Move to use external, portable terminal

This commit is contained in:
mintsuki 2022-10-04 00:58:00 +02:00
parent e466501f92
commit 4d855fc17e
18 changed files with 529 additions and 2171 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
/limine-efi
/freestanding-headers
/libgcc-binaries
/common/term
/ovmf*
*.o
*.d

View File

@ -251,6 +251,7 @@ dist:
cp -r '$(call SHESCAPE,$(SRCDIR))'/.git '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"/
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && git checkout .
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && ./bootstrap
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/common/term/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/freestanding-headers/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.gitignore"
@ -287,7 +288,7 @@ distclean: clean
.PHONY: maintainer-clean
maintainer-clean: distclean
cd '$(call SHESCAPE,$(SRCDIR))' && rm -rf freestanding-headers libgcc-binaries limine-efi cross-detect configure build-aux *'~' autom4te.cache *.tar.xz *.tar.gz
cd '$(call SHESCAPE,$(SRCDIR))' && rm -rf common/term freestanding-headers libgcc-binaries limine-efi cross-detect configure build-aux *'~' autom4te.cache *.tar.xz *.tar.gz
.PHONY: common-uefi-x86-64
common-uefi-x86-64:

View File

@ -7,6 +7,7 @@ test -z "$srcdir" && srcdir=.
cd "$srcdir"
[ -d common/term ] || git clone https://github.com/limine-bootloader/terminal.git common/term
[ -d cross-detect ] || git clone https://github.com/mintsuki/cross-detect.git
[ -d freestanding-headers ] || git clone https://github.com/mintsuki/freestanding-headers.git
[ -d limine-efi ] || git clone https://github.com/limine-bootloader/limine-efi.git

View File

@ -15,149 +15,99 @@
#define VD_COLS (80 * 2)
#define VD_ROWS 25
static volatile uint8_t *video_mem = (uint8_t *)0xb8000;
static uint8_t *back_buffer = NULL;
static uint8_t *front_buffer = NULL;
static struct context {
size_t cursor_offset;
#define cursor_offset context.cursor_offset
bool cursor_status;
#define cursor_status context.cursor_status
uint8_t text_palette;
#define text_palette context.text_palette
bool scroll_enabled;
#define scroll_enabled context.scroll_enabled
uint8_t saved_state_text_palette;
#define saved_state_text_palette context.saved_state_text_palette
size_t saved_state_cursor_offset;
#define saved_state_cursor_offset context.saved_state_cursor_offset
} context;
static size_t old_cursor_offset = 0;
static void draw_cursor(void) {
uint8_t pal = back_buffer[cursor_offset + 1];
video_mem[cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
static void draw_cursor(struct textmode_context *ctx) {
uint8_t pal = ctx->back_buffer[ctx->cursor_offset + 1];
ctx->video_mem[ctx->cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
}
void text_save_state(void) {
saved_state_text_palette = text_palette;
saved_state_cursor_offset = cursor_offset;
void text_save_state(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->saved_state_text_palette = ctx->text_palette;
ctx->saved_state_cursor_offset = ctx->cursor_offset;
}
void text_restore_state(void) {
text_palette = saved_state_text_palette;
cursor_offset = saved_state_cursor_offset;
void text_restore_state(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = ctx->saved_state_text_palette;
ctx->cursor_offset = ctx->saved_state_cursor_offset;
}
void text_swap_palette(void) {
text_palette = (text_palette << 4) | (text_palette >> 4);
void text_swap_palette(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette << 4) | (ctx->text_palette >> 4);
}
bool text_scroll_disable(void) {
bool ret = scroll_enabled;
scroll_enabled = false;
return ret;
}
void text_scroll(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
void text_scroll_enable(void) {
scroll_enabled = true;
}
void text_scroll(void) {
// move the text up by one row
for (size_t i = term_context.scroll_top_margin * VD_COLS;
i < (term_context.scroll_bottom_margin - 1) * VD_COLS; i++) {
back_buffer[i] = back_buffer[i + VD_COLS];
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
i < (_ctx->scroll_bottom_margin - 1) * VD_COLS; i++) {
ctx->back_buffer[i] = ctx->back_buffer[i + VD_COLS];
}
// clear the last line of the screen
for (size_t i = (term_context.scroll_bottom_margin - 1) * VD_COLS;
i < term_context.scroll_bottom_margin * VD_COLS; i += 2) {
back_buffer[i] = ' ';
back_buffer[i + 1] = text_palette;
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS;
i < _ctx->scroll_bottom_margin * VD_COLS; i += 2) {
ctx->back_buffer[i] = ' ';
ctx->back_buffer[i + 1] = ctx->text_palette;
}
}
void text_revscroll(void) {
void text_revscroll(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
// move the text up by one row
for (size_t i = (term_context.scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
back_buffer[i + VD_COLS] = back_buffer[i];
if (i == term_context.scroll_top_margin * VD_COLS) {
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
ctx->back_buffer[i + VD_COLS] = ctx->back_buffer[i];
if (i == _ctx->scroll_top_margin * VD_COLS) {
break;
}
}
// clear the first line of the screen
for (size_t i = term_context.scroll_top_margin * VD_COLS;
i < (term_context.scroll_top_margin + 1) * VD_COLS; i += 2) {
back_buffer[i] = ' ';
back_buffer[i + 1] = text_palette;
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
i < (_ctx->scroll_top_margin + 1) * VD_COLS; i += 2) {
ctx->back_buffer[i] = ' ';
ctx->back_buffer[i + 1] = ctx->text_palette;
}
}
void text_clear(bool move) {
void text_clear(struct term_context *_ctx, bool move) {
struct textmode_context *ctx = (void *)_ctx;
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
back_buffer[i] = ' ';
back_buffer[i + 1] = text_palette;
ctx->back_buffer[i] = ' ';
ctx->back_buffer[i + 1] = ctx->text_palette;
}
if (move) {
ctx->cursor_offset = 0;
}
if (move)
cursor_offset = 0;
}
void text_enable_cursor(void) {
cursor_status = true;
void text_enable_cursor(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->cursor_status = true;
}
bool text_disable_cursor(void) {
bool ret = cursor_status;
cursor_status = false;
bool text_disable_cursor(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
bool ret = ctx->cursor_status;
ctx->cursor_status = false;
return ret;
}
uint64_t text_context_size(void) {
uint64_t ret = 0;
ret += sizeof(struct context);
ret += VD_ROWS * VD_COLS; // front buffer
return ret;
}
void text_context_save(uint64_t ptr) {
memcpy32to64(ptr, (uint64_t)(uintptr_t)&context, sizeof(struct context));
ptr += sizeof(struct context);
memcpy32to64(ptr, (uint64_t)(uintptr_t)front_buffer, VD_ROWS * VD_COLS);
}
void text_context_restore(uint64_t ptr) {
memcpy32to64((uint64_t)(uintptr_t)&context, ptr, sizeof(struct context));
ptr += sizeof(struct context);
memcpy32to64((uint64_t)(uintptr_t)front_buffer, ptr, VD_ROWS * VD_COLS);
void text_full_refresh(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
video_mem[i] = front_buffer[i];
back_buffer[i] = front_buffer[i];
ctx->video_mem[i] = ctx->front_buffer[i];
ctx->back_buffer[i] = ctx->front_buffer[i];
}
if (cursor_status) {
draw_cursor();
old_cursor_offset = cursor_offset;
}
}
void text_full_refresh(void) {
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
video_mem[i] = front_buffer[i];
back_buffer[i] = front_buffer[i];
}
if (cursor_status) {
draw_cursor();
old_cursor_offset = cursor_offset;
if (ctx->cursor_status) {
draw_cursor(ctx);
ctx->old_cursor_offset = ctx->cursor_offset;
}
}
@ -170,23 +120,26 @@ void init_vga_textmode(size_t *_rows, size_t *_cols, bool managed) {
current_video_mode = 0x3;
}
if (back_buffer == NULL) {
back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
struct textmode_context *ctx = (void *)term;
if (ctx->back_buffer == NULL) {
ctx->back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
} else {
memset(back_buffer, 0, VD_ROWS * VD_COLS);
memset(ctx->back_buffer, 0, VD_ROWS * VD_COLS);
}
if (front_buffer == NULL) {
front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
if (ctx->front_buffer == NULL) {
ctx->front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
} else {
memset(front_buffer, 0, VD_ROWS * VD_COLS);
memset(ctx->front_buffer, 0, VD_ROWS * VD_COLS);
}
cursor_offset = 0;
cursor_status = true;
text_palette = 0x07;
scroll_enabled = true;
ctx->cursor_offset = 0;
ctx->cursor_status = true;
ctx->text_palette = 0x07;
text_clear(false);
ctx->video_mem = (volatile uint8_t *)0xb8000;
text_clear(term, false);
*_rows = VD_ROWS;
*_cols = VD_COLS / 2;
@ -194,7 +147,7 @@ void init_vga_textmode(size_t *_rows, size_t *_cols, bool managed) {
// VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor
if (!managed) {
text_disable_cursor();
text_disable_cursor(term);
outb(0x3d4, 0x0a);
outb(0x3d5, (inb(0x3d5) & 0xc0) | 14);
@ -213,51 +166,59 @@ void init_vga_textmode(size_t *_rows, size_t *_cols, bool managed) {
outb(0x3d5, 0x20);
}
text_double_buffer_flush();
text_double_buffer_flush(term);
}
void text_double_buffer_flush(void) {
if (cursor_status) {
draw_cursor();
void text_double_buffer_flush(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
if (ctx->cursor_status) {
draw_cursor(ctx);
}
if (cursor_offset != old_cursor_offset || cursor_status == false) {
video_mem[old_cursor_offset + 1] = back_buffer[old_cursor_offset + 1];
if (ctx->cursor_offset != ctx->old_cursor_offset || ctx->cursor_status == false) {
ctx->video_mem[ctx->old_cursor_offset + 1] = ctx->back_buffer[ctx->old_cursor_offset + 1];
}
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
if (back_buffer[i] == front_buffer[i]) {
if (ctx->back_buffer[i] == ctx->front_buffer[i]) {
continue;
}
if (cursor_status && i == cursor_offset + 1) {
if (ctx->cursor_status && i == ctx->cursor_offset + 1) {
continue;
}
front_buffer[i] = back_buffer[i];
video_mem[i] = back_buffer[i];
ctx->front_buffer[i] = ctx->back_buffer[i];
ctx->video_mem[i] = ctx->back_buffer[i];
}
if (cursor_status) {
old_cursor_offset = cursor_offset;
if (ctx->cursor_status) {
ctx->old_cursor_offset = ctx->cursor_offset;
}
}
void text_get_cursor_pos(size_t *x, size_t *y) {
*x = (cursor_offset % VD_COLS) / 2;
*y = cursor_offset / VD_COLS;
void text_get_cursor_pos(struct term_context *_ctx, size_t *x, size_t *y) {
struct textmode_context *ctx = (void *)_ctx;
*x = (ctx->cursor_offset % VD_COLS) / 2;
*y = ctx->cursor_offset / VD_COLS;
}
void text_move_character(size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
void text_move_character(struct term_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
struct textmode_context *ctx = (void *)_ctx;
if (old_x >= VD_COLS / 2 || old_y >= VD_ROWS
|| new_x >= VD_COLS / 2 || new_y >= VD_ROWS) {
return;
}
back_buffer[new_y * VD_COLS + new_x * 2] = back_buffer[old_y * VD_COLS + old_x * 2];
ctx->back_buffer[new_y * VD_COLS + new_x * 2] = ctx->back_buffer[old_y * VD_COLS + old_x * 2];
}
void text_set_cursor_pos(size_t x, size_t y) {
void text_set_cursor_pos(struct term_context *_ctx, size_t x, size_t y) {
struct textmode_context *ctx = (void *)_ctx;
if (x >= VD_COLS / 2) {
if ((int)x < 0) {
x = 0;
@ -272,48 +233,56 @@ void text_set_cursor_pos(size_t x, size_t y) {
y = VD_ROWS - 1;
}
}
cursor_offset = y * VD_COLS + x * 2;
ctx->cursor_offset = y * VD_COLS + x * 2;
}
static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
void text_set_text_fg(size_t fg) {
text_palette = (text_palette & 0xf0) | ansi_colours[fg];
void text_set_text_fg(struct term_context *_ctx, size_t fg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0xf0) | ansi_colours[fg];
}
void text_set_text_bg(size_t bg) {
text_palette = (text_palette & 0x0f) | (ansi_colours[bg] << 4);
void text_set_text_bg(struct term_context *_ctx, size_t bg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0x0f) | (ansi_colours[bg] << 4);
}
void text_set_text_fg_bright(size_t fg) {
text_palette = (text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3));
void text_set_text_fg_bright(struct term_context *_ctx, size_t fg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3));
}
void text_set_text_bg_bright(size_t bg) {
text_palette = (text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4);
void text_set_text_bg_bright(struct term_context *_ctx, size_t bg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4);
}
void text_set_text_fg_default(void) {
text_palette = (text_palette & 0xf0) | 7;
void text_set_text_fg_default(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0xf0) | 7;
}
void text_set_text_bg_default(void) {
text_palette &= 0x0f;
void text_set_text_bg_default(struct term_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette &= 0x0f;
}
void text_putchar(uint8_t c) {
back_buffer[cursor_offset] = c;
back_buffer[cursor_offset + 1] = text_palette;
if (cursor_offset / VD_COLS == term_context.scroll_bottom_margin - 1
&& cursor_offset % VD_COLS == VD_COLS - 2) {
if (scroll_enabled) {
text_scroll();
cursor_offset -= cursor_offset % VD_COLS;
void text_putchar(struct term_context *_ctx, uint8_t c) {
struct textmode_context *ctx = (void *)_ctx;
ctx->back_buffer[ctx->cursor_offset] = c;
ctx->back_buffer[ctx->cursor_offset + 1] = ctx->text_palette;
if (ctx->cursor_offset / VD_COLS == _ctx->scroll_bottom_margin - 1
&& ctx->cursor_offset % VD_COLS == VD_COLS - 2) {
if (_ctx->scroll_enabled) {
text_scroll(_ctx);
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
}
} else if (cursor_offset >= (VIDEO_BOTTOM - 1)) {
cursor_offset -= cursor_offset % VD_COLS;
} else if (ctx->cursor_offset >= (VIDEO_BOTTOM - 1)) {
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
} else {
cursor_offset += 2;
ctx->cursor_offset += 2;
}
}

View File

@ -4,36 +4,48 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <term/term.h>
struct textmode_context {
struct term_context term;
volatile uint8_t *video_mem;
uint8_t *back_buffer;
uint8_t *front_buffer;
size_t cursor_offset;
size_t old_cursor_offset;
bool cursor_status;
uint8_t text_palette;
uint8_t saved_state_text_palette;
size_t saved_state_cursor_offset;
};
void init_vga_textmode(size_t *rows, size_t *cols, bool managed);
void text_putchar(uint8_t c);
void text_clear(bool move);
void text_enable_cursor(void);
bool text_disable_cursor(void);
void text_set_cursor_pos(size_t x, size_t y);
void text_get_cursor_pos(size_t *x, size_t *y);
void text_set_text_fg(size_t fg);
void text_set_text_bg(size_t bg);
void text_set_text_fg_bright(size_t fg);
void text_set_text_bg_bright(size_t bg);
void text_set_text_fg_default(void);
void text_set_text_bg_default(void);
bool text_scroll_disable(void);
void text_scroll_enable(void);
void text_move_character(size_t new_x, size_t new_y, size_t old_x, size_t old_y);
void text_scroll(void);
void text_revscroll(void);
void text_swap_palette(void);
void text_save_state(void);
void text_restore_state(void);
void text_double_buffer(bool state);
void text_double_buffer_flush(void);
uint64_t text_context_size(void);
void text_context_save(uint64_t ptr);
void text_context_restore(uint64_t ptr);
void text_full_refresh(void);
void text_putchar(struct term_context *ctx, uint8_t c);
void text_clear(struct term_context *ctx, bool move);
void text_enable_cursor(struct term_context *ctx);
bool text_disable_cursor(struct term_context *ctx);
void text_set_cursor_pos(struct term_context *ctx, size_t x, size_t y);
void text_get_cursor_pos(struct term_context *ctx, size_t *x, size_t *y);
void text_set_text_fg(struct term_context *ctx, size_t fg);
void text_set_text_bg(struct term_context *ctx, size_t bg);
void text_set_text_fg_bright(struct term_context *ctx, size_t fg);
void text_set_text_bg_bright(struct term_context *ctx, size_t bg);
void text_set_text_fg_default(struct term_context *ctx);
void text_set_text_bg_default(struct term_context *ctx);
bool text_scroll_disable(struct term_context *ctx);
void text_scroll_enable(struct term_context *ctx);
void text_move_character(struct term_context *ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y);
void text_scroll(struct term_context *ctx);
void text_revscroll(struct term_context *ctx);
void text_swap_palette(struct term_context *ctx);
void text_save_state(struct term_context *ctx);
void text_restore_state(struct term_context *ctx);
void text_double_buffer_flush(struct term_context *ctx);
void text_full_refresh(struct term_context *ctx);
#endif

View File

@ -1,7 +1,6 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/gterm.h>
#include <lib/term.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <lib/config.h>
@ -9,121 +8,23 @@
#include <lib/uri.h>
#include <lib/fb.h>
#include <mm/pmm.h>
// Maximum allowed font size in bytes. 16kB should be enough as 9x32 is the
// largest font I've seen, and that would take 9*32 * 256 * 1/8 byte =
// 9216 bytes.
#define VGA_FONT_MAX 16384
#define VGA_FONT_GLYPHS 256
#define DEFAULT_FONT_WIDTH 8
#define DEFAULT_FONT_HEIGHT 16
static size_t vga_font_width;
static size_t vga_font_height;
static size_t glyph_width = 8;
static size_t glyph_height = 16;
static size_t vga_font_scale_x = 1;
static size_t vga_font_scale_y = 1;
static size_t offset_x, offset_y;
#include <term/term.h>
#include <term/backends/framebuffer.h>
#include <lib/term.h>
struct fb_info fbinfo;
static volatile uint32_t *gterm_framebuffer;
static uint16_t gterm_pitch;
static uint16_t gterm_width;
static uint16_t gterm_height;
static uint16_t gterm_bpp;
extern symbol _binary_font_bin_start;
static uint8_t *vga_font_bits = NULL;
static size_t vga_font_bool_size = 0;
static bool *vga_font_bool = NULL;
static uint32_t ansi_colours[8];
static uint32_t ansi_bright_colours[8];
static uint32_t default_fg, default_bg;
extern symbol _binary_font_bin_start, _binary_font_bin_size;
static struct image *background;
static size_t bg_canvas_size = 0;
static uint32_t *bg_canvas = NULL;
static size_t margin = 64;
static size_t margin_gradient = 4;
static size_t rows;
static size_t cols;
static size_t margin;
static size_t margin_gradient;
static uint32_t default_bg, default_fg;
static size_t grid_size = 0;
static size_t queue_size = 0;
static size_t map_size = 0;
struct gterm_char {
uint32_t c;
uint32_t fg;
uint32_t bg;
};
static struct gterm_char *grid = NULL;
struct queue_item {
size_t x, y;
struct gterm_char c;
};
static struct queue_item *queue = NULL;
static size_t queue_i = 0;
static struct queue_item **map = NULL;
static struct context {
uint32_t text_fg;
#define text_fg context.text_fg
uint32_t text_bg;
#define text_bg context.text_bg
bool cursor_status;
#define cursor_status context.cursor_status
size_t cursor_x;
#define cursor_x context.cursor_x
size_t cursor_y;
#define cursor_y context.cursor_y
bool scroll_enabled;
#define scroll_enabled context.scroll_enabled
uint32_t saved_state_text_fg;
#define saved_state_text_fg context.saved_state_text_fg
uint32_t saved_state_text_bg;
#define saved_state_text_bg context.saved_state_text_bg
size_t saved_state_cursor_x;
#define saved_state_cursor_x context.saved_state_cursor_x
size_t saved_state_cursor_y;
#define saved_state_cursor_y context.saved_state_cursor_y
} context;
static size_t old_cursor_x = 0;
static size_t old_cursor_y = 0;
void gterm_save_state(void) {
saved_state_text_fg = text_fg;
saved_state_text_bg = text_bg;
saved_state_cursor_x = cursor_x;
saved_state_cursor_y = cursor_y;
}
void gterm_restore_state(void) {
text_fg = saved_state_text_fg;
text_bg = saved_state_text_bg;
cursor_x = saved_state_cursor_x;
cursor_y = saved_state_cursor_y;
}
void gterm_swap_palette(void) {
uint32_t tmp = text_bg;
text_bg = text_fg;
text_fg = tmp;
}
static size_t bg_canvas_size;
static uint32_t *bg_canvas;
#define A(rgb) (uint8_t)(rgb >> 24)
#define R(rgb) (uint8_t)(rgb >> 16)
@ -142,20 +43,10 @@ static inline uint32_t colour_blend(uint32_t fg, uint32_t bg) {
return ARGB(0, r, g, b);
}
static inline void gterm_plot_px(size_t x, size_t y, uint32_t hex) {
if (x >= gterm_width || y >= gterm_height) {
return;
}
size_t fb_i = x + (gterm_pitch / sizeof(uint32_t)) * y;
gterm_framebuffer[fb_i] = hex;
}
static uint32_t blend_gradient_from_box(size_t x, size_t y, uint32_t bg_px, uint32_t hex) {
size_t distance, x_distance, y_distance;
size_t gradient_stop_x = gterm_width - margin;
size_t gradient_stop_y = gterm_height - margin;
size_t gradient_stop_x = fbinfo.framebuffer_width - margin;
size_t gradient_stop_y = fbinfo.framebuffer_height - margin;
if (x < margin)
x_distance = margin - x;
@ -199,11 +90,11 @@ __attribute__((always_inline)) static inline void genloop(size_t xstart, size_t
for (size_t y = ystart; y < yend; y++) {
size_t image_y = y % img_height, image_x = xstart % img_width;
const size_t off = img_pitch * (img_height - 1 - image_y);
size_t canvas_off = gterm_width * y, fb_off = gterm_pitch / 4 * y;
size_t canvas_off = fbinfo.framebuffer_width * y;
for (size_t x = xstart; x < xend; x++) {
uint32_t img_pixel = *(uint32_t*)(img + image_x * colsize + off);
uint32_t i = blend(x, y, img_pixel);
bg_canvas[canvas_off + x] = i; gterm_framebuffer[fb_off + x] = i;
bg_canvas[canvas_off + x] = i;
if (image_x++ == img_width) image_x = 0; // image_x = x % img_width, but modulo is too expensive
}
}
@ -213,11 +104,11 @@ __attribute__((always_inline)) static inline void genloop(size_t xstart, size_t
for (size_t y = ystart; y < yend; y++) {
size_t image_y = y - background->y_displacement;
const size_t off = img_pitch * (img_height - 1 - image_y);
size_t canvas_off = gterm_width * y, fb_off = gterm_pitch / 4 * y;
size_t canvas_off = fbinfo.framebuffer_width * y;
if (image_y >= background->y_size) { /* external part */
for (size_t x = xstart; x < xend; x++) {
uint32_t i = blend(x, y, background->back_colour);
bg_canvas[canvas_off + x] = i; gterm_framebuffer[fb_off + x] = i;
bg_canvas[canvas_off + x] = i;
}
}
else { /* internal part */
@ -226,7 +117,7 @@ __attribute__((always_inline)) static inline void genloop(size_t xstart, size_t
bool x_external = image_x >= background->x_size;
uint32_t img_pixel = *(uint32_t*)(img + image_x * colsize + off);
uint32_t i = blend(x, y, x_external ? background->back_colour : img_pixel);
bg_canvas[canvas_off + x] = i; gterm_framebuffer[fb_off + x] = i;
bg_canvas[canvas_off + x] = i;
}
}
}
@ -236,16 +127,16 @@ __attribute__((always_inline)) static inline void genloop(size_t xstart, size_t
// so you can set x = xstart * ratio, and increment by ratio at each iteration
case IMAGE_STRETCHED:
for (size_t y = ystart; y < yend; y++) {
size_t img_y = (y * img_height) / gterm_height; // calculate Y with full precision
size_t img_y = (y * img_height) / fbinfo.framebuffer_height; // calculate Y with full precision
size_t off = img_pitch * (img_height - 1 - img_y);
size_t canvas_off = gterm_width * y, fb_off = gterm_pitch / 4 * y;
size_t canvas_off = fbinfo.framebuffer_width * y;
size_t ratio = int_to_fixedp6(img_width) / gterm_width;
size_t ratio = int_to_fixedp6(img_width) / fbinfo.framebuffer_width;
fixedp6 img_x = ratio * xstart;
for (size_t x = xstart; x < xend; x++) {
uint32_t img_pixel = *(uint32_t*)(img + fixedp6_to_int(img_x) * colsize + off);
uint32_t i = blend(x, y, img_pixel);
bg_canvas[canvas_off + x] = i; gterm_framebuffer[fb_off + x] = i;
bg_canvas[canvas_off + x] = i;
img_x += ratio;
}
}
@ -261,24 +152,27 @@ static void loop_external(size_t xstart, size_t xend, size_t ystart, size_t yend
static void loop_margin(size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(xstart, xend, ystart, yend, blend_margin); }
static void loop_internal(size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(xstart, xend, ystart, yend, blend_internal); }
static void gterm_generate_canvas(void) {
static void *generate_canvas(void) {
if (background) {
bg_canvas_size = fbinfo.framebuffer_width * fbinfo.framebuffer_height * sizeof(uint32_t);
bg_canvas = ext_mem_alloc(bg_canvas_size);
int64_t margin_no_gradient = (int64_t)margin - margin_gradient;
if (margin_no_gradient < 0) {
margin_no_gradient = 0;
}
size_t scan_stop_x = gterm_width - margin_no_gradient;
size_t scan_stop_y = gterm_height - margin_no_gradient;
size_t scan_stop_x = fbinfo.framebuffer_width - margin_no_gradient;
size_t scan_stop_y = fbinfo.framebuffer_height - margin_no_gradient;
loop_external(0, gterm_width, 0, margin_no_gradient);
loop_external(0, gterm_width, scan_stop_y, gterm_height);
loop_external(0, fbinfo.framebuffer_width, 0, margin_no_gradient);
loop_external(0, fbinfo.framebuffer_width, scan_stop_y, fbinfo.framebuffer_height);
loop_external(0, margin_no_gradient, margin_no_gradient, scan_stop_y);
loop_external(scan_stop_x, gterm_width, margin_no_gradient, scan_stop_y);
loop_external(scan_stop_x, fbinfo.framebuffer_width, margin_no_gradient, scan_stop_y);
size_t gradient_stop_x = gterm_width - margin;
size_t gradient_stop_y = gterm_height - margin;
size_t gradient_stop_x = fbinfo.framebuffer_width - margin;
size_t gradient_stop_y = fbinfo.framebuffer_height - margin;
if (margin_gradient) {
loop_margin(margin_no_gradient, scan_stop_x, margin_no_gradient, margin);
@ -288,327 +182,17 @@ static void gterm_generate_canvas(void) {
}
loop_internal(margin, gradient_stop_x, margin, gradient_stop_y);
} else {
for (size_t y = 0; y < gterm_height; y++) {
for (size_t x = 0; x < gterm_width; x++) {
bg_canvas[y * gterm_width + x] = default_bg;
gterm_plot_px(x, y, default_bg);
}
}
}
}
static void plot_char(struct gterm_char *c, size_t x, size_t y) {
if (x >= cols || y >= rows) {
return;
return bg_canvas;
}
x = offset_x + x * glyph_width;
y = offset_y + y * glyph_height;
bool *glyph = &vga_font_bool[c->c * vga_font_height * vga_font_width];
// naming: fx,fy for font coordinates, gx,gy for glyph coordinates
for (size_t gy = 0; gy < glyph_height; gy++) {
uint8_t fy = gy / vga_font_scale_y;
volatile uint32_t *fb_line = gterm_framebuffer + x + (y + gy) * (gterm_pitch / 4);
uint32_t *canvas_line = bg_canvas + x + (y + gy) * gterm_width;
for (size_t fx = 0; fx < vga_font_width; fx++) {
bool draw = glyph[fy * vga_font_width + fx];
for (size_t i = 0; i < vga_font_scale_x; i++) {
size_t gx = vga_font_scale_x * fx + i;
uint32_t bg = c->bg == 0xffffffff ? canvas_line[gx] : c->bg;
uint32_t fg = c->fg == 0xffffffff ? canvas_line[gx] : c->fg;
fb_line[gx] = draw ? fg : bg;
}
}
}
}
static void plot_char_fast(struct gterm_char *old, struct gterm_char *c, size_t x, size_t y) {
if (x >= cols || y >= rows) {
return;
}
x = offset_x + x * glyph_width;
y = offset_y + y * glyph_height;
bool *new_glyph = &vga_font_bool[c->c * vga_font_height * vga_font_width];
bool *old_glyph = &vga_font_bool[old->c * vga_font_height * vga_font_width];
for (size_t gy = 0; gy < glyph_height; gy++) {
uint8_t fy = gy / vga_font_scale_y;
volatile uint32_t *fb_line = gterm_framebuffer + x + (y + gy) * (gterm_pitch / 4);
uint32_t *canvas_line = bg_canvas + x + (y + gy) * gterm_width;
for (size_t fx = 0; fx < vga_font_width; fx++) {
bool old_draw = old_glyph[fy * vga_font_width + fx];
bool new_draw = new_glyph[fy * vga_font_width + fx];
if (old_draw == new_draw)
continue;
for (size_t i = 0; i < vga_font_scale_x; i++) {
size_t gx = vga_font_scale_x * fx + i;
uint32_t bg = c->bg == 0xffffffff ? canvas_line[gx] : c->bg;
uint32_t fg = c->fg == 0xffffffff ? canvas_line[gx] : c->fg;
fb_line[gx] = new_draw ? fg : bg;
}
}
}
}
static inline bool compare_char(struct gterm_char *a, struct gterm_char *b) {
return !(a->c != b->c || a->bg != b->bg || a->fg != b->fg);
}
static void push_to_queue(struct gterm_char *c, size_t x, size_t y) {
if (x >= cols || y >= rows) {
return;
}
size_t i = y * cols + x;
struct queue_item *q = map[i];
if (q == NULL) {
if (compare_char(&grid[i], c)) {
return;
}
q = &queue[queue_i++];
q->x = x;
q->y = y;
map[i] = q;
}
q->c = *c;
}
bool gterm_scroll_disable(void) {
bool ret = scroll_enabled;
scroll_enabled = false;
return ret;
}
void gterm_scroll_enable(void) {
scroll_enabled = true;
}
void gterm_revscroll(void) {
for (size_t i = (term_context.scroll_bottom_margin - 1) * cols - 1; ; i--) {
struct gterm_char *c;
struct queue_item *q = map[i];
if (q != NULL) {
c = &q->c;
} else {
c = &grid[i];
}
push_to_queue(c, (i + cols) % cols, (i + cols) / cols);
if (i == term_context.scroll_top_margin * cols) {
break;
}
}
// Clear the first line of the screen.
struct gterm_char empty;
empty.c = ' ';
empty.fg = text_fg;
empty.bg = text_bg;
for (size_t i = term_context.scroll_top_margin * cols;
i < (term_context.scroll_top_margin + 1) * cols; i++) {
push_to_queue(&empty, i % cols, i / cols);
}
}
void gterm_scroll(void) {
for (size_t i = (term_context.scroll_top_margin + 1) * cols;
i < term_context.scroll_bottom_margin * cols; i++) {
struct gterm_char *c;
struct queue_item *q = map[i];
if (q != NULL) {
c = &q->c;
} else {
c = &grid[i];
}
push_to_queue(c, (i - cols) % cols, (i - cols) / cols);
}
// Clear the last line of the screen.
struct gterm_char empty;
empty.c = ' ';
empty.fg = text_fg;
empty.bg = text_bg;
for (size_t i = (term_context.scroll_bottom_margin - 1) * cols;
i < term_context.scroll_bottom_margin * cols; i++) {
push_to_queue(&empty, i % cols, i / cols);
}
}
void gterm_clear(bool move) {
struct gterm_char empty;
empty.c = ' ';
empty.fg = text_fg;
empty.bg = text_bg;
for (size_t i = 0; i < rows * cols; i++) {
push_to_queue(&empty, i % cols, i / cols);
}
if (move) {
cursor_x = 0;
cursor_y = 0;
}
}
void gterm_enable_cursor(void) {
cursor_status = true;
}
bool gterm_disable_cursor(void) {
bool ret = cursor_status;
cursor_status = false;
return ret;
}
void gterm_set_cursor_pos(size_t x, size_t y) {
if (x >= cols) {
if ((int)x < 0) {
x = 0;
} else {
x = cols - 1;
}
}
if (y >= rows) {
if ((int)y < 0) {
y = 0;
} else {
y = rows - 1;
}
}
cursor_x = x;
cursor_y = y;
}
void gterm_get_cursor_pos(size_t *x, size_t *y) {
*x = cursor_x;
*y = cursor_y;
}
void gterm_move_character(size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
if (old_x >= cols || old_y >= rows
|| new_x >= cols || new_y >= rows) {
return;
}
size_t i = old_x + old_y * cols;
struct gterm_char *c;
struct queue_item *q = map[i];
if (q != NULL) {
c = &q->c;
} else {
c = &grid[i];
}
push_to_queue(c, new_x, new_y);
}
void gterm_set_text_fg(size_t fg) {
text_fg = ansi_colours[fg];
}
void gterm_set_text_bg(size_t bg) {
text_bg = ansi_colours[bg];
}
void gterm_set_text_fg_bright(size_t fg) {
text_fg = ansi_bright_colours[fg];
}
void gterm_set_text_bg_bright(size_t bg) {
text_bg = ansi_bright_colours[bg];
}
void gterm_set_text_fg_rgb(uint32_t fg) {
text_fg = fg;
}
void gterm_set_text_bg_rgb(uint32_t bg) {
text_bg = bg;
}
void gterm_set_text_fg_default(void) {
text_fg = default_fg;
}
void gterm_set_text_bg_default(void) {
text_bg = 0xffffffff;
}
static void draw_cursor(void) {
size_t i = cursor_x + cursor_y * cols;
struct gterm_char c;
struct queue_item *q = map[i];
if (q != NULL) {
c = q->c;
} else {
c = grid[i];
}
uint32_t tmp = c.fg;
c.fg = c.bg;
c.bg = tmp;
plot_char(&c, cursor_x, cursor_y);
if (q != NULL) {
grid[i] = q->c;
map[i] = NULL;
}
}
void gterm_double_buffer_flush(void) {
if (cursor_status) {
draw_cursor();
}
for (size_t i = 0; i < queue_i; i++) {
struct queue_item *q = &queue[i];
size_t offset = q->y * cols + q->x;
if (map[offset] == NULL) {
continue;
}
struct gterm_char *old = &grid[offset];
if (q->c.bg == old->bg && q->c.fg == old->fg) {
plot_char_fast(old, &q->c, q->x, q->y);
} else {
plot_char(&q->c, q->x, q->y);
}
grid[offset] = q->c;
map[offset] = NULL;
}
if ((old_cursor_x != cursor_x || old_cursor_y != cursor_y) || cursor_status == false) {
plot_char(&grid[old_cursor_x + old_cursor_y * cols], old_cursor_x, old_cursor_y);
}
old_cursor_x = cursor_x;
old_cursor_y = cursor_y;
queue_i = 0;
}
void gterm_putchar(uint8_t c) {
struct gterm_char ch;
ch.c = c;
ch.fg = text_fg;
ch.bg = text_bg;
push_to_queue(&ch, cursor_x++, cursor_y);
if (cursor_x == cols && (cursor_y < term_context.scroll_bottom_margin - 1 || scroll_enabled)) {
cursor_x = 0;
cursor_y++;
}
if (cursor_y == term_context.scroll_bottom_margin) {
cursor_y--;
gterm_scroll();
}
return NULL;
}
static bool last_serial = false;
static char *last_config = NULL;
bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t height) {
bool gterm_init(char *config, size_t width, size_t height) {
if (current_video_mode >= 0
#if defined (BIOS)
&& current_video_mode != 0x03
@ -619,9 +203,7 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
&& fbinfo.framebuffer_bpp == 32
&& serial == last_serial
&& config == last_config) {
*_rows = rows;
*_cols = cols;
gterm_clear(true);
term->clear(term, true);
return true;
}
@ -634,9 +216,7 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
&& fbinfo.framebuffer_bpp == 32
&& serial == last_serial
&& config == last_config) {
*_rows = rows;
*_cols = cols;
gterm_clear(true);
term->clear(term, true);
return true;
}
@ -644,15 +224,23 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
if (!fb_init(&fbinfo, width, height, 32))
return false;
last_serial = serial;
// Ensure this is xRGB8888, we only support that for the menu
if (fbinfo.red_mask_size != 8
|| fbinfo.red_mask_shift != 16
|| fbinfo.green_mask_size != 8
|| fbinfo.green_mask_shift != 8
|| fbinfo.blue_mask_size != 8
|| fbinfo.blue_mask_shift != 0)
return false;
cursor_status = true;
scroll_enabled = true;
last_serial = serial;
// default scheme
margin = 64;
margin_gradient = 4;
uint32_t ansi_colours[8];
ansi_colours[0] = 0x00000000; // black
ansi_colours[1] = 0x00aa0000; // red
ansi_colours[2] = 0x0000aa00; // green
@ -678,6 +266,8 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
}
}
uint32_t ansi_bright_colours[8];
ansi_bright_colours[0] = 0x00555555; // black
ansi_bright_colours[1] = 0x00ff5555; // red
ansi_bright_colours[2] = 0x0055ff55; // green
@ -716,9 +306,6 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
default_fg = strtoui(theme_foreground, NULL, 16) & 0xffffff;
}
text_fg = default_fg;
text_bg = 0xffffffff;
background = NULL;
char *background_path = config_get_value(config, 0, "TERM_WALLPAPER");
if (background_path != NULL) {
@ -762,27 +349,14 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
}
}
// Ensure this is xRGB8888, we only support that for the menu
if (fbinfo.red_mask_size != 8
|| fbinfo.red_mask_shift != 16
|| fbinfo.green_mask_size != 8
|| fbinfo.green_mask_shift != 8
|| fbinfo.blue_mask_size != 8
|| fbinfo.blue_mask_shift != 0)
return false;
size_t font_width = 8;
size_t font_height = 16;
size_t font_size = (font_width * font_height * FBTERM_FONT_GLYPHS) / 8;
gterm_framebuffer = (void *)(uintptr_t)fbinfo.framebuffer_addr;
gterm_width = fbinfo.framebuffer_width;
gterm_height = fbinfo.framebuffer_height;
gterm_bpp = fbinfo.framebuffer_bpp;
gterm_pitch = fbinfo.framebuffer_pitch;
#define FONT_MAX 16384
uint8_t *font = ext_mem_alloc(FONT_MAX);
vga_font_width = DEFAULT_FONT_WIDTH, vga_font_height = DEFAULT_FONT_HEIGHT;
size_t font_bytes = (vga_font_width * vga_font_height * VGA_FONT_GLYPHS) / 8;
vga_font_bits = ext_mem_alloc(VGA_FONT_MAX);
memcpy(vga_font_bits, (void *)_binary_font_bin_start, VGA_FONT_MAX);
memcpy(font, (void *)_binary_font_bin_start, (uintptr_t)_binary_font_bin_size);
size_t tmp_font_width, tmp_font_height;
@ -790,14 +364,14 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
if (menu_font_size != NULL) {
parse_resolution(&tmp_font_width, &tmp_font_height, NULL, menu_font_size);
size_t tmp_font_bytes = (tmp_font_width * tmp_font_height * VGA_FONT_GLYPHS) / 8;
size_t tmp_font_size = (tmp_font_width * tmp_font_height * FBTERM_FONT_GLYPHS) / 8;
if (tmp_font_bytes > VGA_FONT_MAX) {
print("Font would be too large (%u bytes, %u bytes allowed). Not loading.\n", tmp_font_bytes, VGA_FONT_MAX);
if (tmp_font_size > FONT_MAX) {
print("Font would be too large (%u bytes, %u bytes allowed). Not loading.\n", tmp_font_size, FONT_MAX);
goto no_load_font;
}
font_bytes = tmp_font_bytes;
font_size = tmp_font_size;
}
char *menu_font = config_get_value(config, 0, "TERM_FONT");
@ -806,10 +380,10 @@ bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t
if ((f = uri_open(menu_font)) == NULL) {
print("menu: Could not open font file.\n");
} else {
fread(f, vga_font_bits, 0, font_bytes);
fread(f, font, 0, font_size);
if (menu_font_size != NULL) {
vga_font_width = tmp_font_width;
vga_font_height = tmp_font_height;
font_width = tmp_font_width;
font_height = tmp_font_height;
}
fclose(f);
}
@ -822,138 +396,31 @@ no_load_font:;
font_spacing = strtoui(font_spacing_str, NULL, 10);
}
vga_font_width += font_spacing;
vga_font_bool_size = VGA_FONT_GLYPHS * vga_font_height * vga_font_width * sizeof(bool);
vga_font_bool = ext_mem_alloc(vga_font_bool_size);
for (size_t i = 0; i < VGA_FONT_GLYPHS; i++) {
uint8_t *glyph = &vga_font_bits[i * vga_font_height];
for (size_t y = 0; y < vga_font_height; y++) {
// NOTE: the characters in VGA fonts are always one byte wide.
// 9 dot wide fonts have 8 dots and one empty column, except
// characters 0xC0-0xDF replicate column 9.
for (size_t x = 0; x < 8; x++) {
size_t offset = i * vga_font_height * vga_font_width + y * vga_font_width + x;
if ((glyph[y] & (0x80 >> x))) {
vga_font_bool[offset] = true;
} else {
vga_font_bool[offset] = false;
}
}
// fill columns above 8 like VGA Line Graphics Mode does
for (size_t x = 8; x < vga_font_width; x++) {
size_t offset = i * vga_font_height * vga_font_width + y * vga_font_width + x;
if (i >= 0xC0 && i <= 0xDF) {
vga_font_bool[offset] = (glyph[y] & 1);
} else {
vga_font_bool[offset] = false;
}
}
}
}
vga_font_scale_x = 1;
vga_font_scale_y = 1;
size_t font_scale_x = 1;
size_t font_scale_y = 1;
char *menu_font_scale = config_get_value(config, 0, "TERM_FONT_SCALE");
if (menu_font_scale != NULL) {
parse_resolution(&vga_font_scale_x, &vga_font_scale_y, NULL, menu_font_scale);
if (vga_font_scale_x > 8 || vga_font_scale_y > 8) {
vga_font_scale_x = 1;
vga_font_scale_y = 1;
parse_resolution(&font_scale_x, &font_scale_y, NULL, menu_font_scale);
if (font_scale_x > 8 || font_scale_y > 8) {
font_scale_x = 1;
font_scale_y = 1;
}
}
glyph_width = vga_font_width * vga_font_scale_x;
glyph_height = vga_font_height * vga_font_scale_y;
uint32_t *canvas = generate_canvas();
*_cols = cols = (gterm_width - margin * 2) / glyph_width;
*_rows = rows = (gterm_height - margin * 2) / glyph_height;
term->deinit(term, pmm_free);
offset_x = margin + ((gterm_width - margin * 2) % glyph_width) / 2;
offset_y = margin + ((gterm_height - margin * 2) % glyph_height) / 2;
grid_size = rows * cols * sizeof(struct gterm_char);
grid = ext_mem_alloc(grid_size);
queue_size = rows * cols * sizeof(struct queue_item);
queue = ext_mem_alloc(queue_size);
queue_i = 0;
map_size = rows * cols * sizeof(struct queue_item *);
map = ext_mem_alloc(map_size);
bg_canvas_size = gterm_width * gterm_height * sizeof(uint32_t);
bg_canvas = ext_mem_alloc(bg_canvas_size);
gterm_generate_canvas();
gterm_clear(true);
gterm_double_buffer_flush();
term = fbterm_init(ext_mem_alloc,
(void *)(uintptr_t)fbinfo.framebuffer_addr,
fbinfo.framebuffer_width, fbinfo.framebuffer_height, fbinfo.framebuffer_pitch,
canvas,
ansi_colours, ansi_bright_colours,
&default_bg, &default_fg,
font, font_width, font_height, font_spacing,
font_scale_x, font_scale_y,
margin);
return true;
}
void gterm_deinit(void) {
if (background != NULL) {
image_close(background);
}
pmm_free(vga_font_bits, VGA_FONT_MAX);
pmm_free(vga_font_bool, vga_font_bool_size);
pmm_free(grid, grid_size);
pmm_free(queue, queue_size);
pmm_free(map, map_size);
pmm_free(bg_canvas, bg_canvas_size);
}
uint64_t gterm_context_size(void) {
uint64_t ret = 0;
ret += sizeof(struct context);
ret += grid_size;
return ret;
}
void gterm_context_save(uint64_t ptr) {
memcpy32to64(ptr, (uint64_t)(uintptr_t)&context, sizeof(struct context));
ptr += sizeof(struct context);
memcpy32to64(ptr, (uint64_t)(uintptr_t)grid, grid_size);
}
void gterm_context_restore(uint64_t ptr) {
memcpy32to64((uint64_t)(uintptr_t)&context, ptr, sizeof(struct context));
ptr += sizeof(struct context);
memcpy32to64((uint64_t)(uintptr_t)grid, ptr, grid_size);
for (size_t i = 0; i < (size_t)rows * cols; i++) {
size_t x = i % cols;
size_t y = i / cols;
plot_char(&grid[i], x, y);
}
if (cursor_status) {
draw_cursor();
}
}
void gterm_full_refresh(void) {
gterm_generate_canvas();
for (size_t i = 0; i < (size_t)rows * cols; i++) {
size_t x = i % cols;
size_t y = i / cols;
plot_char(&grid[i], x, y);
}
if (cursor_status) {
draw_cursor();
}
}

View File

@ -1,44 +1,12 @@
#ifndef __LIB__GTERM_H__
#define __LIB__GTERM_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/image.h>
#include <drivers/vbe.h>
#include <lib/fb.h>
extern struct fb_info fbinfo;
bool gterm_init(char *config, size_t *_rows, size_t *_cols, size_t width, size_t height);
void gterm_deinit(void);
void gterm_putchar(uint8_t c);
void gterm_clear(bool move);
void gterm_enable_cursor(void);
bool gterm_disable_cursor(void);
void gterm_set_cursor_pos(size_t x, size_t y);
void gterm_get_cursor_pos(size_t *x, size_t *y);
void gterm_set_text_fg(size_t fg);
void gterm_set_text_bg(size_t bg);
void gterm_set_text_fg_bright(size_t fg);
void gterm_set_text_bg_bright(size_t bg);
void gterm_set_text_fg_rgb(uint32_t fg);
void gterm_set_text_bg_rgb(uint32_t bg);
void gterm_set_text_fg_default(void);
void gterm_set_text_bg_default(void);
bool gterm_scroll_disable(void);
void gterm_scroll_enable(void);
void gterm_move_character(size_t new_x, size_t new_y, size_t old_x, size_t old_y);
void gterm_scroll(void);
void gterm_revscroll(void);
void gterm_swap_palette(void);
void gterm_save_state(void);
void gterm_restore_state(void);
void gterm_double_buffer_flush(void);
uint64_t gterm_context_size(void);
void gterm_context_save(uint64_t ptr);
void gterm_context_restore(uint64_t ptr);
void gterm_full_refresh(void);
bool gterm_init(char *config, size_t width, size_t height);
#endif

View File

@ -222,7 +222,7 @@ out:
#if defined (BIOS)
if (stage3_loaded) {
#endif
term_write((uint64_t)(uintptr_t)print_buf, print_buf_i);
term_write(term, print_buf, print_buf_i);
#if defined (BIOS)
} else {
s2_print(print_buf, print_buf_i);

View File

@ -348,32 +348,32 @@ again:
static void reprint_string(int x, int y, const char *s) {
size_t orig_x, orig_y;
disable_cursor();
get_cursor_pos(&orig_x, &orig_y);
term->disable_cursor(term);
term->get_cursor_pos(term, &orig_x, &orig_y);
set_cursor_pos_helper(x, y);
print("%s", s);
set_cursor_pos_helper(orig_x, orig_y);
enable_cursor();
term->enable_cursor(term);
}
static void cursor_back(void) {
size_t x, y;
get_cursor_pos(&x, &y);
term->get_cursor_pos(term, &x, &y);
if (x) {
x--;
} else if (y) {
y--;
x = term_cols - 1;
x = term->cols - 1;
}
set_cursor_pos_helper(x, y);
}
static void cursor_fwd(void) {
size_t x, y;
get_cursor_pos(&x, &y);
if (x < term_cols - 1) {
term->get_cursor_pos(term, &x, &y);
if (x < term->cols - 1) {
x++;
} else if (y < term_rows - 1) {
} else if (y < term->rows - 1) {
y++;
x = 0;
}
@ -381,20 +381,20 @@ static void cursor_fwd(void) {
}
void readline(const char *orig_str, char *buf, size_t limit) {
bool prev_autoflush = term_autoflush;
term_autoflush = false;
bool prev_autoflush = term->autoflush;
term->autoflush = false;
size_t orig_str_len = strlen(orig_str);
memmove(buf, orig_str, orig_str_len);
buf[orig_str_len] = 0;
size_t orig_x, orig_y;
get_cursor_pos(&orig_x, &orig_y);
term->get_cursor_pos(term, &orig_x, &orig_y);
print("%s", orig_str);
for (size_t i = orig_str_len; ; ) {
term_double_buffer_flush();
term->double_buffer_flush(term);
int c = getchar();
switch (c) {
case GETCHAR_CURSOR_LEFT:
@ -461,6 +461,6 @@ void readline(const char *orig_str, char *buf, size_t limit) {
}
out:
term_double_buffer_flush();
term_autoflush = prev_autoflush;
term->double_buffer_flush(term);
term->autoflush = prev_autoflush;
}

File diff suppressed because it is too large Load Diff

View File

@ -6,37 +6,7 @@
#include <stdbool.h>
#include <lib/image.h>
#include <lib/print.h>
#define TERM_TABSIZE (8)
#define MAX_ESC_VALUES (16)
extern struct term_context {
bool control_sequence;
bool csi;
bool escape;
bool rrr;
bool discard_next;
bool bold;
bool reverse_video;
bool dec_private;
bool insert_mode;
uint8_t g_select;
uint8_t charsets[2];
size_t current_charset;
size_t escape_offset;
size_t esc_values_i;
size_t saved_cursor_x;
size_t saved_cursor_y;
size_t current_primary;
size_t scroll_top_margin;
size_t scroll_bottom_margin;
uint32_t esc_values[MAX_ESC_VALUES];
bool saved_state_bold;
bool saved_state_reverse_video;
size_t saved_state_current_charset;
size_t saved_state_current_primary;
} term_context;
#include <term/term.h>
enum {
_NOT_READY,
@ -47,76 +17,29 @@ enum {
extern int current_video_mode;
extern int term_backend;
extern size_t term_rows, term_cols;
extern bool term_runtime;
void term_notready(void);
void term_fallback(void);
void term_reinit(void);
void term_deinit(void);
void term_vbe(char *config, size_t width, size_t height);
void term_textmode(void);
void term_write(uint64_t buf, uint64_t count);
extern void (*raw_putchar)(uint8_t c);
extern void (*clear)(bool move);
extern void (*enable_cursor)(void);
extern bool (*disable_cursor)(void);
extern void (*set_cursor_pos)(size_t x, size_t y);
extern void (*get_cursor_pos)(size_t *x, size_t *y);
extern void (*set_text_fg)(size_t fg);
extern void (*set_text_bg)(size_t bg);
extern void (*set_text_fg_bright)(size_t fg);
extern void (*set_text_bg_bright)(size_t bg);
extern void (*set_text_fg_rgb)(uint32_t fg);
extern void (*set_text_bg_rgb)(uint32_t bg);
extern void (*set_text_fg_default)(void);
extern void (*set_text_bg_default)(void);
extern bool (*scroll_disable)(void);
extern void (*scroll_enable)(void);
extern void (*term_move_character)(size_t new_x, size_t new_y, size_t old_x, size_t old_y);
extern void (*term_scroll)(void);
extern void (*term_revscroll)(void);
extern void (*term_swap_palette)(void);
extern void (*term_save_state)(void);
extern void (*term_restore_state)(void);
extern void (*term_double_buffer_flush)(void);
extern uint64_t (*term_context_size)(void);
extern void (*term_context_save)(uint64_t ptr);
extern void (*term_context_restore)(uint64_t ptr);
extern void (*term_full_refresh)(void);
#define TERM_CB_DEC 10
#define TERM_CB_BELL 20
#define TERM_CB_PRIVATE_ID 30
#define TERM_CB_STATUS_REPORT 40
#define TERM_CB_POS_REPORT 50
#define TERM_CB_KBD_LEDS 60
#define TERM_CB_MODE 70
#define TERM_CB_LINUX 80
extern struct term_context *term;
#define TERM_CTX_SIZE ((uint64_t)(-1))
#define TERM_CTX_SAVE ((uint64_t)(-2))
#define TERM_CTX_RESTORE ((uint64_t)(-3))
#define TERM_FULL_REFRESH ((uint64_t)(-4))
extern uint64_t term_arg;
extern void (*term_callback)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
extern bool term_autoflush;
inline void reset_term(void) {
term_autoflush = true;
enable_cursor();
term->autoflush = true;
term->enable_cursor(term);
print("\e[2J\e[H");
term_double_buffer_flush();
term->double_buffer_flush(term);
}
inline void set_cursor_pos_helper(size_t x, size_t y) {
print("\e[%u;%uH", (int)y + 1, (int)x + 1);
}
void term_fallback(void);
void term_vbe(char *config, size_t width, size_t height);
void term_textmode(void);
void _term_write(uint64_t buf, uint64_t count);
#endif

View File

@ -43,6 +43,7 @@ SECTIONS
term_write = .;
term_backend = .;
term_fallback = .;
term = .;
stage3_addr = .;
#else
#ifdef LINKER_NOS2MAP

View File

@ -154,17 +154,17 @@ static void putchar_tokencol(int type, char c) {
static bool editor_no_term_reset = false;
char *config_entry_editor(const char *title, const char *orig_entry) {
term_autoflush = false;
term->autoflush = false;
enable_cursor();
term->enable_cursor(term);
print("\e[2J\e[H");
size_t cursor_offset = 0;
size_t entry_size = strlen(orig_entry);
size_t _window_size = term_rows - 8;
size_t _window_size = term->rows - 8;
size_t window_offset = 0;
size_t line_size = term_cols - 2;
size_t line_size = term->cols - 2;
bool display_overflow_error = false;
@ -200,12 +200,12 @@ refresh:
invalid_syntax = false;
print("\e[2J\e[H");
disable_cursor();
term->disable_cursor(term);
{
size_t x, y;
print("\n");
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y);
term->get_cursor_pos(term, &x, &y);
set_cursor_pos_helper(term->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y);
print("\e[3%sm%s\e[37m", menu_branding_colour, menu_branding);
print("\n\n");
}
@ -213,7 +213,7 @@ refresh:
print(" \e[32mESC\e[0m Discard and Exit \e[32mF10\e[0m Boot\n\n");
print(serial ? "/" : "\xda");
for (size_t i = 0; i < term_cols - 2; i++) {
for (size_t i = 0; i < term->cols - 2; i++) {
switch (i) {
case 1: case 2: case 3:
if (window_offset > 0) {
@ -223,7 +223,7 @@ refresh:
// FALLTHRU
default: {
size_t title_length = strlen(title);
if (i == (term_cols / 2) - DIV_ROUNDUP(title_length, 2) - 1) {
if (i == (term->cols / 2) - DIV_ROUNDUP(title_length, 2) - 1) {
print("%s", title);
i += title_length - 1;
} else {
@ -234,7 +234,7 @@ refresh:
}
size_t tmpx, tmpy;
get_cursor_pos(&tmpx, &tmpy);
term->get_cursor_pos(term, &tmpx, &tmpy);
print(serial ? "\\" : "\xbf");
set_cursor_pos_helper(0, tmpy + 1);
print(serial ? "|" : "\xb3");
@ -250,20 +250,20 @@ refresh:
&& current_line < window_offset + window_size
&& current_line >= window_offset) {
size_t x, y;
get_cursor_pos(&x, &y);
term->get_cursor_pos(term, &x, &y);
if (i == cursor_offset) {
cursor_x = x;
cursor_y = y;
printed_cursor = true;
}
set_cursor_pos_helper(term_cols - 1, y);
set_cursor_pos_helper(term->cols - 1, y);
if (current_line == window_offset + window_size - 1) {
get_cursor_pos(&tmpx, &tmpy);
term->get_cursor_pos(term, &tmpx, &tmpy);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, tmpy + 1);
print(serial ? "\\" : "\xc0");
} else {
get_cursor_pos(&tmpx, &tmpy);
term->get_cursor_pos(term, &tmpx, &tmpy);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, tmpy + 1);
print(serial ? "|" : "\xb3");
@ -281,7 +281,7 @@ refresh:
if (current_line < window_offset + window_size
&& current_line >= window_offset) {
if (i == cursor_offset) {
get_cursor_pos(&cursor_x, &cursor_y);
term->get_cursor_pos(term, &cursor_x, &cursor_y);
printed_cursor = true;
}
if (syntax_highlighting_enabled) {
@ -291,8 +291,8 @@ refresh:
}
printed_early = true;
size_t x, y;
get_cursor_pos(&x, &y);
if (y == term_rows - 3) {
term->get_cursor_pos(term, &x, &y);
if (y == term->rows - 3) {
print(serial ? ">" : "\x1a");
set_cursor_pos_helper(0, y + 1);
print(serial ? "\\" : "\xc0");
@ -309,7 +309,7 @@ refresh:
&& current_line < window_offset + window_size
&& current_line >= window_offset
&& !printed_cursor) {
get_cursor_pos(&cursor_x, &cursor_y);
term->get_cursor_pos(term, &cursor_x, &cursor_y);
printed_cursor = true;
}
@ -356,35 +356,35 @@ refresh:
// syntax error alert
if (validation_enabled) {
size_t x, y;
get_cursor_pos(&x, &y);
set_cursor_pos_helper(0, term_rows-1);
scroll_disable();
term->get_cursor_pos(term, &x, &y);
set_cursor_pos_helper(0, term->rows - 1);
term->scroll_enabled = false;
if (invalid_syntax) {
print("\e[31mConfiguration is INVALID.\e[0m");
} else {
print("\e[32mConfiguration is valid.\e[0m");
}
scroll_enable();
term->scroll_enabled = true;
set_cursor_pos_helper(x, y);
}
if (current_line - window_offset < window_size) {
size_t x, y;
for (size_t i = 0; i < (window_size - (current_line - window_offset)) - 1; i++) {
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols - 1, y);
term->get_cursor_pos(term, &x, &y);
set_cursor_pos_helper(term->cols - 1, y);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, y + 1);
print(serial ? "|" : "\xb3");
}
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols - 1, y);
term->get_cursor_pos(term, &x, &y);
set_cursor_pos_helper(term->cols - 1, y);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, y + 1);
print(serial ? "\\" : "\xc0");
}
for (size_t i = 0; i < term_cols - 2; i++) {
for (size_t i = 0; i < term->cols - 2; i++) {
switch (i) {
case 1: case 2: case 3:
if (current_line - window_offset >= window_size) {
@ -396,22 +396,22 @@ refresh:
print(serial ? "-" : "\xc4");
}
}
get_cursor_pos(&tmpx, &tmpy);
term->get_cursor_pos(term, &tmpx, &tmpy);
print(serial ? "/" : "\xd9");
set_cursor_pos_helper(0, tmpy + 1);
if (display_overflow_error) {
scroll_disable();
term->scroll_enabled = false;
print("\e[31mText buffer not big enough, delete something instead.");
scroll_enable();
term->scroll_enabled = true;
display_overflow_error = false;
}
// Hack to redraw the cursor
set_cursor_pos_helper(cursor_x, cursor_y);
enable_cursor();
term->enable_cursor(term);
term_double_buffer_flush();
term->double_buffer_flush(term);
int c = getchar();
size_t buffer_len = strlen(buffer);
@ -692,16 +692,16 @@ noreturn void _menu(bool first_run) {
menu_init_term();
refresh:
term_autoflush = false;
term->autoflush = false;
disable_cursor();
term->disable_cursor(term);
print("\e[2J\e[H");
{
size_t x, y;
print("\n");
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y);
term->get_cursor_pos(term, &x, &y);
set_cursor_pos_helper(term->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y);
print("\e[3%sm%s\e[37m", menu_branding_colour, menu_branding);
print("\n\n\n\n");
}
@ -710,14 +710,14 @@ refresh:
if (quiet) {
quiet = false;
menu_init_term();
term_autoflush = false;
disable_cursor();
term->autoflush = false;
term->disable_cursor(term);
}
print("Config file %s.\n\n", config_ready ? "contains no valid entries" : "not found");
print("For information on the format of Limine config entries, consult CONFIG.md in\n");
print("the root of the Limine source repository.\n\n");
print("Press a key to enter the Limine console...");
term_double_buffer_flush();
term->double_buffer_flush(term);
getchar();
reset_term();
console();
@ -725,24 +725,24 @@ refresh:
{ // Draw box around boot menu
size_t x, y;
get_cursor_pos(&x, &y);
term->get_cursor_pos(term, &x, &y);
print(serial ? "/" : "\xda");
for (size_t i = 0; i < term_cols - 2; i++) {
for (size_t i = 0; i < term->cols - 2; i++) {
print(serial ? "-" : "\xc4");
}
print(serial ? "\\" : "\xbf");
for (size_t i = y + 1; i < term_rows - 2; i++) {
for (size_t i = y + 1; i < term->rows - 2; i++) {
set_cursor_pos_helper(0, i);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(term_cols - 1, i);
set_cursor_pos_helper(term->cols - 1, i);
print(serial ? "|" : "\xb3");
}
set_cursor_pos_helper(0, term_rows - 2);
set_cursor_pos_helper(0, term->rows - 2);
print(serial ? "\\" : "\xc0");
for (size_t i = 0; i < term_cols - 2; i++) {
for (size_t i = 0; i < term->cols - 2; i++) {
print(serial ? "-" : "\xc4");
}
print(serial ? "/" : "\xd9");
@ -755,7 +755,7 @@ refresh:
{
size_t x, y;
get_cursor_pos(&x, &y);
term->get_cursor_pos(term, &x, &y);
set_cursor_pos_helper(0, 3);
if (editor_enabled && selected_menu_entry->sub == NULL) {
print(" \e[32mARROWS\e[0m Select \e[32mENTER\e[0m Boot \e[32mE\e[0m Edit");
@ -763,7 +763,7 @@ refresh:
print(" \e[32mARROWS\e[0m Select \e[32mENTER\e[0m %s",
selected_menu_entry->expanded ? "Collapse" : "Expand");
}
set_cursor_pos_helper(term_cols - 13, 3);
set_cursor_pos_helper(term->cols - 13, 3);
print("\e[32mC\e[0m Console");
set_cursor_pos_helper(x, y);
}
@ -776,11 +776,11 @@ refresh:
if (skip_timeout == false) {
print("\n\n");
for (size_t i = timeout; i; i--) {
set_cursor_pos_helper(0, term_rows - 1);
scroll_disable();
set_cursor_pos_helper(0, term->rows - 1);
term->scroll_enabled = false;
print("\e[2K\e[32mBooting automatically in \e[92m%u\e[32m, press any key to stop the countdown...\e[0m", i);
scroll_enable();
term_double_buffer_flush();
term->scroll_enabled = true;
term->double_buffer_flush(term);
if ((c = pit_sleep_and_quit_on_keypress(1))) {
skip_timeout = true;
if (quiet) {
@ -789,21 +789,21 @@ refresh:
goto timeout_aborted;
}
print("\e[2K");
term_double_buffer_flush();
term->double_buffer_flush(term);
goto timeout_aborted;
}
}
goto autoboot;
}
set_cursor_pos_helper(0, term_rows - 1);
set_cursor_pos_helper(0, term->rows - 1);
if (selected_menu_entry->comment != NULL) {
scroll_disable();
term->scroll_enabled = false;
print("\e[36m%s\e[0m", selected_menu_entry->comment);
scroll_enable();
term->scroll_enabled = true;
}
term_double_buffer_flush();
term->double_buffer_flush(term);
for (;;) {
c = getchar();

View File

@ -217,7 +217,7 @@ noreturn void efi_chainload_file(char *config, struct file_handle *image) {
pmm_free(_ptr, image->size);
fclose(image);
term_deinit();
term->deinit(term, pmm_free);
size_t req_width = 0, req_height = 0, req_bpp = 0;

View File

@ -269,7 +269,7 @@ void limine_term_callback(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
static void term_write_shim(uint64_t context, uint64_t buf, uint64_t count) {
(void)context;
term_write(buf, count);
_term_write(buf, count);
}
noreturn void limine_load(char *config, char *cmdline) {
@ -697,17 +697,15 @@ FEAT_START
if (terminal_request->callback != 0) {
#if defined (__i386__)
term_callback = limine_term_callback;
term->callback = (void *)limine_term_callback;
limine_term_callback_ptr = terminal_request->callback;
#elif defined (__x86_64__) || defined (__aarch64__)
term_callback = (void *)terminal_request->callback;
term->callback = (void *)terminal_request->callback;
#else
#error Unknown architecture
#endif
}
term_arg = reported_addr(terminal);
#if defined (__i386__)
if (limine_rt_stack == NULL) {
limine_rt_stack = ext_mem_alloc(16384) + 16384;
@ -723,8 +721,8 @@ FEAT_START
term_fb_ptr = &terminal->framebuffer;
terminal->columns = term_cols;
terminal->rows = term_rows;
terminal->columns = term->cols;
terminal->rows = term->rows;
uint64_t *term_list = ext_mem_alloc(1 * sizeof(uint64_t));
term_list[0] = reported_addr(terminal);
@ -737,7 +735,7 @@ FEAT_START
goto skip_fb_init;
FEAT_END
term_deinit();
term->deinit(term, pmm_free);
if (!fb_init(&fb, req_width, req_height, req_bpp)) {
panic(true, "limine: Could not acquire framebuffer");
@ -995,9 +993,9 @@ FEAT_START
FEAT_END
// Clear terminal for kernels that will use the Limine terminal
term_write((uint64_t)(uintptr_t)("\e[2J\e[H"), 7);
term_write(term, "\e[2J\e[H", 7);
term_runtime = true;
term->in_bootloader = false;
#if defined (__x86_64__) || defined (__i386__)
#if defined (BIOS)

View File

@ -495,7 +495,7 @@ noreturn void linux_load(char *config, char *cmdline) {
// Video
///////////////////////////////////////
term_deinit();
term->deinit(term, pmm_free);
struct screen_info *screen_info = &boot_params->screen_info;

View File

@ -302,7 +302,7 @@ noreturn void multiboot1_load(char *config, char *cmdline) {
multiboot1_info->bootloader_name = (uint32_t)(size_t)lowmem_bootname - mb1_info_slide;
multiboot1_info->flags |= (1 << 9);
term_deinit();
term->deinit(term, pmm_free);
if (header.flags & (1 << 2)) {
size_t req_width = header.fb_width;

View File

@ -505,7 +505,7 @@ noreturn void multiboot2_load(char *config, char* cmdline) {
tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
tag->common.size = sizeof(struct multiboot_tag_framebuffer);
term_deinit();
term->deinit(term, pmm_free);
if (fbtag) {
size_t req_width = fbtag->width;