From 1ffb45ac9c603e32a7422c7205d11a4ed1d19d2a Mon Sep 17 00:00:00 2001
From: mintsuki <mintsuki@protonmail.com>
Date: Sun, 4 Apr 2021 03:38:38 +0200
Subject: [PATCH] video: Do not switch video modes when unnecessary to avoid
 flickering and screen blanking out

---
 stage23/drivers/gop.c             | 16 ++++++++++++++++
 stage23/drivers/vbe.c             | 17 ++++++++++++++++-
 stage23/drivers/vga_textmode.s2.c | 28 ++++++++++++++++++++++------
 stage23/lib/term.h                |  1 +
 stage23/lib/term.s2.c             |  9 ++-------
 5 files changed, 57 insertions(+), 14 deletions(-)

diff --git a/stage23/drivers/gop.c b/stage23/drivers/gop.c
index 2f574f91..d42d72e8 100644
--- a/stage23/drivers/gop.c
+++ b/stage23/drivers/gop.c
@@ -2,6 +2,7 @@
 
 #include <efi.h>
 #include <lib/blib.h>
+#include <lib/term.h>
 #include <drivers/gop.h>
 #include <drivers/edid.h>
 #include <lib/print.h>
@@ -98,19 +99,34 @@ static bool try_mode(struct fb_info *ret, size_t mode, int width, int height, in
 
     print("gop: Found matching mode %x, attempting to set...\n", mode);
 
+    if ((int)mode == current_video_mode) {
+        print("gop: Mode was already set, perfect!\n");
+    }
+
     status = uefi_call_wrapper(gop->SetMode, 2, gop, mode);
 
     if (status) {
+        current_video_mode = -2;
         print("gop: Failed to set video mode %x, moving on...\n", mode);
         return false;
     }
 
+    current_video_mode = mode;
+
     ret->memory_model = 0x06;
     ret->framebuffer_addr = gop->Mode->FrameBufferBase;
     ret->framebuffer_pitch = gop->Mode->Info->PixelsPerScanLine * 4;
     ret->framebuffer_width = gop->Mode->Info->HorizontalResolution;
     ret->framebuffer_height = gop->Mode->Info->VerticalResolution;
 
+    // Clear framebuffer
+    for (size_t y = 0; y < ret->framebuffer_height; y++) {
+        for (size_t x = 0; x < ret->framebuffer_pitch; x++) {
+            uint8_t *fbp = (uint8_t *)(uintptr_t)ret->framebuffer_addr;
+            fbp[y * ret->framebuffer_pitch + x] = 0;
+        }
+    }
+
     return true;
 }
 
diff --git a/stage23/drivers/vbe.c b/stage23/drivers/vbe.c
index 6876c652..489db752 100644
--- a/stage23/drivers/vbe.c
+++ b/stage23/drivers/vbe.c
@@ -12,6 +12,7 @@
 #include <lib/image.h>
 #include <lib/config.h>
 #include <lib/uri.h>
+#include <lib/term.h>
 #include <mm/pmm.h>
 
 struct vbe_info_struct {
@@ -175,10 +176,15 @@ retry:
             if (!(vbe_mode_info.mode_attributes & (1 << 7)))
                 continue;
             print("vbe: Found matching mode %x, attempting to set...\n", vid_modes[i]);
-            if (set_vbe_mode(vid_modes[i]) == 0x01) {
+            if (vid_modes[i] == current_video_mode) {
+                print("vbe: Mode was already set, perfect!\n");
+            } else if (set_vbe_mode(vid_modes[i]) == 0x01) {
+                current_video_mode = -2;
                 print("vbe: Failed to set video mode %x, moving on...\n", vid_modes[i]);
                 continue;
             }
+            current_video_mode = vid_modes[i];
+
             print("vbe: Framebuffer address: %x\n", vbe_mode_info.framebuffer_addr);
             ret->memory_model       = vbe_mode_info.memory_model;
             ret->framebuffer_addr   = vbe_mode_info.framebuffer_addr;
@@ -202,6 +208,15 @@ retry:
                 ret->blue_mask_size     = vbe_mode_info.lin_blue_mask_size;
                 ret->blue_mask_shift    = vbe_mode_info.lin_blue_mask_shift;
             }
+
+            // Clear framebuffer
+            for (size_t y = 0; y < ret->framebuffer_height; y++) {
+                for (size_t x = 0; x < ret->framebuffer_pitch; x++) {
+                    uint8_t *fbp = (uint8_t *)(uintptr_t)ret->framebuffer_addr;
+                    fbp[y * ret->framebuffer_pitch + x] = 0;
+                }
+            }
+
             return true;
         }
     }
