// ************************************ // * // * rntviewer // * // * A graphical RNTuple visualizer // * // * @author silverweed, 2024 // * // *********************************** // #include // #include // #include #include #include #include #include #ifdef DEBUG #include #endif #include #include #include #include #define GLFW_INCLUDE_NONE #include #include "root/root_inc.h" #include "root/RMicroFileReader.hxx" #include "types.h" #include "defer.hpp" #include "prof.hpp" #include "mem.h" #include "str.h" #include "rntuple.h" #include "window.h" #include "render.h" #include "app_state.h" // @Platform #include "platform_linux.h" namespace chr = std::chrono; #include "mem.cpp" #include "str.cpp" #include "rntuple.cpp" #include "render.cpp" #include "mainloop.cpp" internal b8 init_imgui(GLFWwindow* window) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void) io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 400"); return true; } internal void app_cleanup(App_State &app) { os_stop_file_watch(app); os_unmap_file(app.inspected_file.mem, app.inspected_file.size); if (app.inspected_file.stream) fclose(app.inspected_file.stream); } internal void print_help(const char *argv0) { fprintf(stderr, "Usage: %s [-t] [-s START] [-l LEN] [-w WIDTH] " "\n" "\n\t-t: no graphics, output to terminal" "\n\t-s: set first displayed byte to START" "\n\t-l: display LEN bytes (only in terminal mode)" "\n\t-w: display WIDTH bytes per column" "\n" , argv0); } struct Cmdline_Args { b8 print_to_terminal; b8 show_help_and_exit; u64 start_addr; u64 nbytes_displayed; u16 n_cols; String8 ntpl_name; String8 file_name; }; struct Conv_Res { u64 num; b8 error; }; internal Conv_Res str_to_u64(String8 s) { Conv_Res res {}; for (u64 i = 0; i < s.size; ++i) { u8 c = s.str[i]; if (c >= '0' && c <= '9') { res.num *= 10; res.num += c - '0'; } else { res.error = true; break; } } return res; } internal void parse_int_arg(i32 &cur_arg_idx, i32 argc, char **argv, u64 &out) { const char *arg = argv[cur_arg_idx]; if (cur_arg_idx < argc - 1) { String8 nxt_arg = str8_from_c(argv[++cur_arg_idx]); Conv_Res res = str_to_u64(nxt_arg); if (res.error) fprintf(stderr, "Invalid integer after %s flag.\n", arg); else out = res.num; } else { fprintf(stderr, "Argument required after %s flag.\n", arg); } } internal Cmdline_Args parse_args(i32 argc, char **argv) { Cmdline_Args args {}; if (argc < 3) { args.show_help_and_exit = true; return args; } // TODO: refactor this probably for (i32 i = 1; i < argc; ++i) { String8 arg = str8_from_c(argv[i]); if (arg.str[0] == '-') { if (arg.size == 2) { if (arg.str[1] == 't') args.print_to_terminal = true; else if (arg.str[1] == 's') parse_int_arg(i, argc, argv, args.start_addr); else if (arg.str[1] == 'l') parse_int_arg(i, argc, argv, args.nbytes_displayed); else if (arg.str[1] == 'w') { u64 n_cols = 0; parse_int_arg(i, argc, argv, n_cols); args.n_cols = (u16)n_cols; } else args.show_help_and_exit = true; } else { args.show_help_and_exit = true; } } else if (args.ntpl_name.size) { if (args.file_name.size) args.show_help_and_exit = true; else args.file_name = arg; } else { args.ntpl_name = arg; } } return args; } int main(int argc, char **argv) { Thread_Ctx tctx; tctx_init(tctx); defer { tctx_release(); }; // Parse cmdline Cmdline_Args args = parse_args(argc, argv); if (args.show_help_and_exit || !args.ntpl_name.size || !args.file_name.size) { print_help(argv[0]); return 1; } // Allocate program memory Arena *arena = arena_alloc(); if (!arena) { fprintf(stderr, "Failed to allocate memory\n"); return 1; } defer { arena_release(arena); }; // Init imgui and GLFW GLFWwindow *window = init_glfw(800, 600); if (!window) { fprintf(stderr, "Failed to init GLFW\n"); return 1; } defer { glfwTerminate(); }; if (!init_imgui(window)) { fprintf(stderr, "Failed to init Imgui\n"); return 1; } App_State app {}; defer { app_cleanup(app); }; // Open and map the file if (args.file_name.size) { if (!os_open_and_map_file(args.file_name, app)) return 1; // Watch file for changes (to adapt the displayed file size - otherwise // we may try to access invalid memory when the file gets shrunk) os_start_file_watch(args.file_name, app); } app.ntpl_name = args.ntpl_name; app.tfile_data = get_tfile_data(app.inspected_file); app.rndata = get_rntuple_data(arena, app.inspected_file, app.ntpl_name); make_viewer(app, args.n_cols); app.viewer.base_display_addr = args.start_addr; if (args.print_to_terminal) { u64 nbytes_displayed = args.nbytes_displayed; if (!nbytes_displayed) nbytes_displayed = 1000; String8 rendered = render_range_to_string(arena, app, nbytes_displayed, args.n_cols); printf("%s\n", rendered.c()); return 0; } // Start main loop run_main_loop(window, arena, app); return 0; }