Files
nt-lite/code/kernel/hal/hal_screen.c
T

371 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// HAL screen support. This file owns GOP framebuffer drawing before a runtime display provider takes over.
// The 8x16 bitmap font is a standard VGA/PC font. We embed just the
// printable ASCII range (0x20–0x7E). Each glyph is 16 bytes; bit 7 of
// each byte is the leftmost pixel.
#include <stdint.h>
#include <string.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/mm_virt.h>
#include <kernel/hal/hal_output.h>
static uint32_t* gFb = (uint32_t *)0;
static uint32_t* gBackbuffer = (uint32_t *)0;
static int gWidth = 0;
static int gHeight = 0;
static int gStride = 0; // pixels per row
static int gCursorX = 0, gCursorY = 0;
#define FONT_W 8
#define FONT_H 16
#define SCREEN_DEFAULT_FG 0xCCCCCCu
#define SCREEN_DEFAULT_BG 0x012456u
// 8x16 VGA font - printable ASCII only (glyph 0 = space = 0x20).
#include <kernel/hal/font8x16.h> // see font8x16.h
// Internals.
static inline uint32_t* ScreenBackbufferPixel(int x, int y) {
return gBackbuffer ? (gBackbuffer + y * gStride + x) : (gFb + y * gStride + x);
}
// Updates pixel Raw.
static inline void PutPixelRaw(int x, int y, uint32_t color) {
if ((unsigned)x >= (unsigned)gWidth || (unsigned)y >= (unsigned)gHeight)
return;
ScreenBackbufferPixel(x, y)[0] = color;
gFb[y * gStride + x] = color;
}
// Implements the screen Flush Rows operation.
static void ScreenFlushRows(int startY, int rows) {
if (!gBackbuffer || rows <= 0)
return;
if (startY < 0) {
rows += startY;
startY = 0;
}
if (startY >= gHeight || rows <= 0)
return;
if (startY + rows > gHeight)
rows = gHeight - startY;
size_t rowBytes = (size_t)gStride * sizeof(uint32_t);
memcpy(gFb + startY * gStride, gBackbuffer + startY * gStride,
(size_t)rows * rowBytes);
}
// HAL_SCREEN implementations.
static void ScreenClear(uint32_t color) {
for (int y = 0; y < gHeight; y++)
for (int x = 0; x < gWidth; x++)
ScreenBackbufferPixel(x, y)[0] = color;
ScreenFlushRows(0, gHeight);
if (!gBackbuffer) {
// ScreenFlushRows is a no-op without a backbuffer.
for (int y = 0; y < gHeight; y++)
for (int x = 0; x < gWidth; x++)
gFb[y * gStride + x] = color;
}
gCursorX = 0; gCursorY = 0;
}
// Implements the screen Put Pixel operation.
static void ScreenPutPixel(int x, int y, uint32_t color) {
PutPixelRaw(x, y, color);
}
// Implements the screen Blit operation.
static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) {
// Calculate clipping bounds.
int startX = (x < 0) ? 0 : x;
int startY = (y < 0) ? 0 : y;
int endX = (x + w > gWidth) ? gWidth : (x + w);
int endY = (y + h > gHeight) ? gHeight : (y + h);
int drawW = endX - startX;
int drawH = endY - startY;
// If the image is entirely off-screen, do nothing.
if (drawW <= 0 || drawH <= 0) {
return;
}
// Adjust the source pointer if the image was clipped on the top or left.
int srcOffsetX = startX - x;
int srcOffsetY = startY - y;
// Calculate bytes to copy per row.
size_t bytesPerRow = (size_t)drawW * sizeof(uint32_t);
for (int row = 0; row < drawH; row++) {
uint32_t* shadow = ScreenBackbufferPixel(startX, startY + row);
uint32_t* fb = gFb + ((startY + row) * gStride) + startX;
const uint32_t* src = pixels + ((srcOffsetY + row) * w) + srcOffsetX;
memcpy(shadow, src, bytesPerRow);
memcpy(fb, src, bytesPerRow);
}
}
// Implements the screen Get Size operation.
static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; }
// Implements the screen Get Framebuffer operation.
static KSTATUS ScreenGetFramebuffer(HAL_FRAMEBUFFER_INFO* outInfo) {
if (!outInfo || !gFb || gWidth <= 0 || gHeight <= 0 || gStride <= 0)
return STATUS_INVALID_PARAMETER;
outInfo->Base = (uintptr_t)gFb;
outInfo->Width = (uint32_t)gWidth;
outInfo->Height = (uint32_t)gHeight;
outInfo->Stride = (uint32_t)gStride;
outInfo->Format = 1; // X8R8G8B8
return STATUS_SUCCESS;
}
// Implements the screen Scroll operation.
static void ScreenScroll(int lines) {
if (lines <= 0)
return;
if (lines >= gHeight) {
ScreenClear(SCREEN_DEFAULT_BG);
return;
}
size_t rowBytes = (size_t)gStride * sizeof(uint32_t);
uint32_t* shadow = gBackbuffer ? gBackbuffer : gFb;
memmove(shadow, shadow + lines * gStride,
(size_t)(gHeight - lines) * rowBytes);
// Fill the revealed rows with the console background. Do not memset here:
// the background color is not zero.
uint32_t* fill = shadow + (gHeight - lines) * gStride;
for (int y = 0; y < lines; y++) {
for (int x = 0; x < gWidth; x++)
fill[y * gStride + x] = SCREEN_DEFAULT_BG;
}
ScreenFlushRows(0, gHeight);
}
// Implements the screen Put Char operation.
static void ScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) {
const uint8_t* glyph = Font8x16 + ((uint8_t)c * 16);
for (int row = 0; row < 16; row++) {
uint8_t bits = glyph[row];
for (int col = 0; col < 8; col++) {
uint32_t color = (bits & (0x80u >> col)) ? fg : bg;
PutPixelRaw(x + col, y + row, color);
}
}
}
// Implements the screen Print Char operation.
static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) {
int cols = gWidth / FONT_W;
int rows = gHeight / FONT_H;
if (c == '\n') {
gCursorX = 0;
gCursorY++;
} else if (c == '\r') {
gCursorX = 0;
} else if (c == '\b') {
if (gCursorX > 0) {
gCursorX--;
} else if (gCursorY > 0) {
gCursorY--;
gCursorX = cols - 1;
}
} else {
ScreenPutChar(c, gCursorX * FONT_W, gCursorY * FONT_H, fg, bg);
if (++gCursorX >= cols) { gCursorX = 0; gCursorY++; }
}
if (gCursorY >= rows) {
ScreenScroll(FONT_H);
gCursorY = rows - 1;
}
}
// Mouse cursor – 12×19 monochrome arrow sprite
// 0 = transparent 1 = black outline 2 = white fill
// The hotspot is at (0,0) (top-left pixel = tip of the arrow).
#define CURSOR_W 16
#define CURSOR_H 25
// Each entry is 0xAARRGGBB stored as a uint32_t.
// Pixels with brightness < 24 are fully transparent.
static const unsigned int gCursorPixels[CURSOR_H][CURSOR_W] = {
{ 0x7F575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFAFAFA, 0x855D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFEFEFE, 0xFFFFFFFF, 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFFFFFF, 0x8A626262, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8D656565, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8E666666, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x91696969, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x946C6C6C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x956D6D6D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9B737373, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9D757575, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0xA0787878, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFF9F9F9, 0xFFFFFFFF, 0xA1797979, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xAE868686, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFFFFFF, 0xFDD5D5D5, 0xF4CCCCCC, 0xF4CCCCCC, 0xF4CCCCCC, 0xF3CBCBCB, 0xF1C9C9C9, 0xFAD2D2D2, 0x7E565656 },
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFEFEFEF, 0xFFFCFCFC, 0xFFFFFFFF, 0x9A727272, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x7F575757, 0x8F676767, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE0E0E0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0xFFEBEBEB, 0xFFFFFFFF, 0xFFFFFFFF, 0x81595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFDFDFD, 0xFFFFFFFF, 0x8C646464, 0x00000000, 0x00000000, 0x00000000, 0xB78F8F8F, 0xFFFFFFFF, 0xFFFEFEFE, 0xECC4C4C4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50282828, 0xFFF9F9F9, 0xFFFFFFFF, 0xFFFFFFFF, 0x623A3A3A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xD8B0B0B0, 0xFFFFFFFF, 0xFFFFFFFF, 0xCDA5A5A5, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6C444444, 0xFFFFFFFF, 0xFFFCFCFC, 0xFFF4F4F4, 0x4D252525, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF2CACACA, 0xFFFFFFFF, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8E666666, 0xEFC7C7C7, 0x7D555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
};
// Saved framebuffer pixels beneath the current cursor position.
static uint32_t gCursorSavedBg[CURSOR_W * CURSOR_H];
static int gCursorDrawX = -1; // -1 = cursor not yet drawn
static int gCursorDrawY = -1;
// Save the background rectangle [x,y,CURSOR_W,CURSOR_H] from the framebuffer.
static void CursorSaveBg(int x, int y) {
for (int row = 0; row < CURSOR_H; row++) {
int fy = y + row;
for (int col = 0; col < CURSOR_W; col++) {
int fx = x + col;
uint32_t px = 0;
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight)
px = ScreenBackbufferPixel(fx, fy)[0];
gCursorSavedBg[row * CURSOR_W + col] = px;
}
}
}
// Restore the background previously saved by CursorSaveBg.
static void CursorRestoreBg(int x, int y) {
for (int row = 0; row < CURSOR_H; row++) {
int fy = y + row;
for (int col = 0; col < CURSOR_W; col++) {
int fx = x + col;
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) {
uint32_t px = gBackbuffer
? ScreenBackbufferPixel(fx, fy)[0]
: gCursorSavedBg[row * CURSOR_W + col];
gFb[fy * gStride + fx] = px;
}
}
}
}
// Alpha-blend a cursor pixel over a framebuffer pixel.
// Framebuffer format: 0x00RRGGBB (high byte ignored / always 0).
// Cursor pixel format: 0xAARRGGBB.
static inline uint32_t BlendPixel(uint32_t dst, uint32_t src) {
uint8_t a = (uint8_t)(src >> 24);
if (a == 0) return dst;
if (a == 255) return src & 0x00FFFFFFu;
uint8_t sr = (uint8_t)(src >> 16);
uint8_t sg = (uint8_t)(src >> 8);
uint8_t sb = (uint8_t)(src );
uint8_t dr = (uint8_t)(dst >> 16);
uint8_t dg = (uint8_t)(dst >> 8);
uint8_t db = (uint8_t)(dst );
uint8_t na = 255 - a;
uint8_t or_ = (uint8_t)((a * sr + na * dr) >> 8);
uint8_t og = (uint8_t)((a * sg + na * dg) >> 8);
uint8_t ob = (uint8_t)((a * sb + na * db) >> 8);
return ((uint32_t)or_ << 16) | ((uint32_t)og << 8) | ob;
}
// Draw the arrow sprite at (x,y), saving the background first.
// Called from hal_mouse.c via extern declaration.
void HalScreenDrawCursor(int x, int y) {
CursorSaveBg(x, y);
gCursorDrawX = x;
gCursorDrawY = y;
for (int row = 0; row < CURSOR_H; row++) {
int fy = y + row;
for (int col = 0; col < CURSOR_W; col++) {
uint32_t src = gCursorPixels[row][col];
if ((src >> 24) == 0) continue; // fully transparent, skip
int fx = x + col;
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) {
uint32_t dst = ScreenBackbufferPixel(fx, fy)[0];
gFb[fy * gStride + fx] = BlendPixel(dst, src);
}
}
}
}
// Erase the cursor by restoring the saved background.
void HalScreenEraseCursor(void) {
if (gCursorDrawX < 0) return; // never drawn
CursorRestoreBg(gCursorDrawX, gCursorDrawY);
gCursorDrawX = -1;
gCursorDrawY = -1;
}
// Writes one HAL output record through the boot framebuffer text console.
static void HalScreenWriteOutputRecord(void* context, const HAL_OUTPUT_RECORD* record) {
(void)context;
if (!record || !record->Buffer)
return;
for (size_t i = 0; i < record->Length; i++)
ScreenPrintChar(record->Buffer[i], record->Foreground, record->Background);
}
// Registers the boot framebuffer text endpoint.
void HalScreenRegisterOutputEndpoint(HAL_OUTPUT_ROUTER* output) {
if (!output || !output->RegisterEndpoint || !gFb || gWidth <= 0 || gHeight <= 0)
return;
HAL_OUTPUT_ENDPOINT endpoint;
endpoint.Name = "boot-framebuffer";
endpoint.StreamMask = HAL_OUTPUT_STREAM_ALL;
endpoint.Capabilities = HAL_OUTPUT_ENDPOINT_PANIC_SAFE;
endpoint.Write = HalScreenWriteOutputRecord;
endpoint.Context = 0;
uint32_t endpointId = HAL_OUTPUT_ENDPOINT_INVALID;
if (KSUCCESS(output->RegisterEndpoint(&endpoint, &endpointId)))
output->BootFramebufferEndpointId = endpointId;
}
// Populate the HAL_SCREEN vtable.
void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int w, int h, int stride,
HAL_SCREEN* out) {
gFb = (uint32_t *)fb;
gWidth = w;
gHeight = h;
gStride = stride;
size_t backbufferPixels = (size_t)gStride * (size_t)gHeight;
size_t backbufferBytes = backbufferPixels * sizeof(uint32_t);
if (gStride > 0 && gHeight > 0 &&
backbufferPixels / (size_t)gStride == (size_t)gHeight &&
backbufferBytes / sizeof(uint32_t) == backbufferPixels) {
gBackbuffer = (uint32_t*)AllocatePool(backbufferBytes);
}
out->Clear = ScreenClear;
out->PutPixel = ScreenPutPixel;
out->Blit = ScreenBlit;
out->GetSize = ScreenGetSize;
out->GetFramebuffer = ScreenGetFramebuffer;
out->Scroll = ScreenScroll;
out->PutChar = ScreenPutChar;
out->PrintChar = ScreenPrintChar;
out->DrawCursor = HalScreenDrawCursor;
out->EraseCursor = HalScreenEraseCursor;
ScreenClear(SCREEN_DEFAULT_BG);
}