// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "console_model.h" #include namespace dragonx { namespace ui { void ConsoleModel::trimPendingLocked() { // Keep at least the newest line so one huge line is never dropped on arrival. Dropping the OLDEST pending // lines past max_lines_ is lossless for the view (drain() would evict them immediately); the byte budget // is the only lossy part and only bites while nobody drains (Console page closed). while (pending_.size() > 1 && (pending_.size() > max_lines_ || pending_bytes_ > max_pending_bytes_)) { pending_bytes_ -= pending_.front().text.size(); pending_.pop_front(); ++dropped_; } } void ConsoleModel::ingest(const std::string& text, ConsoleChannel channel, int foldSpan) { ConsoleModelLine line; // build outside the lock line.text = text; line.channel = channel; line.foldSpan = foldSpan; const std::size_t bytes = line.text.size(); std::lock_guard lock(ingest_mutex_); pending_.push_back(std::move(line)); pending_bytes_ += bytes; trimPendingLocked(); } void ConsoleModel::ingestBatch(std::vector&& lines) { if (lines.empty()) return; std::size_t bytes = 0; for (const auto& l : lines) bytes += l.text.size(); std::lock_guard lock(ingest_mutex_); // ONE acquisition: the block stays contiguous for (auto& l : lines) pending_.push_back(std::move(l)); pending_bytes_ += bytes; trimPendingLocked(); } ConsoleModel::DrainResult ConsoleModel::drain() { std::deque incoming; std::size_t dropped = 0; { std::lock_guard lock(ingest_mutex_); if (pending_.empty()) return {}; incoming.swap(pending_); pending_bytes_ = 0; dropped = dropped_; dropped_ = 0; } DrainResult result; result.added = incoming.size(); result.dropped = dropped; // A dropped pending line never entered lines_, so it does not advance first_seq_ (the seq of // lines_.front()); only a front eviction below or clear() does. Keeps the wrap cache aligned. for (auto& line : incoming) lines_.push_back(std::move(line)); while (lines_.size() > max_lines_) { lines_.pop_front(); ++result.popped; ++first_seq_; } ++revision_; // deque changed — lets the view memoize its filter/layout passes return result; } void ConsoleModel::clear() { first_seq_ += lines_.size(); // the discarded lines keep their sequence numbers (view caches realign by seq) lines_.clear(); ++revision_; } bool ConsoleModel::toggleCollapsed(std::size_t i) { if (i >= lines_.size() || lines_[i].foldSpan <= 0) return false; lines_[i].collapsed = !lines_[i].collapsed; ++revision_; // fold change alters which lines are visible return lines_[i].collapsed; } } // namespace ui } // namespace dragonx