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.
314 lines
9.1 KiB
314 lines
9.1 KiB
// License: Apache 2.0. See LICENSE file in root directory.
|
|
// Copyright(c) 2017 Intel Corporation. All Rights Reserved.
|
|
#ifdef _MSC_VER
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#endif
|
|
|
|
#include "os.h"
|
|
#include <rsutils/easylogging/easyloggingpp.h>
|
|
|
|
#include <thread>
|
|
#include <algorithm>
|
|
#include <regex>
|
|
#include <cmath>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#include <stb_image_write.h>
|
|
|
|
#include <tinyfiledialogs.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <wchar.h>
|
|
#include <KnownFolders.h>
|
|
#include <shlobj.h>
|
|
#include <rsutils/os/hresult.h>
|
|
#endif
|
|
|
|
#if (defined(_WIN32) || defined(_WIN64))
|
|
#include "ShellAPI.h"
|
|
#endif
|
|
|
|
#if defined __linux__ || defined __APPLE__
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
namespace rs2
|
|
{
|
|
// Use shortcuts for long names to avoid trimming of essential data
|
|
std::string truncate_string(const std::string& str, size_t width)
|
|
{
|
|
if (str.length() > width)
|
|
{
|
|
std::ostringstream ss;
|
|
ss << str.substr(0, width / 3) << "..." << str.substr(str.length() - width / 3);
|
|
return ss.str().c_str();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
void open_url(const char* url)
|
|
{
|
|
#if (defined(_WIN32) || defined(_WIN64))
|
|
if (reinterpret_cast<INT_PTR>(ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOW)) < 32)
|
|
throw std::runtime_error("Failed opening URL");
|
|
#elif defined __linux__ || defined(__linux__)
|
|
std::string command_name = "xdg-open ";
|
|
std::string command = command_name + url;
|
|
if (system(command.c_str()))
|
|
throw std::runtime_error("Failed opening URL");
|
|
#elif __APPLE__
|
|
std::string command_name = "open ";
|
|
std::string command = command_name + url;
|
|
if (system(command.c_str()))
|
|
throw std::runtime_error("Failed opening URL");
|
|
#else
|
|
#pragma message ( "\nLibrealsense couldn't establish OS/Build environment. \
|
|
Some auxillary functionalities might be affected. Please report this message if encountered")
|
|
#endif
|
|
}
|
|
|
|
std::vector<std::string> split_string(std::string& input, char delim)
|
|
{
|
|
std::vector<std::string> result;
|
|
auto e = input.end();
|
|
auto i = input.begin();
|
|
while (i != e) {
|
|
i = find_if_not(i, e, [delim](char c) { return c == delim; });
|
|
if (i == e) break;
|
|
auto j = find(i, e, delim);
|
|
result.emplace_back(i, j);
|
|
i = j;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Helper function to get window rect from GLFW
|
|
rect get_window_rect(GLFWwindow* window)
|
|
{
|
|
int width, height;
|
|
glfwGetWindowSize(window, &width, &height);
|
|
int xpos, ypos;
|
|
glfwGetWindowPos(window, &xpos, &ypos);
|
|
|
|
return{ (float)xpos, (float)ypos,
|
|
(float)width, (float)height };
|
|
}
|
|
|
|
// Helper function to get monitor rect from GLFW
|
|
rect get_monitor_rect(GLFWmonitor* monitor)
|
|
{
|
|
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
|
int xpos, ypos;
|
|
glfwGetMonitorPos(monitor, &xpos, &ypos);
|
|
|
|
return{ (float)xpos, (float)ypos,
|
|
(float)mode->width, (float)mode->height };
|
|
}
|
|
|
|
// Select appropriate scale factor based on the display
|
|
// that most of the application is presented on
|
|
int pick_scale_factor(GLFWwindow* window)
|
|
{
|
|
auto window_rect = get_window_rect(window);
|
|
int count;
|
|
GLFWmonitor** monitors = glfwGetMonitors(&count);
|
|
if (count == 0) return 1; // Not sure if possible, but better be safe
|
|
|
|
// Find the monitor that covers most of the application pixels:
|
|
GLFWmonitor* best = monitors[0];
|
|
float best_area = 0.f;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
auto int_area = window_rect.intersection(
|
|
get_monitor_rect(monitors[i])).area();
|
|
if (int_area >= best_area)
|
|
{
|
|
best_area = int_area;
|
|
best = monitors[i];
|
|
}
|
|
}
|
|
|
|
int widthMM = 0;
|
|
int heightMM = 0;
|
|
glfwGetMonitorPhysicalSize(best, &widthMM, &heightMM);
|
|
|
|
// This indicates that the monitor dimentions are unknown
|
|
if (widthMM * heightMM == 0) return 1;
|
|
|
|
// The actual calculation is somewhat arbitrary, but we are going for
|
|
// about 1cm buttons, regardless of resultion
|
|
// We discourage fractional scale factors
|
|
float how_many_pixels_in_mm =
|
|
get_monitor_rect(best).area() / (widthMM * heightMM);
|
|
float scale = sqrt(how_many_pixels_in_mm) / 5.f;
|
|
if (scale < 1.f) return 1;
|
|
return (int)(floor(scale));
|
|
}
|
|
|
|
bool directory_exists(const char* dir)
|
|
{
|
|
struct stat info;
|
|
|
|
if (stat(dir, &info ) != 0)
|
|
return false;
|
|
else if (info.st_mode & S_IFDIR)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
const char* file_dialog_open(file_dialog_mode flags, const char* filters,
|
|
const char* default_path, const char* default_name)
|
|
{
|
|
std::string def = "";
|
|
if (default_path) def += default_path;
|
|
if (default_name && default_path) def += "/";
|
|
if (default_name) def += default_name;
|
|
const char* def_ptr = nullptr;
|
|
if (default_name || default_path)
|
|
def_ptr = def.c_str();
|
|
|
|
char const* const* aFilterPatterns = nullptr;
|
|
char const* aSingleFilterDescription = nullptr;
|
|
|
|
std::vector<std::string> filters_split;
|
|
|
|
if (filters)
|
|
{
|
|
auto curr = filters;
|
|
while (*curr != '\0')
|
|
{
|
|
auto end = curr + strlen(curr);
|
|
filters_split.push_back({ curr, end });
|
|
curr = end + 1;
|
|
}
|
|
}
|
|
|
|
int filters_count = 0;
|
|
std::vector<const char*> filter;
|
|
|
|
if (filters_split.size() >= 1)
|
|
{
|
|
filters_count = int( filters_split.size() - 1 );
|
|
|
|
// set description
|
|
aSingleFilterDescription = filters_split[0].c_str();
|
|
|
|
// fill filter pattern with extensions
|
|
for (int i = 1; i < filters_split.size(); ++i)
|
|
{
|
|
filter.push_back(filters_split[i].c_str());
|
|
}
|
|
aFilterPatterns = filter.data();
|
|
}
|
|
|
|
if (flags == save_file)
|
|
{
|
|
return tinyfd_saveFileDialog("Save File", def_ptr, filters_count, aFilterPatterns, aSingleFilterDescription);
|
|
}
|
|
if (flags == open_file)
|
|
{
|
|
return tinyfd_openFileDialog("Open File", def_ptr, filters_count, aFilterPatterns, aSingleFilterDescription, 0);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int save_to_png(const char* filename,
|
|
size_t pixel_width, size_t pixels_height, size_t bytes_per_pixel,
|
|
const void* raster_data, size_t stride_bytes)
|
|
{
|
|
return stbi_write_png(filename, (int)pixel_width, (int)pixels_height, (int)bytes_per_pixel, raster_data, (int)stride_bytes);
|
|
}
|
|
|
|
std::string get_file_name(const std::string& path)
|
|
{
|
|
std::string file_name;
|
|
for (auto rit = path.rbegin(); rit != path.rend(); ++rit)
|
|
{
|
|
if (*rit == '\\' || *rit == '/')
|
|
break;
|
|
file_name += *rit;
|
|
}
|
|
std::reverse(file_name.begin(), file_name.end());
|
|
return file_name;
|
|
}
|
|
|
|
std::string get_timestamped_file_name()
|
|
{
|
|
std::time_t now = std::time(NULL);
|
|
std::tm * ptm = std::localtime(&now);
|
|
char buffer[16];
|
|
// Format: 20170529_205500
|
|
std::strftime(buffer, 16, "%Y%m%d_%H%M%S", ptm);
|
|
return buffer;
|
|
}
|
|
|
|
bool ends_with(const std::string& s, const std::string& suffix)
|
|
{
|
|
auto i = s.rbegin(), j = suffix.rbegin();
|
|
for (; i != s.rend() && j != suffix.rend() && *i == *j;
|
|
i++, j++);
|
|
return j == suffix.rend();
|
|
}
|
|
|
|
bool starts_with(const std::string& s, const std::string& prefix)
|
|
{
|
|
auto i = s.begin(), j = prefix.begin();
|
|
for (; i != s.end() && j != prefix.end() && *i == *j;
|
|
i++, j++);
|
|
return j == prefix.end();
|
|
}
|
|
|
|
bool is_debug()
|
|
{
|
|
#ifndef NDEBUG
|
|
return false;
|
|
#else
|
|
#ifndef _DEBUG
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
std::string url_encode(const std::string &value)
|
|
{
|
|
// based on https://stackoverflow.com/a/17708801
|
|
using namespace std;
|
|
|
|
ostringstream escaped;
|
|
escaped.fill('0');
|
|
escaped << hex;
|
|
|
|
for (string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
|
|
string::value_type c = (*i);
|
|
|
|
// Keep alphanumeric and other accepted characters intact
|
|
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
escaped << c;
|
|
continue;
|
|
}
|
|
|
|
// Any other characters are percent-encoded
|
|
escaped << uppercase;
|
|
escaped << '%' << setw(2) << int((unsigned char)c);
|
|
escaped << nouppercase;
|
|
}
|
|
|
|
return escaped.str();
|
|
}
|
|
}
|