early progress on screen and GPU

This commit is contained in:
IonutParau 2026-02-12 20:02:51 +01:00
parent 69c6d953da
commit 66b73ec006
3 changed files with 385 additions and 5 deletions

View File

@ -106,6 +106,7 @@ void ne_fsState_truepath(ne_FsState *state, char truepath[NN_MAX_PATH], const ch
} }
} }
nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) { nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) {
nn_Computer *C = req->computer; nn_Computer *C = req->computer;
ne_FsState *state = req->instance; ne_FsState *state = req->instance;
@ -274,6 +275,108 @@ ne_FsState *ne_newFS(const char *path, bool readonly) {
return fs; return fs;
} }
// this struct is quite wasteful and could be made like 10x better
// for performance. But like, this test emulator is ahh anyways
typedef struct ne_Pixel {
int fg;
int bg;
int truefg;
int truebg;
nn_codepoint codepoint;
bool isFgPalette;
bool isBgPalette;
} ne_Pixel;
typedef struct ne_ScreenBuffer {
int maxWidth;
int maxHeight;
int width;
int height;
char maxDepth;
ne_Pixel *pixels;
int maxPalette;
int editableColors;
int *virtualPalette;
int *mappedPalette;
const char *keyboard;
} ne_ScreenBuffer;
ne_ScreenBuffer *ne_newScreenBuf(nn_ScreenConfig conf, const char *keyboard) {
ne_ScreenBuffer *buf = malloc(sizeof(*buf));
buf->maxWidth = conf.maxWidth;
buf->maxHeight = conf.maxHeight;
buf->maxDepth = conf.maxDepth;
buf->maxPalette = conf.paletteColors;
buf->editableColors = conf.editableColors;
buf->pixels = malloc(sizeof(ne_Pixel) * conf.maxWidth * conf.maxHeight);
buf->virtualPalette = malloc(sizeof(int) * conf.paletteColors);
memset(buf->virtualPalette, 0, sizeof(int) * buf->maxPalette);
buf->mappedPalette = malloc(sizeof(int) * conf.paletteColors);
buf->keyboard = keyboard;
int *palette = NULL;
// TODO: uncomment once the palettes are implemented
if(buf->maxPalette == 16) {
//palette = nn_mcpalette4;
}
if(buf->maxPalette == 256) {
//palette = nn_ocpalette8;
}
if(palette) memcpy(buf->virtualPalette, palette, sizeof(int) * buf->maxPalette);
memcpy(buf->mappedPalette, buf->virtualPalette, sizeof(int) * buf->maxPalette);
return buf;
}
nn_Exit ne_screen_handler(nn_ScreenRequest *req) {
ne_ScreenBuffer *buf = req->instance;
switch(req->action) {
case NN_SCR_DROP:
free(buf->pixels);
free(buf->mappedPalette);
free(buf->virtualPalette);
free(buf);
return NN_OK;
case NN_SCR_GETASPECTRATIO:
req->w = 1;
req->h = 1;
return NN_OK;
case NN_SCR_GETKEYBOARD:
if(buf->keyboard == NULL) {
req->keyboard = NULL;
return NN_OK;
}
size_t keylen = strlen(buf->keyboard);
if(keylen > req->w) keylen = req->w;
memcpy(req->keyboard, buf->keyboard, keylen);
req->w = keylen;
return NN_OK;
case NN_SCR_ISON:
req->w = 1;
return NN_OK;
case NN_SCR_TURNON:
req->w = 1;
req->h = 1;
return NN_OK;
case NN_SCR_TURNOFF:
req->w = 1;
req->h = 1;
return NN_OK;
case NN_SCR_ISPRECISE:
req->w = 0;
return NN_OK;
case NN_SCR_SETPRECISE:
req->w = 0;
return NN_OK;
case NN_SCR_ISTOUCHINVERTED:
req->w = 0;
return NN_OK;
case NN_SCR_SETTOUCHINVERTED:
req->w = 0;
return NN_OK;
}
return NN_OK;
}
int main() { int main() {
nn_Context ctx; nn_Context ctx;
nn_initContext(&ctx); nn_initContext(&ctx);
@ -306,6 +409,7 @@ int main() {
for(size_t i = 1; i < 5; i++) { for(size_t i = 1; i < 5; i++) {
fstype[i] = nn_createFilesystem(u, &nn_defaultFilesystems[i-1], ne_fsState_handler, NULL); fstype[i] = nn_createFilesystem(u, &nn_defaultFilesystems[i-1], ne_fsState_handler, NULL);
} }
nn_ComponentType *scrtype = nn_createScreen(u, NULL, ne_screen_handler);
nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256); nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256);
@ -318,6 +422,9 @@ int main() {
ne_FsState *mainFS = ne_newFS("OpenOS", false); ne_FsState *mainFS = ne_newFS("OpenOS", false);
nn_addComponent(c, fstype[1], "mainFS", 2, mainFS); nn_addComponent(c, fstype[1], "mainFS", 2, mainFS);
ne_ScreenBuffer *scrbuf = ne_newScreenBuf(nn_defaultScreens[3], NULL);
nn_addComponent(c, scrtype, "mainScreen", -1, scrbuf);
while(true) { while(true) {
nn_Exit e = nn_tick(c); nn_Exit e = nn_tick(c);
if(e != NN_OK) { if(e != NN_OK) {
@ -351,6 +458,7 @@ cleanup:;
nn_destroyComputer(c); nn_destroyComputer(c);
nn_destroyComponentType(ctype); nn_destroyComponentType(ctype);
nn_destroyComponentType(etype); nn_destroyComponentType(etype);
nn_destroyComponentType(scrtype);
for(size_t i = 0; i < 5; i++) nn_destroyComponentType(fstype[i]); for(size_t i = 0; i < 5; i++) nn_destroyComponentType(fstype[i]);
// rip the universe // rip the universe
nn_destroyUniverse(u); nn_destroyUniverse(u);

View File

@ -2558,3 +2558,183 @@ nn_ComponentType *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem
} }
return t; return t;
} }
nn_ScreenConfig nn_defaultScreens[4] = {
(nn_ScreenConfig) {
.maxWidth = 50,
.maxHeight = 16,
.maxDepth = 1,
.editableColors = 0,
.paletteColors = 0,
.features = NN_SCRF_NONE,
},
(nn_ScreenConfig) {
.maxWidth = 80,
.maxHeight = 25,
.maxDepth = 4,
.editableColors = 0,
.paletteColors = 16,
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED,
},
(nn_ScreenConfig) {
.maxWidth = 160,
.maxHeight = 50,
.maxDepth = 8,
.editableColors = 16,
.paletteColors = 256,
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE,
},
(nn_ScreenConfig) {
.maxWidth = 240,
.maxHeight = 80,
.maxDepth = 16,
.editableColors = 256,
.paletteColors = 256,
.features = NN_SCRF_NONE,
},
};
typedef struct nn_Screen_state {
nn_Universe *universe;
void *userdata;
nn_ScreenHandler *handler;
} nn_Screen_state;
static nn_Exit nn_screen_handler(nn_ComponentRequest *req) {
nn_Screen_state *state = req->typeUserdata;
nn_Context ctx = state->universe->ctx;
nn_Computer *C = req->computer;
nn_ScreenRequest scrreq;
scrreq.computer = C;
scrreq.userdata = state->userdata;
scrreq.instance = req->compUserdata;
const char *method = req->methodCalled;
switch(req->action) {
case NN_COMP_FREETYPE:
nn_free(&ctx, state, sizeof(*state));
return NN_OK;
case NN_COMP_DEINIT:
scrreq.action = NN_SCR_DROP;
return state->handler(&scrreq);
case NN_COMP_INIT:
return NN_OK;
case NN_COMP_ENABLED:
req->methodEnabled = true;
return NN_OK;
case NN_COMP_CALL:
nn_setError(C, "method not implemented yet");
return NN_EBADCALL;
}
return NN_OK;
}
nn_ComponentType *nn_createScreen(nn_Universe *universe, void *userdata, nn_ScreenHandler *handler) {
nn_Context ctx = universe->ctx;
nn_Screen_state *state = nn_alloc(&ctx, sizeof(*state));
if(state == NULL) return NULL;
state->handler = handler;
state->universe = universe;
state->userdata = userdata;
const nn_Method methods[] = {
{"isOn", "function(): boolean - Returns whether the screen is on", NN_DIRECT},
{"turnOn", "function(): boolean, boolean - Turns the screen on. Returns whether the screen is was off and the new power state.", NN_DIRECT},
{"turnOff", "function(): boolean, boolean - Turns the screen off. Returns whether the screen is was on and the new power state.", NN_DIRECT},
{"getAspectRatio", "function(): number, number - Returns how large the screen is, typically in blocks.", NN_DIRECT},
{"getKeyboards", "function(): string[] - Returns a list of keyboards attached to this screen.", NN_DIRECT},
{"setPrecise", "function(enabled: boolean): boolean - Enable or disable precise mode (sub-pixel precision in touch events).", NN_DIRECT},
{"isPrecise", "function(): boolean - Checks if precise mode is enabled.", NN_DIRECT},
{"setTouchModeInverted", "function(enabled: boolean): boolean - Enable or disable inverted touch mode (alters player interactions)", NN_DIRECT},
{"isTouchModeInverted", "function(): boolean - Checks if inverted touch mode is enabled.", NN_DIRECT},
{NULL, NULL, NN_INDIRECT},
};
nn_ComponentType *t = nn_createComponentType(universe, "screen", state, methods, nn_screen_handler);
if(t == NULL) {
nn_free(&ctx, state, sizeof(*state));
return NULL;
}
return t;
}
nn_GPU nn_defaultGPUs[4] = {
(nn_GPU) {
.maxWidth = 50,
.maxHeight = 16,
.maxDepth = 1,
.totalVRAM = 5000,
.copyPerTick = 1,
.fillPerTick = 1,
.setPerTick = 4,
.setForegroundPerTick = 2,
.setBackgroundPerTick = 2,
.energyPerWrite = 0.02,
.energyPerClear = 0.01,
},
(nn_GPU) {
.maxWidth = 80,
.maxHeight = 25,
.maxDepth = 4,
.totalVRAM = 10000,
.copyPerTick = 2,
.fillPerTick = 4,
.setPerTick = 8,
.setForegroundPerTick = 4,
.setBackgroundPerTick = 4,
.energyPerWrite = 0.1,
.energyPerClear = 0.05,
},
(nn_GPU) {
.maxWidth = 160,
.maxHeight = 50,
.maxDepth = 8,
.totalVRAM = 20000,
.copyPerTick = 4,
.fillPerTick = 8,
.setPerTick = 16,
.setForegroundPerTick = 8,
.setBackgroundPerTick = 8,
.energyPerWrite = 0.2,
.energyPerClear = 0.1,
},
(nn_GPU) {
.maxWidth = 240,
.maxHeight = 80,
.maxDepth = 16,
.totalVRAM = 65536,
.copyPerTick = 8,
.fillPerTick = 12,
.setPerTick = 32,
.setForegroundPerTick = 16,
.setBackgroundPerTick = 16,
.energyPerWrite = 0.25,
.energyPerClear = 0.12,
},
};
int nn_mapColor(int color, int *palette, size_t len) {
// TODO: color mapping
(void)palette;
(void)len;
return color;
}
int nn_mapDepth(int color, int depth, bool ocCompatible) {
if(depth == 1) return color == 0 ? 0 : 0xFFFFFF;
// TODO: map the other depths
return color;
}
const char *nn_depthName(int depth) {
if(depth == 1) return "OneBit";
if(depth == 2) return "TwoBit";
if(depth == 3) return "ThreeBit";
if(depth == 4) return "FourBit";
if(depth == 8) return "EightBit";
if(depth == 16) return "SixteenBit";
if(depth == 24) return "TwentyfourBit";
return NULL;
}

View File

@ -893,15 +893,42 @@ typedef enum nn_ScreenAction {
// instance dropped // instance dropped
NN_SCR_DROP, NN_SCR_DROP,
// from screen component // set w to 1 if it is on, or 0 if it is off.
NN_SCR_ISON, NN_SCR_ISON,
// attempt to turn the screen on.
// set w to 1 if it was on, or 0 if it was off.
// set h to 1 if it is now on, or 0 if it is now off.
NN_SCR_TURNON, NN_SCR_TURNON,
// attempt to turn the screen off.
// set w to 1 if it was on, or 0 if it was off.
// set h to 1 if it is now on, or 0 if it is now off.
NN_SCR_TURNOFF, NN_SCR_TURNOFF,
// get a keyboard. The index requested is stored in h.
// If the index is out of bounds, set keyboard to NULL.
// Else, write the keyboard address into the buffer in keyboard.
// The capacity of the buffer is stored in w.
NN_SCR_GETKEYBOARD, NN_SCR_GETKEYBOARD,
// change the screen to/from precise mode.
// Precise mode means mouse events will have real-number coordinates, as opposed to integer-based ones.
// NeoNucleus does not automatically round this, you are meant to round it.
// The new precision value is stored in w, where it is a 1 to enable it and 0 to disable it.
// Set w to 1 if precise mode is now enabled, or 0 if it isn't.
NN_SCR_SETPRECISE, NN_SCR_SETPRECISE,
// Set w to 1 if precise mode is enabled, or 0 if it isn't.
NN_SCR_ISPRECISE, NN_SCR_ISPRECISE,
// change the screen to/from inverted touch mode.
// Inverted touch mode normally provides an alternative way to interact with the touchscreen.
// For example, in OC, it makes the GUI only open with shift+rightclick, and normal rightclick
// triggers a touch event instead. It is best to give it an equivalent meaning to OC's to prevent
// unexpected program behavior.
// The new inverted touch mode state is stored in w, where it is a 1 to enable it and 0 to disable it.
// Set w to 1 if inverted touch mode is now enabled, or 0 if it isn't.
NN_SCR_SETTOUCHINVERTED, NN_SCR_SETTOUCHINVERTED,
// Set w to 1 if inverted touch mode is enabled, or 0 if it isn't.
NN_SCR_ISTOUCHINVERTED, NN_SCR_ISTOUCHINVERTED,
// Gets the aspect ratio (amount of screen blocks joined together).
// Outside of MC, this may not make much sense, in which case you can just set it to 1x1.
// Store the width in w and the height in h.
NN_SCR_GETASPECTRATIO, NN_SCR_GETASPECTRATIO,
} nn_ScreenAction; } nn_ScreenAction;
@ -912,8 +939,42 @@ typedef struct nn_ScreenRequest {
nn_ScreenAction action; nn_ScreenAction action;
int w; int w;
int h; int h;
char *keyboard;
} nn_ScreenRequest; } nn_ScreenRequest;
typedef enum nn_ScreenFeatures {
NN_SCRF_NONE = 0,
// whether it supports mouse input.
// If it doesn't, it should not emit
// touch, drag or other mouse events.
// Walk events should also not be emitted.
NN_SCRF_MOUSE = 1<<0,
// Whether precise mode is supported.
NN_SCRF_PRECISE = 1<<1,
// Whether touch inverted is supported.
NN_SCRF_TOUCHINVERTED = 1<<2,
} nn_ScreenFeatures;
// A struct for the reference screen configurations
// This does not influence the interface at all,
// however it exists as a runtime reference of what
// the conventional screen tiers are.
typedef struct nn_ScreenConfig {
int maxWidth;
int maxHeight;
nn_ScreenFeatures features;
int editableColors;
int paletteColors;
char maxDepth;
} nn_ScreenConfig;
// OC has 3 tiers, NN adds a 4th one as well.
extern nn_ScreenConfig nn_defaultScreens[4];
typedef nn_Exit nn_ScreenHandler(nn_ScreenRequest *req);
nn_ComponentType *nn_createScreen(nn_Universe *universe, void *userdata, nn_ScreenHandler *handler);
// Remember: // Remember:
// - Colors are in 0xRRGGBB format. // - Colors are in 0xRRGGBB format.
// - Screen coordinates and palettes are 1-indexed. // - Screen coordinates and palettes are 1-indexed.
@ -925,15 +986,19 @@ typedef enum nn_GPUAction {
// Conventional GPU functions // Conventional GPU functions
// requests to bind to a GPU connected to the computer. // requests to bind to a screen connected to the computer.
// The address, as well as its length, are stored in text, with the length in width. // The address, as well as its length, are stored in text, with the length in width.
// The interface does check that the computer does have the screen. // The interface does check that the computer does have the screen, but do look out
// for time-of-check/time-of-use issues which may occur in multi-threaded environments.
NN_GPU_BIND, NN_GPU_BIND,
// requests to unbind the GPU from its screen.
// If there is no screen, it just does nothing.
NN_GPU_UNBIND,
// Ask for the screen the GPU is currently bound to. // Ask for the screen the GPU is currently bound to.
// If it is not bound to any, text should be set to NULL. // If it is not bound to any, text should be set to NULL.
// If it is, you must write to text the address of the screen. // If it is, you must write to text the address of the screen.
// width stores the capacity of text, so if needed, truncate it to that many bytes. // width stores the capacity of text, so if needed, truncate it to that many bytes.
// The length of this address should be stored in width. // The length of this address must be stored in width.
NN_GPU_GETSCREEN, NN_GPU_GETSCREEN,
// Gets the current background. // Gets the current background.
// x should store either the color in 0xRRGGBB format or the palette index. // x should store either the color in 0xRRGGBB format or the palette index.
@ -1072,6 +1137,33 @@ typedef struct nn_GPURequest {
int src; int src;
} nn_GPURequest; } nn_GPURequest;
typedef struct nn_GPU {
// the minimum between these and the screen's
// are the maximum width/height/depth supported.
int maxWidth;
int maxHeight;
char maxDepth;
// this is in pixels.
size_t totalVRAM;
// amount of times copy can be called before running out of budget.
int copyPerTick;
// amount of times fill can be called before running out of budget.
int fillPerTick;
// amount of times set can be called before running out of budget.
int setPerTick;
// amount of times setForeground can be called before running out of budget.
int setForegroundPerTick;
// amount of times setBackground can be called before running out of budget.
int setBackgroundPerTick;
// energy per non-space set.
double energyPerWrite;
// energy per space set.
double energyPerClear;
} nn_GPU;
// 1 GPU tier for every screen.
extern nn_GPU nn_defaultGPUs[4];
// Colors and palettes. // Colors and palettes.
// Do note that the // Do note that the