diff --git a/src/app/app.cpp b/src/app/app.cpp index 34fd613..14375e4 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -542,12 +542,17 @@ int App::run() { dlWasActive_ = false; } const bool dlRecent = browser_.downloadActive() || nowT < downloadToast_; + bool dlFailed = false; + for (const auto& j : browser_.downloadJobs()) { + if (j.state == 2) dlFailed = true; + } // No head-locked download text (it followed the view); progress lives // in the Downloads window and the control-bar badge. renderer_.setDownloadStatus(false, ""); renderer_.setDownloadsBadge(browser_.downloadActive(), !browser_.downloadActive() && - browser_.hasDownloads() && dlRecent); + browser_.hasDownloads() && dlRecent, + !browser_.downloadActive() && dlFailed); // Downloads window: job list plus action buttons at the bottom. std::vector drows; std::vector dbtns; diff --git a/src/gfx/vulkan_renderer.cpp b/src/gfx/vulkan_renderer.cpp index b4472e9..9754369 100644 --- a/src/gfx/vulkan_renderer.cpp +++ b/src/gfx/vulkan_renderer.cpp @@ -52,7 +52,7 @@ struct TextPush { // and the pointer raycast so the cursor and highlight line up exactly. constexpr float kPanelW = 1.0f; constexpr float kPanelTop = 0.8f; -constexpr float kRowH = 0.09f; +constexpr float kRowH = 0.105f; // Downloads-window action buttons (content-sized, along the bottom edge). constexpr float kDlBtnH = 0.11f; constexpr float kDlBtnGap = 0.05f; @@ -1617,10 +1617,17 @@ void VulkanRenderer::renderView(uint32_t viewIndex, uint32_t imageIndex, // Downloads button indicator: green tick when all done, or a pulsing // blue dot while downloads are active. if (static_cast(b) == CtrlButton::Downloads && - (downloadsActive_ || downloadsAllDone_)) { + (downloadsActive_ || downloadsAllDone_ || downloadsFailed_)) { const float badgeX = bx + kBtnHalfW * 0.55f; const float badgeY = kBarY + kBtnHalfH * 0.55f; - if (downloadsAllDone_) { + if (downloadsFailed_) { + const float failBg[4] = {0.80f, 0.15f, 0.15f, 1.0f}; + drawUiElement(vpPanel, badgeX, badgeY, kBtnHalfH * 0.55f, + kBtnHalfH * 0.55f, UiShape::RoundedBox, failBg, + 0.50f, 0.0f, nullptr); + drawUiElement(vpPanel, badgeX, badgeY, kBtnHalfH * 0.34f, + kBtnHalfH * 0.34f, UiShape::Close, iconCol); + } else if (downloadsAllDone_) { const float tickBg[4] = {0.10f, 0.65f, 0.25f, 1.0f}; drawUiElement(vpPanel, badgeX, badgeY, kBtnHalfH * 0.55f, kBtnHalfH * 0.55f, UiShape::RoundedBox, tickBg, diff --git a/src/gfx/vulkan_renderer.hpp b/src/gfx/vulkan_renderer.hpp index d9ddeb5..14896bd 100644 --- a/src/gfx/vulkan_renderer.hpp +++ b/src/gfx/vulkan_renderer.hpp @@ -209,9 +209,10 @@ public: // Badge on the control-bar Downloads button: a count while downloading, or a // green tick once all downloads are done (drives the on-button indicator). - void setDownloadsBadge(bool active, bool allDone) { + void setDownloadsBadge(bool active, bool allDone, bool failed = false) { downloadsActive_ = active; downloadsAllDone_ = allDone; + downloadsFailed_ = failed; } // Settings overlay panel (re-uses panel & pointer interaction) @@ -301,6 +302,7 @@ private: std::string downloadsStatus_; bool downloadsActive_ = false; bool downloadsAllDone_ = false; + bool downloadsFailed_ = false; bool downloadsModal_ = false; std::vector downloadsButtons_; int downloadsBtnHovered_ = -1; diff --git a/src/ui/browser.cpp b/src/ui/browser.cpp index 91e9d05..aba879f 100644 --- a/src/ui/browser.cpp +++ b/src/ui/browser.cpp @@ -478,12 +478,22 @@ void Browser::startDownloadFile(const std::string& uri, const std::string& local #endif } -// Forks a curl child that copies `uri` (smb://) to `local`; returns its pid. +// Forks a child that copies `uri` (smb://) to `local`; returns its pid. Uses +// Samba's own smbclient (same stack as our libsmbclient) so guest access matches. int Browser::spawnCurl(const std::string& uri, const std::string& local) { + std::string rest = uri; + if (rest.rfind("smb://", 0) == 0) rest = rest.substr(6); + const std::size_t s1 = rest.find('/'); + const std::size_t s2 = + (s1 == std::string::npos) ? std::string::npos : rest.find('/', s1 + 1); + if (s2 == std::string::npos) return -1; + const std::string service = "//" + rest.substr(0, s2); + const std::string remote = rest.substr(s2 + 1); + const std::string cmd = "get \"" + remote + "\" \"" + local + "\""; const pid_t pid = fork(); if (pid < 0) return -1; if (pid == 0) { - execlp("curl", "curl", "-sS", "--fail", "-o", local.c_str(), uri.c_str(), + execlp("smbclient", "smbclient", service.c_str(), "-N", "-c", cmd.c_str(), static_cast(nullptr)); _exit(127); }