Added everything
This commit is contained in:
commit
89c5629176
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
example/example
|
||||
.vscode/*
|
||||
66
Progress.cpp
Normal file
66
Progress.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "Progress.h"
|
||||
#include <sstream>
|
||||
|
||||
using std::ostream;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
|
||||
Progress::Progress(const string& message, size_t total, size_t current)
|
||||
: m_current(current),
|
||||
m_total(total),
|
||||
m_message(message)
|
||||
{
|
||||
if (m_current > m_total)
|
||||
{
|
||||
m_current = m_total;
|
||||
}
|
||||
}
|
||||
|
||||
void Progress::set_total(size_t total)
|
||||
{
|
||||
m_total = total;
|
||||
|
||||
if (m_current > m_total)
|
||||
{
|
||||
m_current = m_total;
|
||||
}
|
||||
}
|
||||
|
||||
void Progress::set_progress(size_t current)
|
||||
{
|
||||
m_current = current;
|
||||
|
||||
if (m_current > m_total)
|
||||
{
|
||||
m_current = m_total;
|
||||
}
|
||||
}
|
||||
|
||||
void Progress::set_message(const std::string& message)
|
||||
{
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
void Progress::increment_progress(size_t inc)
|
||||
{
|
||||
m_current += inc;
|
||||
|
||||
if (m_current > m_total)
|
||||
{
|
||||
m_current = m_total;
|
||||
}
|
||||
}
|
||||
|
||||
string Progress::print_progress()
|
||||
{
|
||||
stringstream out;
|
||||
out << m_current << "/" << m_total << " - " << m_message;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& out, const Progress& progress)
|
||||
{
|
||||
out << progress.m_current << "/" << progress.m_total;
|
||||
out << " - " << progress.m_message;
|
||||
return out;
|
||||
}
|
||||
29
Progress.h
Normal file
29
Progress.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef PROGRESS_H_
|
||||
#define PROGRESS_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
class Progress
|
||||
{
|
||||
protected:
|
||||
size_t m_current;
|
||||
size_t m_total;
|
||||
|
||||
std::string m_message;
|
||||
|
||||
public:
|
||||
Progress(const std::string& message, size_t total, size_t current = 0);
|
||||
|
||||
void set_total(size_t total);
|
||||
void set_progress(size_t current);
|
||||
void set_message(const std::string& message);
|
||||
|
||||
void increment_progress(size_t inc = 1);
|
||||
|
||||
std::string print_progress();
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Progress& progress);
|
||||
};
|
||||
|
||||
#endif // PROGRESS_H_
|
||||
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Terminal Status Line
|
||||
This simple utility allows you to easily print progress messages at the bottom of any program that outputs to **STDOUT**.
|
||||
Part of this code has been adapted from the package manager *apt*.
|
||||
|
||||
# Example
|
||||
An example of this in action can be found in the example folder and compiled with `make`.
|
||||
187
StatusBarManager.cpp
Normal file
187
StatusBarManager.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
#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();
|
||||
}
|
||||
35
StatusBarManager.h
Normal file
35
StatusBarManager.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef STATUS_BAR_MANAGER_H_
|
||||
#define STATUS_BAR_MANAGER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Progress.h"
|
||||
|
||||
class StatusBarManager
|
||||
{
|
||||
private:
|
||||
bool m_is_started;
|
||||
std::vector<Progress*> m_progress_messages;
|
||||
|
||||
struct TermSize
|
||||
{
|
||||
int rows;
|
||||
int columns;
|
||||
};
|
||||
|
||||
TermSize get_terminal_size();
|
||||
|
||||
void setup_terminal_scroll_area(int nr_rows);
|
||||
|
||||
public:
|
||||
StatusBarManager();
|
||||
~StatusBarManager();
|
||||
|
||||
bool draw_status_line();
|
||||
void start();
|
||||
void stop();
|
||||
void add_progress_message(Progress* message);
|
||||
void clear();
|
||||
};
|
||||
|
||||
#endif // STATUS_BAR_MANAGER_H_
|
||||
4
example/Makefile
Normal file
4
example/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
all:
|
||||
g++ main.cpp ../Progress.cpp ../StatusBarManager.cpp -o example
|
||||
|
||||
.PHONY: all
|
||||
69
example/main.cpp
Normal file
69
example/main.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../Progress.h"
|
||||
#include "../StatusBarManager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
StatusBarManager* g_manager = nullptr;
|
||||
|
||||
// This ensures your terminal is left in normal state even after Ctrl+C
|
||||
void INT_handler(int sig)
|
||||
{
|
||||
if (nullptr != g_manager)
|
||||
{
|
||||
g_manager->stop();
|
||||
}
|
||||
signal(sig, SIG_DFL);
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
signal(SIGINT, INT_handler);
|
||||
Progress p("Normal", 10);
|
||||
Progress p2("Test", 10);
|
||||
StatusBarManager manager;
|
||||
g_manager = &manager;
|
||||
manager.add_progress_message(&p);
|
||||
manager.add_progress_message(&p2);
|
||||
if (isatty(STDOUT_FILENO))
|
||||
{
|
||||
manager.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "NOT A TTY" << endl;
|
||||
}
|
||||
|
||||
string test[2] = {"Message 1", "Message 2"};
|
||||
|
||||
srand(time(nullptr));
|
||||
|
||||
manager.draw_status_line();
|
||||
this_thread::sleep_for(chrono::seconds(1));
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
int random = rand() % 2;
|
||||
if (random != 2)
|
||||
{
|
||||
cout << test[random] << endl;
|
||||
}
|
||||
p.increment_progress();
|
||||
p2.increment_progress();
|
||||
|
||||
manager.draw_status_line();
|
||||
this_thread::sleep_for(chrono::seconds(1));
|
||||
}
|
||||
|
||||
manager.stop();
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user