From 02172510b1c52901e1d84f4dc27596613352a3d9 Mon Sep 17 00:00:00 2001 From: TwentyPast4 Date: Sun, 18 Jun 2023 12:36:52 +0200 Subject: [PATCH 1/2] Added vsync support --- include/nanogui/common.h | 22 ++ include/nanogui/screen.h | 3 + src/common.cpp | 833 ++++++++++++++++++++------------------- src/screen.cpp | 13 +- 4 files changed, 473 insertions(+), 398 deletions(-) diff --git a/include/nanogui/common.h b/include/nanogui/common.h index 2f38fe46..aeed8efc 100644 --- a/include/nanogui/common.h +++ b/include/nanogui/common.h @@ -250,6 +250,28 @@ extern NANOGUI_EXPORT void leave(); /// Return whether or not a main loop is currently active extern NANOGUI_EXPORT bool active(); +/** + * \brief Function to enable or disable vertical synchronization. + * + * Changes how the main loop is called and when redraws occur. + * + * Solves screen tearing and provides fluid operation on all monitors, instead of just looking about-right on 60Hz displays. + * + * Results in higher system load compared to it being disabled due to the more frequent update and draw calls. + * + * Default disabled - set_vsync(false) is called in nanogui::init() + * @remark Some extensions used by GLFW do not allow the vsync mode to be disabled once it has been enabled. + * + * @remark Some GPU drivers do not honor this setting, either + * because of a user setting that overrides the application's request or due to + * bugs in the driver. + * \param enabled True to enable VSYNC, false to disable it. + */ +extern NANOGUI_EXPORT void set_vsync(bool enabled); + +/// Return true or false if vsync is enabled (set with set_vsync) +extern NANOGUI_EXPORT bool vsync_enabled(); + /** * \brief Enqueue a function to be executed executed before * the application is redrawn the next time. diff --git a/include/nanogui/screen.h b/include/nanogui/screen.h index d2e044f4..a1ceaebd 100644 --- a/include/nanogui/screen.h +++ b/include/nanogui/screen.h @@ -272,6 +272,9 @@ class NANOGUI_EXPORT Screen : public Widget { void move_window_to_front(Window *window); void draw_widgets(); + // Checks if the current widget under the mouse pointer has a different cursor than the one which is set (useful when dynamically changing widget cursors) + void update_cursor(); + protected: GLFWwindow *m_glfw_window = nullptr; NVGcontext *m_nvg_context = nullptr; diff --git a/src/common.cpp b/src/common.cpp index 77cef26b..3332f2ce 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1,12 +1,12 @@ /* - nanogui/nanogui.cpp -- Basic initialization and utility routines + 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. + 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. + 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 @@ -38,39 +38,40 @@ NAMESPACE_BEGIN(nanogui) -extern std::map __nanogui_screens; +extern std::map __nanogui_screens; #if defined(__APPLE__) - extern void disable_saved_application_state_osx(); +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(); - glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); - #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!"); +#if !defined(_WIN32) + /* Avoid locale-related number parsing issues */ + setlocale(LC_NUMERIC, "C"); +#endif + +#if defined(__APPLE__) + disable_saved_application_state_osx(); + glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); +#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!"); #if defined(NANOGUI_USE_METAL) - metal_init(); + metal_init(); #endif - glfwSetTime(0); + glfwSetTime(0); + set_vsync(false); } static bool mainloop_active = false; @@ -78,149 +79,180 @@ static bool mainloop_active = false; #if defined(EMSCRIPTEN) static double emscripten_last = 0; static float emscripten_refresh = 0; +#else +static bool vsync = false; #endif std::mutex m_async_mutex; std::vector> m_async_functions; +void set_vsync(bool enabled) +{ + vsync = enabled; +#if defined(NANOGUI_USE_OPENGL) || defined(NANOGUI_USE_GLES) + glfwSwapInterval(vsync ? 1 : 0); +#endif +} + +void mainloop_iteration() +{ + int num_screens = 0; + + if (vsync) + glfwPollEvents(); + +#if defined(EMSCRIPTEN) + double emscripten_now = glfwGetTime(); + bool emscripten_redraw = false; + if (float((emscripten_now - emscripten_last) * 1000) > emscripten_refresh) { + emscripten_redraw = true; + emscripten_last = emscripten_now; + } +#endif + + /* Run async functions */ { + std::lock_guard guard(m_async_mutex); + for (auto& f : m_async_functions) + f(); + m_async_functions.clear(); + } + + for (auto kv : __nanogui_screens) { + Screen* screen = kv.second; + if (!screen->visible()) { + continue; + } + else if (glfwWindowShouldClose(screen->glfw_window())) { + screen->set_visible(false); + continue; + } +#if defined(EMSCRIPTEN) + if (emscripten_redraw || screen->tooltip_fade_in_progress()) + screen->redraw(); +#else + if (vsync) + screen->redraw(); +#endif + screen->draw_all(); + num_screens++; + } + + if (num_screens == 0) { + /* Give up if there was nothing to draw */ + mainloop_active = false; + return; + } + +#if !defined(EMSCRIPTEN) + /* Wait for mouse/keyboard or empty refresh events */ + if (!vsync) + glfwWaitEvents(); +#endif +} + +bool vsync_enabled() +{ + return vsync; +} + +void novsync_refresh(const std::chrono::microseconds& interval, size_t quantum_count) +{ + while (true) { + for (size_t i = 0; i < quantum_count; ++i) { + if (!mainloop_active) + return; + std::this_thread::sleep_for(interval); + for (auto kv : __nanogui_screens) { + if (kv.second->tooltip_fade_in_progress()) + kv.second->redraw(); + } + } + for (auto kv : __nanogui_screens) + kv.second->redraw(); + } +} + void mainloop(float refresh) { - if (mainloop_active) - throw std::runtime_error("Main loop is already running!"); - - auto mainloop_iteration = []() { - int num_screens = 0; - - #if defined(EMSCRIPTEN) - double emscripten_now = glfwGetTime(); - bool emscripten_redraw = false; - if (float((emscripten_now - emscripten_last) * 1000) > emscripten_refresh) { - emscripten_redraw = true; - emscripten_last = emscripten_now; - } - #endif - - /* Run async functions */ { - std::lock_guard guard(m_async_mutex); - for (auto &f : m_async_functions) - f(); - m_async_functions.clear(); - } - - for (auto kv : __nanogui_screens) { - Screen *screen = kv.second; - if (!screen->visible()) { - continue; - } else if (glfwWindowShouldClose(screen->glfw_window())) { - screen->set_visible(false); - continue; - } - #if defined(EMSCRIPTEN) - if (emscripten_redraw || screen->tooltip_fade_in_progress()) - screen->redraw(); - #endif - screen->draw_all(); - num_screens++; - } - - if (num_screens == 0) { - /* Give up if there was nothing to draw */ - mainloop_active = false; - return; - } - - #if !defined(EMSCRIPTEN) - /* Wait for mouse/keyboard or empty refresh events */ - glfwWaitEvents(); - #endif - }; + if (mainloop_active) + throw std::runtime_error("Main loop is already running!"); #if defined(EMSCRIPTEN) - emscripten_refresh = refresh; - /* The following will throw an exception and enter the main - loop within Emscripten. This means that none of the code below - (or in the caller, for that matter) will be executed */ - emscripten_set_main_loop(mainloop_iteration, 0, 1); + emscripten_refresh = refresh; + /* The following will throw an exception and enter the main + loop within Emscripten. This means that none of the code below + (or in the caller, for that matter) will be executed */ + emscripten_set_main_loop(mainloop_iteration, 0, 1); #endif - mainloop_active = true; - - std::thread refresh_thread; - std::chrono::microseconds quantum; - size_t quantum_count = 1; - if (refresh >= 0) { - quantum = std::chrono::microseconds((int64_t)(refresh * 1'000)); - while (quantum.count() > 50'000) { - quantum /= 2; - quantum_count *= 2; - } - } else { - quantum = std::chrono::microseconds(50'000); - quantum_count = std::numeric_limits::max(); - } - - /* 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( - [quantum, quantum_count]() { - while (true) { - for (size_t i = 0; i < quantum_count; ++i) { - if (!mainloop_active) - return; - std::this_thread::sleep_for(quantum); - for (auto kv : __nanogui_screens) { - if (kv.second->tooltip_fade_in_progress()) - kv.second->redraw(); - } - } - for (auto kv : __nanogui_screens) - kv.second->redraw(); - } - } - ); - - try { - while (mainloop_active) - mainloop_iteration(); - - /* Process events once more */ - glfwPollEvents(); - } catch (const std::exception &e) { - std::cerr << "Caught exception in main loop: " << e.what() << std::endl; - leave(); - } - - refresh_thread.join(); + mainloop_active = true; + + std::thread refresh_thread; + if (!vsync) + { + std::chrono::microseconds quantum; + size_t quantum_count = 1; + if (refresh >= 0) { + quantum = std::chrono::microseconds((int64_t)(refresh * 1'000)); + while (quantum.count() > 50'000) { + quantum /= 2; + quantum_count *= 2; + } + } + else { + quantum = std::chrono::microseconds(50'000); + quantum_count = std::numeric_limits::max(); + } + + /* 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(&novsync_refresh, quantum, quantum_count); + } + + try { + while (mainloop_active) + mainloop_iteration(); + + /* Process events once more */ + glfwPollEvents(); + } + catch (const std::exception& e) { + std::cerr << "Caught exception in main loop: " << e.what() << std::endl; + leave(); + } + + if (refresh_thread.joinable()) + refresh_thread.join(); } -void async(const std::function &func) { - std::lock_guard guard(m_async_mutex); - m_async_functions.push_back(func); +void async(const std::function& func) { + std::lock_guard guard(m_async_mutex); + m_async_functions.push_back(func); } void leave() { - mainloop_active = false; + mainloop_active = false; } bool active() { - return mainloop_active; + return mainloop_active; } std::pair test_10bit_edr_support() { #if defined(NANOGUI_USE_METAL) - return metal_10bit_edr_support(); + return metal_10bit_edr_support(); #else - return { false, false }; + return { false, false }; #endif } void shutdown() { - glfwTerminate(); + glfwTerminate(); #if defined(NANOGUI_USE_METAL) - metal_shutdown(); + metal_shutdown(); #endif } @@ -233,297 +265,304 @@ void shutdown() { #endif std::string utf8(uint32_t c) { - char seq[8]; - 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; NANOGUI_FALLTHROUGH - case 5: seq[4] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x200000; NANOGUI_FALLTHROUGH - case 4: seq[3] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x10000; NANOGUI_FALLTHROUGH - case 3: seq[2] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x800; NANOGUI_FALLTHROUGH - case 2: seq[1] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0xc0; NANOGUI_FALLTHROUGH - case 1: seq[0] = c; - } - return std::string(seq, seq + n); + char seq[8]; + 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; NANOGUI_FALLTHROUGH + case 5: seq[4] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x200000; NANOGUI_FALLTHROUGH + case 4: seq[3] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x10000; NANOGUI_FALLTHROUGH + case 3: seq[2] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0x800; NANOGUI_FALLTHROUGH + case 2: seq[1] = 0x80 | (c & 0x3f); c = c >> 6; c |= 0xc0; NANOGUI_FALLTHROUGH + case 1: seq[0] = c; + } + return std::string(seq, seq + n); } -int __nanogui_get_image(NVGcontext *ctx, const std::string &name, uint8_t *data, uint32_t size) { - static std::map icon_cache; - auto it = icon_cache.find(name); - if (it != icon_cache.end()) - return it->second; - int icon_id = nvgCreateImageMem(ctx, 0, data, size); - if (icon_id == 0) - throw std::runtime_error("Unable to load resource data."); - icon_cache[name] = icon_id; - return icon_id; +int __nanogui_get_image(NVGcontext* ctx, const std::string& name, uint8_t* data, uint32_t size) { + static std::map icon_cache; + auto it = icon_cache.find(name); + if (it != icon_cache.end()) + return it->second; + int icon_id = nvgCreateImageMem(ctx, 0, data, size); + if (icon_id == 0) + throw std::runtime_error("Unable to load resource data."); + icon_cache[name] = icon_id; + return icon_id; } std::vector> -load_image_directory(NVGcontext *ctx, const std::string &path) { - std::vector > result; +load_image_directory(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; + 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 search_path = path + "/*.*"; - HANDLE handle = FindFirstFileA(search_path.c_str(), &ffd); - if (handle == INVALID_HANDLE_VALUE) - throw std::runtime_error("Could not open image directory!"); - do { - const char *fname = ffd.cFileName; + WIN32_FIND_DATA ffd; + std::string search_path = path + "/*.*"; + HANDLE handle = FindFirstFileA(search_path.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 full_name = path + "/" + std::string(fname); - int img = nvgCreateImage(ctx, full_name.c_str(), 0); - if (img == 0) - throw std::runtime_error("Could not open image data!"); - result.push_back( - std::make_pair(img, full_name.substr(0, full_name.length() - 4))); + if (strstr(fname, "png") == nullptr) + continue; + std::string full_name = path + "/" + std::string(fname); + int img = nvgCreateImage(ctx, full_name.c_str(), 0); + if (img == 0) + throw std::runtime_error("Could not open image data!"); + result.push_back( + std::make_pair(img, full_name.substr(0, full_name.length() - 4))); #if !defined(_WIN32) - } - closedir(dp); + } + closedir(dp); #else - } while (FindNextFileA(handle, &ffd) != 0); - FindClose(handle); + } while (FindNextFileA(handle, &ffd) != 0); + FindClose(handle); #endif - return result; + return result; } -std::string file_dialog(const std::vector> &filetypes, bool save) { - auto result = file_dialog(filetypes, save, false); - return result.empty() ? "" : result.front(); +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."); - } +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(EMSCRIPTEN) - throw std::runtime_error("Opening files is not supported when NanoGUI is compiled via Emscripten"); + throw std::runtime_error("Opening files is not supported when NanoGUI is compiled via Emscripten"); #elif 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)); - } - - if (save && ofn.nFilterIndex > 0) { - auto ext = filetypes[ofn.nFilterIndex - 1].first; - if (ext != "*") { - ext.insert(0, "."); - - auto &name = result.front(); - if (name.size() <= ext.size() || - name.compare(name.size() - ext.size(), ext.size(), ext) != 0) { - name.append(ext); - } - } - } - - return result; + 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)); + } + + if (save && ofn.nFilterIndex > 0) { + auto ext = filetypes[ofn.nFilterIndex - 1].first; + if (ext != "*") { + ext.insert(0, "."); + + auto& name = result.front(); + if (name.size() <= ext.size() || + name.compare(name.size() - ext.size(), ext.size(), ext) != 0) { + name.append(ext); + } + } + } + + 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; + 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 -static void (*object_inc_ref_py)(PyObject *) noexcept = nullptr; -static void (*object_dec_ref_py)(PyObject *) noexcept = nullptr; +static void (*object_inc_ref_py)(PyObject*) noexcept = nullptr; +static void (*object_dec_ref_py)(PyObject*) noexcept = nullptr; Object::~Object() { } void Object::inc_ref() const noexcept { - uintptr_t value = m_state.load(std::memory_order_relaxed); - - while (true) { - if (value & 1) { - if (!m_state.compare_exchange_weak(value, - value + 2, - std::memory_order_relaxed, - std::memory_order_relaxed)) - continue; - } else { - object_inc_ref_py((PyObject *) value); - } - - break; - } + uintptr_t value = m_state.load(std::memory_order_relaxed); + + while (true) { + if (value & 1) { + if (!m_state.compare_exchange_weak(value, + value + 2, + std::memory_order_relaxed, + std::memory_order_relaxed)) + continue; + } + else { + object_inc_ref_py((PyObject*)value); + } + + break; + } } void Object::dec_ref() const noexcept { - uintptr_t value = m_state.load(std::memory_order_relaxed); - - while (true) { - if (value & 1) { - if (value == 1) { - fprintf(stderr, - "Object::dec_ref(%p): reference count underflow!", - this); - abort(); - } else if (value == 3) { - delete this; - } else { - if (!m_state.compare_exchange_weak(value, - value - 2, - std::memory_order_relaxed, - std::memory_order_relaxed)) - continue; - } - } else { - object_dec_ref_py((PyObject *) value); - } - break; - } + uintptr_t value = m_state.load(std::memory_order_relaxed); + + while (true) { + if (value & 1) { + if (value == 1) { + fprintf(stderr, + "Object::dec_ref(%p): reference count underflow!", + this); + abort(); + } + else if (value == 3) { + delete this; + } + else { + if (!m_state.compare_exchange_weak(value, + value - 2, + std::memory_order_relaxed, + std::memory_order_relaxed)) + continue; + } + } + else { + object_dec_ref_py((PyObject*)value); + } + break; + } } -void Object::set_self_py(PyObject *o) noexcept { - uintptr_t value = m_state.load(std::memory_order_relaxed); - if (value & 1) { - value >>= 1; - for (uintptr_t i = 0; i < value; ++i) - object_inc_ref_py(o); - - uintptr_t o_i = (uintptr_t) o; - if (o_i & 1) { - fprintf(stderr, "Object::set_self_py(%p): invalid pointer alignment!", this); - abort(); - } - - m_state.store(o_i); - } else { - fprintf(stderr, - "Object::set_self_py(%p): a Python object was already present!", - this); - abort(); - } +void Object::set_self_py(PyObject* o) noexcept { + uintptr_t value = m_state.load(std::memory_order_relaxed); + if (value & 1) { + value >>= 1; + for (uintptr_t i = 0; i < value; ++i) + object_inc_ref_py(o); + + uintptr_t o_i = (uintptr_t)o; + if (o_i & 1) { + fprintf(stderr, "Object::set_self_py(%p): invalid pointer alignment!", this); + abort(); + } + + m_state.store(o_i); + } + else { + fprintf(stderr, + "Object::set_self_py(%p): a Python object was already present!", + this); + abort(); + } } -PyObject *Object::self_py() const noexcept { - uintptr_t value = m_state.load(std::memory_order_relaxed); - if (value & 1) - return nullptr; - else - return (PyObject *) value; +PyObject* Object::self_py() const noexcept { + uintptr_t value = m_state.load(std::memory_order_relaxed); + if (value & 1) + return nullptr; + else + return (PyObject*)value; } -void object_init_py(void (*object_inc_ref_py_)(PyObject *) noexcept, - void (*object_dec_ref_py_)(PyObject *) noexcept) { - object_inc_ref_py = object_inc_ref_py_; - object_dec_ref_py = object_dec_ref_py_; +void object_init_py(void (*object_inc_ref_py_)(PyObject*) noexcept, + void (*object_dec_ref_py_)(PyObject*) noexcept) { + object_inc_ref_py = object_inc_ref_py_; + object_dec_ref_py = object_dec_ref_py_; } NAMESPACE_END(nanogui) diff --git a/src/screen.cpp b/src/screen.cpp index 4b04e75c..bb2da4fc 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -260,7 +260,6 @@ Screen::Screen(const Vector2i &size, const std::string &caption, bool resizable, CHK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); - glfwSwapInterval(0); glfwSwapBuffers(m_glfw_window); #endif @@ -724,6 +723,7 @@ void Screen::redraw() { if (!m_redraw) { m_redraw = true; #if !defined(EMSCRIPTEN) + if (!nanogui::vsync_enabled()) glfwPostEmptyEvent(); #endif } @@ -757,6 +757,7 @@ void Screen::cursor_pos_callback_event(double x, double y) { ret = mouse_motion_event(p, p - m_mouse_pos, m_mouse_state, m_modifiers); m_mouse_pos = p; + std::cout << "Meep cursor pos " << p.x() << ", " << p.y() << std::endl; m_redraw |= ret; } catch (const std::exception &e) { std::cerr << "Caught exception in event handler: " << e.what() << std::endl; @@ -894,6 +895,16 @@ void Screen::resize_callback_event(int, int) { redraw(); } +void Screen::update_cursor() +{ + Widget* w = find_widget(m_mouse_pos); + if (w != nullptr && w->cursor() != m_cursor) + { + m_cursor = w->cursor(); + glfwSetCursor(m_glfw_window, m_cursors[(int)m_cursor]); + } +} + void Screen::update_focus(Widget *widget) { for (auto w: m_focus_path) { if (!w->focused()) From f865ebf85f28108c90d991add1b0ace01bb4d538 Mon Sep 17 00:00:00 2001 From: TwentyPast4 Date: Sun, 18 Jun 2023 14:08:57 +0200 Subject: [PATCH 2/2] Removed debug log --- src/screen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/screen.cpp b/src/screen.cpp index bb2da4fc..811e2732 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -757,7 +757,6 @@ void Screen::cursor_pos_callback_event(double x, double y) { ret = mouse_motion_event(p, p - m_mouse_pos, m_mouse_state, m_modifiers); m_mouse_pos = p; - std::cout << "Meep cursor pos " << p.x() << ", " << p.y() << std::endl; m_redraw |= ret; } catch (const std::exception &e) { std::cerr << "Caught exception in event handler: " << e.what() << std::endl;