terminal_status_line/StatusBarManager.cpp
2025-10-27 17:07:57 +02:00

188 lines
3.6 KiB
C++

#include "StatusBarManager.h"
#include <iostream>
#include <string>
#include <sys/ioctl.h>
#include <unistd.h>
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();
}