This commit is contained in:
Jeeves 2025-04-05 16:12:50 -06:00
commit 8d3892b6de
12 changed files with 516 additions and 0 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto eol=lf

19
.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
# This file is for zig-specific build artifacts.
# If you have OS-specific or editor-specific files to ignore,
# such as *.swp or .DS_Store, put those in your global
# ~/.gitignore and put this in your ~/.gitconfig:
#
# [core]
# excludesfile = ~/.gitignore
#
# Cheers!
# -andrewrk
.zig-cache/
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/

74
build.zig Normal file
View file

@ -0,0 +1,74 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
// const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const wasm_mod = b.createModule(.{
.root_source_file = b.path("src/wasm.zig"),
.target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
}),
// .optimize = .ReleaseSafe,
.optimize = optimize,
});
const wasm = b.addExecutable(.{
.name = "ledbox",
.root_module = wasm_mod,
});
wasm.entry = .disabled;
wasm.rdynamic = true;
wasm.import_memory = true;
wasm.initial_memory = std.wasm.page_size * 2;
wasm.max_memory = std.wasm.page_size * 2;
wasm.stack_size = std.wasm.page_size;
wasm.global_base = 6560;
b.installArtifact(wasm);
// const lib_mod = b.createModule(.{
// .root_source_file = b.path("src/root.zig"),
// .target = target,
// .optimize = optimize,
// });
// const exe_mod = b.createModule(.{
// .root_source_file = b.path("src/main.zig"),
// .target = target,
// .optimize = optimize,
// });
// exe_mod.addImport("ledbox_lib", lib_mod);
// const lib = b.addLibrary(.{
// .linkage = .static,
// .name = "ledbox",
// .root_module = lib_mod,
// });
// b.installArtifact(lib);
// const exe = b.addExecutable(.{
// .name = "ledbox",
// .root_module = exe_mod,
// });
// b.installArtifact(exe);
// const lib_unit_tests = b.addTest(.{
// .root_module = lib_mod,
// });
// const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
// const exe_unit_tests = b.addTest(.{
// .root_module = exe_mod,
// });
// const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
// const test_step = b.step("test", "Run unit tests");
// test_step.dependOn(&run_lib_unit_tests.step);
// test_step.dependOn(&run_exe_unit_tests.step);
}

12
build.zig.zon Normal file
View file

