Compare commits
7 Commits
26c52dd278
...
a7b917d3e9
Author | SHA1 | Date | |
---|---|---|---|
a7b917d3e9 | |||
80d0f8c377 | |||
753a06c54d | |||
59fbf04da6 | |||
ad80f250a6 | |||
91fc37d30a | |||
9ca7fd0864 |
|
@ -66,9 +66,13 @@ pub fn build(b: *std.build.Builder) !void {
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
exe_tests.linkLibrary(im_dep.artifact("MagickWand"));
|
||||||
|
exe_tests.linkLibrary(z_dep.artifact("z"));
|
||||||
|
exe_tests.linkLibrary(i2cdriver);
|
||||||
|
exe_tests.addIncludePath("lib/i2cdriver");
|
||||||
|
|
||||||
const test_step = b.step("test", "Run unit tests");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
test_step.dependOn(&exe_tests.step);
|
test_step.dependOn(&exe_tests.run().step);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be able to remove this
|
// Should be able to remove this
|
||||||
|
|
158
src/main.zig
158
src/main.zig
|
@ -24,7 +24,7 @@ const LINES = 8;
|
||||||
// Device specifications
|
// Device specifications
|
||||||
const PAGES = 8;
|
const PAGES = 8;
|
||||||
|
|
||||||
var lines: [LINES]*const [:0]u8 = undefined;
|
var lines: [LINES]*[:0]u8 = undefined;
|
||||||
|
|
||||||
fn usage(args: [][]u8) !void {
|
fn usage(args: [][]u8) !void {
|
||||||
const writer = std.io.getStdErr().writer();
|
const writer = std.io.getStdErr().writer();
|
||||||
|
@ -33,40 +33,85 @@ fn usage(args: [][]u8) !void {
|
||||||
try writer.print("\te.g. \"-1 'hello world'\" will display \"hello world\" on line 1\n", .{});
|
try writer.print("\te.g. \"-1 'hello world'\" will display \"hello world\" on line 1\n", .{});
|
||||||
std.os.exit(1);
|
std.os.exit(1);
|
||||||
}
|
}
|
||||||
|
const Options = struct {
|
||||||
|
background_filename: [:0]u8,
|
||||||
|
device_file: [:0]u8,
|
||||||
|
};
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const alloc = std.heap.c_allocator;
|
const alloc = std.heap.c_allocator;
|
||||||
//defer alloc.deinit();
|
//defer alloc.deinit();
|
||||||
const args = try std.process.argsAlloc(alloc);
|
const args = try std.process.argsAlloc(alloc);
|
||||||
defer std.process.argsFree(alloc, args);
|
defer std.process.argsFree(alloc, args);
|
||||||
if (args.len < 2) try usage(args);
|
|
||||||
const prefix = "/dev/ttyUSB";
|
|
||||||
const device = try alloc.dupeZ(u8, args[1]);
|
|
||||||
defer alloc.free(device);
|
|
||||||
if (!std.mem.eql(u8, device, "-") and !std.mem.startsWith(u8, device, prefix)) try usage(args);
|
|
||||||
|
|
||||||
// stdout is for the actual output of your application, for example if you
|
|
||||||
// are implementing gzip, then only the compressed bytes should be sent to
|
|
||||||
// stdout, not any debugging messages.
|
|
||||||
const stdout_file = std.io.getStdOut().writer();
|
const stdout_file = std.io.getStdOut().writer();
|
||||||
var bw = std.io.bufferedWriter(stdout_file);
|
var bw = std.io.bufferedWriter(stdout_file);
|
||||||
const stdout = bw.writer();
|
const stdout = bw.writer();
|
||||||
defer bw.flush() catch unreachable; // don't forget to flush!
|
defer bw.flush() catch unreachable; // don't forget to flush!
|
||||||
|
|
||||||
var filename: [:0]u8 = @constCast("");
|
// If we have stdin redirected, we want to process that before
|
||||||
var b: [1]u8 = undefined;
|
// we take command line options. So cli will overwrite any lines specified
|
||||||
const nothing: [:0]u8 = try std.fmt.bufPrintZ(&b, "", .{});
|
// from the file
|
||||||
|
const stdin_file = std.io.getStdIn();
|
||||||
|
var stdin_data: [WIDTH * HEIGHT + 1]u8 = undefined;
|
||||||
|
// Need this to support deallocation of memory
|
||||||
|
var line_inx: usize = 0;
|
||||||
|
var stdin_lines: [LINES][:0]u8 = undefined;
|
||||||
|
defer {
|
||||||
|
for (0..line_inx) |i| {
|
||||||
|
alloc.free(stdin_lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var nothing: [:0]u8 = @constCast("");
|
||||||
for (lines, 0..) |_, i| {
|
for (lines, 0..) |_, i| {
|
||||||
lines[i] = ¬hing;
|
lines[i] = ¬hing;
|
||||||
}
|
}
|
||||||
|
if (!stdin_file.isTty()) {
|
||||||
|
const read = try stdin_file.readAll(&stdin_data);
|
||||||
|
if (read == stdin_data.len) {
|
||||||
|
try std.io.getStdErr().writer().print("ERROR: data provided exceeds what can be sent to device!\n", .{});
|
||||||
|
try usage(args);
|
||||||
|
}
|
||||||
|
var read_data = stdin_data[0..read];
|
||||||
|
var it = std.mem.split(u8, read_data, "\n");
|
||||||
|
while (it.next()) |line| {
|
||||||
|
if (line.len == 0) continue;
|
||||||
|
stdin_lines[line_inx] = try alloc.dupeZ(u8, line);
|
||||||
|
lines[line_inx] = &stdin_lines[line_inx];
|
||||||
|
line_inx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std.debug.print("delme: {s}\n", .{lines[0].*});
|
||||||
|
const opts = try processArgs(alloc, args, &lines);
|
||||||
|
defer alloc.destroy(opts);
|
||||||
|
if (opts.background_filename.len > 0) try stdout.print("Converting {s}\n", .{opts.background_filename});
|
||||||
|
var pixels: [WIDTH * HEIGHT]u8 = undefined;
|
||||||
|
try convertImage(opts.background_filename, &pixels, textForLine);
|
||||||
|
try bw.flush();
|
||||||
|
|
||||||
|
// We should take the linux device file here, then inspect for ttyUSB vs
|
||||||
|
// i2c whatever and do the right thing from there...
|
||||||
|
try sendPixels(&pixels, opts.device_file, 0x3c);
|
||||||
|
|
||||||
|
// try stdout.print("Run `zig build test` to run the tests.\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn processArgs(allocator: std.mem.Allocator, args: [][:0]u8, line_array: *[LINES]*const [:0]u8) !*Options {
|
||||||
|
if (args.len < 2) try usage(args);
|
||||||
|
const prefix = "/dev/ttyUSB";
|
||||||
|
var opts = try allocator.create(Options);
|
||||||
|
opts.device_file = args[1];
|
||||||
|
if (!std.mem.eql(u8, opts.device_file, "-") and !std.mem.startsWith(u8, opts.device_file, prefix)) try usage(args);
|
||||||
|
|
||||||
|
opts.background_filename = @constCast("");
|
||||||
var is_filename = false;
|
var is_filename = false;
|
||||||
var line_number: ?usize = null;
|
var line_number: ?usize = null;
|
||||||
for (args, 0..) |arg, i| {
|
for (args[1..], 1..) |arg, i| {
|
||||||
if (std.mem.eql(u8, "-bg", arg)) {
|
if (std.mem.eql(u8, "-bg", arg)) {
|
||||||
is_filename = true;
|
is_filename = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (is_filename) {
|
if (is_filename) {
|
||||||
filename = args[i]; // arg capture changes value...
|
opts.background_filename = args[i]; // arg capture changes value...
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ((arg[0] == '-' and arg.len > 1) and areDigits(arg[1..])) {
|
if ((arg[0] == '-' and arg.len > 1) and areDigits(arg[1..])) {
|
||||||
|
@ -81,30 +126,13 @@ pub fn main() !void {
|
||||||
);
|
);
|
||||||
std.os.exit(1);
|
std.os.exit(1);
|
||||||
}
|
}
|
||||||
std.debug.print("line {d} text: \"{s}\"\n", .{ line, arg });
|
std.log.debug("line {d} text: \"{s}\"\n", .{ line, arg });
|
||||||
lines[line] = &args[i];
|
line_array.*[line] = &args[i];
|
||||||
line_number = null;
|
line_number = null;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return opts;
|
||||||
if (filename.len > 0) try stdout.print("Converting {s}\n", .{filename});
|
|
||||||
var pixels: [WIDTH * HEIGHT]u8 = undefined;
|
|
||||||
try convertImage(alloc, filename, &pixels, textForLine);
|
|
||||||
try stdout.print("Sending pixels to display\n", .{});
|
|
||||||
// var i: usize = 0;
|
|
||||||
// while (i < HEIGHT) {
|
|
||||||
// try stdout.print("{d:0>2}: {s}\n", .{ i, fmtSliceGreyscaleImage(pixels[(i * WIDTH)..((i + 1) * WIDTH)]) });
|
|
||||||
// // try stdout.print("{d:0>2}: {s}\n", .{ i, std.fmt.fmtSliceHexLower(pixels[(i * WIDTH)..((i + 1) * WIDTH)]) });
|
|
||||||
// i += 1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// We should take the linux device file here, then inspect for ttyUSB vs
|
|
||||||
// i2c whatever and do the right thing from there...
|
|
||||||
try sendPixels(&pixels, device, 0x3c);
|
|
||||||
try stdout.print("done\n", .{});
|
|
||||||
|
|
||||||
// try stdout.print("Run `zig build test` to run the tests.\n", .{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn areDigits(bytes: []u8) bool {
|
fn areDigits(bytes: []u8) bool {
|
||||||
|
@ -167,6 +195,7 @@ fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id
|
||||||
defer bw.flush() catch unreachable; // don't forget to flush!
|
defer bw.flush() catch unreachable; // don't forget to flush!
|
||||||
try stdout.print("Connecting to I2CDriver on {s}. If progress stalls, unplug device and re-insert.\n", .{c_file});
|
try stdout.print("Connecting to I2CDriver on {s}. If progress stalls, unplug device and re-insert.\n", .{c_file});
|
||||||
try bw.flush();
|
try bw.flush();
|
||||||
|
try stdout.print("Sending pixels to display...", .{});
|
||||||
c.i2c_connect(&i2c, c_file);
|
c.i2c_connect(&i2c, c_file);
|
||||||
try stdout.print("Device connected\n", .{});
|
try stdout.print("Device connected\n", .{});
|
||||||
if (i2c.connected != 1) return error.I2CConnectionFailed;
|
if (i2c.connected != 1) return error.I2CConnectionFailed;
|
||||||
|
@ -189,9 +218,11 @@ fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id
|
||||||
|
|
||||||
// Write data to device
|
// Write data to device
|
||||||
try i2cWrite(&i2c, &pixels_write_command);
|
try i2cWrite(&i2c, &pixels_write_command);
|
||||||
for (0..HEIGHT) |i| {
|
// Leaving this here because we'll need it later I think
|
||||||
std.debug.print("{d:0>2}: {s}\n", .{ i, fmtSliceGreyscaleImage(pixels[(i * WIDTH)..((i + 1) * WIDTH)]) });
|
// for (0..HEIGHT) |i| {
|
||||||
}
|
// std.log.debug("{d:0>2}: {s}\n", .{ i, fmtSliceGreyscaleImage(pixels[(i * WIDTH)..((i + 1) * WIDTH)]) });
|
||||||
|
// }
|
||||||
|
try stdout.print("done\n", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void {
|
fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void {
|
||||||
|
@ -274,8 +305,7 @@ fn reportMagickError(mw: ?*c.MagickWand) !void {
|
||||||
fn textForLine(line: usize) []u8 {
|
fn textForLine(line: usize) []u8 {
|
||||||
return lines[line].*;
|
return lines[line].*;
|
||||||
}
|
}
|
||||||
fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const fn (usize) []u8) !void {
|
fn convertImage(filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const fn (usize) []u8) !void {
|
||||||
_ = alloc;
|
|
||||||
c.MagickWandGenesis();
|
c.MagickWandGenesis();
|
||||||
defer c.MagickWandTerminus();
|
defer c.MagickWandTerminus();
|
||||||
var mw = c.NewMagickWand();
|
var mw = c.NewMagickWand();
|
||||||
|
@ -309,13 +339,13 @@ fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HE
|
||||||
const w = c.MagickGetImageWidth(mw);
|
const w = c.MagickGetImageWidth(mw);
|
||||||
const h = c.MagickGetImageHeight(mw);
|
const h = c.MagickGetImageHeight(mw);
|
||||||
|
|
||||||
std.debug.print("Original dimensions: {d}x{d}\n", .{ w, h });
|
std.log.debug("Original dimensions: {d}x{d}\n", .{ w, h });
|
||||||
// This should be 48x64 with our test
|
// This should be 48x64 with our test
|
||||||
// Command line resize works differently than this. Here we need to find
|
// Command line resize works differently than this. Here we need to find
|
||||||
// new width and height based on the input aspect ratio ourselves
|
// new width and height based on the input aspect ratio ourselves
|
||||||
const resize_dimensions = getNewDimensions(w, h, WIDTH, HEIGHT);
|
const resize_dimensions = getNewDimensions(w, h, WIDTH, HEIGHT);
|
||||||
|
|
||||||
std.debug.print("Dimensions for resize: {d}x{d}\n", .{ resize_dimensions.width, resize_dimensions.height });
|
std.log.debug("Dimensions for resize: {d}x{d}\n", .{ resize_dimensions.width, resize_dimensions.height });
|
||||||
|
|
||||||
status = c.MagickResizeImage(mw, resize_dimensions.width, resize_dimensions.height, c.UndefinedFilter);
|
status = c.MagickResizeImage(mw, resize_dimensions.width, resize_dimensions.height, c.UndefinedFilter);
|
||||||
if (status == c.MagickFalse)
|
if (status == c.MagickFalse)
|
||||||
|
@ -362,7 +392,6 @@ fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HE
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std.debug.print("left_spaces: {d}. Text: \"{s}\"\n", .{ left_spaces, text });
|
|
||||||
x += (FONT_WIDTH * left_spaces);
|
x += (FONT_WIDTH * left_spaces);
|
||||||
mw = try drawString(mw, text[@intCast(usize, left_spaces)..], x, y);
|
mw = try drawString(mw, text[@intCast(usize, left_spaces)..], x, y);
|
||||||
}
|
}
|
||||||
|
@ -488,23 +517,38 @@ fn getNewDimensions(width: usize, height: usize, desired_width: usize, desired_h
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logo() !void {
|
test "gets correct bytes" {
|
||||||
c.MagickWandGenesis();
|
const bg_file: [:0]u8 = @constCast("logo:");
|
||||||
|
const opts = .{ .background_filename = bg_file, .device_file = "-" };
|
||||||
// Create a wand
|
const empty: [:0]u8 = @constCast("");
|
||||||
var mw = c.NewMagickWand();
|
for (&lines) |*line| {
|
||||||
|
line.* = ∅
|
||||||
// Read the input image
|
|
||||||
_ = c.MagickReadImage(mw, "logo:"); // TODO: What is the return val?
|
|
||||||
// write it
|
|
||||||
_ = c.MagickWriteImage(mw, "logo.jpg"); // TODO: What is the return val?
|
|
||||||
|
|
||||||
// Tidy up
|
|
||||||
if (mw) |w| mw = c.DestroyMagickWand(w);
|
|
||||||
|
|
||||||
c.MagickWandTerminus();
|
|
||||||
}
|
}
|
||||||
|
const line: [:0]u8 = @constCast("Hello\\!");
|
||||||
|
lines[5] = &line;
|
||||||
|
var pixels: [WIDTH * HEIGHT]u8 = undefined;
|
||||||
|
|
||||||
|
var expected_pixels: *const [WIDTH * HEIGHT]u8 = @embedFile("testExpectedBytes.bin");
|
||||||
|
|
||||||
|
// [_]u8{..,..,..}
|
||||||
|
try convertImage(opts.background_filename, &pixels, textForLine);
|
||||||
|
// try writeBytesToFile("testExpectedBytes.bin", &pixels);
|
||||||
|
try std.testing.expectEqualSlices(u8, expected_pixels, &pixels);
|
||||||
|
}
|
||||||
|
fn writeBytesToFile(filename: []const u8, bytes: []u8) !void {
|
||||||
|
const file = try std.fs.cwd().createFile(filename, .{
|
||||||
|
.read = false,
|
||||||
|
.truncate = true,
|
||||||
|
.lock = .Exclusive,
|
||||||
|
.lock_nonblocking = false,
|
||||||
|
.mode = 0o666,
|
||||||
|
.intended_io_mode = .blocking,
|
||||||
|
});
|
||||||
|
defer file.close();
|
||||||
|
const writer = file.writer();
|
||||||
|
try writer.writeAll(bytes);
|
||||||
|
// try writer.print("pub const chars = &[_][]const u8{{\n", .{});
|
||||||
|
}
|
||||||
test "simple test" {
|
test "simple test" {
|
||||||
var list = std.ArrayList(i32).init(std.testing.allocator);
|
var list = std.ArrayList(i32).init(std.testing.allocator);
|
||||||
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
|
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
|
||||||
|
|
BIN
src/testExpectedBytes.bin
Normal file
BIN
src/testExpectedBytes.bin
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user