diff --git a/data/OpenOS/etc/profile.lua b/data/OpenOS/etc/profile.lua index 0938ac9..0fb2ac0 100644 --- a/data/OpenOS/etc/profile.lua +++ b/data/OpenOS/etc/profile.lua @@ -40,5 +40,6 @@ shell.setWorkingDirectory(os.getenv("HOME")) local home_shrc = shell.resolve(".shrc") if fs.exists(home_shrc) then - assert(loadfile(shell.resolve("source", "lua")))(home_shrc) + local resolved = shell.resolve("source", "lua") + assert(loadfile(resolved))(home_shrc) end diff --git a/data/OpenOS/init.lua b/data/OpenOS/init.lua index c08f2b1..ef35e5f 100644 --- a/data/OpenOS/init.lua +++ b/data/OpenOS/init.lua @@ -14,12 +14,10 @@ do end while true do - debug.print("try shell") local result, reason = xpcall(require("shell").getShell(), function(msg) return tostring(msg).."\n"..debug.traceback() end) if not result then - debug.print(reason) io.stderr:write((reason ~= nil and tostring(reason) or "unknown error") .. "\n") io.write("Press any key to continue.\n") os.sleep(0.5) diff --git a/rewrite/luaarch.c b/rewrite/luaarch.c index 19d45ee..0ab102e 100644 --- a/rewrite/luaarch.c +++ b/rewrite/luaarch.c @@ -445,6 +445,12 @@ static int luaArch_unicode_sub(lua_State *L) { if(lua_gettop(L) < 3) lua_pushinteger(L, -1); size_t len = nn_unicode_lenPermissive(s, slen); + + // OpenOS does this... + if(len == 0) { + lua_pushstring(L, ""); + return 1; + } int start = lua_tointeger(L, 2); int end = lua_tointeger(L, 3); @@ -464,6 +470,11 @@ static int luaArch_unicode_sub(lua_State *L) { if(end < 0) end = 0; if(end >= len) end = len-1; + if(start > end) { + lua_pushstring(L, ""); + return 1; + } + nn_codepoint *cp = malloc(sizeof(*cp) * len); nn_unicode_codepointsPermissive(s, slen, cp); @@ -484,6 +495,11 @@ static int luaArch_unicode_wlen(lua_State *L) { return 1; } +static int luaArch_unicode_wtrunc(lua_State *L) { + lua_pushvalue(L, 1); + return 1; +} + static void luaArch_loadEnv(lua_State *L) { lua_createtable(L, 0, 10); int computer = lua_gettop(L); @@ -547,8 +563,10 @@ static void luaArch_loadEnv(lua_State *L) { lua_setfield(L, component, "len"); lua_pushcfunction(L, luaArch_unicode_sub); lua_setfield(L, component, "sub"); - lua_pushcfunction(L, luaArch_unicode_wlen); + lua_pushcfunction(L, luaArch_unicode_len); lua_setfield(L, component, "wlen"); + lua_pushcfunction(L, luaArch_unicode_wtrunc); + lua_setfield(L, component, "wtrunc"); lua_setglobal(L, "unicode"); } diff --git a/rewrite/machine.lua b/rewrite/machine.lua index d84d575..bf2e92d 100644 --- a/rewrite/machine.lua +++ b/rewrite/machine.lua @@ -21,8 +21,9 @@ function coroutine.resume(co, ...) end end -local clist, cinvoke, computer, component, print = component.list, component.invoke, computer, component, print +local clist, cinvoke, computer, component, print, unicode = component.list, component.invoke, computer, component, print, unicode debug.print = print +debug.sysyield = sysyield function component.list(ctype, exact) local list = clist() @@ -155,6 +156,44 @@ function computer.pullSignal(timeout) end end +unicode.upper, unicode.lower = string.upper, string.lower + +unicode.isWide = function(s) + local c = unicode.sub(s, 1, 1) + return unicode.wlen(c) > unicode.len(c) +end + +unicode.wtrunc = function(str,space) + space = space - 1 + return unicode.sub(str, 1, space) +end + +unicode.sub = function(str, a, b) + if not b then b = utf8.len(str) end + if not a then a = 1 end + -- a = math.max(a,1) + + if a < 0 then + -- negative + + a = utf8.len(str) + a + 1 + end + + if b < 0 then + b = utf8.len(str) + b + 1 + end + + if a > b then return "" end + + if b >= utf8.len(str) then b = #str else b = utf8.offset(str,b+1)-1 end + + if a > utf8.len(str) then return "" end + a = utf8.offset(str,a) + + return str:sub(a,b) + -- return str:sub(a, b) +end + function checkArg(arg, val, ...) local t = {...} for i=1,#t do @@ -165,7 +204,7 @@ end if os.getenv("NN_REPL") == "1" then while true do - io.write("lua> ") + io.write("\x1b[34mlua>\x1b[0m ") io.flush() local l = io.read("l") if not l then break end @@ -176,6 +215,7 @@ if os.getenv("NN_REPL") == "1" then f, err = load(l, "=repl") if f then f() else print(err) end end + coroutine.yield() end io.write("\n") print("exiting repl") diff --git a/rewrite/main.c b/rewrite/main.c index 4a82abb..48ac892 100644 --- a/rewrite/main.c +++ b/rewrite/main.c @@ -153,12 +153,16 @@ nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) { switch(mode[0]) { case 'r': mode = "rb"; + break; case 'w': mode = "wb"; + break; case 'a': mode = "ab"; + break; default: mode = "rb"; + break; } ne_fsState_truepath(state, truepath, path); @@ -568,15 +572,21 @@ nn_Exit ne_gpu_handler(nn_GPURequest *req) { x = req->x; y = req->y; - for(int i = 0; i < req->width; i++) { + const char *s = req->text; + for(int i = 0; i < req->width;) { if(!ne_inScreenBuf(activeBuf, x, y)) break; + size_t w = nn_unicode_validateFirstChar(s + i, req->width - i); ne_Pixel p = { .fg = state->currentFg, .bg = state->currentBg, .isFgPalette = state->isFgPalette, .isBgPalette = state->isBgPalette, - .codepoint = req->text[i], + .codepoint = (unsigned char)s[i], }; + if(w > 0) { + p.codepoint = nn_unicode_firstCodepoint(s + i); + i += w; + } else i++; ne_setPixel(activeBuf, x, y, p); x += dx; y += dy; @@ -592,8 +602,7 @@ nn_Exit ne_gpu_handler(nn_GPURequest *req) { y = req->y; w = req->width; h = req->height; - if(x < 1) x = 1; - if(y < 1) y = 1; + // prevent CPU DoS if(w >= activeBuf->width) w = activeBuf->width - 1; if(h >= activeBuf->height) h = activeBuf->height - 1; @@ -609,6 +618,93 @@ nn_Exit ne_gpu_handler(nn_GPURequest *req) { ne_setPixel(activeBuf, x + ox, y + oy, p); } } + ne_remapScreen(activeBuf); + return NN_OK; + case NN_GPU_COPY: + if(activeBuf == NULL) { + nn_setError(C, "no screen"); + return NN_EBADCALL; + } + x = req->x; + y = req->y; + w = req->width; + h = req->height; + // prevent CPU DoS + if(w >= activeBuf->width) w = activeBuf->width - 1; + if(h >= activeBuf->height) h = activeBuf->height - 1; + + ne_Pixel *buf = malloc(sizeof(*buf) * w * h); + if(buf == NULL) return NN_ENOMEM; + + for(int oy = 0; oy < h; oy++) { + for(int ox = 0; ox < w; ox++) { + buf[oy * w + ox] = ne_getPixel(activeBuf, x + ox, y + oy); + } + } + + for(int oy = 0; oy < h; oy++) { + for(int ox = 0; ox < w; ox++) { + p = buf[oy * w + ox]; + ne_setPixel(activeBuf, x + ox + req->tx, y + oy + req->ty, p); + } + } + free(buf); + ne_remapScreen(activeBuf); + return NN_OK; + case NN_GPU_GETDEPTH: + if(activeBuf != NULL) { + req->x = activeBuf->depth; + } else { + req->x = req->gpuConf->maxDepth; + } + return NN_OK; + case NN_GPU_GETVIEWPORT: + if(activeBuf == NULL) { + nn_setError(C, "no screen"); + return NN_EBADCALL; + } + req->width = activeBuf->width; + req->height = activeBuf->height; + return NN_OK; + case NN_GPU_GETFOREGROUND: + req->x = state->currentFg; + req->y = state->isFgPalette ? 1 : 0; + return NN_OK; + case NN_GPU_GETBACKGROUND: + req->x = state->currentBg; + req->y = state->isBgPalette ? 1 : 0; + return NN_OK; + case NN_GPU_SETFOREGROUND: + x = req->x; + y = req->y; + if(y != 0) { + // validate the palette index + if(activeBuf == NULL || x < 0 || x >= activeBuf->maxPalette) { + nn_setError(C, "invalid palette index"); + return NN_EBADCALL; + } + } + req->x = state->currentFg; + req->y = state->isFgPalette ? 1 : 0; + state->currentFg = x; + state->isFgPalette = y != 0; + ne_remapScreen(activeBuf); + return NN_OK; + case NN_GPU_SETBACKGROUND: + x = req->x; + y = req->y; + if(y != 0) { + // validate the palette index + if(activeBuf == NULL || x < 0 || x >= activeBuf->maxPalette) { + nn_setError(C, "invalid palette index"); + return NN_EBADCALL; + } + } + req->x = state->currentBg; + req->y = state->isBgPalette ? 1 : 0; + state->currentBg = x; + state->isBgPalette = y != 0; + ne_remapScreen(activeBuf); return NN_OK; } return NN_OK; @@ -620,11 +716,19 @@ Color ne_processColor(unsigned int color) { return GetColor(color); } +double ne_timeProc(void *_) { + (void)_; + double t = GetTime(); + return (int)(t*100) / 100.0; +} + int main() { nn_Context ctx; nn_initContext(&ctx); nn_initPalettes(); + ctx.time = ne_timeProc; + SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(800, 600, "NeoNucleus Test Emulator"); diff --git a/rewrite/neonucleus.c b/rewrite/neonucleus.c index 7f5d700..5c1cc06 100644 --- a/rewrite/neonucleus.c +++ b/rewrite/neonucleus.c @@ -2752,11 +2752,15 @@ nn_Exit nn_gpu_handler(nn_ComponentRequest *req) { greq.text = (char *)nn_tolstring(C, 0, &len); greq.width = len; greq.x = nn_toboolean(C, 1) ? 1 : 0; - return state->handler(&greq); + err = state->handler(&greq); + if(err) return err; + return nn_pushbool(C, true); } if(nn_strcmp(method, "unbind") == 0) { greq.action = NN_GPU_UNBIND; - return state->handler(&greq); + err = state->handler(&greq); + if(err) return err; + return nn_pushbool(C, true); } if(nn_strcmp(method, "getScreen") == 0) { char buf[NN_MAX_ADDRESS]; @@ -2798,6 +2802,148 @@ nn_Exit nn_gpu_handler(nn_ComponentRequest *req) { req->returnCount = 1; return nn_pushbool(C, true); } + if(nn_strcmp(method, "get") == 0) { + nn_costComponent(C, req->compAddress, conf.setPerTick); + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 1, "bad argument #2 (integer expected)")) return NN_EBADCALL; + greq.action = NN_GPU_GET; + greq.x = nn_tointeger(C, 0); + greq.y = nn_tointeger(C, 1); + err = state->handler(&greq); + if(err) return err; + req->returnCount = 5; + char buf[NN_MAX_UNICODE_BUFFER]; + size_t len = nn_unicode_codepointToChar(buf, greq.codepoint); + err = nn_pushlstring(C, buf, len); + if(err) return err; + err = nn_pushinteger(C, greq.width); + if(err) return err; + err = nn_pushinteger(C, greq.height); + if(err) return err; + if(greq.dest == -1) err = nn_pushnull(C); + else err = nn_pushinteger(C, greq.dest); + if(err) return err; + if(greq.src == -1) err = nn_pushnull(C); + else err = nn_pushinteger(C, greq.src); + if(err) return err; + return NN_OK; + } + if(nn_strcmp(method, "fill") == 0) { + nn_costComponent(C, req->compAddress, conf.fillPerTick); + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 1, "bad argument #2 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 2, "bad argument #3 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 3, "bad argument #4 (integer expected)")) return NN_EBADCALL; + if(nn_checkstring(C, 4, "bad argument #5 (string expected)")) return NN_EBADCALL; + greq.action = NN_GPU_FILL; + size_t len; + const char *text = nn_tolstring(C, 4, &len); + if(nn_unicode_validateFirstChar(text, len) == 0) { + nn_setError(C, "invalid UTF-8 character"); + return NN_EBADCALL; + } + greq.codepoint = nn_unicode_firstCodepoint(text); + greq.x = nn_tointeger(C, 0); + greq.y = nn_tointeger(C, 1); + greq.width = nn_tointeger(C, 2); + greq.height = nn_tointeger(C, 3); + err = state->handler(&greq); + if(err) return err; + req->returnCount = 1; + return nn_pushbool(C, true); + } + if(nn_strcmp(method, "copy") == 0) { + nn_costComponent(C, req->compAddress, conf.copyPerTick); + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 1, "bad argument #2 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 2, "bad argument #3 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 3, "bad argument #4 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 4, "bad argument #5 (integer expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 5, "bad argument #6 (integer expected)")) return NN_EBADCALL; + greq.action = NN_GPU_COPY; + greq.x = nn_tointeger(C, 0); + greq.y = nn_tointeger(C, 1); + greq.width = nn_tointeger(C, 2); + greq.height = nn_tointeger(C, 3); + greq.tx = nn_tointeger(C, 4); + greq.ty = nn_tointeger(C, 5); + err = state->handler(&greq); + if(err) return err; + req->returnCount = 1; + return nn_pushbool(C, true); + } + if(nn_strcmp(method, "getDepth") == 0) { + greq.action = NN_GPU_GETDEPTH; + err = state->handler(&greq); + if(err) return err; + req->returnCount = 1; + return nn_pushinteger(C, greq.x); + } + if(nn_strcmp(method, "getViewport") == 0) { + greq.action = NN_GPU_GETVIEWPORT; + err = state->handler(&greq); + if(err) return err; + req->returnCount = 2; + err = nn_pushinteger(C, greq.width); + if(err) return err; + return nn_pushinteger(C, greq.height); + } + if(nn_strcmp(method, "setForeground") == 0) { + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + err = nn_defaultboolean(C, 1, false); + if(err) return err; + if(nn_checkinteger(C, 1, "bad argument #2 (boolean expected)")) return NN_EBADCALL; + greq.action = NN_GPU_SETFOREGROUND; + greq.x = nn_tointeger(C, 0); + greq.y = nn_toboolean(C, 1) ? 1 : 0; + err = state->handler(&greq); + if(err) return err; + req->returnCount = 2; + err = nn_pushinteger(C, greq.x); + if(err) return err; + err = nn_pushbool(C, greq.y != 0); + if(err) return err; + return NN_OK; + } + if(nn_strcmp(method, "getForeground") == 0) { + greq.action = NN_GPU_GETFOREGROUND; + err = state->handler(&greq); + if(err) return err; + req->returnCount = 2; + err = nn_pushinteger(C, greq.x); + if(err) return err; + err = nn_pushbool(C, greq.y != 0); + if(err) return err; + return NN_OK; + } + if(nn_strcmp(method, "setBackground") == 0) { + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + err = nn_defaultboolean(C, 1, false); + if(err) return err; + if(nn_checkinteger(C, 1, "bad argument #2 (boolean expected)")) return NN_EBADCALL; + greq.action = NN_GPU_SETBACKGROUND; + greq.x = nn_tointeger(C, 0); + greq.y = nn_toboolean(C, 1) ? 1 : 0; + err = state->handler(&greq); + if(err) return err; + req->returnCount = 2; + err = nn_pushinteger(C, greq.x); + if(err) return err; + err = nn_pushbool(C, greq.y != 0); + if(err) return err; + return NN_OK; + } + if(nn_strcmp(method, "getBackground") == 0) { + greq.action = NN_GPU_GETBACKGROUND; + err = state->handler(&greq); + if(err) return err; + req->returnCount = 2; + err = nn_pushinteger(C, greq.x); + if(err) return err; + err = nn_pushbool(C, greq.y != 0); + if(err) return err; + return NN_OK; + } nn_setError(C, "method not yet implemented"); return NN_EBADCALL; } @@ -3254,7 +3400,7 @@ size_t nn_unicode_codepointSize(nn_codepoint codepoint) { return 1; } -size_t nn_unicode_codepointToChar(char buffer[NN_MAXIMUM_UNICODE_BUFFER], nn_codepoint codepoint) { +size_t nn_unicode_codepointToChar(char buffer[NN_MAX_UNICODE_BUFFER], nn_codepoint codepoint) { size_t codepointSize = nn_unicode_codepointSize(codepoint); if (codepointSize == 1) { @@ -3304,7 +3450,9 @@ size_t nn_unicode_wlen(const char *s, size_t len) { for(size_t i = 0; i < len;) { nn_codepoint codepoint = nn_unicode_firstCodepoint(s + i); size_t size = nn_unicode_codepointSize(codepoint); - wlen += nn_unicode_charWidth(codepoint); + size_t width = nn_unicode_charWidth(codepoint); + if(width == 0) width = 1; + wlen += width; i += size; } return wlen; @@ -3315,12 +3463,14 @@ size_t nn_unicode_wlenPermissive(const char *s, size_t len) { for(size_t i = 0; i < len;) { if(nn_unicode_validateFirstChar(s + i, len - i) == 0) { size_t width = nn_unicode_charWidth((unsigned char)s[i]); + if(width == 0) width = 1; wlen += width; i++; } else { nn_codepoint codepoint = nn_unicode_firstCodepoint(s + i); size_t size = nn_unicode_codepointSize(codepoint); size_t width = nn_unicode_charWidth(codepoint); + if(width == 0) width = 1; wlen += width; i += size; } diff --git a/rewrite/neonucleus.h b/rewrite/neonucleus.h index 4793fa8..9b09806 100644 --- a/rewrite/neonucleus.h +++ b/rewrite/neonucleus.h @@ -85,7 +85,7 @@ extern "C" { #define NN_MAX_USERNAME 128 // the maximum size of a UTF-8 character -#define NN_MAXIMUM_UNICODE_BUFFER 4 +#define NN_MAX_UNICODE_BUFFER 4 // the maximum size of a component error message. If the error is bigger than this, // it is truncated. @@ -126,7 +126,7 @@ nn_codepoint nn_unicode_firstCodepoint(const char *s); size_t nn_unicode_codepointSize(nn_codepoint codepoint); // Writes the UTF-8 bytes for a given codepoint into buffer. // It does NOT write a NULL terminator, but it does return the length. -size_t nn_unicode_codepointToChar(char buffer[NN_MAXIMUM_UNICODE_BUFFER], nn_codepoint codepoint); +size_t nn_unicode_codepointToChar(char buffer[NN_MAX_UNICODE_BUFFER], nn_codepoint codepoint); // the width, on a screen, for a codepoint. // This matters for emojies. size_t nn_unicode_charWidth(nn_codepoint codepoint);