@ -0,0 +1,12 @@
.{
.name = .ledbox,
.version = "0.0.0",
.fingerprint = 0xdc8a67c307abf09e,
.minimum_zig_version = "0.15.0-dev.129+b84db311d",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

21
cert.pem Normal file
View file

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUJ4me6Svr4LN0POTKXD8iMsjFDeUwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNTA0MDUxMzE2MzFaFw0zNTA0
MDMxMzE2MzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCTD1fUqbGo7NeCsxjm2l1BJy1cuR706bwxyLEcTi3O
mV0dopVpBACol7jQ9W8NTGTxdDVe93sYdEL3A2VBmo7QK4fysUbMnQ3j/nvl+Fgj
Z0eGKFuC4ReKmOwdx+kmIrbitYG0EntY7JArV50n1GkF8C3gN5RQOzmFJ7uhu0JR
6B8z5PJH41SB07JjxBfntNaoET88RjekSM/Y+IvMWwcWOjGHtHgzfkajkKOmYmIU
KRuJmXNF6iJS6SSIbkDcMH2d8kdG62Qdv2LcGjkqSkmx5xnT1UmllKRtWHDAoWyN
gUmhflE3UEdr5MEf9JMzE8wmy9Edwq8Gwd3ulUT80MeVAgMBAAGjUzBRMB0GA1Ud
DgQWBBRUcVjjuIm3d2UNzoFVQMqqSyksLTAfBgNVHSMEGDAWgBRUcVjjuIm3d2UN
zoFVQMqqSyksLTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBY
KQhTPqp7m6sypagfDsbS3roZZx6FVbFC4z7WDCGmjzBbXDD3sHxswPpbsrpPJedW
5ZgihOhwNXtKKz+Ey12+YxN4htVLrxmRnogqs3bz421LI1R4wJ3aCHBrS1vjvrgh
vxykm/hckEmxkQjuEASEt/rwgBmEnV+JGEpifWiid7A5WIfJONEzxo/BciS/+z63
+UO7IYe2ZX3u4F0dBzpgESAqEeH35lnxmDDkL1DTltihuQgursiIT810d0rnx1nP
C6NJ3B+1D8aDfrHxsTUgOXQI0pxOm1dNQ9MRwvvgFhLHJX5xyv8TC1bwK31Ntq/x
M0H4tXXOtLanPND7O3Md
-----END CERTIFICATE-----

78
flake.lock generated Normal file
View file

@ -0,0 +1,78 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1743125403,
"narHash": "sha256-ax5yY7IA9XaO+qHiu2XNs7oivWlD4+YG+G8+VaAl8bE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b25d37292b5b6a56a6a508d4632feceb52266333",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"zig2nix": "zig2nix"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"zig2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1743144458,
"narHash": "sha256-0vS/7AWx4O7vI7kJ156tk+nahxQb+HXd/CiFx6ZPBQE=",
"owner": "Cloudef",
"repo": "zig2nix",
"rev": "de3fa1e6475471ca6c76ba9a88dfa4df2befe13c",
"type": "github"
},
"original": {
"owner": "Cloudef",
"repo": "zig2nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

78
flake.nix Normal file
View file

@ -0,0 +1,78 @@
{
description = "Zig project flake";
inputs = {
zig2nix.url = "github:Cloudef/zig2nix";
};
outputs = { zig2nix, ... }: let
flake-utils = zig2nix.inputs.flake-utils;
in (flake-utils.lib.eachDefaultSystem (system: let
# Zig flake helper
# Check the flake.nix in zig2nix project for more options:
# <https://github.com/Cloudef/zig2nix/blob/master/flake.nix>
env = zig2nix.outputs.zig-env.${system} { zig = zig2nix.outputs.packages.${system}.zig-master; };
in with builtins; with env.pkgs.lib; rec {
# Produces clean binaries meant to be ship'd outside of nix
# nix build .#foreign
packages.foreign = env.package {
src = cleanSource ./.;
# Packages required for compiling
nativeBuildInputs = with env.pkgs; [];
# Packages required for linking
buildInputs = with env.pkgs; [];
# Smaller binaries and avoids shipping glibc.
zigPreferMusl = true;
};
# nix build .
packages.default = packages.foreign.override (attrs: {
# Prefer nix friendly settings.
zigPreferMusl = false;
# Executables required for runtime
# These packages will be added to the PATH
zigWrapperBins = with env.pkgs; [];
# Libraries required for runtime
# These packages will be added to the LD_LIBRARY_PATH
zigWrapperLibs = attrs.buildInputs or [];
});
# For bundling with nix bundle for running outside of nix
# example: https://github.com/ralismark/nix-appimage
apps.bundle = {
type = "app";
program = "${packages.foreign}/bin/master";
};
# nix run .
apps.default = env.app [] "zig build run -- \"$@\"";
# nix run .#build
apps.build = env.app [] "zig build \"$@\"";
# nix run .#test
apps.test = env.app [] "zig build test -- \"$@\"";
# nix run .#docs
apps.docs = env.app [] "zig build docs -- \"$@\"";
# nix run .#zig2nix
apps.zig2nix = env.app [] "zig2nix \"$@\"";
# nix develop
devShells.default = env.mkShell {
# Packages required for compiling, linking and running
# Libraries added here will be automatically added to the LD_LIBRARY_PATH and PKG_CONFIG_PATH
nativeBuildInputs = []
++ packages.default.nativeBuildInputs
++ packages.default.buildInputs
++ packages.default.zigWrapperBins
++ packages.default.zigWrapperLibs;
};
}));
}

55
index.html Normal file
View file

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>vLEDMatrix</title>
</head>
<body>
<canvas id="canvas" width="768" height="384" />
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function draw(pixelData) {
ctx.clearRect(0, 0, 768, 384);
for (let y = 0; y < 32; y++) {
for (let x = 0; x < 64; x++) {
const idx = y * 64 * 3 + x * 3;
ctx.beginPath();
ctx.arc(x * 12 + 4, y * 12 + 4, 4, 0, 2 * Math.PI);
ctx.fillStyle = `rgb(${pixelData[idx]} ${pixelData[idx + 1]} ${pixelData[idx + 2]})`;
ctx.fill();
}
}
}
const memory = new WebAssembly.Memory({
initial: 2,
maximum: 2,
});
const importObject = {
env: {
memory: memory,
printi64: (arg) => console.log(arg),
printf32: (arg) => console.log(arg),
timestamp: () => {
return BigInt(Math.floor(Date.now() / 1000));
},
},
};
WebAssembly.instantiateStreaming(fetch("zig-out/bin/ledbox.wasm"), importObject).then((result) => {
const wasmMemoryArray = new Uint8Array(memory.buffer);
setInterval(() => {
result.instance.exports.draw();
const pointer = result.instance.exports.getPixelDataPointer();
const pixelData = wasmMemoryArray.slice(pointer, pointer + 64 * 32 * 3);
draw(pixelData);
}, 100);
});
</script>
<body>
</html>

28
key.pem Normal file
View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTD1fUqbGo7NeC
sxjm2l1BJy1cuR706bwxyLEcTi3OmV0dopVpBACol7jQ9W8NTGTxdDVe93sYdEL3
A2VBmo7QK4fysUbMnQ3j/nvl+FgjZ0eGKFuC4ReKmOwdx+kmIrbitYG0EntY7JAr
V50n1GkF8C3gN5RQOzmFJ7uhu0JR6B8z5PJH41SB07JjxBfntNaoET88RjekSM/Y
+IvMWwcWOjGHtHgzfkajkKOmYmIUKRuJmXNF6iJS6SSIbkDcMH2d8kdG62Qdv2Lc
GjkqSkmx5xnT1UmllKRtWHDAoWyNgUmhflE3UEdr5MEf9JMzE8wmy9Edwq8Gwd3u
lUT80MeVAgMBAAECggEACbwhR02jGhibkjuthqf74QfpOSh0A9MZMdPO4vMgldA8
hBgIJfcth/uLMMyeeQCOn2ve60DHYEYJ93S9FjZXWoZsgxn4eCu0CLIG8kvvTGCr
hmuo1dAgESewXFmodcLPMntrcPKBlyW0ds9fnYxdFhc2d4+WUif+1cax5VySYP46
qJPHS7RBSk2d7ceJijM8E3tYgzk8ecMpsJ5kNDb0fd61P/5BKsKCbgrSqd34b6F9
6GsYdvzicbdNGpaks+M68yEMTQw+kenGs5t6i1Sft0brsnDwl0ee+AgxtpT7qe8L
FeSM+51ocUlhIR3IDp90wnkl30Xuu1Vu9SVl/47HHQKBgQDGfLKBWCIe662G58rg
99opgI2IF5j9yCA9LvNDrPNPxLkU4M4ewuLSCtpD70T/uknU7EudRIc2w2KX2aX8
99LTd0rvM8DZWdwv2Oxzn2FzgQEOeef4+I4QUvFYJJL2CNRubasoNP4XkUjjWEqn
bqki4hFz0MIczTRZShz3/WL0kwKBgQC9q+aLKsAv1GitxaXlG2+KFJtWxJX0dzc4
QX3T2/LbSOWd1wVwBQ2gWTlA0B3lum8+ypBJa/mfdntFEnuYdbBdhELx05nlBQ1a
s+ZNkbmWaIaq/PheGOao5QySasNAZHQ572BbIxtdRPkTY8Rdya1vx0LP1O5HNXiw
xDwpiTpUNwKBgBmktJ83Vrkt865RNgnYm6vu+toRSmfHOjsNXMpqhsjQfqTu4lJh
FVLTqh1KxVxQogdLO/2mg92UYqGBsgs7rVY6vEipJAowrNzVpr7NYZa9nPhU0Z59
5kS/ooSjvQTiYSefLZS1O/qpf4Q3vPViu6FRGbYfy2RTMqcyhwmXB/jXAoGBAKix
6JXkixS/Ve8geKFiGn10QIkWxcyt77YgQbqZyNGSb29IXDS6udjhIpPrxnuZqvHt
FhHHcDiNF6xieP0rx5YVWbleG2VfbfY7RV2+e5M0GnqgDoMaoKSdO+ZKAKkX72vL
5SaJ/f+hperB9Ff6VUCyuFDDML6y50pI7r1+qRtXAoGBAKnEgB62mNZsJMcj0Sma
OEsLDDlZifubKnz6eGTyRnTCvQQLB3ZrGtrCuB/491THbwRGHoWfH433jgybkB+t
curB5sOv7Grn6gyc91RPWh/a4RwUWz1ZhW/bCz77idOfukVG4yCvaEdHCrNc7C4U
5pgpK7wpQ+Egl2Vr+1M6Zs0K
-----END PRIVATE KEY-----

3
src/main.zig Normal file
View file

@ -0,0 +1,3 @@
const std = @import("std");
pub fn main() !void {}

13
src/root.zig Normal file
View file

@ -0,0 +1,13 @@
//! By convention, root.zig is the root source file when making a library. If
//! you are making an executable, the convention is to delete this file and
//! start with main.zig instead.
const std = @import("std");
const testing = std.testing;
pub export fn add(a: i32, b: i32) i32 {
return a + b;
}
test "basic add functionality" {
try testing.expect(add(3, 7) == 10);
}

134
src/wasm.zig Normal file
View file

@ -0,0 +1,134 @@
const std = @import("std");
const width = 64;
const height = 32;
var pixel_data = std.mem.zeroes([height * width * 3]u8);
export fn getPixelDataPointer() [*]u8 {
return @ptrCast(&pixel_data);
}
const marker_color = Color{ .r = 0xAA, .g = 0x61, .b = 0xB7 };
const seconds_color = Color{ .r = 0xFF, .g = 0x98, .b = 0x00 };
const minutes_color = Color{ .r = 0x29, .g = 0xB6, .b = 0xF6 };
const hours_color = Color{ .r = 0x38, .g = 0x8E, .b = 0x3C };
const indicator_from = 6;
const indicator_to = 6;
const seconds_from = 5;
const seconds_to = 5;
const minutes_from = 0;
const minutes_to = 4;
const hours_from = 0;
const hours_to = 3;
const position = Vector2{ .x = 6, .y = 6 };
export fn draw() void {
@memset(&pixel_data, 0);
for (0..12) |a| {
clockHand(position, indicator_from, indicator_to, (@as(f32, @floatFromInt(a)) / 12.0) * 360.0, marker_color);
}
const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = timestamp() - std.time.s_per_hour * 6 };
const day_seconds = epoch_seconds.getDaySeconds();
const seconds: f32 = @floatFromInt(day_seconds.getSecondsIntoMinute());
const minutes: f32 = @floatFromInt(day_seconds.getMinutesIntoHour());
const hours: f32 = @floatFromInt(day_seconds.getHoursIntoDay());
clockHand(position, seconds_from, seconds_to, 270 + (seconds / 60.0) * 360.0, seconds_color);
clockHand(position, minutes_from, minutes_to, 270 + (minutes / 60.0) * 360.0, minutes_color);
clockHand(position, hours_from, hours_to, 270 + (hours / 12.0) * 360.0, hours_color);
}
fn clockHand(pos: Vector2, from: u8, to: u8, angle: f32, color: Color) void {
const radians = angle * std.math.pi / 180.0;
const x: f32 = @floatFromInt(pos.x);
const y: f32 = @floatFromInt(pos.y);
const from_float: f32 = @floatFromInt(from);
const to_float: f32 = @floatFromInt(to);
line(
.{
.x = @intFromFloat(@round(x + @cos(radians) * from_float)),
.y = @intFromFloat(@round(y + @sin(radians) * from_float)),
},
.{
.x = @intFromFloat(@round(x + @cos(radians) * to_float)),
.y = @intFromFloat(@round(y + @sin(radians) * to_float)),
},
color,
);
}
fn line(from: Vector2, to: Vector2, color: Color) void {
const dx = @abs(@as(i16, @intCast(to.x)) - @as(i16, @intCast(from.x)));
const dy = @abs(@as(i16, @intCast(to.y)) - @as(i16, @intCast(from.y)));
const sx: i2 = if (from.x < to.x) 1 else -1;
const sy: i2 = if (from.y < to.y) 1 else -1;
var v = from;
if (dx > dy) {
var err = @as(f32, @floatFromInt(dx)) / 2.0;
while (v.x != to.x) {
setPixel(v, color);
err -= @floatFromInt(dy);
if (err < 0) {
v.y = @intCast(@as(i16, @intCast(v.y)) + sy);
err += @floatFromInt(dx);
}
v.x = @intCast(@as(i16, @intCast(v.x)) + sx);
}
} else {
var err = @as(f32, @floatFromInt(dy)) / 2.0;
while (v.y != to.y) {
setPixel(v, color);
err -= @floatFromInt(dx);
if (err < 0) {
v.x = @intCast(@as(i16, @intCast(v.x)) + sx);
err += @floatFromInt(dy);
}
v.y = @intCast(@as(i16, @intCast(v.y)) + sy);
}
}
setPixel(v, color);
}
inline fn setPixel(pos: Vector2, color: Color) void {
const ptr: *[height][width][3]u8 = @ptrCast(&pixel_data);
ptr[pos.y][pos.x][0] = color.r;
ptr[pos.y][pos.x][1] = color.g;
ptr[pos.y][pos.x][2] = color.b;
}
inline fn getPixel(pos: Vector2) Color {
const ptr: *[height][width][3]u8 = @ptrCast(&pixel_data);
return .{
.r = ptr[pos.x][pos.x][0],
.g = ptr[pos.x][pos.x][1],
.b = ptr[pos.x][pos.x][2],
};
}
const Color = struct {
r: u8,
g: u8,
b: u8,
pub const WHITE = Color{ .r = 0xFF, .g = 0xFF, .b = 0xFF };
pub const BLACK = Color{ .r = 0x00, .g = 0x00, .b = 0x00 };
};
const Vector2 = struct {
x: u8,
y: u8,
};
extern fn timestamp() u64;
extern fn printi64(arg: i64) void;
extern fn printf32(arg: f32) void;