You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

799 lines
28 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2023 Intel Corporation. All Rights Reserved.
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <glad/glad.h>
#include <rs-config.h>
#include "ux-window.h"
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include "device-model.h"
#include <rsutils/os/special-folder.h>
#include "os.h"
// We use STB image to load the splash-screen from memory
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
// int-rs-splash.hpp contains the PNG image from res/int-rs-splash.png
#include "res/int-rs-splash.hpp"
#include "res/icon.h"
#include "ux-alignment.h"
#include <opengl3.h>
#include <iostream>
void glfw_error_callback(int error, const char* description)
{
std::cerr << "GLFW Driver Error: " << description << "\n";
}
namespace rs2
{
void GLAPIENTRY MessageCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* userParam)
{
if (type == GL_DEBUG_TYPE_ERROR)
{
fprintf(stderr, "GL CALLBACK: ** GL ERROR ** type = 0x%x, severity = 0x%x, message = %s\n",
type, severity, message);
}
}
void prepare_config_file()
{
config_file::instance().set_default(configurations::update::allow_rc_firmware, false);
config_file::instance().set_default(configurations::update::recommend_calibration, true);
config_file::instance().set_default(configurations::update::recommend_updates, true);
config_file::instance().set_default(configurations::update::sw_updates_url, server_versions_db_url);
config_file::instance().set_default(configurations::update::sw_updates_official_server, true);
config_file::instance().set_default(configurations::window::is_fullscreen, false);
config_file::instance().set_default(configurations::window::saved_pos, false);
config_file::instance().set_default(configurations::window::saved_size, false);
config_file::instance().set_default(configurations::window::font_size, 16);
config_file::instance().set_default(configurations::viewer::is_measuring, false);
config_file::instance().set_default(configurations::viewer::log_to_console, true);
config_file::instance().set_default(configurations::viewer::log_to_file, false);
config_file::instance().set_default(configurations::viewer::log_severity, 2);
config_file::instance().set_default(configurations::viewer::metric_system, true);
config_file::instance().set_default(configurations::viewer::ground_truth_r, 2500);
config_file::instance().set_default(configurations::viewer::dashboard_open, true);
config_file::instance().set_default(configurations::record::compression_mode, 2); // Let the device decide
config_file::instance().set_default(configurations::record::file_save_mode, 0); // Auto-select name
config_file::instance().set_default(configurations::performance::show_fps, false);
config_file::instance().set_default(configurations::performance::vsync, true);
config_file::instance().set_default(configurations::performance::occlusion_invalidation, true);
config_file::instance().set_default(configurations::ply::mesh, true);
config_file::instance().set_default(configurations::ply::use_normals, false);
config_file::instance().set_default(configurations::ply::encoding, configurations::ply::binary);
config_file::instance().set_default(configurations::viewer::commands_xml, "./Commands.xml");
config_file::instance().set_default(configurations::viewer::hwlogger_xml, "./HWLoggerEvents.xml");
std::string path;
try
{
path = rsutils::os::get_special_folder( rsutils::os::special_folder::user_documents );
}
catch (const std::exception&)
{
std::string msg = "Failed to get Documents folder";
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
path = "";
}
config_file::instance().set_default(configurations::viewer::log_filename, path + "librealsense.log");
config_file::instance().set_default(configurations::record::default_path, path);
#ifdef __APPLE__
config_file::instance().set_default(configurations::performance::font_oversample, 2);
config_file::instance().set_default(configurations::performance::enable_msaa, true);
config_file::instance().set_default(configurations::performance::msaa_samples, 4);
// On Mac-OS, mixing OpenGL 2 with OpenGL 3 is not supported by the driver
// while this can be worked-around, this will take more development time,
// so for now Macs should not use the GLSL stuff
config_file::instance().set_default(configurations::performance::glsl_for_processing, false);
config_file::instance().set_default(configurations::performance::glsl_for_rendering, false);
#else
auto vendor = (const char*)glGetString(GL_VENDOR);
auto renderer = (const char*)glGetString(GL_RENDERER);
auto version = (const char*)glGetString(GL_VERSION);
auto glsl = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
bool use_glsl = false;
// Absolutely arbitrary list of manufacturers that are likely to benefit from GLSL optimisation
if (starts_with(rsutils::string::to_lower(vendor), "intel") ||
starts_with(rsutils::string::to_lower(vendor), "ati") ||
starts_with(rsutils::string::to_lower(vendor), "nvidia"))
{
use_glsl = true;
}
// Double-check that GLSL 1.3+ is supported
if (starts_with(rsutils::string::to_lower(vendor), "1.1") || starts_with(rsutils::string::to_lower(vendor), "1.2"))
{
use_glsl = false;
}
if (use_glsl)
{
config_file::instance().set_default(configurations::performance::show_skybox, true);
config_file::instance().set_default(configurations::performance::font_oversample, 2);
config_file::instance().set_default(configurations::performance::enable_msaa, false);
config_file::instance().set_default(configurations::performance::msaa_samples, 2);
config_file::instance().set_default(configurations::performance::glsl_for_processing, true);
config_file::instance().set_default(configurations::performance::glsl_for_rendering, true);
config_file::instance().set_default(configurations::viewer::shading_mode, 2);
}
else
{
config_file::instance().set_default(configurations::performance::show_skybox, false);
config_file::instance().set_default(configurations::performance::font_oversample, 1);
config_file::instance().set_default(configurations::performance::enable_msaa, false);
config_file::instance().set_default(configurations::performance::msaa_samples, 2);
config_file::instance().set_default(configurations::performance::glsl_for_processing, false);
config_file::instance().set_default(configurations::performance::glsl_for_rendering, false);
config_file::instance().set_default(configurations::viewer::shading_mode, 0);
}
#endif
}
void ux_window::reload()
{
_reload = true;
}
void ux_window::refresh()
{
if (_use_glsl_proc) rs2::gl::shutdown_processing();
rs2::gl::shutdown_rendering();
_use_glsl_render = config_file::instance().get(configurations::performance::glsl_for_rendering);
_use_glsl_proc = config_file::instance().get(configurations::performance::glsl_for_processing);
rs2::gl::init_rendering(_use_glsl_render);
if (_use_glsl_proc) rs2::gl::init_processing(_win, _use_glsl_proc);
}
void ux_window::link_hovered()
{
_link_hovered = true;
}
void ux_window::cross_hovered()
{
_cross_hovered = true;
}
void ux_window::setup_icon()
{
GLFWimage icon[4];
int x, y, comp;
auto icon_16 = stbi_load_from_memory(icon_16_png_data, (int)icon_16_png_size, &x, &y, &comp, false);
icon[0].width = x; icon[0].height = y;
icon[0].pixels = icon_16;
auto icon_24 = stbi_load_from_memory(icon_24_png_data, (int)icon_24_png_size, &x, &y, &comp, false);
icon[1].width = x; icon[1].height = y;
icon[1].pixels = icon_24;
auto icon_64 = stbi_load_from_memory(icon_64_png_data, (int)icon_64_png_size, &x, &y, &comp, false);
icon[2].width = x; icon[2].height = y;
icon[2].pixels = icon_64;
auto icon_256 = stbi_load_from_memory(icon_256_png_data, (int)icon_256_png_size, &x, &y, &comp, false);
icon[3].width = x; icon[3].height = y;
icon[3].pixels = icon_256;
glfwSetWindowIcon(_win, 4, icon);
stbi_image_free(icon_16);
stbi_image_free(icon_24);
stbi_image_free(icon_64);
stbi_image_free(icon_256);
}
void ux_window::open_window()
{
if (_win)
{
rs2::gl::shutdown_rendering();
if (_use_glsl_proc) rs2::gl::shutdown_processing();
ImGui::GetIO().Fonts->ClearFonts(); // To be refactored into Viewer theme object
ImGui_ImplGlfw_Shutdown();
glfwDestroyWindow(_win);
glfwDestroyCursor(_hand_cursor);
glfwDestroyCursor(_cross_cursor);
glfwTerminate();
}
if (!glfwInit())
exit(1);
glfwSetErrorCallback(glfw_error_callback);
_hand_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
_cross_cursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR);
{
glfwWindowHint(GLFW_VISIBLE, 0);
auto ctx = glfwCreateWindow(640, 480, "Offscreen Context", nullptr, nullptr);
if (!ctx) throw std::runtime_error("Could not initialize offscreen context!");
glfwMakeContextCurrent(ctx);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
// OpenGL 2.1 backward-compatibility fixes.
// On macOS, the compatibility profile is OpenGL 2.1 + extensions.
if (!GLAD_GL_VERSION_3_0 && !GLAD_GL_ARB_vertex_array_object) {
if (GLAD_GL_APPLE_vertex_array_object) {
glBindVertexArray = glBindVertexArrayAPPLE;
glDeleteVertexArrays = glDeleteVertexArraysAPPLE;
glGenVertexArrays = glGenVertexArraysAPPLE;
glIsVertexArray = glIsVertexArrayAPPLE;
} else {
throw std::runtime_error("OpenGL 3.0 or ARB_vertex_array_object extension required!");
}
}
prepare_config_file();
glfwDestroyWindow(ctx);
}
_use_glsl_render = config_file::instance().get(configurations::performance::glsl_for_rendering);
_use_glsl_proc = config_file::instance().get(configurations::performance::glsl_for_processing);
_enable_msaa = config_file::instance().get(configurations::performance::enable_msaa);
_msaa_samples = config_file::instance().get(configurations::performance::msaa_samples);
_fullscreen = config_file::instance().get(configurations::window::is_fullscreen);
rs2_error* e = nullptr;
_title_str = rsutils::string::from() << _title << " v" << api_version_to_string(rs2_get_api_version(&e));
auto debug = is_debug();
if (debug)
{
_title_str = _title_str + ", DEBUG";
}
_width = 1024;
_height = 768;
// Dynamically adjust new window size (by detecting monitor resolution)
auto primary = glfwGetPrimaryMonitor();
if (primary)
{
const auto mode = glfwGetVideoMode(primary);
if (_fullscreen)
{
_width = mode->width;
_height = mode->height;
}
else
{
_width = int(mode->width * 0.7f);
_height = int(mode->height * 0.7f);
}
}
if (_enable_msaa)
glfwWindowHint(GLFW_SAMPLES, _msaa_samples);
glfwWindowHint(GLFW_VISIBLE, 0);
// Create GUI Windows
_win = glfwCreateWindow(_width, _height, _title_str.c_str(),
(_fullscreen ? primary : nullptr), nullptr);
if (!_win)
throw std::runtime_error("Could not open OpenGL window, please check your graphic drivers or use the textual SDK tools");
if (config_file::instance().get(configurations::window::saved_pos))
{
int x = config_file::instance().get(configurations::window::position_x);
int y = config_file::instance().get(configurations::window::position_y);
int count;
GLFWmonitor** monitors = glfwGetMonitors(&count);
if (count > 0)
{
bool legal_position = false;
for (int i = 0; i < count; i++)
{
auto rect = get_monitor_rect(monitors[i]);
if (rect.contains({ (float)x, (float)y }))
{
legal_position = true;
}
}
if (legal_position) glfwSetWindowPos(_win, x, y);
}
}
if (config_file::instance().get(configurations::window::saved_size))
{
int w = config_file::instance().get(configurations::window::width);
int h = config_file::instance().get(configurations::window::height);
glfwSetWindowSize(_win, w, h);
if (config_file::instance().get(configurations::window::maximized))
glfwMaximizeWindow(_win);
}
glfwMakeContextCurrent(_win);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
if (glDebugMessageCallback)
{
// During init, enable debug output
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(MessageCallback, 0);
}
glfwSetWindowPosCallback(_win, [](GLFWwindow* w, int x, int y)
{
config_file::instance().set(configurations::window::saved_pos, true);
config_file::instance().set(configurations::window::position_x, x);
config_file::instance().set(configurations::window::position_y, y);
});
glfwSetWindowSizeCallback( _win, []( GLFWwindow * window, int width, int height ) {
if( width > 0 && height > 0 )
{
config_file::instance().set( configurations::window::saved_size, true );
config_file::instance().set( configurations::window::width, width );
config_file::instance().set( configurations::window::height, height );
config_file::instance().set( configurations::window::maximized,
glfwGetWindowAttrib( window, GLFW_MAXIMIZED ) );
}
} );
setup_icon();
ImGui_ImplGlfw_Init(_win, true);
if (_use_glsl_render)
_2d_vis = std::make_shared<visualizer_2d>(std::make_shared<splash_screen_shader>());
// Load fonts to be used with the ImGui - TODO move to RAII
imgui_easy_theming(_font_dynamic, _font_18, _monofont, font_size);
// Register for UI-controller events
glfwSetWindowUserPointer(_win, this);
glfwSetCursorPosCallback(_win, [](GLFWwindow* w, double cx, double cy)
{
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
data->_mouse.cursor = { (float)cx / data->_scale_factor,
(float)cy / data->_scale_factor };
});
glfwSetMouseButtonCallback(_win, [](GLFWwindow* w, int button, int action, int mods)
{
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
data->_mouse.mouse_down[0] = (button == GLFW_MOUSE_BUTTON_1) && (action != GLFW_RELEASE);
data->_mouse.mouse_down[1] = (button == GLFW_MOUSE_BUTTON_2) && (action != GLFW_RELEASE);
});
glfwSetScrollCallback(_win, [](GLFWwindow * w, double xoffset, double yoffset)
{
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
data->_mouse.mouse_wheel = static_cast<int>(yoffset);
data->_mouse.ui_wheel += static_cast<int>(yoffset);
});
glfwSetDropCallback(_win, [](GLFWwindow* w, int count, const char** paths)
{
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
if (count <= 0) return;
for (int i = 0; i < count; i++)
{
data->on_file_drop(paths[i]);
}
});
rs2::gl::init_rendering(_use_glsl_render);
if (_use_glsl_proc) rs2::gl::init_processing(_win, _use_glsl_proc);
glfwShowWindow(_win);
glfwFocusWindow(_win);
_show_fps = config_file::instance().get(configurations::performance::show_fps);
_vsync = config_file::instance().get(configurations::performance::vsync);
// Prepare the splash screen and do some initialization in the background
int x, y, comp;
auto r = stbi_load_from_memory(splash, (int)splash_size, &x, &y, &comp, false);
_splash_tex.upload_image(x, y, r);
stbi_image_free(r);
}
ux_window::ux_window(const char* title, context &ctx) :
_win(nullptr), _width(0), _height(0), _output_height(0),
_font_dynamic(nullptr), _font_18(nullptr), _monofont(nullptr), font_size(16), _app_ready(false),
_first_frame(true), _query_devices(true), _missing_device(false),
_hourglass_index(0), _dev_stat_message{}, _keep_alive(true), _title(title), _ctx(ctx)
{
open_window();
// Apply initial UI state
reset();
}
void ux_window::add_on_load_message(const std::string& msg)
{
std::lock_guard<std::mutex> lock(_on_load_message_mtx);
_on_load_message.push_back(msg);
}
void ux_window::imgui_config_pop()
{
ImGui::PopFont();
ImGui::End();
ImGui::PopStyleColor(3);
ImGui::PopStyleVar(2);
end_frame();
glPopMatrix();
}
void ux_window::imgui_config_push()
{
glPushMatrix();
glViewport(0, 0, _fb_width, _fb_height);
glClearColor(0.036f, 0.044f, 0.051f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glOrtho(0, _width, _height, 0, -1, +1);
// Fade-in the logo
auto opacity = smoothstep(float(_splash_timer.get_elapsed_ms()), 100.f, 2500.f);
auto ox = 0.7f - smoothstep(float(_splash_timer.get_elapsed_ms()), 200.f, 1900.f) * 0.4f;
auto oy = 0.5f;
auto power = std::sin(smoothstep(float(_splash_timer.get_elapsed_ms()), 150.f, 2200.f) * 3.14f) * 0.96f;
if (_use_glsl_render)
{
auto shader = ((splash_screen_shader*)&_2d_vis->get_shader());
shader->begin();
shader->set_power(power);
shader->set_ray_center(float2{ ox, oy });
shader->end();
_2d_vis->draw_texture(_splash_tex.get_gl_handle(), opacity);
}
else
{
_splash_tex.show({ 0.f,0.f,float(_width),float(_height) }, opacity);
}
std::string hourglass = u8"\uf251";
static rsutils::time::periodic_timer every_200ms(std::chrono::milliseconds(200));
bool do_200ms = every_200ms;
if (_query_devices && do_200ms)
{
_missing_device = _ctx.query_devices(RS2_PRODUCT_LINE_ANY_INTEL).size() == 0;
_hourglass_index = (_hourglass_index + 1) % 4;
if (!_missing_device)
{
_dev_stat_message = u8"\uf287 RealSense device detected.";
_query_devices = false;
}
}
hourglass[2] += _hourglass_index;
auto flags = ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar;
auto text_color = light_grey;
text_color.w = opacity;
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, white);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 5, 5 });
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 1);
ImGui::PushStyleColor(ImGuiCol_WindowBg, transparent);
ImGui::SetNextWindowPos({ (float)_width / 2 - 150, (float)_height / 2 + 70 });
ImGui::SetNextWindowSize({ (float)_width, (float)_height });
ImGui::Begin("Splash Screen Banner", nullptr, flags);
ImGui::PushFont(_font_18);
ImGui::Text("%s Loading %s...", hourglass.c_str(), _title_str.c_str());
}
// Check that the graphic subsystem is valid and start a new frame
ux_window::operator bool()
{
end_frame();
if (_show_fps)
{
std::stringstream temp_title;
temp_title << _title_str;
auto fps = ImGui::GetIO().Framerate;
temp_title << ", FPS: " << fps;
glfwSetWindowTitle(_win, temp_title.str().c_str());
}
// Yield the CPU
if (!_vsync)
{
std::this_thread::yield();
glfwSwapInterval(0);
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
auto res = !glfwWindowShouldClose(_win);
if (_first_frame)
{
assert(!_first_load.joinable()); // You must call to reset() before initiate new thread
_first_load = std::thread([&]() {
while (_keep_alive && !_app_ready)
{
try
{
_app_ready = on_load();
}
catch (...)
{
std::this_thread::sleep_for(std::chrono::seconds(1)); // Wait for connect event and retry
}
}
});
}
// If we are just getting started, render the Splash Screen instead of normal UI
while (res && (!_app_ready || _splash_timer.get_elapsed_ms() < 2000.f))
{
res = !glfwWindowShouldClose(_win);
glfwPollEvents();
begin_frame();
if (_first_frame)
{
_is_ui_aligned = is_gui_aligned(_win);
_first_frame = false;
}
imgui_config_push();
{
std::lock_guard<std::mutex> lock(_on_load_message_mtx);
if (_on_load_message.empty())
{
ImGui::Text("%s", _dev_stat_message.c_str());
}
else if (!_on_load_message.empty())
{
ImGui::Text("%s", _dev_stat_message.c_str());
for (auto& msg : _on_load_message)
{
auto is_last_msg = (msg == _on_load_message.back());
if (is_last_msg)
ImGui::Text("%s", msg.c_str());
else if (!is_last_msg)
ImGui::Text("%s", msg.c_str());
}
}
}
imgui_config_pop();
// Yield the CPU
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
// reset graphic pipe
begin_frame();
if (_link_hovered)
glfwSetCursor(_win, _hand_cursor);
else if (_cross_hovered)
glfwSetCursor(_win, _cross_cursor);
else
glfwSetCursor(_win, nullptr);
_cross_hovered = false;
_link_hovered = false;
_hovers_any_input_window = false;
return res;
}
ux_window::~ux_window()
{
if (_first_load.joinable())
{
_keep_alive = false;
_first_load.join();
}
end_frame();
try
{
rs2::gl::shutdown_rendering();
if (_use_glsl_proc) rs2::gl::shutdown_processing();
}
catch( ... )
{
}
ImGui::GetIO().Fonts->ClearFonts(); // To be refactored into Viewer theme object
ImGui_ImplGlfw_Shutdown();
glfwDestroyWindow(_win);
glfwDestroyCursor(_hand_cursor);
glfwDestroyCursor(_cross_cursor);
glfwTerminate();
}
void ux_window::begin_frame()
{
glfwPollEvents();
int state = glfwGetKey(_win, GLFW_KEY_F8);
if (state == GLFW_PRESS)
{
_fullscreen_pressed = true;
}
else
{
if (_fullscreen_pressed)
{
_fullscreen = !_fullscreen;
config_file::instance().set(configurations::window::is_fullscreen, _fullscreen);
open_window();
}
_fullscreen_pressed = false;
}
if (_reload)
{
open_window();
_reload = false;
on_reload_complete();
}
int w = _width; int h = _height;
glfwGetWindowSize(_win, &_width, &_height);
// Set minimum size 1
if (_width <= 0) _width = 1;
if (_height <= 0) _height = 1;
int fw = _fb_width;
int fh = _fb_height;
glfwGetFramebufferSize(_win, &_fb_width, &_fb_height);
// Set minimum size 1
if (_fb_width <= 0) _fb_width = 1;
if (_fb_height <= 0) _fb_height = 1;
if (fw != _fb_width || fh != _fb_height)
{
std::string msg = rsutils::string::from() << "Framebuffer size changed to " << _fb_width << " x " << _fb_height;
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
}
auto sf = _scale_factor;
// Update the scale factor each frame
// based on resolution and physical display size
_scale_factor = static_cast<float>(pick_scale_factor(_win));
_width = static_cast<int>(_width / _scale_factor);
_height = static_cast<int>(_height / _scale_factor);
if (w != _width || h != _height)
{
std::string msg = rsutils::string::from() << "Window size changed to " << _width << " x " << _height;
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
}
if (_scale_factor != sf)
{
std::string msg = rsutils::string::from() << "Scale Factor is now " << _scale_factor;
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
}
// Reset ImGui state
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
ImGui::GetIO().MouseWheel = _mouse.ui_wheel;
_mouse.ui_wheel = 0.f;
ImGui_ImplGlfw_NewFrame(_scale_factor);
//ImGui::NewFrame();
}
void ux_window::begin_viewport()
{
// Rendering
glViewport(0, 0,
static_cast<int>(ImGui::GetIO().DisplaySize.x * _scale_factor),
static_cast<int>(ImGui::GetIO().DisplaySize.y * _scale_factor));
if (_enable_msaa) glEnable(GL_MULTISAMPLE);
else glDisable(GL_MULTISAMPLE);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
}
void ux_window::end_frame()
{
if (!_first_frame)
{
ImGui::Render();
glfwSwapBuffers(_win);
_mouse.mouse_wheel = 0;
}
}
void ux_window::reset()
{
if (_first_load.joinable())
{
_keep_alive = false;
_first_load.join();
_keep_alive = true;
}
_query_devices = true;
_missing_device = false;
_hourglass_index = 0;
_first_frame = true;
_app_ready = false;
_splash_timer.reset();
_dev_stat_message = u8"\uf287 Please connect Intel RealSense device!";
{
std::lock_guard<std::mutex> lock(_on_load_message_mtx);
_on_load_message.clear();
}
}
}