internal b8 init_imgui(GLFWwindow* window) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void) io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.IniFilename = nullptr; io.LogFilename = nullptr; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 330"); return true; } internal void glfw_error_callback(i32 error, const char *description) { fprintf(stderr, "GLFW error %d: %s\n", error, description); } internal void glfw_key_callback(GLFWwindow *window, i32 key, i32 /*scancode*/, i32 action, i32 /*mods*/) { App_State *app = (App_State *)glfwGetWindowUserPointer(window); Input_Key ikey = app->viewer.glfw_key_mapping[key]; if (ikey == KEY_INVALID) return; u8 &state = app->user_input.key_state[ikey]; switch (action) { case GLFW_PRESS: if (!(state & KEY_STATE_IS_DOWN)) state |= KEY_STATE_JUST_PRESSED; state |= KEY_STATE_IS_DOWN; break; case GLFW_RELEASE: if (state & KEY_STATE_IS_DOWN) state |= KEY_STATE_JUST_RELEASED; state &= ~KEY_STATE_IS_DOWN; break; case GLFW_REPEAT: state |= KEY_STATE_JUST_PRESSED; break; default:; } } internal void glfw_init_key_mapping(Viewer &viewer) { viewer.glfw_key_mapping[GLFW_KEY_Q] = KEY_Q; viewer.glfw_key_mapping[GLFW_KEY_UP] = KEY_UP; viewer.glfw_key_mapping[GLFW_KEY_DOWN] = KEY_DOWN; viewer.glfw_key_mapping[GLFW_KEY_LEFT] = KEY_LEFT; viewer.glfw_key_mapping[GLFW_KEY_RIGHT] = KEY_RIGHT; viewer.glfw_key_mapping[GLFW_KEY_LEFT_ALT] = KEY_ALT; viewer.glfw_key_mapping[GLFW_KEY_TAB] = KEY_TAB; viewer.glfw_key_mapping[GLFW_KEY_LEFT_SHIFT] = KEY_SHIFT; viewer.glfw_key_mapping[GLFW_KEY_KP_ADD] = KEY_PLUS; viewer.glfw_key_mapping[GLFW_KEY_KP_SUBTRACT] = KEY_MINUS; viewer.glfw_key_mapping[GLFW_KEY_ESCAPE] = KEY_ESC; } internal GLFWwindow *init_glfw(App_State &app, i32 desired_win_width, i32 desired_win_height) { glfw_init_key_mapping(app.viewer); glfwSetErrorCallback(glfw_error_callback); glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); glfwWindowHint(GLFW_DEPTH_BITS, 32); #ifndef NDEBUG glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); #endif GLFWwindow *window = glfwCreateWindow( desired_win_width, desired_win_height, "RNTuple Viewer " V_MAJOR "." V_MINOR, nullptr, nullptr ); glfwSetWindowUserPointer(window, &app); glfwSetKeyCallback(window, glfw_key_callback); glfwMakeContextCurrent(window); return window; } internal void monitor_mouse_btn(GLFWwindow *window, u8 *mouse_btn_state, i32 glfw_btn, Mouse_Button btn) { u8 *state = &mouse_btn_state[btn]; if (glfwGetMouseButton(window, glfw_btn) == GLFW_PRESS) { if (!(*state & MOUSE_BTN_STATE_IS_DOWN)) *state |= MOUSE_BTN_STATE_JUST_PRESSED; *state |= MOUSE_BTN_STATE_IS_DOWN; } else if (glfwGetMouseButton(window, glfw_btn) == GLFW_RELEASE) { if (*state & MOUSE_BTN_STATE_IS_DOWN) *state |= MOUSE_BTN_STATE_JUST_RELEASED; *state &= ~MOUSE_BTN_STATE_IS_DOWN; } } internal void reset_user_input(User_Input &input) { for (u32 i = 0; i < KEY_COUNT; ++i) input.key_state[i] &= ~(KEY_STATE_JUST_PRESSED|KEY_STATE_JUST_RELEASED); for (u32 i = 0; i < MOUSE_BTN_COUNT; ++i) input.mouse_btn_state[i] &= ~(MOUSE_BTN_STATE_JUST_PRESSED|MOUSE_BTN_STATE_JUST_RELEASED); } internal void run_main_loop(GLFWwindow *window, Arena *arena, App_State &app) { f32 delta_time_ms; chr::time_point last_saved_time = chr::high_resolution_clock::now(); app.delta_time_accum.max = 100; app.delta_time_accum.base = arena_push_array<f32>(arena, app.delta_time_accum.max); while (!app.should_quit) { chr::time_point frame_start = chr::high_resolution_clock::now(); u64 time_since_prev_frame_us = chr::duration_cast<chr::microseconds>(frame_start - last_saved_time).count(); delta_time_ms = time_since_prev_frame_us * 0.001f; last_saved_time = frame_start; reset_user_input(app.user_input); glfwPollEvents(); // Update window size { Window_Data &wdata = app.win_data; i32 prev_width = wdata.width; i32 prev_height = wdata.height; glfwGetWindowSize(window, &wdata.width, &wdata.height); wdata.size_just_changed = prev_width != wdata.width || prev_height != wdata.height; } ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); // Check if the inspected file changed { // TODO: this should re-run get_tfile_data and get_rntuple_data! char buf[sizeof(inotify_event) + NAME_MAX + 1]; ssize_t nbytes = read(app.inspected_file.inot, buf, sizeof(buf)); if (nbytes) app.inspected_file.size = file_size(app.inspected_file.stream); } if ((app.user_input.key_state[KEY_ESC] & KEY_STATE_IS_DOWN) || glfwWindowShouldClose(window)) { app.should_quit = true; break; } b32 focused = glfwGetWindowAttrib(window, GLFW_FOCUSED); if (focused) { // NOTE: keyboard is updated via the callback because that's the only way to intercept GLFW_REPEAT events // Update mouse f64 mposx, mposy; glfwGetCursorPos(window, &mposx, &mposy); u8 *mouse_btn_state = app.user_input.mouse_btn_state; monitor_mouse_btn(window, mouse_btn_state, GLFW_MOUSE_BUTTON_LEFT, MOUSE_BTN_LEFT); monitor_mouse_btn(window, mouse_btn_state, GLFW_MOUSE_BUTTON_RIGHT, MOUSE_BTN_RIGHT); app.user_input.mouse_pos.x = static_cast<i32>(mposx); app.user_input.mouse_pos.y = static_cast<i32>(mposy); } update_and_render(arena, app, delta_time_ms); // ImGui::ShowDemoWindow(); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } }