diff --git a/build.zig b/build.zig index 4e6de10..0767875 100644 --- a/build.zig +++ b/build.zig @@ -9,6 +9,7 @@ pub fn build(b: *Builder) void { "disable_colour", "no colour", ) orelse false; + exe.addBuildOption(bool, "disable_colour", disable_colour); const terminal_version = b.option( bool, @@ -22,6 +23,14 @@ pub fn build(b: *Builder) void { "use debug allocator for testing", ) orelse false; exe.addBuildOption(bool, "debug_allocator", debug_allocator); + const weather_location = b.option( + []const u8, + "weather_location", + "weather_location", + ) orelse "\"\""; + exe.addBuildOption([]const u8, "weather_location", weather_location); + + //exe.strip = true; exe.addPackage(.{ diff --git a/src/bar/bar.zig b/src/bar/bar.zig index c58215e..19235bd 100644 --- a/src/bar/bar.zig +++ b/src/bar/bar.zig @@ -15,6 +15,7 @@ pub const Bar = struct { out_file: std.fs.File, pub fn start(self: *Bar) !void { self.running = true; + // i3bar/swaybar requires starting with this to get click events. if (!terminal_version) try self.out_file.writer().writeAll("{\"version\": 1,\"click_events\": true}\n[\n"); for (self.widgets) |w| { try self.infos.append(try self.dupe_info(w.initial_info())); @@ -24,8 +25,10 @@ pub const Bar = struct { var thread = try std.Thread.spawn(w, Widget.start); } var thread = try std.Thread.spawn(self, Bar.process); + // TODO: wait for kill signal to kill bar instead of waiting for thread. thread.wait(); self.running = false; + // Wait for most widgets to stop. std.time.sleep(1000 * std.time.ns_per_ms); for (self.infos.items) |info| { try self.free_info(info); @@ -34,6 +37,7 @@ pub const Bar = struct { } inline fn print_i3bar_infos(self: *Bar) !void { + // Serialize all bar items and put on stdout. try self.out_file.writer().writeAll("["); for (self.infos.items) |info, i| { try std.json.stringify(info, .{}, self.out_file.writer()); @@ -46,6 +50,7 @@ pub const Bar = struct { } inline fn print_terminal_infos(self: *Bar) !void { + // For terminal we just need to directly print. for (self.infos.items) |info, i| { try self.out_file.writer().writeAll(info.full_text); if (i < self.infos.items.len - 1) { @@ -68,35 +73,50 @@ pub const Bar = struct { } inline fn terminal_input_process(self: *Bar) !void { + // TODO: make work on other OSes other than xterm compatable terminals. + + // Write to stdout that we want to recieve all terminal click events. try self.out_file.writer().writeAll("\u{001b}[?1000;1006;1015h"); var termios = try os.tcgetattr(0); + // Set terminal to raw mode so that all input goes directly into stdin. termios.iflag &= ~@as( os.tcflag_t, os.IGNBRK | os.BRKINT | os.PARMRK | os.ISTRIP | os.INLCR | os.IGNCR | os.ICRNL | os.IXON, ); + // Disable echo so that you don't see mouse events in terminal. termios.lflag |= ~@as(os.tcflag_t, (os.ECHO | os.ICANON)); termios.lflag &= os.ISIG; - + // Set terminal attributes. + // TODO: reset on bar end. try os.tcsetattr(0, .FLUSH, termios); while (true) { var line_buffer: [128]u8 = undefined; - + // 0x1b is the ESC key which is used for sending and recieving events to xterm terminals. const line_opt = try std.io.getStdIn().inStream().readUntilDelimiterOrEof(&line_buffer, 0x1b); if (line_opt) |l| { + // I honestly have no idea what this does but I assume that it checks + // that this is the right event? if (l.len < 2) continue; var it = std.mem.tokenize(l, ";"); + // First number is just the mouse event, skip processing it for now. + // TODO: map mouse click and scroll events to the right enum value. _ = it.next(); - const n = try std.fmt.parseInt(u64, it.next().?, 10); + const click_x_position = try std.fmt.parseInt(u64, it.next().?, 10); var y = it.next().?; + // This makes it so it only works on the end of a click not the start + // preventing a single click pressing the button twice. if (y[y.len - 1] == 'm') continue; - var xe: u64 = 0; + var current_info_line_length: u64 = 0; for (self.infos.items) |infoItem, index| { + // Because the terminal output contains colour codes, we need to strip them. + // To do this we only count the number of characters that are actually printed. var isEscape: bool = false; for (infoItem.full_text) |char| { + // Skip all of the escape codes. if (char == 0x1b) { isEscape = true; continue; @@ -108,18 +128,20 @@ pub const Bar = struct { isEscape = false; continue; } - xe = xe + 1; + // If we get here, it is the start of some amount of actual printed characters. + current_info_line_length = current_info_line_length + 1; } - if (n <= xe) { + // Get the first widget that the click is in. + if (click_x_position <= current_info_line_length) { for (self.widgets) |w| { if (std.mem.eql(u8, w.name(), infoItem.name)) { w.mouse_event(.{ .button = .LeftClick }) catch {}; } } - //std.debug.print("Info Item Clicky{}\n", .{infoItem.name}); break; } - xe = xe + 1; + // Compensate for the | seporator on the terminal. + current_info_line_length = current_info_line_length + 1; } } } @@ -135,26 +157,40 @@ pub const Bar = struct { if (line_opt) |l| { var line = l; if (std.mem.eql(u8, line, "[")) continue; + // Prevention from crashing when running via `zig build run` when pressing enter key. if (line.len == 0) continue; + // Why do you even do this i3bar? + // Why cant you just send one single {} without a comma at start. + // This is stupid and I don't get it, maybe if you were streaming the data + // instead of looping and getting it, maybe then it would make more sense? + // Anyway this just strips off the prefix of ',' so I can parse the json. if (line[0] == ',') line = line[1..line.len]; const parseOptions = std.json.ParseOptions{ .allocator = allocator }; const data = try std.json.parse(MouseEvent, &std.json.TokenStream.init(line), parseOptions); + // TODO: maybe make a function for getting the widget or widget index by name? + // We do use this patern a lot in this code. for (self.widgets) |w| { if (std.mem.eql(u8, w.name(), data.name)) { w.mouse_event(data) catch {}; } } + // If mouse_event needs to store the event for after the call is finished, + // it should do it by itself, this just keeps the lifetime of the event to bare minimum. + // Free the memory allocated by the MouseEvent struct. std.json.parseFree(MouseEvent, data, parseOptions); } } } fn process(self: *Bar) !void { + // Right now this is what we do for the debug allocator for testing memory usage. + // If it the best code? Heck no but until we can gracefully ^C the program + // this is the best we can do. if (debug_allocator) { std.time.sleep(std.time.ns_per_ms * 2000 * 5); if (true) return; } - + // TODO: log errors. if (terminal_version) { self.terminal_input_process() catch {}; } else { @@ -162,8 +198,11 @@ pub const Bar = struct { } } pub fn keep_running(self: *Bar) bool { + // TODO: maybe rename this function to something more descriptive? return self.running; } + + /// This frees the name and text fields of a Info struct. fn free_info(self: *Bar, info: Info) !void { self.allocator.free(info.name); self.allocator.free(info.full_text); @@ -180,10 +219,10 @@ pub const Bar = struct { return Info{ .name = new_name, .full_text = new_text, - .markup = "pango", + .markup = "pango", // setting markup to pango all the time seems OK for perf, no reason not to. }; } - + /// Add a Info to the bar. pub fn add(self: *Bar, info: Info) !void { const lock = self.items_mutex.acquire(); defer lock.release(); @@ -202,7 +241,7 @@ pub const Bar = struct { } }; -pub fn InitBar(allocator: *std.mem.Allocator) Bar { +pub fn initBar(allocator: *std.mem.Allocator) Bar { return Bar{ .allocator = allocator, .widgets = undefined, diff --git a/src/formatting/colour.zig b/src/formatting/colour.zig index 3cab18f..f21f000 100644 --- a/src/formatting/colour.zig +++ b/src/formatting/colour.zig @@ -28,6 +28,7 @@ const TerminalYellowColour = "\u{001b}[33m"; const TerminalGreenColour = "\u{001b}[32m"; const TerminalPurpleColour = "\u{001b}[35m"; +/// This colours a string but at comptime. pub fn comptimeColour(comptime clr: []const u8, comptime str: []const u8) []const u8 { if (disable_colour) return str; @@ -38,7 +39,7 @@ pub fn comptimeColour(comptime clr: []const u8, comptime str: []const u8) []cons return "" ++ str ++ ""; } } else { - var colourText: []const u8 = ""; + comptime var colourText: []const u8 = ""; if (eql(u8, clr, "text")) { colourText = if (!terminal_version) TextColour else TerminalTextColour; } else if (eql(u8, clr, "dark")) { @@ -61,13 +62,17 @@ pub fn comptimeColour(comptime clr: []const u8, comptime str: []const u8) []cons colourText = if (!terminal_version) GreenColour else TerminalGreenColour; } else if (eql(u8, clr, "purple")) { colourText = if (!terminal_version) PurpleColour else TerminalPurpleColour; - } else { - @compileError("colour not found"); + } + if (colourText.len == 0) { + unreachable; } return comptimeColour(colourText, str); } } +/// This colours a dynamic string at runtime. +/// It may make more than one allocation, +/// so put it on a ArenaAllocator so you can free what else it allocates. pub fn colour(alloc: *std.mem.Allocator, clr: []const u8, str: []const u8) ![]const u8 { if (disable_colour) return str; diff --git a/src/main.zig b/src/main.zig index dc3bde8..0e9844a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -18,19 +18,20 @@ pub fn main() !void { var allocator: *std.mem.Allocator = undefined; var dbgAlloc: *DebugAllocator = undefined; if (debug_allocator) { + // Warning that DebugAllocator can get a little crashy. dbgAlloc = &DebugAllocator.init(std.heap.page_allocator, 8192 * 8192); allocator = &dbgAlloc.allocator; } else { allocator = std.heap.page_allocator; } - var bar = barImpl.InitBar(allocator); + var bar = barImpl.initBar(allocator); var br = Bar.init(&bar); const widgets = [_]*Widget{ //&Widget.init(&textWidget.New("owo", "potato")), // 4KiB //&Widget.init(&textWidget.New("uwu", "tomato")), // 4KiB &Widget.init(&memoryWidget.New(&br)), // 4.08KiB - &Widget.init(&weatherWidget.New(allocator, &br, "Stockholm")), // 16.16KiB + &Widget.init(&weatherWidget.New(allocator, &br, @import("build_options").weather_location)), // 16.16KiB &Widget.init(&batteryWidget.New(allocator, &br)), // 12.11KiB &Widget.init(&timeWidget.New(allocator, &br)), // 32.46KiB }; diff --git a/src/types/loopingcounter.zig b/src/types/loopingcounter.zig index fcd8f36..2a18c7c 100644 --- a/src/types/loopingcounter.zig +++ b/src/types/loopingcounter.zig @@ -1,6 +1,8 @@ const std = @import("std"); const testing = std.testing; +/// A LoopingCounter is *extremely* helpful when making clickable widgets +/// that have different data to show and loops around. pub fn LoopingCounter( comptime max_number: comptime_int, ) type { @@ -24,6 +26,7 @@ pub fn LoopingCounter( }; } +// Woot we actually have some god damn tests in here. test "looping test" { var lc = LoopingCounter(3).init(); testing.expect(lc.get() == 0); diff --git a/src/widgets/battery/battery.zig b/src/widgets/battery/battery.zig index 6766705..d0c83b1 100644 --- a/src/widgets/battery/battery.zig +++ b/src/widgets/battery/battery.zig @@ -5,6 +5,7 @@ const fs = std.fs; const cwd = fs.cwd; const colour = @import("../../formatting/colour.zig").colour; const MouseEvent = @import("../../types/mouseevent.zig"); +const comptimeColour = @import("../../formatting/colour.zig").comptimeColour; pub const PowerPaths = struct { status_path: []const u8 = "", @@ -14,7 +15,9 @@ pub const PowerPaths = struct { voltage_now_path: []const u8 = "", }; -pub fn read_file_to_unsigned_int64(path: []const u8) u64 { +pub fn readFileToUnsignedInt64(path: []const u8) u64 { + // Calculate the max length of a u64 encoded as a string at comptime + // adding 1 for newline and 1 for good luck. var buffer: [std.math.log10(std.math.maxInt(u64)) + 2]u8 = undefined; var file = fs.cwd().openFile(path, .{}) catch return 0; defer file.close(); @@ -22,7 +25,7 @@ pub fn read_file_to_unsigned_int64(path: []const u8) u64 { return std.fmt.parseInt(u64, buffer[0 .. siz - 1], 10) catch return 0; } -pub fn read_file(path: []const u8) ![]const u8 { +pub fn readFile(path: []const u8) ![]const u8 { var buffer: [128]u8 = undefined; var file = try fs.cwd().openFile(path, .{}); defer file.close(); @@ -47,6 +50,7 @@ pub const BatteryWidget = struct { pub fn mouse_event(self: *BatteryWidget, event: MouseEvent) void {} + // Find all the paths for power info. pub fn get_power_paths(self: *BatteryWidget, provided_allocator: *std.mem.Allocator) anyerror!PowerPaths { var arena = std.heap.ArenaAllocator.init(provided_allocator); defer arena.deinit(); @@ -106,8 +110,8 @@ pub const BatteryWidget = struct { var sign: []const u8 = "?"; var power_colour: []const u8 = "#ffffff"; - const capacity = @intToFloat(f64, read_file_to_unsigned_int64(pp.capacity_path)); - const status = try read_file(pp.status_path); + const capacity = @intToFloat(f64, readFileToUnsignedInt64(pp.capacity_path)); + const status = try readFile(pp.status_path); if (capacity > 80) { power_colour = "green"; @@ -120,22 +124,22 @@ pub const BatteryWidget = struct { } if (std.mem.eql(u8, status, "Charging")) { - descriptor = try colour(allocator, "green", "(C)"); + descriptor = comptimeColour("green", "(C)"); sign = "+"; } else if (std.mem.eql(u8, status, "Discharging")) { descriptor = try colour(allocator, power_colour, "(D)"); sign = "-"; } else if (std.mem.eql(u8, status, "Unknown")) { - descriptor = try colour(allocator, "yellow", "(U)"); + descriptor = comptimeColour("yellow", "(U)"); sign = "?"; } if (pp.power_now_path.len != 0) { - watts = @intToFloat(f64, read_file_to_unsigned_int64(pp.power_now_path)) / 1000000; + watts = @intToFloat(f64, readFileToUnsignedInt64(pp.power_now_path)) / 1000000; can_get_watts = true; } else if (pp.current_now_path.len != 0 and pp.voltage_now_path.len != 0) { - const current_now = @intToFloat(f64, read_file_to_unsigned_int64(pp.current_now_path)) / 1000000; - const voltage_now = @intToFloat(f64, read_file_to_unsigned_int64(pp.voltage_now_path)) / 1000000; + const current_now = @intToFloat(f64, readFileToUnsignedInt64(pp.current_now_path)) / 1000000; + const voltage_now = @intToFloat(f64, readFileToUnsignedInt64(pp.voltage_now_path)) / 1000000; if (current_now == 0 or voltage_now == 0) { can_get_watts = false; } else { @@ -151,10 +155,10 @@ pub const BatteryWidget = struct { } var bat_info = try std.fmt.allocPrint(allocator, "{} {} {}{}{}", .{ - colour(allocator, "accentlight", "bat"), + comptimeColour("accentlight", "bat"), descriptor, colour(allocator, power_colour, try std.fmt.allocPrint(allocator, "{d:.2}", .{capacity})), - colour(allocator, "accentdark", "%"), + comptimeColour("accentdark", "%"), watts_info, }); diff --git a/src/widgets/memory/memory.zig b/src/widgets/memory/memory.zig index db8704e..f31f67d 100644 --- a/src/widgets/memory/memory.zig +++ b/src/widgets/memory/memory.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Info = @import("../../types/info.zig"); const Bar = @import("../../types/bar.zig").Bar; const colour = @import("../../formatting/colour.zig").colour; -const comptimeColour = @import("../../formatting/colour.zig").colour; +const comptimeColour = @import("../../formatting/colour.zig").comptimeColour; const MouseEvent = @import("../../types/mouseevent.zig"); const LoopingCounter = @import("../../types/loopingcounter.zig").LoopingCounter; @@ -35,7 +35,7 @@ fn formatMemoryPercent(allocator: *std.mem.Allocator, percent: f64) ![]const u8 } else { percentColour = "green"; } - const percentString = try std.fmt.allocPrint(allocator, "{d:.3}{}", .{ percent, comptimeColour(allocator, "accentdark", "%") }); + const percentString = try std.fmt.allocPrint(allocator, "{d:.3}{}", .{ percent, comptimeColour("accentdark", "%") }); return colour(allocator, percentColour, percentString); } @@ -122,49 +122,51 @@ pub const MemoryWidget = struct { var allocator = &fba.allocator; const memInfo = try fetchTotalMemory(); var text: []const u8 = " "; + + // And this is why I love the looping counter. if (self.lc.get() == 0) { text = try std.fmt.allocPrint(allocator, "{} {}", .{ - colour(allocator, "accentlight", "mem"), + comptimeColour("accentlight", "mem"), formatMemoryPercent(allocator, (@intToFloat(f64, memInfo.memTotal - memInfo.memFree - memInfo.buffers - memInfo.cached) / @intToFloat(f64, memInfo.memTotal)) * 100), }); } else if (self.lc.get() == 1) { text = try std.fmt.allocPrint(allocator, "{} {}", .{ - colour(allocator, "accentlight", "swap"), + comptimeColour("accentlight", "swap"), formatMemoryPercent(allocator, (@intToFloat(f64, memInfo.swapTotal - memInfo.swapFree) / @intToFloat(f64, memInfo.swapTotal)) * 100), }); } else if (self.lc.get() == 2) { text = try std.fmt.allocPrint(allocator, "{} {d:0<2} MB", .{ - colour(allocator, "accentlight", "mem free"), + comptimeColour("accentlight", "mem free"), kibibytesToMegabytes(memInfo.memFree), }); } else if (self.lc.get() == 3) { text = try std.fmt.allocPrint(allocator, "{} {d:0<2} MB", .{ - colour(allocator, "accentlight", "swap free"), + comptimeColour("accentlight", "swap free"), kibibytesToMegabytes(memInfo.swapFree), }); } else if (self.lc.get() == 4) { text = try std.fmt.allocPrint(allocator, "{} {d:0<2} MB", .{ - colour(allocator, "accentlight", "mem used"), + comptimeColour("accentlight", "mem used"), kibibytesToMegabytes(memInfo.memTotal - memInfo.memFree - memInfo.buffers - memInfo.cached), }); } else if (self.lc.get() == 5) { text = try std.fmt.allocPrint(allocator, "{} {d:0<2} MB", .{ - colour(allocator, "accentlight", "swap used"), + comptimeColour("accentlight", "swap used"), kibibytesToMegabytes(memInfo.swapTotal - memInfo.swapFree), }); } else if (self.lc.get() == 6) { text = try std.fmt.allocPrint(allocator, "{} {d:0<2} MB", .{ - colour(allocator, "accentlight", "mem cache"), + comptimeColour("accentlight", "mem cache"), kibibytesToMegabytes(memInfo.cached), }); } else if (self.lc.get() == 7) { text = try std.fmt.allocPrint(allocator, "{} {d:0<2} MB", .{ - colour(allocator, "accentlight", "swap cache"), + comptimeColour("accentlight", "swap cache"), kibibytesToMegabytes(memInfo.swapCached), }); } else if (self.lc.get() == 8) { text = try std.fmt.allocPrint(allocator, "{} {d:0<2} MB", .{ - colour(allocator, "accentlight", "mem buf"), + comptimeColour("accentlight", "mem buf"), kibibytesToMegabytes(memInfo.buffers), }); } diff --git a/src/widgets/time/time.zig b/src/widgets/time/time.zig index 72b245b..7ca18e5 100644 --- a/src/widgets/time/time.zig +++ b/src/widgets/time/time.zig @@ -4,6 +4,7 @@ const Bar = @import("../../types/bar.zig").Bar; const time = @import("time"); const colour = @import("../../formatting/colour.zig").colour; const MouseEvent = @import("../../types/mouseevent.zig"); +const comptimeColour = @import("../../formatting/colour.zig").comptimeColour; pub const TimeWidget = struct { bar: *Bar, @@ -23,6 +24,8 @@ pub const TimeWidget = struct { pub fn mouse_event(self: *TimeWidget, event: MouseEvent) void {} pub fn start(self: *TimeWidget) anyerror!void { + // TODO: find a god damn decent time library thats better than this bullshit. + while (self.bar.keep_running()) { var arena = std.heap.ArenaAllocator.init(self.allocator); defer arena.deinit(); @@ -45,9 +48,9 @@ pub const TimeWidget = struct { var timeStr = try std.fmt.allocPrint(allocator, "{}{}{}{}{}{}", .{ colour(allocator, "red", try std.fmt.allocPrint(allocator, "{d:0<2}", .{@intCast(u7, hour)})), - colour(allocator, "accentlight", ":"), + comptimeColour("accentlight", ":"), colour(allocator, "orange", try std.fmt.allocPrint(allocator, "{d:0<2}", .{@intCast(u7, clock.min)})), - colour(allocator, "accentmedium", ":"), + comptimeColour("accentmedium", ":"), colour(allocator, "yellow", try std.fmt.allocPrint(allocator, "{d:0<2}", .{@intCast(u7, clock.sec)})), colour(allocator, "accentdark", end), }); @@ -69,14 +72,14 @@ pub const TimeWidget = struct { var h = try std.fmt.allocPrint(allocator, "{} {} {}{} {} {} {} {} {} {}", .{ colour(allocator, "green", now.weekday().string()), - colour(allocator, "purple", "the"), + comptimeColour("purple", "the"), colour(allocator, "yellow", try std.fmt.allocPrint(allocator, "{}", .{date.day})), colour(allocator, "accentmedium", suffix), - colour(allocator, "purple", "of"), + comptimeColour("purple", "of"), colour(allocator, "red", date.month.string()), - colour(allocator, "purple", "in"), + comptimeColour("purple", "in"), colour(allocator, "accentlight", try std.fmt.allocPrint(allocator, "{}", .{date.year})), - colour(allocator, "purple", "at"), + comptimeColour("purple", "at"), timeStr, }); diff --git a/src/widgets/weather/weather.zig b/src/widgets/weather/weather.zig index f7e4ea0..0c16e2f 100644 --- a/src/widgets/weather/weather.zig +++ b/src/widgets/weather/weather.zig @@ -5,6 +5,8 @@ const hzzp = @import("hzzp"); const Info = @import("../../types/info.zig"); const Bar = @import("../../types/bar.zig").Bar; const colour = @import("../../formatting/colour.zig").colour; +const comptimeColour = @import("../../formatting/colour.zig").comptimeColour; + const DebugAllocator = @import("../../debug_allocator.zig"); const MouseEvent = @import("../../types/mouseevent.zig"); @@ -60,6 +62,8 @@ pub const WeatherWidget = struct { var main: []const u8 = ""; var message: []const u8 = ""; + + // This parser is clunky, it may be worth a rewrite but it seems like it optimizes decently. while (try client.readEvent()) |event| { switch (event) { .chunk => |chunk| { @@ -93,7 +97,8 @@ pub const WeatherWidget = struct { message = str; } if (isNextCode) { - // why the fuck would you make code both a string and a int are you wanting me to question my sanity??? + // Why the fuck would you make code both a string and a int. + // Are you wanting me to question my sanity??? isNextCode = false; code = try std.fmt.parseInt(i16, str, 10); } @@ -125,7 +130,6 @@ pub const WeatherWidget = struct { var arenacator = &arena.allocator; if (self.get_weather_info(arenacator)) |i| { inf = i; - //std.debug.print("{}", .{i}); } else |err| switch (err) { error.TemporaryNameServerFailure => { try self.bar.add(Info{ @@ -169,6 +173,11 @@ pub const WeatherWidget = struct { var temp = inf.temp; var main = inf.main; + + // Please note that these are *my* personal temp preferences. + // TODO: it may be worth making a way for the user to change this with a function on init. + // If you happen to read this and you plan on inviting me round your house one day, + // then feel free to set your A/C to a optimal temp as shown below. var tempColour: []const u8 = "green"; if (temp >= 20) { tempColour = "red"; @@ -183,10 +192,10 @@ pub const WeatherWidget = struct { var i = Info{ .name = self.name, .full_text = try std.fmt.allocPrint(arenacator, "{} {}{}{} {}", .{ - colour(arenacator, "accentlight", "weather"), + comptimeColour("accentlight", "weather"), colour(arenacator, tempColour, try std.fmt.allocPrint(arenacator, "{}", .{temp})), - colour(arenacator, "accentlight", "°"), - colour(arenacator, "accentdark", "C"), + comptimeColour("accentlight", "°"), + comptimeColour("accentdark", "C"), colour(arenacator, "green", main), }), .markup = "pango", @@ -207,6 +216,9 @@ pub inline fn New(allocator: *std.mem.Allocator, bar: *Bar, comptime location: [ .allocator = allocator, .bar = bar, .name = "weather " ++ location, + // Yeah I know I'm leaking a token here. + // So what? It ain't my token. + // It was the first result on github code search for "openweathermap appid" .weather_api_url = "/data/2.5/weather?q=" ++ location ++ "&appid=dcea3595afe693d1c17846141f58ea10&units=metric", }; }