mirror of
https://github.com/fairyglade/ly.git
synced 2026-06-22 07:22:00 +00:00
feat: LuaJIT Animations (#1001)
## What are the changes about? TL;DR:  Slaps the entire LuaJIT runtime onto Ly, allowing for the creation of custom dynamic animations like GameOfLife, ColorWave, Doom, etc. This PR adds the [ziglua](https://github.com/natecraddock/ziglua?ref=zig-0.16) dependency for its zig bindings and considerable buildtime config (mainly lua version selection). ### Example <video src="/attachments/3f91cf72-ae24-459c-8ef6-099f71e866fd" title="Screencast_20260519_172320" controls></video> ```lua ly.frame_delay = 5 local timer = 0 local clock = os.clock() local clock_diff = 0 function draw() timer = timer + 1 byte = string.byte(' ') clock_diff = os.clock() - clock clock = os.clock() timer = timer + clock_diff for x = 0, ly.width-1 do for y = 0, ly.height-1 do local xc = 0xFF if x < 255 then xc = ((x + math.floor(timer / 2)) * 3) % 255 else xc = 0 end local yc = 0xFF if y < 255 then yc = ((y) * 3) % 255 else yc = 0 end ly.putCell(byte, xc, bit.bor(xc, yc), x, y) end end end ``` ### The API The API that Ly gives to the user is minimal. A table is globally available, named `ly`, which provides the following: | Member | Purpose | |---------|---------| | `ly.width` & `ly.height` | Respective Width/Height from the `TerminalBuffer` | | `ly.putCell(byte, fg, bg, x, y)` | Literally `Cell.init(byte, fg, bg).put(x, y)`.| | `ly.clock()` | The current real-time, in microseconds. | ### Error Handling On a Lua Error, Ly won't quit but will instead paint the entire background red. The lua error in question can be found in the Ly log file and on-screen. ```log 2026-05-19 16:13:40 [err/Lua] Error (Cannot call draw()): attempt to call a nil value 2026-05-19 11:05:51 [err/Lua] Lua Error: ...dsammyt/programming/probe/ly/scratch/testConfig/test.lua:30: bad argument #1 to 'ipairs' (table expected, got number) ``` ## Pre-requisites - [X] I have tested & confirmed the changes work locally - [X] I have run `zig fmt` throughout my changes Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1001 Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
This commit is contained in:
11
build.zig
11
build.zig
@@ -71,12 +71,19 @@ 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"));
|
||||||
|
|
||||||
const ly_ui = b.dependency("ly_ui", .{
|
const ly_ui = b.dependency("ly_ui", .{
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.enable_x11_support = enable_x11_support,
|
.enable_x11_support = enable_x11_support,
|
||||||
.fallback_uid_min = fallback_uid_min,
|
.fallback_uid_min = fallback_uid_min,
|
||||||
.fallback_uid_max = fallback_uid_max
|
.fallback_uid_max = fallback_uid_max,
|
||||||
});
|
});
|
||||||
exe.root_module.addImport("ly-ui", ly_ui.module("ly-ui"));
|
exe.root_module.addImport("ly-ui", ly_ui.module("ly-ui"));
|
||||||
|
|
||||||
@@ -189,6 +196,8 @@ fn install_ly(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap, ins
|
|||||||
try installText(io, patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .permissions = .fromMode(0o755) });
|
try installText(io, patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .permissions = .fromMode(0o755) });
|
||||||
|
|
||||||
try installFile(io, "res/example.dur", config_dir, ly_config_directory, "example.dur", .{ .permissions = .fromMode(0o755) });
|
try installFile(io, "res/example.dur", config_dir, ly_config_directory, "example.dur", .{ .permissions = .fromMode(0o755) });
|
||||||
|
|
||||||
|
try installFile(io, "res/example.lua", config_dir, ly_config_directory, "example.lua", .{ .permissions = .fromMode(0o755) });
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
.url = "git+https://github.com/Hejsil/zig-clap#fc1e5cc3f6d9d3001112385ee6256d694e959d2f",
|
.url = "git+https://github.com/Hejsil/zig-clap#fc1e5cc3f6d9d3001112385ee6256d694e959d2f",
|
||||||
.hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1",
|
.hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1",
|
||||||
},
|
},
|
||||||
|
.zlua = .{
|
||||||
|
.url = "git+https://github.com/natecraddock/ziglua?ref=zig-0.16#8f271c82baa5fc43aa02a72f6da020c2025d9436",
|
||||||
|
.hash = "zlua-0.1.0-hGRpC2aABQD4D9PBVH3wAW8k32-I4969MRQ0CpOwoley",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"build.zig",
|
"build.zig",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ allow_empty_password = true
|
|||||||
# colormix -> Color mixing shader
|
# colormix -> Color mixing shader
|
||||||
# gameoflife -> John Conway's Game of Life
|
# gameoflife -> John Conway's Game of Life
|
||||||
# dur_file -> .dur file format (https://github.com/cmang/durdraw/tree/master)
|
# dur_file -> .dur file format (https://github.com/cmang/durdraw/tree/master)
|
||||||
|
# lua -> user-made animation written in LuaJIT
|
||||||
animation = none
|
animation = none
|
||||||
|
|
||||||
# Delay between each animation frame in milliseconds
|
# Delay between each animation frame in milliseconds
|
||||||
@@ -298,6 +299,9 @@ login_defs_path = /etc/login.defs
|
|||||||
# no need to add `exec "$@"` at the end
|
# no need to add `exec "$@"` at the end
|
||||||
logout_cmd = null
|
logout_cmd = null
|
||||||
|
|
||||||
|
# The file pointing to the Lua file to be used when using the Lua animation option
|
||||||
|
lua_animation_file = $CONFIG_DIRECTORY/ly/example.lua
|
||||||
|
|
||||||
# General log file path
|
# General log file path
|
||||||
# If null, syslog will be used instead
|
# If null, syslog will be used instead
|
||||||
ly_log = /var/log/ly.log
|
ly_log = /var/log/ly.log
|
||||||
|
|||||||
119
res/example.lua
Normal file
119
res/example.lua
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
-- [[
|
||||||
|
-- This is an example of using LuaJIT to create a custom animation in Ly, in this case
|
||||||
|
-- bouncing squares that change colors.
|
||||||
|
--
|
||||||
|
-- You are given the following `ly` table:
|
||||||
|
-- {
|
||||||
|
-- height: number -- The height of the terminal
|
||||||
|
-- width: number -- The width of the terminal
|
||||||
|
-- putCell(byte, fg, bg, x, y) -- Draw a cell.
|
||||||
|
-- All arguments to this function are integers, and
|
||||||
|
-- must be in the unsigned 32-bit integer range: 0 to 2^32-1.
|
||||||
|
-- If an argument cannot be converted to this range, it will throw
|
||||||
|
-- an error.
|
||||||
|
--
|
||||||
|
-- For reference, the XY coordinates (0,0) draw a cell on the top-left
|
||||||
|
-- of the terminal, where the positive-X axis moves right and the
|
||||||
|
-- positive-Y axis moves down.
|
||||||
|
--
|
||||||
|
-- For arguments fg and bg: they are colors in the format
|
||||||
|
-- 0xSSRRGGBB, where SS is for styling. See your
|
||||||
|
-- config.ini for more details.
|
||||||
|
--
|
||||||
|
-- For the byte argument, you may use string.byte to fill this argument.
|
||||||
|
--
|
||||||
|
-- putCell(byte, fg, bg, x, y, w, h) -- Draw a rectangle.
|
||||||
|
-- Arguments are the same as putCell except for w and h, which are also
|
||||||
|
-- unsigned integers. The rectangle will be drawn from the top-left, with
|
||||||
|
-- argument w extending it to the right and argument h extending downwards.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- putLabel(str, fg, bg, x, y) -- Draw text in argument str. See putCell()
|
||||||
|
-- for info on the rest of the arguments.
|
||||||
|
--
|
||||||
|
-- clock() -- The time, in microseconds.
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- A function named `draw()` must be declared in the script. This is ran every
|
||||||
|
-- frame.
|
||||||
|
--
|
||||||
|
-- In addition to the base library, you are also given the following standard
|
||||||
|
-- libraries:
|
||||||
|
-- bit (A library exclusive to LuaJIT, see https://bitop.luajit.org/api.html)
|
||||||
|
-- math
|
||||||
|
-- string
|
||||||
|
-- table
|
||||||
|
--
|
||||||
|
-- The std libraries io and debug are NOT included.
|
||||||
|
--
|
||||||
|
-- ]]
|
||||||
|
|
||||||
|
-- You should probably copy FPS and FPS_COUNT into any future LuaJIT animations
|
||||||
|
-- you create.
|
||||||
|
local FPS_COUNT = 40
|
||||||
|
local function FPS()
|
||||||
|
return (1/FPS_COUNT)*1000000
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local SQUARE_WIDTH = 10
|
||||||
|
local SQUARE_HEIGHT = 5
|
||||||
|
|
||||||
|
local SQUARE_COUNT = 25
|
||||||
|
|
||||||
|
local squares = {}
|
||||||
|
|
||||||
|
for i = 1, SQUARE_COUNT do
|
||||||
|
local vx = 1
|
||||||
|
local vy = 1
|
||||||
|
if math.random(1, 2) == 2 then vx = -vx end
|
||||||
|
if math.random(1, 2) == 2 then vy = -vy end
|
||||||
|
squares[#squares+1] = {
|
||||||
|
x = math.random(1, ly.width - SQUARE_WIDTH),
|
||||||
|
y = math.random(1, ly.height - SQUARE_HEIGHT),
|
||||||
|
vx = vx,
|
||||||
|
vy = vy,
|
||||||
|
color = math.random(0xFFFFFF)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local timer = ly.clock()
|
||||||
|
local perf = ly.clock()
|
||||||
|
|
||||||
|
function draw()
|
||||||
|
-- Rather than progressing the animation by frame, do it based on
|
||||||
|
-- seconds, via ly.clock(). In this timeframe, you can update the animation
|
||||||
|
-- state.
|
||||||
|
-- DO NOT DRAW CELLS IN THIS TIMEFRAME. You will get flickering.
|
||||||
|
|
||||||
|
-- if this check passes, we can update the animation
|
||||||
|
if timer + FPS() < ly.clock() then
|
||||||
|
for i, v in ipairs(squares) do
|
||||||
|
v.x = v.x + v.vx
|
||||||
|
v.y = v.y + v.vy
|
||||||
|
if v.x == 0 then
|
||||||
|
v.vx = 1; v.color = math.random(0xFFFFFF)
|
||||||
|
end
|
||||||
|
if v.x + SQUARE_WIDTH >= ly.width-1 then
|
||||||
|
v.vx = -1; v.color = math.random(0xFFFFFF)
|
||||||
|
end
|
||||||
|
if v.y == 0 then
|
||||||
|
v.vy = 1; v.color = math.random(0xFFFFFF)
|
||||||
|
end
|
||||||
|
if v.y + SQUARE_HEIGHT >= ly.height-1 then
|
||||||
|
v.vy = -1; v.color = math.random(0xFFFFFF)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
timer = ly.clock()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
for i, v in ipairs(squares) do
|
||||||
|
ly.putRect(string.byte(' '), 0, v.color, v.x, v.y, SQUARE_WIDTH, SQUARE_HEIGHT)
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_perf = ly.clock()
|
||||||
|
local str = "FT: "..((new_perf - perf) / 1000).."ms"
|
||||||
|
ly.putLabel(str , 0x00FFFFFF, 0, (ly.width/2) - (string.len(str)/2), ly.height-1)
|
||||||
|
perf = new_perf
|
||||||
|
end
|
||||||
302
src/animations/Lua.zig
Normal file
302
src/animations/Lua.zig
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const LogFile = ly_ui.ly_core.LogFile;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Cell = ly_ui.Cell;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const InfoLine = @import("../components/InfoLine.zig");
|
||||||
|
const Lang = @import("../config/Lang.zig");
|
||||||
|
|
||||||
|
const zlua = @import("zlua");
|
||||||
|
|
||||||
|
const ly_lua = @embedFile("ly.lua");
|
||||||
|
|
||||||
|
const Lua = @This();
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
instance: ?Widget = null,
|
||||||
|
lua: *zlua.Lua,
|
||||||
|
log: *LogFile,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
margin: usize,
|
||||||
|
io: std.Io,
|
||||||
|
animation_delay: u16,
|
||||||
|
|
||||||
|
info_line: *InfoLine,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
|
||||||
|
lang: Lang,
|
||||||
|
full_color: bool,
|
||||||
|
|
||||||
|
lua_error: bool = false,
|
||||||
|
lua_error_logged: bool = false,
|
||||||
|
lua_str: ?[:0]const u8 = null,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
io: std.Io,
|
||||||
|
alloc: Allocator,
|
||||||
|
log: *LogFile,
|
||||||
|
buf: *TerminalBuffer,
|
||||||
|
file: []const u8,
|
||||||
|
margin: u8,
|
||||||
|
animation_delay: u16,
|
||||||
|
info_line: *InfoLine,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
lang: Lang,
|
||||||
|
full_color: bool,
|
||||||
|
) !Lua {
|
||||||
|
var self: Lua = .{
|
||||||
|
.lua = try zlua.Lua.init(alloc),
|
||||||
|
.allocator = alloc,
|
||||||
|
.terminal_buffer = buf,
|
||||||
|
.instance = null,
|
||||||
|
.log = log,
|
||||||
|
.width = 0,
|
||||||
|
.height = 0,
|
||||||
|
.margin = margin,
|
||||||
|
.io = io,
|
||||||
|
.animation_delay = animation_delay,
|
||||||
|
.info_line = info_line,
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
.lang = lang,
|
||||||
|
.full_color = full_color,
|
||||||
|
};
|
||||||
|
|
||||||
|
// exclude IO and debug libraries
|
||||||
|
self.lua.openBase();
|
||||||
|
self.lua.openBit();
|
||||||
|
self.lua.openMath();
|
||||||
|
self.lua.openString();
|
||||||
|
self.lua.openTable();
|
||||||
|
|
||||||
|
file_loading: {
|
||||||
|
const zf = std.mem.concatWithSentinel(alloc, u8, &[1][]const u8{file}, 0) catch |e| {
|
||||||
|
try self.log.err(self.io, "lua", "failed to allocate file path: {}", .{e});
|
||||||
|
self.lua_str = "failed to allocate file path!";
|
||||||
|
self.info_line.addMessage(lang.err_alloc, self.bg, self.fg) catch {};
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
defer alloc.free(zf);
|
||||||
|
|
||||||
|
// create the ly table
|
||||||
|
self.lua.newTable();
|
||||||
|
self.lua.setGlobal("ly");
|
||||||
|
|
||||||
|
// create ly.width and ly.height from TerminalBuffer width/height
|
||||||
|
self.propagateTerminalBounds();
|
||||||
|
|
||||||
|
_ = self.lua.getGlobal("ly");
|
||||||
|
_ = self.lua.pushString("clock");
|
||||||
|
self.lua.pushFunction(luaLyClock);
|
||||||
|
self.lua.setTable(-3);
|
||||||
|
_ = self.lua.pushString("putCell");
|
||||||
|
self.lua.pushFunction(luaPutCell);
|
||||||
|
self.lua.setTable(-3);
|
||||||
|
_ = self.lua.pushString("putLabel");
|
||||||
|
self.lua.pushFunction(luaPutLabel);
|
||||||
|
self.lua.setTable(-3);
|
||||||
|
_ = self.lua.pushString("putRect");
|
||||||
|
self.lua.pushFunction(luaPutRect);
|
||||||
|
self.lua.setTable(-3);
|
||||||
|
self.lua.setGlobal("ly");
|
||||||
|
|
||||||
|
self.lua.doFile(zf) catch {
|
||||||
|
const errorStr = self.lua.toString(-1) catch unreachable;
|
||||||
|
self.lua_str = try self.allocator.dupeSentinel(u8, errorStr, 0);
|
||||||
|
try self.log.err(self.io, "lua", "lua error: {s}", .{errorStr});
|
||||||
|
self.lua_error = true;
|
||||||
|
break :file_loading;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Lua) void {
|
||||||
|
self.propagateTerminalBounds();
|
||||||
|
if (self.lua_error) {
|
||||||
|
// Ly's Red Screen of Omega-Death:tm:
|
||||||
|
const RED: u32 = if (self.full_color) TerminalBuffer.Color.TRUE_RED else TerminalBuffer.Color.ECOL_RED;
|
||||||
|
const cell = Cell.init(0x2588, RED, RED);
|
||||||
|
for (0..self.terminal_buffer.height) |y|
|
||||||
|
for (0..self.terminal_buffer.width) |x|
|
||||||
|
cell.put(x, y) catch {};
|
||||||
|
if (self.lua_str) |str|
|
||||||
|
for (str, 0..) |c, i| {
|
||||||
|
Cell.init(c, 0x00FFFFFF, 0).put(
|
||||||
|
@divFloor(self.width, 2) - @divFloor(str.len, 2) + i,
|
||||||
|
self.margin + 5,
|
||||||
|
) catch {};
|
||||||
|
};
|
||||||
|
if (!self.lua_error_logged) {
|
||||||
|
self.info_line.addMessage("lua animation failed", self.bg, self.fg) catch {};
|
||||||
|
self.lua_error_logged = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = self.lua.getGlobal("draw");
|
||||||
|
self.lua.protectedCall(.{}) catch {
|
||||||
|
const errorStr = self.lua.toString(-1) catch unreachable;
|
||||||
|
self.lua_str = std.mem.concatWithSentinel(
|
||||||
|
self.allocator,
|
||||||
|
u8,
|
||||||
|
&.{ "cannot call draw(): ", errorStr },
|
||||||
|
0,
|
||||||
|
) catch unreachable;
|
||||||
|
self.log.err(self.io, "lua", "error (cannot call draw()): {s}", .{errorStr}) catch unreachable;
|
||||||
|
self.lua_error = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *Lua, _: *anyopaque) !?usize {
|
||||||
|
return self.animation_delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *Lua) void {
|
||||||
|
if (self.lua_str) |str| self.allocator.free(str);
|
||||||
|
self.lua.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *Lua) *Widget {
|
||||||
|
if (self.instance) |*inst| return inst;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Lua",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagateTerminalBounds(self: *Lua) void {
|
||||||
|
if (self.terminal_buffer.height == self.height and
|
||||||
|
self.terminal_buffer.width == self.width)
|
||||||
|
return;
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn luaLyClock(state: ?*zlua.LuaState) callconv(.c) c_int {
|
||||||
|
var threaded = std.Io.Threaded.init_single_threaded;
|
||||||
|
const lua: *zlua.Lua = @ptrCast(@alignCast(state orelse unreachable));
|
||||||
|
lua.pushInteger(std.Io.Timestamp.now(threaded.io(), .real).toMicroseconds());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn luaPutCell(state: ?*zlua.LuaState) callconv(.c) c_int {
|
||||||
|
const lua: *zlua.Lua = @ptrCast(@alignCast(state orelse unreachable));
|
||||||
|
const MSG = "ly.putCell: cannot convert %s-typed ";
|
||||||
|
const byte = lua.toNumeric(u32, 1) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(1));
|
||||||
|
lua.raiseErrorStr(MSG ++ "byte to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const fg = lua.toNumeric(u32, 2) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(2));
|
||||||
|
lua.raiseErrorStr(MSG ++ "fg to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const bg = lua.toNumeric(u32, 3) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(3));
|
||||||
|
lua.raiseErrorStr(MSG ++ "bg to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const x = lua.toNumeric(usize, 4) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(4));
|
||||||
|
lua.raiseErrorStr(MSG ++ "x to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
const y = lua.toNumeric(usize, 5) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(5));
|
||||||
|
lua.raiseErrorStr(MSG ++ "y to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
TerminalBuffer.setCell(x, y, .{
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
.ch = byte,
|
||||||
|
}) catch {};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn luaPutRect(state: ?*zlua.LuaState) callconv(.c) c_int {
|
||||||
|
const lua: *zlua.Lua = @ptrCast(@alignCast(state orelse unreachable));
|
||||||
|
const MSG = "ly.putRect: cannot convert %s-typed ";
|
||||||
|
const byte = lua.toNumeric(u32, 1) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(1));
|
||||||
|
lua.raiseErrorStr(MSG ++ "byte to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const fg = lua.toNumeric(u32, 2) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(2));
|
||||||
|
lua.raiseErrorStr(MSG ++ "fg to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const bg = lua.toNumeric(u32, 3) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(3));
|
||||||
|
lua.raiseErrorStr(MSG ++ "bg to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const x = lua.toNumeric(usize, 4) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(4));
|
||||||
|
lua.raiseErrorStr(MSG ++ "x to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
const y = lua.toNumeric(usize, 5) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(5));
|
||||||
|
lua.raiseErrorStr(MSG ++ "y to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
const w = lua.toNumeric(usize, 6) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(5));
|
||||||
|
lua.raiseErrorStr(MSG ++ "w to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
const h = lua.toNumeric(usize, 7) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(5));
|
||||||
|
lua.raiseErrorStr(MSG ++ "h to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
for (0..w) |wx| for (0..h) |hy|
|
||||||
|
TerminalBuffer.setCell(x + wx, y + hy, .{
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
.ch = byte,
|
||||||
|
}) catch {};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn luaPutLabel(state: ?*zlua.LuaState) callconv(.c) c_int {
|
||||||
|
const lua: *zlua.Lua = @ptrCast(@alignCast(state orelse unreachable));
|
||||||
|
const MSG = "ly.putLabel: cannot convert %s-typed ";
|
||||||
|
const str = lua.toString(1) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(2));
|
||||||
|
lua.raiseErrorStr(MSG ++ "str to string", .{t.ptr});
|
||||||
|
};
|
||||||
|
const fg = lua.toNumeric(u32, 2) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(2));
|
||||||
|
lua.raiseErrorStr(MSG ++ "fg to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const bg = lua.toNumeric(u32, 3) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(3));
|
||||||
|
lua.raiseErrorStr(MSG ++ "bg to u32", .{t.ptr});
|
||||||
|
};
|
||||||
|
const x = lua.toNumeric(usize, 4) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(4));
|
||||||
|
lua.raiseErrorStr(MSG ++ "x to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
const y = lua.toNumeric(usize, 5) catch {
|
||||||
|
const t = lua.typeName(lua.typeOf(5));
|
||||||
|
lua.raiseErrorStr(MSG ++ "y to usize", .{t.ptr});
|
||||||
|
};
|
||||||
|
TerminalBuffer.drawText(str, x, y, fg, bg) catch {};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -74,6 +74,7 @@ lang: []const u8 = "en",
|
|||||||
login_cmd: ?[]const u8 = null,
|
login_cmd: ?[]const u8 = null,
|
||||||
login_defs_path: []const u8 = "/etc/login.defs",
|
login_defs_path: []const u8 = "/etc/login.defs",
|
||||||
logout_cmd: ?[]const u8 = null,
|
logout_cmd: ?[]const u8 = null,
|
||||||
|
lua_animation_file: []const u8 = build_options.config_directory ++ "/ly/example.lua",
|
||||||
ly_log: ?[]const u8 = "/var/log/ly.log",
|
ly_log: ?[]const u8 = "/var/log/ly.log",
|
||||||
margin_box_h: u8 = 2,
|
margin_box_h: u8 = 2,
|
||||||
margin_box_v: u8 = 1,
|
margin_box_v: u8 = 1,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub const Animation = enum {
|
|||||||
colormix,
|
colormix,
|
||||||
gameoflife,
|
gameoflife,
|
||||||
dur_file,
|
dur_file,
|
||||||
|
lua,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DisplayServer = enum {
|
pub const DisplayServer = enum {
|
||||||
|
|||||||
18
src/main.zig
18
src/main.zig
@@ -29,6 +29,7 @@ const Doom = @import("animations/Doom.zig");
|
|||||||
const DurFile = @import("animations/DurFile.zig");
|
const DurFile = @import("animations/DurFile.zig");
|
||||||
const GameOfLife = @import("animations/GameOfLife.zig");
|
const GameOfLife = @import("animations/GameOfLife.zig");
|
||||||
const Matrix = @import("animations/Matrix.zig");
|
const Matrix = @import("animations/Matrix.zig");
|
||||||
|
const Lua = @import("animations/Lua.zig");
|
||||||
const auth = @import("auth.zig");
|
const auth = @import("auth.zig");
|
||||||
const InfoLine = @import("components/InfoLine.zig");
|
const InfoLine = @import("components/InfoLine.zig");
|
||||||
const Session = @import("components/Session.zig");
|
const Session = @import("components/Session.zig");
|
||||||
@@ -1093,6 +1094,23 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
);
|
);
|
||||||
animation = dur.widget();
|
animation = dur.widget();
|
||||||
},
|
},
|
||||||
|
.lua => {
|
||||||
|
var lua = try Lua.init(
|
||||||
|
state.io,
|
||||||
|
state.allocator,
|
||||||
|
&state.log_file,
|
||||||
|
&state.buffer,
|
||||||
|
state.config.lua_animation_file,
|
||||||
|
state.config.edge_margin,
|
||||||
|
state.config.animation_frame_delay,
|
||||||
|
&state.info_line,
|
||||||
|
state.config.error_fg,
|
||||||
|
state.config.error_bg,
|
||||||
|
state.lang,
|
||||||
|
state.config.full_color,
|
||||||
|
);
|
||||||
|
animation = lua.widget();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
defer if (animation) |a| a.deinit();
|
defer if (animation) |a| a.deinit();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user