From 23f13abca69263e2e667195a900d2d403f940203 Mon Sep 17 00:00:00 2001 From: Pyric Date: Tue, 18 Aug 2026 14:20:02 -0500 Subject: [PATCH 1/6] Updated flake.nix and added to gitignore --- .gitignore | 4 +++ flake.lock | 19 ++++++++++- flake.nix | 99 ++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 99 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index d237981..658b3d4 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,7 @@ AppDir .cache imgui.ini compile_commands.json + +# Flake.nix stuff +result +result-* diff --git a/flake.lock b/flake.lock index ed20e51..4ebc47d 100644 --- a/flake.lock +++ b/flake.lock @@ -16,9 +16,26 @@ "type": "github" } }, + "oshot-src": { + "flake": false, + "locked": { + "lastModified": 1787001618, + "narHash": "sha256-GDe/8Be0h/tEv854//FtmWfMhArc4wAjbKmBFMRlGQE=", + "owner": "Toni500github", + "repo": "oshot", + "rev": "122636db7327078d0759649c4f55c472aa67c6a9", + "type": "github" + }, + "original": { + "owner": "Toni500github", + "repo": "oshot", + "type": "github" + } + }, "root": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "oshot-src": "oshot-src" } } }, diff --git a/flake.nix b/flake.nix index 55f9c65..0ec4030 100644 --- a/flake.nix +++ b/flake.nix @@ -3,28 +3,83 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - }; - outputs = { self, nixpkgs }: { - packages.x86_64-linux.oshot = - with import nixpkgs { system = "x86_64-linux"; }; - stdenv.mkDerivation { - name = "oshot"; - src = self; - nativeBuildInputs = [ cmake gnumake pkg-config git ]; - buildInputs = [ glfw3 leptonica libx11.dev tesseract zbar.dev libappindicator-gtk3.dev dbus.dev systemd.dev libsysprof-capture pcre2.dev libxdmcp.dev libuuid.dev libselinux.dev libsepol.dev libthai.dev libdatrie.dev libdeflate lerc.dev xz.dev zstd.dev libwebp libxkbcommon.dev libepoxy.dev libxtst giflib ]; - configurePhase = '' - cmake -DCMAKE_BUILD_PREFIX=/usr -DDEBUG=0 -G "Unix Makefiles" -B build -S . - ''; - buildPhase = '' - cmake --build build -j$(nproc) - ''; - installPhase = '' - install -Dm755 build/oshot $out/bin/oshot - install -Dm644 oshot.desktop $out/share/applications/oshot.desktop - install -Dm644 LICENSE $out/share/licenses/oshot/LICENSE - ''; - }; - packages.x86_64-linux.default = self.packages.x86_64-linux.oshot; + oshot-src = { + url = "github:Toni500github/oshot"; + flake = false; + }; }; + + outputs = { self, nixpkgs, oshot-src }: + let + supportedSystems = [ "x86_64-linux" ]; + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); + in + { + packages = forAllSystems (system: + let + pkgs = nixpkgsFor.${system}; + in + { + oshot = pkgs.stdenv.mkDerivation { + pname = "oshot"; + version = "0.1.0"; + + src = oshot-src; + + nativeBuildInputs = with pkgs; [ + cmake + gnumake + pkg-config + git + wrapGAppsHook3 + ]; + + buildInputs = with pkgs; [ + glfw3 + leptonica + tesseract + zbar + gtk3 + libappindicator-gtk3 + dbus + systemd + libX11 + libXtst + giflib + libwebp + libsysprof-capture + libXdmcp + zenity + ]; + + # Initialize git AND add a tag so `git describe` works + preConfigure = '' + git init + git config user.name "Nix" + git config user.email "nix@localhost" + git add . + git commit -m "dummy commit for build" + git tag -a v0.1.0 -m "v0.1.0" + ''; + + installPhase = '' + runHook preInstall + + install -Dm755 oshot $out/bin/oshot + if [ -f oshot.desktop ]; then + install -Dm644 oshot.desktop $out/share/applications/oshot.desktop + fi + if [ -f LICENSE ]; then + install -Dm644 LICENSE $out/share/licenses/oshot/LICENSE + fi + + runHook postInstall + ''; + }; + + default = self.packages.${system}.oshot; + }); + }; } From dd5173941de631bcb74943bea1670ee07cefc781 Mon Sep 17 00:00:00 2001 From: Toni500git Date: Fri, 21 Aug 2026 19:53:34 +0200 Subject: [PATCH 2/6] config: follow only the 'theme-file' + change the default look of oshot Shootout @PyricRL on GitHub for the new oshot look :) --- include/config.hpp | 1 - include/screenshot_tool.hpp | 7 +- include/texts.hpp | 127 +++++++++++++++---------------- include/util.hpp | 5 ++ src/config.cpp | 13 ++-- src/main_tool_metal.mm | 1 - src/main_tool_opengl3.cpp | 1 - src/screenshot_tool.cpp | 144 ++++++++++++++++++++++++------------ 8 files changed, 172 insertions(+), 127 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 4ce4000..376f664 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -55,7 +55,6 @@ class Config : public TomlAPI #endif std::string ocr_get_repo = "tesseract-ocr/tessdata"; std::string ocr_model = "eng"; - std::string theme_style = "auto"; std::string theme_file_path = "theme.toml"; std::string image_out_fmt = "oshot_{:%F_%H-%M}"; std::string image_out_size_fmt = "auto"; diff --git a/include/screenshot_tool.hpp b/include/screenshot_tool.hpp index b04e788..c216337 100644 --- a/include/screenshot_tool.hpp +++ b/include/screenshot_tool.hpp @@ -192,7 +192,7 @@ struct annotation_t point_t start; point_t end; std::string text; // For text tool - std::uint8_t count = 0; // For CounterBubble tool + unsigned int count = 0; // For CounterBubble tool std::vector points; // For pencil tool rgba_t color = rgba_t(0xFF0000FF); // RGBA float thickness = 3.0f; @@ -335,8 +335,9 @@ class ScreenshotTool ImFont* CacheAndGetFont(const std::string& font_name, const float font_size); - void RenderOverlay(); - void Cancel(); + void RenderOverlay(); + void Cancel(); + static void StyleDefaultColor(); template void SetError(ErrorContext& ctx, Enum e, const std::string_view err = "") diff --git a/include/texts.hpp b/include/texts.hpp index 8fd3bc8..51f8e34 100644 --- a/include/texts.hpp +++ b/include/texts.hpp @@ -183,33 +183,26 @@ image-out-fmt = "{}" # Size format of the capture in the selected image # extension from image-out-ext, shown under the capture size widget. # Case Sensitive. -# Accepts: "off", "auto", "B", "KiB", "MiB", "KB", "MB", more... +# Accepts: "off", "auto", "B", "KiB", "MiB", "KB", "MB", more... image-out-size-ind = "{}" -# Base UI theme: "auto" (follow OS dark/light), "dark", "light", or "classic". -# Fine-grained overrides live in theme.toml. -theme = "{}" - # Path to a theme file. Absolute or relative to this config's directory. -# Delete or comment out to use only the base theme above. theme-file = "{}" )#"; -inline constexpr std::string_view AUTOTHEME = (R"( -# Drop this next to config.toml or point theme-file at its path. -# All sections and keys are optional — omit anything you don't want to override. +inline constexpr std::string_view AUTOTHEME = (R"([theme] +smooth-animations = true -[theme] -smooth-animations = false +# All sections and keys are optional. Omit anything you don't want to override. # --------------------------------------------------------------- # Rounding (pixels, 0 = sharp corners, max ~12) # --------------------------------------------------------------- [theme.style] -window-rounding = 8.0 +window-rounding = 4.0 frame-rounding = 4.0 -grab-rounding = 4.0 -tab-rounding = 4.0 +grab-rounding = 1.5 +tab-rounding = 2.0 # Border width in pixels. 0 = none, 1 = thin line. window-border = 1.0 @@ -226,59 +219,59 @@ frame-border = 0.0 # (search for "GetStyleColorName") # --------------------------------------------------------------- [theme.colors] -# --- Text --- -Text = "#cdd6f4FF" -TextDisabled = "#6c7086FF" - -# --- Backgrounds --- -WindowBg = "#1e1e2eFF" -ChildBg = "#181825FF" -PopupBg = "#1e1e2eFF" -FrameBg = "#313244FF" -FrameBgHovered = "#45475aFF" -FrameBgActive = "#585b70FF" -MenuBarBg = "#181825FF" - -# --- Title bar --- -TitleBg = "#181825FF" -TitleBgActive = "#313244FF" - -# --- Borders --- -Border = "#585b70FF" -BorderShadow = "#00000000" - -# --- Scrollbar --- -ScrollbarBg = "#181825FF" -ScrollbarGrab = "#585b70FF" -ScrollbarGrabHovered = "#6c7086FF" -ScrollbarGrabActive = "#7f849cFF" - -# --- Buttons --- -Button = "#313244FF" -ButtonHovered = "#45475aFF" -ButtonActive = "#585b70FF" - -# --- Headers (selectables, tree nodes, collapsing headers) --- -Header = "#313244FF" -HeaderHovered = "#45475aFF" -HeaderActive = "#585b70FF" - -# --- Sliders / checkmarks --- -CheckMark = "#cba6f7FF" -SliderGrab = "#cba6f7FF" -SliderGrabActive = "#b4befeff" - -# --- Tabs --- -Tab = "#313244FF" -TabHovered = "#cba6f7FF" -TabSelected = "#45475aFF" - -# --- Misc --- -Separator = "#585b70FF" -ResizeGrip = "#cba6f7FF" -ResizeGripHovered = "#cba6f7FF" -ResizeGripActive = "#cba6f7FF" -PlotHistogram = "#2ba2f0FF" # original: "#e6b300FF" + +# Main text +Text = "#DCDDE1FF" +TextDisabled = "#666870FF" + +# Main application backgrounds +WindowBg = "#0D1015FF" +ChildBg = "#0D1015FF" +PopupBg = "#10131AFF" + +# Borders +Border = "#242933FF" + +# Title bars +TitleBg = "#090B0FFF" +TitleBgActive = "#5274F0FF" + +# Input fields and other framed widgets +FrameBg = "#151920FF" +FrameBgHovered = "#202630FF" +FrameBgActive = "#29313EFF" + +# Buttons +Button = "#294CC7FF" +ButtonHovered = "#385DE0FF" +ButtonActive = "#203CA6FF" + +# Headers +Header = "#1A1F28FF" +HeaderHovered = "#252C38FF" +HeaderActive = "#303946FF" + +# Tabs +Tab = "#10141BFF" +TabHovered = "#252E42FF" +TabSelected = "#294CC7FF" + +# Sliders +SliderGrab = "#294CC7FF" +SliderGrabActive = "#5274F0FF" + +# Scrollbars +ScrollbarBg = "#07090CFF" +ScrollbarGrab = "#2A303AFF" + +# Checkboxes and other selection indicators +CheckMark = "#5274F0FF" + +# Menu bar +MenuBarBg = "#090B0FFF" + +# Graphs / histogram elements +PlotHistogram = "#5274F0FF" )"); inline constexpr std::string_view AUTO_MANIFEST = R"([repository] diff --git a/include/util.hpp b/include/util.hpp index 8fd9700..3fda4b4 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -299,6 +299,11 @@ inline rgba_t load_rgba(const uint8_t* p) return rgba_t(p[0], p[1], p[2], p[3]); } +constexpr rgba_t operator""_rgba(unsigned long long v) +{ + return rgba_t(static_cast(v)); +} + inline void store_rgba(uint8_t* p, const rgba_t& c) { p[0] = c.r; diff --git a/src/config.cpp b/src/config.cpp index bd68c3f..68bca37 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -60,7 +60,6 @@ void Config::LoadConfigFile(const std::string& filename) File.ocr_path = GetValue("default.ocr-path", File.ocr_path); File.ocr_get_repo = GetValue("default.ocr-repo-downlaod", "tesseract-ocr/tessdata"); File.ocr_model = GetValue("default.ocr-model", "eng"); - File.theme_style = GetValue("default.theme", "auto"); File.theme_file_path = GetValue("default.theme-file", ""); File.image_out_fmt = GetValue("default.image-out-fmt", "oshot_{:%F_%H-%M}"); File.delay = GetValue("default.delay", -1); @@ -97,6 +96,9 @@ void Config::LoadConfigFile(const std::string& filename) File.image_out_size_fmt = GetValue("default.image-out-size-ind", "auto"); if (std::find(prefixes.begin(), prefixes.end(), File.image_out_size_fmt) == prefixes.end()) File.image_out_size_fmt = "auto"; + + if (fs::path(g_config->File.theme_file_path).is_relative()) + g_config->File.theme_file_path.insert(0, m_config_dir_path + "/"); } void Config::LoadThemeFile(const std::string& filename) @@ -168,7 +170,6 @@ void Config::GenerateConfig(const std::string& filename, const bool force) File.image_out_type.first, File.image_out_fmt, File.image_out_size_fmt, - File.theme_style, File.theme_file_path); } @@ -188,12 +189,10 @@ void Config::GenerateTheme(const std::string& filename, const bool force) } theme_overrides_t& ov = theme_overrides; - f.print(R"( -# Drop this next to config.toml or point theme-file at its path. -# All sections and keys are optional — omit anything you don't want to override. + f.print(R"([theme] +smooth-animations = true -[theme] -smooth-animations = {} +# All sections and keys are optional. Omit anything you don't want to override. # --------------------------------------------------------------- # Rounding (pixels, 0 = sharp corners, max ~12) diff --git a/src/main_tool_metal.mm b/src/main_tool_metal.mm index bbcad1a..55dc73e 100644 --- a/src/main_tool_metal.mm +++ b/src/main_tool_metal.mm @@ -173,7 +173,6 @@ int run_main_tool() // ImGui setup IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGui::StyleColorsDark(); const std::string ini = (get_config_dir() / "imgui.ini").string(); ImGuiIO& io = ImGui::GetIO(); diff --git a/src/main_tool_opengl3.cpp b/src/main_tool_opengl3.cpp index 489fc44..3c748c2 100644 --- a/src/main_tool_opengl3.cpp +++ b/src/main_tool_opengl3.cpp @@ -214,7 +214,6 @@ int run_main_tool() // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGui::StyleColorsDark(); const std::string ini = (get_config_dir() / "imgui.ini").string(); ImGuiIO& io = ImGui::GetIO(); diff --git a/src/screenshot_tool.cpp b/src/screenshot_tool.cpp index 72cb0df..494f06c 100644 --- a/src/screenshot_tool.cpp +++ b/src/screenshot_tool.cpp @@ -95,18 +95,19 @@ inline rgba_t blend(rgba_t src, rgba_t dst) uint8_t(src.a + dst.a * ia) }; } -static void get_training_data_list(const std::string& datadir, std::vector& langs) +static void get_filtered_filenames(const std::string& dir, + std::vector& list, + std::function filter, + std::function value_push) { - langs.clear(); - - if (!fs::exists(datadir)) + if (!fs::exists(dir)) return; for (const auto& entry : fs::recursive_directory_iterator( - datadir, fs::directory_options::follow_directory_symlink | fs::directory_options::skip_permission_denied)) + dir, fs::directory_options::follow_directory_symlink | fs::directory_options::skip_permission_denied)) { - if (entry.path().extension() == ".traineddata") - langs.push_back(entry.path().stem().string()); + if (filter(entry.path())) + list.emplace_back(value_push(entry.path())); } } @@ -157,9 +158,18 @@ static void draw_input_text_path(const char* label, { minimize_window(); - const char* dialog_path = is_file - ? tinyfd_openFileDialog("Open file", nullptr, filter_count, filters, nullptr, 0) - : tinyfd_selectFolderDialog("Open folder", nullptr); + const fs::path default_path = path; + + std::string start_path; + if (is_file && default_path.has_parent_path()) + { + start_path = default_path.parent_path().string(); + start_path += '/'; // tinyfd treats a trailing separator as "open here" + } + + const char* dialog_path = + !is_file ? tinyfd_selectFolderDialog("Open folder", nullptr) + : tinyfd_openFileDialog("Open file", start_path.c_str(), filter_count, filters, nullptr, 0); maximize_window(); @@ -174,7 +184,7 @@ static void draw_input_text_path(const char* label, ImGui::PopID(); ImGui::SameLine(0, 3); - ImGui::Text("%s", label); + ImGui::TextUnformatted(label); } static void draw_input_text_file(const char* label, @@ -278,21 +288,7 @@ static std::unordered_map& color_name_map() void apply_imgui_theme() { - const std::string& base = g_config->File.theme_style; - - if (base == "classic") - { - ImGui::StyleColorsClassic(); - } - else if (base == "light") - { - ImGui::StyleColorsLight(); - } - else - { - bool dark = (base == "dark") || (base == "auto" && is_system_dark_mode()); - dark ? ImGui::StyleColorsDark() : ImGui::StyleColorsLight(); - } + is_system_dark_mode() ? ScreenshotTool::StyleDefaultColor() : ImGui::StyleColorsLight(); ImGuiStyle& style = ImGui::GetStyle(); const auto& ov = g_config->theme_overrides; @@ -1861,13 +1857,11 @@ void ScreenshotTool::DrawAnnotationToolbar() static void draw_preference_edit_config(const std::function& refresh_models_func, bool window_just_opened) { static const char* image_prev_units[] = { "off", "auto", "B", "KiB", "MiB", "KB", "MB" }; - static const char* themes_names[] = { "auto", "light", "dark", "classic" }; static const char* font_filters[] = { "*.ttf", "*.otf", "*.ttc", "*.woff", "*.woff2" }; static const char* toml_filters[] = { "*.toml" }; static int image_ext_sel = 0; static int image_preuni_sel = 0; - static int theme_selected = 0; static std::string new_font; static Result r = get_config_image_out_fmt(); static std::vector resolved_font_paths; @@ -1908,29 +1902,22 @@ static void draw_preference_edit_config(const std::function& refresh_mod ImGui::Spacing(); // --- Theme settings --- - if (window_just_opened) - { - const std::string& ts = g_config->File.theme_style; - if (ts == "light") - theme_selected = 1; - else if (ts == "dark") - theme_selected = 2; - else if (ts == "classic") - theme_selected = 3; - else - theme_selected = 0; - } - ImGui::Text("Default Theme style"); - if (ImGui::Combo("##config_theme_style", &theme_selected, themes_names, IM_ARRAYSIZE(themes_names))) - g_config->File.theme_style = themes_names[theme_selected]; - ImGui::Spacing(); - ImGui::Text("Default Theme file path"); ImGui::SameLine(); HelpMarker( "Full path to the theme file (aka. theme.toml), or relative to the config directory. Supports drag-and-drop."); draw_input_text_file( - "", "##config_theme_file_path", toml_filters, 1, [] { /* applied on Save */ }, g_config->File.theme_file_path); + "", + "##config_theme_file_path", + toml_filters, + 1, + [&] { + if (fs::path(g_config->File.theme_file_path).is_relative()) + g_config->File.theme_file_path.insert(0, get_config_dir().string() + "/"); + }, + g_config->File.theme_file_path); + if (!fs::exists(g_config->File.theme_file_path)) + ImGui::TextColored(rgba_t(0xff7444FF).to_imvec4(), "File doesn't exist, fallback to default hardcoded theme"); ImGui::Spacing(); ImGui::Text("Color picker style"); @@ -4128,7 +4115,11 @@ void ScreenshotTool::RefreshOcrModels() return; } - get_training_data_list(m_inputs.ocr_path, m_ocr_models_list); + get_filtered_filenames( + m_inputs.ocr_path, + m_ocr_models_list, + [](const fs::path& entry) { return entry.extension().string() == ".traineddata"; }, + [](const fs::path& entry) { return entry.stem().string(); }); m_last_scanned_ocr_path = m_inputs.ocr_path; ClearError(ectx, OcrError::NeedToScanDir); @@ -4147,6 +4138,65 @@ void ScreenshotTool::RefreshOcrModels() } } +void ScreenshotTool::StyleDefaultColor() +{ + // clang-format off + static const std::unordered_map theme_colors = { + { ImGuiCol_Text, 0xDCDDE1FF_rgba }, + { ImGuiCol_TextDisabled, 0x666870FF_rgba }, + { ImGuiCol_WindowBg, 0x0D1015FF_rgba }, + { ImGuiCol_ChildBg, 0x0D1015FF_rgba }, + { ImGuiCol_PopupBg, 0x10131AFF_rgba }, + + { ImGuiCol_Border, 0x242933FF_rgba }, + + { ImGuiCol_TitleBg, 0x090B0FFF_rgba }, + { ImGuiCol_TitleBgActive, 0x5274F0FF_rgba }, + + { ImGuiCol_FrameBg, 0x151920FF_rgba }, + { ImGuiCol_FrameBgHovered,0x202630FF_rgba }, + { ImGuiCol_FrameBgActive, 0x29313EFF_rgba }, + + { ImGuiCol_Button, 0x294CC7FF_rgba }, + { ImGuiCol_ButtonHovered, 0x385DE0FF_rgba }, + { ImGuiCol_ButtonActive, 0x203CA6FF_rgba }, + + { ImGuiCol_Header, 0x1A1F28FF_rgba }, + { ImGuiCol_HeaderHovered, 0x252C38FF_rgba }, + { ImGuiCol_HeaderActive, 0x303946FF_rgba }, + + { ImGuiCol_Tab, 0x10141BFF_rgba }, + { ImGuiCol_TabHovered, 0x252E42FF_rgba }, + { ImGuiCol_TabSelected, 0x294CC7FF_rgba }, + + { ImGuiCol_SliderGrab, 0x294CC7FF_rgba }, + { ImGuiCol_SliderGrabActive, 0x5274F0FF_rgba }, + + { ImGuiCol_ScrollbarBg, 0x07090CFF_rgba }, + { ImGuiCol_ScrollbarGrab, 0x2A303AFF_rgba }, + + { ImGuiCol_CheckMark, 0x5274F0FF_rgba }, + { ImGuiCol_MenuBarBg, 0x090B0FFF_rgba }, + { ImGuiCol_PlotHistogram, 0x5274F0FF_rgba }, + }; + // clang-format on + + ImGui::StyleColorsDark(); + + auto& style = ImGui::GetStyle(); + + style.WindowRounding = 4.0f; + style.FrameRounding = 4.0f; + style.GrabRounding = 1.5f; + style.TabRounding = 2.0f; + + style.WindowBorderSize = 1.0f; + style.FrameBorderSize = 0.0f; + + for (const auto& [color, rgba] : theme_colors) + style.Colors[color] = rgba.to_imvec4(); +} + Result ScreenshotTool::CreateTexture(void* tex, std::span data, int w, int h) { #if OSHOT_MACOS From f42385c0c1549f932b1e1c7c531f4739be9fcfc7 Mon Sep 17 00:00:00 2001 From: Toni500git Date: Sun, 23 Aug 2026 20:53:30 +0200 Subject: [PATCH 3/6] config: remove redundant functions --- include/config.hpp | 24 ------------------------ src/config.cpp | 17 ++++++++--------- src/main.cpp | 3 ++- src/main_tool_metal.mm | 1 + src/main_tool_opengl3.cpp | 1 + 5 files changed, 12 insertions(+), 34 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 376f664..32a6412 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -30,7 +30,6 @@ #include #include -#include "fmt/format.h" #include "toml_api.hpp" #include "util.hpp" @@ -135,28 +134,6 @@ class Config : public TomlAPI using TomlAPI::GetValue; using TomlAPI::SetValue; - template - T GetThemeValue(const std::string_view value, const T& fallback, bool dont_expand_var = true) const - { - return m_theme.GetValue(fmt::format("theme.{}", value), fallback, dont_expand_var); - } - - template - T GetThemeStyleValue(const std::string_view value, const T& fallback, bool dont_expand_var = true) const - { - return m_theme.GetValue(fmt::format("theme.style.{}", value), fallback, dont_expand_var); - } - - uint32_t GetThemeColorValue(const std::string_view value, - const std::string& fallback, - bool dont_expand_var = true) const - { - uint32_t out; - hexstr_to_col(m_theme.GetValue(fmt::format("theme.colors.{}", value), fallback, dont_expand_var), - out); - return out; - } - const std::string& GetConfigPath() const { return m_config_path; } const std::string& GetThemePath() const { return m_theme_path; } const std::string& GetConfigDirPath() const { return m_config_dir_path; } @@ -172,5 +149,4 @@ class Config : public TomlAPI extern std::unique_ptr g_config; -void apply_imgui_theme(); #endif // !_CONFIG_HPP_ diff --git a/src/config.cpp b/src/config.cpp index 68bca37..d7904a2 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -122,17 +122,16 @@ void Config::LoadThemeFile(const std::string& filename) if (const toml::table* colors = m_theme.GetTbl().at_path("theme.colors").as_table()) { colors->for_each( - [&](const toml::key& k, const toml::value& v) { ov.colors[std::string(k.str())] = v.get(); }); + [&](const toml::key& k, const toml::value& v) { ov.colors[k.str().data()] = v.get(); }); } - ov.window_rounding = GetThemeStyleValue("window-rounding", -1.f); - ov.frame_rounding = GetThemeStyleValue("frame-rounding", -1.f); - ov.grab_rounding = GetThemeStyleValue("grab-rounding", -1.f); - ov.tab_rounding = GetThemeStyleValue("tab-rounding", -1.f); - ov.window_border = GetThemeStyleValue("window-border", -1.f); - ov.frame_border = GetThemeStyleValue("frame-border", -1.f); - - ov.smooth_animations = GetThemeValue("smooth-animations", false); + ov.smooth_animations = m_theme.GetValue("smooth-animations", false); + ov.window_rounding = m_theme.GetValue("window-rounding", -1.f); + ov.frame_rounding = m_theme.GetValue("frame-rounding", -1.f); + ov.grab_rounding = m_theme.GetValue("grab-rounding", -1.f); + ov.tab_rounding = m_theme.GetValue("tab-rounding", -1.f); + ov.window_border = m_theme.GetValue("window-border", -1.f); + ov.frame_border = m_theme.GetValue("frame-border", -1.f); } void Config::GenerateConfig(const std::string& filename, const bool force) diff --git a/src/main.cpp b/src/main.cpp index 396f56a..4ba2e2f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,6 +69,7 @@ #include "util.hpp" using namespace Tray; +using namespace std::chrono_literals; // Avoid dragging glfw headers struct GLFWwindow; @@ -436,7 +437,7 @@ int main(int argc, char* argv[]) logger.info("=== oshot starting ==="); logger.info("Log file path: {}", file->filename()); logger.flush(); - spdlog::flush_every(std::chrono::seconds(1)); + spdlog::flush_every(1s); const bool tray_lock_acquired = acquire_tray_lock(); diff --git a/src/main_tool_metal.mm b/src/main_tool_metal.mm index 55dc73e..5c55d60 100644 --- a/src/main_tool_metal.mm +++ b/src/main_tool_metal.mm @@ -48,6 +48,7 @@ # include "tool_icons.h" # include "util.hpp" +void apply_imgui_theme(); void glfw_error_callback(int error, const char* description); void glfw_drop_callback(GLFWwindow*, int count, const char** paths); void register_window_callbacks(void (*minimize_fn)(), diff --git a/src/main_tool_opengl3.cpp b/src/main_tool_opengl3.cpp index 3c748c2..397252d 100644 --- a/src/main_tool_opengl3.cpp +++ b/src/main_tool_opengl3.cpp @@ -48,6 +48,7 @@ GLFWwindow* window = nullptr; +void apply_imgui_theme(); void glfw_error_callback(int i_error, const char* description); void glfw_drop_callback(GLFWwindow*, int count, const char** paths); void register_window_callbacks(void (*minimize_fn)(), From 7a514bd77c59532e4ac18d7e9a015eb97bd34f4e Mon Sep 17 00:00:00 2001 From: Toni500git Date: Sun, 23 Aug 2026 22:29:05 +0200 Subject: [PATCH 4/6] chore: use g_config->GetConfigDirPath() instead --- src/main_tool_metal.mm | 2 +- src/main_tool_opengl3.cpp | 2 +- src/screenshot_tool.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main_tool_metal.mm b/src/main_tool_metal.mm index 5c55d60..58eaece 100644 --- a/src/main_tool_metal.mm +++ b/src/main_tool_metal.mm @@ -175,7 +175,7 @@ int run_main_tool() IMGUI_CHECKVERSION(); ImGui::CreateContext(); - const std::string ini = (get_config_dir() / "imgui.ini").string(); + const std::string ini = g_config->GetConfigDirPath() + "/imgui.ini"; ImGuiIO& io = ImGui::GetIO(); io.IniFilename = ini.c_str(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; diff --git a/src/main_tool_opengl3.cpp b/src/main_tool_opengl3.cpp index 397252d..0e86a93 100644 --- a/src/main_tool_opengl3.cpp +++ b/src/main_tool_opengl3.cpp @@ -216,7 +216,7 @@ int run_main_tool() IMGUI_CHECKVERSION(); ImGui::CreateContext(); - const std::string ini = (get_config_dir() / "imgui.ini").string(); + const std::string ini = g_config->GetConfigDirPath() + "/imgui.ini"; ImGuiIO& io = ImGui::GetIO(); io.IniFilename = ini.c_str(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; diff --git a/src/screenshot_tool.cpp b/src/screenshot_tool.cpp index 494f06c..7716f87 100644 --- a/src/screenshot_tool.cpp +++ b/src/screenshot_tool.cpp @@ -379,7 +379,7 @@ Result<> ScreenshotTool::StartWindow() g_config->File.ocr_model, g_config->File.ocr_get_repo, #if defined(__unix__) && !defined(__APPLE__) - get_config_dir() / "models", + g_config->GetConfigDirPath() + "/models", #else "./models", #endif @@ -1913,7 +1913,7 @@ static void draw_preference_edit_config(const std::function& refresh_mod 1, [&] { if (fs::path(g_config->File.theme_file_path).is_relative()) - g_config->File.theme_file_path.insert(0, get_config_dir().string() + "/"); + g_config->File.theme_file_path.insert(0, g_config->GetConfigDirPath()); }, g_config->File.theme_file_path); if (!fs::exists(g_config->File.theme_file_path)) From 9010ef985e117feaef8932a9cce002b7dd78702a Mon Sep 17 00:00:00 2001 From: Toni500git Date: Sun, 23 Aug 2026 22:36:48 +0200 Subject: [PATCH 5/6] util: flush GTK events after nvdialog close GDK queues the dialog's surface destroy/unmap request rather than sending it immediately. Since oshot never runs a GTK main loop after create_dialog() returns, that request stayed unflushed and Wayland compositors kept rendering the last frame until the whole process exited, even though the dialog had already been dismissed. Pump gtk_events_pending()/gtk_main_iteration() right after nvd_free_object() to force the flush. --- include/util.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/util.hpp b/include/util.hpp index 3fda4b4..dc21f36 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -252,6 +252,12 @@ struct capture_result_t; struct ImVec4; struct ImGuiIO; enum class ImageExt; +// I'm not including just for a couple of functions +extern "C" { +using gboolean = int32_t; +gboolean gtk_events_pending(); +gboolean gtk_main_iteration(); +} // taken from "fmt/color.h" with the addition of alpha. // useful in contexts where ImVec4 is not used. @@ -407,6 +413,10 @@ static void create_dialog(const char* title, const NvdDialogType type, const std NvdDialogBox* dialog = nvd_dialog_box_new(title, str.c_str(), type); nvd_show_dialog(dialog); nvd_free_object(dialog); +#if OSHOT_LINUX + while (gtk_events_pending()) + gtk_main_iteration(); +#endif } template From 101842a143c8517ebbf604df9b2be575edb0fcbb Mon Sep 17 00:00:00 2001 From: Pyric <97044307+PyricRL@users.noreply.github.com> Date: Sun, 30 Aug 2026 17:33:11 -0500 Subject: [PATCH 6/6] Update flake.nix --- flake.nix | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/flake.nix b/flake.nix index 0ec4030..b3abac9 100644 --- a/flake.nix +++ b/flake.nix @@ -28,12 +28,107 @@ src = oshot-src; + nativeBuildInputs = with pkgs; [ + cmake + gnumake + pkg-config + git{ + description = "Nix flake for oshot; a simple and lightweight tool for extracting text from a screenshot/image (on the fly)"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + supportedSystems = [ "x86_64-linux" ]; + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); + in + { + packages = forAllSystems (system: + let + pkgs = nixpkgsFor.${system}; + in + { + oshot = pkgs.stdenv.mkDerivation { + pname = "oshot"; + version = "0.5.0-rc1"; + + src = ./.; + nativeBuildInputs = with pkgs; [ cmake gnumake pkg-config git wrapGAppsHook3 + wayland-scanner + ]; + + buildInputs = with pkgs; [ + glfw3 + leptonica + tesseract + zbar + gtk3 + libappindicator-gtk3 + dbus + systemd + libX11 + libXtst + giflib + libwebp + libsysprof-capture + libXdmcp + zenity + pcre2 + ]; + + # Initialize git AND add a tag so `git describe` works + preConfigure = '' + export SOURCE_DATE_EPOCH=1 + export GIT_AUTHOR_DATE="1970-01-01T00:00:01Z" + export GIT_COMMENTOR_DATE="1970-01-01T00:00:01Z" + + git init -b nix-build + git config user.name "Nix" + git config user.email "nix@localhost" + git add . + git commit -m "dummy commit for build" + git tag -a v0.1.0 -m "v0.1.0" + ''; + + installPhase = '' + runHook preInstall + + install -Dm755 oshot $out/bin/oshot + if [ -f oshot.desktop ]; then + install -Dm644 oshot.desktop $out/share/applications/oshot.desktop + fi + if [ -f LICENSE ]; then + install -Dm644 LICENSE $out/share/licenses/oshot/LICENSE + fi + + runHook postInstall + ''; + }; + + default = self.packages.${system}.oshot; + }); + + nixosModules.default = { config, lib, pkgs, ... }: { + options.programs.oshot.enable = lib.mkEnableOption "oshot text extraction and screenshot tool"; + + config = lib.mkIf config.programs.oshot.enable { + environment.systemPackages = [ + self.packages.${pkgs.system}.oshot + ]; + }; + }; + }; +} + wrapGAppsHook3 ]; buildInputs = with pkgs; [