commit 8d3892b6dea072f692805c9d9450f0d6f559fcf5 Author: Jeeves Date: Sat Apr 5 16:12:50 2025 -0600 init diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f27e682 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..bdc469f --- /dev/null +++ b/build.zig @@ -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); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..05c41d1 --- /dev/null +++ b/build.zig.zon @@ -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", + }, +} diff --git a/cert.pem b/cert.pem new file mode 100644 index 0000000..34e538b --- /dev/null +++ b/cert.pem @@ -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----- diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..68aab57 --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c127471 --- /dev/null +++ b/flake.nix @@ -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: + # + 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; + }; + })); +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..a4dea70 --- /dev/null +++ b/index.html @@ -0,0 +1,55 @@ + + + + vLEDMatrix + + + + + + + + diff --git a/key.pem b/key.pem new file mode 100644 index 0000000..b3d32c5 --- /dev/null +++ b/key.pem @@ -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----- diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..13ab026 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub fn main() !void {} diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..27d2be8 --- /dev/null +++ b/src/root.zig @@ -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); +} diff --git a/src/wasm.zig b/src/wasm.zig new file mode 100644 index 0000000..f869897 --- /dev/null +++ b/src/wasm.zig @@ -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;