212
src/glyphcache.c
Normal file
212
src/glyphcache.c
Normal file
@@ -0,0 +1,212 @@
|
||||
#include <raylib.h>
|
||||
#include "glyphcache.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* Dynamic glyph cache for raylib
|
||||
* (C) - "Raylib's text renderer handles unicode like garbage"
|
||||
* Problem: raylib's LoadFont only loads ~95 ASCII glyphs.
|
||||
* LoadFontEx can load arbitrary codepoints, but you must know
|
||||
* them upfront, and rebuilding every frame is expensive.
|
||||
*
|
||||
* So we should lazily collect codepoints the screen actually uses,
|
||||
* rebuild the font atlas only when new ones appear.
|
||||
* Typically this stabilises after the first few frames.
|
||||
*
|
||||
*/
|
||||
|
||||
#define GC_INITIAL_CAP 4096
|
||||
#define GC_BUCKET_COUNT 8192 // must be power of 2
|
||||
#define GC_BUCKET_MASK (GC_BUCKET_COUNT - 1)
|
||||
|
||||
// Codepoint set (open-addressing hash set)
|
||||
|
||||
typedef struct {
|
||||
int *slots; // 0 = empty sentinel (U+0000 never needed)
|
||||
size_t count;
|
||||
size_t cap; // always a power of 2
|
||||
} CpSet;
|
||||
|
||||
static void cpset_init(CpSet *s) {
|
||||
s->cap = GC_BUCKET_COUNT;
|
||||
s->count = 0;
|
||||
s->slots = calloc(s->cap, sizeof(int));
|
||||
}
|
||||
|
||||
static void cpset_free(CpSet *s) {
|
||||
free(s->slots);
|
||||
s->slots = NULL;
|
||||
s->count = 0;
|
||||
}
|
||||
|
||||
static bool cpset_contains(const CpSet *s, int cp) {
|
||||
size_t idx = (unsigned)cp & (s->cap - 1);
|
||||
for(size_t i = 0; i < s->cap; i++) {
|
||||
size_t j = (idx + i) & (s->cap - 1);
|
||||
if(s->slots[j] == 0) return false;
|
||||
if(s->slots[j] == cp) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void cpset_grow(CpSet *s);
|
||||
|
||||
// Returns true if the codepoint was newly inserted.
|
||||
static bool cpset_insert(CpSet *s, int cp) {
|
||||
if(cp == 0) return false; // sentinel
|
||||
if(cpset_contains(s, cp)) return false;
|
||||
|
||||
if(s->count * 4 >= s->cap * 3) cpset_grow(s);
|
||||
|
||||
size_t idx = (unsigned)cp & (s->cap - 1);
|
||||
for(size_t i = 0; i < s->cap; i++) {
|
||||
size_t j = (idx + i) & (s->cap - 1);
|
||||
if(s->slots[j] == 0) {
|
||||
s->slots[j] = cp;
|
||||
s->count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; // should never happen after grow
|
||||
}
|
||||
|
||||
static void cpset_grow(CpSet *s) {
|
||||
size_t oldCap = s->cap;
|
||||
int *old = s->slots;
|
||||
|
||||
s->cap *= 2;
|
||||
s->slots = calloc(s->cap, sizeof(int));
|
||||
s->count = 0;
|
||||
|
||||
for(size_t i = 0; i < oldCap; i++)
|
||||
if(old[i] != 0) cpset_insert(s, old[i]);
|
||||
|
||||
free(old);
|
||||
}
|
||||
|
||||
// Fill dst (must hold at least s->count ints).
|
||||
static void cpset_collect(const CpSet *s, int *dst) {
|
||||
size_t n = 0;
|
||||
for(size_t i = 0; i < s->cap; i++)
|
||||
if(s->slots[i] != 0) dst[n++] = s->slots[i];
|
||||
}
|
||||
|
||||
// Glyph cache
|
||||
|
||||
struct ncl_GlyphCache {
|
||||
char *fontPath;
|
||||
int fontSize;
|
||||
Font font;
|
||||
bool fontLoaded;
|
||||
bool dirty; // new codepoints since last rebuild
|
||||
CpSet known; // all codepoints we have glyphs for
|
||||
};
|
||||
|
||||
// Pre-seed the most common ranges so the first frame is not barren.
|
||||
static void gc_seed(ncl_GlyphCache *gc) {
|
||||
// ASCII printable
|
||||
for(int i = 0x0020; i <= 0x007E; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// Latin-1 Supplement
|
||||
for(int i = 0x00A0; i <= 0x00FF; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// Cyrillic (common)
|
||||
for(int i = 0x0400; i <= 0x04FF; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// General punctuation
|
||||
for(int i = 0x2010; i <= 0x2027; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// Box drawing
|
||||
for(int i = 0x2500; i <= 0x257F; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// Block elements
|
||||
for(int i = 0x2580; i <= 0x259F; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// Geometric shapes (partial)
|
||||
for(int i = 0x25A0; i <= 0x25FF; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// Braille patterns
|
||||
for(int i = 0x2800; i <= 0x28FF; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
// Powerline / private use (common in OC themes)
|
||||
for(int i = 0xE000; i <= 0xE0FF; i++) cpset_insert(&gc->known, i);
|
||||
|
||||
gc->dirty = true;
|
||||
}
|
||||
|
||||
static void gc_rebuild(ncl_GlyphCache *gc) {
|
||||
if(gc->fontLoaded) UnloadFont(gc->font);
|
||||
|
||||
size_t n = gc->known.count;
|
||||
if(n == 0) { gc->fontLoaded = false; gc->dirty = false; return; }
|
||||
|
||||
int *cps = malloc(sizeof(int) * n);
|
||||
cpset_collect(&gc->known, cps);
|
||||
|
||||
gc->font = LoadFontEx(gc->fontPath, gc->fontSize, cps, (int)n);
|
||||
gc->fontLoaded = true;
|
||||
gc->dirty = false;
|
||||
|
||||
// Let raylib use bilinear for scaled glyphs, nearest for 1:1.
|
||||
SetTextureFilter(gc->font.texture, TEXTURE_FILTER_POINT);
|
||||
|
||||
free(cps);
|
||||
|
||||
fprintf(stderr, "[glyphcache] rebuilt atlas: %zu glyphs, tex %dx%d\n",
|
||||
n, gc->font.texture.width, gc->font.texture.height);
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
ncl_GlyphCache *ncl_createGlyphCache(const char *fontPath, int fontSize) {
|
||||
ncl_GlyphCache *gc = calloc(1, sizeof(*gc));
|
||||
gc->fontPath = strdup(fontPath);
|
||||
gc->fontSize = fontSize;
|
||||
gc->fontLoaded = false;
|
||||
gc->dirty = false;
|
||||
cpset_init(&gc->known);
|
||||
gc_seed(gc);
|
||||
gc_rebuild(gc);
|
||||
return gc;
|
||||
}
|
||||
|
||||
void ncl_destroyGlyphCache(ncl_GlyphCache *gc) {
|
||||
if(!gc) return;
|
||||
if(gc->fontLoaded) UnloadFont(gc->font);
|
||||
cpset_free(&gc->known);
|
||||
free(gc->fontPath);
|
||||
free(gc);
|
||||
}
|
||||
|
||||
Font ncl_getFont(ncl_GlyphCache *gc) {
|
||||
return gc->font;
|
||||
}
|
||||
|
||||
void ncl_flushGlyphs(ncl_GlyphCache *gc) {
|
||||
if(gc->dirty) gc_rebuild(gc);
|
||||
}
|
||||
|
||||
void ncl_needGlyph(ncl_GlyphCache *gc, nn_codepoint cp) {
|
||||
if(cp == 0) return;
|
||||
if(cpset_insert(&gc->known, (int)cp))
|
||||
gc->dirty = true;
|
||||
}
|
||||
|
||||
void ncl_drawGlyph(ncl_GlyphCache *gc, nn_codepoint cp,
|
||||
Vector2 pos, float size, Color tint)
|
||||
{
|
||||
ncl_needGlyph(gc, cp);
|
||||
DrawTextCodepoint(gc->font, (int)cp, pos, size, tint);
|
||||
}
|
||||
|
||||
int ncl_cellWidth(ncl_GlyphCache *gc) {
|
||||
// Measure 'A' as the reference cell.
|
||||
if(!gc->fontLoaded) return 8;
|
||||
return MeasureTextEx(gc->font, "A", (float)gc->fontSize, 0).x;
|
||||
}
|
||||
|
||||
int ncl_cellHeight(ncl_GlyphCache *gc) {
|
||||
return gc->fontSize;
|
||||
}
|
||||
19
src/glyphcache.h
Normal file
19
src/glyphcache.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef NCL_GLYPHCACHE_H
|
||||
#define NCL_GLYPHCACHE_H
|
||||
|
||||
#include <raylib.h>
|
||||
#include "neonucleus.h"
|
||||
|
||||
typedef struct ncl_GlyphCache ncl_GlyphCache;
|
||||
|
||||
ncl_GlyphCache *ncl_createGlyphCache(const char *fontPath, int fontSize);
|
||||
void ncl_destroyGlyphCache(ncl_GlyphCache *gc);
|
||||
Font ncl_getFont(ncl_GlyphCache *gc);
|
||||
void ncl_flushGlyphs(ncl_GlyphCache *gc);
|
||||
void ncl_needGlyph(ncl_GlyphCache *gc, nn_codepoint cp);
|
||||
void ncl_drawGlyph(ncl_GlyphCache *gc, nn_codepoint cp,
|
||||
Vector2 pos, float size, Color tint);
|
||||
int ncl_cellWidth(ncl_GlyphCache *gc);
|
||||
int ncl_cellHeight(ncl_GlyphCache *gc);
|
||||
|
||||
#endif
|
||||
212
src/main.c
212
src/main.c
@@ -323,207 +323,6 @@ double ne_energy_accumulator(void *state, nn_Computer *c, double n) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef NN_WINDOWS
|
||||
// Quick self-tests for Windows-specific fixes
|
||||
// These run before anything else so failures are caught early
|
||||
|
||||
static jmp_buf nn_test_jmpbuf;
|
||||
static volatile int nn_test_caught;
|
||||
|
||||
static void nn_test_crash_handler(int sig) {
|
||||
nn_test_caught = 1;
|
||||
longjmp(nn_test_jmpbuf, 1);
|
||||
}
|
||||
|
||||
static int nn_test_try(void (*func)(void *), void *arg) {
|
||||
nn_test_caught = 0;
|
||||
signal(SIGSEGV, nn_test_crash_handler);
|
||||
signal(SIGABRT, nn_test_crash_handler);
|
||||
if(setjmp(nn_test_jmpbuf) == 0) {
|
||||
func(arg);
|
||||
}
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
return nn_test_caught;
|
||||
}
|
||||
|
||||
static int nn_test_failed = 0;
|
||||
static int nn_test_passed = 0;
|
||||
|
||||
static void nn_test_report(const char *name, int crashed, int expected_crash) {
|
||||
if(crashed && !expected_crash) {
|
||||
printf("[CRASH] %s\n", name);
|
||||
nn_test_failed++;
|
||||
} else if(!crashed && expected_crash) {
|
||||
printf("[FAIL] %s (expected crash)\n", name);
|
||||
nn_test_failed++;
|
||||
} else {
|
||||
printf("[OK] %s\n", name);
|
||||
nn_test_passed++;
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// --- realloc tests ---
|
||||
|
||||
static void nn_test_realloc_null(void *arg) {
|
||||
nn_Context *ctx = arg;
|
||||
void *p = nn_realloc(ctx, NULL, 0, 64);
|
||||
if(p == NULL) { nn_test_failed++; return; }
|
||||
nn_free(ctx, p, 64);
|
||||
}
|
||||
|
||||
static void nn_test_realloc_grow(void *arg) {
|
||||
nn_Context *ctx = arg;
|
||||
void *a = nn_alloc(ctx, 64);
|
||||
if(a == NULL) return;
|
||||
void *b = nn_realloc(ctx, a, 64, 128);
|
||||
if(b != NULL) nn_free(ctx, b, 128);
|
||||
else nn_free(ctx, a, 64);
|
||||
}
|
||||
|
||||
static void nn_test_realloc_free(void *arg) {
|
||||
nn_Context *ctx = arg;
|
||||
void *c = nn_alloc(ctx, 64);
|
||||
if(c == NULL) return;
|
||||
nn_realloc(ctx, c, 64, 0);
|
||||
}
|
||||
|
||||
// --- lock tests ---
|
||||
|
||||
static void nn_test_lock_create_destroy(void *arg) {
|
||||
nn_Context *ctx = arg;
|
||||
nn_Lock *lock = nn_createLock(ctx);
|
||||
if(lock == NULL) { nn_test_failed++; return; }
|
||||
nn_destroyLock(ctx, lock);
|
||||
}
|
||||
|
||||
static void nn_test_lock_cycle(void *arg) {
|
||||
nn_Context *ctx = arg;
|
||||
nn_Lock *lock = nn_createLock(ctx);
|
||||
if(lock == NULL) { nn_test_failed++; return; }
|
||||
// lock and unlock 100 times to stress it
|
||||
for(int i = 0; i < 100; i++) {
|
||||
nn_lock(ctx, lock);
|
||||
nn_unlock(ctx, lock);
|
||||
}
|
||||
nn_destroyLock(ctx, lock);
|
||||
}
|
||||
|
||||
static void nn_test_lock_two(void *arg) {
|
||||
nn_Context *ctx = arg;
|
||||
// two locks at the same time, make sure they dont interfere
|
||||
nn_Lock *a = nn_createLock(ctx);
|
||||
nn_Lock *b = nn_createLock(ctx);
|
||||
if(a == NULL || b == NULL) { nn_test_failed++; return; }
|
||||
nn_lock(ctx, a);
|
||||
nn_lock(ctx, b);
|
||||
nn_unlock(ctx, b);
|
||||
nn_unlock(ctx, a);
|
||||
nn_destroyLock(ctx, a);
|
||||
nn_destroyLock(ctx, b);
|
||||
}
|
||||
|
||||
// --- VFS tests ---
|
||||
|
||||
static void nn_test_vfs_stat(void *arg) {
|
||||
(void)arg;
|
||||
// stat current directory, should always work
|
||||
ncl_Stat s;
|
||||
bool ok = ncl_stat(ncl_defaultFS, ".", &s);
|
||||
if(!ok) nn_test_failed++;
|
||||
if(!s.isDirectory) nn_test_failed++;
|
||||
}
|
||||
|
||||
static void nn_test_vfs_dir(void *arg) {
|
||||
(void)arg;
|
||||
void *dir = ncl_opendir(ncl_defaultFS, ".");
|
||||
if(dir == NULL) { nn_test_failed++; return; }
|
||||
char name[NN_MAX_PATH];
|
||||
// just read one entry, dont care what it is
|
||||
ncl_readdir(ncl_defaultFS, dir, name);
|
||||
ncl_closedir(ncl_defaultFS, dir);
|
||||
}
|
||||
|
||||
static void nn_test_vfs_mkdir_remove(void *arg) {
|
||||
(void)arg;
|
||||
const char *testdir = "nn_test_tmpdir";
|
||||
ncl_mkdir(ncl_defaultFS, testdir);
|
||||
ncl_Stat s;
|
||||
bool ok = ncl_stat(ncl_defaultFS, testdir, &s);
|
||||
if(!ok || !s.isDirectory) nn_test_failed++;
|
||||
ncl_remove(ncl_defaultFS, testdir);
|
||||
// should be gone now
|
||||
ok = ncl_stat(ncl_defaultFS, testdir, &s);
|
||||
if(ok) nn_test_failed++;
|
||||
}
|
||||
|
||||
static void nn_test_vfs_seek(void *arg) {
|
||||
(void)arg;
|
||||
// write a small file, seek around, read back
|
||||
const char *path = "nn_test_seekfile";
|
||||
const char *data = "abcdefghij";
|
||||
void *f = ncl_openfile(ncl_defaultFS, path, "w");
|
||||
if(f == NULL) { nn_test_failed++; return; }
|
||||
ncl_writefile(ncl_defaultFS, f, data, 10);
|
||||
ncl_closefile(ncl_defaultFS, f);
|
||||
|
||||
f = ncl_openfile(ncl_defaultFS, path, "r");
|
||||
if(f == NULL) { nn_test_failed++; ncl_remove(ncl_defaultFS, path); return; }
|
||||
// seek to offset 5 from start
|
||||
int off = 5;
|
||||
bool ok = ncl_seekfile(ncl_defaultFS, f, NN_SEEK_SET, &off);
|
||||
if(!ok || off != 5) nn_test_failed++;
|
||||
// read from there
|
||||
char buf[5];
|
||||
size_t len = 5;
|
||||
ok = ncl_readfile(ncl_defaultFS, f, buf, &len);
|
||||
if(!ok || len != 5) nn_test_failed++;
|
||||
// should be "fghij"
|
||||
if(buf[0] != 'f' || buf[4] != 'j') nn_test_failed++;
|
||||
ncl_closefile(ncl_defaultFS, f);
|
||||
ncl_remove(ncl_defaultFS, path);
|
||||
}
|
||||
|
||||
static void nn_run_selftests(nn_Context *ctx) {
|
||||
printf("--- nn self tests ---\n");
|
||||
fflush(stdout);
|
||||
|
||||
nn_test_report("realloc(NULL)",
|
||||
nn_test_try(nn_test_realloc_null, ctx), 0);
|
||||
nn_test_report("realloc(ptr, grow)",
|
||||
nn_test_try(nn_test_realloc_grow, ctx), 0);
|
||||
nn_test_report("realloc(ptr, free)",
|
||||
nn_test_try(nn_test_realloc_free, ctx), 0);
|
||||
|
||||
nn_test_report("lock create/destroy",
|
||||
nn_test_try(nn_test_lock_create_destroy, ctx), 0);
|
||||
nn_test_report("lock 100 cycles",
|
||||
nn_test_try(nn_test_lock_cycle, ctx), 0);
|
||||
nn_test_report("two locks interleaved",
|
||||
nn_test_try(nn_test_lock_two, ctx), 0);
|
||||
|
||||
nn_test_report("vfs stat cwd",
|
||||
nn_test_try(nn_test_vfs_stat, NULL), 0);
|
||||
nn_test_report("vfs readdir cwd",
|
||||
nn_test_try(nn_test_vfs_dir, NULL), 0);
|
||||
nn_test_report("vfs mkdir/remove",
|
||||
nn_test_try(nn_test_vfs_mkdir_remove, NULL), 0);
|
||||
nn_test_report("vfs seek",
|
||||
nn_test_try(nn_test_vfs_seek, NULL), 0);
|
||||
|
||||
printf("--- %d passed, %d failed ---\n\n", nn_test_passed, nn_test_failed);
|
||||
fflush(stdout);
|
||||
|
||||
if(nn_test_failed > 0) {
|
||||
printf("self tests failed, aborting\n");
|
||||
fflush(stdout);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *player = getenv("USER");
|
||||
#ifdef NN_WINDOWS
|
||||
@@ -540,9 +339,7 @@ int main(int argc, char **argv) {
|
||||
nn_Context ctx;
|
||||
nn_initContext(&ctx);
|
||||
nn_initPalettes();
|
||||
#ifdef NN_WINDOWS
|
||||
nn_run_selftests(&ctx);
|
||||
#endif
|
||||
|
||||
ne_memSand sand;
|
||||
sand.buf = NULL;
|
||||
|
||||
@@ -608,7 +405,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[3]);
|
||||
nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[3]);
|
||||
nn_Component *keyboard = nn_createComponent(
|
||||
u, "mainKB", "keyboard");
|
||||
|
||||
ncl_ScreenState *scrstate = nn_getComponentState(screen);
|
||||
ncl_mountKeyboard(scrstate, "mainKB");
|
||||
{
|
||||
// draw test
|
||||
const char *s = "hello there";
|
||||
@@ -638,7 +439,7 @@ restart:;
|
||||
nn_mountComponent(c, eepromCard, 0);
|
||||
nn_mountComponent(c, managedfs, 1);
|
||||
nn_mountComponent(c, gpuCard, 2);
|
||||
|
||||
nn_mountComponent(c, keyboard, -1);
|
||||
while(true) {
|
||||
if(WindowShouldClose()) break;
|
||||
|
||||
@@ -773,6 +574,7 @@ cleanup:;
|
||||
nn_dropComponent(managedfs);
|
||||
nn_dropComponent(screen);
|
||||
nn_dropComponent(gpuCard);
|
||||
nn_dropComponent(keyboard);
|
||||
// rip the universe
|
||||
nn_destroyUniverse(u);
|
||||
UnloadFont(font);
|
||||
|
||||
973
src/ncomplib.c
973
src/ncomplib.c
File diff suppressed because it is too large
Load Diff
985
src/neonucleus.c
985
src/neonucleus.c
File diff suppressed because it is too large
Load Diff
173
src/neonucleus.h
173
src/neonucleus.h
@@ -1229,24 +1229,8 @@ typedef struct nn_ScreenConfig {
|
||||
// OC has 3 tiers, NN adds a 4th one as well.
|
||||
extern const nn_ScreenConfig nn_defaultScreens[4];
|
||||
|
||||
typedef enum nn_ScreenAction {
|
||||
NN_SCREEN_DROP,
|
||||
} nn_ScreenAction;
|
||||
|
||||
typedef struct nn_ScreenRequest {
|
||||
nn_Context *ctx;
|
||||
nn_Computer *computer;
|
||||
void *state;
|
||||
const nn_ScreenConfig *screen;
|
||||
nn_ScreenAction action;
|
||||
} nn_ScreenRequest;
|
||||
|
||||
typedef nn_Exit (nn_ScreenHandler)(nn_ScreenRequest *req);
|
||||
|
||||
nn_Component *nn_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *scrconf, void *state, nn_ScreenHandler *handler);
|
||||
|
||||
// GPU class
|
||||
|
||||
typedef struct nn_GPU {
|
||||
// the minimum between these and the screen's
|
||||
// are the maximum width/height/depth supported.
|
||||
@@ -1276,6 +1260,35 @@ extern const nn_GPU nn_defaultGPUs[4];
|
||||
|
||||
typedef enum nn_GPUAction {
|
||||
NN_GPU_DROP,
|
||||
NN_GPU_BIND,
|
||||
NN_GPU_GETSCREEN,
|
||||
NN_GPU_GETBG,
|
||||
NN_GPU_SETBG,
|
||||
NN_GPU_GETFG,
|
||||
NN_GPU_SETFG,
|
||||
NN_GPU_GETPALETTE,
|
||||
NN_GPU_SETPALETTE,
|
||||
NN_GPU_MAXDEPTH,
|
||||
NN_GPU_GETDEPTH,
|
||||
NN_GPU_SETDEPTH,
|
||||
NN_GPU_MAXRES,
|
||||
NN_GPU_GETRES,
|
||||
NN_GPU_SETRES,
|
||||
NN_GPU_GETVIEWPORT,
|
||||
NN_GPU_SETVIEWPORT,
|
||||
NN_GPU_GET,
|
||||
NN_GPU_SET,
|
||||
NN_GPU_COPY,
|
||||
NN_GPU_FILL,
|
||||
NN_GPU_GETACTIVEBUF,
|
||||
NN_GPU_SETACTIVEBUF,
|
||||
NN_GPU_BUFFERS,
|
||||
NN_GPU_ALLOCBUF,
|
||||
NN_GPU_FREEBUF,
|
||||
NN_GPU_FREEALLBUFS,
|
||||
NN_GPU_FREEMEM,
|
||||
NN_GPU_GETBUFSIZE,
|
||||
NN_GPU_BITBLT,
|
||||
} nn_GPUAction;
|
||||
|
||||
typedef struct nn_GPURequest {
|
||||
@@ -1284,11 +1297,137 @@ typedef struct nn_GPURequest {
|
||||
void *state;
|
||||
const nn_GPU *gpu;
|
||||
nn_GPUAction action;
|
||||
union {
|
||||
struct {
|
||||
const char *address;
|
||||
bool reset;
|
||||
} bind;
|
||||
// GETSCREEN result
|
||||
char screenAddr[NN_MAX_ADDRESS];
|
||||
// GET/SET BG/FG
|
||||
struct {
|
||||
int color;
|
||||
bool isPalette;
|
||||
int oldColor;
|
||||
bool wasPalette;
|
||||
int oldPaletteIdx; // -1 if none
|
||||
} color;
|
||||
// GET/SET PALETTE
|
||||
struct {
|
||||
int index;
|
||||
int color;
|
||||
int oldColor;
|
||||
} palette;
|
||||
// MAXDEPTH / GETDEPTH / SETDEPTH
|
||||
struct {
|
||||
char depth;
|
||||
char oldDepth;
|
||||
} depth;
|
||||
// MAXRES/GETRES/SETRES/GETVIEWPORT/SETVIEWPORT
|
||||
struct {
|
||||
int width;
|
||||
int height;
|
||||
} resolution;
|
||||
// GET pixel
|
||||
struct {
|
||||
int x, y;
|
||||
nn_codepoint codepoint;
|
||||
int fg, bg;
|
||||
int fgIdx, bgIdx; // -1 if not palette
|
||||
} get;
|
||||
// SET string
|
||||
struct {
|
||||
int x, y;
|
||||
const char *value;
|
||||
size_t len;
|
||||
bool vertical;
|
||||
} set;
|
||||
// COPY
|
||||
struct {
|
||||
int x, y, w, h, tx, ty;
|
||||
} copy;
|
||||
// FILL
|
||||
struct {
|
||||
int x, y, w, h;
|
||||
nn_codepoint codepoint;
|
||||
} fill;
|
||||
// GET/SET ACTIVE BUFFER, FREE BUFFER
|
||||
struct {
|
||||
int index;
|
||||
} buffer;
|
||||
// ALLOCATE BUFFER
|
||||
struct {
|
||||
int w, h, index;
|
||||
} allocBuf;
|
||||
// TOTALMEM / FREEMEM
|
||||
size_t memory;
|
||||
// GETBUFSIZE
|
||||
struct {
|
||||
int index, w, h;
|
||||
} bufSize;
|
||||
// BITBLT
|
||||
struct {
|
||||
int dst, col, row, w, h;
|
||||
int src, fromCol, fromRow;
|
||||
} bitblt;
|
||||
// BUFFERS / count returned here, indices
|
||||
// pushed on stack by handler
|
||||
size_t bufCount;
|
||||
};
|
||||
} nn_GPURequest;
|
||||
|
||||
typedef nn_Exit (nn_GPUHandler)(nn_GPURequest *req);
|
||||
|
||||
nn_Component *nn_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu, void *state, nn_GPUHandler *handler);
|
||||
nn_Component *nn_createGPU(
|
||||
nn_Universe *universe, const char *address,
|
||||
const nn_GPU *gpu, void *state,
|
||||
nn_GPUHandler *handler);
|
||||
|
||||
typedef enum nn_ScreenAction {
|
||||
NN_SCREEN_DROP,
|
||||
NN_SCREEN_ISON,
|
||||
NN_SCREEN_TURNON,
|
||||
NN_SCREEN_TURNOFF,
|
||||
NN_SCREEN_GETASPECTRATIO,
|
||||
NN_SCREEN_GETKEYBOARDS,
|
||||
NN_SCREEN_SETPRECISE,
|
||||
NN_SCREEN_ISPRECISE,
|
||||
NN_SCREEN_SETTOUCHINVERTED,
|
||||
NN_SCREEN_ISTOUCHINVERTED,
|
||||
} nn_ScreenAction;
|
||||
|
||||
typedef struct nn_ScreenRequest {
|
||||
nn_Context *ctx;
|
||||
nn_Computer *computer;
|
||||
void *state;
|
||||
const nn_ScreenConfig *screen;
|
||||
nn_ScreenAction action;
|
||||
union {
|
||||
// turnOn / turnOff / isOn
|
||||
struct {
|
||||
bool wasOn;
|
||||
bool isOn;
|
||||
} power;
|
||||
// getAspectRatio
|
||||
struct {
|
||||
int w, h;
|
||||
} aspect;
|
||||
// getKeyboards — addresses pushed on stack by
|
||||
// handler; count returned here
|
||||
size_t kbCount;
|
||||
// setPrecise / isPrecise /
|
||||
// setTouchModeInverted / isTouchModeInverted
|
||||
bool flag;
|
||||
};
|
||||
} nn_ScreenRequest;
|
||||
|
||||
typedef nn_Exit (nn_ScreenHandler)(nn_ScreenRequest *req);
|
||||
|
||||
nn_Component *nn_createScreen(
|
||||
nn_Universe *universe, const char *address,
|
||||
const nn_ScreenConfig *scrconf, void *state,
|
||||
nn_ScreenHandler *handler
|
||||
);
|
||||
|
||||
// Colors and palettes.
|
||||
// Do note that the
|
||||
|
||||
Reference in New Issue
Block a user