259 lines
7.5 KiB
C++
259 lines
7.5 KiB
C++
/*
|
|
nanogui/nanogui.cpp -- Basic initialization and utility routines
|
|
|
|
NanoGUI was developed by Wenzel Jakob <wenzel@inf.ethz.ch>.
|
|
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 <nanogui/screen.h>
|
|
#if defined(_WIN32)
|
|
#include <windows.h>
|
|
#endif
|
|
#include <nanogui/opengl.h>
|
|
#include <map>
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <iostream>
|
|
|
|
#if !defined(_WIN32)
|
|
#include <locale.h>
|
|
#include <signal.h>
|
|
#include <sys/dir.h>
|
|
#endif
|
|
|
|
NAMESPACE_BEGIN(nanogui)
|
|
|
|
static bool __mainloop_active = false;
|
|
extern std::map<GLFWwindow *, Screen *> __nanogui_screens;
|
|
|
|
void init() {
|
|
#if !defined(_WIN32)
|
|
/* Avoid locale-related number parsing issues */
|
|
setlocale(LC_NUMERIC, "C");
|
|
#endif
|
|
|
|
glfwSetErrorCallback(
|
|
[](int error, const char *desc) {
|
|
std::cerr << "GLFW error " << error << ": " << desc << std::endl;
|
|
}
|
|
);
|
|
|
|
if (!glfwInit())
|
|
throw std::runtime_error("Could not initialize GLFW!");
|
|
|
|
glfwSetTime(0);
|
|
}
|
|
|
|
void mainloop() {
|
|
__mainloop_active = true;
|
|
|
|
/* If there are no mouse/keyboard events, try to refresh the
|
|
view roughly every 50 ms; this is to support animations
|
|
such as progress bars while keeping the system load
|
|
reasonably low */
|
|
/*std::thread refresh_thread = std::thread(
|
|
[&]() {
|
|
std::chrono::milliseconds time(50);
|
|
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();
|
|
glfwPollEvents();
|
|
}
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "Caught exception in main loop: " << e.what() << std::endl;
|
|
abort();
|
|
}
|
|
|
|
//refresh_thread.join();
|
|
}
|
|
|
|
void leave() {
|
|
__mainloop_active = false;
|
|
}
|
|
|
|
void shutdown() {
|
|
glfwTerminate();
|
|
}
|
|
|
|
std::array<char, 8> utf8(int c) {
|
|
std::array<char, 8> 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<std::string, int> 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<std::pair<int, std::string>>
|
|
loadImageDirectory(NVGcontext *ctx, const std::string &path) {
|
|
std::vector<std::pair<int, std::string> > 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;
|
|
}
|
|
|
|
#if !defined(__APPLE__)
|
|
std::string file_dialog(const std::vector<std::pair<std::string, std::string>> &filetypes, bool save) {
|
|
#define FILE_DIALOG_MAX_BUFFER 1024
|
|
#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 (GetOpenFileNameA(&ofn) == FALSE)
|
|
return "";
|
|
}
|
|
return std::string(ofn.lpstrFile);
|
|
#else
|
|
char buffer[FILE_DIALOG_MAX_BUFFER];
|
|
std::string cmd = "/usr/bin/zenity --file-selection ";
|
|
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 result(buffer);
|
|
result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
|
|
return result;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
NAMESPACE_END(nanogui)
|
|
|