~mil/mepo-devel

mepo: Migrate to Allocgate interface v1 APPLIED

Nguyễn Gia Phong: 10
 Migrate to Allocgate interface
 Use C enums as integers
 Migrate to Zig 0.9 mutex API
 Replace std.debug.warn with std.debug.print
 Replace c_void with anyopaque
 Use Zig 0.9's generic std.mem.tokenize
 Use OS-independent signal handling
 Fix name shadowing in Color.sdl
 Fix unused stuff
 Finish Zig 0.9 migration

 45 files changed, 140 insertions(+), 219 deletions(-)
I missed the Zig 0.8.1 mentions in doc/developguide.md and
src/util/utilconversion.zig, but that's probably OK since it might takes
some extra time for Zig 0.9 to arrive in Alpine.  I sent this today
as I'm migrating another project and wanted to make use of my bio cache.

On the bright side, Mepo is working on NixOS unstable now.
I'll try to package it unless someone else beats me to it.



          
          
          
        
      

      
      
      
      

      

      
      
      
      

      

      
      
      
      

      
      
        
          






Mepo landed in nixpkgs!
Nguyễn Gia Phong kirjoitti 13.1.2022 klo 19.02:
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~mil/mepo-devel/patches/28224/mbox | git am -3
Learn more about email & git

[PATCH mepo 01/10] Migrate to Allocgate interface Export this patch

---
 src/Mepo.zig                        | 16 ++++++++--------
 src/TileCache.zig                   |  8 ++++----
 src/api/shellpipe.zig               | 22 +++++++++++-----------
 src/datastructure/datastructure.zig |  2 +-
 src/main.zig                        |  2 +-
 src/util/utilfile.zig               |  2 +-
 src/util/utilmepolang.zig           |  8 ++++----
 7 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/src/Mepo.zig b/src/Mepo.zig
index 48e9991..1470044 100644
--- a/src/Mepo.zig
+++ b/src/Mepo.zig
@@ -13,7 +13,7 @@ const zoom_relative = @import("./api/zoom_relative.zig").zoom_relative;
const FnTable = @import("./api/_FnTable.zig");

var pending_signal: ?u6 = null;
allocator: *std.mem.Allocator,
allocator: std.mem.Allocator,
crosshair_on: bool = true,
crosshair_size: u8 = 10,
debug_message: ?[]const u8 = null,
@@ -218,13 +218,13 @@ fn blit_overlay_pindetails(mepo: *@This()) !void {
        },
    );

    var al = std.ArrayList([2][:0]const u8).init(&arena.allocator);
    var al = std.ArrayList([2][:0]const u8).init(arena.allocator());
    defer al.deinit();

    if (mepo.pin_group_active_item) |_| {
        const pin = mepo.pin_groups[mepo.pin_group_active].pins.items[mepo.pin_group_active_item.?];
        const pin_coords_str = try std.fmt.allocPrintZ(&arena.allocator, "{d:.5} lat / {d:.5} lon", .{ pin.lat, pin.lon });
        const distance_str = try std.fmt.allocPrintZ(&arena.allocator, "{d:.2}{s} away", .{
        const pin_coords_str = try std.fmt.allocPrintZ(arena.allocator(), "{d:.5} lat / {d:.5} lon", .{ pin.lat, pin.lon });
        const distance_str = try std.fmt.allocPrintZ(arena.allocator(), "{d:.2}{s} away", .{
            utilconversion.distance_haversine(
                if (mepo.distance_unit_tf_km_mi) .Km else .Mi,
                mepo.lat,
@@ -239,7 +239,7 @@ fn blit_overlay_pindetails(mepo: *@This()) !void {
        try al.append(([_][:0]const u8{ "Coords:", pin_coords_str })[0..].*);
        try al.append(([_][:0]const u8{ "Dist:", distance_str })[0..].*);
        for (pin.metadata.keys()) |k| {
            const duped = try std.fmt.allocPrintZ(&arena.allocator, "{s}", .{k});
            const duped = try std.fmt.allocPrintZ(arena.allocator(), "{s}", .{k});
            try al.append(([_][:0]const u8{ duped, pin.metadata.get(k).? })[0..].*);
        }
        try mepo.blit_table(5, 5, 5, al.items);
@@ -573,13 +573,13 @@ pub fn mepolang_execute(mepo: *@This(), mepolang_text: []const u8) !void {
    // cleaner that way, but either way underlying tests / fn boundaries &
    // API would stay the same..

    for (try utilmepolang.statementize(&arena.allocator, try utilmepolang.tokenize(&arena.allocator, mepolang_text))) |statement| {
    for (try utilmepolang.statementize(arena.allocator(), try utilmepolang.tokenize(arena.allocator(), mepolang_text))) |statement| {
        utildbg.log("Executing statment: {s}\n", .{statement});
        if (statement.len < 1) continue;

        // Lookup and run fn
        if (FnTable.get(statement[0])) |func| {
            const args: [types.MepoFnNargs]types.MepoArg = try utilmepolang.argize(&arena.allocator, statement[1..statement.len]);
            const args: [types.MepoFnNargs]types.MepoArg = try utilmepolang.argize(arena.allocator(), statement[1..statement.len]);
            utildbg.log("Running API function for: {s} requested by mepolang input statement: {s}\n", .{ statement[0], statement });
            func(mepo, args) catch |mepolang_err| {
                utildbg.log("Error running API function for: {s}: {}\n", .{ statement[0], mepolang_err });
@@ -883,7 +883,7 @@ pub fn sighandle_user(arg: c_int) callconv(.C) void {
    pending_signal = @truncate(u6, @bitCast(c_uint, arg));
}

pub fn init(allocator: *std.mem.Allocator, tile_cache: *TileCache, cfg_opt: ?[]const u8) anyerror!@This() {
pub fn init(allocator: std.mem.Allocator, tile_cache: *TileCache, cfg_opt: ?[]const u8) anyerror!@This() {
    // Initialize video, create window
    const window = window: {
        try utilsdl.errorcheck(sdl.SDL_Init(sdl.SDL_INIT_VIDEO));
diff --git a/src/TileCache.zig b/src/TileCache.zig
index 1fa4953..4daec2b 100644
--- a/src/TileCache.zig
+++ b/src/TileCache.zig
@@ -39,7 +39,7 @@ const TileData = union(TileDataTag) {
const TileDataTag = enum { transfer_datum, texture, queued_position, error_type };
const QueuedInfo = struct { n_queued: usize, n_cached: usize };

allocator: *std.mem.Allocator,
allocator: std.mem.Allocator,
thread_download: ?*sdl.SDL_Thread = null,
thread_queuebbox: ?*sdl.SDL_Thread = null,
bbox_queue: ?DownloadBBoxRequest = null,
@@ -460,7 +460,7 @@ fn load_data_to_texture(tile_cache: *@This(), coords: types.XYZ, data: []u8) !*s
    return tile_cache.texture_map.get(coords).?;
}

fn png_path(allocator: *std.mem.Allocator, source: []const u8, coords: types.XYZ) ![]u8 {
fn png_path(allocator: std.mem.Allocator, source: []const u8, coords: types.XYZ) ![]u8 {
    var source_hash: [32:0]u8 = undefined;
    std.crypto.hash.sha2.Sha256.hash(source, &source_hash, .{});
    return try std.fmt.allocPrint(
@@ -506,8 +506,8 @@ fn threadable_download_loop_sdl(userdata: ?*c_void) callconv(.C) c_int {
    return 0;
}

pub fn init(allocator: *std.mem.Allocator) anyerror!@This() {
    if (curl.curl_multi_init()) |curl_multi| {
pub fn init(allocator: std.mem.Allocator) anyerror!@This() {
    if (curl.curl_multi_init()) |_| {
        return @as(@This(), .{
            .allocator = allocator,
            .curl_multi = curl.curl_multi_init().?,
diff --git a/src/api/shellpipe.zig b/src/api/shellpipe.zig
index 6472c61..ebcd302 100644
--- a/src/api/shellpipe.zig
+++ b/src/api/shellpipe.zig
@@ -14,23 +14,23 @@ fn shellpipe(mepo: *Mepo, cmd: []const u8) !void {
    defer arena.deinit();

    const env_vars = env_vars: {
        var v = try std.process.getEnvMap(&arena.allocator);
        var v = try std.process.getEnvMap(arena.allocator());
        const bbox = mepo.bounding_box();

        try v.put("MEPO_WIN_W", try std.fmt.allocPrint(&arena.allocator, "{d}", .{mepo.win_w}));
        try v.put("MEPO_WIN_H", try std.fmt.allocPrint(&arena.allocator, "{d}", .{mepo.win_h}));
        try v.put("MEPO_ZOOM", try std.fmt.allocPrint(&arena.allocator, "{d}", .{mepo.zoom}));
        try v.put("MEPO_C_LAT", try std.fmt.allocPrint(&arena.allocator, "{d}", .{mepo.lat}));
        try v.put("MEPO_C_LON", try std.fmt.allocPrint(&arena.allocator, "{d}", .{mepo.lon}));
        try v.put("MEPO_TL_LAT", try std.fmt.allocPrint(&arena.allocator, "{d}", .{bbox.topleft_lat}));
        try v.put("MEPO_TL_LON", try std.fmt.allocPrint(&arena.allocator, "{d}", .{bbox.topleft_lon}));
        try v.put("MEPO_BR_LAT", try std.fmt.allocPrint(&arena.allocator, "{d}", .{bbox.bottomright_lat}));
        try v.put("MEPO_BR_LON", try std.fmt.allocPrint(&arena.allocator, "{d}", .{bbox.bottomright_lon}));
        try v.put("MEPO_WIN_W", try std.fmt.allocPrint(arena.allocator(), "{d}", .{mepo.win_w}));
        try v.put("MEPO_WIN_H", try std.fmt.allocPrint(arena.allocator(), "{d}", .{mepo.win_h}));
        try v.put("MEPO_ZOOM", try std.fmt.allocPrint(arena.allocator(), "{d}", .{mepo.zoom}));
        try v.put("MEPO_C_LAT", try std.fmt.allocPrint(arena.allocator(), "{d}", .{mepo.lat}));
        try v.put("MEPO_C_LON", try std.fmt.allocPrint(arena.allocator(), "{d}", .{mepo.lon}));
        try v.put("MEPO_TL_LAT", try std.fmt.allocPrint(arena.allocator(), "{d}", .{bbox.topleft_lat}));
        try v.put("MEPO_TL_LON", try std.fmt.allocPrint(arena.allocator(), "{d}", .{bbox.topleft_lon}));
        try v.put("MEPO_BR_LAT", try std.fmt.allocPrint(arena.allocator(), "{d}", .{bbox.bottomright_lat}));
        try v.put("MEPO_BR_LON", try std.fmt.allocPrint(arena.allocator(), "{d}", .{bbox.bottomright_lon}));
        break :env_vars v;
    };

    const args = [_][]const u8{ "sh", "-c", cmd };
    const process_result = try std.ChildProcess.exec(.{ .allocator = &arena.allocator, .argv = args[0..], .env_map = &env_vars });
    const process_result = try std.ChildProcess.exec(.{ .allocator = arena.allocator(), .argv = args[0..], .env_map = &env_vars });
    const exitcode = process_result.term.Exited;

    if (exitcode == 0) {
diff --git a/src/datastructure/datastructure.zig b/src/datastructure/datastructure.zig
index f57a1be..b4c02c7 100644
--- a/src/datastructure/datastructure.zig
+++ b/src/datastructure/datastructure.zig
@@ -74,7 +74,7 @@ pub fn QueueHashMap(comptime key_type: type, comptime metadata_type: type) type
            self.array_hash_map.clearAndFree();
        }

        pub fn init(allocator: *std.mem.Allocator) @This() {
        pub fn init(allocator: std.mem.Allocator) @This() {
            return @as(@This(), .{
                .mutex = std.Thread.Mutex{},
                .array_hash_map = std.array_hash_map.AutoArrayHashMap(key_type, metadata_type).init(allocator),
diff --git a/src/main.zig b/src/main.zig
index 31ce3a1..0a06123 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -54,7 +54,7 @@ pub fn main() !void {
    }
}

fn read_stdin(allocator: *std.mem.Allocator) anyerror![]const u8 {
fn read_stdin(allocator: std.mem.Allocator) anyerror![]const u8 {
    const stream = &std.io.getStdIn().reader();
    const buffer = try allocator.alloc(u8, 1024 * 100);
    _ = try stream.read(buffer);
diff --git a/src/util/utilfile.zig b/src/util/utilfile.zig
index 74b6a70..5314a79 100644
--- a/src/util/utilfile.zig
+++ b/src/util/utilfile.zig
@@ -7,7 +7,7 @@ const wordexp = @cImport({
/// expansion.
///
/// Caller responsible for freeing returned ptr memory
pub fn wordexp_filepath(allocator: *std.mem.Allocator, path: []const u8) ![:0]const u8 {
pub fn wordexp_filepath(allocator: std.mem.Allocator, path: []const u8) ![:0]const u8 {
    var expansion: wordexp.wordexp_t = undefined;
    defer wordexp.wordfree(&expansion);

diff --git a/src/util/utilmepolang.zig b/src/util/utilmepolang.zig
index fdebc0e..acb050e 100644
--- a/src/util/utilmepolang.zig
+++ b/src/util/utilmepolang.zig
@@ -5,7 +5,7 @@ const types = @import("../types.zig");
/// union types.
///
/// Caller responsible for freeing returned memory
pub fn argize(allocator: *std.mem.Allocator, tokens: []const []const u8) ![types.MepoFnNargs]types.MepoArg {
pub fn argize(allocator: std.mem.Allocator, tokens: []const []const u8) ![types.MepoFnNargs]types.MepoArg {
    var args_i: usize = 0;
    var args: [types.MepoFnNargs]types.MepoArg = undefined;
    var i: usize = 0;
@@ -40,7 +40,7 @@ pub fn argize(allocator: *std.mem.Allocator, tokens: []const []const u8) ![types
/// always be treated as seperate tokens
///
/// Caller responsible for freeing returned memory
pub fn tokenize(allocator: *std.mem.Allocator, input: []const u8) ![][]const u8 {
pub fn tokenize(allocator: std.mem.Allocator, input: []const u8) ![][]const u8 {
    var tokens = std.ArrayList([]const u8).init(allocator);
    var current_token = std.ArrayList(u8).init(allocator);

@@ -68,7 +68,7 @@ pub fn tokenize(allocator: *std.mem.Allocator, input: []const u8) ![][]const u8
/// "foo [bar [ bil; jil ]; ]; bar; barn" that would be treated as 3 statements.
///
/// Caller responsible for freeing returned memory
pub fn statementize(allocator: *std.mem.Allocator, tokens: []const []const u8) ![][][]const u8 {
pub fn statementize(allocator: std.mem.Allocator, tokens: []const []const u8) ![][][]const u8 {
    var statements = std.ArrayList([][]const u8).init(allocator);
    var current_statement = std.ArrayList([]const u8).init(allocator);
    var nest_level: usize = 0;
@@ -202,7 +202,7 @@ test "argize" {
    };

    for (specs) |s| {
        const result = try argize(&arena.allocator, s.input[0..]);
        const result = try argize(arena.allocator(), s.input[0..]);
        for (s.expect) |_, i| {
            try std.testing.expectEqual(@TypeOf(s.expect[i]), @TypeOf(result[i]));
            switch (result[i]) {
-- 
2.34.1
Thanks again for your work on this; zig was finally bumped to 0.9.1 in Alpine
and applied your patchset with some changes. Was a useful guide.

Next tagged version of mepo will be built against zig 0.9.1.

[PATCH mepo 02/10] Use C enums as integers Export this patch

From Zig 0.9, C enum types are translated as the underlying integer type
instead of an extern num.
---
 src/Mepo.zig         | 49 ++++++++++++++++++++------------------------
 src/TileCache.zig    | 10 ++++-----
 src/api/bind_key.zig |  4 ++--
 src/util/utilsdl.zig |  6 +++---
 4 files changed, 32 insertions(+), 37 deletions(-)

diff --git a/src/Mepo.zig b/src/Mepo.zig
index 1470044..f8018d5 100644
--- a/src/Mepo.zig
+++ b/src/Mepo.zig
@@ -140,7 +140,7 @@ pub fn blit_tile_surface(mepo: *@This(), tile_x: u32, tile_y: u32, zoom: u8, x_o

    var rect_intersect: sdl.SDL_Rect = undefined;

    if (@intToEnum(sdl.SDL_bool, sdl.SDL_FALSE) == sdl.SDL_IntersectRect(&rect_dest, &bbox, &rect_intersect)) return;
    if (sdl.SDL_IntersectRect(&rect_dest, &bbox, &rect_intersect) == sdl.SDL_FALSE) return;

    const rect_src = sdl.SDL_Rect{
        .x = if (rect_intersect.x > rect_dest.x) rect_dest.w - rect_intersect.w else 0,
@@ -384,20 +384,15 @@ fn blit_help(mepo: *@This()) !void {
        var it = mepo.table_keybindings.iterator();

        while (it.next()) |kv| {
            const keymod = k: {
                if (@enumToInt(kv.key_ptr.keymod) == sdl.KMOD_LCTRL) {
                    break :k " C";
                } else if (@enumToInt(kv.key_ptr.keymod) == sdl.KMOD_LSHIFT) {
                    break :k " S";
                } else if (@enumToInt(kv.key_ptr.keymod) == sdl.KMOD_LSHIFT | sdl.KMOD_LCTRL) {
                    break :k "CS";
                } else if (@enumToInt(kv.key_ptr.keymod) == sdl.KMOD_LALT) {
                    break :k " A";
                }
                break :k "  ";
            const keymod = switch (kv.key_ptr.keymod) {
                sdl.KMOD_LCTRL => " C",
                sdl.KMOD_LSHIFT => " S",
                sdl.KMOD_LSHIFT | sdl.KMOD_LCTRL => "CS",
                sdl.KMOD_LALT => " A",
                else => "  ",
            };

            try acc.append(try std.fmt.allocPrint(mepo.allocator, "{s} {s} = {s}", .{ keymod, sdl.SDL_GetKeyName(@enumToInt(kv.key_ptr.keycode)), kv.value_ptr.* }));
            try acc.append(try std.fmt.allocPrint(mepo.allocator, "{s} {s} = {s}", .{ keymod, sdl.SDL_GetKeyName(@intCast(i32, kv.key_ptr.keycode)), kv.value_ptr.* }));
        }

        break :msg try std.mem.join(mepo.allocator, "\n", acc.items);
@@ -511,8 +506,8 @@ fn event_mousewheel(mepo: *@This(), e: sdl.SDL_Event) types.Pending {

fn event_keydown(mepo: *@This(), e: sdl.SDL_Event) types.Pending {
    if (mepo.table_keybindings.get(.{
        .keycode = @intToEnum(sdl.SDL_KeyCode, @intCast(c_int, e.key.keysym.sym)),
        .keymod = @intToEnum(sdl.SDL_Keymod, @intCast(c_int, e.key.keysym.mod)),
        .keycode = @intCast(c_uint, e.key.keysym.sym),
        .keymod = @intCast(c_uint, e.key.keysym.mod),
    })) |run_expression| {
        utildbg.log("Got keybindings table function with run expression: <{s}>\n", .{run_expression});
        mepo.mepolang_execute(run_expression) catch unreachable;
@@ -531,8 +526,8 @@ fn event_signal(mepo: *@This(), signal: u6) types.Pending {
}

fn event_windowevent(mepo: *@This(), e: sdl.SDL_Event) types.Pending {
    switch (@intToEnum(sdl.SDL_WindowEventID, e.window.event)) {
        .SDL_WINDOWEVENT_RESIZED => {
    switch (@intCast(c_uint, e.window.event)) {
        sdl.SDL_WINDOWEVENT_RESIZED => {
            mepo.win_w = @intCast(u32, e.window.data1);
            mepo.win_h = @intCast(u32, e.window.data2);
            //mepo.center_on_coords(old_lat, old_lon);
@@ -615,7 +610,7 @@ pub fn sdl_event_loop(mepo: *@This()) !void {

    while (true) {
        // Redraw
        const has_pending_motion_events = (sdl.SDL_bool.SDL_TRUE == sdl.SDL_HasEvent(@enumToInt(sdl.SDL_EventType.SDL_MOUSEMOTION)));
        const has_pending_motion_events = sdl.SDL_HasEvent(sdl.SDL_MOUSEMOTION) == sdl.SDL_TRUE;
        if ((!has_pending_motion_events and pending == .Drag) or pending == .Redraw) {
            try blit(mepo);
        }
@@ -624,15 +619,15 @@ pub fn sdl_event_loop(mepo: *@This()) !void {

        // Process SDL events
        if (sdl.SDL_WaitEventTimeout(&e, config.DragThresholdTicks) > 0) {
            pending = switch (@intToEnum(sdl.SDL_EventType, @intCast(c_int, e.type))) {
                .SDL_KEYDOWN => event_keydown,
                .SDL_MOUSEBUTTONDOWN => event_mousebuttondown,
                .SDL_MOUSEMOTION => event_mousemotion,
                .SDL_MOUSEBUTTONUP => event_mousebuttonup,
                .SDL_MOUSEWHEEL => event_mousewheel,
                .SDL_MULTIGESTURE => event_multigesture,
                .SDL_QUIT => event_quit,
                .SDL_WINDOWEVENT => event_windowevent,
            pending = switch (e.type) {
                sdl.SDL_KEYDOWN => event_keydown,
                sdl.SDL_MOUSEBUTTONDOWN => event_mousebuttondown,
                sdl.SDL_MOUSEMOTION => event_mousemotion,
                sdl.SDL_MOUSEBUTTONUP => event_mousebuttonup,
                sdl.SDL_MOUSEWHEEL => event_mousewheel,
                sdl.SDL_MULTIGESTURE => event_multigesture,
                sdl.SDL_QUIT => event_quit,
                sdl.SDL_WINDOWEVENT => event_windowevent,
                else => event_unhandled,
            }(mepo, e);
        }
diff --git a/src/TileCache.zig b/src/TileCache.zig
index 4daec2b..3db6efb 100644
--- a/src/TileCache.zig
+++ b/src/TileCache.zig
@@ -70,7 +70,7 @@ pub fn download_loop(tile_cache: *@This(), graphical_mode: bool) !void {
        curl_processing_loop: while (tile_cache.download_loop_should_continue(graphical_mode)) {
            try curl_errorcheck(curl.curl_multi_perform(tile_cache.curl_multi, &running));
            if (curl.curl_multi_info_read(tile_cache.curl_multi, &n_msgs)) |msg| {
                if (msg.?.*.msg == ._DONE) {
                if (msg.*.msg == curl.CURLMSG_DONE) {
                    tile_cache.download_loop_transfer_complete(msg) catch |err| {
                        utildbg.log("Failed to successfully complete transfer: {}!\n", .{err});
                    };
@@ -350,7 +350,7 @@ fn curl_callback_tile_write(
}

fn curl_errorcheck(response: curl.CURLMcode) !void {
    if (response == @intToEnum(curl.CURLMcode, curl.CURLM_OK)) return;
    if (response == curl.CURLM_OK) return;
    return error.CurlMultiFail;
}

@@ -365,7 +365,7 @@ fn curl_client_to_coords(tile_cache: *@This(), client: ?*curl.CURL) ?types.XYZ {
}

fn curl_setopt(client: *curl.CURL, opt: c_int, value: anytype) !void {
    if (@intToEnum(curl.CURLcode, curl.CURLE_OK) != curl.curl_easy_setopt(client, @intToEnum(curl.CURLoption, opt), value))
    if (curl.curl_easy_setopt(client, @intCast(c_uint, opt), value) != curl.CURLE_OK)
        return error.CurlEasySetOptFail;
}

@@ -399,10 +399,10 @@ fn download_loop_should_continue(tile_cache: *@This(), graphical_mode: bool) boo

fn download_loop_transfer_complete(tile_cache: *@This(), msg: *curl.CURLMsg) !void {
    switch (msg.data.result) {
        .CURLE_OPERATION_TIMEDOUT => {
        curl.CURLE_OPERATION_TIMEDOUT => {
            utildbg.log("Curl timed out for msg: {}\n", .{msg});
        },
        .CURLE_OK => {
        curl.CURLE_OK => {
            if (tile_cache.curl_client_to_coords(msg.*.easy_handle)) |coords| {
                const transfer_datum = tile_cache.transfer_map.get(coords).?;
                const datum_array = transfer_datum.data_arraylist.items;
diff --git a/src/api/bind_key.zig b/src/api/bind_key.zig
index 8a93f42..643a725 100644
--- a/src/api/bind_key.zig
+++ b/src/api/bind_key.zig
@@ -36,8 +36,8 @@ fn bind_key(mepo: *Mepo, mod_str: [:0]const u8, key_str: [:0]const u8, expressio
    }

    const key = .{
        .keymod = @intToEnum(sdl.SDL_Keymod, keymod),
        .keycode = @intToEnum(sdl.SDL_KeyCode, @intCast(c_int, keycode)),
        .keymod = @intCast(c_uint, keymod),
        .keycode = @intCast(c_uint, keycode),
    };

    if (mepo.table_keybindings.get(key)) |heap_str| {
diff --git a/src/util/utilsdl.zig b/src/util/utilsdl.zig
index 61927fa..e55b0ee 100644
--- a/src/util/utilsdl.zig
+++ b/src/util/utilsdl.zig
@@ -6,8 +6,8 @@ const utildbg = @import("./utildbg.zig");
const RectOpt = enum { Draw, Fill };

pub fn sdl_push_resize_event() void {
    const pending_drag = sdl.SDL_bool.SDL_TRUE == sdl.SDL_HasEvent(@enumToInt(sdl.SDL_EventType.SDL_MOUSEMOTION));
    const pending_redraw = sdl.SDL_bool.SDL_TRUE == sdl.SDL_HasEvent(@enumToInt(sdl.SDL_EventType.SDL_WINDOWEVENT));
    const pending_drag = sdl.SDL_HasEvent(sdl.SDL_MOUSEMOTION) == sdl.SDL_TRUE;
    const pending_redraw = sdl.SDL_HasEvent(sdl.SDL_WINDOWEVENT) == sdl.SDL_TRUE;
    if (pending_drag or pending_redraw) return;

    var sdlevent: sdl.SDL_Event = undefined;
@@ -19,7 +19,7 @@ pub fn sdl_push_resize_event() void {
pub fn sdl_renderer_set_draw_color(renderer: *sdl.SDL_Renderer, color: types.Color) errors.SDLError!void {
    var sdl_color = color.sdl();
    const blend_mode = if (sdl_color.a != sdl.SDL_ALPHA_OPAQUE) sdl.SDL_BLENDMODE_ADD else sdl.SDL_BLENDMODE_NONE;
    try errorcheck(sdl.SDL_SetRenderDrawBlendMode(renderer, @intToEnum(sdl.SDL_BlendMode, blend_mode)));
    try errorcheck(sdl.SDL_SetRenderDrawBlendMode(renderer, @intCast(c_uint, blend_mode)));
    try errorcheck(sdl.SDL_SetRenderDrawColor(renderer, sdl_color.r, sdl_color.g, sdl_color.b, color.opacity));
}

-- 
2.34.1

[PATCH mepo 03/10] Migrate to Zig 0.9 mutex API Export this patch

---
 src/datastructure/datastructure.zig | 40 ++++++++++++++---------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/datastructure/datastructure.zig b/src/datastructure/datastructure.zig
index b4c02c7..4e86daa 100644
--- a/src/datastructure/datastructure.zig
+++ b/src/datastructure/datastructure.zig
@@ -2,62 +2,62 @@ const std = @import("std");

pub fn QueueHashMap(comptime key_type: type, comptime metadata_type: type) type {
    return struct {
        array_hash_map: std.array_hash_map.AutoArrayHashMap(comptime key_type, comptime metadata_type),
        array_hash_map: std.array_hash_map.AutoArrayHashMap(key_type, metadata_type),
        mutex: std.Thread.Mutex,

        pub fn count(self: *@This()) usize {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            return self.array_hash_map.count();
        }

        pub fn get(self: *@This(), key: key_type) ?metadata_type {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            return self.array_hash_map.get(key);
        }

        pub fn pop(self: *@This()) struct { key: key_type, value: metadata_type } {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            const popped = self.array_hash_map.pop();
            return .{ .key = popped.key, .value = popped.value };
        }

        pub fn getIndex(self: *@This(), key: key_type) ?usize {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            return self.array_hash_map.getIndex(key);
        }

        pub fn swapRemove(self: *@This(), key: key_type) bool {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            return self.array_hash_map.swapRemove(key);
        }

        pub fn values(self: *@This()) []metadata_type {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            return self.array_hash_map.values();
        }

        pub fn iterator(self: *@This()) std.array_hash_map.AutoArrayHashMap(comptime key_type, comptime metadata_type).Iterator {
            const lock = self.mutex.acquire();
            defer lock.release();
        pub fn iterator(self: *@This()) std.array_hash_map.AutoArrayHashMap(key_type, metadata_type).Iterator {
            self.mutex.lock();
            defer self.mutex.unlock();

            return self.array_hash_map.iterator();
        }

        pub fn put(self: *@This(), k: key_type, v: metadata_type) !void {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            // E.g. ensures when putting items, we always reorder / bump
            // the most recently added item to the tail, thus makes lifo
@@ -68,8 +68,8 @@ pub fn QueueHashMap(comptime key_type: type, comptime metadata_type: type) type
        }

        pub fn clearAndFree(self: *@This()) void {
            const lock = self.mutex.acquire();
            defer lock.release();
            self.mutex.lock();
            defer self.mutex.unlock();

            self.array_hash_map.clearAndFree();
        }
-- 
2.34.1

[PATCH mepo 04/10] Replace std.debug.warn with std.debug.print Export this patch

The former was removed as of Zig 0.9.0
---
 src/Downloader.zig         | 4 ++--
 src/TileCache.zig          | 4 ++--
 src/api/cache_dlradius.zig | 2 +-
 src/util/utildbg.zig       | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/Downloader.zig b/src/Downloader.zig
index 19fa5d4..6ce338b 100644
--- a/src/Downloader.zig
+++ b/src/Downloader.zig
@@ -3,7 +3,7 @@ const utilconversion = @import("util/utilconversion.zig");
const TileCache = @import("./TileCache.zig");

pub fn cache_dlbbox(tile_cache: *TileCache, download_request_str: []const u8) !void {
    std.debug.warn("Downloading based on BBox request: {s}\n", .{download_request_str});
    std.debug.print("Downloading based on BBox request: {s}\n", .{download_request_str});

    try tile_cache.set_cache_dir("~/.cache/mepo/tiles");
    try tile_cache.set_cache_url("https://tile.openstreetmap.org/%3$d/%1$d/%2$d.png");
@@ -27,7 +27,7 @@ pub fn cache_dlbbox(tile_cache: *TileCache, download_request_str: []const u8) !v
        dl_req.a_lon,
    );

    std.debug.warn(
    std.debug.print(
        \\Bbox Distance: {d:.3}km squared ({d:.3}m x {d:.3}m)
        \\Bbox Tiles queued ({} tiles): {} tiles queued; {} tiles already cached
        \\Estimate download: {d:.5} megabytes
diff --git a/src/TileCache.zig b/src/TileCache.zig
index 3db6efb..4ec7bae 100644
--- a/src/TileCache.zig
+++ b/src/TileCache.zig
@@ -371,8 +371,8 @@ fn curl_setopt(client: *curl.CURL, opt: c_int, value: anytype) !void {

/// Show progress indicator for the download loop (for non interactive downloading)
fn download_loop_progress_indicator(tile_cache: *@This(), initial_queue_size: usize) void {
    std.debug.warn("\x1b[1;1H\x1b[2J", .{});
    std.debug.warn(
    std.debug.print("\x1b[1;1H\x1b[2J", .{});
    std.debug.print(
        \\Mepo - download mode
        \\====================
        \\Progress: {d:.2}% ({}/{} tiles downloaded)
diff --git a/src/api/cache_dlradius.zig b/src/api/cache_dlradius.zig
index 68219a4..2c158d1 100644
--- a/src/api/cache_dlradius.zig
+++ b/src/api/cache_dlradius.zig
@@ -32,7 +32,7 @@ fn cache_dlradius(mepo: *Mepo, center_lat: f64, center_lon: f64, zoom_min: i32,
    while (km_radius > utilconversion.distance_haversine(.Km, center_lat, end_lon, center_lat, center_lon))
        end_lon += 1;

    std.debug.warn("Queueing {} {} to {} {} from {} {} based on {} {}\n", .{ start_lat, end_lat, start_lon, end_lon, zoom_min, zoom_max, center_lat, center_lon });
    std.debug.print("Queueing {} {} to {} {} from {} {} based on {} {}\n", .{ start_lat, end_lat, start_lon, end_lon, zoom_min, zoom_max, center_lat, center_lon });

    mepo.tile_cache.set_queue(TileCache.DownloadBBoxRequest{
        .a_lat = start_lat,
diff --git a/src/util/utildbg.zig b/src/util/utildbg.zig
index c02d005..89e7780 100644
--- a/src/util/utildbg.zig
+++ b/src/util/utildbg.zig
@@ -3,5 +3,5 @@ const std = @import("std");
pub var debug_stderr: bool = true;

pub fn log(comptime msg: []const u8, args: anytype) void {
    if (debug_stderr) std.debug.warn(msg, args);
    if (debug_stderr) std.debug.print(msg, args);
}
-- 
2.34.1

[PATCH mepo 05/10] Replace c_void with anyopaque Export this patch

---
 src/Mepo.zig      |  4 ++--
 src/TileCache.zig | 14 +++++++-------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/Mepo.zig b/src/Mepo.zig
index f8018d5..d071c42 100644
--- a/src/Mepo.zig
+++ b/src/Mepo.zig
@@ -908,7 +908,7 @@ pub fn init(allocator: std.mem.Allocator, tile_cache: *TileCache, cfg_opt: ?[]co
                fonts[font_size] = try utilsdl.errorcheck_ptr(
                    sdl.TTF_Font,
                    sdl.TTF_OpenFontRW(
                        sdl.SDL_RWFromConstMem(@ptrCast(*const c_void, &font_dat[0]), font_dat.len),
                        sdl.SDL_RWFromConstMem(@ptrCast(*const anyopaque, &font_dat[0]), font_dat.len),
                        1,
                        @intCast(c_int, font_size),
                    ),
@@ -923,7 +923,7 @@ pub fn init(allocator: std.mem.Allocator, tile_cache: *TileCache, cfg_opt: ?[]co
                fonts[font_size] = try utilsdl.errorcheck_ptr(
                    sdl.TTF_Font,
                    sdl.TTF_OpenFontRW(
                        sdl.SDL_RWFromConstMem(@ptrCast(*const c_void, &font_dat[0]), font_dat.len),
                        sdl.SDL_RWFromConstMem(@ptrCast(*const anyopaque, &font_dat[0]), font_dat.len),
                        1,
                        @intCast(c_int, font_size),
                    ),
diff --git a/src/TileCache.zig b/src/TileCache.zig
index 4ec7bae..022b746 100644
--- a/src/TileCache.zig
+++ b/src/TileCache.zig
@@ -310,7 +310,7 @@ fn curl_add_to_multi_and_register_transfer(tile_cache: *@This(), coords: types.X
    try curl_setopt(transfer_datum.client, curl.CURLOPT_NOSIGNAL, @intCast(c_long, 1));
    try curl_setopt(transfer_datum.client, curl.CURLOPT_CONNECTTIMEOUT, @intCast(c_long, config.DownloadTimeoutSeconds));
    try curl_setopt(transfer_datum.client, curl.CURLOPT_TIMEOUT, @intCast(c_long, config.DownloadTimeoutSeconds));
    try curl_setopt(transfer_datum.client, curl.CURLOPT_URL, @ptrCast(*c_void, tile_url));
    try curl_setopt(transfer_datum.client, curl.CURLOPT_URL, @ptrCast(*anyopaque, tile_url));
    try curl_setopt(transfer_datum.client, curl.CURLOPT_USERAGENT, config.DownloadUseragent);
    try curl_setopt(transfer_datum.client, curl.CURLOPT_VERBOSE, @intCast(c_long, 0));
    try curl_setopt(transfer_datum.client, curl.CURLOPT_WRITEDATA, transfer_datum);
@@ -323,7 +323,7 @@ fn curl_add_to_multi_and_register_transfer(tile_cache: *@This(), coords: types.X
}

fn curl_callback_tile_xferinfo(
    user_data: *c_void,
    user_data: *anyopaque,
    dl_total: curl.curl_off_t,
    dl_now: curl.curl_off_t,
    ul_total: curl.curl_off_t,
@@ -337,10 +337,10 @@ fn curl_callback_tile_xferinfo(
}

fn curl_callback_tile_write(
    data: *c_void,
    data: *anyopaque,
    size: c_uint,
    nmemb: c_uint,
    user_data: *c_void,
    user_data: *anyopaque,
) callconv(.C) c_uint {
    //utildbg.log("Write!!: \n", .{});
    var transfer_datum = @intToPtr(*TransferDatum, @ptrToInt(user_data));
@@ -449,7 +449,7 @@ fn download_loop_transfer_cleanup(tile_cache: *@This(), client: ?*curl.CURL) !vo
fn load_data_to_texture(tile_cache: *@This(), coords: types.XYZ, data: []u8) !*sdl.SDL_Texture {
    if (data.len == 0) return error.LoadToSurfaceFailEmptyData;

    const memory = try utilsdl.errorcheck_ptr(sdl.SDL_RWops, sdl.SDL_RWFromConstMem(@ptrCast(*c_void, &data[0]), @intCast(c_int, data.len)));
    const memory = try utilsdl.errorcheck_ptr(sdl.SDL_RWops, sdl.SDL_RWFromConstMem(@ptrCast(*anyopaque, &data[0]), @intCast(c_int, data.len)));
    const surface = try utilsdl.errorcheck_ptr(sdl.SDL_Surface, sdl.IMG_Load_RW(memory, 1));
    defer sdl.SDL_FreeSurface(surface);
    const texture = try utilsdl.errorcheck_ptr(sdl.SDL_Texture, sdl.SDL_CreateTextureFromSurface(tile_cache.renderer, surface));
@@ -487,7 +487,7 @@ fn tile_exists_in_fs_and_non_expired(tile_cache: *@This(), coords: types.XYZ) !b
    return false;
}

fn threadable_tile_bg_bbox_queue(userdata: ?*c_void) callconv(.C) c_int {
fn threadable_tile_bg_bbox_queue(userdata: ?*anyopaque) callconv(.C) c_int {
    var tile_cache = @ptrCast(*@This(), @alignCast(@alignOf(*@This()), userdata.?));
    if (tile_cache.tile_bg_bbox_queue(tile_cache.bbox_queue.?, true)) |q| {
        utildbg.log("Done: {}\n", .{q});
@@ -498,7 +498,7 @@ fn threadable_tile_bg_bbox_queue(userdata: ?*c_void) callconv(.C) c_int {
    return 0;
}

fn threadable_download_loop_sdl(userdata: ?*c_void) callconv(.C) c_int {
fn threadable_download_loop_sdl(userdata: ?*anyopaque) callconv(.C) c_int {
    var tile_cache = @ptrCast(*@This(), @alignCast(@alignOf(*@This()), userdata.?));
    tile_cache.download_loop(true) catch |e| {
        utildbg.log("Error running download loop: {}\n", .{e});
-- 
2.34.1

[PATCH mepo 06/10] Use Zig 0.9's generic std.mem.tokenize Export this patch

---
 src/Downloader.zig | 2 +-
 src/Mepo.zig       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Downloader.zig b/src/Downloader.zig
index 6ce338b..0dfe403 100644
--- a/src/Downloader.zig
+++ b/src/Downloader.zig
@@ -58,7 +58,7 @@ fn parse_download_request(download_request_str: []const u8) !TileCache.DownloadB
    var zoom_min: i32 = undefined;
    var zoom_max: i32 = undefined;

    var tokens_it = std.mem.tokenize(download_request_str, ",");
    var tokens_it = std.mem.tokenize(u8, download_request_str, ",");
    var i: usize = 0;
    while (tokens_it.next()) |token| {
        switch (i) {
diff --git a/src/Mepo.zig b/src/Mepo.zig
index d071c42..da389ae 100644
--- a/src/Mepo.zig
+++ b/src/Mepo.zig
@@ -761,7 +761,7 @@ pub fn blit_multiline_text(
        var textures_array: [50]*sdl.SDL_Texture = undefined;
        var textures_array_size: usize = 0;

        var lines_it = std.mem.tokenize(msg, "\n");
        var lines_it = std.mem.tokenize(u8, msg, "\n");
        while (lines_it.next()) |line| {
            if (textures_array_size + 1 > textures_array.len - 1) break;

-- 
2.34.1

[PATCH mepo 07/10] Use OS-independent signal handling Export this patch

Zig 0.9 does not export signal enum and function in std.os.linux
---
 src/Mepo.zig            | 8 +++-----
 src/api/bind_signal.zig | 4 ++--
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/Mepo.zig b/src/Mepo.zig
index da389ae..85eb55f 100644
--- a/src/Mepo.zig
+++ b/src/Mepo.zig
@@ -957,15 +957,13 @@ pub fn init(allocator: std.mem.Allocator, tile_cache: *TileCache, cfg_opt: ?[]co

    // Setup TERM/INT signals
    {
        const sigs: [2]u6 = .{ std.os.linux.SIGTERM, std.os.linux.SIGINT };
        const sigs: [2]u6 = .{ std.os.SIG.TERM, std.os.SIG.INT };
        for (sigs) |sig| {
            if (0 != std.os.linux.sigaction(sig, &.{
            std.os.sigaction(sig, &.{
                .handler = .{ .handler = sighandle_terminate },
                .mask = [_]u32{0} ** 32,
                .flags = @as(c_uint, 0),
            }, null)) {
                return error.FailedToSetupSighandler;
            }
            }, null);
        }
    }

diff --git a/src/api/bind_signal.zig b/src/api/bind_signal.zig
index fa9dece..7fdc70e 100644
--- a/src/api/bind_signal.zig
+++ b/src/api/bind_signal.zig
@@ -16,9 +16,9 @@ fn bind_signal(mepo: *Mepo, signo_str: [:0]const u8, expression: []const u8) !vo
    // Signal
    var signo: u6 = 0;
    if (std.cstr.cmp(signo_str, "USR1") == 0) {
        signo = std.os.linux.SIGUSR1;
        signo = std.os.SIG.USR1;
    } else if (std.cstr.cmp(signo_str, "USR2") == 0) {
        signo = std.os.linux.SIGUSR2;
        signo = std.os.SIG.USR2;
    } else {
        return error.InvalidSignalName;
    }
-- 
2.34.1

[PATCH mepo 08/10] Fix name shadowing in Color.sdl Export this patch

---
 src/Mepo.zig         | 2 +-
 src/types.zig        | 2 +-
 src/util/utilsdl.zig | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Mepo.zig b/src/Mepo.zig
index 85eb55f..f862865 100644
--- a/src/Mepo.zig
+++ b/src/Mepo.zig
@@ -770,7 +770,7 @@ pub fn blit_multiline_text(

            const text_surf = try utilsdl.errorcheck_ptr(
                sdl.SDL_Surface,
                sdl.TTF_RenderText_Blended(mepo.fonts_normal[font_size], @ptrCast([*c]const u8, line_sentinel_terminated), (types.Color{ .value = color }).sdl()),
                sdl.TTF_RenderText_Blended(mepo.fonts_normal[font_size], @ptrCast([*c]const u8, line_sentinel_terminated), (types.Color{ .value = color }).toSdl()),
            );
            defer sdl.SDL_FreeSurface(text_surf);

diff --git a/src/types.zig b/src/types.zig
index 9eea0cf..19018fc 100644
--- a/src/types.zig
+++ b/src/types.zig
@@ -35,7 +35,7 @@ pub const Color = struct {
    value: u24 = 0x000000,
    opacity: u8 = 255,

    pub fn sdl(self: @This()) sdl.SDL_Color {
    pub fn toSdl(self: @This()) sdl.SDL_Color {
        return .{
            .r = @intCast(u8, self.value >> 16),
            .g = @intCast(u8, self.value >> 8 & 0xff),
diff --git a/src/util/utilsdl.zig b/src/util/utilsdl.zig
index e55b0ee..5d70c23 100644
--- a/src/util/utilsdl.zig
+++ b/src/util/utilsdl.zig
@@ -17,7 +17,7 @@ pub fn sdl_push_resize_event() void {
}

pub fn sdl_renderer_set_draw_color(renderer: *sdl.SDL_Renderer, color: types.Color) errors.SDLError!void {
    var sdl_color = color.sdl();
    var sdl_color = color.toSdl();
    const blend_mode = if (sdl_color.a != sdl.SDL_ALPHA_OPAQUE) sdl.SDL_BLENDMODE_ADD else sdl.SDL_BLENDMODE_NONE;
    try errorcheck(sdl.SDL_SetRenderDrawBlendMode(renderer, @intCast(c_uint, blend_mode)));
    try errorcheck(sdl.SDL_SetRenderDrawColor(renderer, sdl_color.r, sdl_color.g, sdl_color.b, color.opacity));
@@ -47,7 +47,7 @@ pub fn sdl_renderer_draw_rect(renderer: *sdl.SDL_Renderer, color: types.Color, r
            break :rect_ptr null;
        }
    };
    try sdl_renderer_set_draw_color(renderer, color, opacity);
    try sdl_renderer_set_draw_color(renderer, color);
    try errorcheck(sdl.SDL_RenderDrawRect(renderer, rect_ptr));
}

-- 
2.34.1

[PATCH mepo 09/10] Fix unused stuff Export this patch

Zig 0.9 is more strict about this.
---
 build.zig                    |  9 +++++----
 src/Downloader.zig           |  2 +-
 src/Mepo.zig                 | 31 +++----------------------------
 src/TileCache.zig            |  7 +++----
 src/api/cache_dlbbox.zig     | 23 ++++++-----------------
 src/api/cache_dlradius.zig   |  1 -
 src/api/cache_queueclear.zig |  6 +-----
 src/api/center_on_coords.zig | 10 ++--------
 src/api/center_on_mouse.zig  |  2 +-
 src/api/center_on_pin.zig    |  6 +-----
 src/api/fileload.zig         |  4 ----
 src/api/move_relative.zig    |  8 ++------
 src/api/pin_deactivate.zig   |  6 +-----
 src/api/pin_meta.zig         |  3 ---
 src/api/pin_purge.zig        |  6 +-----
 src/api/quit.zig             |  2 +-
 src/main.zig                 |  1 -
 src/test.zig                 |  1 -
 src/util/utilmepolang.zig    |  2 +-
 19 files changed, 29 insertions(+), 101 deletions(-)

diff --git a/build.zig b/build.zig
index 60dd0ff..09a3cfe 100644
--- a/build.zig
@@ -1,7 +1,8 @@
const Builder = @import("std").build.Builder;
const Builder = std.build.Builder;
const LibExeObjStep = std.build.LibExeObjStep;
const std = @import("std");

fn setDependencies(b: *Builder, step: *std.build.LibExeObjStep) void {
fn setDependencies(step: *LibExeObjStep) void {
    step.linkSystemLibrary("c");
    step.linkSystemLibrary("SDL2");
    step.linkSystemLibrary("SDL2_image");
@@ -17,7 +18,7 @@ pub fn build(b: *Builder) void {
    exe.setTarget(target);
    exe.setBuildMode(mode);

    setDependencies(b, exe);
    setDependencies(exe);

    exe.install();

@@ -30,6 +31,6 @@ pub fn build(b: *Builder) void {
    // Setup test
    const test_all_step = b.step("test", "Run all tests.");
    const run_tests = b.addTest("./src/test.zig");
    setDependencies(b, run_tests);
    setDependencies(run_tests);
    test_all_step.dependOn(&run_tests.step);
}
diff --git a/src/Downloader.zig b/src/Downloader.zig
index 0dfe403..e8190a9 100644
--- a/src/Downloader.zig
+++ b/src/Downloader.zig
@@ -47,7 +47,7 @@ pub fn cache_dlbbox(tile_cache: *TileCache, download_request_str: []const u8) !v
        },
    );
    std.time.sleep(std.time.ns_per_s * delay_seconds);
    const result = tile_cache.download_loop(false);
    _ = try tile_cache.download_loop(false);
}

fn parse_download_request(download_request_str: []const u8) !TileCache.DownloadBBoxRequest {
diff --git a/src/Mepo.zig b/src/Mepo.zig
index f862865..28cbe5b 100644
--- a/src/Mepo.zig
+++ b/src/Mepo.zig
@@ -6,7 +6,6 @@ const errors = @import("./errors.zig");
const TileCache = @import("./TileCache.zig");
const utilconversion = @import("./util/utilconversion.zig");
const utilmepolang = @import("util/utilmepolang.zig");
const utilstrings = @import("util/utilstrings.zig");
const utildbg = @import("util/utildbg.zig");
const utilsdl = @import("util/utilsdl.zig");
const zoom_relative = @import("./api/zoom_relative.zig").zoom_relative;
@@ -170,7 +169,7 @@ pub fn blit_tile_surface(mepo: *@This(), tile_x: u32, tile_y: u32, zoom: u8, x_o
        .transfer_datum => {
            try utilsdl.sdl_renderer_rect(mepo.renderer, .{ .value = 0x006400 }, rect_intersect, .Fill);
        },
        .error_type => |error_type| {
        .error_type => {
            try utilsdl.sdl_renderer_rect(mepo.renderer, .{ .value = 0xffdfd1 }, rect_intersect, .Fill);
            try utilsdl.sdl_renderer_rect(mepo.renderer, .{ .value = 0xebebeb }, rect_intersect, .Draw);
            try mepo.blit_multiline_text(
@@ -190,33 +189,10 @@ pub fn blit_tile_surface(mepo: *@This(), tile_x: u32, tile_y: u32, zoom: u8, x_o
fn blit_overlay_pindetails(mepo: *@This()) !void {
    if (!mepo.overlay_pindetails) return;

    const bbox = mepo.bounding_box();
    const d_unit = if (mepo.distance_unit_tf_km_mi) "km" else "mi";

    var arena = std.heap.ArenaAllocator.init(mepo.allocator);
    defer arena.deinit();
    const str_coords = try std.fmt.allocPrintZ(&arena.allocator, "{d:.5}, {d:.5}", .{ mepo.lat, mepo.lon });
    const str_bbox = try std.fmt.allocPrintZ(
        &arena.allocator,
        "{d:.3}x{d:.3}{s}",
        .{
            utilconversion.distance_haversine(
                if (mepo.distance_unit_tf_km_mi) .Km else .Mi,
                mepo.lat,
                bbox.topleft_lon,
                mepo.lat,
                bbox.bottomright_lon,
            ),
            utilconversion.distance_haversine(
                if (mepo.distance_unit_tf_km_mi) .Km else .Mi,
                bbox.topleft_lat,
                mepo.lon,
                bbox.bottomright_lat,
                mepo.lon,
            ),
            d_unit,
        },
    );

    var al = std.ArrayList([2][:0]const u8).init(arena.allocator());
    defer al.deinit();
@@ -537,12 +513,12 @@ fn event_windowevent(mepo: *@This(), e: sdl.SDL_Event) types.Pending {
    return .Redraw;
}

fn event_quit(mepo: *@This(), e: sdl.SDL_Event) types.Pending {
fn event_quit(_: *@This(), _: sdl.SDL_Event) types.Pending {
    graceful_terminate();
    return .None;
}

fn event_unhandled(mepo: *@This(), e: sdl.SDL_Event) types.Pending {
fn event_unhandled(_: *@This(), e: sdl.SDL_Event) types.Pending {
    utildbg.log("Unhandled SDL Event: {}\n", .{e.type});
    return .None;
}
@@ -688,7 +664,6 @@ pub fn blit_table(mepo: *@This(), x: i32, y: i32, padding: c_int, rows: []const

    for (rows) |row, row_i| {


        // Label
        //sdl.TTF_SetFontStyle(mepo.fonts[config.FontSize], sdl.TTF_STYLE_BOLD);
        const surf_lab = try utilsdl.errorcheck_ptr(
diff --git a/src/TileCache.zig b/src/TileCache.zig
index 022b746..628299f 100644
--- a/src/TileCache.zig
+++ b/src/TileCache.zig
@@ -330,6 +330,8 @@ fn curl_callback_tile_xferinfo(
    ul_now: curl.curl_off_t,
) callconv(.C) c_uint {
    //utildbg.log("Progress: {} {}\n", .{ dl_now, dl_total });
    _ = ul_total;
    _ = ul_now;
    var tile_data = @intToPtr(*TransferDatum, @ptrToInt(user_data));
    tile_data.progress_dl_now = dl_now;
    tile_data.progress_dl_total = dl_total;
@@ -436,7 +438,6 @@ fn download_loop_transfer_complete(tile_cache: *@This(), msg: *curl.CURLMsg) !vo
fn download_loop_transfer_cleanup(tile_cache: *@This(), client: ?*curl.CURL) !void {
    if (tile_cache.curl_client_to_coords(client)) |coords| {
        const transfer_datum = tile_cache.transfer_map.get(coords).?;
        const datum_array = transfer_datum.data_arraylist.items;
        tile_cache.transfer_map.get(coords).?.data_arraylist.deinit();
        _ = tile_cache.transfer_map.swapRemove(coords);
        tile_cache.allocator.destroy(transfer_datum);
@@ -476,9 +477,7 @@ fn tile_exists_in_fs_and_non_expired(tile_cache: *@This(), coords: types.XYZ) !b
    if (tile_cache.cache_dir) |cache_dir| {
        const png = try png_path(tile_cache.allocator, tile_cache.source_url.?, coords);
        defer tile_cache.allocator.free(png);
        const tile_file = cache_dir.openFile(png, .{}) catch |_| {
            return false;
        };
        const tile_file = cache_dir.openFile(png, .{}) catch return false;
        defer tile_file.close();
        if (tile_cache.expiry_seconds < 0 or std.time.timestamp() < (try tile_file.stat()).ctime + tile_cache.expiry_seconds) {
            return true;
diff --git a/src/api/cache_dlbbox.zig b/src/api/cache_dlbbox.zig
index 1c5c675..79c39cb 100644
--- a/src/api/cache_dlbbox.zig
+++ b/src/api/cache_dlbbox.zig
@@ -7,23 +7,12 @@ const utildbg = @import("../util/utildbg.zig");
const config = @import("../config.zig");

pub fn f_cache_dlbbox(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    const a_lat = args[0].Number;
    const a_lon = args[1].Number;
    const b_lat = args[2].Number;
    const b_lon = args[3].Number;
    const zoom_min = @floatToInt(i32, args[4].Number);
    const zoom_max = @floatToInt(i32, args[5].Number);

    try cache_dlbbox(mepo, a_lat, a_lon, b_lat, b_lon, zoom_min, zoom_max);
}

fn cache_dlbbox(mepo: *Mepo, a_lat: f64, a_lon: f64, b_lat: f64, b_lon: f64, zoom_min: i32, zoom_max: i32) !void {
    mepo.tile_cache.set_queue(TileCache.DownloadBBoxRequest{
        .a_lat = a_lat,
        .a_lon = a_lon,
        .b_lat = b_lat,
        .b_lon = b_lon,
        .zoom_min = zoom_min,
        .zoom_max = zoom_max,
        .a_lat = args[0].Number,
        .a_lon = args[1].Number,
        .b_lat = args[2].Number,
        .b_lon = args[3].Number,
        .zoom_min = @floatToInt(i32, args[4].Number),
        .zoom_max = @floatToInt(i32, args[5].Number),
    });
}
diff --git a/src/api/cache_dlradius.zig b/src/api/cache_dlradius.zig
index 2c158d1..5f39671 100644
--- a/src/api/cache_dlradius.zig
+++ b/src/api/cache_dlradius.zig
@@ -17,7 +17,6 @@ pub fn f_cache_dlradius(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !vo
}

fn cache_dlradius(mepo: *Mepo, center_lat: f64, center_lon: f64, zoom_min: i32, zoom_max: i32, km_radius: f64) !void {
    const step_km = 1;
    var start_lat = center_lat;
    var end_lat = center_lat;
    var start_lon = center_lon;
diff --git a/src/api/cache_queueclear.zig b/src/api/cache_queueclear.zig
index d160c84..52fc90f 100644
--- a/src/api/cache_queueclear.zig
+++ b/src/api/cache_queueclear.zig
@@ -5,10 +5,6 @@ const utilconversion = @import("../util/utilconversion.zig");
const utildbg = @import("../util/utildbg.zig");
const config = @import("../config.zig");

pub fn f_cache_queueclear(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    try cache_queueclear(mepo);
}

fn cache_queueclear(mepo: *Mepo) !void {
pub fn f_cache_queueclear(mepo: *Mepo, _: [types.MepoFnNargs]types.MepoArg) !void {
    mepo.tile_cache.set_queue(null);
}
diff --git a/src/api/center_on_coords.zig b/src/api/center_on_coords.zig
index 06ca6cb..da53b8d 100644
--- a/src/api/center_on_coords.zig
+++ b/src/api/center_on_coords.zig
@@ -3,12 +3,6 @@ const types = @import("../types.zig");
const std = @import("std");

pub fn f_center_on_coords(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    const lat = args[0].Number;
    const lon = args[1].Number;
    center_on_coords(mepo, lat, lon);
}

fn center_on_coords(mepo: *Mepo, lat: f64, lon: f64) void {
    mepo.lon = lon;
    mepo.lat = lat;
    mepo.lon = args[0].Number;
    mepo.lat = args[1].Number;
}
diff --git a/src/api/center_on_mouse.zig b/src/api/center_on_mouse.zig
index e49d106..6b25582 100644
--- a/src/api/center_on_mouse.zig
+++ b/src/api/center_on_mouse.zig
@@ -3,7 +3,7 @@ const types = @import("../types.zig");
const std = @import("std");
const sdl = @import("../sdlshim.zig");

pub fn f_center_on_mouse(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
pub fn f_center_on_mouse(mepo: *Mepo, _: [types.MepoFnNargs]types.MepoArg) !void {
    var x: c_int = undefined;
    var y: c_int = undefined;
    _ = sdl.SDL_GetMouseState(&x, &y);
diff --git a/src/api/center_on_pin.zig b/src/api/center_on_pin.zig
index e7046bd..18db5d0 100644
--- a/src/api/center_on_pin.zig
+++ b/src/api/center_on_pin.zig
@@ -2,11 +2,7 @@ const Mepo = @import("../Mepo.zig");
const types = @import("../types.zig");
const std = @import("std");

pub fn f_center_on_pin(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    try center_on_pin(mepo);
}

fn center_on_pin(mepo: *Mepo) !void {
pub fn f_center_on_pin(mepo: *Mepo, _: [types.MepoFnNargs]types.MepoArg) !void {
    if (mepo.pin_group_active_item != null) {
        const pin = mepo.pin_groups[mepo.pin_group_active].pins.items[mepo.pin_group_active_item.?];
        mepo.lat = pin.lat;
diff --git a/src/api/fileload.zig b/src/api/fileload.zig
index 55272a1..04323e2 100644
--- a/src/api/fileload.zig
+++ b/src/api/fileload.zig
@@ -5,10 +5,6 @@ const utilfile = @import("../util/utilfile.zig");

pub fn f_fileload(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    const filepath = args[0].Text;
    try fileload(mepo, filepath);
}

fn fileload(mepo: *Mepo, filepath: []const u8) !void {
    const expanded_path = try utilfile.wordexp_filepath(mepo.allocator, filepath);
    defer mepo.allocator.free(expanded_path);
    const mepolang_file = try std.fs.openFileAbsolute(expanded_path, .{ .read = true });
diff --git a/src/api/move_relative.zig b/src/api/move_relative.zig
index 70d77ae..ca3140a 100644
--- a/src/api/move_relative.zig
+++ b/src/api/move_relative.zig
@@ -5,10 +5,6 @@ const std = @import("std");
pub fn f_move_relative(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    const rel_x = @floatToInt(i32, args[0].Number);
    const rel_y = @floatToInt(i32, args[1].Number);
    move_relative(mepo, rel_x, rel_y);
}

fn move_relative(mepo: *Mepo, relative_x: i32, relative_y: i32) void {
    mepo.set_x(mepo.get_x() + relative_x);
    mepo.set_y(mepo.get_y() + relative_y);
    mepo.set_x(mepo.get_x() + rel_x);
    mepo.set_y(mepo.get_y() + rel_y);
}
diff --git a/src/api/pin_deactivate.zig b/src/api/pin_deactivate.zig
index 44d638c..287f201 100644
--- a/src/api/pin_deactivate.zig
+++ b/src/api/pin_deactivate.zig
@@ -2,10 +2,6 @@ const Mepo = @import("../Mepo.zig");
const types = @import("../types.zig");
const std = @import("std");

pub fn f_pin_deactivate(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    try pin_deactivate(mepo);
}

fn pin_deactivate(mepo: *Mepo) !void {
pub fn f_pin_deactivate(mepo: *Mepo, _: [types.MepoFnNargs]types.MepoArg) !void {
    mepo.pin_group_active_item = null;
}
diff --git a/src/api/pin_meta.zig b/src/api/pin_meta.zig
index 3f944e4..b4b5ee2 100644
--- a/src/api/pin_meta.zig
+++ b/src/api/pin_meta.zig
@@ -6,10 +6,7 @@ pub fn f_pin_meta(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    const handle = args[0].Text;
    const key = args[1].Text;
    const value = args[2].Text;
    try pin_meta(mepo, handle, key, value);
}

pub fn pin_meta(mepo: *Mepo, handle: [:0]const u8, key: [:0]const u8, value: [:0]const u8) !void {
    var pin: *types.Pin = pin: {
        for (mepo.pin_groups[mepo.pin_group_active].pins.items) |*pin| {
            if (std.mem.eql(u8, pin.name, handle)) {
diff --git a/src/api/pin_purge.zig b/src/api/pin_purge.zig
index e5fe7dd..8dd4b4c 100644
--- a/src/api/pin_purge.zig
+++ b/src/api/pin_purge.zig
@@ -2,11 +2,7 @@ const Mepo = @import("../Mepo.zig");
const types = @import("../types.zig");
const std = @import("std");

pub fn f_pin_purge(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
    try pin_purge(mepo);
}

fn pin_purge(mepo: *Mepo) !void {
pub fn f_pin_purge(mepo: *Mepo, _: [types.MepoFnNargs]types.MepoArg) !void {
    mepo.pin_group_active_item = null;

    for (mepo.pin_groups[mepo.pin_group_active].pins.items) |p| {
diff --git a/src/api/quit.zig b/src/api/quit.zig
index 62beb84..092c0cd 100644
--- a/src/api/quit.zig
+++ b/src/api/quit.zig
@@ -2,6 +2,6 @@ const Mepo = @import("../Mepo.zig");
const types = @import("../types.zig");
const std = @import("std");

pub fn f_quit(mepo: *Mepo, args: [types.MepoFnNargs]types.MepoArg) !void {
pub fn f_quit(_: *Mepo, _: [types.MepoFnNargs]types.MepoArg) !void {
    Mepo.graceful_terminate();
}
diff --git a/src/main.zig b/src/main.zig
index 0a06123..e31736c 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -4,7 +4,6 @@ const Mepo = @import("./Mepo.zig");
const Downloader = @import("./Downloader.zig");
const TileCache = @import("./TileCache.zig");
const sdl = @import("./sdlshim.zig");
const utilstrings = @import("./util/utilstrings.zig");
const utildbg = @import("./util/utildbg.zig");

pub fn main() !void {
diff --git a/src/test.zig b/src/test.zig
index a37981d..9870356 100644
--- a/src/test.zig
+++ b/src/test.zig
@@ -1,5 +1,4 @@
comptime {
    _ = @import("util/utilmepolang.zig");
    _ = @import("util/utilconversion.zig");
    _ = @import("util/utilstrings.zig");
}
diff --git a/src/util/utilmepolang.zig b/src/util/utilmepolang.zig
index acb050e..3c4da49 100644
--- a/src/util/utilmepolang.zig
+++ b/src/util/utilmepolang.zig
@@ -146,7 +146,7 @@ test "tokenize" {
        // e.g. treats [] brackets as individual tokens irregardless of whitespace (test [foo])
        const input = "foo bar [baz]";
        var expect = [_][]const u8{ "foo", "bar", "[", "baz", "]" };
        var result = try tokenize(std.heap.c_allocator, "foo bar [baz]");
        var result = try tokenize(std.heap.c_allocator, input);
        try std.testing.expectEqual(@intCast(usize, 5), result.len);
        for (result) |_, idx| {
            try std.testing.expect(std.mem.eql(u8, expect[idx], result[idx]));
-- 
2.34.1

[PATCH mepo 10/10] Finish Zig 0.9 migration Export this patch

---
 src/main.zig | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/main.zig b/src/main.zig
index e31736c..98708e0 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -8,9 +8,9 @@ const utildbg = @import("./util/utildbg.zig");

pub fn main() !void {
    comptime {
        if (std.SemanticVersion.order(builtin.zig_version, try std.SemanticVersion.parse("0.8.1")) != .eq) {
            @panic("Must be built against Zig 0.8.1");
        }
        const v = builtin.zig_version;
        if (v.major != 0 or v.minor != 9)
            @panic("Must be built against Zig 0.9");
    }

    const allocator = std.heap.c_allocator;
-- 
2.34.1
I missed the Zig 0.8.1 mentions in doc/developguide.md and
src/util/utilconversion.zig, but that's probably OK since it might takes
some extra time for Zig 0.9 to arrive in Alpine.  I sent this today
as I'm migrating another project and wanted to make use of my bio cache.

On the bright side, Mepo is working on NixOS unstable now.
I'll try to package it unless someone else beats me to it.