1340 lines
44 KiB
C
1340 lines
44 KiB
C
// ke_display.c - kernel display manager
|
|
//
|
|
// The display manager owns the primary display device, cursor policy, dirty
|
|
// rectangles, and the kernel/user screen API. The first provider is a boot
|
|
// framebuffer backend over the HAL GOP framebuffer; later GPU drivers register
|
|
// DISPLAY_DEVICE providers here.
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <intrin.h>
|
|
|
|
#include <kernel/ke_display.h>
|
|
#include <kernel/ke_debug.h>
|
|
#include <kernel/hal/font8x16.h>
|
|
#include <kernel/display_cursor.h>
|
|
#include <kernel/mm_virt.h>
|
|
#include <kernel/ke_spinlock.h>
|
|
|
|
#define DISPLAY_DEFAULT_FG 0x00CCCCCCu
|
|
#define DISPLAY_DEFAULT_BG 0x00012456u
|
|
|
|
#define DISPLAY_MAX_DIRTY_RECTS 64u
|
|
#define DISPLAY_CURSOR_W DISPLAY_DEFAULT_CURSOR_W
|
|
#define DISPLAY_CURSOR_H DISPLAY_DEFAULT_CURSOR_H
|
|
#define FONT_W 8
|
|
#define FONT_H 16
|
|
#define DISPLAY_CONSOLE_MAX_COLS 256
|
|
#define DISPLAY_CONSOLE_MAX_ROWS 128
|
|
#define DISPLAY_CONSOLE_MAX_CELLS (DISPLAY_CONSOLE_MAX_COLS * DISPLAY_CONSOLE_MAX_ROWS)
|
|
#define DISPLAY_CONSOLE_DEFAULT_ATTR 0x07u
|
|
|
|
typedef struct BOOTFB_CONTEXT {
|
|
const HAL_API* Hal;
|
|
uint32_t* Framebuffer;
|
|
uint32_t* Backbuffer;
|
|
uint32_t Width;
|
|
uint32_t Height;
|
|
uint32_t Stride;
|
|
DISPLAY_STATS Stats;
|
|
} BOOTFB_CONTEXT;
|
|
|
|
static const HAL_API* gDisplayHal;
|
|
static DISPLAY_DEVICE* gDisplayList;
|
|
static DISPLAY_DEVICE* gPrimaryDisplay;
|
|
static SPINLOCK gDisplayLock = SPINLOCK_INIT;
|
|
|
|
static DISPLAY_DEVICE gBootFbDevice;
|
|
static BOOTFB_CONTEXT gBootFb;
|
|
|
|
static DISPLAY_RECT gDirtyRects[DISPLAY_MAX_DIRTY_RECTS];
|
|
static size_t gDirtyRectCount;
|
|
static bool gDirtyAll;
|
|
static uint32_t gDrawDepth;
|
|
static DISPLAY_STATS gDisplayStats;
|
|
|
|
static bool gCursorVisible;
|
|
static bool gCursorDrawn;
|
|
static int gCursorX;
|
|
static int gCursorY;
|
|
static int gCursorDrawX;
|
|
static int gCursorDrawY;
|
|
|
|
static int gConsoleX;
|
|
static int gConsoleY;
|
|
static uint16_t gConsoleAttribute;
|
|
static char gConsoleCells[DISPLAY_CONSOLE_MAX_CELLS];
|
|
static uint8_t gConsoleAttrs[DISPLAY_CONSOLE_MAX_CELLS];
|
|
static volatile long gDisplayConsoleSuspended;
|
|
|
|
static KSTATUS BootFbGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode);
|
|
static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels);
|
|
static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color);
|
|
static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h);
|
|
static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride);
|
|
static KSTATUS BootFbPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, size_t rectCount);
|
|
static KSTATUS BootFbSync(DISPLAY_DEVICE* device);
|
|
static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels);
|
|
static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int x, int y);
|
|
static KSTATUS BootFbShowCursor(DISPLAY_DEVICE* device, bool show);
|
|
static KSTATUS BootFbQueryStats(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats);
|
|
|
|
static const DISPLAY_DEVICE_OPS gBootFbOps = {
|
|
.GetMode = BootFbGetMode,
|
|
.Blit = BootFbBlit,
|
|
.FillRect = BootFbFillRect,
|
|
.CopyRect = BootFbCopyRect,
|
|
.ReadRect = BootFbReadRect,
|
|
.Present = BootFbPresent,
|
|
.Sync = BootFbSync,
|
|
.SetCursorImage = BootFbSetCursorImage,
|
|
.SetCursorPosition = BootFbSetCursorPosition,
|
|
.ShowCursor = BootFbShowCursor,
|
|
.QueryStats = BootFbQueryStats,
|
|
};
|
|
|
|
|
|
static bool KiDisplayHasDevice(void) {
|
|
return gPrimaryDisplay && gPrimaryDisplay->Ops;
|
|
}
|
|
|
|
static bool KiDisplayUsesSoftwareCursor(void) {
|
|
return KiDisplayHasDevice() &&
|
|
(gPrimaryDisplay->Caps & DISPLAY_CAP_SOFTWARE_CURSOR) &&
|
|
gPrimaryDisplay->Ops->ShowCursor;
|
|
}
|
|
|
|
static bool KiDisplayUsesHardwareCursor(void) {
|
|
return KiDisplayHasDevice() &&
|
|
(gPrimaryDisplay->Caps & DISPLAY_CAP_HARDWARE_CURSOR) &&
|
|
gPrimaryDisplay->Ops->ShowCursor;
|
|
}
|
|
|
|
static bool KiDisplayHasCursorOps(void) {
|
|
return KiDisplayHasDevice() &&
|
|
(gPrimaryDisplay->Caps & (DISPLAY_CAP_SOFTWARE_CURSOR | DISPLAY_CAP_HARDWARE_CURSOR)) &&
|
|
gPrimaryDisplay->Ops->ShowCursor;
|
|
}
|
|
|
|
static bool KiDisplayClipRect(const DISPLAY_MODE* mode, DISPLAY_RECT* rect) {
|
|
if (!mode || !rect || rect->W <= 0 || rect->H <= 0)
|
|
return false;
|
|
|
|
int64_t x0 = rect->X;
|
|
int64_t y0 = rect->Y;
|
|
int64_t x1 = x0 + rect->W;
|
|
int64_t y1 = y0 + rect->H;
|
|
|
|
if (x0 < 0) x0 = 0;
|
|
if (y0 < 0) y0 = 0;
|
|
if (x1 > (int64_t)mode->Width) x1 = mode->Width;
|
|
if (y1 > (int64_t)mode->Height) y1 = mode->Height;
|
|
if (x0 >= x1 || y0 >= y1)
|
|
return false;
|
|
|
|
rect->X = (int32_t)x0;
|
|
rect->Y = (int32_t)y0;
|
|
rect->W = (int32_t)(x1 - x0);
|
|
rect->H = (int32_t)(y1 - y0);
|
|
return true;
|
|
}
|
|
|
|
static void KiDisplayDirtyAll(void) {
|
|
gDisplayStats.DirtyAllCount++;
|
|
gDirtyAll = true;
|
|
gDirtyRectCount = 0;
|
|
}
|
|
|
|
static void KiDisplayAddDirty(int x, int y, int w, int h) {
|
|
if (!KiDisplayHasDevice() || gDirtyAll || w <= 0 || h <= 0)
|
|
return;
|
|
|
|
DISPLAY_MODE mode;
|
|
if (!KSUCCESS(gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode)))
|
|
return;
|
|
|
|
DISPLAY_RECT rect = { x, y, w, h };
|
|
if (!KiDisplayClipRect(&mode, &rect))
|
|
return;
|
|
|
|
if (rect.X == 0 && rect.Y == 0 &&
|
|
rect.W == (int32_t)mode.Width && rect.H == (int32_t)mode.Height) {
|
|
KiDisplayDirtyAll();
|
|
return;
|
|
}
|
|
|
|
if (gDirtyRectCount >= DISPLAY_MAX_DIRTY_RECTS) {
|
|
KiDisplayDirtyAll();
|
|
return;
|
|
}
|
|
|
|
gDisplayStats.DirtyRectCount++;
|
|
gDirtyRects[gDirtyRectCount++] = rect;
|
|
}
|
|
|
|
static KSTATUS KiDisplayFlushDirty(void) {
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Present)
|
|
return STATUS_SUCCESS;
|
|
if (!gDirtyAll && gDirtyRectCount == 0)
|
|
return STATUS_SUCCESS;
|
|
|
|
DISPLAY_RECT full;
|
|
const DISPLAY_RECT* rects = gDirtyRects;
|
|
size_t count = gDirtyRectCount;
|
|
|
|
if (gDirtyAll) {
|
|
DISPLAY_MODE mode;
|
|
KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode);
|
|
if (!KSUCCESS(s))
|
|
return s;
|
|
full.X = 0;
|
|
full.Y = 0;
|
|
full.W = (int32_t)mode.Width;
|
|
full.H = (int32_t)mode.Height;
|
|
rects = &full;
|
|
count = 1;
|
|
}
|
|
|
|
gDisplayStats.PresentCount++;
|
|
gDisplayStats.PresentRectCount += count;
|
|
KSTATUS s = gPrimaryDisplay->Ops->Present(gPrimaryDisplay, rects, count);
|
|
if (KSUCCESS(s)) {
|
|
gDirtyAll = false;
|
|
gDirtyRectCount = 0;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static void KiDisplayHideCursorForDraw(void) {
|
|
if (!gCursorDrawn || !KiDisplayUsesSoftwareCursor())
|
|
return;
|
|
|
|
(void)gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, false);
|
|
gCursorDrawn = false;
|
|
}
|
|
|
|
static void KiDisplayRestoreCursorAfterDraw(void) {
|
|
if (!gCursorVisible || gCursorDrawn || !KiDisplayHasCursorOps())
|
|
return;
|
|
|
|
if (gPrimaryDisplay->Ops->SetCursorPosition)
|
|
(void)gPrimaryDisplay->Ops->SetCursorPosition(gPrimaryDisplay, gCursorX, gCursorY);
|
|
|
|
if (KSUCCESS(gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, true)))
|
|
gCursorDrawn = true;
|
|
}
|
|
|
|
static void KiDisplayBeginDraw(void) {
|
|
if (gDrawDepth++ == 0)
|
|
KiDisplayHideCursorForDraw();
|
|
}
|
|
|
|
static KSTATUS KiDisplayEndDraw(void) {
|
|
KSTATUS s = STATUS_SUCCESS;
|
|
if (gDrawDepth > 0)
|
|
gDrawDepth--;
|
|
|
|
if (gDrawDepth == 0) {
|
|
s = KiDisplayFlushDirty();
|
|
KiDisplayRestoreCursorAfterDraw();
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static KSTATUS KiDisplayDrawBlitNoCursor(int x, int y, int w, int h, const uint32_t* pixels) {
|
|
if (!pixels || w <= 0 || h <= 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Blit)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
KSTATUS s = gPrimaryDisplay->Ops->Blit(gPrimaryDisplay, x, y, w, h, pixels);
|
|
if (KSUCCESS(s)) {
|
|
gDisplayStats.BlitCount++;
|
|
gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t);
|
|
KiDisplayAddDirty(x, y, w, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static KSTATUS KiDisplayFillRectNoCursor(int x, int y, int w, int h, uint32_t color) {
|
|
if (w <= 0 || h <= 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->FillRect)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
KSTATUS s = gPrimaryDisplay->Ops->FillRect(gPrimaryDisplay, x, y, w, h, color);
|
|
if (KSUCCESS(s)) {
|
|
gDisplayStats.FillRectCount++;
|
|
gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t);
|
|
KiDisplayAddDirty(x, y, w, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static KSTATUS KiDisplayCopyRectNoCursor(int dstX, int dstY, int srcX, int srcY, int w, int h) {
|
|
if (w <= 0 || h <= 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->CopyRect)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
KSTATUS s = gPrimaryDisplay->Ops->CopyRect(gPrimaryDisplay, dstX, dstY, srcX, srcY, w, h);
|
|
if (KSUCCESS(s)) {
|
|
gDisplayStats.CopyRectCount++;
|
|
gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t);
|
|
KiDisplayAddDirty(dstX, dstY, w, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static size_t KiDisplayConsoleIndex(int col, int row) {
|
|
return (size_t)row * DISPLAY_CONSOLE_MAX_COLS + (size_t)col;
|
|
}
|
|
|
|
static uint32_t KiDisplayConsoleColor(uint8_t index) {
|
|
static const uint32_t colors[16] = {
|
|
0x00000000u, 0x000000AAu, 0x0000AA00u, 0x0000AAAAu,
|
|
0x00AA0000u, 0x00AA00AAu, 0x00AA5500u, 0x00AAAAAAu,
|
|
0x00555555u, 0x005555FFu, 0x0055FF55u, 0x0055FFFFu,
|
|
0x00FF5555u, 0x00FF55FFu, 0x00FFFF55u, 0x00FFFFFFu,
|
|
};
|
|
return colors[index & 0x0Fu];
|
|
}
|
|
|
|
static uint32_t KiDisplayConsoleFg(uint16_t attr) {
|
|
return KiDisplayConsoleColor((uint8_t)(attr & 0x0Fu));
|
|
}
|
|
|
|
static uint32_t KiDisplayConsoleBg(uint16_t attr) {
|
|
return KiDisplayConsoleColor((uint8_t)((attr >> 4) & 0x0Fu));
|
|
}
|
|
|
|
static bool KiDisplayGetConsoleGridLocked(int* outCols, int* outRows) {
|
|
DISPLAY_MODE mode;
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode)
|
|
return false;
|
|
if (!KSUCCESS(gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode)))
|
|
return false;
|
|
|
|
int cols = (int)mode.Width / FONT_W;
|
|
int rows = (int)mode.Height / FONT_H;
|
|
if (cols > DISPLAY_CONSOLE_MAX_COLS) cols = DISPLAY_CONSOLE_MAX_COLS;
|
|
if (rows > DISPLAY_CONSOLE_MAX_ROWS) rows = DISPLAY_CONSOLE_MAX_ROWS;
|
|
if (cols <= 0 || rows <= 0)
|
|
return false;
|
|
|
|
if (outCols) *outCols = cols;
|
|
if (outRows) *outRows = rows;
|
|
return true;
|
|
}
|
|
|
|
static void KiDisplayConsoleResetCellsLocked(void) {
|
|
for (size_t i = 0; i < DISPLAY_CONSOLE_MAX_CELLS; i++) {
|
|
gConsoleCells[i] = ' ';
|
|
gConsoleAttrs[i] = (uint8_t)DISPLAY_CONSOLE_DEFAULT_ATTR;
|
|
}
|
|
gConsoleAttribute = DISPLAY_CONSOLE_DEFAULT_ATTR;
|
|
}
|
|
|
|
static void KiDisplayConsoleScrollCellsLocked(int cols, int rows, int cellLines) {
|
|
if (cols <= 0 || rows <= 0 || cellLines <= 0)
|
|
return;
|
|
|
|
if (cellLines >= rows) {
|
|
for (int y = 0; y < rows; y++) {
|
|
for (int x = 0; x < cols; x++) {
|
|
size_t i = KiDisplayConsoleIndex(x, y);
|
|
gConsoleCells[i] = ' ';
|
|
gConsoleAttrs[i] = (uint8_t)gConsoleAttribute;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (int y = 0; y < rows - cellLines; y++) {
|
|
for (int x = 0; x < cols; x++) {
|
|
size_t dst = KiDisplayConsoleIndex(x, y);
|
|
size_t src = KiDisplayConsoleIndex(x, y + cellLines);
|
|
gConsoleCells[dst] = gConsoleCells[src];
|
|
gConsoleAttrs[dst] = gConsoleAttrs[src];
|
|
}
|
|
}
|
|
|
|
for (int y = rows - cellLines; y < rows; y++) {
|
|
for (int x = 0; x < cols; x++) {
|
|
size_t i = KiDisplayConsoleIndex(x, y);
|
|
gConsoleCells[i] = ' ';
|
|
gConsoleAttrs[i] = (uint8_t)gConsoleAttribute;
|
|
}
|
|
}
|
|
}
|
|
|
|
static KSTATUS KiDisplayDrawCharPixelsLocked(char c, int x, int y, uint32_t fg, uint32_t bg) {
|
|
uint32_t glyphPixels[FONT_W * FONT_H];
|
|
const uint8_t* glyph = Font8x16 + ((uint8_t)c * 16u);
|
|
|
|
for (int row = 0; row < FONT_H; row++) {
|
|
uint8_t bits = glyph[row];
|
|
for (int col = 0; col < FONT_W; col++)
|
|
glyphPixels[row * FONT_W + col] = (bits & (0x80u >> col)) ? fg : bg;
|
|
}
|
|
|
|
return KiDisplayDrawBlitNoCursor(x, y, FONT_W, FONT_H, glyphPixels);
|
|
}
|
|
|
|
static KSTATUS KiDisplayDrawConsoleCellLocked(int col, int row, char c, uint16_t attr, bool updateCell) {
|
|
if (updateCell) {
|
|
size_t i = KiDisplayConsoleIndex(col, row);
|
|
gConsoleCells[i] = c;
|
|
gConsoleAttrs[i] = (uint8_t)attr;
|
|
}
|
|
|
|
return KiDisplayDrawCharPixelsLocked(c, col * FONT_W, row * FONT_H,
|
|
KiDisplayConsoleFg(attr),
|
|
KiDisplayConsoleBg(attr));
|
|
}
|
|
|
|
static void KiDisplaySerialWriteChar(char c) {
|
|
while ((__inbyte(0x3FD) & 0x20) == 0) { }
|
|
if (c == '\n')
|
|
__outbyte(0x3F8, '\r');
|
|
__outbyte(0x3F8, (uint8_t)c);
|
|
}
|
|
|
|
void KeDisplayInitialize(const HAL_API* hal) {
|
|
gDisplayHal = hal;
|
|
gDisplayList = NULL;
|
|
gPrimaryDisplay = NULL;
|
|
gDirtyRectCount = 0;
|
|
gDirtyAll = false;
|
|
gDrawDepth = 0;
|
|
memset(&gDisplayStats, 0, sizeof(gDisplayStats));
|
|
gCursorVisible = false;
|
|
gCursorDrawn = false;
|
|
gCursorX = 0;
|
|
gCursorY = 0;
|
|
gCursorDrawX = -1;
|
|
gCursorDrawY = -1;
|
|
gConsoleX = 0;
|
|
gConsoleY = 0;
|
|
KiDisplayConsoleResetCellsLocked();
|
|
_InterlockedExchange((volatile long*)&gDisplayConsoleSuspended, 0);
|
|
memset(&gBootFb, 0, sizeof(gBootFb));
|
|
memset(&gBootFbDevice, 0, sizeof(gBootFbDevice));
|
|
|
|
if (!hal || !hal->Screen.GetFramebuffer)
|
|
return;
|
|
|
|
HAL_FRAMEBUFFER_INFO fb;
|
|
memset(&fb, 0, sizeof(fb));
|
|
if (!KSUCCESS(hal->Screen.GetFramebuffer(&fb)) || !fb.Base || fb.Width == 0 || fb.Height == 0 || fb.Stride == 0)
|
|
return;
|
|
|
|
gBootFb.Hal = hal;
|
|
gBootFb.Framebuffer = (uint32_t*)fb.Base;
|
|
gBootFb.Width = fb.Width;
|
|
gBootFb.Height = fb.Height;
|
|
gBootFb.Stride = fb.Stride;
|
|
|
|
size_t pixels = (size_t)gBootFb.Stride * (size_t)gBootFb.Height;
|
|
size_t bytes = pixels * sizeof(uint32_t);
|
|
if (gBootFb.Stride > 0 && gBootFb.Height > 0 &&
|
|
pixels / (size_t)gBootFb.Stride == (size_t)gBootFb.Height &&
|
|
bytes / sizeof(uint32_t) == pixels) {
|
|
gBootFb.Backbuffer = (uint32_t*)MmAllocatePool(bytes);
|
|
if (gBootFb.Backbuffer)
|
|
memcpy(gBootFb.Backbuffer, gBootFb.Framebuffer, bytes);
|
|
}
|
|
|
|
gBootFbDevice.Name = "bootfb";
|
|
gBootFbDevice.Caps = DISPLAY_CAP_FRAMEBUFFER;
|
|
if (gBootFb.Backbuffer)
|
|
gBootFbDevice.Caps |= DISPLAY_CAP_SOFTWARE_CURSOR;
|
|
gBootFbDevice.DriverContext = &gBootFb;
|
|
gBootFbDevice.Ops = &gBootFbOps;
|
|
|
|
KSTATUS s = KeDisplayRegisterDevice(&gBootFbDevice);
|
|
if (KSUCCESS(s)) {
|
|
(void)KeDisplaySetPrimary(&gBootFbDevice);
|
|
DISPLAY_MODE mode;
|
|
if (KSUCCESS(KeDisplayGetMode(&mode))) {
|
|
DbgPrint("Display: bootfb primary %ux%u stride=%u\n",
|
|
mode.Width, mode.Height, mode.Stride);
|
|
}
|
|
|
|
// KeDisplayInitialize runs while the debug console still points at the
|
|
// HAL early console, so the registration messages above are written
|
|
// directly through HAL_SCREEN. Resync the bootfb backbuffer once before
|
|
// kmain switches DbgPrint to KeDisplayDebugPrintChar.
|
|
if (gBootFb.Backbuffer) {
|
|
size_t pixels2 = (size_t)gBootFb.Stride * (size_t)gBootFb.Height;
|
|
memcpy(gBootFb.Backbuffer, gBootFb.Framebuffer, pixels2 * sizeof(uint32_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device) {
|
|
if (!device || !device->Name || !device->Ops || !device->Ops->GetMode)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS result = STATUS_SUCCESS;
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next) {
|
|
if (it == device) {
|
|
result = STATUS_ALREADY_EXISTS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (KSUCCESS(result)) {
|
|
device->Next = gDisplayList;
|
|
gDisplayList = device;
|
|
}
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
|
|
if (KSUCCESS(result))
|
|
DbgPrint("Display: registered device %s caps=0x%x\n", device->Name, device->Caps);
|
|
return result;
|
|
}
|
|
|
|
static KSTATUS KiDisplaySeedNewPrimary(DISPLAY_DEVICE* oldDevice, DISPLAY_DEVICE* newDevice) {
|
|
if (!oldDevice || !newDevice || oldDevice == newDevice)
|
|
return STATUS_SUCCESS;
|
|
if (!oldDevice->Ops || !oldDevice->Ops->ReadRect ||
|
|
!newDevice->Ops || !newDevice->Ops->GetMode || !newDevice->Ops->Blit)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
DISPLAY_MODE oldMode;
|
|
DISPLAY_MODE newMode;
|
|
KSTATUS s = oldDevice->Ops->GetMode(oldDevice, &oldMode);
|
|
if (!KSUCCESS(s))
|
|
return s;
|
|
s = newDevice->Ops->GetMode(newDevice, &newMode);
|
|
if (!KSUCCESS(s))
|
|
return s;
|
|
if (oldMode.Format != DisplayPixelFormatX8R8G8B8 ||
|
|
newMode.Format != DisplayPixelFormatX8R8G8B8 ||
|
|
oldMode.Width != newMode.Width || oldMode.Height != newMode.Height)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
uint32_t* row = (uint32_t*)MmAllocatePool((size_t)newMode.Width * sizeof(uint32_t));
|
|
if (!row)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
for (uint32_t y = 0; y < newMode.Height; y++) {
|
|
s = oldDevice->Ops->ReadRect(oldDevice, 0, (int)y, (int)newMode.Width, 1, row, newMode.Width);
|
|
if (!KSUCCESS(s))
|
|
break;
|
|
s = newDevice->Ops->Blit(newDevice, 0, (int)y, (int)newMode.Width, 1, row);
|
|
if (!KSUCCESS(s))
|
|
break;
|
|
}
|
|
|
|
MmFreePool(row);
|
|
return s;
|
|
}
|
|
|
|
|
|
DISPLAY_API void KeDisplaySuspendConsole(bool suspend) {
|
|
_InterlockedExchange((volatile long*)&gDisplayConsoleSuspended, suspend ? 1 : 0);
|
|
}
|
|
|
|
static bool KiDisplayOutputSuspended(void) {
|
|
return _InterlockedCompareExchange((volatile long*)&gDisplayConsoleSuspended, 0, 0) != 0;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device) {
|
|
if (!device || !device->Ops || !device->Ops->GetMode)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
const char* name = device->Name ? device->Name : "?";
|
|
KSTATUS result = STATUS_SUCCESS;
|
|
KSTATUS seedResult = STATUS_SUCCESS;
|
|
const char* seedOldName = NULL;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
|
|
bool found = false;
|
|
for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next) {
|
|
if (it == device) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
DISPLAY_DEVICE* oldDevice = gPrimaryDisplay;
|
|
bool cursorWasVisible = gCursorVisible;
|
|
|
|
if (oldDevice && gCursorDrawn && oldDevice->Ops && oldDevice->Ops->ShowCursor)
|
|
(void)oldDevice->Ops->ShowCursor(oldDevice, false);
|
|
|
|
if (oldDevice && oldDevice != device) {
|
|
seedOldName = oldDevice->Name;
|
|
seedResult = KiDisplaySeedNewPrimary(oldDevice, device);
|
|
}
|
|
|
|
gPrimaryDisplay = device;
|
|
gCursorDrawn = false;
|
|
gDirtyAll = false;
|
|
gDirtyRectCount = 0;
|
|
|
|
if (device->Ops->Present) {
|
|
DISPLAY_MODE mode;
|
|
if (KSUCCESS(device->Ops->GetMode(device, &mode))) {
|
|
DISPLAY_RECT full = { 0, 0, (int32_t)mode.Width, (int32_t)mode.Height };
|
|
(void)device->Ops->Present(device, &full, 1);
|
|
}
|
|
}
|
|
|
|
if (cursorWasVisible)
|
|
KiDisplayRestoreCursorAfterDraw();
|
|
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
|
|
if (!KSUCCESS(seedResult) && seedResult != STATUS_NOT_SUPPORTED) {
|
|
DbgPrint("Display: primary handoff seed %s -> %s failed st=0x%x\n",
|
|
seedOldName ? seedOldName : "?", name, seedResult);
|
|
}
|
|
if (KSUCCESS(result))
|
|
DbgPrint("Display: primary is %s\n", name);
|
|
return result;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayGetMode(DISPLAY_MODE* outMode) {
|
|
if (!outMode)
|
|
return STATUS_INVALID_PARAMETER;
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KSTATUS s = (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode)
|
|
? STATUS_INVALID_PARAMETER
|
|
: gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, outMode);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayGetSize(int* outWidth, int* outHeight) {
|
|
DISPLAY_MODE mode;
|
|
KSTATUS s = KeDisplayGetMode(&mode);
|
|
if (!KSUCCESS(s))
|
|
return s;
|
|
if (outWidth) *outWidth = (int)mode.Width;
|
|
if (outHeight) *outHeight = (int)mode.Height;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayBlit(int x, int y, int w, int h, const uint32_t* pixels) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KiDisplayBeginDraw();
|
|
KSTATUS s = KiDisplayDrawBlitNoCursor(x, y, w, h, pixels);
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayFillRect(int x, int y, int w, int h, uint32_t color) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KiDisplayBeginDraw();
|
|
KSTATUS s = KiDisplayFillRectNoCursor(x, y, w, h, color);
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
static KSTATUS KiDisplayClearLocked(uint32_t color) {
|
|
DISPLAY_MODE mode;
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode)
|
|
return STATUS_NOT_SUPPORTED;
|
|
KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode);
|
|
if (!KSUCCESS(s))
|
|
return s;
|
|
KiDisplayBeginDraw();
|
|
s = KiDisplayFillRectNoCursor(0, 0, (int)mode.Width, (int)mode.Height, color);
|
|
if (KSUCCESS(s))
|
|
KiDisplayConsoleResetCellsLocked();
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayClear(uint32_t color) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KSTATUS s = KiDisplayClearLocked(color);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayCopyRect(int dstX, int dstY, int srcX, int srcY, int w, int h) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KiDisplayBeginDraw();
|
|
KSTATUS s = KiDisplayCopyRectNoCursor(dstX, dstY, srcX, srcY, w, h);
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
static KSTATUS KiDisplayScrollLocked(int lines) {
|
|
if (lines <= 0)
|
|
return STATUS_SUCCESS;
|
|
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode)
|
|
return STATUS_NOT_SUPPORTED;
|
|
DISPLAY_MODE mode;
|
|
KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode);
|
|
if (!KSUCCESS(s))
|
|
return s;
|
|
if ((uint32_t)lines >= mode.Height)
|
|
return KiDisplayClearLocked(DISPLAY_DEFAULT_BG);
|
|
|
|
KiDisplayBeginDraw();
|
|
s = KiDisplayCopyRectNoCursor(0, 0, 0, lines, (int)mode.Width, (int)(mode.Height - (uint32_t)lines));
|
|
if (KSUCCESS(s)) {
|
|
s = KiDisplayFillRectNoCursor(0, (int)(mode.Height - (uint32_t)lines),
|
|
(int)mode.Width, lines, DISPLAY_DEFAULT_BG);
|
|
}
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayScroll(int lines) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KSTATUS s = KiDisplayScrollLocked(lines);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
static KSTATUS KiDisplayPutCharLocked(char c, int x, int y, uint32_t fg, uint32_t bg) {
|
|
KiDisplayBeginDraw();
|
|
KSTATUS s = KiDisplayDrawCharPixelsLocked(c, x, y, fg, bg);
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KSTATUS s = KiDisplayPutCharLocked(c, x, y, fg, bg);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayPrintChar(char c, uint32_t fg, uint32_t bg) {
|
|
KiDisplaySerialWriteChar(c);
|
|
|
|
if (KiDisplayOutputSuspended())
|
|
return STATUS_SUCCESS;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
|
|
DISPLAY_MODE mode;
|
|
KSTATUS s = (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode)
|
|
? STATUS_NOT_SUPPORTED
|
|
: gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode);
|
|
if (!KSUCCESS(s)) {
|
|
const HAL_API* hal = gDisplayHal;
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
if (hal && hal->Screen.PrintChar)
|
|
hal->Screen.PrintChar(c, fg, bg);
|
|
return s;
|
|
}
|
|
|
|
int cols = 0;
|
|
int rows = 0;
|
|
if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_DEVICE_ERROR;
|
|
}
|
|
|
|
if (c == '\n') {
|
|
gConsoleX = 0;
|
|
gConsoleY++;
|
|
} else if (c == '\r') {
|
|
gConsoleX = 0;
|
|
} else if (c == '\b') {
|
|
if (gConsoleX > 0) {
|
|
gConsoleX--;
|
|
} else if (gConsoleY > 0) {
|
|
gConsoleY--;
|
|
gConsoleX = cols - 1;
|
|
}
|
|
} else {
|
|
s = KiDisplayPutCharLocked(c, gConsoleX * FONT_W, gConsoleY * FONT_H, fg, bg);
|
|
if (!KSUCCESS(s)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
if (gConsoleX >= 0 && gConsoleX < cols && gConsoleY >= 0 && gConsoleY < rows) {
|
|
size_t i = KiDisplayConsoleIndex(gConsoleX, gConsoleY);
|
|
gConsoleCells[i] = c;
|
|
gConsoleAttrs[i] = (uint8_t)gConsoleAttribute;
|
|
}
|
|
if (++gConsoleX >= cols) {
|
|
gConsoleX = 0;
|
|
gConsoleY++;
|
|
}
|
|
}
|
|
|
|
if (gConsoleY >= rows) {
|
|
s = KiDisplayScrollLocked(FONT_H);
|
|
if (!KSUCCESS(s)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
KiDisplayConsoleScrollCellsLocked(cols, rows, 1);
|
|
gConsoleY = rows - 1;
|
|
}
|
|
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
void KeDisplayDebugPrintChar(char c, uint32_t fg, uint32_t bg) {
|
|
(void)KeDisplayPrintChar(c, fg, bg);
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo) {
|
|
if (!outInfo)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
int cols = 0;
|
|
int rows = 0;
|
|
if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
outInfo->Columns = (uint32_t)cols;
|
|
outInfo->Rows = (uint32_t)rows;
|
|
outInfo->CursorX = (uint32_t)gConsoleX;
|
|
outInfo->CursorY = (uint32_t)gConsoleY;
|
|
outInfo->Attribute = gConsoleAttribute;
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
int cols = 0;
|
|
int rows = 0;
|
|
if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
if (col < 0 || row < 0 || col >= cols || row >= rows) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
gConsoleX = col;
|
|
gConsoleY = row;
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t length, size_t* writtenOut) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
int cols = 0;
|
|
int rows = 0;
|
|
if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
if (col < 0 || row < 0 || col >= cols || row >= rows) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
if (writtenOut) *writtenOut = 0;
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
size_t written = 0;
|
|
KiDisplayBeginDraw();
|
|
KSTATUS s = STATUS_SUCCESS;
|
|
int x = col;
|
|
int y = row;
|
|
while (written < length && y < rows) {
|
|
size_t i = KiDisplayConsoleIndex(x, y);
|
|
uint16_t attr = gConsoleAttrs[i];
|
|
s = KiDisplayDrawConsoleCellLocked(x, y, c, attr, true);
|
|
if (!KSUCCESS(s))
|
|
break;
|
|
written++;
|
|
if (++x >= cols) {
|
|
x = 0;
|
|
y++;
|
|
}
|
|
}
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
|
|
if (writtenOut) *writtenOut = written;
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, size_t length, size_t* writtenOut) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
int cols = 0;
|
|
int rows = 0;
|
|
if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
if (col < 0 || row < 0 || col >= cols || row >= rows) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
if (writtenOut) *writtenOut = 0;
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
size_t written = 0;
|
|
KiDisplayBeginDraw();
|
|
KSTATUS s = STATUS_SUCCESS;
|
|
int x = col;
|
|
int y = row;
|
|
while (written < length && y < rows) {
|
|
size_t i = KiDisplayConsoleIndex(x, y);
|
|
char c = gConsoleCells[i];
|
|
s = KiDisplayDrawConsoleCellLocked(x, y, c, attr, true);
|
|
if (!KSUCCESS(s))
|
|
break;
|
|
written++;
|
|
if (++x >= cols) {
|
|
x = 0;
|
|
y++;
|
|
}
|
|
}
|
|
KSTATUS ps = KiDisplayEndDraw();
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
|
|
if (writtenOut) *writtenOut = written;
|
|
return KSUCCESS(s) ? ps : s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayPresent(const DISPLAY_RECT* rects, size_t rectCount) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Present) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!rects || rectCount == 0) {
|
|
KSTATUS s = KiDisplayFlushDirty();
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
bool hideCursor = gCursorDrawn && KiDisplayUsesSoftwareCursor();
|
|
if (hideCursor)
|
|
KiDisplayHideCursorForDraw();
|
|
|
|
gDisplayStats.PresentCount++;
|
|
gDisplayStats.PresentRectCount += rectCount;
|
|
KSTATUS s = gPrimaryDisplay->Ops->Present(gPrimaryDisplay, rects, rectCount);
|
|
|
|
if (hideCursor)
|
|
KiDisplayRestoreCursorAfterDraw();
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplaySync(void) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Sync) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
gDisplayStats.SyncCount++;
|
|
KSTATUS s = gPrimaryDisplay->Ops->Sync(gPrimaryDisplay);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayQueryInfo(KE_DISPLAY_INFO* outInfo) {
|
|
if (!outInfo)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
memset(outInfo, 0, sizeof(*outInfo));
|
|
|
|
uint32_t count = 0;
|
|
for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next)
|
|
count++;
|
|
outInfo->DeviceCount = count;
|
|
outInfo->ManagerStats = gDisplayStats;
|
|
|
|
if (!KiDisplayHasDevice()) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
const char* name = gPrimaryDisplay->Name ? gPrimaryDisplay->Name : "?";
|
|
size_t len = strlen(name);
|
|
if (len >= sizeof(outInfo->PrimaryName))
|
|
len = sizeof(outInfo->PrimaryName) - 1;
|
|
memcpy(outInfo->PrimaryName, name, len);
|
|
outInfo->PrimaryName[len] = '\0';
|
|
|
|
outInfo->Caps = gPrimaryDisplay->Caps;
|
|
if (gPrimaryDisplay->Ops->GetMode) {
|
|
KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &outInfo->Mode);
|
|
if (!KSUCCESS(s)) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
}
|
|
|
|
if (gPrimaryDisplay->Ops->QueryStats)
|
|
(void)gPrimaryDisplay->Ops->QueryStats(gPrimaryDisplay, &outInfo->DeviceStats);
|
|
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplaySetCursorImage(int width, int height, int hotX, int hotY, const uint32_t* argbPixels) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
KSTATUS s = (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->SetCursorImage)
|
|
? STATUS_NOT_SUPPORTED
|
|
: gPrimaryDisplay->Ops->SetCursorImage(gPrimaryDisplay, width, height, hotX, hotY, argbPixels);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int x, int y) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
gCursorX = x;
|
|
gCursorY = y;
|
|
gDisplayStats.CursorMoveCount++;
|
|
|
|
if (KiDisplayOutputSuspended()) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!gCursorVisible || !KiDisplayHasCursorOps()) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (gCursorDrawn && KiDisplayUsesSoftwareCursor())
|
|
(void)gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, false);
|
|
|
|
if (gPrimaryDisplay->Ops->SetCursorPosition)
|
|
(void)gPrimaryDisplay->Ops->SetCursorPosition(gPrimaryDisplay, x, y);
|
|
|
|
if (gCursorDrawn && KiDisplayUsesHardwareCursor()) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KSTATUS s = gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, true);
|
|
gCursorDrawn = KSUCCESS(s);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
DISPLAY_API KSTATUS KeDisplayShowCursor(bool show) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
|
gCursorVisible = show;
|
|
if (show) gDisplayStats.CursorShowCount++; else gDisplayStats.CursorHideCount++;
|
|
|
|
if (KiDisplayOutputSuspended()) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!KiDisplayHasCursorOps()) {
|
|
gCursorDrawn = false;
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!show) {
|
|
if (gCursorDrawn)
|
|
(void)gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, false);
|
|
gCursorDrawn = false;
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (gCursorDrawn) {
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (gPrimaryDisplay->Ops->SetCursorPosition)
|
|
(void)gPrimaryDisplay->Ops->SetCursorPosition(gPrimaryDisplay, gCursorX, gCursorY);
|
|
|
|
KSTATUS s = gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, true);
|
|
gCursorDrawn = KSUCCESS(s);
|
|
KeReleaseSpinlock(&gDisplayLock, flags);
|
|
return s;
|
|
}
|
|
|
|
static BOOTFB_CONTEXT* BootFbContext(DISPLAY_DEVICE* device) {
|
|
return device ? (BOOTFB_CONTEXT*)device->DriverContext : NULL;
|
|
}
|
|
|
|
static KSTATUS BootFbGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !fb->Framebuffer || !outMode)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
outMode->Width = fb->Width;
|
|
outMode->Height = fb->Height;
|
|
outMode->Stride = fb->Stride;
|
|
outMode->Format = DisplayPixelFormatX8R8G8B8;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !fb->Framebuffer || !pixels || w <= 0 || h <= 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t* dstBase = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer;
|
|
|
|
int startX = x < 0 ? 0 : x;
|
|
int startY = y < 0 ? 0 : y;
|
|
int endX = x + w > (int)fb->Width ? (int)fb->Width : x + w;
|
|
int endY = y + h > (int)fb->Height ? (int)fb->Height : y + h;
|
|
if (startX >= endX || startY >= endY)
|
|
return STATUS_SUCCESS;
|
|
|
|
int srcOffsetX = startX - x;
|
|
int srcOffsetY = startY - y;
|
|
size_t bytesPerRow = (size_t)(endX - startX) * sizeof(uint32_t);
|
|
|
|
fb->Stats.BlitCount++;
|
|
fb->Stats.BytesCopied += (uint64_t)(endX - startX) * (uint64_t)(endY - startY) * sizeof(uint32_t);
|
|
|
|
for (int row = 0; row < endY - startY; row++) {
|
|
uint32_t* dst = dstBase + ((size_t)(startY + row) * fb->Stride) + startX;
|
|
const uint32_t* src = pixels + ((size_t)(srcOffsetY + row) * (size_t)w) + srcOffsetX;
|
|
memcpy(dst, src, bytesPerRow);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !fb->Framebuffer || w <= 0 || h <= 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t* dstBase = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer;
|
|
|
|
int startX = x < 0 ? 0 : x;
|
|
int startY = y < 0 ? 0 : y;
|
|
int endX = x + w > (int)fb->Width ? (int)fb->Width : x + w;
|
|
int endY = y + h > (int)fb->Height ? (int)fb->Height : y + h;
|
|
if (startX >= endX || startY >= endY)
|
|
return STATUS_SUCCESS;
|
|
|
|
fb->Stats.FillRectCount++;
|
|
fb->Stats.BytesCopied += (uint64_t)(endX - startX) * (uint64_t)(endY - startY) * sizeof(uint32_t);
|
|
|
|
for (int yy = startY; yy < endY; yy++) {
|
|
uint32_t* dst = dstBase + ((size_t)yy * fb->Stride) + startX;
|
|
for (int xx = startX; xx < endX; xx++)
|
|
*dst++ = color;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !fb->Framebuffer || w <= 0 || h <= 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t* base = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer;
|
|
|
|
if (srcX < 0) { int d = -srcX; srcX = 0; dstX += d; w -= d; }
|
|
if (srcY < 0) { int d = -srcY; srcY = 0; dstY += d; h -= d; }
|
|
if (dstX < 0) { int d = -dstX; dstX = 0; srcX += d; w -= d; }
|
|
if (dstY < 0) { int d = -dstY; dstY = 0; srcY += d; h -= d; }
|
|
if (srcX + w > (int)fb->Width) w = (int)fb->Width - srcX;
|
|
if (dstX + w > (int)fb->Width) w = (int)fb->Width - dstX;
|
|
if (srcY + h > (int)fb->Height) h = (int)fb->Height - srcY;
|
|
if (dstY + h > (int)fb->Height) h = (int)fb->Height - dstY;
|
|
if (w <= 0 || h <= 0)
|
|
return STATUS_SUCCESS;
|
|
|
|
fb->Stats.CopyRectCount++;
|
|
fb->Stats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t);
|
|
|
|
size_t rowBytes = (size_t)w * sizeof(uint32_t);
|
|
if (dstY > srcY) {
|
|
for (int row = h - 1; row >= 0; row--) {
|
|
uint32_t* dst = base + ((size_t)(dstY + row) * fb->Stride) + dstX;
|
|
uint32_t* src = base + ((size_t)(srcY + row) * fb->Stride) + srcX;
|
|
memmove(dst, src, rowBytes);
|
|
}
|
|
} else {
|
|
for (int row = 0; row < h; row++) {
|
|
uint32_t* dst = base + ((size_t)(dstY + row) * fb->Stride) + dstX;
|
|
uint32_t* src = base + ((size_t)(srcY + row) * fb->Stride) + srcX;
|
|
memmove(dst, src, rowBytes);
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !fb->Framebuffer || !outPixels || outStride == 0 || w <= 0 || h <= 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t* srcBase = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer;
|
|
DISPLAY_MODE mode = { fb->Width, fb->Height, fb->Stride, DisplayPixelFormatX8R8G8B8 };
|
|
DISPLAY_RECT r = { x, y, w, h };
|
|
if (!KiDisplayClipRect(&mode, &r))
|
|
return STATUS_SUCCESS;
|
|
|
|
fb->Stats.ReadRectCount++;
|
|
fb->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t);
|
|
|
|
int dstOffsetX = r.X - x;
|
|
int dstOffsetY = r.Y - y;
|
|
for (int row = 0; row < r.H; row++) {
|
|
const uint32_t* src = srcBase + ((size_t)(r.Y + row) * fb->Stride) + r.X;
|
|
uint32_t* dst = outPixels + ((size_t)(dstOffsetY + row) * outStride) + dstOffsetX;
|
|
memcpy(dst, src, (size_t)r.W * sizeof(uint32_t));
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, size_t rectCount) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !fb->Framebuffer)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (!fb->Backbuffer || !rects || rectCount == 0)
|
|
return STATUS_SUCCESS;
|
|
|
|
DISPLAY_MODE mode;
|
|
KSTATUS s = BootFbGetMode(device, &mode);
|
|
if (!KSUCCESS(s))
|
|
return s;
|
|
|
|
fb->Stats.PresentCount++;
|
|
fb->Stats.PresentRectCount += rectCount;
|
|
|
|
for (size_t i = 0; i < rectCount; i++) {
|
|
DISPLAY_RECT r = rects[i];
|
|
if (!KiDisplayClipRect(&mode, &r))
|
|
continue;
|
|
fb->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t);
|
|
size_t bytesPerRow = (size_t)r.W * sizeof(uint32_t);
|
|
for (int row = 0; row < r.H; row++) {
|
|
uint32_t* dst = fb->Framebuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X;
|
|
uint32_t* src = fb->Backbuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X;
|
|
memcpy(dst, src, bytesPerRow);
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbSync(DISPLAY_DEVICE* device) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (fb) fb->Stats.SyncCount++;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) {
|
|
(void)device;
|
|
(void)width;
|
|
(void)height;
|
|
(void)hotX;
|
|
(void)hotY;
|
|
(void)argbPixels;
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int x, int y) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (fb) fb->Stats.CursorMoveCount++;
|
|
(void)x;
|
|
(void)y;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static inline uint32_t KiDisplayBlendPixel(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 = (uint8_t)(255 - a);
|
|
uint8_t rr = (uint8_t)((a * sr + na * dr) >> 8);
|
|
uint8_t gg = (uint8_t)((a * sg + na * dg) >> 8);
|
|
uint8_t bb = (uint8_t)((a * sb + na * db) >> 8);
|
|
return ((uint32_t)rr << 16) | ((uint32_t)gg << 8) | bb;
|
|
}
|
|
|
|
static void BootFbRestoreCursor(BOOTFB_CONTEXT* fb) {
|
|
if (!fb || !fb->Framebuffer || !fb->Backbuffer || gCursorDrawX < 0)
|
|
return;
|
|
|
|
DISPLAY_RECT r = { gCursorDrawX, gCursorDrawY, DISPLAY_CURSOR_W, DISPLAY_CURSOR_H };
|
|
DISPLAY_MODE mode = { fb->Width, fb->Height, fb->Stride, DisplayPixelFormatX8R8G8B8 };
|
|
if (KiDisplayClipRect(&mode, &r)) {
|
|
for (int row = 0; row < r.H; row++) {
|
|
uint32_t* dst = fb->Framebuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X;
|
|
uint32_t* src = fb->Backbuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X;
|
|
memcpy(dst, src, (size_t)r.W * sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
gCursorDrawX = -1;
|
|
gCursorDrawY = -1;
|
|
}
|
|
|
|
static void BootFbDrawCursor(BOOTFB_CONTEXT* fb, int x, int y) {
|
|
if (!fb || !fb->Framebuffer || !fb->Backbuffer)
|
|
return;
|
|
|
|
gCursorDrawX = x;
|
|
gCursorDrawY = y;
|
|
|
|
for (int row = 0; row < DISPLAY_CURSOR_H; row++) {
|
|
int fy = y + row;
|
|
if ((unsigned)fy >= fb->Height)
|
|
continue;
|
|
for (int col = 0; col < DISPLAY_CURSOR_W; col++) {
|
|
int fx = x + col;
|
|
if ((unsigned)fx >= fb->Width)
|
|
continue;
|
|
uint32_t src = gDisplayDefaultCursorArgb[row][col];
|
|
if ((src >> 24) == 0)
|
|
continue;
|
|
uint32_t dst = fb->Backbuffer[(size_t)fy * fb->Stride + fx];
|
|
fb->Framebuffer[(size_t)fy * fb->Stride + fx] = KiDisplayBlendPixel(dst, src);
|
|
}
|
|
}
|
|
}
|
|
|
|
static KSTATUS BootFbShowCursor(DISPLAY_DEVICE* device, bool show) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !fb->Framebuffer || !fb->Backbuffer)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
if (show) {
|
|
fb->Stats.CursorShowCount++;
|
|
BootFbDrawCursor(fb, gCursorX, gCursorY);
|
|
} else {
|
|
fb->Stats.CursorHideCount++;
|
|
BootFbRestoreCursor(fb);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS BootFbQueryStats(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats) {
|
|
BOOTFB_CONTEXT* fb = BootFbContext(device);
|
|
if (!fb || !outStats)
|
|
return STATUS_INVALID_PARAMETER;
|
|
*outStats = fb->Stats;
|
|
return STATUS_SUCCESS;
|
|
}
|