#include "StatusBarManager.h" #include #include #include #include using std::cout; using std::flush; using std::string; StatusBarManager::StatusBarManager() : m_is_started(false) {} StatusBarManager::~StatusBarManager() { if(m_is_started) { stop(); } } StatusBarManager::TermSize StatusBarManager::get_terminal_size() { struct winsize win; StatusBarManager::TermSize result = { 0, 0 }; if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) != 0) { return result; } result.rows = win.ws_row; result.columns = win.ws_col; return result; } void StatusBarManager::setup_terminal_scroll_area(int nr_rows) { int status_rows = m_progress_messages.size(); if (nr_rows <= 1) { return; } // scroll down a bit to avoid visual glitch when the screen // area shrinks by needed ammount of rows for (int i = 0; i < status_rows; ++i) { cout << std::endl; } // save cursor cout << "\e7"; // set scroll region (this will place the cursor in the top left) cout << "\e[0;" << std::to_string(nr_rows - status_rows) << "r"; // restore cursor but ensure its inside the scrolling area cout << "\e8"; cout << "\e[" << status_rows << "A"; // ensure its flushed flush(cout); // Leftovers from apt #if 0 // setup tty size to ensure xterm/linux console are working properly too // see bug #731738 struct winsize win; if (ioctl(child_pty, TIOCGWINSZ, (char *)&win) != -1) { win.ws_row = nr_rows - 1; ioctl(child_pty, TIOCSWINSZ, (char *)&win); } #endif } void StatusBarManager::start() { if (!m_is_started) { int const nr_terminal_rows = get_terminal_size().rows; setup_terminal_scroll_area(nr_terminal_rows); m_is_started = true; } } void StatusBarManager::stop() { if (m_is_started) { int const nr_terminal_rows = get_terminal_size().rows; if (nr_terminal_rows > 0) { setup_terminal_scroll_area(nr_terminal_rows + m_progress_messages.size()); // override the progress line (sledgehammer) static const char* clear_screen_below_cursor = "\e[J"; cout << clear_screen_below_cursor; flush(cout); } m_is_started = false; } } bool StatusBarManager::draw_status_line() { if (!m_is_started) { return false; } StatusBarManager::TermSize const size = get_terminal_size(); if (size.rows < 1 || size.columns < 1) { return false; } static string save_cursor = "\e7"; static string restore_cursor = "\e8"; // green static string set_bg_color = "\e[42m"; // black static string set_fg_color = "\e[30m"; static string restore_bg = "\e[49m"; static string restore_fg = "\e[39m"; cout << save_cursor; int status_rows = m_progress_messages.size(); for (int i = 0; i < status_rows; ++i) { // move cursor position to current row cout << "\e[" << std::to_string(size.rows + 1 - status_rows + i) << ";0f" << set_bg_color << set_fg_color; if (nullptr != m_progress_messages[i]) { cout << *(m_progress_messages[i]); } flush(cout); } cout << restore_bg << restore_fg; flush(cout); // leftover progress-bar from apt #if 0 // draw text progress bar int padding = 4; auto const progressbar_size = size.columns - padding - String::DisplayLength(progress_str); auto const current_percent = percentage / 100.0f; cout << " " << GetTextProgressStr(current_percent, progressbar_size) << " "; flush(cout); #endif // restore cout << restore_cursor; flush(cout); return true; } void StatusBarManager::add_progress_message(Progress* message) { if (message == nullptr) { return; } m_progress_messages.push_back(message); } void StatusBarManager::clear() { m_progress_messages.clear(); }