diff --git a/stage23/drivers/vga_textmode.s2.c b/stage23/drivers/vga_textmode.s2.c
index 99cad37f..c580453b 100644
--- a/stage23/drivers/vga_textmode.s2.c
+++ b/stage23/drivers/vga_textmode.s2.c
@@ -7,6 +7,7 @@
 #include <sys/cpu.h>
 #include <lib/real.h>
 #include <lib/libc.h>
+#include <lib/term.h>
 #include <mm/pmm.h>
 
 #define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
@@ -19,10 +20,10 @@ static uint8_t *video_mem = (uint8_t *)0xb8000;
 
 static uint8_t *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 size_t cursor_offset;
+static int cursor_status;
+static uint8_t text_palette;
+static uint8_t cursor_palette;
 
 static void clear_cursor(void) {
     current_buffer[cursor_offset + 1] = text_palette;
@@ -75,13 +76,28 @@ void text_disable_cursor(void) {
 // VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor
 
 void init_vga_textmode(int *_rows, int *_cols) {
+    if (current_video_mode != -1) {
+        struct rm_regs r = {0};
+        r.eax = 0x0003;
+        rm_int(0x10, &r, &r);
+
+        current_video_mode = -1;
+    }
+
     outb(0x3d4, 0x0a);
     outb(0x3d5, 0x20);
 
-    *_rows = VD_ROWS;
-    *_cols = VD_COLS / 2;
+    cursor_offset = 0;
+    cursor_status = 1;
+    text_palette = 0x07;
+    cursor_palette = 0x70;
 
     text_double_buffer(false);
+
+    text_clear(false);
+
+    *_rows = VD_ROWS;
+    *_cols = VD_COLS / 2;
 }
 
 void text_double_buffer(bool state) {
diff --git a/stage23/lib/term.h b/stage23/lib/term.h
index 63680e8c..03c4bebf 100644
--- a/stage23/lib/term.h
+++ b/stage23/lib/term.h
@@ -31,5 +31,6 @@ enum {
 };
 
 extern int term_backend;
+extern int current_video_mode;
 
 #endif
diff --git a/stage23/lib/term.s2.c b/stage23/lib/term.s2.c
index 469fafe0..23b3c973 100644
--- a/stage23/lib/term.s2.c
+++ b/stage23/lib/term.s2.c
@@ -7,6 +7,8 @@
 #include <lib/blib.h>
 #include <drivers/vga_textmode.h>
 
+int current_video_mode = -1;
+
 int term_backend = NOT_READY;
 
 void (*raw_putchar)(uint8_t c);
@@ -25,7 +27,6 @@ int term_rows, term_cols;
 
 #if defined (bios)
 void term_textmode(void) {
-    term_deinit();
     init_vga_textmode(&term_rows, &term_cols);
 
     raw_putchar    = text_putchar;
@@ -45,12 +46,6 @@ void term_textmode(void) {
 #endif
 
 void term_deinit(void) {
-#if defined (bios)
-    struct rm_regs r = {0};
-    r.eax = 0x0003;
-    rm_int(0x10, &r, &r);
-#endif
-
     term_backend = NOT_READY;
 }