From 855577e9dd1a846c08de0716ea31fdd33c5e8f33 Mon Sep 17 00:00:00 2001 From: Janis Date: Mon, 19 Dec 2022 15:00:27 +0100 Subject: [PATCH] added commandline options --- box.zig | 81 ++++++++++++++++++++++- build.zig | 10 +++ main.zig | 188 +++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 225 insertions(+), 54 deletions(-) diff --git a/box.zig b/box.zig index 912ef34..c4b4e44 100644 --- a/box.zig +++ b/box.zig @@ -199,6 +199,41 @@ pub const Box = struct { pub fn size(self: *Self) usize { return self.extents.size(); } + + pub fn parseFromStr(str: [:0]const u8) !struct { + box: Box, + label: ?[]u8, + + pub fn destroy(self: @This()) void { + if (self.label) |label| { + std.c.free(label.ptr); + } + } + } { + var box: Box = undefined; + var label: ?[*] u8 = null; + + const Stdio = @cImport({@cInclude("stdio.h");@cInclude("string.h");}); + if (Stdio.sscanf( + str.ptr, + "%d,%d %dx%d %m[^\n]", + &box.position.x, + &box.position.y, + &box.extents.width, + &box.extents.height, + &label, + ) < 4) { + return error.ScanFFailed; + } + + const lbl = if (label == null) null else + label.?[0..Stdio.strlen(label)]; + + return .{ + .box = box, + .label = lbl, + }; + } }; pub const Boxes = struct { @@ -244,11 +279,11 @@ pub const Boxes = struct { }; } - pub fn deinit(self: *Self) void { + pub fn deinit(self: Self) void { self.boxes.deinit(); } - pub fn iter(self: *Self) Iterator { + pub fn iter(self: *const Self) Iterator { return .{ .items = self.boxes.items, }; @@ -339,7 +374,49 @@ test "boxes" { try boxes.boxes.append(a4); } +test "parse box" { + std.testing.refAllDecls(Box); + + { + const expected = Box{ + .position = .{.x = 10, .y = 10,}, + .extents = .{.width = 100, .height = 100,}, + }; + + const box = try Box.parseFromStr("10,10 100x100 cool_box"); + defer box.destroy(); + std.debug.assert(box.box.eq(expected)); + std.debug.assert(std.mem.eql(u8, box.label.?, "cool_box")); + } + + { + const expected = Box{ + .position = .{.x = 10, .y = -10,}, + .extents = .{.width = 100, .height = 100,}, + }; + + const box = try Box.parseFromStr("10,-10 100x100 cool_\nbox"); + defer box.destroy(); + std.debug.assert(box.box.eq(expected)); + std.debug.assert(std.mem.eql(u8, box.label.?, "cool_")); + } + + { + const expected = Box{ + .position = .{.x = 10, .y = 10,}, + .extents = .{.width = 100, .height = 100,}, + }; + + const box = try Box.parseFromStr("10,10 100x100 \n"); + defer box.destroy(); + std.debug.assert(box.box.eq(expected)); + std.debug.assert(box.label == null); + } +} + test "box" { + std.testing.refAllDecls(Box); + const a = Box.default(); const a2 = Box{.extents = .{.width = 1, .height = 1,}}; const b = Box.default(); diff --git a/build.zig b/build.zig index 5a35abd..359c427 100644 --- a/build.zig +++ b/build.zig @@ -72,6 +72,16 @@ pub fn build(b: *std.build.Builder) void { exe_tests.setBuildMode(mode); exe_tests.linkLibC(); + exe_tests.step.dependOn(&scanner.step); + exe_tests.addPackage(wayland); + exe_tests.addPackage(zig_args); + + exe_tests.linkSystemLibrary("wayland-client"); + exe_tests.linkSystemLibrary("xkbcommon"); + exe_tests.linkSystemLibrary("cairo"); + + scanner.addCSource(exe_tests); + const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&exe_tests.step); } diff --git a/main.zig b/main.zig index cbedab8..c630c3b 100644 --- a/main.zig +++ b/main.zig @@ -10,6 +10,7 @@ const list = wayland.server.wl.list; const Utils = @import("box.zig"); const Point = Utils.Point; const Box = Utils.Box; +const Boxes = Utils.Boxes; const Size = Utils.Size; const Selection = Utils.Selection; @@ -222,49 +223,6 @@ fn ListItem(comptime T: type) type { }; } -const Options = struct { - const Self = @This(); - - help: bool = false, - @"display-dimensions": bool = false, - background: ?u32 = null, - border: ?u32 = null, - selection: ?u32 = null, - choice: ?u32 = null, - format: ?[]const u8 = null, - @"font-family": ?[]const u8 = null, - @"border-weight": ?u32 = null, - @"single-point": bool = false, - @"output-boxes": bool = false, - @"restrict-selection": bool = false, - // TODO: parse this in the format of W:H instead of a fraction - @"aspect-ratio": ?f64 = null, - - pub const shorthands = .{ - .h = "help", - .b = "background", - .c = "border", - .s = "selection", - .B = "choice", - .f = "format", - .F = "font-family", - .w = "border-weight", - .p = "single-point", - .o = "output-boxes", - .r = "restrict-selection", - .a = "aspect-ratio", - }; - - const usage = @embedFile("usage"); - - fn parse() !zig_args.ParseArgsResult(Self, null) { - const self = try zig_args.parseForCurrentProcess(Self, std.heap.page_allocator, .print); - // TODO: print help - - return self; - } -}; - const Output = struct { const Self = @This(); @@ -343,10 +301,29 @@ const Output = struct { fn render(self: *Self, buffer: *Buffer) void { const cairo = &buffer.cairo; + const opts = self.state.getOptions(); + + // clear screen Cairo.cairo_set_operator(cairo.ctx, Cairo.CAIRO_OPERATOR_SOURCE); - setSourceU32(cairo.ctx, 0xFFFFFF40); + setSourceU32(cairo.ctx, opts.background); Cairo.cairo_paint(cairo.ctx); + // draw in_boxes + if (self.state.config.in_boxes) |boxes| { + std.debug.print("in_boxes: {}\n", .{boxes}); + var iter = boxes.iter(); + while (iter.next()) |box| { + if (box.intersects(&self.logical_geometry)) { + const corrected_box = box.translated(self.logical_geometry.position.inverted()); + + drawRect(buffer, corrected_box, opts.choice); + Cairo.cairo_fill(cairo.ctx); + } + } + } + + + // draw selection for each seat var seat_iter = self.state.seats.iterator(.forward); while (seat_iter.next()) |link| { const seat = link.get(); @@ -359,11 +336,11 @@ const Output = struct { const corrected_box = box.translated(self.logical_geometry.position.inverted()); std.debug.print("selection: {}\n", .{corrected_box}); - drawRect(buffer, corrected_box, 0x0); + drawRect(buffer, corrected_box, opts.selection); Cairo.cairo_fill(cairo.ctx); - Cairo.cairo_set_line_width(cairo.ctx, 2.0); - drawRect(buffer, corrected_box, 0x000000FF); + Cairo.cairo_set_line_width(cairo.ctx, @intToFloat(f64, opts.@"border-weight")); + drawRect(buffer, corrected_box, opts.border); Cairo.cairo_stroke(cairo.ctx); // TODO: draw dimensions @@ -897,8 +874,7 @@ const State = struct { seats: *ListItem(Seat).Head() = undefined, outputs: *ListItem(Output).Head() = undefined, - fn create() !*Self { - const ally = std.heap.c_allocator; + fn create(ally: std.mem.Allocator) !*Self { const dpy = try wl.Display.connect(null); const registry = try dpy.getRegistry(); @@ -1020,7 +996,7 @@ const State = struct { const Init = struct { const Self = @This(); - ally: std.mem.Allocator = std.heap.c_allocator, + ally: std.mem.Allocator = undefined, running: bool = true, @@ -1042,6 +1018,8 @@ const State = struct { seats: *ListItem(Seat).Head() = undefined, outputs: *ListItem(Output).Head() = undefined, + config: Input = undefined, + fn init(self: *Self, uninit: *Uninit) !void { const xkb_context = xkb.xkb_context_new(xkb.XKB_CONTEXT_NO_FLAGS) orelse return error.NoXkbContext; @@ -1076,6 +1054,8 @@ const State = struct { .seats = uninit.seats, .outputs = uninit.outputs, + + .config = try Input.init(uninit.ally), }; // i hope this is fine but appears to be requried to reset the listener on the registry @@ -1110,6 +1090,10 @@ const State = struct { } } + fn getOptions(self: *Self) *Args { + return &self.config.args.options; + } + fn stuff(self: *Self) !void { while (self.running) { if (self.dpy.dispatch() != .SUCCESS) return error.DispatchFailed; @@ -1165,18 +1149,118 @@ const State = struct { self.registry.destroy(); self.ally.destroy(self.registry_listener); + self.config.deinit(); + self.ally.free(self.cursor_theme); } }; }; -pub fn main() !void { +const Args = struct { + const Self = @This(); + + help: bool = false, + @"display-dimensions": bool = false, + background: u32 = 0xFFFFFF40, + border: u32 = 0x000000FF, + selection: u32 = 0x00000000, + choice: u32 = 0xFFFFFF40, + format: ?[]const u8 = null, + @"font-family": ?[]const u8 = null, + @"border-weight": u32 = 2, + @"single-point": bool = false, + @"output-boxes": bool = false, + @"restrict-selection": bool = false, + // TODO: parse this in the format of W:H instead of a fraction + @"aspect-ratio": ?f64 = null, + + pub const shorthands = .{ + .h = "help", + .b = "background", + .c = "border", + .s = "selection", + .B = "choice", + .f = "format", + .F = "font-family", + .w = "border-weight", + .p = "single-point", + .o = "output-boxes", + .r = "restrict-selection", + .a = "aspect-ratio", + }; + + const usage = @embedFile("usage"); + + const ResultType = zig_args.ParseArgsResult(Self, null); + + fn parse(ally: std.mem.Allocator) !ResultType { + const self = try zig_args.parseForCurrentProcess(Self, ally, .print); + // TODO: print help + + return self; + } +}; + +const Input = struct { + const Self = @This(); + + args: Args.ResultType, + in_boxes: ?Boxes = null, + + fn init(ally: std.mem.Allocator) !Self { + const args = try Args.parse(ally); + const in_boxes = try readInBoxes(ally); + + return Self { + .in_boxes = in_boxes, + .args = args, + }; + } + + fn deinit(self: Self) void { + self.args.deinit(); + if (self.in_boxes) |boxes| { + boxes.deinit(); + } + } + + fn readInBoxes(alloc: std.mem.Allocator) !?Boxes { + var buf = [_]u8{0} ** 256; + if (!std.io.getStdIn().isTty()) { + var in_boxes = Boxes.init(alloc); + errdefer in_boxes.deinit(); + + var reader = std.io.getStdIn().reader(); + while (try reader.readUntilDelimiterOrEof(&buf, '\n')) |line| { + if (line.len == 0) continue; + + buf[line.len + 1] = 0; + const sentineld = buf[0..line.len + 1:0]; + + const result = try Box.parseFromStr(sentineld); + defer result.destroy(); + + try in_boxes.boxes.append(result.box); + } + + return in_boxes; + } else { + return null; + } + } +}; + +pub fn run(ally: std.mem.Allocator) !void { // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); - const state = try State.Uninit.create(); + const state = try State.Uninit.create(ally); const init = try state.intoInit(); defer init.deinit(); try init.stuff(); } + +pub fn main() !void { + try run(std.heap.c_allocator); +}