#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include std::string g_base_address; std::string g_text; ImFont * g_font_regular; ImFont * g_font_bold; ImFont * g_font_bold_large; struct Image { ImTextureID m_texture_id; int m_width; int m_height; }; std::unordered_map g_images; static std::string get(const std::string & command) { std::string result; std::array buffer; std::unique_ptr opener(popen(command.c_str(), "r"), pclose); if (!opener) return std::string(); // TODO: time to do some error handling while (nullptr != fgets(buffer.data(), buffer.size(), opener.get())) { result += buffer.data(); } return result; } static std::string expand_url(const std::string & href) { std::string_view prefix(href.data(), 4); if (prefix == "http") return href; else { prefix.remove_suffix(2); if (prefix == "//") return "https:" + href; else { prefix.remove_suffix(1); if (prefix == "/") { if (g_base_address.empty()) throw std::runtime_error("missing base address"); else return g_base_address + href; } else throw std::runtime_error("invalid href"); } } } static bool load_image(const std::string & filename, Image & image) { unsigned char * data = stbi_load(filename.c_str(), &image.m_width, &image.m_height, nullptr, 4); if (nullptr == data) return false; GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #if defined(GL_UNPACK_ROW_LENGTH) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); #endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.m_width, image.m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); stbi_image_free(data); image.m_texture_id = reinterpret_cast(texture); return true; } struct Markdown : public imgui_md { ImFont * get_font() const override { if (m_is_table_header) { return g_font_bold; } switch (m_hlevel) { case 0: return m_is_strong ? g_font_bold : g_font_regular; case 1: return g_font_bold_large; default: return g_font_bold; } } void open_url() const override { std::string command = "browse -f ./markdown " + expand_url(m_href); std::packaged_task task(get); auto future = task.get_future(); std::thread t(std::move(task), command); t.detach(); const auto result = future.get(); const auto n = result.find(" "); const auto filename = result.substr(n + 1, result.size() - n - 2); std::ifstream in; in.open(filename); g_text = std::string(std::istreambuf_iterator(in), {}); in.close(); } bool get_image(image_info & nfo) const override { auto search = g_images.find(m_href); if (g_images.end() == search) { std::string command = "phttp " + expand_url(m_href); std::packaged_task task(get); auto future = task.get_future(); std::thread t(std::move(task), command); t.detach(); const auto result = future.get(); auto n = result.find(" "); n = result.find(" ", n + 1); const auto filename = result.substr(n + 1, result.size() - n - 2); if (!load_image(filename, g_images[m_href])) throw std::runtime_error("could not load image"); } nfo.texture_id = g_images[m_href].m_texture_id; nfo.size = {g_images[m_href].m_width, g_images[m_href].m_height}; nfo.uv0 = {0, 0}; nfo.uv1 = {1, 1}; nfo.col_tint = {1, 1, 1, 1}; nfo.col_border = {0, 0, 0, 0}; return true; } }; static void glfw_error_callback(int error, const char * description) { std::cerr << "glfw (" << error << "): " << description << std::endl; } static void markdown(const char * str, const char * str_end) { static Markdown s_printer; s_printer.print(str, str_end); } ImVec2 g_window_size; static void resized(GLFWwindow *, int width, int height) { g_window_size.x = static_cast(width); g_window_size.y = static_cast(height); } static void print_usage(const char * program) { std::cerr << "Usage: " << program << " [-b BASE_ADDRESS] FILE" << std::endl; } int main(int argc, char ** argv) { int opt; while (-1 != (opt = getopt(argc, argv, "b:"))) { switch (opt) { case 'b': g_base_address = optarg; break; case '?': default: print_usage(argv[0]); return 2; } } if (optind >= argc) { print_usage(argv[0]); return 2; } glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; const char * glsl_version = "#version 130"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); GLFWwindow * window = glfwCreateWindow(1280, 720, "Markdown", nullptr, nullptr); if (nullptr == window) return 1; glfwMakeContextCurrent(window); glfwSwapInterval(1); glfwSetWindowSizeCallback(window, resized); if (GLEW_OK != glewInit()) return 1; IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO & io = ImGui::GetIO(); io.IniFilename = nullptr; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; g_font_regular = io.Fonts->AddFontFromFileTTF("OpenSans-Regular.ttf", 18); g_font_bold = io.Fonts->AddFontFromFileTTF("OpenSans-Bold.ttf", 18); g_font_bold_large = io.Fonts->AddFontFromFileTTF("OpenSans-Bold.ttf", 26); ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); std::ifstream in; in.open(argv[optind]); g_text = std::string(std::istreambuf_iterator(in), {}); in.close(); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); ImGui::SetNextWindowPos({0, 0}); ImGui::SetNextWindowSize(g_window_size); ImGui::Begin( "Markdown", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings ); markdown(g_text.data(), g_text.data() + g_text.size()); ImGui::End(); ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); }