Created
July 18, 2025 05:42
-
-
Save notcancername/fc3b8d6cb6e308d8b39f2891cba88e23 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub const std = @import("std"); | |
pub const dvui = @import("dvui"); | |
const Backend = dvui.backend; | |
pub const tardy = @import("tardy"); | |
const Tardy = tardy.Tardy(.io_uring); | |
const Runtime = tardy.Runtime; | |
const Socket = tardy.Socket; | |
const Timer = tardy.Timer; | |
const DataMap = std.StringArrayHashMapUnmanaged(struct { std.ArrayListUnmanaged(u64), u64 }); | |
pub fn parseLine(line: []const u8, out: *DataMap, allocator: std.mem.Allocator) !void { | |
var tok = std.mem.tokenizeScalar(u8, line, ' '); | |
const name = tok.next() orelse return error.Shit; | |
if (std.mem.eql(u8, name, "2s") or std.mem.indexOfScalar(u8, line, '\x1b') != null) return; | |
const two_secs = try std.fmt.parseInt(u64, tok.next() orelse return error.Shit, 10); | |
const total = try std.fmt.parseInt(u64, tok.next() orelse return error.Shit, 10); | |
if (out.getPtr(name)) |val_p| { | |
try val_p.@"0".append(allocator, two_secs); | |
val_p.@"1" = total; | |
} else { | |
const a_name = try allocator.dupe(u8, name); | |
errdefer allocator.free(a_name); | |
const gop = try out.getOrPutValue(allocator, a_name, .{ .empty, total }); | |
errdefer _ = out.orderedRemove(a_name); | |
try gop.value_ptr.@"0".append(allocator, two_secs); | |
} | |
} | |
const Entry = struct { | |
gpa: std.mem.Allocator, | |
out: DataMap = .empty, | |
mutex: std.Thread.Mutex = .{}, | |
in: std.fs.File, | |
err: anyerror!void = {}, | |
window: ?*dvui.Window = null, | |
}; | |
pub fn main() !void { | |
const gpa = std.heap.smp_allocator; | |
var t = try Tardy.init(gpa, .{ .threading = .single }); | |
var e = Entry{ .gpa = gpa, .in = std.io.getStdIn() }; | |
try t.entry_in_new_thread( | |
&e, | |
struct { | |
fn entry(rt: *Runtime, p: *Entry) !void { | |
try rt.spawn(.{ rt, p }, ioFrame, 2 << 20); | |
try rt.spawn(.{ rt, p }, sortFrame, 2 << 20); | |
} | |
}.entry, | |
); | |
try t.entry( | |
&e, | |
struct { | |
fn entry(rt: *Runtime, p: *Entry) !void { | |
try rt.spawn(.{ rt, p }, guiFrame, 8 << 20); | |
} | |
}.entry, | |
); | |
} | |
fn sortFrame(rt: *Runtime, p: *Entry) !void { | |
try Timer.delay(rt, .{ .seconds = 2 }); | |
const SortContext = struct { | |
slice: DataMap.DataList.Slice, | |
pub fn init(dm: *DataMap) @This() { | |
return .{ .slice = dm.entries.slice() }; | |
} | |
pub fn lessThan(self: *@This(), a: usize, b: usize) bool { | |
const it = self.slice.items(.value); | |
return it[a].@"1" > it[b].@"1"; | |
} | |
pub fn swap(self: *@This(), a: usize, b: usize) void { | |
const a_old = self.slice.get(a); | |
const b_old = self.slice.get(b); | |
self.slice.set(b, a_old); | |
self.slice.set(a, b_old); | |
} | |
}; | |
{ | |
p.mutex.lock(); | |
defer p.mutex.unlock(); | |
var sc = SortContext.init(&p.out); | |
std.sort.insertionContext(0, p.out.entries.len, &sc); | |
try p.out.reIndex(p.gpa); | |
} | |
dvui.refresh(p.window, @src(), null); | |
} | |
fn ioFrame(rt: *Runtime, p: *Entry) !void { | |
var b: std.fifo.LinearFifo(u8, .{ .Static = 4096 }) = .init(); | |
while (true) { | |
b.realign(); | |
const len = try tardy.File.from_std(p.in).read(rt, b.writableSlice(0), null); | |
if (len == 0) { | |
p.err = error.EndOfStream; | |
break; | |
} | |
b.update(len); | |
const end_of_line = std.mem.indexOfScalar(u8, b.readableSlice(0), '\n') orelse { | |
continue; | |
}; | |
{ | |
p.mutex.lock(); | |
defer p.mutex.unlock(); | |
parseLine(b.readableSlice(0)[0..end_of_line], &p.out, p.gpa) catch |e| { | |
p.err = e; | |
}; | |
} | |
b.discard(end_of_line + 1); | |
dvui.refresh(p.window, @src(), null); | |
} | |
} | |
fn guiFrame(rt: *Runtime, p: *Entry) !void { | |
var backend = try Backend.initWindow(.{ | |
.allocator = p.gpa, | |
.size = .{ .w = 800.0, .h = 600.0 }, | |
.min_size = .{ .w = 250.0, .h = 350.0 }, | |
.vsync = true, | |
.title = "bcachefs graph", | |
.icon = null, | |
}); | |
defer backend.deinit(); | |
var win = try dvui.Window.init( | |
@src(), | |
p.gpa, | |
backend.backend(), | |
.{}, | |
); | |
defer win.deinit(); | |
p.window = &win; | |
var exit = false; | |
var interrupted = false; | |
while (true) { | |
if (exit) rt.stop(); | |
try win.begin(win.beginWait(interrupted)); | |
const quit = try backend.addAllEvents(&win); | |
if (quit) break; | |
_ = Backend.c.SDL_SetRenderDrawColor(backend.renderer, 0, 0, 0, 255); | |
_ = Backend.c.SDL_RenderClear(backend.renderer); | |
{ | |
{ | |
var m = dvui.menu(@src(), .horizontal, .{ .background = true, .expand = .horizontal }); | |
defer m.deinit(); | |
if (dvui.menuItemLabel(@src(), "File", .{ .submenu = true }, .{ .expand = .none })) |r| { | |
var fw = dvui.floatingMenu(@src(), .{ .from = r }, .{}); | |
defer fw.deinit(); | |
if (dvui.menuItemLabel(@src(), "Quit", .{}, .{}) != null) { | |
exit = true; | |
} | |
} | |
} | |
var scaler = dvui.scale( | |
@src(), | |
.{ .scale = &dvui.currentWindow().content_scale, .pinch_zoom = .global }, | |
.{ .rect = .cast(dvui.windowRect()) }, | |
); | |
scaler.deinit(); | |
var scroll = dvui.scrollArea(@src(), .{}, .{ .expand = .both, .color_fill = .fill_window }); | |
defer scroll.deinit(); | |
p.err catch |e| dvui.toast(@src(), .{ .message = @errorName(e) }); | |
if (dvui.Theme.picker(@src(), .{})) {} | |
var vboxo = dvui.box(@src(), .vertical, .{ | |
.expand = .horizontal, | |
}); | |
defer vboxo.deinit(); | |
p.mutex.lock(); | |
defer p.mutex.unlock(); | |
for (p.out.keys(), p.out.values()) |k, *v| { | |
// const static = struct { | |
// var xaxis: dvui.PlotWidget.Axis = .{ | |
// .name = "Seconds", | |
// }; | |
// var yaxis: dvui.PlotWidget.Axis = .{ | |
// .name = "Events", | |
// }; | |
// }; | |
var vbox = dvui.box(@src(), .vertical, .{ | |
.min_size_content = .{ .w = 300, .h = 100 }, | |
.max_size_content = .height(100), | |
.expand = .both, | |
.id_extra = std.hash.Wyhash.hash(69, k), | |
}); | |
defer vbox.deinit(); | |
var plot = dvui.plot(@src(), .{ | |
.title = k, | |
// .x_axis = &static.xaxis, | |
// .y_axis = &static.yaxis, | |
.border_thick = 1.0, | |
.mouse_hover = true, | |
}, .{ | |
.expand = .both, | |
.id_extra = std.hash.Wyhash.hash(69, k), | |
}); | |
var s1 = plot.line(); | |
for (v.@"0".items, 0..) |count, idx| { | |
s1.point(@floatFromInt(idx), @floatFromInt(count)); | |
} | |
s1.stroke(2, dvui.themeGet().color_accent); | |
s1.deinit(); | |
plot.deinit(); | |
} | |
} | |
const end_micros = try win.end(.{}); | |
try backend.setCursor(win.cursorRequested()); | |
try backend.textInputRect(win.textInputRequested()); | |
try backend.renderPresent(); | |
const end_wait = win.waitTime(end_micros, 60); | |
interrupted = try backend.waitEventTimeout(end_wait); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment