const c = @cImport({ @cInclude("SDL2/SDL.h"); @cInclude("lua/lua.h"); @cInclude("lua/lualib.h"); @cInclude("lua/lauxlib.h"); }); const errors = @import("./errors.zig"); const io = @import("./io.zig"); const stack = @import("./stack.zig"); const std = @import("std"); const Request = struct { next: ?*Request = null, frame: anyframe, message: union(enum) { close: struct { file: *c.SDL_RWops, is_closed: *bool, }, open_readable: struct { uri: *const io.Uri, file: ?*c.SDL_RWops, }, }, }; fn luaAlloc(userdata: ?*anyopaque, ptr: ?*anyopaque, original_size: usize, updated_size: usize) callconv(.C) ?*anyopaque { // Implementation derived from // https://github.com/natecraddock/ziglua/blob/master/src/ziglua.zig. const alignment = @alignOf(std.c.max_align_t); const Allocator = std.mem.Allocator; const allocator = @ptrCast(*Allocator, @alignCast(@alignOf(Allocator), userdata.?)); if (@ptrCast(?[*]align(alignment) u8, @alignCast(alignment, ptr))) |prev_ptr| { // Allocator is working with an existing pointer. const prev_slice = prev_ptr[0 .. original_size]; if (updated_size == 0) { // Updated size of `0` to free the existing memory block. allocator.free(prev_slice); return null; } // Resize the existing memory block. return (allocator.reallocAdvanced(prev_slice, alignment, updated_size, .exact) catch return null).ptr; } // No existing pointer, allocate a new block of memory. return (allocator.alignedAlloc(u8, alignment, updated_size) catch return null).ptr; } /// /// Entry point. /// pub fn main() anyerror!void { if (c.SDL_Init(c.SDL_INIT_EVERYTHING) != 0) { c.SDL_LogCritical(c.SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize SDL2 runtime"); return error.InitFailure; } defer c.SDL_Quit(); const pref_path = create_pref_path: { const path = c.SDL_GetPrefPath("ona", "ona") orelse { c.SDL_LogCritical(c.SDL_LOG_CATEGORY_APPLICATION, "Failed to load user path"); return error.InitFailure; }; break: create_pref_path path[0 .. std.mem.len(path)]; }; defer c.SDL_free(pref_path.ptr); const window = create_window: { const pos = c.SDL_WINDOWPOS_UNDEFINED; var flags = @as(u32, 0); break: create_window c.SDL_CreateWindow("Ona", pos, pos, 640, 480, flags) orelse { c.SDL_LogCritical(c.SDL_LOG_CATEGORY_APPLICATION, "Failed to load SDL2 window"); return error.InitFailure; }; }; defer c.SDL_DestroyWindow(window); const renderer = create_renderer: { var flags = @as(u32, 0); break: create_renderer c.SDL_CreateRenderer(window, -1, flags) orelse { c.SDL_LogCritical(c.SDL_LOG_CATEGORY_APPLICATION, "Failed to load SDL2 renderer"); return error.InitFailure; }; }; defer c.SDL_DestroyRenderer(renderer); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var lua_allocator = gpa.allocator(); const lua_state = c.lua_newstate(luaAlloc, @ptrCast(*anyopaque, &lua_allocator)) orelse { c.SDL_LogCritical(c.SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize Lua virtual machine"); return error.InitFailure; }; defer c.lua_close(lua_state); var request_chain = @as(?*Request, null); var is_running = true; while (is_running) { var event = std.mem.zeroes(c.SDL_Event); while (c.SDL_PollEvent(&event) != 0) { switch (event.type) { c.SDL_QUIT => is_running = false, else => {}, } } if (c.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255) != 0) { c.SDL_LogError(c.SDL_LOG_CATEGORY_VIDEO, c.SDL_GetError()); c.SDL_ClearError(); } if (c.SDL_RenderClear(renderer) != 0) { c.SDL_LogError(c.SDL_LOG_CATEGORY_VIDEO, c.SDL_GetError()); c.SDL_ClearError(); } c.SDL_RenderPresent(renderer); while (request_chain) |request_head| { const request = request_head; request_chain = request_head.next; switch (request.message) { .close => |*close| close.is_closed.* = (c.SDL_RWclose(close.file) == 0), .open_readable => |*open_readable| { if (open_readable.uri.isScheme("data")) { var path = stack.Fixed(u8, 4096){}; // These can never fail as the sum of the potential bytes written will // always be less than 4096. path.pushAll("./") catch unreachable; std.debug.assert(open_readable.uri.writePath(path.asWriter())); open_readable.file = c.SDL_RWFromFile(&path.buffer, "r"); } else if (open_readable.uri.isScheme("user")) { var path = stack.Fixed(u8, 4096){}; const isOk = errors.isOk; // Cannot guarantee that the sum of potential bytes written will always be // less than path max. if (isOk(stack.FinitePushError, path.pushAll(pref_path)) and open_readable.uri.writePath(path.asWriter())) { open_readable.file = c.SDL_RWFromFile(&path.buffer, "r"); } } }, } resume request.frame; } c.SDL_Delay(1); } }