diff --git a/build.zig b/build.zig index 953db9f..51e1fb4 100644 --- a/build.zig +++ b/build.zig @@ -74,6 +74,7 @@ pub fn build(b: *std.Build) !void { const zlua = b.dependency("zlua", .{ .target = target, .optimize = optimize, + .lang = .luajit }); exe.root_module.addImport("zlua", zlua.module("zlua")); diff --git a/src/animations/Lua.zig b/src/animations/Lua.zig index 86e3b82..a4ba069 100644 --- a/src/animations/Lua.zig +++ b/src/animations/Lua.zig @@ -17,6 +17,8 @@ instance: ?Widget = null, lua: *zlua.Lua, log: *LogFile, terminal_buffer: *TerminalBuffer, +width: usize, +height: usize, io: std.Io, lua_error: bool = false, @@ -33,53 +35,110 @@ pub fn init( .terminal_buffer = buf, .instance = null, .log = log, + .width = 0, + .height = 0, .io = io, }; - self.lua.openLibs(); + + // exclude IO and debug libraries + self.lua.openBase(); + self.lua.openBit(); + self.lua.openMath(); + self.lua.openOS(); + self.lua.openString(); + self.lua.openTable(); + self.lua.openString(); + if (file) |f| { const zf = std.mem.concatWithSentinel(alloc, u8, &[1][]const u8{f}, 0) catch unreachable; defer alloc.free(zf); self.lua.doString(LyLua) catch { const errorStr = self.lua.toString(-1) catch unreachable; self.log.err(self.io, "Lua", "Lua Error: {s}", .{errorStr}) catch unreachable; - self.lua.setGlobal("_ERROR"); self.lua_error = true; }; self.lua.doFile(zf) catch { const errorStr = self.lua.toString(-1) catch unreachable; self.log.err(self.io, "Lua", "Lua Error: {s}", .{errorStr}) catch unreachable; - self.lua.setGlobal("_ERROR"); self.lua_error = true; }; } - _ = self.lua.getGlobal("ly"); - addFuncToTable(self.lua, "putCell", luaPutCell); - self.lua.setGlobal("ly"); - return self; } fn draw(self: *Lua) void { - _ = self.lua.getGlobal("ly"); - _ =self.lua.pushString("width"); - self.lua.pushInteger(@intCast(self.terminal_buffer.width)); - self.lua.setTable(-3); - _ =self.lua.pushString("height"); - self.lua.pushInteger(@intCast(self.terminal_buffer.height)); - self.lua.setTable(-3); - - self.lua.setGlobal("ly"); + if (self.terminal_buffer.height != self.height or + self.terminal_buffer.width != self.width) { + self.width = self.terminal_buffer.width; + self.height = self.terminal_buffer.height; + _ = self.lua.getGlobal("ly"); + _ =self.lua.pushString("width"); + self.lua.pushInteger(@intCast(self.terminal_buffer.width)); + self.lua.setTable(-3); + _ =self.lua.pushString("height"); + self.lua.pushInteger(@intCast(self.terminal_buffer.height)); + self.lua.setTable(-3); + self.lua.setGlobal("ly"); + } if (!self.lua_error) { - self.lua.doString( - "draw()" - ) catch { + _ = self.lua.getGlobal("draw"); + self.lua.protectedCall(.{}) catch { const errorStr = self.lua.toString(-1) catch unreachable; - self.log.err(self.io, "Lua", "Lua Error: {s}", .{errorStr}) catch unreachable; - self.lua.setGlobal("_ERROR"); + self.log.err(self.io, "Lua", "Error (Cannot call draw()): {s}", .{errorStr}) catch unreachable; self.lua_error = true; }; + const bufferType = self.lua.getGlobal("_BUFFER"); + if (bufferType != .table) { + self.log.err(self.io, "Lua", "global '_BUFFER' must be a table: got {}", .{bufferType}) + catch {}; + self.lua_error = true; + return; + } + self.lua.pushNil(); + while (self.lua.next(-2)) { + // expect the key to be an int + // expect the value to be a table + const pos = self.lua.toInteger(-2) catch { + self.log.err(self.io, "Lua", "pos: cannot convert to integer", .{}) + catch {}; + self.lua_error = true; + return; + }; + + _ = self.lua.pushString("char"); + _ = self.lua.getTable(-2); + const char = self.lua.toInteger(-1) catch { + self.log.err(self.io, "Lua", "char: cannot convert to integer", .{}) + catch {}; + self.lua_error = true; + return; + }; + self.lua.pop(1); + _ = self.lua.pushString("fg"); + _ = self.lua.getTable(-2); + const fg = self.lua.toInteger(-1) catch { + self.log.err(self.io, "Lua", "fg: cannot convert to integer", .{}) + catch {}; + self.lua_error = true; + return; + }; + self.lua.pop(1); + _ = self.lua.pushString("bg"); + _ = self.lua.getTable(-2); + const bg = self.lua.toInteger(-1) catch { + self.log.err(self.io, "Lua", "bg: cannot convert to integer", .{}) + catch {}; + self.lua_error = true; + return; + }; + self.lua.pop(1); + self.lua.pop(1); + if (pos < 0) continue; // do not draw cells that underflow + Cell.init(@intCast(char), @intCast(fg), @intCast(bg)) + .put(@rem(@as(usize, @intCast(pos)), self.width), @divFloor(@as(usize, @bitCast(pos)), self.width)) catch {}; + } } else { - // set the entire background to red + // Ly's Red Screen of Omega-Death:tm: const cell = Cell.init(0x2588, 0x00ff0000, 0x00ff0000); for (0..self.terminal_buffer.height) |y| for (0..self.terminal_buffer.width) |x| @@ -88,13 +147,24 @@ fn draw(self: *Lua) void { } fn calculateTimeout(self: *Lua, _: *anyopaque) !?usize { + if (self.lua_error) return null; const lyType = self.lua.getGlobal("ly"); // +1 _ = self.lua.pushString("frame_delay"); // + 1 - if (lyType != .table) - std.debug.panic("Lua: global 'ly' must be a table: got {}", .{lyType}); + if (lyType != .table) { + try self.log.err(self.io, "Lua", "global 'ly' must be a table: got {}", .{lyType}); + self.lua_error = true; + self.lua.pop(-1); + self.lua.pop(-1); + return null; + } const frameDelayType = self.lua.getTable(-2); // neut - if (frameDelayType != .number) - std.debug.panic("Lua: 'ly.frame_delay' must be a number: got {}", .{frameDelayType}); + if (frameDelayType != .number) { + try self.log.err(self.io, "Lua", "'ly.frame_delay' must be a number: got {}", .{lyType}); + self.lua_error = true; + self.lua.pop(-1); + self.lua.pop(-1); + return null; + } const timeout = try self.lua.toNumeric(usize, -1); self.lua.pop(-1); // pop number from stack self.lua.pop(-1); // pop table 'ly' from stack @@ -120,32 +190,3 @@ pub fn widget(self: *Lua) *Widget { ); return &self.instance.?; } - -fn luaPutCell(lua: ?*zlua.LuaState) callconv(.c) c_int { - var vm = @as(*zlua.Lua, @ptrCast(lua)); - const cell = vm.checkNumeric(u32, 1); - const fg = vm.checkNumeric(u32, 2); - const bg = vm.checkNumeric(u32, 3); - const x = vm.checkNumeric(usize, 4); - const y = vm.checkNumeric(usize, 5); - Cell.init(cell, fg, bg).put(x, y) catch {}; - - return 0; // This function doesnt return anything -} - -/// Adds member function `name` to table at the top of the stack -/// -/// Pushes: 0 -/// Pops: 0, this function can be called continuously -/// Errors: unknown -fn addFuncToTable( - vm: *zlua.Lua, - comptime name: []const u8, - func: zlua.CFn, -) void -{ - _ = vm.pushString(name); - vm.pushFunction(func); - vm.setTable(-3); -} - diff --git a/src/animations/ly.lua b/src/animations/ly.lua index 5d53f45..763ec7b 100644 --- a/src/animations/ly.lua +++ b/src/animations/ly.lua @@ -1,3 +1,18 @@ ly = { frame_delay = 0, + width = 0, + height = 0, } +_BUFFER = {} + +function ly.putCell(byte, fg, bg, x, y) + _BUFFER[x + (y * ly.width)] = { + char = math.floor(byte), + fg = math.floor(fg), + bg = math.floor(bg), + } +end + +function ly.clear() + _BUFFER = {} +end