/* nanogui/nanogui.cpp -- Basic initialization and utility routines NanoGUI was developed by Wenzel Jakob . The widget drawing code is based on the NanoVG demo application by Mikko Mononen. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE.txt file. */ #include #if defined(_WIN32) # include #endif #include #include #include #include #include #if !defined(_WIN32) # include # include # include #endif NAMESPACE_BEGIN(nanogui) extern std::map __nanogui_screens; #if defined(__APPLE__) extern void disable_saved_application_state_osx(); #endif void init() { #if !defined(_WIN32) /* Avoid locale-related number parsing issues */ setlocale(LC_NUMERIC, "C"); #endif #if defined(__APPLE__) disable_saved_application_state_osx(); #endif glfwSetErrorCallback( [](int error, const char *descr) { if (error == GLFW_NOT_INITIALIZED) return; /* Ignore */ std::cerr << "GLFW error " << error << ": " << descr << std::endl; } ); if (!glfwInit()) throw std::runtime_error("Could not initialize GLFW!"); glfwSetTime(0); } static bool mainloop_active = false; void mainloop(int refresh) { if (mainloop_active) throw std::runtime_error("Main loop is already running!"); mainloop_active = true; std::thread refresh_thread; if (refresh > 0) { /* If there are no mouse/keyboard events, try to refresh the view roughly every 50 ms (default); this is to support animations such as progress bars while keeping the system load reasonably low */ refresh_thread = std::thread( [refresh]() { std::chrono::milliseconds time(refresh); while (mainloop_active) { std::this_thread::sleep_for(time); glfwPostEmptyEvent(); } } ); } try { while (mainloop_active) { int numScreens = 0; for (auto kv : __nanogui_screens) { Screen *screen = kv.second; if (!screen->visible()) { continue; } else if (glfwWindowShouldClose(screen->glfwWindow())) { screen->setVisible(false); continue; } screen->drawAll(); numScreens++; } if (numScreens == 0) { /* Give up if there was nothing to draw */ mainloop_active = false; break; } /* Wait for mouse/keyboard or empty refresh events */ glfwWaitEvents(); } /* Process events once more */ glfwPollEvents(); } catch (const std::exception &e) { std::cerr << "Caught exception in main loop: " << e.what() << std::endl; leave(); } if (refresh > 0) refresh_thread.join(); } void leave() { mainloop_active = false; } bool active() { return mainloop_active; } void shutdown() { glfwTerminate(); } std::array utf8(int c) { std::array seq; int n = 0; if (c < 0x80) n = 1; else if (c < 0x800) n = 2; else if (c < 0x10000) n = 3; else if (c < 0x200000) n = 4; else if (c < 0x4000000) n = 5; else if (c <= 0x7fffffff) n = 6; seq[n] = '\0'; switch (n) { case 6: seq[5] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x4000000; case 5: seq[4] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x200000; case 4: seq[3] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x10000; case 3: seq[2] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x800; case 2: seq[1] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0xc0; case 1: seq[0] = c; } return seq; } int __nanogui_get_image(NVGcontext *ctx, const std::string &name, uint8_t *data, uint32_t size) { static std::map iconCache; auto it = iconCache.find(name); if (it != iconCache.end()) return it->second; int iconID = nvgCreateImageMem(ctx, 0, data, size); if (iconID == 0) throw std::runtime_error("Unable to load resource data."); iconCache[name] = iconID; return iconID; } std::vector> loadImageDirectory(NVGcontext *ctx, const std::string &path) { std::vector > result; #if !defined(_WIN32) DIR *dp = opendir(path.c_str()); if (!dp) throw std::runtime_error("Could not open image directory!"); struct dirent *ep; while ((ep = readdir(dp))) { const char *fname = ep->d_name; #else WIN32_FIND_DATA ffd; std::string searchPath = path + "/*.*"; HANDLE handle = FindFirstFileA(searchPath.c_str(), &ffd); if (handle == INVALID_HANDLE_VALUE) throw std::runtime_error("Could not open image directory!"); do { const char *fname = ffd.cFileName; #endif if (strstr(fname, "png") == nullptr) continue; std::string fullName = path + "/" + std::string(fname); int img = nvgCreateImage(ctx, fullName.c_str(), 0); if (img == 0) throw std::runtime_error("Could not open image data!"); result.push_back( std::make_pair(img, fullName.substr(0, fullName.length() - 4))); #if !defined(_WIN32) } closedir(dp); #else } while (FindNextFileA(handle, &ffd) != 0); FindClose(handle); #endif return result; } std::string file_dialog(const std::vector> &filetypes, bool save) { auto result = file_dialog(filetypes, save, false); return result.empty() ? "" : result.front(); } #if !defined(__APPLE__) std::vector file_dialog(const std::vector> &filetypes, bool save, bool multiple) { static const int FILE_DIALOG_MAX_BUFFER = 16384; if (save && multiple) { throw std::invalid_argument("save and multiple must not both be true."); } #if defined(_WIN32) OPENFILENAME ofn; ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); char tmp[FILE_DIALOG_MAX_BUFFER]; ofn.lpstrFile = tmp; ZeroMemory(tmp, FILE_DIALOG_MAX_BUFFER); ofn.nMaxFile = FILE_DIALOG_MAX_BUFFER; ofn.nFilterIndex = 1; std::string filter; if (!save && filetypes.size() > 1) { filter.append("Supported file types ("); for (size_t i = 0; i < filetypes.size(); ++i) { filter.append("*."); filter.append(filetypes[i].first); if (i + 1 < filetypes.size()) filter.append(";"); } filter.append(")"); filter.push_back('\0'); for (size_t i = 0; i < filetypes.size(); ++i) { filter.append("*."); filter.append(filetypes[i].first); if (i + 1 < filetypes.size()) filter.append(";"); } filter.push_back('\0'); } for (auto pair : filetypes) { filter.append(pair.second); filter.append(" (*."); filter.append(pair.first); filter.append(")"); filter.push_back('\0'); filter.append("*."); filter.append(pair.first); filter.push_back('\0'); } filter.push_back('\0'); ofn.lpstrFilter = filter.data(); if (save) { ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; if (GetSaveFileNameA(&ofn) == FALSE) return {}; } else { ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if (multiple) ofn.Flags |= OFN_ALLOWMULTISELECT; if (GetOpenFileNameA(&ofn) == FALSE) return {}; } size_t i = 0; std::vector result; while (tmp[i] != '\0') { result.emplace_back(&tmp[i]); i += result.back().size() + 1; } if (result.size() > 1) { for (i = 1; i < result.size(); ++i) { result[i] = result[0] + "\\" + result[i]; } result.erase(begin(result)); } return result; #else char buffer[FILE_DIALOG_MAX_BUFFER]; buffer[0] = '\0'; std::string cmd = "zenity --file-selection "; // The safest separator for multiple selected paths is /, since / can never occur // in file names. Only where two paths are concatenated will there be two / following // each other. if (multiple) cmd += "--multiple --separator=\"/\" "; if (save) cmd += "--save "; cmd += "--file-filter=\""; for (auto pair : filetypes) cmd += "\"*." + pair.first + "\" "; cmd += "\""; FILE *output = popen(cmd.c_str(), "r"); if (output == nullptr) throw std::runtime_error("popen() failed -- could not launch zenity!"); while (fgets(buffer, FILE_DIALOG_MAX_BUFFER, output) != NULL) ; pclose(output); std::string paths(buffer); paths.erase(std::remove(paths.begin(), paths.end(), '\n'), paths.end()); std::vector result; while (!paths.empty()) { size_t end = paths.find("//"); if (end == std::string::npos) { result.emplace_back(paths); paths = ""; } else { result.emplace_back(paths.substr(0, end)); paths = paths.substr(end + 1); } } return result; #endif } #endif void Object::decRef(bool dealloc) const noexcept { --m_refCount; if (m_refCount == 0 && dealloc) { delete this; } else if (m_refCount < 0) { fprintf(stderr, "Internal error: Object reference count < 0!\n"); abort(); } } Object::~Object() { } NAMESPACE_END(nanogui)