feat: Mouse cursor

This commit is contained in:
2026-02-17 12:45:04 +01:00
parent 641ec4681b
commit a3859dc3c4
3 changed files with 189 additions and 1 deletions
+165
View File
@@ -0,0 +1,165 @@
/*
* Cursor.cpp
* Simple framebuffer mouse cursor with background save/restore
* Copyright (c) 2025 Daniel Hammer
*/
#include "Cursor.hpp"
#include <Drivers/PS2/Mouse.hpp>
#include <Terminal/Terminal.hpp>
namespace Graphics::Cursor {
// Classic arrow cursor bitmap (12x16 pixels)
// Each row is a pair: mask (where cursor has pixels) and fill (white interior)
// Bit 0 = leftmost pixel in the row
static constexpr int CursorWidth = 12;
static constexpr int CursorHeight = 16;
// 1 = cursor pixel present
static constexpr uint16_t CursorMask[CursorHeight] = {
0b000000000001, // X
0b000000000011, // XX
0b000000000111, // XXX
0b000000001111, // XXXX
0b000000011111, // XXXXX
0b000000111111, // XXXXXX
0b000001111111, // XXXXXXX
0b000011111111, // XXXXXXXX
0b000111111111, // XXXXXXXXX
0b001111111111, // XXXXXXXXXX
0b011111111111, // XXXXXXXXXXX
0b111111111111, // XXXXXXXXXXXX
0b000001111111, // XXXXXXX
0b000011001111, // XXXX XX
0b000110000111, // XXX XX
0b000100000011, // XX X
};
// 1 = white fill (interior), 0 = black outline (where mask is set)
static constexpr uint16_t CursorFill[CursorHeight] = {
0b000000000000, //
0b000000000010, // W
0b000000000110, // WW
0b000000001110, // WWW
0b000000011110, // WWWW
0b000000111110, // WWWWW
0b000001111110, // WWWWWW
0b000011111110, // WWWWWWW
0b000111111110, // WWWWWWWW
0b001111111110, // WWWWWWWWW
0b000001111110, // WWWWWW
0b000011011110, // WWWW WW
0b000000001110, // WWW
0b000010000110, // WW W
0b000100000010, // W W
0b000000000000, //
};
static constexpr uint32_t ColorBlack = 0x00000000;
static constexpr uint32_t ColorWhite = 0x00FFFFFF;
// Framebuffer state
static uint32_t* g_FbBase = nullptr;
static uint64_t g_FbWidth = 0;
static uint64_t g_FbHeight = 0;
static uint64_t g_FbPitch = 0; // in bytes
// Saved background under the cursor
static uint32_t g_SavedBg[CursorWidth * CursorHeight];
static int32_t g_OldX = -1;
static int32_t g_OldY = -1;
static inline uint32_t* PixelAt(int32_t x, int32_t y) {
return reinterpret_cast<uint32_t*>(
reinterpret_cast<uint8_t*>(g_FbBase) + y * g_FbPitch + x * 4
);
}
static void SaveBackground(int32_t cx, int32_t cy) {
for (int row = 0; row < CursorHeight; ++row) {
int32_t py = cy + row;
if (py < 0 || py >= (int32_t)g_FbHeight) {
for (int col = 0; col < CursorWidth; ++col)
g_SavedBg[row * CursorWidth + col] = 0;
continue;
}
for (int col = 0; col < CursorWidth; ++col) {
int32_t px = cx + col;
if (px < 0 || px >= (int32_t)g_FbWidth) {
g_SavedBg[row * CursorWidth + col] = 0;
} else {
g_SavedBg[row * CursorWidth + col] = *PixelAt(px, py);
}
}
}
}
static void RestoreBackground(int32_t cx, int32_t cy) {
for (int row = 0; row < CursorHeight; ++row) {
int32_t py = cy + row;
if (py < 0 || py >= (int32_t)g_FbHeight) continue;
for (int col = 0; col < CursorWidth; ++col) {
int32_t px = cx + col;
if (px < 0 || px >= (int32_t)g_FbWidth) continue;
*PixelAt(px, py) = g_SavedBg[row * CursorWidth + col];
}
}
}
static void DrawCursor(int32_t cx, int32_t cy) {
for (int row = 0; row < CursorHeight; ++row) {
int32_t py = cy + row;
if (py < 0 || py >= (int32_t)g_FbHeight) continue;
uint16_t mask = CursorMask[row];
uint16_t fill = CursorFill[row];
for (int col = 0; col < CursorWidth; ++col) {
if (!(mask & (1 << col))) continue;
int32_t px = cx + col;
if (px < 0 || px >= (int32_t)g_FbWidth) continue;
uint32_t color = (fill & (1 << col)) ? ColorWhite : ColorBlack;
*PixelAt(px, py) = color;
}
}
}
void Initialize(limine_framebuffer* framebuffer) {
g_FbBase = reinterpret_cast<uint32_t*>(framebuffer->address);
g_FbWidth = framebuffer->width;
g_FbHeight = framebuffer->height;
g_FbPitch = framebuffer->pitch;
Drivers::PS2::Mouse::SetBounds(
static_cast<int32_t>(g_FbWidth - 1),
static_cast<int32_t>(g_FbHeight - 1)
);
g_OldX = -1;
g_OldY = -1;
Kt::KernelLogStream(Kt::OK, "Graphics/Cursor") << "Cursor initialized ("
<< (uint64_t)g_FbWidth << "x" << (uint64_t)g_FbHeight << ")";
}
void Update() {
auto state = Drivers::PS2::Mouse::GetMouseState();
int32_t newX = state.X;
int32_t newY = state.Y;
// Only redraw if the position changed
if (newX == g_OldX && newY == g_OldY) return;
// Restore the old background
if (g_OldX >= 0 && g_OldY >= 0) {
RestoreBackground(g_OldX, g_OldY);
}
// Save new background, draw cursor
SaveBackground(newX, newY);
DrawCursor(newX, newY);
g_OldX = newX;
g_OldY = newY;
}
};
+16
View File
@@ -0,0 +1,16 @@
/*
* Cursor.hpp
* Simple framebuffer mouse cursor
* Copyright (c) 2025 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <limine.h>
namespace Graphics::Cursor {
void Initialize(limine_framebuffer* framebuffer);
void Update();
};
+8 -1
View File
@@ -32,6 +32,7 @@
#include <Drivers/PS2/Keyboard.hpp>
#include <Drivers/PS2/Mouse.hpp>
#include <CppLib/BoxUI.hpp>
#include <Graphics/Cursor.hpp>
using namespace Kt;
@@ -130,5 +131,11 @@ extern "C" void kmain() {
Efi::SystemTable* ST = (Efi::SystemTable*)Memory::HHDM(system_table_request.response->address);
Efi::Init(ST);
Hal::Halt();
Graphics::Cursor::Initialize(framebuffer);
// Main loop: update cursor position and halt until next interrupt
for (;;) {
Graphics::Cursor::Update();
asm volatile ("hlt");
}
}