Files
nt-lite/code/kernel/ke_display.c
T

2253 lines
78 KiB
C

// Kernel display manager. This file owns provider registration, framebuffer routing, dirty rectangles, text console state, and cursor delegation.
#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>
#include <kernel/ke_work.h>
#include <kernel/ke_mutex.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_irql.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
#define DISPLAY_CURSOR_POS_INVALID (-2147483647 - 1)
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 MUTEX* gDisplayProviderMutex;
static KWORK_ITEM gDisplayCursorWorkItem;
static volatile long gDisplayCursorWorkInitialized;
static bool gDisplayTransitionInProgress;
static uint64_t gDisplayGeneration;
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 bool gCursorUpdatePending;
static uint64_t gCursorRequestGeneration;
static uint64_t gCursorAppliedGeneration;
static int gCursorCommittedX;
static int gCursorCommittedY;
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 KiDisplayOutputSuspended(void);
static void KiDisplayCursorWorker(void* context);
static void KiDisplayDirtyAll(void);
static bool KiDisplayHasCurrentThread(void) {
return KeGetCurrentThread() != (THREAD*)0;
}
static bool KiDisplayProviderCallbacksRequirePassive(void) {
return KiDisplayHasCurrentThread() && KeIsWorkQueueStarted();
}
static KSTATUS KiDisplayAcquireProviderMutex(bool* acquired) {
if (acquired)
*acquired = false;
if (!gDisplayProviderMutex || !KiDisplayProviderCallbacksRequirePassive())
return STATUS_SUCCESS;
if (!KeCanWait())
return STATUS_INVALID_IRQL;
if (KeGetDebugFlags() & KE_DEBUG_DISPLAY_ASSERTS)
KeAssertIrql(PASSIVE_LEVEL);
KSTATUS s = KeAcquireMutex(gDisplayProviderMutex);
if (KSUCCESS(s) && acquired)
*acquired = true;
return s;
}
static void KiDisplayReleaseProviderMutex(bool acquired) {
if (acquired && gDisplayProviderMutex)
KeReleaseMutex(gDisplayProviderMutex);
}
// Writes one character through the boot HAL framebuffer path when the display
// manager cannot safely call provider callbacks, such as during a panic.
static KSTATUS KiDisplayFallbackPrintChar(char c, uint32_t fg, uint32_t bg) {
const HAL_API* hal = gDisplayHal;
if (hal && hal->Screen.PrintChar) {
hal->Screen.PrintChar(c, fg, bg);
return STATUS_SUCCESS;
}
return STATUS_INVALID_IRQL;
}
static void KiDisplayQueueCursorWork(void) {
if (!_InterlockedCompareExchange(&gDisplayCursorWorkInitialized, 1, 1))
return;
if (KeIsWorkQueueStarted()) {
(void)KeQueueWorkItem(&gDisplayCursorWorkItem);
return;
}
// During single-threaded early boot there is no worker thread yet. The
// boot framebuffer console still needs synchronous cursor setup; once the
// scheduler work queue exists all cursor commits are asynchronous.
if (!KiDisplayProviderCallbacksRequirePassive())
KiDisplayCursorWorker(0);
}
// Checks whether a primary display provider is available.
static bool KiDisplayHasDevice(void) {
return gPrimaryDisplay && gPrimaryDisplay->Ops;
}
// Checks whether the active provider expects the manager to draw the cursor into the framebuffer.
static bool KiDisplayUsesSoftwareCursor(void) {
return KiDisplayHasDevice() &&
(gPrimaryDisplay->Caps & DISPLAY_CAP_SOFTWARE_CURSOR) &&
gPrimaryDisplay->Ops->ShowCursor;
}
// Checks whether the active provider owns cursor presentation in hardware or device firmware.
static bool KiDisplayUsesHardwareCursor(void) {
return KiDisplayHasDevice() &&
(gPrimaryDisplay->Caps & DISPLAY_CAP_HARDWARE_CURSOR) &&
gPrimaryDisplay->Ops->ShowCursor;
}
// Checks whether the active provider exposes any cursor-control path.
static bool KiDisplayHasCursorOps(void) {
return KiDisplayHasDevice() &&
(gPrimaryDisplay->Caps & (DISPLAY_CAP_SOFTWARE_CURSOR | DISPLAY_CAP_HARDWARE_CURSOR)) &&
gPrimaryDisplay->Ops->ShowCursor;
}
static void KiDisplayCursorWorker(void* context) {
(void)context;
bool providerHeld = false;
if (!KSUCCESS(KiDisplayAcquireProviderMutex(&providerHeld)))
return;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
int x = 0;
int y = 0;
bool visible = false;
bool drawn = false;
bool suspended = false;
uint64_t displayGeneration = 0;
uint64_t requestGeneration = 0;
int committedX = 0;
int committedY = 0;
bool hadPendingWork = false;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
hadPendingWork = gCursorUpdatePending || (gCursorRequestGeneration != gCursorAppliedGeneration);
if (!hadPendingWork) {
KeReleaseSpinlock(&gDisplayLock, flags);
KiDisplayReleaseProviderMutex(providerHeld);
return;
}
gCursorUpdatePending = false;
if (KiDisplayHasDevice()) {
device = gPrimaryDisplay;
ops = gPrimaryDisplay->Ops;
caps = gPrimaryDisplay->Caps;
x = gCursorX;
y = gCursorY;
visible = gCursorVisible;
drawn = gCursorDrawn;
suspended = KiDisplayOutputSuspended() || gDisplayTransitionInProgress;
displayGeneration = gDisplayGeneration;
requestGeneration = gCursorRequestGeneration;
committedX = gCursorCommittedX;
committedY = gCursorCommittedY;
}
KeReleaseSpinlock(&gDisplayLock, flags);
bool newDrawn = false;
bool positionCommitted = false;
int newCommittedX = committedX;
int newCommittedY = committedY;
KSTATUS status = STATUS_SUCCESS;
bool hasCursorOps = device && ops && ops->ShowCursor &&
(caps & (DISPLAY_CAP_SOFTWARE_CURSOR | DISPLAY_CAP_HARDWARE_CURSOR));
bool softwareCursor = hasCursorOps && (caps & DISPLAY_CAP_SOFTWARE_CURSOR) != 0;
bool hardwareCursor = hasCursorOps && (caps & DISPLAY_CAP_HARDWARE_CURSOR) != 0;
if (hasCursorOps) {
if (!visible || suspended) {
if (drawn)
status = ops->ShowCursor(device, false);
newDrawn = false;
if (KSUCCESS(status)) {
positionCommitted = true;
newCommittedX = DISPLAY_CURSOR_POS_INVALID;
newCommittedY = DISPLAY_CURSOR_POS_INVALID;
}
} else if (hardwareCursor) {
// Hardware cursors are independent of framebuffer drawing. Commit
// position only when the logical cursor request changed or the
// provider has not yet been synchronized. This prevents stale or
// duplicate work items from hammering device cursor registers.
bool needPosition = !drawn || x != committedX || y != committedY;
if (needPosition && ops->SetCursorPosition) {
status = ops->SetCursorPosition(device, x, y);
if (KSUCCESS(status)) {
positionCommitted = true;
newCommittedX = x;
newCommittedY = y;
}
} else {
positionCommitted = true;
newCommittedX = committedX;
newCommittedY = committedY;
}
if (KSUCCESS(status)) {
if (!drawn)
status = ops->ShowCursor(device, true);
newDrawn = KSUCCESS(status);
}
} else if (softwareCursor) {
if (drawn)
(void)ops->ShowCursor(device, false);
if (ops->SetCursorPosition)
(void)ops->SetCursorPosition(device, x, y);
status = ops->ShowCursor(device, true);
newDrawn = KSUCCESS(status);
if (newDrawn) {
positionCommitted = true;
newCommittedX = x;
newCommittedY = y;
}
}
}
flags = KeAcquireSpinlock(&gDisplayLock);
if (displayGeneration == gDisplayGeneration && device == gPrimaryDisplay) {
gCursorDrawn = newDrawn;
if (positionCommitted) {
gCursorCommittedX = newCommittedX;
gCursorCommittedY = newCommittedY;
}
// Do not spin forever on provider failure. A later explicit cursor
// request, show/hide request, or display-generation change will retry.
gCursorAppliedGeneration = requestGeneration;
if (gCursorRequestGeneration != requestGeneration)
gCursorUpdatePending = true;
} else {
gCursorUpdatePending = true;
}
bool requeue = gCursorUpdatePending && !gDisplayTransitionInProgress;
KeReleaseSpinlock(&gDisplayLock, flags);
KiDisplayReleaseProviderMutex(providerHeld);
if (requeue)
KiDisplayQueueCursorWork();
}
// Clips a rectangle to a display mode so providers never see negative or out-of-bounds regions.
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;
}
// Snapshots the current primary under the manager spinlock. The caller must
// hold the provider mutex before invoking callbacks through the returned ops;
// KeDisplaySetPrimary takes the same mutex, so the provider object cannot be
// switched while the callback sequence is in progress.
static bool KiDisplaySnapshotPrimary(DISPLAY_DEVICE** outDevice,
const DISPLAY_DEVICE_OPS** outOps,
uint32_t* outCaps,
uint64_t* outGeneration) {
if (outDevice) *outDevice = (DISPLAY_DEVICE*)0;
if (outOps) *outOps = (const DISPLAY_DEVICE_OPS*)0;
if (outCaps) *outCaps = 0;
if (outGeneration) *outGeneration = 0;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
DISPLAY_DEVICE* device = KiDisplayHasDevice() ? gPrimaryDisplay : (DISPLAY_DEVICE*)0;
if (device) {
if (outDevice) *outDevice = device;
if (outOps) *outOps = device->Ops;
if (outCaps) *outCaps = device->Caps;
if (outGeneration) *outGeneration = gDisplayGeneration;
}
KeReleaseSpinlock(&gDisplayLock, flags);
return device != (DISPLAY_DEVICE*)0;
}
static bool KiDisplayConsoleGridFromMode(const DISPLAY_MODE* mode, int* outCols, int* outRows) {
if (!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 KiDisplayAddDirtyForModeLocked(const DISPLAY_MODE* mode, int x, int y, int w, int h) {
if (!mode || gDirtyAll || w <= 0 || h <= 0)
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 void KiDisplayRecordBlitLocked(const DISPLAY_MODE* mode, int x, int y, int w, int h) {
gDisplayStats.BlitCount++;
gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t);
KiDisplayAddDirtyForModeLocked(mode, x, y, w, h);
}
static void KiDisplayRecordFillLocked(const DISPLAY_MODE* mode, int x, int y, int w, int h) {
gDisplayStats.FillRectCount++;
gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t);
KiDisplayAddDirtyForModeLocked(mode, x, y, w, h);
}
static void KiDisplayRecordCopyLocked(const DISPLAY_MODE* mode, int dstX, int dstY, int w, int h) {
gDisplayStats.CopyRectCount++;
gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t);
KiDisplayAddDirtyForModeLocked(mode, dstX, dstY, w, h);
}
static KSTATUS KiDisplayFlushDirtyWithSnapshot(DISPLAY_DEVICE* device, const DISPLAY_DEVICE_OPS* ops) {
if (!device || !ops || !ops->Present)
return STATUS_SUCCESS;
DISPLAY_RECT localRects[DISPLAY_MAX_DIRTY_RECTS];
DISPLAY_RECT fullRect;
const DISPLAY_RECT* rects = localRects;
size_t count = 0;
bool dirtyAll = false;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device != gPrimaryDisplay || (!gDirtyAll && gDirtyRectCount == 0)) {
KeReleaseSpinlock(&gDisplayLock, flags);
return STATUS_SUCCESS;
}
dirtyAll = gDirtyAll;
if (!dirtyAll) {
count = gDirtyRectCount;
if (count > DISPLAY_MAX_DIRTY_RECTS)
count = DISPLAY_MAX_DIRTY_RECTS;
for (size_t i = 0; i < count; i++)
localRects[i] = gDirtyRects[i];
}
KeReleaseSpinlock(&gDisplayLock, flags);
if (dirtyAll) {
DISPLAY_MODE mode;
if (!ops->GetMode)
return STATUS_NOT_SUPPORTED;
KSTATUS ms = ops->GetMode(device, &mode);
if (!KSUCCESS(ms))
return ms;
fullRect.X = 0;
fullRect.Y = 0;
fullRect.W = (int32_t)mode.Width;
fullRect.H = (int32_t)mode.Height;
rects = &fullRect;
count = 1;
}
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
gDisplayStats.PresentCount++;
gDisplayStats.PresentRectCount += count;
}
KeReleaseSpinlock(&gDisplayLock, flags);
KSTATUS s = ops->Present(device, rects, count);
if (KSUCCESS(s)) {
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
gDirtyAll = false;
gDirtyRectCount = 0;
}
KeReleaseSpinlock(&gDisplayLock, flags);
}
return s;
}
static bool KiDisplayHideCursorForDrawSnapshot(DISPLAY_DEVICE* device,
const DISPLAY_DEVICE_OPS* ops,
uint32_t caps) {
if (!device || !ops || !ops->ShowCursor || !(caps & DISPLAY_CAP_SOFTWARE_CURSOR))
return false;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
bool hide = (device == gPrimaryDisplay) && gCursorDrawn;
if (hide)
gCursorDrawn = false;
KeReleaseSpinlock(&gDisplayLock, flags);
if (hide)
(void)ops->ShowCursor(device, false);
return hide;
}
static void KiDisplayRestoreCursorAfterDrawSnapshot(DISPLAY_DEVICE* device,
const DISPLAY_DEVICE_OPS* ops,
uint32_t caps) {
// Only software cursors are hidden/restored around framebuffer drawing.
// Hardware cursors are owned by the provider/device and must not be
// re-positioned after every blit/present; the coalesced cursor worker is
// the only path that commits hardware cursor movement.
if (!device || !ops || !ops->ShowCursor || !(caps & DISPLAY_CAP_SOFTWARE_CURSOR))
return;
int x = 0;
int y = 0;
bool shouldShow = false;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
shouldShow = (device == gPrimaryDisplay) &&
gCursorVisible && !gCursorDrawn &&
!KiDisplayOutputSuspended() &&
!gDisplayTransitionInProgress;
if (shouldShow) {
x = gCursorX;
y = gCursorY;
}
KeReleaseSpinlock(&gDisplayLock, flags);
if (!shouldShow)
return;
if (ops->SetCursorPosition)
(void)ops->SetCursorPosition(device, x, y);
KSTATUS s = ops->ShowCursor(device, true);
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay && KSUCCESS(s)) {
gCursorDrawn = true;
gCursorCommittedX = x;
gCursorCommittedY = y;
gCursorAppliedGeneration = gCursorRequestGeneration;
gCursorUpdatePending = false;
} else if (device != gPrimaryDisplay) {
gCursorUpdatePending = true;
}
bool requeue = gCursorUpdatePending && !gDisplayTransitionInProgress;
KeReleaseSpinlock(&gDisplayLock, flags);
if (requeue)
KiDisplayQueueCursorWork();
}
static KSTATUS KiDisplayGetProviderSnapshot(DISPLAY_DEVICE** outDevice,
const DISPLAY_DEVICE_OPS** outOps,
uint32_t* outCaps,
DISPLAY_MODE* outMode) {
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
if (!KiDisplaySnapshotPrimary(&device, &ops, &caps, 0) || !device || !ops)
return STATUS_NOT_SUPPORTED;
if (!ops->GetMode)
return STATUS_NOT_SUPPORTED;
KSTATUS s = ops->GetMode(device, outMode);
if (!KSUCCESS(s))
return s;
if (outDevice) *outDevice = device;
if (outOps) *outOps = ops;
if (outCaps) *outCaps = caps;
return STATUS_SUCCESS;
}
static void KiDisplayBuildGlyphPixels(char c, uint32_t fg, uint32_t bg, uint32_t* outPixels) {
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++)
outPixels[row * FONT_W + col] = (bits & (0x80u >> col)) ? fg : bg;
}
}
static KSTATUS KiDisplayBlitGlyphSnapshot(DISPLAY_DEVICE* device,
const DISPLAY_DEVICE_OPS* ops,
const DISPLAY_MODE* mode,
char c,
int x,
int y,
uint32_t fg,
uint32_t bg) {
if (!device || !ops || !ops->Blit || !mode)
return STATUS_NOT_SUPPORTED;
uint32_t glyphPixels[FONT_W * FONT_H];
KiDisplayBuildGlyphPixels(c, fg, bg, glyphPixels);
KSTATUS s = ops->Blit(device, x, y, FONT_W, FONT_H, glyphPixels);
if (KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay)
KiDisplayRecordBlitLocked(mode, x, y, FONT_W, FONT_H);
KeReleaseSpinlock(&gDisplayLock, flags);
}
return s;
}
static KSTATUS KiDisplayDrawBlitSnapshot(DISPLAY_DEVICE* device,
const DISPLAY_DEVICE_OPS* ops,
const DISPLAY_MODE* mode,
int x,
int y,
int w,
int h,
const uint32_t* pixels) {
if (!pixels || w <= 0 || h <= 0)
return STATUS_INVALID_PARAMETER;
if (!device || !ops || !ops->Blit || !mode)
return STATUS_NOT_SUPPORTED;
KSTATUS s = ops->Blit(device, x, y, w, h, pixels);
if (KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay)
KiDisplayRecordBlitLocked(mode, x, y, w, h);
KeReleaseSpinlock(&gDisplayLock, flags);
}
return s;
}
static KSTATUS KiDisplayFillRectSnapshot(DISPLAY_DEVICE* device,
const DISPLAY_DEVICE_OPS* ops,
const DISPLAY_MODE* mode,
int x,
int y,
int w,
int h,
uint32_t color) {
if (w <= 0 || h <= 0)
return STATUS_INVALID_PARAMETER;
if (!device || !ops || !ops->FillRect || !mode)
return STATUS_NOT_SUPPORTED;
KSTATUS s = ops->FillRect(device, x, y, w, h, color);
if (KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay)
KiDisplayRecordFillLocked(mode, x, y, w, h);
KeReleaseSpinlock(&gDisplayLock, flags);
}
return s;
}
static KSTATUS KiDisplayCopyRectSnapshot(DISPLAY_DEVICE* device,
const DISPLAY_DEVICE_OPS* ops,
const DISPLAY_MODE* mode,
int dstX,
int dstY,
int srcX,
int srcY,
int w,
int h) {
if (w <= 0 || h <= 0)
return STATUS_INVALID_PARAMETER;
if (!device || !ops || !ops->CopyRect || !mode)
return STATUS_NOT_SUPPORTED;
KSTATUS s = ops->CopyRect(device, dstX, dstY, srcX, srcY, w, h);
if (KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay)
KiDisplayRecordCopyLocked(mode, dstX, dstY, w, h);
KeReleaseSpinlock(&gDisplayLock, flags);
}
return s;
}
static KSTATUS KiDisplayFinishProviderDraw(DISPLAY_DEVICE* device,
const DISPLAY_DEVICE_OPS* ops,
uint32_t caps,
KSTATUS drawStatus) {
KSTATUS presentStatus = STATUS_SUCCESS;
if (KSUCCESS(drawStatus))
presentStatus = KiDisplayFlushDirtyWithSnapshot(device, ops);
KiDisplayRestoreCursorAfterDrawSnapshot(device, ops, caps);
return KSUCCESS(drawStatus) ? presentStatus : drawStatus;
}
// Implements the display Dirty All operation.
static void KiDisplayDirtyAll(void) {
gDisplayStats.DirtyAllCount++;
gDirtyAll = true;
gDirtyRectCount = 0;
}
// Implements the display Add Dirty operation.
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;
}
// Implements the display Flush Dirty operation.
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;
}
// Implements the display Hide Cursor For Draw operation.
static void KiDisplayHideCursorForDraw(void) {
if (!gCursorDrawn || !KiDisplayUsesSoftwareCursor())
return;
(void)gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, false);
gCursorDrawn = false;
}
// Implements the display Restore Cursor After Draw operation.
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;
}
// Implements the display Begin Draw operation.
static void KiDisplayBeginDraw(void) {
if (gDrawDepth++ == 0)
KiDisplayHideCursorForDraw();
}
// Implements the display End Draw operation.
static KSTATUS KiDisplayEndDraw(void) {
KSTATUS s = STATUS_SUCCESS;
if (gDrawDepth > 0)
gDrawDepth--;
if (gDrawDepth == 0) {
s = KiDisplayFlushDirty();
KiDisplayRestoreCursorAfterDraw();
}
return s;
}
// Implements the display Draw Blit No Cursor operation.
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;
}
// Implements the display Fill Rect No Cursor operation.
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;
}
// Implements the display Copy Rect No Cursor operation.
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;
}
// Implements the display Console Index operation.
static size_t KiDisplayConsoleIndex(int col, int row) {
return (size_t)row * DISPLAY_CONSOLE_MAX_COLS + (size_t)col;
}
// Implements the display Console Color operation.
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];
}
// Implements the display Console Fg operation.
static uint32_t KiDisplayConsoleFg(uint16_t attr) {
return KiDisplayConsoleColor((uint8_t)(attr & 0x0Fu));
}
// Implements the display Console Bg operation.
static uint32_t KiDisplayConsoleBg(uint16_t attr) {
return KiDisplayConsoleColor((uint8_t)((attr >> 4) & 0x0Fu));
}
// Implements the display Get Console Grid Locked operation.
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;
}
// Implements the display Console Reset Cells Locked operation.
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;
}
// Implements the display Console Scroll Cells Locked operation.
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;
}
}
}
// Implements the display Draw Char Pixels Locked operation.
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);
}
// Implements the display Draw Console Cell Locked operation.
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));
}
// Implements the display Serial Write Char operation.
static void KiDisplaySerialWriteChar(char c) {
while ((__inbyte(0x3FD) & 0x20) == 0) { }
if (c == '\n')
__outbyte(0x3F8, '\r');
__outbyte(0x3F8, (uint8_t)c);
}
// Implements the display Initialize operation.
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;
gCursorUpdatePending = false;
gCursorRequestGeneration = 0;
gCursorAppliedGeneration = 0;
gCursorCommittedX = DISPLAY_CURSOR_POS_INVALID;
gCursorCommittedY = DISPLAY_CURSOR_POS_INVALID;
gDisplayTransitionInProgress = false;
gDisplayGeneration = 1;
if (!gDisplayProviderMutex)
(void)KeMutexCreate(&gDisplayProviderMutex);
KeInitializeWorkItem(&gDisplayCursorWorkItem, KiDisplayCursorWorker, 0);
_InterlockedExchange(&gDisplayCursorWorkInitialized, 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;
}
// Implements the display Seed New Primary operation.
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);
}
// Implements the display Output Suspended operation.
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 : "?";
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 oldCursorDrawn = gCursorDrawn;
gDisplayTransitionInProgress = true;
gDisplayGeneration++;
gCursorUpdatePending = gCursorVisible;
KeReleaseSpinlock(&gDisplayLock, flags);
bool providerHeld = false;
KSTATUS result = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(result)) {
flags = KeAcquireSpinlock(&gDisplayLock);
gDisplayTransitionInProgress = false;
gDisplayGeneration++;
KeReleaseSpinlock(&gDisplayLock, flags);
KiDisplayQueueCursorWork();
return result;
}
if (oldDevice && oldCursorDrawn && oldDevice->Ops && oldDevice->Ops->ShowCursor)
(void)oldDevice->Ops->ShowCursor(oldDevice, false);
// Clean architecture policy: handoff does not read back/copy the old boot
// framebuffer. The new primary is cleared to the kernel console blue and
// user/session code redraws state above the provider boundary.
if (device->Ops->GetMode && device->Ops->FillRect) {
DISPLAY_MODE mode;
KSTATUS modeStatus = device->Ops->GetMode(device, &mode);
if (KSUCCESS(modeStatus)) {
(void)device->Ops->FillRect(device, 0, 0,
(int)mode.Width,
(int)mode.Height,
DISPLAY_DEFAULT_BG);
if (device->Ops->Present) {
DISPLAY_RECT full = { 0, 0, (int32_t)mode.Width, (int32_t)mode.Height };
(void)device->Ops->Present(device, &full, 1);
}
}
}
flags = KeAcquireSpinlock(&gDisplayLock);
gPrimaryDisplay = device;
gCursorDrawn = false;
gCursorCommittedX = DISPLAY_CURSOR_POS_INVALID;
gCursorCommittedY = DISPLAY_CURSOR_POS_INVALID;
gDirtyAll = false;
gDirtyRectCount = 0;
gDrawDepth = 0;
// The provider handoff clears the framebuffer instead of preserving boot
// text. Keep the console model coherent with that clear so subsequent
// kernel/debug output restarts at the top-left of the new primary.
KiDisplayConsoleResetCellsLocked();
gConsoleX = 0;
gConsoleY = 0;
gDisplayTransitionInProgress = false;
gDisplayGeneration++;
bool needCursor = gCursorVisible;
gCursorUpdatePending = needCursor;
if (needCursor)
gCursorRequestGeneration++;
KeReleaseSpinlock(&gDisplayLock, flags);
KiDisplayReleaseProviderMutex(providerHeld);
if (needCursor)
KiDisplayQueueCursorWork();
DbgPrint("Display: primary is %s\n", name);
return result;
}
DISPLAY_API KSTATUS KeDisplayGetMode(DISPLAY_MODE* outMode) {
if (!outMode)
return STATUS_INVALID_PARAMETER;
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
DISPLAY_DEVICE* device = KiDisplayHasDevice() ? gPrimaryDisplay : (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = device ? device->Ops : (const DISPLAY_DEVICE_OPS*)0;
KeReleaseSpinlock(&gDisplayLock, flags);
s = (!device || !ops || !ops->GetMode)
? STATUS_INVALID_PARAMETER
: ops->GetMode(device, outMode);
KiDisplayReleaseProviderMutex(providerHeld);
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 KeDisplaySync(void) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
DISPLAY_DEVICE* device = KiDisplayHasDevice() ? gPrimaryDisplay : (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = device ? device->Ops : (const DISPLAY_DEVICE_OPS*)0;
KeReleaseSpinlock(&gDisplayLock, flags);
if (!device || !ops || !ops->Sync) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
s = ops->Sync(device);
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay)
gDisplayStats.SyncCount++;
KeReleaseSpinlock(&gDisplayLock, flags);
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayQueryInfo(KE_DISPLAY_INFO* outInfo) {
if (!outInfo)
return STATUS_INVALID_PARAMETER;
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
KE_DISPLAY_INFO info;
memset(&info, 0, sizeof(info));
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
uint32_t count = 0;
for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next)
count++;
info.DeviceCount = count;
info.ManagerStats = gDisplayStats;
DISPLAY_DEVICE* device = KiDisplayHasDevice() ? gPrimaryDisplay : (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = device ? device->Ops : (const DISPLAY_DEVICE_OPS*)0;
if (device) {
const char* name = device->Name ? device->Name : "?";
size_t len = strlen(name);
if (len >= sizeof(info.PrimaryName))
len = sizeof(info.PrimaryName) - 1;
memcpy(info.PrimaryName, name, len);
info.PrimaryName[len] = '\0';
info.Caps = device->Caps;
}
KeReleaseSpinlock(&gDisplayLock, flags);
if (!device || !ops) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_FOUND;
}
if (ops->GetMode) {
s = ops->GetMode(device, &info.Mode);
if (!KSUCCESS(s)) {
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
}
if (ops->QueryStats)
(void)ops->QueryStats(device, &info.DeviceStats);
*outInfo = info;
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_SUCCESS;
}
DISPLAY_API KSTATUS KeDisplayBlit(int x, int y, int w, int h, const uint32_t* pixels) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (KSUCCESS(s)) {
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
s = KiDisplayDrawBlitSnapshot(device, ops, &mode, x, y, w, h, pixels);
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
}
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayBlitRows(int x, int y, int w, int h, KE_DISPLAY_ROW_SOURCE source, void* context, uint32_t* scratch) {
if (!source || !scratch || w <= 0 || h <= 0)
return STATUS_INVALID_PARAMETER;
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (KSUCCESS(s)) {
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
for (int row = 0; row < h; ++row) {
s = source(context, row, scratch, (size_t)w);
if (!KSUCCESS(s))
break;
s = KiDisplayDrawBlitSnapshot(device, ops, &mode, x, y + row, w, 1, scratch);
if (!KSUCCESS(s))
break;
}
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
}
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayFillRect(int x, int y, int w, int h, uint32_t color) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (KSUCCESS(s)) {
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
s = KiDisplayFillRectSnapshot(device, ops, &mode, x, y, w, h, color);
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
}
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayClear(uint32_t color) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (KSUCCESS(s)) {
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
s = KiDisplayFillRectSnapshot(device, ops, &mode, 0, 0, (int)mode.Width, (int)mode.Height, color);
if (KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
KiDisplayConsoleResetCellsLocked();
gConsoleX = 0;
gConsoleY = 0;
}
KeReleaseSpinlock(&gDisplayLock, flags);
}
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
}
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayCopyRect(int dstX, int dstY, int srcX, int srcY, int w, int h) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (KSUCCESS(s)) {
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
s = KiDisplayCopyRectSnapshot(device, ops, &mode, dstX, dstY, srcX, srcY, w, h);
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
}
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayScroll(int lines) {
if (lines <= 0)
return STATUS_SUCCESS;
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (KSUCCESS(s)) {
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
if ((uint32_t)lines >= mode.Height) {
s = KiDisplayFillRectSnapshot(device, ops, &mode, 0, 0, (int)mode.Width, (int)mode.Height, DISPLAY_DEFAULT_BG);
if (KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
KiDisplayConsoleResetCellsLocked();
gConsoleX = 0;
gConsoleY = 0;
}
KeReleaseSpinlock(&gDisplayLock, flags);
}
} else {
s = KiDisplayCopyRectSnapshot(device, ops, &mode, 0, 0, 0, lines,
(int)mode.Width, (int)(mode.Height - (uint32_t)lines));
if (KSUCCESS(s)) {
s = KiDisplayFillRectSnapshot(device, ops, &mode, 0,
(int)(mode.Height - (uint32_t)lines),
(int)mode.Width, lines, DISPLAY_DEFAULT_BG);
}
}
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
}
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (KSUCCESS(s)) {
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
s = KiDisplayBlitGlyphSnapshot(device, ops, &mode, c, x, y, fg, bg);
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
}
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
static KSTATUS KiDisplayPrintCharInternal(char c, uint32_t fg, uint32_t bg, bool mirrorSerial) {
if (mirrorSerial)
KiDisplaySerialWriteChar(c);
if (KiDisplayOutputSuspended()) {
if (!KeCanWait())
return KiDisplayFallbackPrintChar(c, fg, bg);
return STATUS_SUCCESS;
}
// Once the scheduler/work-queue era begins, display provider callbacks are
// PASSIVE_LEVEL-only. DbgPrint queues screen output through the work queue;
// panic and explicit console paths may still use the serial mirroring entry.
// A panic has interrupts disabled and cannot take the provider mutex, so it
// falls back to the boot framebuffer writer instead of disappearing from
// the screen while still printing to COM1.
if (KiDisplayProviderCallbacksRequirePassive() && !KeCanWait())
return KiDisplayFallbackPrintChar(c, fg, bg);
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (!KSUCCESS(s)) {
KiDisplayReleaseProviderMutex(providerHeld);
const HAL_API* hal = gDisplayHal;
if (hal && hal->Screen.PrintChar)
hal->Screen.PrintChar(c, fg, bg);
return s;
}
int cols = 0;
int rows = 0;
if (!KiDisplayConsoleGridFromMode(&mode, &cols, &rows)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_DEVICE_ERROR;
}
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
bool drawChar = false;
bool needScroll = false;
int drawCol = 0;
int drawRow = 0;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
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 {
drawChar = true;
drawCol = gConsoleX;
drawRow = gConsoleY;
if (++gConsoleX >= cols) {
gConsoleX = 0;
gConsoleY++;
}
}
if (gConsoleY >= rows)
needScroll = true;
KeReleaseSpinlock(&gDisplayLock, flags);
if (drawChar) {
s = KiDisplayBlitGlyphSnapshot(device, ops, &mode, c, drawCol * FONT_W, drawRow * FONT_H, fg, bg);
if (KSUCCESS(s)) {
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay && drawCol >= 0 && drawCol < cols && drawRow >= 0 && drawRow < rows) {
size_t i = KiDisplayConsoleIndex(drawCol, drawRow);
gConsoleCells[i] = c;
gConsoleAttrs[i] = (uint8_t)gConsoleAttribute;
}
KeReleaseSpinlock(&gDisplayLock, flags);
}
} else {
s = STATUS_SUCCESS;
}
if (KSUCCESS(s) && needScroll) {
s = KiDisplayCopyRectSnapshot(device, ops, &mode, 0, 0, 0, FONT_H,
(int)mode.Width, (int)(mode.Height - FONT_H));
if (KSUCCESS(s)) {
s = KiDisplayFillRectSnapshot(device, ops, &mode, 0,
(int)(mode.Height - FONT_H),
(int)mode.Width, FONT_H, DISPLAY_DEFAULT_BG);
}
if (KSUCCESS(s)) {
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
KiDisplayConsoleScrollCellsLocked(cols, rows, 1);
gConsoleY = rows - 1;
if (gConsoleX < 0) gConsoleX = 0;
if (gConsoleX >= cols) gConsoleX = cols - 1;
}
KeReleaseSpinlock(&gDisplayLock, flags);
}
}
s = KiDisplayFinishProviderDraw(device, ops, caps, s);
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplayPrintChar(char c, uint32_t fg, uint32_t bg) {
return KiDisplayPrintCharInternal(c, fg, bg, true);
}
// Implements the display Debug Print Char operation.
void KeDisplayDebugPrintChar(char c, uint32_t fg, uint32_t bg) {
(void)KiDisplayPrintCharInternal(c, fg, bg, false);
}
DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo) {
if (!outInfo)
return STATUS_INVALID_PARAMETER;
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
(void)caps;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (!KSUCCESS(s)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
int cols = 0;
int rows = 0;
if (!KiDisplayConsoleGridFromMode(&mode, &cols, &rows)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
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);
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_SUCCESS;
}
DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
(void)caps;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (!KSUCCESS(s)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
int cols = 0;
int rows = 0;
if (!KiDisplayConsoleGridFromMode(&mode, &cols, &rows)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
if (col < 0 || row < 0 || col >= cols || row >= rows) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_INVALID_PARAMETER;
}
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
gConsoleX = col;
gConsoleY = row;
KeReleaseSpinlock(&gDisplayLock, flags);
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_SUCCESS;
}
DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t length, size_t* writtenOut) {
if (writtenOut) *writtenOut = 0;
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (!KSUCCESS(s)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
int cols = 0;
int rows = 0;
if (!KiDisplayConsoleGridFromMode(&mode, &cols, &rows)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
if (col < 0 || row < 0 || col >= cols || row >= rows) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_INVALID_PARAMETER;
}
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
size_t written = 0;
int x = col;
int y = row;
while (written < length && y < rows) {
uint16_t attr = DISPLAY_CONSOLE_DEFAULT_ATTR;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
size_t i = KiDisplayConsoleIndex(x, y);
attr = gConsoleAttrs[i];
}
KeReleaseSpinlock(&gDisplayLock, flags);
s = KiDisplayBlitGlyphSnapshot(device, ops, &mode, c, x * FONT_W, y * FONT_H,
KiDisplayConsoleFg(attr), KiDisplayConsoleBg(attr));
if (!KSUCCESS(s))
break;
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
size_t i = KiDisplayConsoleIndex(x, y);
gConsoleCells[i] = c;
gConsoleAttrs[i] = (uint8_t)attr;
}
KeReleaseSpinlock(&gDisplayLock, flags);
written++;
if (++x >= cols) {
x = 0;
y++;
}
}
KSTATUS fs = KiDisplayFinishProviderDraw(device, ops, caps, s);
KiDisplayReleaseProviderMutex(providerHeld);
if (writtenOut) *writtenOut = written;
return KSUCCESS(s) ? fs : s;
}
DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, size_t length, size_t* writtenOut) {
if (writtenOut) *writtenOut = 0;
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (!KSUCCESS(s)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
int cols = 0;
int rows = 0;
if (!KiDisplayConsoleGridFromMode(&mode, &cols, &rows)) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
if (col < 0 || row < 0 || col >= cols || row >= rows) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_INVALID_PARAMETER;
}
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
size_t written = 0;
int x = col;
int y = row;
while (written < length && y < rows) {
char ch = ' ';
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
size_t i = KiDisplayConsoleIndex(x, y);
ch = gConsoleCells[i];
}
KeReleaseSpinlock(&gDisplayLock, flags);
s = KiDisplayBlitGlyphSnapshot(device, ops, &mode, ch, x * FONT_W, y * FONT_H,
KiDisplayConsoleFg(attr), KiDisplayConsoleBg(attr));
if (!KSUCCESS(s))
break;
flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
size_t i = KiDisplayConsoleIndex(x, y);
gConsoleCells[i] = ch;
gConsoleAttrs[i] = (uint8_t)attr;
}
KeReleaseSpinlock(&gDisplayLock, flags);
written++;
if (++x >= cols) {
x = 0;
y++;
}
}
KSTATUS fs = KiDisplayFinishProviderDraw(device, ops, caps, s);
KiDisplayReleaseProviderMutex(providerHeld);
if (writtenOut) *writtenOut = written;
return KSUCCESS(s) ? fs : s;
}
DISPLAY_API KSTATUS KeDisplayPresent(const DISPLAY_RECT* rects, size_t rectCount) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0;
uint32_t caps = 0;
DISPLAY_MODE mode;
s = KiDisplayGetProviderSnapshot(&device, &ops, &caps, &mode);
if (!KSUCCESS(s)) {
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
if (!ops->Present) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
(void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps);
if (!rects || rectCount == 0) {
s = KiDisplayFlushDirtyWithSnapshot(device, ops);
} else {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
if (device == gPrimaryDisplay) {
gDisplayStats.PresentCount++;
gDisplayStats.PresentRectCount += rectCount;
}
KeReleaseSpinlock(&gDisplayLock, flags);
s = ops->Present(device, rects, rectCount);
}
KiDisplayRestoreCursorAfterDrawSnapshot(device, ops, caps);
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplaySetCursorImage(int width, int height, int hotX, int hotY, const uint32_t* argbPixels) {
bool providerHeld = false;
KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld);
if (!KSUCCESS(s))
return s;
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
DISPLAY_DEVICE* device = KiDisplayHasDevice() ? gPrimaryDisplay : (DISPLAY_DEVICE*)0;
const DISPLAY_DEVICE_OPS* ops = device ? device->Ops : (const DISPLAY_DEVICE_OPS*)0;
KeReleaseSpinlock(&gDisplayLock, flags);
if (!device || !ops || !ops->SetCursorImage) {
KiDisplayReleaseProviderMutex(providerHeld);
return STATUS_NOT_SUPPORTED;
}
s = ops->SetCursorImage(device, width, height, hotX, hotY, argbPixels);
KiDisplayReleaseProviderMutex(providerHeld);
return s;
}
DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int x, int y) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
gCursorX = x;
gCursorY = y;
gDisplayStats.CursorMoveCount++;
bool shouldQueue = !KiDisplayOutputSuspended() &&
gCursorVisible &&
KiDisplayHasCursorOps();
if (shouldQueue) {
gCursorUpdatePending = true;
gCursorRequestGeneration++;
}
KeReleaseSpinlock(&gDisplayLock, flags);
if (shouldQueue)
KiDisplayQueueCursorWork();
return STATUS_SUCCESS;
}
DISPLAY_API KSTATUS KeDisplayShowCursor(bool show) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
gCursorVisible = show;
if (show) gDisplayStats.CursorShowCount++; else gDisplayStats.CursorHideCount++;
bool shouldQueue = !KiDisplayOutputSuspended() && KiDisplayHasCursorOps();
if (!shouldQueue && !show) {
gCursorDrawn = false;
gCursorCommittedX = DISPLAY_CURSOR_POS_INVALID;
gCursorCommittedY = DISPLAY_CURSOR_POS_INVALID;
}
if (shouldQueue) {
gCursorUpdatePending = true;
gCursorRequestGeneration++;
}
KeReleaseSpinlock(&gDisplayLock, flags);
if (shouldQueue)
KiDisplayQueueCursorWork();
return STATUS_SUCCESS;
}
// Implements the boot Fb Context operation.
static BOOTFB_CONTEXT* BootFbContext(DISPLAY_DEVICE* device) {
return device ? (BOOTFB_CONTEXT*)device->DriverContext : NULL;
}
// Implements the boot Fb Get Mode operation.
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;
}
// Implements the boot Fb Blit operation.
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;
}
// Implements the boot Fb Fill Rect operation.
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;
}
// Implements the boot Fb Copy Rect operation.
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;
}
// Implements the boot Fb Read Rect operation.
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;
}
// Implements the boot Fb Present operation.
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;
}
// Implements the boot Fb Sync operation.
static KSTATUS BootFbSync(DISPLAY_DEVICE* device) {
BOOTFB_CONTEXT* fb = BootFbContext(device);
if (fb) fb->Stats.SyncCount++;
return STATUS_SUCCESS;
}
// Implements the boot Fb Set Cursor Image operation.
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;
}
// Implements the boot Fb Set Cursor Position operation.
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;
}
// Implements the display Blend Pixel operation.
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;
}
// Implements the boot Fb Restore Cursor operation.
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;
gCursorUpdatePending = false;
}
// Implements the boot Fb Draw Cursor operation.
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);
}
}
}
// Implements the boot Fb Show Cursor operation.
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;
}
// Implements the boot Fb Query Stats operation.
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;
}