fix(ui): stop spurious "Blockchain rescan complete" toast after restoring from minimize
While minimized the main loop skips the frame body (SDL_Delay+continue), so App::update() — which drains the daemon output via outputSince(daemon_output_offset_) — never runs and the offset isn't advanced. On restore the whole accumulated backlog is parsed in one batch: a background witness rebuild's progress lines (parsed as a rescan → state_.sync.rescanning=true) AND its "rebuilt … in Xms" completion (parsed as finished) arrive together and fire "Blockchain rescan complete" for a scan the user never initiated. On WINDOW_RESTORED, discard the daemon-output backlog (advance the offset to the current end, via App::skipDaemonOutputBacklog) before the resumed update parses it — so only new output is parsed. Genuine user-initiated rescans still surface completion via the getrescaninfo monitor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
12
src/app.cpp
12
src/app.cpp
@@ -5047,6 +5047,18 @@ void App::refreshNow()
|
|||||||
invalidateShieldedHistoryScanProgress(true);
|
invalidateShieldedHistoryScanProgress(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::skipDaemonOutputBacklog()
|
||||||
|
{
|
||||||
|
// While minimized, App::update() is paused, so daemon_output_offset_ is never advanced and a large
|
||||||
|
// backlog of daemon output piles up. Parsing it all at once on restore would replay a background
|
||||||
|
// witness rebuild's progress + completion in a single batch and fire a spurious "Blockchain rescan
|
||||||
|
// complete" toast. Advance the offset to the current end so only NEW (post-restore) output is parsed.
|
||||||
|
// A genuine user-initiated rescan still surfaces completion via the getrescaninfo monitor.
|
||||||
|
if (daemon_controller_ && daemon_controller_->isRunning()) {
|
||||||
|
(void)daemon_controller_->outputSince(daemon_output_offset_); // advances daemon_output_offset_ to the end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void App::handlePaymentURI(const std::string& uri)
|
void App::handlePaymentURI(const std::string& uri)
|
||||||
{
|
{
|
||||||
auto payment = util::parsePaymentURI(uri);
|
auto payment = util::parsePaymentURI(uri);
|
||||||
|
|||||||
@@ -393,6 +393,10 @@ public:
|
|||||||
|
|
||||||
// Force refresh
|
// Force refresh
|
||||||
void refreshNow();
|
void refreshNow();
|
||||||
|
// Called on window restore: drop the daemon-output backlog that accumulated while minimized (the
|
||||||
|
// per-frame update loop was paused), so a background witness rebuild that started+finished during
|
||||||
|
// the minimize isn't parsed in one batch and mistaken for a completed rescan (spurious toast).
|
||||||
|
void skipDaemonOutputBacklog();
|
||||||
void refreshMiningInfo();
|
void refreshMiningInfo();
|
||||||
void refreshPeerInfo();
|
void refreshPeerInfo();
|
||||||
void refreshMarketData();
|
void refreshMarketData();
|
||||||
|
|||||||
33
src/main.cpp
33
src/main.cpp
@@ -1263,6 +1263,36 @@ int main(int argc, char* argv[])
|
|||||||
SDL_SetWindowMinimumSize(window, (int)(1024 * currentDpiScale), (int)(720 * currentDpiScale));
|
SDL_SetWindowMinimumSize(window, (int)(1024 * currentDpiScale), (int)(720 * currentDpiScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEV/TEST hook (dormant unless the env is set): DRAGONX_WIN_GEOM="WxH" forces an exact window
|
||||||
|
// size, placing it on the largest display that can hold it and bypassing the primary-monitor
|
||||||
|
// clamp. Lets a headless WSLg sweep render at sizes wider than the 1280 primary (e.g. 2560x1440
|
||||||
|
// on the mirrored 4K/1440p outputs). Needs the x11 backend so absolute positioning takes effect.
|
||||||
|
int wantW = 0, wantH = 0;
|
||||||
|
if (const char* geom = std::getenv("DRAGONX_WIN_GEOM"))
|
||||||
|
sscanf(geom, "%dx%d", &wantW, &wantH);
|
||||||
|
if (wantW > 0 && wantH > 0) {
|
||||||
|
int count = 0;
|
||||||
|
SDL_DisplayID* disp = SDL_GetDisplays(&count);
|
||||||
|
SDL_DisplayID best = 0; SDL_Rect bestUsable{0, 0, 0, 0};
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
SDL_Rect u;
|
||||||
|
if (!SDL_GetDisplayUsableBounds(disp[i], &u)) continue;
|
||||||
|
bool fits = (u.w >= wantW && u.h >= wantH);
|
||||||
|
bool bestFits = (bestUsable.w >= wantW && bestUsable.h >= wantH);
|
||||||
|
// Prefer a display that fits; among those, the smallest; else the largest available.
|
||||||
|
if ((fits && !bestFits) ||
|
||||||
|
(fits && bestFits && (long)u.w * u.h < (long)bestUsable.w * bestUsable.h) ||
|
||||||
|
(!fits && !bestFits && (long)u.w * u.h > (long)bestUsable.w * bestUsable.h)) {
|
||||||
|
bestUsable = u; best = disp[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (disp) SDL_free(disp);
|
||||||
|
if (best) {
|
||||||
|
SDL_SetWindowMinimumSize(window, 320, 240);
|
||||||
|
SDL_SetWindowPosition(window, bestUsable.x + 10, bestUsable.y + 10);
|
||||||
|
SDL_SetWindowSize(window, wantW, wantH);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Clamp to the current display's work area — runs on EVERY startup (this clamp used to live
|
// Clamp to the current display's work area — runs on EVERY startup (this clamp used to live
|
||||||
// inside the HiDPI branch, so a size saved on a larger/disconnected monitor could open the
|
// inside the HiDPI branch, so a size saved on a larger/disconnected monitor could open the
|
||||||
// window off-screen or bigger than the screen on a same-DPI cold start).
|
// window off-screen or bigger than the screen on a same-DPI cold start).
|
||||||
@@ -1281,6 +1311,7 @@ int main(int argc, char* argv[])
|
|||||||
DEBUG_LOGF("Startup: window fitted %dx%d -> %dx%d (scale %.2f)\n",
|
DEBUG_LOGF("Startup: window fitted %dx%d -> %dx%d (scale %.2f)\n",
|
||||||
curW, curH, newW, newH, currentDpiScale);
|
curW, curH, newW, newH, currentDpiScale);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
winlog("STARTUP savedSize=%dx%d currentDpiScale=%.3f", savedWinW, savedWinH, currentDpiScale);
|
winlog("STARTUP savedSize=%dx%d currentDpiScale=%.3f", savedWinW, savedWinH, currentDpiScale);
|
||||||
@@ -1552,6 +1583,7 @@ int main(int argc, char* argv[])
|
|||||||
// Window restored from minimized — trigger immediate data refresh
|
// Window restored from minimized — trigger immediate data refresh
|
||||||
if (waitEvent.type == SDL_EVENT_WINDOW_RESTORED &&
|
if (waitEvent.type == SDL_EVENT_WINDOW_RESTORED &&
|
||||||
waitEvent.window.windowID == SDL_GetWindowID(window)) {
|
waitEvent.window.windowID == SDL_GetWindowID(window)) {
|
||||||
|
app.skipDaemonOutputBacklog(); // drop the minimized-period backlog (avoids a spurious "rescan complete" toast)
|
||||||
app.refreshNow();
|
app.refreshNow();
|
||||||
}
|
}
|
||||||
// Handle DPI change that arrived while idle (same logic as poll loop)
|
// Handle DPI change that arrived while idle (same logic as poll loop)
|
||||||
@@ -1681,6 +1713,7 @@ int main(int argc, char* argv[])
|
|||||||
// Window restored from minimized — trigger immediate data refresh
|
// Window restored from minimized — trigger immediate data refresh
|
||||||
if (event.type == SDL_EVENT_WINDOW_RESTORED &&
|
if (event.type == SDL_EVENT_WINDOW_RESTORED &&
|
||||||
event.window.windowID == SDL_GetWindowID(window)) {
|
event.window.windowID == SDL_GetWindowID(window)) {
|
||||||
|
app.skipDaemonOutputBacklog(); // drop the minimized-period backlog (avoids a spurious "rescan complete" toast)
|
||||||
app.refreshNow();
|
app.refreshNow();
|
||||||
}
|
}
|
||||||
// Handle DPI/display scale changes (e.g. window dragged to a
|
// Handle DPI/display scale changes (e.g. window dragged to a
|
||||||
|
|||||||
Reference in New Issue
Block a user