Compare commits
5 commits
6742491be6
...
78955de959
Author | SHA1 | Date | |
---|---|---|---|
|
78955de959 | ||
|
cc205d45df | ||
|
dda61ec788 | ||
|
dd35c163d4 | ||
|
855577e9dd |
101
box.zig
101
box.zig
|
@ -161,6 +161,26 @@ pub const Box = struct {
|
|||
return getInner(T, self.position.y);
|
||||
}
|
||||
|
||||
/// lowest X-axis value
|
||||
pub fn getLeftMost(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.x);
|
||||
}
|
||||
|
||||
/// highest X-axis value
|
||||
pub fn getRightMost(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.x + self.getWidth(isize));
|
||||
}
|
||||
|
||||
/// lowest Y-axis value
|
||||
pub fn getTopMost(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.y);
|
||||
}
|
||||
|
||||
/// highest Y-axis value
|
||||
pub fn getBottomMost(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.y + self.getHeight(isize));
|
||||
}
|
||||
|
||||
pub fn getWidth(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.extents.width);
|
||||
}
|
||||
|
@ -199,6 +219,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 +299,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 +394,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();
|
||||
|
|
10
build.zig
10
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);
|
||||
}
|
||||
|
|
3
in_boxes
Normal file
3
in_boxes
Normal file
|
@ -0,0 +1,3 @@
|
|||
10,10 100x50 cool_label
|
||||
100,100 100x50 this is also a very cool label innit?
|
||||
|
407
main.zig
407
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();
|
||||
|
||||
|
@ -288,8 +246,6 @@ const Output = struct {
|
|||
frame_callback: ?*wl.Callback = null,
|
||||
|
||||
fn init(self: *Self, state: *State.Init) !void {
|
||||
std.debug.print("output.init()\n", .{});
|
||||
|
||||
self.state = state;
|
||||
try self.buffers.init(state);
|
||||
|
||||
|
@ -318,7 +274,6 @@ const Output = struct {
|
|||
self.output.destroy();
|
||||
}
|
||||
|
||||
|
||||
fn setSourceU32(c: *Cairo.cairo_t, color: u32) void {
|
||||
Cairo.cairo_set_source_rgba(
|
||||
c,
|
||||
|
@ -341,12 +296,58 @@ const Output = struct {
|
|||
);
|
||||
}
|
||||
|
||||
fn drawDimensions(self: *Self, c: *Cairo.cairo_t, color: u32, box: *const Box) void {
|
||||
const opts = self.state.getOptions();
|
||||
const font_family = opts.@"font-family".ptr;
|
||||
Cairo.cairo_select_font_face(c,
|
||||
font_family,
|
||||
Cairo.CAIRO_FONT_SLANT_NORMAL,
|
||||
Cairo.CAIRO_FONT_WEIGHT_NORMAL,
|
||||
);
|
||||
Cairo.cairo_set_font_size(c, 14);
|
||||
setSourceU32(c, color);
|
||||
var buffer = [_]u8{0} ** 12;
|
||||
const printed = std.fmt.bufPrint(&buffer,
|
||||
"{}x{}",
|
||||
.{box.extents.width, box.extents.height}) catch null;
|
||||
|
||||
if (printed) |str| {
|
||||
Cairo.cairo_move_to(c,
|
||||
box.getRightMost(f64) + 10.0,
|
||||
box.getBottomMost(f64) + 10.0,
|
||||
);
|
||||
Cairo.cairo_show_text(c, @ptrCast([*c]const u8, str.ptr));
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
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);
|
||||
|
||||
if (opts.@"display-dimensions") {
|
||||
self.drawDimensions(cairo.ctx, opts.border, box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// draw selection for each seat
|
||||
var seat_iter = self.state.seats.iterator(.forward);
|
||||
while (seat_iter.next()) |link| {
|
||||
const seat = link.get();
|
||||
|
@ -359,14 +360,16 @@ 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
|
||||
if (opts.@"display-dimensions") {
|
||||
self.drawDimensions(cairo.ctx, opts.border, &box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -434,8 +437,6 @@ const Output = struct {
|
|||
if (self.needs_redraw) {
|
||||
self.commitFrame();
|
||||
}
|
||||
|
||||
std.debug.print("frame_callback done\n", .{});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -526,7 +527,7 @@ const Seat = struct {
|
|||
|
||||
pointer: ?struct {
|
||||
pointer: *wl.Pointer,
|
||||
current_surface_offset: ?Point = null,
|
||||
current_surface_geom: ?Box = null,
|
||||
stage: SelectionStage = .Edit,
|
||||
selection: SelectionState = SelectionState.None(),
|
||||
} = null,
|
||||
|
@ -589,7 +590,6 @@ const Seat = struct {
|
|||
fn init(self: *Self, state: *State.Init) void {
|
||||
self.state = state;
|
||||
|
||||
std.debug.print("seat.init()\n", .{});
|
||||
self.seat.setListener(*Self, seatListener, self);
|
||||
}
|
||||
|
||||
|
@ -613,21 +613,21 @@ const Seat = struct {
|
|||
switch (event) {
|
||||
.enter => |enter| {
|
||||
if (self.state.getOutputForSurface(enter.surface.?)) |output| {
|
||||
self.pointer.?.current_surface_offset = output.logical_geometry.position;
|
||||
self.pointer.?.current_surface_geom = output.logical_geometry;
|
||||
}
|
||||
|
||||
std.debug.print("surface offset: {?}\n", .{self.pointer.?.current_surface_offset});
|
||||
std.debug.print("surface geom: {?}\n", .{self.pointer.?.current_surface_geom});
|
||||
},
|
||||
.leave => {
|
||||
self.pointer.?.current_surface_offset = null;
|
||||
self.pointer.?.current_surface_geom = null;
|
||||
},
|
||||
.motion => |motion| {
|
||||
const x = motion.surface_x.toInt();
|
||||
const y = motion.surface_y.toInt();
|
||||
const xy = Point{.x = x, .y = y,};
|
||||
|
||||
if (self.pointer.?.current_surface_offset) |offset| {
|
||||
const point = xy.added(offset);
|
||||
if (self.pointer.?.current_surface_geom) |geom| {
|
||||
const point = xy.added(geom.position);
|
||||
|
||||
switch (self.pointer.?.stage) {
|
||||
.Edit => {
|
||||
|
@ -675,7 +675,7 @@ const Seat = struct {
|
|||
.updating => |selection| {
|
||||
const box = selection.asBox();
|
||||
self.pointer.?.selection = .{.post = box};
|
||||
return self.calculateRedraws();
|
||||
return self.state.finish(box, self.pointer.?.current_surface_geom.?);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
@ -742,13 +742,6 @@ const Seat = struct {
|
|||
if (self.keyboard.?.xkb) |xkb_| {
|
||||
const keysym = xkb.xkb_state_key_get_one_sym(xkb_.state, key.key + 8);
|
||||
|
||||
// const buf_len = 32;
|
||||
// const buf = self.state.ally.alloc(u8, buf_len) catch null;
|
||||
// if (buf) |buffer| {
|
||||
// _ = xkb.xkb_keysym_get_name(keysym, @ptrCast([*c]u8, buffer), buf_len);
|
||||
// std.debug.print("key: {s} {s}\n", .{buffer, if (key.state == .pressed) "pressed" else "released"});
|
||||
// }
|
||||
|
||||
switch (key.state) {
|
||||
.pressed => {
|
||||
switch (keysym) {
|
||||
|
@ -897,8 +890,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 +1012,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 +1034,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 +1070,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
|
||||
|
@ -1103,22 +1099,60 @@ const State = struct {
|
|||
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Self) void {
|
||||
_ = registry;
|
||||
_ = self;
|
||||
std.debug.print("registryListener called after init", .{});
|
||||
switch (event) {
|
||||
.global => {},
|
||||
.global_remove => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn stuff(self: *Self) !void {
|
||||
fn getOptions(self: *Self) *Args {
|
||||
return &self.config.args.options;
|
||||
}
|
||||
|
||||
fn run(self: *Self) !void {
|
||||
while (self.running) {
|
||||
if (self.dpy.dispatch() != .SUCCESS) return error.DispatchFailed;
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(self: *Self, box: Box) void {
|
||||
_ = self;
|
||||
_ = box;
|
||||
fn finish(self: *Self, box: Box, surface_geom: Box) void {
|
||||
self.running = false;
|
||||
|
||||
const format = OutputFormat.fromFormatStr(self.ally, self.getOptions().format) catch return;
|
||||
var iter = format.iter();
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
while (iter.next()) |entry| {
|
||||
if (entry.piece) |piece| {
|
||||
_ = stdout.write(piece) catch return;
|
||||
}
|
||||
if (entry.arg) |arg| {
|
||||
switch (arg) {
|
||||
.X => {stdout.print("{}", .{box.getX(i32)}) catch return;},
|
||||
.Y => {stdout.print("{}", .{box.getY(i32)}) catch return;},
|
||||
.Width => {stdout.print("{}", .{box.getWidth(u32)}) catch return;},
|
||||
.Height => {stdout.print("{}", .{box.getHeight(u32)}) catch return;},
|
||||
.XRel => {stdout.print("{}", .{box.getX(i32) - surface_geom.getX(i32)}) catch return;},
|
||||
.YRel => {stdout.print("{}", .{box.getY(i32) - surface_geom.getY(i32)}) catch return;},
|
||||
.WidthClamped => {
|
||||
stdout.print(
|
||||
"{}",
|
||||
.{@min(box.getWidth(i32),
|
||||
surface_geom.getRightMost(i32) - box.getX(i32))},
|
||||
) catch return;
|
||||
},
|
||||
.HeightClamped=> {
|
||||
stdout.print(
|
||||
"{}",
|
||||
.{@min(box.getHeight(i32),
|
||||
surface_geom.getBottomMost(i32) - box.getY(i32))},
|
||||
) catch return;
|
||||
},
|
||||
// TODO: implement label and outputname
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = stdout.write("\n") catch null;
|
||||
}
|
||||
|
||||
fn removeOutput(self: *Self, output: *Output) void {
|
||||
|
@ -1157,6 +1191,9 @@ const State = struct {
|
|||
}
|
||||
self.ally.destroy(self.outputs);
|
||||
|
||||
// Make sure the compositor has unmapped our surfaces by the time we exit
|
||||
_ = self.dpy.roundtrip();
|
||||
|
||||
self.shm.destroy();
|
||||
self.compositor.destroy();
|
||||
self.layer_shell.destroy();
|
||||
|
@ -1165,18 +1202,226 @@ 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 {
|
||||
// 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 Args = struct {
|
||||
const Self = @This();
|
||||
|
||||
const state = try State.Uninit.create();
|
||||
help: bool = false,
|
||||
@"display-dimensions": bool = false,
|
||||
background: u32 = 0xFFFFFF40,
|
||||
border: u32 = 0x000000FF,
|
||||
selection: u32 = 0x00000000,
|
||||
choice: u32 = 0xFFFFFF40,
|
||||
format: []const u8 = "%x,%y %wx%h\n",
|
||||
@"font-family": [:0]const u8 = "sans-serif",
|
||||
@"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 {
|
||||
const state = try State.Uninit.create(ally);
|
||||
const init = try state.intoInit();
|
||||
defer init.deinit();
|
||||
|
||||
try init.stuff();
|
||||
try init.run();
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
try run(std.heap.c_allocator);
|
||||
}
|
||||
|
||||
const OutputFormat = struct {
|
||||
const Self = @This();
|
||||
|
||||
const Arg = enum {
|
||||
X,
|
||||
Y,
|
||||
Width,
|
||||
Height,
|
||||
XRel,
|
||||
YRel,
|
||||
WidthClamped,
|
||||
HeightClamped,
|
||||
// TODO:
|
||||
// OutputName,
|
||||
// SelectionName,
|
||||
|
||||
fn fromChar(c: u8) ?Arg {
|
||||
switch (c) {
|
||||
'x' => {return .X;},
|
||||
'y' => {return .Y;},
|
||||
'X' => {return .XRel;},
|
||||
'Y' => {return .YRel;},
|
||||
'w' => {return .Width;},
|
||||
'h' => {return .Height;},
|
||||
'W' => {return .WidthClamped;},
|
||||
'H' => {return .HeightClamped;},
|
||||
else => {return null;},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Entry = struct {piece: ?[]const u8, arg: Arg};
|
||||
|
||||
list: std.ArrayList(Entry),
|
||||
last_piece: ?[]const u8,
|
||||
|
||||
const Iterator = struct {
|
||||
format: Self,
|
||||
i: usize = 0,
|
||||
|
||||
pub fn next(self: *@This()) ?struct {piece: ?[]const u8, arg: ?Arg} {
|
||||
const items = self.format.list.items;
|
||||
if (self.i < items.len) {
|
||||
const item = &items[self.i];
|
||||
self.i += 1;
|
||||
|
||||
return .{.piece = item.piece, .arg = item.arg};
|
||||
} else if (self.i == items.len and self.format.last_piece != null) {
|
||||
self.i += 1;
|
||||
return .{.piece = self.format.last_piece, .arg = null};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn iter(self: Self) Iterator {
|
||||
return .{
|
||||
.format = self,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: Self) void {
|
||||
self.list.deinit();
|
||||
}
|
||||
|
||||
/// this does not copy! returned object is only valid as long as 'str' is valid.
|
||||
fn fromFormatStr(ally: std.mem.Allocator, str: []const u8) !Self {
|
||||
var window: struct {start: usize, end: usize} = .{.start = 0, .end = 0};
|
||||
var pieces = std.ArrayList(Entry).init(ally);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < str.len) {
|
||||
defer i += 1;
|
||||
const c = str[i];
|
||||
|
||||
window.end = i;
|
||||
|
||||
if (c == '%') {
|
||||
if (str.len > i) {
|
||||
i += 1;
|
||||
if (Arg.fromChar(str[i])) |arg| {
|
||||
const piece = if (window.start != window.end) str[window.start..window.end] else null;
|
||||
window.start = i + 1;
|
||||
window.end = i + 1;
|
||||
|
||||
std.debug.print("piece: '{?s}', arg: {?}\n", .{piece, arg});
|
||||
try pieces.append(.{.piece = piece, .arg = arg});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const last_piece = if (window.start != window.end) str[window.start..window.end] else null;
|
||||
std.debug.print("piece: '{?s}'\n", .{last_piece});
|
||||
|
||||
return Self{
|
||||
.list = pieces,
|
||||
.last_piece = last_piece,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "format parsing" {
|
||||
const ally = std.testing.allocator;
|
||||
const a = try OutputFormat.fromFormatStr(ally, "%x,%y blob %wx%h");
|
||||
defer a.deinit();
|
||||
const b = try OutputFormat.fromFormatStr(ally, "%t,%y blob %wx%h");
|
||||
defer b.deinit();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue