The Pedigree Project 0.1
FramebufferConsole.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef MACHINE_FRAMEBUFFER_CONSOLE_H
3#define MACHINE_FRAMEBUFFER_CONSOLE_H
4
5#include <stddef.h>
6#include <stdint.h>
7
8#include "ConsoleFont.h"
9
10// Preserve the VGA cell interface used by BootIO, the debugger and TextIO.
12 public:
13 static const size_t Columns = 80;
14 static const size_t Rows = 25;
15 static const size_t CellCount = Columns * Rows;
16
17 bool initialise(void* pixels, size_t bytes, uint32_t width, uint32_t height, uint32_t pitch,
18 uint32_t format) {
19 if (!pixels || (reinterpret_cast<uintptr_t>(pixels) & 3) || width < 640 || height < 400 ||
20 static_cast<uint64_t>(width) * 4 > pitch || (pitch & 3) || format > 1 ||
21 static_cast<uint64_t>(pitch) * height > bytes) {
22 return false;
23 }
24 m_Pixels = static_cast<volatile uint32_t*>(pixels);
25 m_Stride = pitch / 4;
26 m_Width = width;
27 m_Height = height;
28 m_Format = format;
29 m_Scale = width / 640 < height / 400 ? width / 640 : height / 400;
30 m_Left = (width - 640 * m_Scale) / 2;
31 m_Top = (height - 400 * m_Scale) / 2;
32 m_FirstFrame = true;
33 for (size_t y = 0; y < height; ++y) {
34 for (size_t x = 0; x < width; ++x) {
35 m_Pixels[y * m_Stride + x] = 0;
36 }
37 }
38 return true;
39 }
40
41 uint16_t* cells() {
42 return m_Cells;
43 }
44
45 const uint16_t* cells() const {
46 return m_Cells;
47 }
48
49 void moveCursor(size_t x, size_t y) {
50 m_Cursor = x < Columns && y < Rows ? y * Columns + x : CellCount;
51 }
52
53 void invalidate() {
54 m_FirstFrame = true;
55 }
56
57 void clear() {
58 if (!m_Pixels) {
59 return;
60 }
61 for (size_t y = 0; y < m_Height; ++y) {
62 for (size_t x = 0; x < m_Width; ++x) {
63 m_Pixels[y * m_Stride + x] = 0;
64 }
65 }
66 invalidate();
67 }
68
69 void flush() {
70 if (!m_Pixels) {
71 return;
72 }
73 // Firmware or the splash renderer may have painted the framebuffer after
74 // initialise(). The first cell-rendered frame owns the whole display, so
75 // remove those pixels before drawing the logical console.
76 if (m_FirstFrame)
77 clear();
78 for (size_t i = 0; i < CellCount; ++i) {
79 uint16_t cell = m_Cells[i];
80 if (!m_FirstFrame && cell == m_Rendered[i] && i != m_Cursor && i != m_PreviousCursor) {
81 continue;
82 }
83 const uint8_t* glyph = font_data + ((cell & 0xff) * FONT_HEIGHT);
84 uint32_t foreground = colour((cell >> 8) & 15);
85 uint32_t background = colour((cell >> 12) & 15);
86 size_t left = m_Left + (i % Columns) * FONT_WIDTH * m_Scale;
87 size_t top = m_Top + (i / Columns) * FONT_HEIGHT * m_Scale;
88 for (size_t y = 0; y < FONT_HEIGHT * m_Scale; ++y) {
89 uint8_t bits = glyph[y / m_Scale];
90 bool cursor = i == m_Cursor && y >= 14 * m_Scale;
91 for (size_t x = 0; x < FONT_WIDTH * m_Scale; ++x) {
92 bool set = (bits & (1U << (FONT_WIDTH - 1 - (x / m_Scale)))) != 0;
93 m_Pixels[(top + y) * m_Stride + left + x] = (set != cursor) ? foreground : background;
94 }
95 }
96 m_Rendered[i] = cell;
97 }
98 m_PreviousCursor = m_Cursor;
99 m_FirstFrame = false;
100 }
101
102 private:
103 uint32_t colour(size_t index) const {
104 static const uint32_t palette[16] = {0x000000, 0x0000aa, 0x00aa00, 0x00aaaa, 0xaa0000, 0xaa00aa,
105 0xaa5500, 0xaaaaaa, 0x555555, 0x5555ff, 0x55ff55, 0x55ffff,
106 0xff5555, 0xff55ff, 0xffff55, 0xffffff};
107 uint32_t value = palette[index];
108 return m_Format == 1 ? value : ((value & 0xff) << 16) | (value & 0xff00) | (value >> 16);
109 }
110
111 volatile uint32_t* m_Pixels = nullptr;
112 size_t m_Stride = 0;
113 size_t m_Width = 0;
114 size_t m_Height = 0;
115 size_t m_Left = 0;
116 size_t m_Top = 0;
117 size_t m_Scale = 1;
118 uint32_t m_Format = 0;
119 uint16_t m_Cells[CellCount] = {};
120 uint16_t m_Rendered[CellCount] = {};
121 size_t m_Cursor = CellCount;
122 size_t m_PreviousCursor = CellCount;
123 bool m_FirstFrame = true;
124};
125
126#endif