term: Add double buffering
diff --git a/limine-pxe.bin b/limine-pxe.bin
index 4bc1e3b8..0181e5a6 100644
Binary files a/limine-pxe.bin and b/limine-pxe.bin differ
diff --git a/limine.bin b/limine.bin
index b2c29d4b..2bd8f53d 100644
Binary files a/limine.bin and b/limine.bin differ
diff --git a/stage2.map b/stage2.map
index 34fb9da4..b87a3cf2 100644
Binary files a/stage2.map and b/stage2.map differ
diff --git a/stage2/drivers/vbe.c b/stage2/drivers/vbe.c
index b1956e8b..602ae5c5 100644
--- a/stage2/drivers/vbe.c
+++ b/stage2/drivers/vbe.c
@@ -44,6 +44,9 @@ static int frame_height, frame_width;
static struct image *background;
static struct vbe_char *grid;
+static struct vbe_char *front_grid;
+
+static bool double_buffer_enabled = false;
static bool cursor_status = true;
@@ -181,26 +184,27 @@ void vbe_plot_char(struct vbe_char *c, int x, int y) {
}
static void plot_char_grid(struct vbe_char *c, int x, int y) {
- vbe_plot_char(c, x * VGA_FONT_WIDTH + frame_width,
- y * VGA_FONT_HEIGHT + frame_height);
+ if (!double_buffer_enabled) {
+ vbe_plot_char(c, x * VGA_FONT_WIDTH + frame_width,
+ y * VGA_FONT_HEIGHT + frame_height);
+ }
grid[x + y * cols] = *c;
}
static void clear_cursor(void) {
- if (cursor_status) {
- vbe_plot_char(&grid[cursor_x + cursor_y * cols],
- cursor_x * VGA_FONT_WIDTH + frame_width,
- cursor_y * VGA_FONT_HEIGHT + frame_height);
- }
+ struct vbe_char c = grid[cursor_x + cursor_y * cols];
+ c.fg = text_fg;
+ c.bg = text_bg;
+ plot_char_grid(&c, cursor_x, cursor_y);
}
static void draw_cursor(void) {
- struct vbe_char c = grid[cursor_x + cursor_y * cols];
- c.fg = cursor_fg;
- c.bg = cursor_bg;
- if (cursor_status)
- vbe_plot_char(&c, cursor_x * VGA_FONT_WIDTH + frame_width,
- cursor_y * VGA_FONT_HEIGHT + frame_height);
+ if (cursor_status) {
+ struct vbe_char c = grid[cursor_x + cursor_y * cols];
+ c.fg = cursor_fg;
+ c.bg = cursor_bg;
+ plot_char_grid(&c, cursor_x, cursor_y);
+ }
}
static void scroll(void) {
@@ -271,6 +275,35 @@ void vbe_set_text_bg(int bg) {
text_bg = ansi_colours[bg];
}
+void vbe_double_buffer_flush(void) {
+ for (size_t i = 0; i < (size_t)rows * cols; i++) {
+ struct vbe_char c = grid[i];
+
+ if (!memcmp(&c, &front_grid[i], sizeof(struct vbe_char)))
+ continue;
+
+ front_grid[i] = grid[i];
+
+ int x = i % cols;
+ int y = i / cols;
+
+ vbe_plot_char(&c, x * VGA_FONT_WIDTH + frame_width,
+ y * VGA_FONT_HEIGHT + frame_height);
+ }
+}
+
+void vbe_double_buffer(bool state) {
+ double_buffer_enabled = state;
+ if (state) {
+ memset(grid, 0, rows * cols * sizeof(struct vbe_char));
+ memset(front_grid, 0, rows * cols * sizeof(struct vbe_char));
+ vbe_clear(true);
+ vbe_double_buffer_flush();
+ } else {
+ vbe_clear(true);
+ }
+}
+
void vbe_putchar(char c) {
switch (c) {
case '\b':
@@ -352,6 +385,7 @@ bool vbe_tty_init(int *_rows, int *_cols, uint32_t *_colours, int _margin, int _
*_cols = cols = (vbe_width - _margin * 2) / VGA_FONT_WIDTH;
*_rows = rows = (vbe_height - _margin * 2) / VGA_FONT_HEIGHT;
grid = ext_mem_alloc(rows * cols * sizeof(struct vbe_char));
+ front_grid = ext_mem_alloc(rows * cols * sizeof(struct vbe_char));
background = _background;
if (background)
diff --git a/stage2/drivers/vbe.h b/stage2/drivers/vbe.h
index 48e6f9fd..3f595e1a 100644
--- a/stage2/drivers/vbe.h
+++ b/stage2/drivers/vbe.h
@@ -34,4 +34,7 @@ void vbe_get_cursor_pos(int *x, int *y);
void vbe_set_text_fg(int fg);
void vbe_set_text_bg(int bg);
+void vbe_double_buffer_flush(void);
+void vbe_double_buffer(bool state);
+
#endif
diff --git a/stage2/drivers/vga_textmode.c b/stage2/drivers/vga_textmode.c
index 4271ff80..a6aa412a 100644
--- a/stage2/drivers/vga_textmode.c
+++ b/stage2/drivers/vga_textmode.c
@@ -1,28 +1,35 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
+#include <drivers/vga_textmode.h>
#include <sys/cpu.h>
#include <lib/real.h>
-#include <drivers/vga_textmode.h>
+#include <lib/libc.h>
+#include <mm/pmm.h>
#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
#define VD_COLS (80 * 2)
#define VD_ROWS 25
+static char *back_buffer = NULL;
+static char *front_buffer = NULL;
static char *video_mem = (char *)0xb8000;
+
+static char *current_buffer;
+
static size_t cursor_offset = 0;
static int cursor_status = 1;
static uint8_t text_palette = 0x07;
static uint8_t cursor_palette = 0x70;
static void clear_cursor(void) {
- video_mem[cursor_offset + 1] = text_palette;
+ current_buffer[cursor_offset + 1] = text_palette;
return;
}
static void draw_cursor(void) {
if (cursor_status) {
- video_mem[cursor_offset + 1] = cursor_palette;
+ current_buffer[cursor_offset + 1] = cursor_palette;
}
return;
}
@@ -30,11 +37,11 @@ static void draw_cursor(void) {
static void scroll(void) {
// move the text up by one row
for (size_t i = 0; i <= VIDEO_BOTTOM - VD_COLS; i++)
- video_mem[i] = video_mem[i + VD_COLS];
+ current_buffer[i] = current_buffer[i + VD_COLS];
// clear the last line of the screen
for (size_t i = VIDEO_BOTTOM; i > VIDEO_BOTTOM - VD_COLS; i -= 2) {
- video_mem[i] = text_palette;
- video_mem[i - 1] = ' ';
+ current_buffer[i] = text_palette;
+ current_buffer[i - 1] = ' ';
}
return;
}
@@ -42,8 +49,8 @@ static void scroll(void) {
void text_clear(bool move) {
clear_cursor();
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
- video_mem[i] = ' ';
- video_mem[i + 1] = text_palette;
+ current_buffer[i] = ' ';
+ current_buffer[i + 1] = text_palette;
}
if (move)
cursor_offset = 0;
@@ -68,10 +75,39 @@ void text_disable_cursor(void) {
void init_vga_textmode(int *_rows, int *_cols) {
outb(0x3d4, 0x0a);
outb(0x3d5, 0x20);
- text_clear(true);
*_rows = VD_ROWS;
*_cols = VD_COLS / 2;
+
+ text_double_buffer(false);
+}
+
+void text_double_buffer(bool state) {
+ if (state) {
+ if (back_buffer == NULL)
+ back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
+ if (front_buffer == NULL)
+ front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
+ memset(video_mem, 0, VD_ROWS * VD_COLS);
+ memset(back_buffer, 0, VD_ROWS * VD_COLS);
+ memset(front_buffer, 0, VD_ROWS * VD_COLS);
+ current_buffer = back_buffer;
+ text_clear(true);
+ text_double_buffer_flush();
+ } else {
+ current_buffer = video_mem;
+ text_clear(true);
+ }
+}
+
+void text_double_buffer_flush(void) {
+ for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
+ if (back_buffer[i] == front_buffer[i])
+ continue;
+
+ front_buffer[i] = back_buffer[i];
+ video_mem[i] = back_buffer[i];
+ }
}
static int text_get_cursor_pos_y(void) {
@@ -122,7 +158,7 @@ void text_putchar(char c) {
break;
default:
clear_cursor();
- video_mem[cursor_offset] = c;
+ current_buffer[cursor_offset] = c;
if (cursor_offset >= (VIDEO_BOTTOM - 1)) {
scroll();
cursor_offset = VIDEO_BOTTOM - (VD_COLS - 1);
diff --git a/stage2/drivers/vga_textmode.h b/stage2/drivers/vga_textmode.h
index 7cc3cc97..6d06c438 100644
--- a/stage2/drivers/vga_textmode.h
+++ b/stage2/drivers/vga_textmode.h
@@ -14,4 +14,7 @@ void text_get_cursor_pos(int *x, int *y);
void text_set_text_fg(int fg);
void text_set_text_bg(int bg);
+void text_double_buffer(bool state);
+void text_double_buffer_flush(void);
+
#endif
diff --git a/stage2/lib/term.c b/stage2/lib/term.c
index 64c8972a..1e4abbf5 100644
--- a/stage2/lib/term.c
+++ b/stage2/lib/term.c
@@ -22,6 +22,9 @@ void (*get_cursor_pos)(int *x, int *y);
void (*set_text_fg)(int fg);
void (*set_text_bg)(int bg);
+void (*term_double_buffer)(bool status);
+void (*term_double_buffer_flush)(void);
+
int term_rows, term_cols;
void term_vbe(uint32_t *colours, int margin, int margin_gradient, struct image *background) {
@@ -41,6 +44,9 @@ void term_vbe(uint32_t *colours, int margin, int margin_gradient, struct image *
set_text_fg = vbe_set_text_fg;
set_text_bg = vbe_set_text_bg;
+ term_double_buffer = vbe_double_buffer;
+ term_double_buffer_flush = vbe_double_buffer_flush;
+
term_backend = VBE;
}
@@ -57,6 +63,9 @@ void term_textmode(void) {
set_text_fg = text_set_text_fg;
set_text_bg = text_set_text_bg;
+ term_double_buffer = text_double_buffer;
+ term_double_buffer_flush = text_double_buffer_flush;
+
term_backend = TEXTMODE;
}
diff --git a/stage2/lib/term.h b/stage2/lib/term.h
index 4bde2c32..d63a12bb 100644
--- a/stage2/lib/term.h
+++ b/stage2/lib/term.h
@@ -14,6 +14,9 @@ extern void (*get_cursor_pos)(int *x, int *y);
extern void (*set_text_fg)(int fg);
extern void (*set_text_bg)(int bg);
+extern void (*term_double_buffer)(bool status);
+extern void (*term_double_buffer_flush)(void);
+
void term_vbe(uint32_t *colours, int margin, int margin_gradient, struct image *background);
void term_textmode(void);
void term_deinit(void);
diff --git a/stage2/menu.c b/stage2/menu.c
index 41e71e2c..b88d028d 100644
--- a/stage2/menu.c
+++ b/stage2/menu.c
@@ -135,6 +135,8 @@ refresh:
set_cursor_pos(cursor_x, cursor_y);
enable_cursor();
+ term_double_buffer_flush();
+
int c = getchar();
switch (c) {
case 0:
@@ -324,6 +326,8 @@ char *menu(char **cmdline_ret) {
if (menu_tree == NULL)
panic("Config contains no entries.");
+ term_double_buffer(true);
+
refresh:
clear(true);
print("\n\n \e[36m Limine " LIMINE_VERSION " \e[37m\n\n\n");
@@ -341,6 +345,7 @@ refresh:
print("\n\n");
for (int i = timeout; i; i--) {
print("\rBooting automatically in %u, press any key to stop the countdown...", i);
+ term_double_buffer_flush();
if ((c = pit_sleep_and_quit_on_keypress(18))) {
skip_timeout = true;
print("\e[2K\r\e[2A");
@@ -350,6 +355,8 @@ refresh:
goto autoboot;
}
+ term_double_buffer_flush();
+
for (;;) {
c = getchar();
timeout_aborted:
@@ -377,6 +384,7 @@ timeout_aborted:
}
clear(true);
*cmdline_ret = cmdline;
+ term_double_buffer(false);
return selected_menu_entry->body;
case 'e': {
if (selected_menu_entry->sub != NULL)
diff --git a/test/limine.cfg b/test/limine.cfg
index c1cd3dd5..b52a6c0c 100644
--- a/test/limine.cfg
+++ b/test/limine.cfg
@@ -12,66 +12,6 @@ BACKGROUND_PATH=bios://:1/boot/bg.bmp
:Legacy
-::12345
-PROTOCOL=stivale
-KERNEL_PATH=guid://@GUID@/boot/test.elf
-KERNEL_CMDLINE=Hi! This is an example!
-
-MODULE_PATH=bios://:1/boot/test.elf
-MODULE_STRING=yooooo
-
-::Stivale Test
-
-PROTOCOL=stivale
-KERNEL_PATH=bios://:1/boot/test.elf
-KERNEL_CMDLINE=Hi! This is an example!
-
-MODULE_PATH=bios://:1/boot/test.elf
-MODULE_STRING=yooooo
-
-MODULE_PATH=bios://:1/boot/bg.bmp
-MODULE_STRING=yooooo
-
-::123456
-
-:::Stivale Test
-
-PROTOCOL=stivale
-KERNEL_PATH=bios://:1/boot/test.elf
-KERNEL_CMDLINE=Hi! This is an example!
-
-MODULE_PATH=bios://:1/boot/test.elf
-MODULE_STRING=yooooo
-
-MODULE_PATH=bios://:1/boot/bg.bmp
-MODULE_STRING=yooooo
-
-:::123123123
-
-PROTOCOL=stivale
-KERNEL_PATH=bios://:1/boot/test.elf
-KERNEL_CMDLINE=Hi! This is an example!
-
-MODULE_PATH=bios://:1/boot/test.elf
-MODULE_STRING=yooooo
-
-MODULE_PATH=bios://:1/boot/bg.bmp
-MODULE_STRING=yooooo
-
-:::foo
-
-::::bar
-
-PROTOCOL=stivale
-KERNEL_PATH=bios://:1/boot/test.elf
-KERNEL_CMDLINE=Hi! This is an example!
-
-MODULE_PATH=bios://:1/boot/test.elf
-MODULE_STRING=yooooo
-
-MODULE_PATH=bios://:1/boot/bg.bmp
-MODULE_STRING=yooooo
-
::Stivale Test
PROTOCOL=stivale
