From 1247f5344f1a2d102d174bbe7e445ea1a59eb239 Mon Sep 17 00:00:00 2001 From: Toni500git Date: Mon, 16 Sep 2024 20:01:39 +0200 Subject: [PATCH] experimental: improve image as logo in terminal --- include/util.hpp | 1 + src/config.cpp | 2 +- src/display.cpp | 83 +++++++++++++++++++++++++++++++++++++----------- src/util.cpp | 43 +++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 20 deletions(-) diff --git a/include/util.hpp b/include/util.hpp index 2a650df..f4463a5 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -62,6 +62,7 @@ byte_units_t auto_devide_bytes(const size_t num); bool is_file_image(const unsigned char* bytes); void ctrl_d_handler(const std::istream& cin); std::string expandVar(std::string ret); +bool taur_exec(const std::vector cmd_str, const bool noerror_print = true); std::string which(const std::string& command); bool read_binary_file(std::ifstream& f, std::string& ret); void replace_str(std::string& str, const std::string_view from, const std::string_view to); diff --git a/src/config.cpp b/src/config.cpp index 1ebd407..a6cc526 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -49,9 +49,9 @@ void Config::loadConfigFile(const std::string_view filename, colors_t& colors) this->offset = this->getValue("config.offset", 5); this->logo_padding_left = this->getValue("config.logo-padding-left", 0); this->layout_padding_top = this->getValue("config.layout-padding-top", 0); + this->logo_padding_top = this->getValue("config.logo-padding-top", 0); this->font = this->getValue("gui.font", "Liberation Mono Normal 12"); this->gui_bg_image = this->getValue("gui.bg-image", "disable"); - this->logo_padding_top = this->getValue("config.logo-padding-top", 0); this->builtin_title_sep = this->getValue("config.title-sep", "-"); diff --git a/src/display.cpp b/src/display.cpp index eb15041..eb6e7d2 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -3,7 +3,7 @@ #include "display.hpp" #define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +#include #include #include @@ -16,6 +16,7 @@ #include "fmt/core.h" #include "parse.hpp" #include "query.hpp" +#include "stb_image.h" #include "util.hpp" std::string Display::detect_distro(const Config& config) @@ -43,9 +44,10 @@ std::string Display::detect_distro(const Config& config) } } -static std::vector render_with_image(const Config& config, const colors_t& colors) +static std::vector render_with_image(const Config& config, const colors_t& colors, + const std::string_view path) { - std::string path{ Display::detect_distro(config) }; + std::string distro_path{ Display::detect_distro(config) }; systemInfo_t systemInfo{}; std::vector layout{ config.layout }; @@ -58,19 +60,19 @@ static std::vector render_with_image(const Config& config, const co stbi_image_free(img); else die("Unable to load image '{}'", config.source_path); - + if (!config.ascii_logo_type.empty()) { const size_t& pos = path.rfind('.'); - + if (pos != std::string::npos) - path.insert(pos, "_" + config.ascii_logo_type); + distro_path.insert(pos, "_" + config.ascii_logo_type); else - path += "_" + config.ascii_logo_type; + distro_path += "_" + config.ascii_logo_type; } // this is just for parse() to auto add the distro colors - std::ifstream file(path, std::ios::binary); + std::ifstream file(distro_path, std::ios::binary); std::string line, _; while (std::getline(file, line)) @@ -85,14 +87,59 @@ static std::vector render_with_image(const Config& config, const co layout.end()); for (size_t i = 0; i < layout.size(); i++) - { - for (size_t _ = 0; _ < config.offset; _++) // I use _ because we don't need it + for (size_t _ = 0; _ < config.offset + 40; _++) // I use _ because we don't need it layout.at(i).insert(0, " "); - } return layout; } +bool get_pos(std::uint32_t& y, std::uint32_t& x) +{ + char buf[30] = { 0 }; + int ret, i, pow; + char ch; + + y = 0; + x = 0; + + struct termios term, restore; + + tcgetattr(0, &term); + tcgetattr(0, &restore); + term.c_lflag &= ~(ICANON | ECHO); + tcsetattr(0, TCSANOW, &term); + + write(1, "\033[6n", 4); + + for (i = 0, ch = 0; ch != 'R'; i++) + { + ret = read(0, &ch, 1); + if (!ret) + { + tcsetattr(0, TCSANOW, &restore); + fprintf(stderr, "getpos: error reading response!\n"); + return false; + } + buf[i] = ch; + debug("buf[{}]: \t{} \t{}", i, ch, ch); + } + + if (i < 2) + { + tcsetattr(0, TCSANOW, &restore); + return false; + } + + for (i -= 2, pow = 1; buf[i] != ';'; i--, pow *= 10) + x = x + (buf[i] - '0') * pow; + + for (i--, pow = 1; buf[i] != '['; i--, pow *= 10) + y = y + (buf[i] - '0') * pow; + + tcsetattr(0, TCSANOW, &restore); + return true; +} + std::vector Display::render(const Config& config, const colors_t& colors, const bool already_analyzed_file, const std::string_view path) { @@ -120,8 +167,6 @@ std::vector Display::render(const Config& config, const colors_t& c std::string line; std::vector pureAsciiArtLens; int maxLineLength = -1; - std::string image_backend_cmd; - // first check if the file is an image // without even using the same library that "file" uses @@ -133,12 +178,13 @@ std::vector Display::render(const Config& config, const colors_t& c fileToAnalyze.read(reinterpret_cast(&buffer.at(0)), buffer.size()); if (is_file_image(buffer.data())) { + std::uint32_t x = 0, y = 0; + get_pos(x, y); + fmt::print("\033[{};{}H", x, y); if (config.m_image_backend == "kitty") - { - image_backend_cmd = fmt::format("kitty +kitten icat --align left {}", path); - } - shell_exec(image_backend_cmd); - return render_with_image(config, colors); + taur_exec({ "kitty", "+kitten", "icat", "--align", "left", path.data() }); + + return render_with_image(config, colors, path); } /* die("The source file '{}' is a binary file.\n" "Please currently use the GUI mode for rendering the image/gif (use -h for more details)", @@ -251,7 +297,6 @@ std::vector Display::render(const Config& config, const colors_t& c } return layout; - } void Display::display(const std::vector& renderResult) diff --git a/src/util.cpp b/src/util.cpp index c594dd1..d95e13c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -366,6 +366,49 @@ bool read_exec(std::vector cmd, std::string& output, bool useStdErr return false; } +/** Executes commands with execvp() and keep the program running without existing + * @param cmd_str The command to execute + * @param exitOnFailure Whether to call exit(1) on command failure. + * @return true if the command successed, else false + */ +bool taur_exec(const std::vector cmd_str, const bool noerror_print) +{ + std::vector cmd; + for (const std::string_view str : cmd_str) + cmd.push_back(str.data()); + + int pid = fork(); + + if (pid < 0) + { + die("fork() failed: {}", strerror(errno)); + } + + if (pid == 0) + { + debug("running {}", cmd); + cmd.push_back(nullptr); + execvp(cmd[0], const_cast(cmd.data())); + + // execvp() returns instead of exiting when failed + die("An error has occurred: {}: {}", cmd[0], strerror(errno)); + } + else if (pid > 0) + { // we wait for the command to finish then start executing the rest + int status; + waitpid(pid, &status, 0); // Wait for the child to finish + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + return true; + else + { + if (!noerror_print) + error("Failed to execute the command: {}", fmt::join(cmd, " ")); + } + } + + return false; +} std::string str_tolower(std::string str) { for (char& x : str)