const std = @import("std"); const c = @cImport({ @cInclude("SDL.h"); @cInclude("SDL_ttf.h"); }); const m = @cImport({ @cInclude("magic.h"); }); const assert = @import("std").debug.assert; const DrawnText = struct { surface: [*c]c.SDL_Surface, texture: ?*c.SDL_Texture, rect: c.SDL_Rect, }; const FileDocMap = struct { title_text: DrawnText, contents: []u8, }; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer { if (gpa.deinit()) { std.debug.print("Allocator leak\n", .{}); } } var magic = m.magic_open(m.MAGIC_MIME | m.MAGIC_CHECK); const load_result = m.magic_load(magic, null); if (load_result != 0) { std.debug.print("Unable to initialize magic: {d}", .{load_result}); return error.MagicInitializationFailed; } const allocator = gpa.allocator(); const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); const window_width = 700; const window_height = 500; const column_width = 100; if (c.SDL_Init(c.SDL_INIT_VIDEO) != 0) { c.SDL_Log("Unable to initialize SDL: %s", c.SDL_GetError()); return error.SDLInitializationFailed; } defer c.SDL_Quit(); const screen = c.SDL_CreateWindow("Document Map Viewer", c.SDL_WINDOWPOS_UNDEFINED, c.SDL_WINDOWPOS_UNDEFINED, window_width, window_height, c.SDL_WINDOW_OPENGL) orelse { c.SDL_Log("Unable to create window: %s", c.SDL_GetError()); return error.SDLInitializationFailed; }; defer c.SDL_DestroyWindow(screen); const renderer = c.SDL_CreateRenderer(screen, -1, 0) orelse { c.SDL_Log("Unable to create renderer: %s", c.SDL_GetError()); return error.SDLInitializationFailed; }; defer c.SDL_DestroyRenderer(renderer); // Turn on anti-aliasing // :( doesn't seem to work const aa_result = c.SDL_SetHint(c.SDL_HINT_RENDER_SCALE_QUALITY, "1"); if (aa_result != c.SDL_TRUE) { c.SDL_Log("Unable to set anti-aliasing: %d", c.SDL_GetError()); return error.SDLInitializationFailed; } // https://github.com/cirosantilli/cpp-cheat/blob/e52ed4b838e2697d8f44ab5bef3e7a170705d48e/sdl/ttf.c const ttf_init_result: c_int = c.TTF_Init(); if (ttf_init_result != 0) { c.SDL_Log("Unable to init TTF: %d", ttf_init_result); return error.TTFInitializationFailed; } defer c.TTF_Quit(); const text_color: c.SDL_Color = c.SDL_Color{ .r = 0, .g = 0, .b = 0, .a = 255 }; // TODO: can we embed this file? const font: ?*c.TTF_Font = c.TTF_OpenFont("FreeSans.ttf", 12); if (font == null) { const err = c.TTF_GetError(); c.SDL_Log("Unable to load font: %s", err); return error.TTFFontLoadFailed; } var file_paths = std.ArrayList([]const u8).init(allocator); defer file_paths.deinit(); defer { for (file_paths.items) |item| { allocator.free(item); } } var args_i: usize = 1; while (args_i < args.len) : (args_i += 1) { const arg = args[args_i]; if (std.fs.cwd().openIterableDir(arg, .{})) |iter_dir| { std.debug.print("searching dir for files: '{s}'\n", .{arg}); var iter = iter_dir.iterate(); while (try iter.next()) |entry| { if (entry.kind == .File) { const entry_path = try std.fmt.allocPrint(allocator, "{s}{c}{s}\x00", .{ arg, std.fs.path.sep, entry.name, }); const magic_result = m.magic_file(magic, @ptrCast([*c]const u8, entry_path)); std.debug.print("'{s}': {s}\n", .{ entry.name, magic_result }); if (std.mem.eql(u8, magic_result[0..5], "text/")) { try file_paths.append(entry_path[0..(entry_path.len - 1)]); } else { allocator.free(entry_path); } } } } else |_| { if (std.fs.cwd().openFile(arg, .{})) |file| { file.close(); std.debug.print("Will do docmap for: '{s}'\n", .{arg}); var path = try allocator.alloc(u8, arg.len); std.mem.copy(u8, path, arg); try file_paths.append(path); } else |_| { std.debug.print("Arg not file: '{s}'\n", .{arg}); } } } var file_docmaps = std.ArrayList(FileDocMap).init(allocator); defer file_docmaps.deinit(); for (file_paths.items) |path| { var file_name: []const u8 = ""; var it = std.mem.tokenize(u8, path, std.fs.path.sep_str); while (it.next()) |token| { file_name = token; } const surface: [*c]c.SDL_Surface = c.TTF_RenderText_Solid(font, @ptrCast([*c]const u8, file_name), text_color); if (surface == null) { c.SDL_Log("Unable to render text"); return error.RenderTextFailed; } const texture: ?*c.SDL_Texture = c.SDL_CreateTextureFromSurface(renderer, surface); if (texture == null) { c.SDL_Log("Unable to create texture from surface"); return error.CreateTextureFailed; } var rect = c.SDL_Rect{ .x = 0, .y = 0, .w = surface.*.w, .h = surface.*.h, }; std.debug.print("open file: {s}\n", .{path}); const file = try std.fs.cwd().openFile(path, .{}); defer file.close(); const stat = try file.stat(); var buffer = try allocator.alloc(u8, stat.size); const bytes_read = try file.readAll(buffer); if (bytes_read != stat.size) { std.debug.print("bytes_read: {any}, stat.size: {any}\n", .{ bytes_read, stat.size, }); return error.FileReadError; } var docmap = FileDocMap{ .title_text = DrawnText{ .surface = surface, .texture = texture, .rect = rect, }, .contents = buffer, }; try file_docmaps.append(docmap); } defer { for (file_docmaps.items) |docmap| { c.SDL_FreeSurface(docmap.title_text.surface); c.SDL_DestroyTexture(docmap.title_text.texture); allocator.free(docmap.contents); } } const color_result: c_int = c.SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); if (color_result != 0) { c.SDL_Log("Unable to set draw color: %d", color_result); return error.SDLInitializationFailed; } // const surface = c.SDL_GetWindowSurface(screen); var quit = false; while (!quit) { var event: c.SDL_Event = undefined; while (c.SDL_PollEvent(&event) != 0) { switch (event.@"type") { c.SDL_QUIT => { quit = true; }, else => {}, } } const initial_color_result = c.SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); if (initial_color_result != 0) { c.SDL_Log("Unable to set draw color: %d", initial_color_result); return error.SDLInitializationFailed; } // Draw white background var x: c_int = 0; while (x < window_width) : (x += 1) { var y: c_int = 0; while (y < window_height) : (y += 1) { const draw_result = c.SDL_RenderDrawPoint(renderer, x, y); if (draw_result != 0) { c.SDL_Log("Unable to draw point: %d", draw_result); return error.SDLDrawingFailed; } } } var i: usize = 0; var column: c_int = 0; x = 0; var y: c_int = 0; for (file_docmaps.items) |docmap| { i = 0; var rect = c.SDL_Rect{ .x = x, .y = y, .w = docmap.title_text.surface.*.w, .h = docmap.title_text.surface.*.h, }; // Display text if (c.SDL_RenderCopy(renderer, docmap.title_text.texture, null, &rect) != 0) { c.SDL_Log("Unable to render copy"); } y += docmap.title_text.rect.h; // white: false, black: true var color: bool = false; while (i < docmap.contents.len) : (i += 1) { if (docmap.contents[i] == 10) { y += 1; if (y > window_height) { y = 0; column += 1; // std.debug.print("Column: {any}, x: {any}\n", .{column, column*column_width}); } x = column * column_width; continue; } if (docmap.contents[i] == 32 and color) { color = false; const white_color_result = c.SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); if (white_color_result != 0) { c.SDL_Log("Unable to set draw color: %d", white_color_result); return error.SDLInitializationFailed; } } else if (docmap.contents[i] != 32 and !color) { color = true; const black_color_result = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); if (black_color_result != 0) { c.SDL_Log("Unable to set draw color: %d", black_color_result); return error.SDLInitializationFailed; } } if (x < (column + 1) * column_width) { const draw_result = c.SDL_RenderDrawPoint(renderer, x, y); if (draw_result != 0) { c.SDL_Log("Unable to draw point: %d", draw_result); return error.SDLDrawingFailed; } } else { const red_color_result = c.SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); if (red_color_result != 0) { c.SDL_Log("Unable to set draw color: %d", red_color_result); return error.SDLInitializationFailed; } const draw_result = c.SDL_RenderDrawPoint(renderer, (column + 1) * column_width - 1, y); if (draw_result != 0) { c.SDL_Log("Unable to draw point: %d", draw_result); return error.SDLDrawingFailed; } if (!color) { const white_color_result = c.SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); if (white_color_result != 0) { c.SDL_Log("Unable to set draw color: %d", white_color_result); return error.SDLInitializationFailed; } } else { const black_color_result = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); if (black_color_result != 0) { c.SDL_Log("Unable to set draw color: %d", black_color_result); return error.SDLInitializationFailed; } } } x += 1; } } c.SDL_RenderPresent(renderer); c.SDL_Delay(17); } }