From 66b73ec0061c7b48e8a15b21be683c22148f7700 Mon Sep 17 00:00:00 2001 From: IonutParau Date: Thu, 12 Feb 2026 20:02:51 +0100 Subject: [PATCH] early progress on screen and GPU --- rewrite/main.c | 110 +++++++++++++++++++++++++- rewrite/neonucleus.c | 180 +++++++++++++++++++++++++++++++++++++++++++ rewrite/neonucleus.h | 100 +++++++++++++++++++++++- 3 files changed, 385 insertions(+), 5 deletions(-) diff --git a/rewrite/main.c b/rewrite/main.c index 1e62580..ad5773b 100644 --- a/rewrite/main.c +++ b/rewrite/main.c @@ -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_Computer *C = req->computer; ne_FsState *state = req->instance; @@ -274,6 +275,108 @@ ne_FsState *ne_newFS(const char *path, bool readonly) { 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() { nn_Context ctx; nn_initContext(&ctx); @@ -306,6 +409,7 @@ int main() { for(size_t i = 1; i < 5; i++) { 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); @@ -317,7 +421,10 @@ int main() { ne_FsState *mainFS = ne_newFS("OpenOS", false); 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) { nn_Exit e = nn_tick(c); if(e != NN_OK) { @@ -351,6 +458,7 @@ cleanup:; nn_destroyComputer(c); nn_destroyComponentType(ctype); nn_destroyComponentType(etype); + nn_destroyComponentType(scrtype); for(size_t i = 0; i < 5; i++) nn_destroyComponentType(fstype[i]); // rip the universe nn_destroyUniverse(u); diff --git a/rewrite/neonucleus.c b/rewrite/neonucleus.c index fe08bec..6d66597 100644 --- a/rewrite/neonucleus.c +++ b/rewrite/neonucleus.c @@ -2558,3 +2558,183 @@ nn_ComponentType *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem } 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; +} diff --git a/rewrite/neonucleus.h b/rewrite/neonucleus.h index 6568700..9459b8c 100644 --- a/rewrite/neonucleus.h +++ b/rewrite/neonucleus.h @@ -893,15 +893,42 @@ typedef enum nn_ScreenAction { // instance dropped NN_SCR_DROP, - // from screen component + // set w to 1 if it is on, or 0 if it is off. 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, + // 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, + // 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, + // 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, + // Set w to 1 if precise mode is enabled, or 0 if it isn't. 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, + // Set w to 1 if inverted touch mode is enabled, or 0 if it isn't. 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_ScreenAction; @@ -912,8 +939,42 @@ typedef struct nn_ScreenRequest { nn_ScreenAction action; int w; int h; + char *keyboard; } 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: // - Colors are in 0xRRGGBB format. // - Screen coordinates and palettes are 1-indexed. @@ -925,15 +986,19 @@ typedef enum nn_GPUAction { // 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 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, + // 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. // 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. // 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, // Gets the current background. // x should store either the color in 0xRRGGBB format or the palette index. @@ -1072,6 +1137,33 @@ typedef struct nn_GPURequest { int src; } 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. // Do note that the