feat: rudimentary userspace desktop environment
This commit is contained in:
+12
-3
@@ -57,7 +57,7 @@ BINDIR := bin
|
||||
PROGRAMS := $(notdir $(wildcard src/*))
|
||||
|
||||
# Programs with custom Makefiles (built separately).
|
||||
CUSTOM_BUILDS := doom fetch wiki
|
||||
CUSTOM_BUILDS := doom fetch wiki desktop
|
||||
SYSTEM_PROGRAMS := $(filter-out $(CUSTOM_BUILDS),$(PROGRAMS))
|
||||
|
||||
# Build targets: system programs go to bin/os/, games are handled separately.
|
||||
@@ -82,9 +82,9 @@ CA_CERTS := $(BINDIR)/etc/ca-certificates.crt
|
||||
# Home directory placeholder.
|
||||
HOMEKEEP := $(BINDIR)/home/.keep
|
||||
|
||||
.PHONY: all clean doom fetch wiki bearssl libc
|
||||
.PHONY: all clean doom fetch wiki desktop icons bearssl libc
|
||||
|
||||
all: bearssl libc $(TARGETS) fetch wiki doom $(GAME_DATA) $(MANDST) $(WWWDST) $(CA_CERTS) $(HOMEKEEP)
|
||||
all: bearssl libc $(TARGETS) fetch wiki doom desktop icons $(GAME_DATA) $(MANDST) $(WWWDST) $(CA_CERTS) $(HOMEKEEP)
|
||||
|
||||
# Build BearSSL static library.
|
||||
bearssl:
|
||||
@@ -102,6 +102,14 @@ fetch: bearssl libc
|
||||
wiki: bearssl libc
|
||||
$(MAKE) -C src/wiki
|
||||
|
||||
# Build desktop via its own Makefile.
|
||||
desktop:
|
||||
$(MAKE) -C src/desktop
|
||||
|
||||
# Copy SVG icons for the desktop into bin/icons/.
|
||||
icons:
|
||||
../scripts/copy_icons.sh
|
||||
|
||||
# Build doom via its own Makefile.
|
||||
doom:
|
||||
$(MAKE) -C src/doom
|
||||
@@ -143,3 +151,4 @@ clean:
|
||||
$(MAKE) -C lib/libc clean
|
||||
$(MAKE) -C src/fetch clean
|
||||
$(MAKE) -C src/doom clean
|
||||
$(MAKE) -C src/desktop clean
|
||||
|
||||
@@ -57,6 +57,14 @@ namespace Zenith {
|
||||
static constexpr uint64_t SYS_TERMSCALE = 43;
|
||||
static constexpr uint64_t SYS_RESOLVE = 44;
|
||||
static constexpr uint64_t SYS_GETRANDOM = 45;
|
||||
static constexpr uint64_t SYS_KLOG = 46;
|
||||
static constexpr uint64_t SYS_MOUSESTATE = 47;
|
||||
static constexpr uint64_t SYS_SETMOUSEBOUNDS = 48;
|
||||
static constexpr uint64_t SYS_SPAWN_REDIR = 49;
|
||||
static constexpr uint64_t SYS_CHILDIO_READ = 50;
|
||||
static constexpr uint64_t SYS_CHILDIO_WRITE = 51;
|
||||
static constexpr uint64_t SYS_CHILDIO_WRITEKEY = 52;
|
||||
static constexpr uint64_t SYS_CHILDIO_SETTERMSZ = 53;
|
||||
|
||||
static constexpr int SOCK_TCP = 1;
|
||||
static constexpr int SOCK_UDP = 2;
|
||||
@@ -103,4 +111,11 @@ namespace Zenith {
|
||||
bool alt;
|
||||
};
|
||||
|
||||
struct MouseState {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t scrollDelta;
|
||||
uint8_t buttons;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* desktop.hpp
|
||||
* ZenithOS desktop state and compositor declarations
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/framebuffer.hpp"
|
||||
#include "gui/svg.hpp"
|
||||
#include "gui/window.hpp"
|
||||
#include "gui/widgets.hpp"
|
||||
#include "gui/terminal.hpp"
|
||||
#include <Api/Syscall.hpp>
|
||||
|
||||
namespace gui {
|
||||
|
||||
static constexpr int MAX_WINDOWS = 8;
|
||||
static constexpr int PANEL_HEIGHT = 32;
|
||||
|
||||
struct DesktopState {
|
||||
Framebuffer fb;
|
||||
Window windows[MAX_WINDOWS];
|
||||
int window_count;
|
||||
int focused_window;
|
||||
|
||||
Zenith::MouseState mouse;
|
||||
uint8_t prev_buttons;
|
||||
|
||||
bool app_menu_open;
|
||||
|
||||
SvgIcon icon_terminal;
|
||||
SvgIcon icon_filemanager;
|
||||
SvgIcon icon_sysinfo;
|
||||
SvgIcon icon_appmenu;
|
||||
SvgIcon icon_folder;
|
||||
SvgIcon icon_file;
|
||||
SvgIcon icon_computer;
|
||||
|
||||
int screen_w, screen_h;
|
||||
};
|
||||
|
||||
// Forward declarations - implemented in main.cpp
|
||||
void desktop_init(DesktopState* ds);
|
||||
void desktop_run(DesktopState* ds);
|
||||
void desktop_compose(DesktopState* ds);
|
||||
int desktop_create_window(DesktopState* ds, const char* title, int x, int y, int w, int h);
|
||||
void desktop_close_window(DesktopState* ds, int idx);
|
||||
void desktop_raise_window(DesktopState* ds, int idx);
|
||||
void desktop_draw_panel(DesktopState* ds);
|
||||
void desktop_draw_window(DesktopState* ds, int idx);
|
||||
void desktop_handle_mouse(DesktopState* ds);
|
||||
void desktop_handle_keyboard(DesktopState* ds, const Zenith::KeyEvent& key);
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* draw.hpp
|
||||
* ZenithOS drawing primitives (lines, circles, rounded rects, cursor)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/framebuffer.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
// Fast horizontal line
|
||||
inline void draw_hline(Framebuffer& fb, int x, int y, int w, Color c) {
|
||||
for (int i = 0; i < w; i++) fb.put_pixel(x + i, y, c);
|
||||
}
|
||||
|
||||
// Fast vertical line
|
||||
inline void draw_vline(Framebuffer& fb, int x, int y, int h, Color c) {
|
||||
for (int i = 0; i < h; i++) fb.put_pixel(x, y + i, c);
|
||||
}
|
||||
|
||||
// Rectangle outline
|
||||
inline void draw_rect(Framebuffer& fb, int x, int y, int w, int h, Color c) {
|
||||
draw_hline(fb, x, y, w, c);
|
||||
draw_hline(fb, x, y + h - 1, w, c);
|
||||
draw_vline(fb, x, y, h, c);
|
||||
draw_vline(fb, x + w - 1, y, h, c);
|
||||
}
|
||||
|
||||
// Filled rounded rectangle using corner circles
|
||||
inline void fill_rounded_rect(Framebuffer& fb, int x, int y, int w, int h, int radius, Color c) {
|
||||
if (radius <= 0) {
|
||||
fb.fill_rect(x, y, w, h, c);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp radius to half the smaller dimension
|
||||
int max_r = gui_min(w / 2, h / 2);
|
||||
if (radius > max_r) radius = max_r;
|
||||
|
||||
// Fill the center rectangle
|
||||
fb.fill_rect(x + radius, y, w - 2 * radius, h, c);
|
||||
// Fill the left and right strips (excluding corners)
|
||||
fb.fill_rect(x, y + radius, radius, h - 2 * radius, c);
|
||||
fb.fill_rect(x + w - radius, y + radius, radius, h - 2 * radius, c);
|
||||
|
||||
// Draw the four rounded corners using midpoint circle
|
||||
int cx_tl = x + radius;
|
||||
int cy_tl = y + radius;
|
||||
int cx_tr = x + w - radius - 1;
|
||||
int cy_tr = y + radius;
|
||||
int cx_bl = x + radius;
|
||||
int cy_bl = y + h - radius - 1;
|
||||
int cx_br = x + w - radius - 1;
|
||||
int cy_br = y + h - radius - 1;
|
||||
|
||||
int px = 0;
|
||||
int py = radius;
|
||||
int d = 1 - radius;
|
||||
|
||||
while (px <= py) {
|
||||
// Top-left corner
|
||||
draw_hline(fb, cx_tl - py, cy_tl - px, py - radius + radius, c);
|
||||
draw_hline(fb, cx_tl - px, cy_tl - py, px, c);
|
||||
|
||||
// Top-right corner
|
||||
draw_hline(fb, cx_tr + 1, cy_tr - px, py, c);
|
||||
draw_hline(fb, cx_tr + 1, cy_tr - py, px, c);
|
||||
|
||||
// Bottom-left corner
|
||||
draw_hline(fb, cx_bl - py, cy_bl + px, py, c);
|
||||
draw_hline(fb, cx_bl - px, cy_bl + py, px, c);
|
||||
|
||||
// Bottom-right corner
|
||||
draw_hline(fb, cx_br + 1, cy_br + px, py, c);
|
||||
draw_hline(fb, cx_br + 1, cy_br + py, px, c);
|
||||
|
||||
if (d < 0) {
|
||||
d += 2 * px + 3;
|
||||
} else {
|
||||
d += 2 * (px - py) + 5;
|
||||
py--;
|
||||
}
|
||||
px++;
|
||||
}
|
||||
}
|
||||
|
||||
// Filled circle (midpoint algorithm)
|
||||
inline void fill_circle(Framebuffer& fb, int cx, int cy, int r, Color c) {
|
||||
if (r <= 0) return;
|
||||
|
||||
int x = 0;
|
||||
int y = r;
|
||||
int d = 1 - r;
|
||||
|
||||
// Draw initial horizontal lines
|
||||
draw_hline(fb, cx - r, cy, 2 * r + 1, c);
|
||||
|
||||
while (x < y) {
|
||||
if (d < 0) {
|
||||
d += 2 * x + 3;
|
||||
} else {
|
||||
d += 2 * (x - y) + 5;
|
||||
y--;
|
||||
draw_hline(fb, cx - x, cy + y + 1, 2 * x + 1, c);
|
||||
draw_hline(fb, cx - x, cy - y - 1, 2 * x + 1, c);
|
||||
}
|
||||
x++;
|
||||
draw_hline(fb, cx - y, cy + x, 2 * y + 1, c);
|
||||
draw_hline(fb, cx - y, cy - x, 2 * y + 1, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Circle outline (midpoint algorithm)
|
||||
inline void draw_circle(Framebuffer& fb, int cx, int cy, int r, Color c) {
|
||||
if (r <= 0) {
|
||||
fb.put_pixel(cx, cy, c);
|
||||
return;
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = r;
|
||||
int d = 1 - r;
|
||||
|
||||
while (x <= y) {
|
||||
fb.put_pixel(cx + x, cy + y, c);
|
||||
fb.put_pixel(cx - x, cy + y, c);
|
||||
fb.put_pixel(cx + x, cy - y, c);
|
||||
fb.put_pixel(cx - x, cy - y, c);
|
||||
fb.put_pixel(cx + y, cy + x, c);
|
||||
fb.put_pixel(cx - y, cy + x, c);
|
||||
fb.put_pixel(cx + y, cy - x, c);
|
||||
fb.put_pixel(cx - y, cy - x, c);
|
||||
|
||||
if (d < 0) {
|
||||
d += 2 * x + 3;
|
||||
} else {
|
||||
d += 2 * (x - y) + 5;
|
||||
y--;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
// Bresenham line drawing
|
||||
inline void draw_line(Framebuffer& fb, int x0, int y0, int x1, int y1, Color c) {
|
||||
int dx = gui_abs(x1 - x0);
|
||||
int dy = gui_abs(y1 - y0);
|
||||
int sx = x0 < x1 ? 1 : -1;
|
||||
int sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx - dy;
|
||||
|
||||
for (;;) {
|
||||
fb.put_pixel(x0, y0, c);
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
int e2 = 2 * err;
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop shadow: offset darker rectangles below/right
|
||||
inline void draw_shadow(Framebuffer& fb, int x, int y, int w, int h, int offset, Color shadow_color) {
|
||||
// Bottom shadow strip
|
||||
fb.fill_rect_alpha(x + offset, y + h, w, offset, shadow_color);
|
||||
// Right shadow strip
|
||||
fb.fill_rect_alpha(x + w, y + offset, offset, h, shadow_color);
|
||||
// Corner
|
||||
fb.fill_rect_alpha(x + w, y + h, offset, offset, shadow_color);
|
||||
}
|
||||
|
||||
// 16x16 mouse cursor bitmaps
|
||||
// Outline (black, where design has '1')
|
||||
static constexpr uint16_t cursor_outline[16] = {
|
||||
0x8000, // 1000000000000000
|
||||
0xC000, // 1100000000000000
|
||||
0xA000, // 1010000000000000
|
||||
0x9000, // 1001000000000000
|
||||
0x8800, // 1000100000000000
|
||||
0x8400, // 1000010000000000
|
||||
0x8200, // 1000001000000000
|
||||
0x8100, // 1000000100000000
|
||||
0x8080, // 1000000010000000
|
||||
0x8040, // 1000000001000000
|
||||
0x8780, // 1000011110000000 (changed: row 10 = 1 2 2 2 2 2 1 1 1 1)
|
||||
0x9200, // 1001001000000000 (row 11 = 1 2 2 1 2 2 1)
|
||||
0xA900, // 1010100100000000 (row 12 = 1 2 1 0 1 2 2 1)
|
||||
0xC900, // 1100100100000000 (row 13 = 1 1 0 0 1 2 2 1)
|
||||
0x8480, // 1000010010000000 (row 14 = 1 0 0 0 0 1 2 2 1)
|
||||
0x0700, // 0000011100000000 (row 15 = 0 0 0 0 0 1 1 1)
|
||||
};
|
||||
|
||||
// Fill (white, where design has '2')
|
||||
static constexpr uint16_t cursor_fill[16] = {
|
||||
0x0000, // row 0: no fill
|
||||
0x0000, // row 1: no fill
|
||||
0x4000, // row 2: 0100000000000000
|
||||
0x6000, // row 3: 0110000000000000
|
||||
0x7000, // row 4: 0111000000000000
|
||||
0x7800, // row 5: 0111100000000000
|
||||
0x7C00, // row 6: 0111110000000000
|
||||
0x7E00, // row 7: 0111111000000000
|
||||
0x7F00, // row 8: 0111111100000000
|
||||
0x7F80, // row 9: 0111111110000000
|
||||
0x7800, // row 10: 0111100000000000 (fill only in positions 1-5)
|
||||
0x6C00, // row 11: 0110110000000000 (fill at positions 1,2 and 4,5)
|
||||
0x4600, // row 12: 0100011000000000 (fill at position 1 and 5,6)
|
||||
0x0600, // row 13: 0000011000000000 (fill at positions 5,6)
|
||||
0x0300, // row 14: 0000001100000000 (fill at positions 6,7)
|
||||
0x0000, // row 15: no fill
|
||||
};
|
||||
|
||||
// Draw the mouse cursor at (x, y)
|
||||
inline void draw_cursor(Framebuffer& fb, int x, int y) {
|
||||
Color black = colors::BLACK;
|
||||
Color white = colors::WHITE;
|
||||
|
||||
for (int row = 0; row < 16; row++) {
|
||||
uint16_t outline = cursor_outline[row];
|
||||
uint16_t fill = cursor_fill[row];
|
||||
for (int col = 0; col < 16; col++) {
|
||||
uint16_t mask = (uint16_t)(0x8000 >> col);
|
||||
if (outline & mask) {
|
||||
fb.put_pixel(x + col, y + row, black);
|
||||
} else if (fill & mask) {
|
||||
fb.put_pixel(x + col, y + row, white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* font.hpp
|
||||
* ZenithOS 8x16 VGA bitmap font rendering
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/framebuffer.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
static constexpr int FONT_WIDTH = 8;
|
||||
static constexpr int FONT_HEIGHT = 16;
|
||||
|
||||
// Defined in font_data.cpp
|
||||
extern const uint8_t font_data[256 * 16];
|
||||
|
||||
inline void draw_char(Framebuffer& fb, int x, int y, char c, Color fg) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)c * FONT_HEIGHT];
|
||||
for (int row = 0; row < FONT_HEIGHT; row++) {
|
||||
uint8_t bits = glyph[row];
|
||||
for (int col = 0; col < FONT_WIDTH; col++) {
|
||||
if (bits & (0x80 >> col)) {
|
||||
fb.put_pixel(x + col, y + row, fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_char_bg(Framebuffer& fb, int x, int y, char c, Color fg, Color bg) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)c * FONT_HEIGHT];
|
||||
for (int row = 0; row < FONT_HEIGHT; row++) {
|
||||
uint8_t bits = glyph[row];
|
||||
for (int col = 0; col < FONT_WIDTH; col++) {
|
||||
if (bits & (0x80 >> col)) {
|
||||
fb.put_pixel(x + col, y + row, fg);
|
||||
} else {
|
||||
fb.put_pixel(x + col, y + row, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_text(Framebuffer& fb, int x, int y, const char* text, Color fg) {
|
||||
for (int i = 0; text[i]; i++) {
|
||||
draw_char(fb, x + i * FONT_WIDTH, y, text[i], fg);
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_text_bg(Framebuffer& fb, int x, int y, const char* text, Color fg, Color bg) {
|
||||
for (int i = 0; text[i]; i++) {
|
||||
draw_char_bg(fb, x + i * FONT_WIDTH, y, text[i], fg, bg);
|
||||
}
|
||||
}
|
||||
|
||||
inline int text_width(const char* text) {
|
||||
int len = 0;
|
||||
while (text[len]) len++;
|
||||
return len * FONT_WIDTH;
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* framebuffer.hpp
|
||||
* ZenithOS double-buffered framebuffer abstraction
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <zenith/syscall.h>
|
||||
#include "gui/gui.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
class Framebuffer {
|
||||
uint32_t* hw_fb;
|
||||
uint32_t* back_buf;
|
||||
int fb_width;
|
||||
int fb_height;
|
||||
int fb_pitch; // in bytes
|
||||
|
||||
public:
|
||||
Framebuffer() : hw_fb(nullptr), back_buf(nullptr), fb_width(0), fb_height(0), fb_pitch(0) {
|
||||
Zenith::FbInfo info;
|
||||
zenith::fb_info(&info);
|
||||
|
||||
fb_width = (int)info.width;
|
||||
fb_height = (int)info.height;
|
||||
fb_pitch = (int)info.pitch;
|
||||
|
||||
hw_fb = (uint32_t*)zenith::fb_map();
|
||||
back_buf = (uint32_t*)zenith::alloc((uint64_t)fb_height * fb_pitch);
|
||||
}
|
||||
|
||||
int width() const { return fb_width; }
|
||||
int height() const { return fb_height; }
|
||||
int pitch() const { return fb_pitch; }
|
||||
|
||||
uint32_t* buffer() { return back_buf; }
|
||||
|
||||
inline void put_pixel(int x, int y, Color c) {
|
||||
if (x < 0 || x >= fb_width || y < 0 || y >= fb_height) return;
|
||||
uint32_t* row = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch);
|
||||
row[x] = c.to_pixel();
|
||||
}
|
||||
|
||||
inline void put_pixel_alpha(int x, int y, Color c) {
|
||||
if (x < 0 || x >= fb_width || y < 0 || y >= fb_height) return;
|
||||
if (c.a == 0) return;
|
||||
if (c.a == 255) {
|
||||
put_pixel(x, y, c);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t* row = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch);
|
||||
uint32_t dst = row[x];
|
||||
|
||||
uint8_t dr = (dst >> 16) & 0xFF;
|
||||
uint8_t dg = (dst >> 8) & 0xFF;
|
||||
uint8_t db = dst & 0xFF;
|
||||
|
||||
uint32_t a = c.a;
|
||||
uint32_t inv_a = 255 - a;
|
||||
|
||||
// Fast alpha blend: out = (src * alpha + dst * (255 - alpha) + 128) / 255
|
||||
// Approximation: (x + 1 + (x >> 8)) >> 8 for division by 255
|
||||
uint32_t rr = a * c.r + inv_a * dr;
|
||||
uint32_t gg = a * c.g + inv_a * dg;
|
||||
uint32_t bb = a * c.b + inv_a * db;
|
||||
|
||||
rr = (rr + 1 + (rr >> 8)) >> 8;
|
||||
gg = (gg + 1 + (gg >> 8)) >> 8;
|
||||
bb = (bb + 1 + (bb >> 8)) >> 8;
|
||||
|
||||
row[x] = (0xFF000000) | (rr << 16) | (gg << 8) | bb;
|
||||
}
|
||||
|
||||
inline void fill_rect(int x, int y, int w, int h, Color c) {
|
||||
// Clip to screen bounds
|
||||
int x0 = x < 0 ? 0 : x;
|
||||
int y0 = y < 0 ? 0 : y;
|
||||
int x1 = (x + w) > fb_width ? fb_width : (x + w);
|
||||
int y1 = (y + h) > fb_height ? fb_height : (y + h);
|
||||
|
||||
if (x0 >= x1 || y0 >= y1) return;
|
||||
|
||||
uint32_t pixel = c.to_pixel();
|
||||
int clipped_w = x1 - x0;
|
||||
|
||||
for (int row = y0; row < y1; row++) {
|
||||
uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + row * fb_pitch) + x0;
|
||||
for (int col = 0; col < clipped_w; col++) {
|
||||
dst[col] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void fill_rect_alpha(int x, int y, int w, int h, Color c) {
|
||||
if (c.a == 0) return;
|
||||
if (c.a == 255) {
|
||||
fill_rect(x, y, w, h, c);
|
||||
return;
|
||||
}
|
||||
|
||||
int x0 = x < 0 ? 0 : x;
|
||||
int y0 = y < 0 ? 0 : y;
|
||||
int x1 = (x + w) > fb_width ? fb_width : (x + w);
|
||||
int y1 = (y + h) > fb_height ? fb_height : (y + h);
|
||||
|
||||
if (x0 >= x1 || y0 >= y1) return;
|
||||
|
||||
uint32_t a = c.a;
|
||||
uint32_t inv_a = 255 - a;
|
||||
uint32_t src_r = a * c.r;
|
||||
uint32_t src_g = a * c.g;
|
||||
uint32_t src_b = a * c.b;
|
||||
|
||||
for (int row = y0; row < y1; row++) {
|
||||
uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + row * fb_pitch) + x0;
|
||||
for (int col = 0; col < x1 - x0; col++) {
|
||||
uint32_t d = dst[col];
|
||||
uint32_t dr = (d >> 16) & 0xFF;
|
||||
uint32_t dg = (d >> 8) & 0xFF;
|
||||
uint32_t db = d & 0xFF;
|
||||
|
||||
uint32_t rr = src_r + inv_a * dr;
|
||||
uint32_t gg = src_g + inv_a * dg;
|
||||
uint32_t bb = src_b + inv_a * db;
|
||||
|
||||
rr = (rr + 1 + (rr >> 8)) >> 8;
|
||||
gg = (gg + 1 + (gg >> 8)) >> 8;
|
||||
bb = (bb + 1 + (bb >> 8)) >> 8;
|
||||
|
||||
dst[col] = (0xFF000000) | (rr << 16) | (gg << 8) | bb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void blit(int x, int y, int w, int h, const uint32_t* pixels) {
|
||||
for (int row = 0; row < h; row++) {
|
||||
int dy = y + row;
|
||||
if (dy < 0 || dy >= fb_height) continue;
|
||||
for (int col = 0; col < w; col++) {
|
||||
int dx = x + col;
|
||||
if (dx < 0 || dx >= fb_width) continue;
|
||||
uint32_t* dst_row = (uint32_t*)((uint8_t*)back_buf + dy * fb_pitch);
|
||||
dst_row[dx] = pixels[row * w + col];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void blit_alpha(int x, int y, int w, int h, const uint32_t* pixels) {
|
||||
for (int row = 0; row < h; row++) {
|
||||
int dy = y + row;
|
||||
if (dy < 0 || dy >= fb_height) continue;
|
||||
for (int col = 0; col < w; col++) {
|
||||
int dx = x + col;
|
||||
if (dx < 0 || dx >= fb_width) continue;
|
||||
|
||||
uint32_t src = pixels[row * w + col];
|
||||
uint8_t sa = (src >> 24) & 0xFF;
|
||||
if (sa == 0) continue;
|
||||
|
||||
uint8_t sr = (src >> 16) & 0xFF;
|
||||
uint8_t sg = (src >> 8) & 0xFF;
|
||||
uint8_t sb = src & 0xFF;
|
||||
|
||||
if (sa == 255) {
|
||||
uint32_t* dst_row = (uint32_t*)((uint8_t*)back_buf + dy * fb_pitch);
|
||||
dst_row[dx] = src;
|
||||
continue;
|
||||
}
|
||||
|
||||
Color sc = {sr, sg, sb, sa};
|
||||
put_pixel_alpha(dx, dy, sc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void clear(Color c) {
|
||||
fill_rect(0, 0, fb_width, fb_height, c);
|
||||
}
|
||||
|
||||
inline void flip() {
|
||||
// Copy back buffer to hardware framebuffer, row by row (pitch may differ)
|
||||
int row_pixels = fb_width;
|
||||
for (int y = 0; y < fb_height; y++) {
|
||||
uint32_t* src = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch);
|
||||
uint32_t* dst = (uint32_t*)((uint8_t*)hw_fb + y * fb_pitch);
|
||||
for (int x = 0; x < row_pixels; x++) {
|
||||
dst[x] = src[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* gui.hpp
|
||||
* ZenithOS core GUI types and utilities
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace gui {
|
||||
|
||||
// 16.16 fixed-point type
|
||||
using fixed_t = int32_t;
|
||||
|
||||
static constexpr int FIXED_SHIFT = 16;
|
||||
|
||||
inline fixed_t int_to_fixed(int v) { return v << FIXED_SHIFT; }
|
||||
inline int fixed_to_int(fixed_t v) { return v >> FIXED_SHIFT; }
|
||||
inline fixed_t fixed_mul(fixed_t a, fixed_t b) { return (int32_t)(((int64_t)a * b) >> FIXED_SHIFT); }
|
||||
inline fixed_t fixed_div(fixed_t a, fixed_t b) { return (int32_t)(((int64_t)a << FIXED_SHIFT) / b); }
|
||||
inline fixed_t fixed_from_parts(int whole, int frac_num, int frac_den) {
|
||||
return int_to_fixed(whole) + (int32_t)(((int64_t)frac_num << FIXED_SHIFT) / frac_den);
|
||||
}
|
||||
|
||||
struct Color {
|
||||
uint8_t r, g, b, a;
|
||||
|
||||
static Color from_rgb(uint8_t r, uint8_t g, uint8_t b) { return {r, g, b, 255}; }
|
||||
static Color from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return {r, g, b, a}; }
|
||||
static Color from_hex(uint32_t hex) {
|
||||
return {(uint8_t)((hex >> 16) & 0xFF), (uint8_t)((hex >> 8) & 0xFF), (uint8_t)(hex & 0xFF), 255};
|
||||
}
|
||||
|
||||
uint32_t to_pixel() const { return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; }
|
||||
};
|
||||
|
||||
// Named colors for the desktop theme
|
||||
namespace colors {
|
||||
static constexpr Color DESKTOP_BG = {0xE0, 0xE0, 0xE0, 0xFF};
|
||||
static constexpr Color PANEL_BG = {0x2B, 0x3E, 0x50, 0xFF};
|
||||
static constexpr Color TITLEBAR_BG = {0xF5, 0xF5, 0xF5, 0xFF};
|
||||
static constexpr Color WINDOW_BG = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
static constexpr Color BORDER = {0xCC, 0xCC, 0xCC, 0xFF};
|
||||
static constexpr Color TEXT_COLOR = {0x33, 0x33, 0x33, 0xFF};
|
||||
static constexpr Color PANEL_TEXT = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
static constexpr Color ACCENT = {0x36, 0x7B, 0xF0, 0xFF};
|
||||
static constexpr Color CLOSE_BTN = {0xFF, 0x5F, 0x57, 0xFF};
|
||||
static constexpr Color MAX_BTN = {0x28, 0xCA, 0x42, 0xFF};
|
||||
static constexpr Color MIN_BTN = {0xFF, 0xBD, 0x2E, 0xFF};
|
||||
static constexpr Color SHADOW = {0x00, 0x00, 0x00, 0x40};
|
||||
static constexpr Color TRANSPARENT = {0x00, 0x00, 0x00, 0x00};
|
||||
static constexpr Color BLACK = {0x00, 0x00, 0x00, 0xFF};
|
||||
static constexpr Color WHITE = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
static constexpr Color ICON_COLOR = {0x5C, 0x61, 0x6C, 0xFF};
|
||||
static constexpr Color SCROLLBAR_BG = {0xF0, 0xF0, 0xF0, 0xFF};
|
||||
static constexpr Color SCROLLBAR_FG = {0xC0, 0xC0, 0xC0, 0xFF};
|
||||
static constexpr Color MENU_BG = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
static constexpr Color MENU_HOVER = {0xE8, 0xF0, 0xFE, 0xFF};
|
||||
static constexpr Color TERM_BG = {0x2D, 0x2D, 0x2D, 0xFF};
|
||||
static constexpr Color TERM_FG = {0xCC, 0xCC, 0xCC, 0xFF};
|
||||
}
|
||||
|
||||
struct Point {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct Rect {
|
||||
int x, y, w, h;
|
||||
|
||||
bool contains(int px, int py) const {
|
||||
return px >= x && px < x + w && py >= y && py < y + h;
|
||||
}
|
||||
|
||||
Rect intersect(const Rect& other) const {
|
||||
int rx = x > other.x ? x : other.x;
|
||||
int ry = y > other.y ? y : other.y;
|
||||
int rx2 = (x + w) < (other.x + other.w) ? (x + w) : (other.x + other.w);
|
||||
int ry2 = (y + h) < (other.y + other.h) ? (y + h) : (other.y + other.h);
|
||||
if (rx2 <= rx || ry2 <= ry) return {0, 0, 0, 0};
|
||||
return {rx, ry, rx2 - rx, ry2 - ry};
|
||||
}
|
||||
|
||||
bool empty() const { return w <= 0 || h <= 0; }
|
||||
};
|
||||
|
||||
// Simple inline utility functions
|
||||
inline int gui_min(int a, int b) { return a < b ? a : b; }
|
||||
inline int gui_max(int a, int b) { return a > b ? a : b; }
|
||||
inline int gui_abs(int a) { return a < 0 ? -a : a; }
|
||||
inline int gui_clamp(int v, int lo, int hi) { return v < lo ? lo : (v > hi ? hi : v); }
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,972 @@
|
||||
/*
|
||||
* svg.hpp
|
||||
* ZenithOS SVG icon parser and scanline rasterizer
|
||||
* Handles the Flat-Remix symbolic icon subset (path, circle, rect)
|
||||
* All math uses 16.16 fixed-point -- NO floating point.
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include <zenith/syscall.h>
|
||||
|
||||
namespace gui {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SVG icon result
|
||||
// ---------------------------------------------------------------------------
|
||||
struct SvgIcon {
|
||||
uint32_t* pixels; // ARGB pixel data (heap-allocated)
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Edge used by the scanline rasterizer
|
||||
// ---------------------------------------------------------------------------
|
||||
struct SvgEdge {
|
||||
fixed_t x0, y0, x1, y1;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
static constexpr int SVG_MAX_EDGES = 4096;
|
||||
static constexpr int SVG_MAX_PATH_LEN = 4096;
|
||||
static constexpr int SVG_MAX_FILE_SIZE = 32768;
|
||||
static constexpr int SVG_BEZIER_STEPS = 8;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fixed-point number parser (NO floating point)
|
||||
// Parses strings like "3.25", "-0.5", ".1115", "16"
|
||||
// Returns the number of characters consumed.
|
||||
// ---------------------------------------------------------------------------
|
||||
inline int svg_parse_fixed(const char* s, fixed_t* out) {
|
||||
const char* p = s;
|
||||
bool neg = false;
|
||||
|
||||
if (*p == '-') { neg = true; ++p; }
|
||||
else if (*p == '+') { ++p; }
|
||||
|
||||
// Integer part
|
||||
int32_t integer = 0;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
integer = integer * 10 + (*p - '0');
|
||||
++p;
|
||||
}
|
||||
|
||||
// Fractional part
|
||||
int32_t frac = 0;
|
||||
int32_t frac_div = 1;
|
||||
if (*p == '.') {
|
||||
++p;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
if (frac_div < 100000) { // prevent overflow
|
||||
frac = frac * 10 + (*p - '0');
|
||||
frac_div *= 10;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
fixed_t val = int_to_fixed(integer);
|
||||
if (frac_div > 1) {
|
||||
val += (int32_t)(((int64_t)frac << 16) / frac_div);
|
||||
}
|
||||
|
||||
if (neg) val = -val;
|
||||
*out = val;
|
||||
return (int)(p - s);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// String helpers (no stdlib)
|
||||
// ---------------------------------------------------------------------------
|
||||
inline bool svg_char_is_ws(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
inline bool svg_char_is_sep(char c) {
|
||||
return svg_char_is_ws(c) || c == ',';
|
||||
}
|
||||
|
||||
inline bool svg_char_is_num_start(char c) {
|
||||
return (c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.';
|
||||
}
|
||||
|
||||
inline bool svg_char_is_cmd(char c) {
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
inline int svg_strlen(const char* s) {
|
||||
int n = 0;
|
||||
while (s[n]) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
inline bool svg_strncmp(const char* a, const char* b, int n) {
|
||||
for (int i = 0; i < n; ++i)
|
||||
if (a[i] != b[i]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void svg_memset(void* dst, uint8_t val, int n) {
|
||||
auto* d = (uint8_t*)dst;
|
||||
for (int i = 0; i < n; ++i) d[i] = val;
|
||||
}
|
||||
|
||||
inline void svg_memcpy(void* dst, const void* src, int n) {
|
||||
auto* d = (uint8_t*)dst;
|
||||
auto* s = (const uint8_t*)src;
|
||||
for (int i = 0; i < n; ++i) d[i] = s[i];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mini XML attribute extraction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Find the next occurrence of needle in haystack (haystack has length hLen).
|
||||
// Returns pointer to start of match, or nullptr.
|
||||
inline const char* svg_strstr(const char* haystack, int hLen, const char* needle) {
|
||||
int nLen = svg_strlen(needle);
|
||||
if (nLen == 0 || nLen > hLen) return nullptr;
|
||||
for (int i = 0; i <= hLen - nLen; ++i) {
|
||||
if (svg_strncmp(haystack + i, needle, nLen))
|
||||
return haystack + i;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if a character can be part of an XML attribute name
|
||||
inline bool svg_char_is_attrname(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '-' || c == '_' || c == ':';
|
||||
}
|
||||
|
||||
// Extract the value of an XML attribute: attr="value"
|
||||
// The attr string should include a leading space (e.g., " cx") to distinguish
|
||||
// from substrings of other attribute names.
|
||||
// Writes into buf (up to maxLen-1 chars), returns length or -1 if not found.
|
||||
inline int svg_get_attr(const char* tag, int tagLen, const char* attr, char* buf, int maxLen) {
|
||||
int attrLen = svg_strlen(attr);
|
||||
const char* search_start = tag;
|
||||
int search_len = tagLen;
|
||||
|
||||
while (search_len > 0) {
|
||||
const char* p = svg_strstr(search_start, search_len, attr);
|
||||
if (!p) return -1;
|
||||
|
||||
// Ensure this is the exact attribute name, not a prefix of another
|
||||
// (e.g., " r" should not match " rx" or " ry")
|
||||
const char* after_name = p + attrLen;
|
||||
if (after_name < tag + tagLen && svg_char_is_attrname(*after_name)) {
|
||||
// This is a prefix of a longer attribute name, skip and search again
|
||||
search_start = after_name;
|
||||
search_len = tagLen - (int)(search_start - tag);
|
||||
continue;
|
||||
}
|
||||
|
||||
p = after_name;
|
||||
// skip whitespace around '='
|
||||
while (p < tag + tagLen && svg_char_is_ws(*p)) ++p;
|
||||
if (p >= tag + tagLen || *p != '=') return -1;
|
||||
++p;
|
||||
while (p < tag + tagLen && svg_char_is_ws(*p)) ++p;
|
||||
if (p >= tag + tagLen) return -1;
|
||||
char quote = *p;
|
||||
if (quote != '"' && quote != '\'') return -1;
|
||||
++p;
|
||||
int len = 0;
|
||||
while (p < tag + tagLen && *p != quote && len < maxLen - 1) {
|
||||
buf[len++] = *p++;
|
||||
}
|
||||
buf[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse an integer from a string (no sign, simple decimal).
|
||||
inline int svg_parse_int(const char* s) {
|
||||
int v = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10 + (*s - '0');
|
||||
++s;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// Parse a hex color like "#5c616c" into a Color.
|
||||
inline Color svg_parse_hex_color(const char* s) {
|
||||
if (*s == '#') ++s;
|
||||
auto hexval = [](char c) -> uint8_t {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
|
||||
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
|
||||
return 0;
|
||||
};
|
||||
uint8_t r = (hexval(s[0]) << 4) | hexval(s[1]);
|
||||
uint8_t g = (hexval(s[2]) << 4) | hexval(s[3]);
|
||||
uint8_t b = (hexval(s[4]) << 4) | hexval(s[5]);
|
||||
return Color::from_rgb(r, g, b);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Edge list builder
|
||||
// ---------------------------------------------------------------------------
|
||||
struct SvgEdgeList {
|
||||
SvgEdge* edges;
|
||||
int count;
|
||||
int capacity;
|
||||
|
||||
void init(int cap) {
|
||||
edges = (SvgEdge*)zenith::alloc(cap * sizeof(SvgEdge));
|
||||
count = 0;
|
||||
capacity = cap;
|
||||
}
|
||||
|
||||
void add(fixed_t x0, fixed_t y0, fixed_t x1, fixed_t y1) {
|
||||
if (count >= capacity) return;
|
||||
// skip horizontal edges (they don't contribute to scanline crossings)
|
||||
if (y0 == y1) return;
|
||||
edges[count++] = {x0, y0, x1, y1};
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bezier flattening (fixed-point)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Cubic bezier: add line segments approximating B(t) for t in [0,1]
|
||||
inline void svg_flatten_cubic(SvgEdgeList& el,
|
||||
fixed_t x0, fixed_t y0,
|
||||
fixed_t x1, fixed_t y1,
|
||||
fixed_t x2, fixed_t y2,
|
||||
fixed_t x3, fixed_t y3) {
|
||||
constexpr int N = SVG_BEZIER_STEPS;
|
||||
fixed_t px = x0, py = y0;
|
||||
for (int i = 1; i <= N; ++i) {
|
||||
// t = i/N in 16.16: (i << 16) / N
|
||||
fixed_t t = (int32_t)(((int64_t)i << 16) / N);
|
||||
fixed_t omt = int_to_fixed(1) - t; // 1 - t
|
||||
|
||||
// (1-t)^2 and t^2
|
||||
fixed_t omt2 = fixed_mul(omt, omt);
|
||||
fixed_t t2 = fixed_mul(t, t);
|
||||
|
||||
// (1-t)^3 and t^3
|
||||
fixed_t omt3 = fixed_mul(omt2, omt);
|
||||
fixed_t t3 = fixed_mul(t2, t);
|
||||
|
||||
// 3*(1-t)^2*t and 3*(1-t)*t^2
|
||||
fixed_t c1 = fixed_mul(omt2, t) * 3;
|
||||
fixed_t c2 = fixed_mul(omt, t2) * 3;
|
||||
|
||||
fixed_t nx = fixed_mul(omt3, x0) + fixed_mul(c1, x1) + fixed_mul(c2, x2) + fixed_mul(t3, x3);
|
||||
fixed_t ny = fixed_mul(omt3, y0) + fixed_mul(c1, y1) + fixed_mul(c2, y2) + fixed_mul(t3, y3);
|
||||
|
||||
el.add(px, py, nx, ny);
|
||||
px = nx;
|
||||
py = ny;
|
||||
}
|
||||
}
|
||||
|
||||
// Quadratic bezier: add line segments approximating B(t) for t in [0,1]
|
||||
inline void svg_flatten_quad(SvgEdgeList& el,
|
||||
fixed_t x0, fixed_t y0,
|
||||
fixed_t x1, fixed_t y1,
|
||||
fixed_t x2, fixed_t y2) {
|
||||
constexpr int N = SVG_BEZIER_STEPS;
|
||||
fixed_t px = x0, py = y0;
|
||||
for (int i = 1; i <= N; ++i) {
|
||||
fixed_t t = (int32_t)(((int64_t)i << 16) / N);
|
||||
fixed_t omt = int_to_fixed(1) - t;
|
||||
|
||||
// (1-t)^2*P0 + 2*(1-t)*t*P1 + t^2*P2
|
||||
fixed_t omt2 = fixed_mul(omt, omt);
|
||||
fixed_t t2 = fixed_mul(t, t);
|
||||
fixed_t c1 = fixed_mul(omt, t) * 2;
|
||||
|
||||
fixed_t nx = fixed_mul(omt2, x0) + fixed_mul(c1, x1) + fixed_mul(t2, x2);
|
||||
fixed_t ny = fixed_mul(omt2, y0) + fixed_mul(c1, y1) + fixed_mul(t2, y2);
|
||||
|
||||
el.add(px, py, nx, ny);
|
||||
px = nx;
|
||||
py = ny;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Circle to edges: approximate a circle as N line segments
|
||||
// ---------------------------------------------------------------------------
|
||||
inline void svg_circle_edges(SvgEdgeList& el, fixed_t cx, fixed_t cy, fixed_t r) {
|
||||
// Approximate circle with 16 segments using a precomputed sin/cos table
|
||||
// for angles 0, 22.5, 45, ... 337.5 degrees.
|
||||
// sin/cos in 16.16 fixed-point for 16 evenly-spaced angles:
|
||||
static const fixed_t cos16[16] = {
|
||||
65536, 60547, 46341, 25080, 0, -25080, -46341, -60547,
|
||||
-65536, -60547, -46341, -25080, 0, 25080, 46341, 60547
|
||||
};
|
||||
static const fixed_t sin16[16] = {
|
||||
0, 25080, 46341, 60547, 65536, 60547, 46341, 25080,
|
||||
0, -25080, -46341, -60547, -65536, -60547, -46341, -25080
|
||||
};
|
||||
|
||||
fixed_t px = cx + fixed_mul(r, cos16[0]);
|
||||
fixed_t py = cy + fixed_mul(r, sin16[0]);
|
||||
for (int i = 1; i <= 16; ++i) {
|
||||
int idx = i & 15;
|
||||
fixed_t nx = cx + fixed_mul(r, cos16[idx]);
|
||||
fixed_t ny = cy + fixed_mul(r, sin16[idx]);
|
||||
el.add(px, py, nx, ny);
|
||||
px = nx;
|
||||
py = ny;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Rounded rect to edges
|
||||
// ---------------------------------------------------------------------------
|
||||
inline void svg_rect_edges(SvgEdgeList& el, fixed_t x, fixed_t y, fixed_t w, fixed_t h,
|
||||
fixed_t rx, fixed_t ry) {
|
||||
if (rx <= 0 && ry <= 0) {
|
||||
// Simple rectangle: 4 edges
|
||||
fixed_t x2 = x + w;
|
||||
fixed_t y2 = y + h;
|
||||
el.add(x, y, x2, y); // top
|
||||
el.add(x2, y, x2, y2); // right
|
||||
el.add(x2, y2, x, y2); // bottom
|
||||
el.add(x, y2, x, y); // left
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp radii
|
||||
fixed_t half_w = w >> 1;
|
||||
fixed_t half_h = h >> 1;
|
||||
if (rx > half_w) rx = half_w;
|
||||
if (ry > half_h) ry = half_h;
|
||||
|
||||
// Quarter-circle corner with 4 segments per corner.
|
||||
// cos/sin for 0, 22.5, 45, 67.5, 90 degrees (5 points, 4 segments):
|
||||
static const fixed_t qcos[5] = { 65536, 60547, 46341, 25080, 0 };
|
||||
static const fixed_t qsin[5] = { 0, 25080, 46341, 60547, 65536 };
|
||||
|
||||
// Corners: top-right, bottom-right, bottom-left, top-left
|
||||
// Each corner has a center and a quadrant direction for cos/sin application
|
||||
struct Corner { fixed_t cx, cy; int sx, sy; };
|
||||
Corner corners[4] = {
|
||||
{ x + w - rx, y + ry, 1, -1 }, // top-right
|
||||
{ x + w - rx, y + h - ry, 1, 1 }, // bottom-right
|
||||
{ x + rx, y + h - ry, -1, 1 }, // bottom-left
|
||||
{ x + rx, y + ry, -1, -1 }, // top-left
|
||||
};
|
||||
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
Corner& cn = corners[c];
|
||||
fixed_t px = cn.cx + fixed_mul(rx, qcos[0]) * cn.sx;
|
||||
fixed_t py = cn.cy + fixed_mul(ry, qsin[0]) * cn.sy;
|
||||
for (int i = 1; i <= 4; ++i) {
|
||||
fixed_t nx = cn.cx + fixed_mul(rx, qcos[i]) * cn.sx;
|
||||
fixed_t ny = cn.cy + fixed_mul(ry, qsin[i]) * cn.sy;
|
||||
el.add(px, py, nx, ny);
|
||||
px = nx;
|
||||
py = ny;
|
||||
}
|
||||
}
|
||||
|
||||
// Straight edges between corners
|
||||
// Top edge: top-left corner end -> top-right corner start
|
||||
el.add(x + rx, y, x + w - rx, y);
|
||||
// Right edge: top-right corner end -> bottom-right corner start
|
||||
el.add(x + w, y + ry, x + w, y + h - ry);
|
||||
// Bottom edge: bottom-right corner end -> bottom-left corner start
|
||||
el.add(x + w - rx, y + h, x + rx, y + h);
|
||||
// Left edge: bottom-left corner end -> top-left corner start
|
||||
el.add(x, y + h - ry, x, y + ry);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Path command parser: tokenize the 'd' attribute
|
||||
// ---------------------------------------------------------------------------
|
||||
struct SvgPathParser {
|
||||
const char* data;
|
||||
int len;
|
||||
int pos;
|
||||
|
||||
void init(const char* d, int l) { data = d; len = l; pos = 0; }
|
||||
|
||||
void skip_separators() {
|
||||
while (pos < len && svg_char_is_sep(data[pos])) ++pos;
|
||||
}
|
||||
|
||||
bool has_more() const { return pos < len; }
|
||||
|
||||
// Peek at what's next: command letter, number, or end
|
||||
bool next_is_number() {
|
||||
skip_separators();
|
||||
if (pos >= len) return false;
|
||||
return svg_char_is_num_start(data[pos]);
|
||||
}
|
||||
|
||||
char read_command() {
|
||||
skip_separators();
|
||||
if (pos >= len) return '\0';
|
||||
if (svg_char_is_cmd(data[pos]) && data[pos] != 'e' && data[pos] != 'E') {
|
||||
return data[pos++];
|
||||
}
|
||||
return '\0';
|
||||
}
|
||||
|
||||
fixed_t read_number() {
|
||||
skip_separators();
|
||||
if (pos >= len) return 0;
|
||||
fixed_t val = 0;
|
||||
int consumed = svg_parse_fixed(data + pos, &val);
|
||||
pos += consumed;
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Process an SVG path 'd' attribute into edges
|
||||
// ---------------------------------------------------------------------------
|
||||
inline void svg_path_to_edges(SvgEdgeList& el, const char* d, int dLen,
|
||||
fixed_t scale_x, fixed_t scale_y,
|
||||
fixed_t off_x, fixed_t off_y) {
|
||||
SvgPathParser pp;
|
||||
pp.init(d, dLen);
|
||||
|
||||
fixed_t cur_x = 0, cur_y = 0; // current point
|
||||
fixed_t start_x = 0, start_y = 0; // subpath start
|
||||
fixed_t last_cx = 0, last_cy = 0; // last control point (for S/T)
|
||||
char last_cmd = '\0';
|
||||
|
||||
auto scale_pt = [&](fixed_t x, fixed_t y, fixed_t* ox, fixed_t* oy) {
|
||||
*ox = fixed_mul(x - off_x, scale_x);
|
||||
*oy = fixed_mul(y - off_y, scale_y);
|
||||
};
|
||||
|
||||
while (pp.has_more()) {
|
||||
char cmd = '\0';
|
||||
|
||||
// Try reading a command letter
|
||||
pp.skip_separators();
|
||||
if (pp.pos < pp.len && svg_char_is_cmd(pp.data[pp.pos]) &&
|
||||
pp.data[pp.pos] != 'e' && pp.data[pp.pos] != 'E') {
|
||||
cmd = pp.data[pp.pos++];
|
||||
} else if (pp.next_is_number()) {
|
||||
// Implicit repeat of last command
|
||||
// After M, implicit repeat is L; after m, implicit repeat is l
|
||||
if (last_cmd == 'M') cmd = 'L';
|
||||
else if (last_cmd == 'm') cmd = 'l';
|
||||
else cmd = last_cmd;
|
||||
} else {
|
||||
// Skip unknown character
|
||||
if (pp.pos < pp.len) pp.pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd == '\0') break;
|
||||
|
||||
switch (cmd) {
|
||||
case 'M': {
|
||||
fixed_t x = pp.read_number();
|
||||
fixed_t y = pp.read_number();
|
||||
cur_x = x; cur_y = y;
|
||||
start_x = x; start_y = y;
|
||||
last_cmd = 'M';
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
fixed_t dx = pp.read_number();
|
||||
fixed_t dy = pp.read_number();
|
||||
cur_x += dx; cur_y += dy;
|
||||
start_x = cur_x; start_y = cur_y;
|
||||
last_cmd = 'm';
|
||||
break;
|
||||
}
|
||||
case 'L': {
|
||||
fixed_t x = pp.read_number();
|
||||
fixed_t y = pp.read_number();
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x, y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_x = x; cur_y = y;
|
||||
last_cmd = 'L';
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
fixed_t dx = pp.read_number();
|
||||
fixed_t dy = pp.read_number();
|
||||
fixed_t nx = cur_x + dx, ny = cur_y + dy;
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(nx, ny, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_x = nx; cur_y = ny;
|
||||
last_cmd = 'l';
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
fixed_t x = pp.read_number();
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x, cur_y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_x = x;
|
||||
last_cmd = 'H';
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
fixed_t dx = pp.read_number();
|
||||
fixed_t nx = cur_x + dx;
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(nx, cur_y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_x = nx;
|
||||
last_cmd = 'h';
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
fixed_t y = pp.read_number();
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(cur_x, y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_y = y;
|
||||
last_cmd = 'V';
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
fixed_t dy = pp.read_number();
|
||||
fixed_t ny = cur_y + dy;
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(cur_x, ny, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_y = ny;
|
||||
last_cmd = 'v';
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
fixed_t x1 = pp.read_number(), y1 = pp.read_number();
|
||||
fixed_t x2 = pp.read_number(), y2 = pp.read_number();
|
||||
fixed_t x3 = pp.read_number(), y3 = pp.read_number();
|
||||
fixed_t sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x1, y1, &sx1, &sy1);
|
||||
scale_pt(x2, y2, &sx2, &sy2);
|
||||
scale_pt(x3, y3, &sx3, &sy3);
|
||||
svg_flatten_cubic(el, sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3);
|
||||
last_cx = x2; last_cy = y2;
|
||||
cur_x = x3; cur_y = y3;
|
||||
last_cmd = 'C';
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
fixed_t dx1 = pp.read_number(), dy1 = pp.read_number();
|
||||
fixed_t dx2 = pp.read_number(), dy2 = pp.read_number();
|
||||
fixed_t dx3 = pp.read_number(), dy3 = pp.read_number();
|
||||
fixed_t x1 = cur_x + dx1, y1 = cur_y + dy1;
|
||||
fixed_t x2 = cur_x + dx2, y2 = cur_y + dy2;
|
||||
fixed_t x3 = cur_x + dx3, y3 = cur_y + dy3;
|
||||
fixed_t sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x1, y1, &sx1, &sy1);
|
||||
scale_pt(x2, y2, &sx2, &sy2);
|
||||
scale_pt(x3, y3, &sx3, &sy3);
|
||||
svg_flatten_cubic(el, sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3);
|
||||
last_cx = x2; last_cy = y2;
|
||||
cur_x = x3; cur_y = y3;
|
||||
last_cmd = 'c';
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
// Smooth cubic: reflect last control point
|
||||
fixed_t rcx = cur_x * 2 - last_cx;
|
||||
fixed_t rcy = cur_y * 2 - last_cy;
|
||||
if (last_cmd != 'C' && last_cmd != 'c' && last_cmd != 'S' && last_cmd != 's') {
|
||||
rcx = cur_x; rcy = cur_y;
|
||||
}
|
||||
fixed_t x2 = pp.read_number(), y2 = pp.read_number();
|
||||
fixed_t x3 = pp.read_number(), y3 = pp.read_number();
|
||||
fixed_t sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(rcx, rcy, &sx1, &sy1);
|
||||
scale_pt(x2, y2, &sx2, &sy2);
|
||||
scale_pt(x3, y3, &sx3, &sy3);
|
||||
svg_flatten_cubic(el, sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3);
|
||||
last_cx = x2; last_cy = y2;
|
||||
cur_x = x3; cur_y = y3;
|
||||
last_cmd = 'S';
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
fixed_t rcx = cur_x * 2 - last_cx;
|
||||
fixed_t rcy = cur_y * 2 - last_cy;
|
||||
if (last_cmd != 'C' && last_cmd != 'c' && last_cmd != 'S' && last_cmd != 's') {
|
||||
rcx = cur_x; rcy = cur_y;
|
||||
}
|
||||
fixed_t dx2 = pp.read_number(), dy2 = pp.read_number();
|
||||
fixed_t dx3 = pp.read_number(), dy3 = pp.read_number();
|
||||
fixed_t x2 = cur_x + dx2, y2 = cur_y + dy2;
|
||||
fixed_t x3 = cur_x + dx3, y3 = cur_y + dy3;
|
||||
fixed_t sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(rcx, rcy, &sx1, &sy1);
|
||||
scale_pt(x2, y2, &sx2, &sy2);
|
||||
scale_pt(x3, y3, &sx3, &sy3);
|
||||
svg_flatten_cubic(el, sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3);
|
||||
last_cx = x2; last_cy = y2;
|
||||
cur_x = x3; cur_y = y3;
|
||||
last_cmd = 's';
|
||||
break;
|
||||
}
|
||||
case 'Q': {
|
||||
fixed_t x1 = pp.read_number(), y1 = pp.read_number();
|
||||
fixed_t x2 = pp.read_number(), y2 = pp.read_number();
|
||||
fixed_t sx0, sy0, sx1, sy1, sx2, sy2;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x1, y1, &sx1, &sy1);
|
||||
scale_pt(x2, y2, &sx2, &sy2);
|
||||
svg_flatten_quad(el, sx0, sy0, sx1, sy1, sx2, sy2);
|
||||
last_cx = x1; last_cy = y1;
|
||||
cur_x = x2; cur_y = y2;
|
||||
last_cmd = 'Q';
|
||||
break;
|
||||
}
|
||||
case 'q': {
|
||||
fixed_t dx1 = pp.read_number(), dy1 = pp.read_number();
|
||||
fixed_t dx2 = pp.read_number(), dy2 = pp.read_number();
|
||||
fixed_t x1 = cur_x + dx1, y1 = cur_y + dy1;
|
||||
fixed_t x2 = cur_x + dx2, y2 = cur_y + dy2;
|
||||
fixed_t sx0, sy0, sx1, sy1, sx2, sy2;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x1, y1, &sx1, &sy1);
|
||||
scale_pt(x2, y2, &sx2, &sy2);
|
||||
svg_flatten_quad(el, sx0, sy0, sx1, sy1, sx2, sy2);
|
||||
last_cx = x1; last_cy = y1;
|
||||
cur_x = x2; cur_y = y2;
|
||||
last_cmd = 'q';
|
||||
break;
|
||||
}
|
||||
case 'A': case 'a': {
|
||||
// Arc command: consume parameters but approximate as a line
|
||||
// (arcs are rare in these icons)
|
||||
fixed_t rx = pp.read_number();
|
||||
fixed_t ry = pp.read_number();
|
||||
pp.read_number(); // x-rotation
|
||||
pp.read_number(); // large-arc-flag
|
||||
pp.read_number(); // sweep-flag
|
||||
fixed_t x = pp.read_number();
|
||||
fixed_t y = pp.read_number();
|
||||
if (cmd == 'a') { x += cur_x; y += cur_y; }
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x, y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_x = x; cur_y = y;
|
||||
last_cmd = cmd;
|
||||
(void)rx; (void)ry;
|
||||
break;
|
||||
}
|
||||
case 'Z': case 'z': {
|
||||
if (cur_x != start_x || cur_y != start_y) {
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(start_x, start_y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
}
|
||||
cur_x = start_x; cur_y = start_y;
|
||||
last_cmd = 'Z';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown command, skip
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scanline rasterizer (even-odd fill rule)
|
||||
// ---------------------------------------------------------------------------
|
||||
inline void svg_rasterize(const SvgEdgeList& el, uint32_t* pixels, int w, int h, uint32_t fill) {
|
||||
// Temporary array for x-intersections on each scanline
|
||||
// Allocate enough for all edges (each edge can intersect at most once per scanline)
|
||||
int maxIsect = el.count + 16;
|
||||
fixed_t* isect = (fixed_t*)zenith::alloc(maxIsect * sizeof(fixed_t));
|
||||
|
||||
for (int y = 0; y < h; ++y) {
|
||||
// Scanline center in fixed-point
|
||||
fixed_t scanY = int_to_fixed(y) + (1 << 15); // y + 0.5
|
||||
|
||||
int isectCount = 0;
|
||||
|
||||
// Find intersections with all edges
|
||||
for (int i = 0; i < el.count; ++i) {
|
||||
const SvgEdge& e = el.edges[i];
|
||||
fixed_t ey0 = e.y0, ey1 = e.y1;
|
||||
|
||||
// Ensure ey0 <= ey1 for the range check
|
||||
fixed_t emin = ey0 < ey1 ? ey0 : ey1;
|
||||
fixed_t emax = ey0 > ey1 ? ey0 : ey1;
|
||||
|
||||
// Does this edge cross scanY?
|
||||
if (scanY < emin || scanY >= emax) continue;
|
||||
|
||||
// Compute x at intersection: x = x0 + (scanY - y0) * (x1 - x0) / (y1 - y0)
|
||||
fixed_t dy = ey1 - ey0;
|
||||
if (dy == 0) continue; // horizontal, skip
|
||||
fixed_t dx = e.x1 - e.x0;
|
||||
fixed_t t_num = scanY - ey0;
|
||||
// x_intersect = x0 + dx * t_num / dy
|
||||
fixed_t x_int = e.x0 + (int32_t)(((int64_t)dx * t_num) / dy);
|
||||
|
||||
if (isectCount < maxIsect)
|
||||
isect[isectCount++] = x_int;
|
||||
}
|
||||
|
||||
// Sort intersections (simple insertion sort -- usually very few)
|
||||
for (int i = 1; i < isectCount; ++i) {
|
||||
fixed_t key = isect[i];
|
||||
int j = i - 1;
|
||||
while (j >= 0 && isect[j] > key) {
|
||||
isect[j + 1] = isect[j];
|
||||
--j;
|
||||
}
|
||||
isect[j + 1] = key;
|
||||
}
|
||||
|
||||
// Fill between pairs (even-odd rule)
|
||||
for (int i = 0; i + 1 < isectCount; i += 2) {
|
||||
int x0 = fixed_to_int(isect[i]);
|
||||
int x1 = fixed_to_int(isect[i + 1]);
|
||||
|
||||
// Clamp to pixel bounds
|
||||
if (x0 < 0) x0 = 0;
|
||||
if (x1 > w) x1 = w;
|
||||
|
||||
for (int x = x0; x < x1; ++x) {
|
||||
pixels[y * w + x] = fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zenith::free(isect);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SVG document parser: extract paths, circles, rects and rasterize
|
||||
// ---------------------------------------------------------------------------
|
||||
inline SvgIcon svg_render(const char* svg_data, int svg_len, int target_w, int target_h, Color fill_color) {
|
||||
SvgIcon icon;
|
||||
icon.width = target_w;
|
||||
icon.height = target_h;
|
||||
icon.pixels = (uint32_t*)zenith::alloc(target_w * target_h * sizeof(uint32_t));
|
||||
// Clear to transparent
|
||||
svg_memset(icon.pixels, 0, target_w * target_h * sizeof(uint32_t));
|
||||
|
||||
uint32_t fill_px = fill_color.to_pixel();
|
||||
|
||||
// Parse SVG dimensions: width and height
|
||||
int svg_w = 16, svg_h = 16;
|
||||
fixed_t vb_x = 0, vb_y = 0, vb_w = 0, vb_h = 0;
|
||||
bool has_viewbox = false;
|
||||
|
||||
// Find <svg tag
|
||||
const char* svg_tag = svg_strstr(svg_data, svg_len, "<svg");
|
||||
if (svg_tag) {
|
||||
// Find the end of the <svg ...> tag
|
||||
int tag_offset = (int)(svg_tag - svg_data);
|
||||
int tag_end = tag_offset;
|
||||
while (tag_end < svg_len && svg_data[tag_end] != '>') ++tag_end;
|
||||
int tag_len = tag_end - tag_offset + 1;
|
||||
|
||||
char attr_buf[64];
|
||||
|
||||
// width
|
||||
if (svg_get_attr(svg_tag, tag_len, " width", attr_buf, sizeof(attr_buf)) > 0) {
|
||||
svg_w = svg_parse_int(attr_buf);
|
||||
}
|
||||
// height
|
||||
if (svg_get_attr(svg_tag, tag_len, " height", attr_buf, sizeof(attr_buf)) > 0) {
|
||||
svg_h = svg_parse_int(attr_buf);
|
||||
}
|
||||
// viewBox
|
||||
if (svg_get_attr(svg_tag, tag_len, " viewBox", attr_buf, sizeof(attr_buf)) > 0) {
|
||||
has_viewbox = true;
|
||||
const char* vp = attr_buf;
|
||||
int c;
|
||||
c = svg_parse_fixed(vp, &vb_x); vp += c; while (*vp && svg_char_is_sep(*vp)) ++vp;
|
||||
c = svg_parse_fixed(vp, &vb_y); vp += c; while (*vp && svg_char_is_sep(*vp)) ++vp;
|
||||
c = svg_parse_fixed(vp, &vb_w); vp += c; while (*vp && svg_char_is_sep(*vp)) ++vp;
|
||||
svg_parse_fixed(vp, &vb_h);
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_viewbox) {
|
||||
vb_x = 0;
|
||||
vb_y = 0;
|
||||
vb_w = int_to_fixed(svg_w);
|
||||
vb_h = int_to_fixed(svg_h);
|
||||
}
|
||||
|
||||
// Compute scale: pixel = (svg_coord - vb_origin) * target_size / vb_size
|
||||
fixed_t scale_x = vb_w > 0 ? fixed_div(int_to_fixed(target_w), vb_w) : int_to_fixed(1);
|
||||
fixed_t scale_y = vb_h > 0 ? fixed_div(int_to_fixed(target_h), vb_h) : int_to_fixed(1);
|
||||
|
||||
// Edge list
|
||||
SvgEdgeList el;
|
||||
el.init(SVG_MAX_EDGES);
|
||||
|
||||
// Scan for <path, <circle, <rect elements
|
||||
const char* p = svg_data;
|
||||
const char* end = svg_data + svg_len;
|
||||
|
||||
while (p < end) {
|
||||
// Find next '<'
|
||||
while (p < end && *p != '<') ++p;
|
||||
if (p >= end) break;
|
||||
|
||||
int remaining = (int)(end - p);
|
||||
|
||||
// Check for <path
|
||||
if (remaining > 5 && svg_strncmp(p, "<path", 5) && (svg_char_is_ws(p[5]) || p[5] == '/')) {
|
||||
// Find end of this element
|
||||
const char* elem_start = p;
|
||||
const char* elem_end = p;
|
||||
while (elem_end < end && *elem_end != '>') ++elem_end;
|
||||
if (elem_end < end) ++elem_end; // include '>'
|
||||
int elem_len = (int)(elem_end - elem_start);
|
||||
|
||||
// Extract d attribute
|
||||
char d_buf[SVG_MAX_PATH_LEN];
|
||||
int d_len = svg_get_attr(elem_start, elem_len, " d", d_buf, SVG_MAX_PATH_LEN);
|
||||
if (d_len > 0) {
|
||||
svg_path_to_edges(el, d_buf, d_len, scale_x, scale_y, vb_x, vb_y);
|
||||
}
|
||||
|
||||
p = elem_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for <circle
|
||||
if (remaining > 7 && svg_strncmp(p, "<circle", 7) && (svg_char_is_ws(p[7]) || p[7] == '/')) {
|
||||
const char* elem_start = p;
|
||||
const char* elem_end = p;
|
||||
while (elem_end < end && *elem_end != '>') ++elem_end;
|
||||
if (elem_end < end) ++elem_end;
|
||||
int elem_len = (int)(elem_end - elem_start);
|
||||
|
||||
char attr_buf[32];
|
||||
fixed_t cx = 0, cy = 0, r = 0;
|
||||
if (svg_get_attr(elem_start, elem_len, " cx", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &cx);
|
||||
if (svg_get_attr(elem_start, elem_len, " cy", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &cy);
|
||||
if (svg_get_attr(elem_start, elem_len, " r", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &r);
|
||||
|
||||
// Scale to target coordinates
|
||||
fixed_t scx = fixed_mul(cx - vb_x, scale_x);
|
||||
fixed_t scy = fixed_mul(cy - vb_y, scale_y);
|
||||
fixed_t srx = fixed_mul(r, scale_x);
|
||||
fixed_t sry = fixed_mul(r, scale_y);
|
||||
// Use average of scaled radii
|
||||
fixed_t sr = (srx + sry) >> 1;
|
||||
|
||||
svg_circle_edges(el, scx, scy, sr);
|
||||
|
||||
p = elem_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for <rect
|
||||
if (remaining > 5 && svg_strncmp(p, "<rect", 5) && (svg_char_is_ws(p[5]) || p[5] == '/')) {
|
||||
const char* elem_start = p;
|
||||
const char* elem_end = p;
|
||||
while (elem_end < end && *elem_end != '>') ++elem_end;
|
||||
if (elem_end < end) ++elem_end;
|
||||
int elem_len = (int)(elem_end - elem_start);
|
||||
|
||||
char attr_buf[32];
|
||||
fixed_t rx_val = 0, ry_val = 0, rw = 0, rh = 0, rrx = 0, rry = 0;
|
||||
|
||||
if (svg_get_attr(elem_start, elem_len, " x", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &rx_val);
|
||||
if (svg_get_attr(elem_start, elem_len, " y", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &ry_val);
|
||||
if (svg_get_attr(elem_start, elem_len, " width", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &rw);
|
||||
if (svg_get_attr(elem_start, elem_len, " height", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &rh);
|
||||
if (svg_get_attr(elem_start, elem_len, " rx", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &rrx);
|
||||
if (svg_get_attr(elem_start, elem_len, " ry", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &rry);
|
||||
|
||||
// Scale all to target pixel space
|
||||
fixed_t sx = fixed_mul(rx_val - vb_x, scale_x);
|
||||
fixed_t sy = fixed_mul(ry_val - vb_y, scale_y);
|
||||
fixed_t sw = fixed_mul(rw, scale_x);
|
||||
fixed_t sh = fixed_mul(rh, scale_y);
|
||||
fixed_t srx = fixed_mul(rrx, scale_x);
|
||||
fixed_t sry = fixed_mul(rry, scale_y);
|
||||
|
||||
svg_rect_edges(el, sx, sy, sw, sh, srx, sry);
|
||||
|
||||
p = elem_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
// Rasterize all accumulated edges
|
||||
if (el.count > 0) {
|
||||
svg_rasterize(el, icon.pixels, target_w, target_h, fill_px);
|
||||
}
|
||||
|
||||
zenith::free(el.edges);
|
||||
return icon;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Load SVG from VFS and render
|
||||
// ---------------------------------------------------------------------------
|
||||
inline SvgIcon svg_load(const char* vfs_path, int target_w, int target_h, Color fill_color) {
|
||||
int fd = zenith::open(vfs_path);
|
||||
if (fd < 0) {
|
||||
return {nullptr, 0, 0};
|
||||
}
|
||||
|
||||
uint64_t size = zenith::getsize(fd);
|
||||
if (size == 0 || size > SVG_MAX_FILE_SIZE) {
|
||||
zenith::close(fd);
|
||||
return {nullptr, 0, 0};
|
||||
}
|
||||
|
||||
char* buf = (char*)zenith::alloc(size + 1);
|
||||
zenith::read(fd, (uint8_t*)buf, 0, size);
|
||||
zenith::close(fd);
|
||||
buf[size] = '\0';
|
||||
|
||||
SvgIcon icon = svg_render(buf, (int)size, target_w, target_h, fill_color);
|
||||
zenith::free(buf);
|
||||
return icon;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Free icon pixel data
|
||||
// ---------------------------------------------------------------------------
|
||||
inline void svg_free(SvgIcon& icon) {
|
||||
if (icon.pixels) zenith::free(icon.pixels);
|
||||
icon.pixels = nullptr;
|
||||
icon.width = 0;
|
||||
icon.height = 0;
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* terminal.hpp
|
||||
* ZenithOS terminal emulator with ANSI escape sequence support
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/font.hpp"
|
||||
#include <zenith/syscall.h>
|
||||
#include <zenith/string.h>
|
||||
#include <Api/Syscall.hpp>
|
||||
|
||||
namespace gui {
|
||||
|
||||
struct TermCell {
|
||||
char ch;
|
||||
Color fg;
|
||||
Color bg;
|
||||
};
|
||||
|
||||
static constexpr int TERM_MAX_SCROLLBACK = 500;
|
||||
|
||||
struct TerminalState {
|
||||
TermCell* cells;
|
||||
TermCell* alt_cells; // alternate screen buffer
|
||||
int cols, rows;
|
||||
int cursor_x, cursor_y;
|
||||
int saved_cursor_x, saved_cursor_y; // saved cursor for alternate screen
|
||||
int scroll_top;
|
||||
int total_rows;
|
||||
int child_pid;
|
||||
Color current_fg;
|
||||
Color current_bg;
|
||||
bool cursor_visible;
|
||||
bool alt_screen_active;
|
||||
bool reverse_video;
|
||||
|
||||
enum { STATE_NORMAL, STATE_ESC, STATE_CSI } parse_state;
|
||||
bool csi_private; // true if '?' was seen after CSI
|
||||
int csi_params[8];
|
||||
int csi_param_count;
|
||||
int csi_current_param;
|
||||
};
|
||||
|
||||
// Standard ANSI color palette as ARGB pixels
|
||||
static inline Color term_ansi_color(int idx) {
|
||||
switch (idx) {
|
||||
case 0: return Color::from_hex(0x000000);
|
||||
case 1: return Color::from_hex(0xCC0000);
|
||||
case 2: return Color::from_hex(0x4E9A06);
|
||||
case 3: return Color::from_hex(0xC4A000);
|
||||
case 4: return Color::from_hex(0x3465A4);
|
||||
case 5: return Color::from_hex(0x75507B);
|
||||
case 6: return Color::from_hex(0x06989A);
|
||||
case 7: return Color::from_hex(0xD3D7CF);
|
||||
case 8: return Color::from_hex(0x555753);
|
||||
case 9: return Color::from_hex(0xEF2929);
|
||||
case 10: return Color::from_hex(0x8AE234);
|
||||
case 11: return Color::from_hex(0xFCE94F);
|
||||
case 12: return Color::from_hex(0x729FCF);
|
||||
case 13: return Color::from_hex(0xAD7FA8);
|
||||
case 14: return Color::from_hex(0x34E2E2);
|
||||
case 15: return Color::from_hex(0xEEEEEC);
|
||||
default: return colors::TERM_FG;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_scroll_up(TerminalState* t) {
|
||||
// Move all rows up by one
|
||||
for (int r = 0; r < t->rows - 1; r++) {
|
||||
for (int c = 0; c < t->cols; c++) {
|
||||
t->cells[r * t->cols + c] = t->cells[(r + 1) * t->cols + c];
|
||||
}
|
||||
}
|
||||
// Clear last row
|
||||
int last = t->rows - 1;
|
||||
for (int c = 0; c < t->cols; c++) {
|
||||
t->cells[last * t->cols + c] = {' ', t->current_fg, colors::TERM_BG};
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_init(TerminalState* t, int cols, int rows) {
|
||||
t->cols = cols;
|
||||
t->rows = rows;
|
||||
t->cursor_x = 0;
|
||||
t->cursor_y = 0;
|
||||
t->saved_cursor_x = 0;
|
||||
t->saved_cursor_y = 0;
|
||||
t->scroll_top = 0;
|
||||
t->total_rows = rows;
|
||||
t->current_fg = colors::TERM_FG;
|
||||
t->current_bg = colors::TERM_BG;
|
||||
t->cursor_visible = true;
|
||||
t->alt_screen_active = false;
|
||||
t->reverse_video = false;
|
||||
t->parse_state = TerminalState::STATE_NORMAL;
|
||||
t->csi_private = false;
|
||||
t->csi_param_count = 0;
|
||||
t->csi_current_param = 0;
|
||||
|
||||
int total_cells = cols * rows;
|
||||
t->cells = (TermCell*)zenith::alloc(total_cells * sizeof(TermCell));
|
||||
t->alt_cells = (TermCell*)zenith::alloc(total_cells * sizeof(TermCell));
|
||||
for (int i = 0; i < total_cells; i++) {
|
||||
t->cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
|
||||
t->alt_cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
|
||||
}
|
||||
|
||||
t->child_pid = zenith::spawn_redir("0:/os/shell.elf");
|
||||
zenith::childio_settermsz(t->child_pid, cols, rows);
|
||||
}
|
||||
|
||||
static inline void terminal_put_char(TerminalState* t, char ch) {
|
||||
if (t->cursor_x >= t->cols) {
|
||||
t->cursor_x = 0;
|
||||
t->cursor_y++;
|
||||
}
|
||||
if (t->cursor_y >= t->rows) {
|
||||
terminal_scroll_up(t);
|
||||
t->cursor_y = t->rows - 1;
|
||||
}
|
||||
int idx = t->cursor_y * t->cols + t->cursor_x;
|
||||
t->cells[idx].ch = ch;
|
||||
t->cells[idx].fg = t->current_fg;
|
||||
t->cells[idx].bg = t->current_bg;
|
||||
t->cursor_x++;
|
||||
}
|
||||
|
||||
static inline void terminal_enter_alt_screen(TerminalState* t) {
|
||||
if (t->alt_screen_active) return;
|
||||
t->alt_screen_active = true;
|
||||
// Save cursor
|
||||
t->saved_cursor_x = t->cursor_x;
|
||||
t->saved_cursor_y = t->cursor_y;
|
||||
// Swap buffers: save main screen to alt_cells, clear main
|
||||
int total = t->cols * t->rows;
|
||||
for (int i = 0; i < total; i++) {
|
||||
t->alt_cells[i] = t->cells[i];
|
||||
t->cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
|
||||
}
|
||||
t->cursor_x = 0;
|
||||
t->cursor_y = 0;
|
||||
}
|
||||
|
||||
static inline void terminal_exit_alt_screen(TerminalState* t) {
|
||||
if (!t->alt_screen_active) return;
|
||||
t->alt_screen_active = false;
|
||||
// Restore main screen from alt_cells
|
||||
int total = t->cols * t->rows;
|
||||
for (int i = 0; i < total; i++) {
|
||||
t->cells[i] = t->alt_cells[i];
|
||||
}
|
||||
// Restore cursor
|
||||
t->cursor_x = t->saved_cursor_x;
|
||||
t->cursor_y = t->saved_cursor_y;
|
||||
}
|
||||
|
||||
static inline void terminal_process_private_mode(TerminalState* t, char cmd) {
|
||||
int p0 = t->csi_param_count > 0 ? t->csi_params[0] : 0;
|
||||
|
||||
if (cmd == 'h') {
|
||||
// Set private mode
|
||||
if (p0 == 25) {
|
||||
t->cursor_visible = true;
|
||||
} else if (p0 == 1049) {
|
||||
terminal_enter_alt_screen(t);
|
||||
}
|
||||
} else if (cmd == 'l') {
|
||||
// Reset private mode
|
||||
if (p0 == 25) {
|
||||
t->cursor_visible = false;
|
||||
} else if (p0 == 1049) {
|
||||
terminal_exit_alt_screen(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_process_csi(TerminalState* t, char cmd) {
|
||||
// Finalize current param
|
||||
if (t->csi_param_count < 8) {
|
||||
t->csi_params[t->csi_param_count] = t->csi_current_param;
|
||||
t->csi_param_count++;
|
||||
}
|
||||
|
||||
// Handle private mode sequences (ESC[?...)
|
||||
if (t->csi_private) {
|
||||
terminal_process_private_mode(t, cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
int p0 = t->csi_param_count > 0 ? t->csi_params[0] : 0;
|
||||
int p1 = t->csi_param_count > 1 ? t->csi_params[1] : 0;
|
||||
|
||||
switch (cmd) {
|
||||
case 'H': case 'f': {
|
||||
// Cursor position: ESC[row;colH (1-based)
|
||||
int row = (p0 > 0 ? p0 : 1) - 1;
|
||||
int col = (p1 > 0 ? p1 : 1) - 1;
|
||||
if (row < 0) row = 0;
|
||||
if (row >= t->rows) row = t->rows - 1;
|
||||
if (col < 0) col = 0;
|
||||
if (col >= t->cols) col = t->cols - 1;
|
||||
t->cursor_y = row;
|
||||
t->cursor_x = col;
|
||||
break;
|
||||
}
|
||||
case 'A': {
|
||||
// Cursor up
|
||||
int n = p0 > 0 ? p0 : 1;
|
||||
t->cursor_y -= n;
|
||||
if (t->cursor_y < 0) t->cursor_y = 0;
|
||||
break;
|
||||
}
|
||||
case 'B': {
|
||||
// Cursor down
|
||||
int n = p0 > 0 ? p0 : 1;
|
||||
t->cursor_y += n;
|
||||
if (t->cursor_y >= t->rows) t->cursor_y = t->rows - 1;
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
// Cursor forward
|
||||
int n = p0 > 0 ? p0 : 1;
|
||||
t->cursor_x += n;
|
||||
if (t->cursor_x >= t->cols) t->cursor_x = t->cols - 1;
|
||||
break;
|
||||
}
|
||||
case 'D': {
|
||||
// Cursor backward
|
||||
int n = p0 > 0 ? p0 : 1;
|
||||
t->cursor_x -= n;
|
||||
if (t->cursor_x < 0) t->cursor_x = 0;
|
||||
break;
|
||||
}
|
||||
case 'J': {
|
||||
// Erase in display
|
||||
if (p0 == 0) {
|
||||
// Clear from cursor to end
|
||||
for (int x = t->cursor_x; x < t->cols; x++)
|
||||
t->cells[t->cursor_y * t->cols + x] = {' ', t->current_fg, colors::TERM_BG};
|
||||
for (int r = t->cursor_y + 1; r < t->rows; r++)
|
||||
for (int c = 0; c < t->cols; c++)
|
||||
t->cells[r * t->cols + c] = {' ', t->current_fg, colors::TERM_BG};
|
||||
} else if (p0 == 1) {
|
||||
// Clear from start to cursor
|
||||
for (int r = 0; r < t->cursor_y; r++)
|
||||
for (int c = 0; c < t->cols; c++)
|
||||
t->cells[r * t->cols + c] = {' ', t->current_fg, colors::TERM_BG};
|
||||
for (int x = 0; x <= t->cursor_x; x++)
|
||||
t->cells[t->cursor_y * t->cols + x] = {' ', t->current_fg, colors::TERM_BG};
|
||||
} else if (p0 == 2) {
|
||||
// Clear entire screen
|
||||
for (int i = 0; i < t->rows * t->cols; i++)
|
||||
t->cells[i] = {' ', t->current_fg, colors::TERM_BG};
|
||||
t->cursor_x = 0;
|
||||
t->cursor_y = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'K': {
|
||||
// Erase in line
|
||||
int start = 0, end = t->cols;
|
||||
if (p0 == 0) { start = t->cursor_x; end = t->cols; }
|
||||
else if (p0 == 1) { start = 0; end = t->cursor_x + 1; }
|
||||
else if (p0 == 2) { start = 0; end = t->cols; }
|
||||
for (int x = start; x < end; x++)
|
||||
t->cells[t->cursor_y * t->cols + x] = {' ', t->current_fg, colors::TERM_BG};
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
// SGR - Set Graphics Rendition
|
||||
for (int i = 0; i < t->csi_param_count; i++) {
|
||||
int code = t->csi_params[i];
|
||||
if (code == 0) {
|
||||
t->current_fg = colors::TERM_FG;
|
||||
t->current_bg = colors::TERM_BG;
|
||||
t->reverse_video = false;
|
||||
} else if (code == 1) {
|
||||
// Bold: map to bright version of current color
|
||||
uint8_t r = t->current_fg.r;
|
||||
uint8_t g = t->current_fg.g;
|
||||
uint8_t b = t->current_fg.b;
|
||||
int add = 50;
|
||||
r = (r + add > 255) ? 255 : r + add;
|
||||
g = (g + add > 255) ? 255 : g + add;
|
||||
b = (b + add > 255) ? 255 : b + add;
|
||||
t->current_fg = Color::from_rgb(r, g, b);
|
||||
} else if (code == 2) {
|
||||
// Dim: darken current fg color
|
||||
t->current_fg.r = t->current_fg.r / 2;
|
||||
t->current_fg.g = t->current_fg.g / 2;
|
||||
t->current_fg.b = t->current_fg.b / 2;
|
||||
} else if (code == 7) {
|
||||
// Reverse video
|
||||
if (!t->reverse_video) {
|
||||
t->reverse_video = true;
|
||||
Color tmp = t->current_fg;
|
||||
t->current_fg = t->current_bg;
|
||||
t->current_bg = tmp;
|
||||
}
|
||||
} else if (code == 27) {
|
||||
// Reverse off
|
||||
if (t->reverse_video) {
|
||||
t->reverse_video = false;
|
||||
Color tmp = t->current_fg;
|
||||
t->current_fg = t->current_bg;
|
||||
t->current_bg = tmp;
|
||||
}
|
||||
} else if (code >= 30 && code <= 37) {
|
||||
t->current_fg = term_ansi_color(code - 30);
|
||||
if (t->reverse_video) {
|
||||
// In reverse mode, fg is displayed as bg
|
||||
Color tmp = t->current_fg;
|
||||
t->current_fg = t->current_bg;
|
||||
t->current_bg = tmp;
|
||||
}
|
||||
} else if (code >= 40 && code <= 47) {
|
||||
t->current_bg = term_ansi_color(code - 40);
|
||||
} else if (code >= 90 && code <= 97) {
|
||||
t->current_fg = term_ansi_color(code - 90 + 8);
|
||||
} else if (code >= 100 && code <= 107) {
|
||||
t->current_bg = term_ansi_color(code - 100 + 8);
|
||||
} else if (code == 39) {
|
||||
t->current_fg = colors::TERM_FG;
|
||||
} else if (code == 49) {
|
||||
t->current_bg = colors::TERM_BG;
|
||||
}
|
||||
}
|
||||
if (t->csi_param_count == 0) {
|
||||
// ESC[m with no params = reset
|
||||
t->current_fg = colors::TERM_FG;
|
||||
t->current_bg = colors::TERM_BG;
|
||||
t->reverse_video = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_feed(TerminalState* t, const char* data, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
char ch = data[i];
|
||||
|
||||
switch (t->parse_state) {
|
||||
case TerminalState::STATE_NORMAL:
|
||||
if (ch == '\033') {
|
||||
t->parse_state = TerminalState::STATE_ESC;
|
||||
} else if (ch == '\n') {
|
||||
t->cursor_x = 0; // CR+LF: shell sends \n without \r
|
||||
t->cursor_y++;
|
||||
if (t->cursor_y >= t->rows) {
|
||||
terminal_scroll_up(t);
|
||||
t->cursor_y = t->rows - 1;
|
||||
}
|
||||
} else if (ch == '\r') {
|
||||
t->cursor_x = 0;
|
||||
} else if (ch == '\b') {
|
||||
if (t->cursor_x > 0) t->cursor_x--;
|
||||
} else if (ch == '\t') {
|
||||
int next = (t->cursor_x + 8) & ~7;
|
||||
if (next > t->cols) next = t->cols;
|
||||
while (t->cursor_x < next) {
|
||||
terminal_put_char(t, ' ');
|
||||
}
|
||||
} else if (ch >= 32 || ch < 0) {
|
||||
// Printable character (also treat high-bit chars as printable)
|
||||
terminal_put_char(t, ch);
|
||||
}
|
||||
break;
|
||||
|
||||
case TerminalState::STATE_ESC:
|
||||
if (ch == '[') {
|
||||
t->parse_state = TerminalState::STATE_CSI;
|
||||
t->csi_private = false;
|
||||
t->csi_param_count = 0;
|
||||
t->csi_current_param = 0;
|
||||
for (int j = 0; j < 8; j++) t->csi_params[j] = 0;
|
||||
} else if (ch == 'c') {
|
||||
// Reset terminal
|
||||
t->current_fg = colors::TERM_FG;
|
||||
t->current_bg = colors::TERM_BG;
|
||||
t->cursor_x = 0;
|
||||
t->cursor_y = 0;
|
||||
t->parse_state = TerminalState::STATE_NORMAL;
|
||||
} else {
|
||||
// Unknown ESC sequence, ignore
|
||||
t->parse_state = TerminalState::STATE_NORMAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case TerminalState::STATE_CSI:
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
t->csi_current_param = t->csi_current_param * 10 + (ch - '0');
|
||||
} else if (ch == ';') {
|
||||
if (t->csi_param_count < 8) {
|
||||
t->csi_params[t->csi_param_count] = t->csi_current_param;
|
||||
t->csi_param_count++;
|
||||
}
|
||||
t->csi_current_param = 0;
|
||||
} else if (ch == '?') {
|
||||
t->csi_private = true;
|
||||
} else if (ch >= 0x40 && ch <= 0x7E) {
|
||||
// Final byte - execute command
|
||||
terminal_process_csi(t, ch);
|
||||
t->parse_state = TerminalState::STATE_NORMAL;
|
||||
} else {
|
||||
// Unknown, abort CSI
|
||||
t->parse_state = TerminalState::STATE_NORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, int ph) {
|
||||
// Fill background
|
||||
uint32_t bg_px = colors::TERM_BG.to_pixel();
|
||||
int total = pw * ph;
|
||||
for (int i = 0; i < total; i++) {
|
||||
pixels[i] = bg_px;
|
||||
}
|
||||
|
||||
// Render each visible cell
|
||||
int visible_rows = ph / FONT_HEIGHT;
|
||||
int visible_cols = pw / FONT_WIDTH;
|
||||
if (visible_rows > t->rows) visible_rows = t->rows;
|
||||
if (visible_cols > t->cols) visible_cols = t->cols;
|
||||
|
||||
for (int r = 0; r < visible_rows; r++) {
|
||||
for (int c = 0; c < visible_cols; c++) {
|
||||
int idx = r * t->cols + c;
|
||||
TermCell& cell = t->cells[idx];
|
||||
|
||||
int px = c * FONT_WIDTH;
|
||||
int py = r * FONT_HEIGHT;
|
||||
|
||||
uint32_t cell_bg = cell.bg.to_pixel();
|
||||
uint32_t cell_fg = cell.fg.to_pixel();
|
||||
|
||||
// Draw cell background
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = py + fy;
|
||||
if (dy >= ph) break;
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
int dx = px + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = cell_bg;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw character glyph
|
||||
if (cell.ch > 32 || cell.ch < 0) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)cell.ch * FONT_HEIGHT];
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = py + fy;
|
||||
if (dy >= ph) break;
|
||||
uint8_t bits = glyph[fy];
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
if (bits & (0x80 >> fx)) {
|
||||
int dx = px + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = cell_fg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor
|
||||
if (t->cursor_visible && t->cursor_x < visible_cols && t->cursor_y < visible_rows) {
|
||||
int cx = t->cursor_x * FONT_WIDTH;
|
||||
int cy = t->cursor_y * FONT_HEIGHT;
|
||||
uint32_t cursor_px = colors::WHITE.to_pixel();
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = cy + fy;
|
||||
if (dy >= ph) break;
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
int dx = cx + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = cursor_px;
|
||||
}
|
||||
}
|
||||
// Draw character on top of cursor in black
|
||||
if (t->cursor_y < t->rows && t->cursor_x < t->cols) {
|
||||
int idx = t->cursor_y * t->cols + t->cursor_x;
|
||||
char ch = t->cells[idx].ch;
|
||||
if (ch > 32 || ch < 0) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)ch * FONT_HEIGHT];
|
||||
uint32_t black_px = colors::BLACK.to_pixel();
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = cy + fy;
|
||||
if (dy >= ph) break;
|
||||
uint8_t bits = glyph[fy];
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
if (bits & (0x80 >> fx)) {
|
||||
int dx = cx + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = black_px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_handle_key(TerminalState* t, const Zenith::KeyEvent& key) {
|
||||
if (t->child_pid > 0) {
|
||||
zenith::childio_writekey(t->child_pid, &key);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_poll(TerminalState* t) {
|
||||
if (t->child_pid <= 0) return;
|
||||
char buf[512];
|
||||
int n = zenith::childio_read(t->child_pid, buf, sizeof(buf));
|
||||
if (n > 0) {
|
||||
terminal_feed(t, buf, n);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* widgets.hpp
|
||||
* ZenithOS GUI widget toolkit (Label, Button, TextBox, Scrollbar)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/framebuffer.hpp"
|
||||
#include "gui/font.hpp"
|
||||
#include "gui/draw.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
// ---- Mouse event ----
|
||||
|
||||
struct MouseEvent {
|
||||
int x, y;
|
||||
uint8_t buttons;
|
||||
uint8_t prev_buttons;
|
||||
int32_t scroll;
|
||||
|
||||
bool left_held() const { return buttons & 0x01; }
|
||||
bool right_held() const { return buttons & 0x02; }
|
||||
bool middle_held() const { return buttons & 0x04; }
|
||||
|
||||
bool left_pressed() const { return (buttons & 0x01) && !(prev_buttons & 0x01); }
|
||||
bool left_released() const { return !(buttons & 0x01) && (prev_buttons & 0x01); }
|
||||
bool right_pressed() const { return (buttons & 0x02) && !(prev_buttons & 0x02); }
|
||||
bool right_released() const { return !(buttons & 0x02) && (prev_buttons & 0x02); }
|
||||
};
|
||||
|
||||
// ---- Callback types ----
|
||||
|
||||
using ClickCallback = void (*)(void* userdata);
|
||||
|
||||
// ---- Label ----
|
||||
|
||||
struct Label {
|
||||
int x, y;
|
||||
const char* text;
|
||||
Color color;
|
||||
|
||||
void draw(Framebuffer& fb) const {
|
||||
if (text) {
|
||||
draw_text(fb, x, y, text, color);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---- Button ----
|
||||
|
||||
struct Button {
|
||||
Rect bounds;
|
||||
const char* text;
|
||||
Color bg;
|
||||
Color fg;
|
||||
Color hover_bg;
|
||||
bool hovered;
|
||||
bool pressed;
|
||||
ClickCallback on_click;
|
||||
void* userdata;
|
||||
|
||||
void init(int x, int y, int w, int h, const char* label) {
|
||||
bounds = {x, y, w, h};
|
||||
text = label;
|
||||
bg = colors::ACCENT;
|
||||
fg = colors::WHITE;
|
||||
hover_bg = Color::from_rgb(0x2B, 0x6B, 0xE0);
|
||||
hovered = false;
|
||||
pressed = false;
|
||||
on_click = nullptr;
|
||||
userdata = nullptr;
|
||||
}
|
||||
|
||||
void draw(Framebuffer& fb) const {
|
||||
Color bg_color = hovered ? hover_bg : bg;
|
||||
fill_rounded_rect(fb, bounds.x, bounds.y, bounds.w, bounds.h, 4, bg_color);
|
||||
// Center text
|
||||
int tw = text_width(text);
|
||||
int tx = bounds.x + (bounds.w - tw) / 2;
|
||||
int ty = bounds.y + (bounds.h - FONT_HEIGHT) / 2;
|
||||
draw_text(fb, tx, ty, text, fg);
|
||||
}
|
||||
|
||||
bool handle_mouse(const MouseEvent& ev) {
|
||||
hovered = bounds.contains(ev.x, ev.y);
|
||||
if (hovered && ev.left_pressed()) {
|
||||
pressed = true;
|
||||
}
|
||||
if (pressed && ev.left_released()) {
|
||||
pressed = false;
|
||||
if (hovered && on_click) {
|
||||
on_click(userdata);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!ev.left_held()) {
|
||||
pressed = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// ---- IconButton (for panel/menu items with SVG icon + optional text) ----
|
||||
|
||||
struct IconButton {
|
||||
Rect bounds;
|
||||
const char* text; // optional label (can be nullptr)
|
||||
uint32_t* icon_pixels; // icon pixel data (ARGB)
|
||||
int icon_w, icon_h;
|
||||
Color bg;
|
||||
Color hover_bg;
|
||||
Color text_color;
|
||||
bool hovered;
|
||||
bool pressed;
|
||||
ClickCallback on_click;
|
||||
void* userdata;
|
||||
|
||||
void init(int x, int y, int w, int h) {
|
||||
bounds = {x, y, w, h};
|
||||
text = nullptr;
|
||||
icon_pixels = nullptr;
|
||||
icon_w = 0;
|
||||
icon_h = 0;
|
||||
bg = {0, 0, 0, 0}; // transparent by default
|
||||
hover_bg = colors::MENU_HOVER;
|
||||
text_color = colors::TEXT_COLOR;
|
||||
hovered = false;
|
||||
pressed = false;
|
||||
on_click = nullptr;
|
||||
userdata = nullptr;
|
||||
}
|
||||
|
||||
void draw(Framebuffer& fb) const {
|
||||
if (hovered) {
|
||||
fill_rounded_rect(fb, bounds.x, bounds.y, bounds.w, bounds.h, 3, hover_bg);
|
||||
} else if (bg.a > 0) {
|
||||
fill_rounded_rect(fb, bounds.x, bounds.y, bounds.w, bounds.h, 3, bg);
|
||||
}
|
||||
|
||||
int content_x = bounds.x + 6;
|
||||
|
||||
// Draw icon
|
||||
if (icon_pixels && icon_w > 0 && icon_h > 0) {
|
||||
int iy = bounds.y + (bounds.h - icon_h) / 2;
|
||||
fb.blit_alpha(content_x, iy, icon_w, icon_h, icon_pixels);
|
||||
content_x += icon_w + 6;
|
||||
}
|
||||
|
||||
// Draw text
|
||||
if (text) {
|
||||
int ty = bounds.y + (bounds.h - FONT_HEIGHT) / 2;
|
||||
draw_text(fb, content_x, ty, text, text_color);
|
||||
}
|
||||
}
|
||||
|
||||
bool handle_mouse(const MouseEvent& ev) {
|
||||
hovered = bounds.contains(ev.x, ev.y);
|
||||
if (hovered && ev.left_pressed()) {
|
||||
pressed = true;
|
||||
}
|
||||
if (pressed && ev.left_released()) {
|
||||
pressed = false;
|
||||
if (hovered && on_click) {
|
||||
on_click(userdata);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!ev.left_held()) {
|
||||
pressed = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// ---- TextBox ----
|
||||
|
||||
struct TextBox {
|
||||
Rect bounds;
|
||||
char text[256];
|
||||
int cursor;
|
||||
int text_len;
|
||||
bool focused;
|
||||
Color bg;
|
||||
Color fg;
|
||||
Color border_color;
|
||||
Color cursor_color;
|
||||
|
||||
void init(int x, int y, int w, int h) {
|
||||
bounds = {x, y, w, h};
|
||||
text[0] = '\0';
|
||||
cursor = 0;
|
||||
text_len = 0;
|
||||
focused = false;
|
||||
bg = colors::WHITE;
|
||||
fg = colors::TEXT_COLOR;
|
||||
border_color = colors::BORDER;
|
||||
cursor_color = colors::ACCENT;
|
||||
}
|
||||
|
||||
void draw(Framebuffer& fb) const {
|
||||
fb.fill_rect(bounds.x, bounds.y, bounds.w, bounds.h, bg);
|
||||
draw_rect(fb, bounds.x, bounds.y, bounds.w, bounds.h,
|
||||
focused ? cursor_color : border_color);
|
||||
|
||||
// Draw text with 4px padding
|
||||
int tx = bounds.x + 4;
|
||||
int ty = bounds.y + (bounds.h - FONT_HEIGHT) / 2;
|
||||
draw_text(fb, tx, ty, text, fg);
|
||||
|
||||
// Draw cursor if focused
|
||||
if (focused) {
|
||||
int cx = tx + cursor * FONT_WIDTH;
|
||||
draw_vline(fb, cx, ty, FONT_HEIGHT, cursor_color);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_mouse(const MouseEvent& ev) {
|
||||
if (ev.left_pressed()) {
|
||||
focused = bounds.contains(ev.x, ev.y);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_key(const Zenith::KeyEvent& key) {
|
||||
if (!focused || !key.pressed) return;
|
||||
|
||||
if (key.ascii == '\b' || key.scancode == 0x0E) {
|
||||
// Backspace
|
||||
if (cursor > 0) {
|
||||
for (int i = cursor - 1; i < text_len - 1; i++) {
|
||||
text[i] = text[i + 1];
|
||||
}
|
||||
text_len--;
|
||||
cursor--;
|
||||
text[text_len] = '\0';
|
||||
}
|
||||
} else if (key.ascii >= 32 && key.ascii < 127) {
|
||||
// Printable character
|
||||
if (text_len < 254) {
|
||||
for (int i = text_len; i > cursor; i--) {
|
||||
text[i] = text[i - 1];
|
||||
}
|
||||
text[cursor] = key.ascii;
|
||||
cursor++;
|
||||
text_len++;
|
||||
text[text_len] = '\0';
|
||||
}
|
||||
} else if (key.scancode == 0x4B) {
|
||||
// Left arrow
|
||||
if (cursor > 0) cursor--;
|
||||
} else if (key.scancode == 0x4D) {
|
||||
// Right arrow
|
||||
if (cursor < text_len) cursor++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---- Scrollbar ----
|
||||
|
||||
struct Scrollbar {
|
||||
Rect bounds;
|
||||
int content_height;
|
||||
int view_height;
|
||||
int scroll_offset;
|
||||
bool dragging;
|
||||
int drag_start_y;
|
||||
int drag_start_offset;
|
||||
Color bg;
|
||||
Color fg;
|
||||
Color hover_fg;
|
||||
bool hovered;
|
||||
|
||||
void init(int x, int y, int w, int h) {
|
||||
bounds = {x, y, w, h};
|
||||
content_height = 0;
|
||||
view_height = h;
|
||||
scroll_offset = 0;
|
||||
dragging = false;
|
||||
drag_start_y = 0;
|
||||
drag_start_offset = 0;
|
||||
bg = colors::SCROLLBAR_BG;
|
||||
fg = colors::SCROLLBAR_FG;
|
||||
hover_fg = Color::from_rgb(0xA0, 0xA0, 0xA0);
|
||||
hovered = false;
|
||||
}
|
||||
|
||||
int thumb_height() const {
|
||||
if (content_height <= view_height) return bounds.h;
|
||||
int th = (view_height * bounds.h) / content_height;
|
||||
return th < 20 ? 20 : th;
|
||||
}
|
||||
|
||||
int thumb_y() const {
|
||||
if (content_height <= view_height) return bounds.y;
|
||||
int range = bounds.h - thumb_height();
|
||||
int max_scroll = content_height - view_height;
|
||||
if (max_scroll <= 0) return bounds.y;
|
||||
return bounds.y + (scroll_offset * range) / max_scroll;
|
||||
}
|
||||
|
||||
int max_scroll() const {
|
||||
int ms = content_height - view_height;
|
||||
return ms > 0 ? ms : 0;
|
||||
}
|
||||
|
||||
void draw(Framebuffer& fb) const {
|
||||
if (content_height <= view_height) return; // no scrollbar needed
|
||||
|
||||
fb.fill_rect(bounds.x, bounds.y, bounds.w, bounds.h, bg);
|
||||
int th = thumb_height();
|
||||
int ty = thumb_y();
|
||||
Color thumb_color = (hovered || dragging) ? hover_fg : fg;
|
||||
fill_rounded_rect(fb, bounds.x + 1, ty, bounds.w - 2, th, 3, thumb_color);
|
||||
}
|
||||
|
||||
void handle_mouse(const MouseEvent& ev) {
|
||||
if (content_height <= view_height) return;
|
||||
|
||||
Rect thumb_rect = {bounds.x, thumb_y(), bounds.w, thumb_height()};
|
||||
hovered = thumb_rect.contains(ev.x, ev.y);
|
||||
|
||||
if (hovered && ev.left_pressed()) {
|
||||
dragging = true;
|
||||
drag_start_y = ev.y;
|
||||
drag_start_offset = scroll_offset;
|
||||
}
|
||||
|
||||
if (dragging && ev.left_held()) {
|
||||
int dy = ev.y - drag_start_y;
|
||||
int range = bounds.h - thumb_height();
|
||||
if (range > 0) {
|
||||
int ms = max_scroll();
|
||||
scroll_offset = drag_start_offset + (dy * ms) / range;
|
||||
if (scroll_offset < 0) scroll_offset = 0;
|
||||
if (scroll_offset > ms) scroll_offset = ms;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ev.left_held()) {
|
||||
dragging = false;
|
||||
}
|
||||
|
||||
// Handle scroll wheel
|
||||
if (bounds.contains(ev.x, ev.y) && ev.scroll != 0) {
|
||||
scroll_offset += ev.scroll * 20;
|
||||
int ms = max_scroll();
|
||||
if (scroll_offset < 0) scroll_offset = 0;
|
||||
if (scroll_offset > ms) scroll_offset = ms;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* window.hpp
|
||||
* ZenithOS window management types
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/framebuffer.hpp"
|
||||
#include "gui/widgets.hpp"
|
||||
#include <Api/Syscall.hpp>
|
||||
|
||||
namespace gui {
|
||||
|
||||
enum WindowState { WIN_NORMAL, WIN_MINIMIZED, WIN_MAXIMIZED, WIN_CLOSED };
|
||||
|
||||
static constexpr int TITLEBAR_HEIGHT = 30;
|
||||
static constexpr int BORDER_WIDTH = 1;
|
||||
static constexpr int SHADOW_SIZE = 3;
|
||||
static constexpr int BTN_RADIUS = 6;
|
||||
static constexpr int MAX_TITLE_LEN = 64;
|
||||
|
||||
struct Window;
|
||||
using WindowDrawCallback = void (*)(Window* win, Framebuffer& fb);
|
||||
using WindowMouseCallback = void (*)(Window* win, MouseEvent& ev);
|
||||
using WindowKeyCallback = void (*)(Window* win, const Zenith::KeyEvent& key);
|
||||
using WindowCloseCallback = void (*)(Window* win);
|
||||
|
||||
struct Window {
|
||||
char title[MAX_TITLE_LEN];
|
||||
Rect frame;
|
||||
WindowState state;
|
||||
int z_order;
|
||||
bool focused;
|
||||
bool dirty;
|
||||
|
||||
uint32_t* content;
|
||||
int content_w, content_h;
|
||||
|
||||
bool dragging;
|
||||
int drag_offset_x, drag_offset_y;
|
||||
|
||||
bool resizing;
|
||||
|
||||
Rect saved_frame;
|
||||
|
||||
WindowDrawCallback on_draw;
|
||||
WindowMouseCallback on_mouse;
|
||||
WindowKeyCallback on_key;
|
||||
WindowCloseCallback on_close;
|
||||
void* app_data;
|
||||
|
||||
Rect titlebar_rect() const {
|
||||
return {frame.x, frame.y, frame.w, TITLEBAR_HEIGHT};
|
||||
}
|
||||
|
||||
Rect content_rect() const {
|
||||
return {frame.x + BORDER_WIDTH, frame.y + TITLEBAR_HEIGHT,
|
||||
frame.w - 2 * BORDER_WIDTH, frame.h - TITLEBAR_HEIGHT - BORDER_WIDTH};
|
||||
}
|
||||
|
||||
Rect close_btn_rect() const {
|
||||
int by = frame.y + (TITLEBAR_HEIGHT - BTN_RADIUS * 2) / 2;
|
||||
return {frame.x + 12, by, BTN_RADIUS * 2, BTN_RADIUS * 2};
|
||||
}
|
||||
|
||||
Rect min_btn_rect() const {
|
||||
int by = frame.y + (TITLEBAR_HEIGHT - BTN_RADIUS * 2) / 2;
|
||||
return {frame.x + 12 + 22, by, BTN_RADIUS * 2, BTN_RADIUS * 2};
|
||||
}
|
||||
|
||||
Rect max_btn_rect() const {
|
||||
int by = frame.y + (TITLEBAR_HEIGHT - BTN_RADIUS * 2) / 2;
|
||||
return {frame.x + 12 + 44, by, BTN_RADIUS * 2, BTN_RADIUS * 2};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
@@ -256,4 +256,27 @@ namespace zenith {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
// Mouse
|
||||
inline void mouse_state(Zenith::MouseState* out) { syscall1(Zenith::SYS_MOUSESTATE, (uint64_t)out); }
|
||||
inline void set_mouse_bounds(int32_t maxX, int32_t maxY) {
|
||||
syscall2(Zenith::SYS_SETMOUSEBOUNDS, (uint64_t)maxX, (uint64_t)maxY);
|
||||
}
|
||||
|
||||
// I/O redirection
|
||||
inline int spawn_redir(const char* path, const char* args = nullptr) {
|
||||
return (int)syscall2(Zenith::SYS_SPAWN_REDIR, (uint64_t)path, (uint64_t)args);
|
||||
}
|
||||
inline int childio_read(int childPid, char* buf, int maxLen) {
|
||||
return (int)syscall3(Zenith::SYS_CHILDIO_READ, (uint64_t)childPid, (uint64_t)buf, (uint64_t)maxLen);
|
||||
}
|
||||
inline int childio_write(int childPid, const char* data, int len) {
|
||||
return (int)syscall3(Zenith::SYS_CHILDIO_WRITE, (uint64_t)childPid, (uint64_t)data, (uint64_t)len);
|
||||
}
|
||||
inline int childio_writekey(int childPid, const Zenith::KeyEvent* key) {
|
||||
return (int)syscall2(Zenith::SYS_CHILDIO_WRITEKEY, (uint64_t)childPid, (uint64_t)key);
|
||||
}
|
||||
inline int childio_settermsz(int childPid, int cols, int rows) {
|
||||
return (int)syscall3(Zenith::SYS_CHILDIO_SETTERMSZ, (uint64_t)childPid, (uint64_t)cols, (uint64_t)rows);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,86 @@
|
||||
# Makefile for desktop (GUI desktop environment) on ZenithOS
|
||||
# Copyright (c) 2025 Daniel Hammer
|
||||
|
||||
MAKEFLAGS += -rR
|
||||
.SUFFIXES:
|
||||
|
||||
# ---- Toolchain ----
|
||||
|
||||
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
||||
ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
|
||||
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||
else
|
||||
CXX := g++
|
||||
endif
|
||||
|
||||
# ---- Paths ----
|
||||
|
||||
PROG_INC := ../../include
|
||||
LINK_LD := ../../link.ld
|
||||
BINDIR := ../../bin
|
||||
OBJDIR := obj
|
||||
|
||||
# ---- Compiler flags ----
|
||||
|
||||
CXXFLAGS := \
|
||||
-std=gnu++20 \
|
||||
-g -O2 -pipe \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-unused-function \
|
||||
-nostdinc \
|
||||
-ffreestanding \
|
||||
-fno-stack-protector \
|
||||
-fno-stack-check \
|
||||
-fno-PIC \
|
||||
-fno-rtti \
|
||||
-fno-exceptions \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-m64 \
|
||||
-march=x86-64 \
|
||||
-mno-80387 \
|
||||
-mno-mmx \
|
||||
-mno-sse \
|
||||
-mno-sse2 \
|
||||
-mno-red-zone \
|
||||
-mcmodel=small \
|
||||
-I $(PROG_INC) \
|
||||
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
|
||||
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
|
||||
|
||||
# ---- Linker flags ----
|
||||
|
||||
LDFLAGS := \
|
||||
-nostdlib \
|
||||
-static \
|
||||
-Wl,--build-id=none \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,-m,elf_x86_64 \
|
||||
-z max-page-size=0x1000 \
|
||||
-T $(LINK_LD)
|
||||
|
||||
# ---- Source files ----
|
||||
|
||||
SRCS := main.cpp font_data.cpp
|
||||
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
|
||||
|
||||
# ---- Target ----
|
||||
|
||||
TARGET := $(BINDIR)/os/desktop.elf
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) $(LINK_LD) Makefile
|
||||
mkdir -p $(BINDIR)/os
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp Makefile
|
||||
mkdir -p $(OBJDIR)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJDIR) $(TARGET)
|
||||
@@ -0,0 +1,783 @@
|
||||
/*
|
||||
* font_data.cpp
|
||||
* Standard VGA 8x16 bitmap font (Code Page 437)
|
||||
* 256 characters, 16 bytes each (8 pixels wide, 1 bit per pixel, MSB left)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "gui/font.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
const uint8_t font_data[256 * 16] = {
|
||||
// Character 0 (NUL)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 1 (smiley face)
|
||||
0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD,
|
||||
0x99, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 2 (inverse smiley)
|
||||
0x00, 0x00, 0x7E, 0xFF, 0xDB, 0xFF, 0xFF, 0xC3,
|
||||
0xE7, 0xFF, 0xFF, 0x7E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 3 (heart)
|
||||
0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xFE, 0xFE,
|
||||
0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 4 (diamond)
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0xFE,
|
||||
0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 5 (club)
|
||||
0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0xE7, 0xE7,
|
||||
0xE7, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 6 (spade)
|
||||
0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF,
|
||||
0x7E, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 7 (bullet)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C,
|
||||
0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 8 (inverse bullet)
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC3,
|
||||
0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
// Character 9 (circle)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x42,
|
||||
0x42, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 10 (inverse circle)
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x99, 0xBD,
|
||||
0xBD, 0x99, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
// Character 11 (male sign)
|
||||
0x00, 0x00, 0x1E, 0x0E, 0x1A, 0x32, 0x78, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 12 (female sign)
|
||||
0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C,
|
||||
0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 13 (note)
|
||||
0x00, 0x00, 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x30,
|
||||
0x30, 0x70, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 14 (double note)
|
||||
0x00, 0x00, 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x63,
|
||||
0x63, 0x67, 0xE7, 0xE6, 0xC0, 0x00, 0x00, 0x00,
|
||||
// Character 15 (sun)
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C, 0xE7,
|
||||
0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 16 (right triangle)
|
||||
0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, 0xF8,
|
||||
0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 17 (left triangle)
|
||||
0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0xFE, 0x3E,
|
||||
0x1E, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 18 (up-down arrow)
|
||||
0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18,
|
||||
0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 19 (double exclamation)
|
||||
0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 20 (paragraph)
|
||||
0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B,
|
||||
0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 21 (section)
|
||||
0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6,
|
||||
0x6C, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00,
|
||||
// Character 22 (thick underscore)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFE, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 23 (up-down arrow underlined)
|
||||
0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18,
|
||||
0x7E, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 24 (up arrow)
|
||||
0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 25 (down arrow)
|
||||
0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 26 (right arrow)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0xFE,
|
||||
0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 27 (left arrow)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xFE,
|
||||
0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 28 (right angle)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0,
|
||||
0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 29 (left-right arrow)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xFF,
|
||||
0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 30 (up triangle)
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7C,
|
||||
0x7C, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 31 (down triangle)
|
||||
0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x7C, 0x7C,
|
||||
0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 32 (space)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 33 '!'
|
||||
0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18,
|
||||
0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 34 '"'
|
||||
0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 35 '#'
|
||||
0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C,
|
||||
0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 36 '$'
|
||||
0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06,
|
||||
0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00,
|
||||
// Character 37 '%'
|
||||
0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18,
|
||||
0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 38 '&'
|
||||
0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 39 '''
|
||||
0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 40 '('
|
||||
0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 41 ')'
|
||||
0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C,
|
||||
0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 42 '*'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3C, 0xFF,
|
||||
0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 43 '+'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E,
|
||||
0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 44 ','
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
|
||||
// Character 45 '-'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 46 '.'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 47 '/'
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18,
|
||||
0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 48 '0'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xCE, 0xDE, 0xF6,
|
||||
0xE6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 49 '1'
|
||||
0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 50 '2'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30,
|
||||
0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 51 '3'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06,
|
||||
0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 52 '4'
|
||||
0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE,
|
||||
0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 53 '5'
|
||||
0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06,
|
||||
0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 54 '6'
|
||||
0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 55 '7'
|
||||
0x00, 0x00, 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18,
|
||||
0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 56 '8'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 57 '9'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06,
|
||||
0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 58 ':'
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
|
||||
0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 59 ';'
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
|
||||
0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 60 '<'
|
||||
0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60,
|
||||
0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 61 '='
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00,
|
||||
0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 62 '>'
|
||||
0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06,
|
||||
0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 63 '?'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18,
|
||||
0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 64 '@'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xDE, 0xDE,
|
||||
0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 65 'A'
|
||||
0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE,
|
||||
0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 66 'B'
|
||||
0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66,
|
||||
0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 67 'C'
|
||||
0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0,
|
||||
0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 68 'D'
|
||||
0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 69 'E'
|
||||
0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68,
|
||||
0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 70 'F'
|
||||
0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68,
|
||||
0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 71 'G'
|
||||
0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE,
|
||||
0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 72 'H'
|
||||
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 73 'I'
|
||||
0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 74 'J'
|
||||
0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
|
||||
0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 75 'K'
|
||||
0x00, 0x00, 0xE6, 0x66, 0x66, 0x6C, 0x78, 0x78,
|
||||
0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 76 'L'
|
||||
0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 77 'M'
|
||||
0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 78 'N'
|
||||
0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE,
|
||||
0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 79 'O'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 80 'P'
|
||||
0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60,
|
||||
0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 81 'Q'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00,
|
||||
// Character 82 'R'
|
||||
0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C,
|
||||
0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 83 'S'
|
||||
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C,
|
||||
0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 84 'T'
|
||||
0x00, 0x00, 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 85 'U'
|
||||
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 86 'V'
|
||||
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 87 'W'
|
||||
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6,
|
||||
0xD6, 0xFE, 0xEE, 0x6C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 88 'X'
|
||||
0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x7C, 0x38, 0x38,
|
||||
0x7C, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 89 'Y'
|
||||
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 90 'Z'
|
||||
0x00, 0x00, 0xFE, 0xC6, 0x86, 0x0C, 0x18, 0x30,
|
||||
0x60, 0xC2, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 91 '['
|
||||
0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 92 '\'
|
||||
0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38,
|
||||
0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 93 ']'
|
||||
0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 94 '^'
|
||||
0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 95 '_'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
|
||||
// Character 96 '`'
|
||||
0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 97 'a'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 98 'b'
|
||||
0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 99 'c'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0,
|
||||
0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 100 'd'
|
||||
0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 101 'e'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE,
|
||||
0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 102 'f'
|
||||
0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60,
|
||||
0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 103 'g'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00,
|
||||
// Character 104 'h'
|
||||
0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66,
|
||||
0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 105 'i'
|
||||
0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 106 'j'
|
||||
0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06,
|
||||
0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00,
|
||||
// Character 107 'k'
|
||||
0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78,
|
||||
0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 108 'l'
|
||||
0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 109 'm'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xD6,
|
||||
0xD6, 0xD6, 0xD6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 110 'n'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 111 'o'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 112 'p'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00,
|
||||
// Character 113 'q'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00,
|
||||
// Character 114 'r'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x76, 0x66,
|
||||
0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 115 's'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60,
|
||||
0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 116 't'
|
||||
0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30,
|
||||
0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 117 'u'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 118 'v'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 119 'w'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xD6,
|
||||
0xD6, 0xD6, 0xFE, 0x6C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 120 'x'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38,
|
||||
0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 121 'y'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00,
|
||||
// Character 122 'z'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18,
|
||||
0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 123 '{'
|
||||
0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18,
|
||||
0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 124 '|'
|
||||
0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 125 '}'
|
||||
0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18,
|
||||
0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 126 '~'
|
||||
0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 127 (DEL - block)
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6,
|
||||
0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 128 (C cedilla)
|
||||
0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0,
|
||||
0xC2, 0x66, 0x3C, 0x0C, 0x06, 0x7C, 0x00, 0x00,
|
||||
// Character 129 (u umlaut)
|
||||
0x00, 0x00, 0xCC, 0x00, 0x00, 0xCC, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 130 (e acute)
|
||||
0x00, 0x0C, 0x18, 0x30, 0x00, 0x7C, 0xC6, 0xFE,
|
||||
0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 131 (a circumflex)
|
||||
0x00, 0x10, 0x38, 0x6C, 0x00, 0x78, 0x0C, 0x7C,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 132 (a umlaut)
|
||||
0x00, 0x00, 0xCC, 0x00, 0x00, 0x78, 0x0C, 0x7C,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 133 (a grave)
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0C, 0x7C,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 134 (a ring)
|
||||
0x00, 0x38, 0x6C, 0x38, 0x00, 0x78, 0x0C, 0x7C,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 135 (c cedilla)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0,
|
||||
0xC0, 0xC6, 0x7C, 0x0C, 0x06, 0x7C, 0x00, 0x00,
|
||||
// Character 136 (e circumflex)
|
||||
0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xFE,
|
||||
0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 137 (e umlaut)
|
||||
0x00, 0x00, 0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xFE,
|
||||
0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 138 (e grave)
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xFE,
|
||||
0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 139 (i umlaut)
|
||||
0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 140 (i circumflex)
|
||||
0x00, 0x18, 0x3C, 0x66, 0x00, 0x38, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 141 (i grave)
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 142 (A umlaut)
|
||||
0x00, 0xC6, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6,
|
||||
0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 143 (A ring)
|
||||
0x38, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0xC6, 0xC6,
|
||||
0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 144 (E acute)
|
||||
0x0C, 0x18, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78,
|
||||
0x68, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 145 (ae)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x3B, 0x1B,
|
||||
0x7E, 0xD8, 0xDC, 0x77, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 146 (AE)
|
||||
0x00, 0x00, 0x3E, 0x6C, 0xCC, 0xCC, 0xFE, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 147 (o circumflex)
|
||||
0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 148 (o umlaut)
|
||||
0x00, 0x00, 0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 149 (o grave)
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 150 (u circumflex)
|
||||
0x00, 0x30, 0x78, 0xCC, 0x00, 0xCC, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 151 (u grave)
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0xCC, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 152 (y umlaut)
|
||||
0x00, 0x00, 0xC6, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0x78, 0x00,
|
||||
// Character 153 (O umlaut)
|
||||
0x00, 0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 154 (U umlaut)
|
||||
0x00, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 155 (cent)
|
||||
0x00, 0x18, 0x18, 0x7C, 0xC6, 0xC0, 0xC0, 0xC0,
|
||||
0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 156 (pound)
|
||||
0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60,
|
||||
0x60, 0x60, 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 157 (yen)
|
||||
0x00, 0x00, 0xC6, 0x6C, 0x38, 0x38, 0xFE, 0x38,
|
||||
0xFE, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 158 (Pt)
|
||||
0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC4, 0xCC, 0xDE,
|
||||
0xCC, 0xCC, 0xCC, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 159 (f hook)
|
||||
0x00, 0x0E, 0x1B, 0x18, 0x18, 0x18, 0x7E, 0x18,
|
||||
0x18, 0x18, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 160 (a acute)
|
||||
0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0C, 0x7C,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 161 (i acute)
|
||||
0x00, 0x0C, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 162 (o acute)
|
||||
0x00, 0x18, 0x30, 0x60, 0x00, 0x7C, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 163 (u acute)
|
||||
0x00, 0x18, 0x30, 0x60, 0x00, 0xCC, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 164 (n tilde)
|
||||
0x00, 0x00, 0x76, 0xDC, 0x00, 0xDC, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 165 (N tilde)
|
||||
0x76, 0xDC, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE,
|
||||
0xCE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 166 (feminine ordinal)
|
||||
0x00, 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 167 (masculine ordinal)
|
||||
0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 168 (inverted question)
|
||||
0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60,
|
||||
0xC0, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 169 (not left)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC0,
|
||||
0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 170 (not right)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06,
|
||||
0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 171 (fraction 1/2)
|
||||
0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30,
|
||||
0x60, 0xDC, 0x86, 0x0C, 0x18, 0x3E, 0x00, 0x00,
|
||||
// Character 172 (fraction 1/4)
|
||||
0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30,
|
||||
0x66, 0xCE, 0x9E, 0x3E, 0x06, 0x06, 0x00, 0x00,
|
||||
// Character 173 (inverted exclamation)
|
||||
0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18,
|
||||
0x3C, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 174 (double left angle)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6C, 0xD8,
|
||||
0x6C, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 175 (double right angle)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x6C, 0x36,
|
||||
0x6C, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 176 (light shade)
|
||||
0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
|
||||
0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
|
||||
// Character 177 (medium shade)
|
||||
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
|
||||
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
|
||||
// Character 178 (dark shade)
|
||||
0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77,
|
||||
0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77,
|
||||
// Character 179 (box vertical)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 180 (box vertical-left)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 181 (box double vertical-left single)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 182 (box double vertical-left)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 183 (box double horizontal-down)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 184 (box double vertical-left single)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0xF8,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 185 (box double vertical-left double)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xF6,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 186 (box double vertical)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 187 (box double down-left)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, 0xF6,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 188 (box double up-left)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xFE,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 189 (box double up-right)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFE,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 190 (box horizontal-down)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 191 (box down-left)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 192 (box up-right)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 193 (box horizontal-up)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 194 (box horizontal-down)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 195 (box vertical-right)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 196 (box horizontal)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 197 (box cross)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 198 (box single vert-right double)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 199 (box double vert-right)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 200 (box double up-right)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 201 (box double down-right)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x30, 0x37,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 202 (box double horizontal-up)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 203 (box double horizontal-down)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 204 (box double vertical-right)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 205 (box double horizontal)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 206 (box double cross)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, 0xF7,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 207 (box single horiz-up double)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 208 (box double horizontal-up single)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 209 (box single horiz-down double)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 210 (box double horizontal-down single)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 211 (box double up-right single)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 212 (box single up-right double)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 213 (box single down-right double)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x1F,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 214 (box double down-right single)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 215 (box double cross single)
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
// Character 216 (box single cross double)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0xFF,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 217 (box up-left)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 218 (box down-right)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 219 (full block)
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
// Character 220 (bottom half block)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
// Character 221 (left half block)
|
||||
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
// Character 222 (right half block)
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
// Character 223 (top half block)
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 224 (alpha)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0xD8,
|
||||
0xD8, 0xD8, 0xDC, 0x76, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 225 (beta/sharp s)
|
||||
0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xD8, 0xCC,
|
||||
0xC6, 0xC6, 0xC6, 0xCC, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 226 (gamma)
|
||||
0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC0, 0xC0, 0xC0,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 227 (pi)
|
||||
0x00, 0x00, 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C,
|
||||
0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 228 (sigma uppercase)
|
||||
0x00, 0x00, 0x00, 0xFE, 0xC6, 0x60, 0x30, 0x18,
|
||||
0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 229 (sigma lowercase)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xD8, 0xD8,
|
||||
0xD8, 0xD8, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 230 (mu)
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x7C, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00,
|
||||
// Character 231 (tau)
|
||||
0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 232 (phi uppercase)
|
||||
0x00, 0x00, 0x00, 0x7E, 0x18, 0x3C, 0x66, 0x66,
|
||||
0x66, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 233 (theta)
|
||||
0x00, 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xFE,
|
||||
0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 234 (omega)
|
||||
0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0x6C, 0x6C, 0x6C, 0xEE, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 235 (delta)
|
||||
0x00, 0x00, 0x1E, 0x30, 0x18, 0x0C, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 236 (infinity)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDB, 0xDB,
|
||||
0xDB, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 237 (phi lowercase)
|
||||
0x00, 0x00, 0x00, 0x02, 0x06, 0x7C, 0xCE, 0xDE,
|
||||
0xF6, 0xE6, 0x7C, 0xC0, 0x80, 0x00, 0x00, 0x00,
|
||||
// Character 238 (epsilon)
|
||||
0x00, 0x00, 0x1C, 0x30, 0x60, 0x60, 0x7C, 0x60,
|
||||
0x60, 0x60, 0x30, 0x1C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 239 (intersection)
|
||||
0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6,
|
||||
0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 240 (triple bar)
|
||||
0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE,
|
||||
0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 241 (plus-minus)
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18,
|
||||
0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 242 (greater-equal)
|
||||
0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, 0x0C,
|
||||
0x18, 0x30, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 243 (less-equal)
|
||||
0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x60, 0x30,
|
||||
0x18, 0x0C, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 244 (top integral)
|
||||
0x00, 0x00, 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
// Character 245 (bottom integral)
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0xD8, 0xD8, 0xD8, 0x70, 0x00, 0x00, 0x00,
|
||||
// Character 246 (division)
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7E,
|
||||
0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 247 (approximately equal)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00,
|
||||
0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 248 (degree)
|
||||
0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 249 (bullet operator)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 250 (middle dot)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 251 (square root)
|
||||
0x00, 0x0F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xEC,
|
||||
0x6C, 0x6C, 0x3C, 0x1C, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 252 (superscript n)
|
||||
0x00, 0xD8, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 253 (superscript 2)
|
||||
0x00, 0x70, 0xD8, 0x30, 0x60, 0xC8, 0xF8, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 254 (filled square)
|
||||
0x00, 0x00, 0x00, 0x00, 0x7C, 0x7C, 0x7C, 0x7C,
|
||||
0x7C, 0x7C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Character 255 (non-breaking space)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -153,8 +153,11 @@ extern "C" void _start() {
|
||||
// ---- Stage 1: Network configuration ----
|
||||
run_service("0:/os/dhcp.elf", "dhcp");
|
||||
|
||||
// ---- Stage 2: Interactive shell ----
|
||||
run_service("0:/os/shell.elf", "shell");
|
||||
// ---- Stage 2: Desktop environment (falls back to shell) ----
|
||||
if (!run_service("0:/os/desktop.elf", "desktop")) {
|
||||
log_warn("Desktop failed, falling back to shell");
|
||||
run_service("0:/os/shell.elf", "shell");
|
||||
}
|
||||
|
||||
log_warn("All services exited");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user