From 7ff679769bf6bc49619b19da00772a88758827e5 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 22 Oct 2022 14:17:03 +0200 Subject: Add a config file to store position/rotation/zoom and use those the next time vr-video-player starts --- include/config.hpp | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/mpv.hpp | 2 +- 2 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 include/config.hpp (limited to 'include') diff --git a/include/config.hpp b/include/config.hpp new file mode 100644 index 0000000..fcf69bf --- /dev/null +++ b/include/config.hpp @@ -0,0 +1,299 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct SphereConfig { + glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); + glm::quat rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f); + float zoom = 0.0f; +}; + +struct Sphere360Config { + glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); + glm::quat rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f); + float zoom = 0.0f; +}; + +struct FlatConfig { + glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); + glm::quat rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f); + float zoom = 0.0f; +}; + +struct PlaneConfig { + glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); + glm::quat rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f); + float zoom = 0.0f; +}; + +struct Config { + SphereConfig sphere; + Sphere360Config sphere360; + FlatConfig flat; + PlaneConfig plane; +}; + +static std::string get_home_dir() { + const char *home_dir = getenv("HOME"); + if(!home_dir) { + passwd *pw = getpwuid(getuid()); + home_dir = pw->pw_dir; + } + + if(!home_dir) { + fprintf(stderr, "Error: Failed to get home directory of user, using /tmp directory\n"); + home_dir = "/tmp"; + } + + return home_dir; +} + +static std::string get_config_dir() { + std::string config_dir; + const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + if(xdg_config_home) { + config_dir = xdg_config_home; + } else { + config_dir = get_home_dir() + "/.config"; + } + config_dir += "/vr-video-player"; + return config_dir; +} + +static int create_directory_recursive(char *path) { + int path_len = strlen(path); + char *p = path; + char *end = path + path_len; + for(;;) { + char *slash_p = strchr(p, '/'); + + // Skips first '/', we don't want to try and create the root directory + if(slash_p == path) { + ++p; + continue; + } + + if(!slash_p) + slash_p = end; + + char prev_char = *slash_p; + *slash_p = '\0'; + int err = mkdir(path, S_IRWXU); + *slash_p = prev_char; + + if(err == -1 && errno != EEXIST) + return err; + + if(slash_p == end) + break; + else + p = slash_p + 1; + } + return 0; +} + +static bool file_get_content(const char *filepath, std::string &file_content) { + file_content.clear(); + bool success = false; + + FILE *file = fopen(filepath, "rb"); + if(!file) + return success; + + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + if(file_size != -1) { + file_content.resize(file_size); + fseek(file, 0, SEEK_SET); + if((long)fread(&file_content[0], 1, file_size, file) == file_size) + success = true; + } + + fclose(file); + return success; +} + +struct StringView { + const char *str; + size_t size; + + bool operator == (const char *other) const { + int len = strlen(other); + return (size_t)len == size && memcmp(str, other, size) == 0; + } + + size_t find(char c) const { + const void *p = memchr(str, c, size); + if(!p) + return std::string::npos; + return (const char*)p - str; + } +}; + +using StringSplitCallback = std::function; + +static void string_split_char(const std::string &str, char delimiter, StringSplitCallback callback_func) { + size_t index = 0; + while(index < str.size()) { + size_t new_index = str.find(delimiter, index); + if(new_index == std::string::npos) + new_index = str.size(); + + if(!callback_func({str.data() + index, new_index - index})) + break; + + index = new_index + 1; + } +} + +static bool config_split_key_value(const StringView str, StringView &key, StringView &value) { + key.str = nullptr; + key.size = 0; + + value.str = nullptr; + value.size = 0; + + size_t index = str.find(' '); + if(index == std::string::npos) + return std::string::npos; + + key.str = str.str; + key.size = index; + + value.str = str.str + index + 1; + value.size = str.size - (index + 1); + + return true; +} + +static bool string_to_float(std::string str, float &value, const StringView key) { + value = 0.0; + errno = 0; + char *endptr; + value = strtod(str.c_str(), &endptr); + if(endptr == str.c_str() || errno != 0) { + fprintf(stderr, "Warning: Invalid config option %.*s is not a float\n", (int)key.size, key.str); + return false; + } + return true; +} + +static bool string_to_vec3(std::string str, glm::vec3 &value, const StringView key) { + if(sscanf(str.data(), "%f|%f|%f", &value.x, &value.y, &value.z) != 3) { + fprintf(stderr, "Warning: Invalid config option %.*s is not a glm::vec3\n", (int)key.size, key.str); + return false; + } + return true; +} + +static bool string_to_quat(std::string str, glm::quat &value, const StringView key) { + if(sscanf(str.data(), "%f|%f|%f|%f", &value.x, &value.y, &value.z, &value.w) != 4) { + fprintf(stderr, "Warning: Invalid config option %.*s is not a glm::quat\n", (int)key.size, key.str); + return false; + } + return true; +} + +static Config read_config(bool &exists) { + setlocale(LC_ALL, "C"); + + Config config; + + const std::string config_path = get_config_dir() + "/config"; + std::string file_content; + if(!file_get_content(config_path.c_str(), file_content)) { + fprintf(stderr, "Warning: Failed to read config file: %s\n", config_path.c_str()); + exists = false; + return config; + } + + string_split_char(file_content, '\n', [&](StringView line) { + StringView key, value; + if(!config_split_key_value(line, key, value)) { + fprintf(stderr, "Warning: Invalid config option format: %.*s\n", (int)line.size, line.str); + return true; + } + + if(key == "sphere.position") { + string_to_vec3(std::string(value.str, value.size), config.sphere.position, key); + } else if(key == "sphere.rotation") { + string_to_quat(std::string(value.str, value.size), config.sphere.rotation, key); + } else if(key == "sphere.zoom") { + string_to_float(std::string(value.str, value.size), config.sphere.zoom, key); + } else if(key == "sphere360.position") { + string_to_vec3(std::string(value.str, value.size), config.sphere360.position, key); + } else if(key == "sphere360.rotation") { + string_to_quat(std::string(value.str, value.size), config.sphere360.rotation, key); + } else if(key == "sphere360.zoom") { + string_to_float(std::string(value.str, value.size), config.sphere360.zoom, key); + } else if(key == "flat.position") { + string_to_vec3(std::string(value.str, value.size), config.flat.position, key); + } else if(key == "flat.rotation") { + string_to_quat(std::string(value.str, value.size), config.flat.rotation, key); + } else if(key == "flat.zoom") { + string_to_float(std::string(value.str, value.size), config.flat.zoom, key); + } else if(key == "plane.position") { + string_to_vec3(std::string(value.str, value.size), config.plane.position, key); + } else if(key == "plane.rotation") { + string_to_quat(std::string(value.str, value.size), config.plane.rotation, key); + } else if(key == "plane.zoom") { + string_to_float(std::string(value.str, value.size), config.plane.zoom, key); + } else { + fprintf(stderr, "Warning: Invalid config option: %.*s\n", (int)line.size, line.str); + } + + return true; + }); + + exists = true; + return config; +} + +static void save_config(const Config &config) { + setlocale(LC_ALL, "C"); + + const std::string config_path = get_config_dir() + "/config"; + + char dir_tmp[PATH_MAX]; + strcpy(dir_tmp, config_path.c_str()); + char *dir = dirname(dir_tmp); + + if(create_directory_recursive(dir) != 0) { + fprintf(stderr, "Warning: Failed to create config directory: %s\n", dir); + return; + } + + FILE *file = fopen(config_path.c_str(), "wb"); + if(!file) { + fprintf(stderr, "Warning: Failed to create config file: %s\n", config_path.c_str()); + return; + } + + fprintf(file, "sphere.position %f|%f|%f\n", config.sphere.position.x, config.sphere.position.y, config.sphere.position.z); + fprintf(file, "sphere.rotation %f|%f|%f|%f\n", config.sphere.rotation.x, config.sphere.rotation.y, config.sphere.rotation.z, config.sphere.rotation.w); + fprintf(file, "sphere.zoom %f\n", config.sphere.zoom); + + fprintf(file, "sphere360.position %f|%f|%f\n", config.sphere360.position.x, config.sphere360.position.y, config.sphere360.position.z); + fprintf(file, "sphere360.rotation %f|%f|%f|%f\n", config.sphere360.rotation.x, config.sphere360.rotation.y, config.sphere360.rotation.z, config.sphere360.rotation.w); + fprintf(file, "sphere360.zoom %f\n", config.sphere360.zoom); + + fprintf(file, "flat.position %f|%f|%f\n", config.flat.position.x, config.flat.position.y, config.flat.position.z); + fprintf(file, "flat.rotation %f|%f|%f|%f\n", config.flat.rotation.x, config.flat.rotation.y, config.flat.rotation.z, config.flat.rotation.w); + fprintf(file, "flat.zoom %f\n", config.flat.zoom); + + fprintf(file, "plane.position %f|%f|%f\n", config.plane.position.x, config.plane.position.y, config.plane.position.z); + fprintf(file, "plane.rotation %f|%f|%f|%f\n", config.plane.rotation.x, config.plane.rotation.y, config.plane.rotation.z, config.plane.rotation.w); + fprintf(file, "plane.zoom %f\n", config.plane.zoom); + + fclose(file); +} \ No newline at end of file diff --git a/include/mpv.hpp b/include/mpv.hpp index b4e22b3..09ba2c1 100644 --- a/include/mpv.hpp +++ b/include/mpv.hpp @@ -16,7 +16,7 @@ public: bool load_file(const char *path); // |width| and |ħeight| are set to 0 unless there is an event to reconfigure video size - void on_event(SDL_Event &event, bool *render_update, int64_t *width, int64_t *height, bool *quit); + void on_event(SDL_Event &event, bool *render_update, int64_t *width, int64_t *height, bool *quit, int *error); void seek(double seconds); void toggle_pause(); void draw(unsigned int framebuffer_id, int width, int height); -- cgit v1.2.3