Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 167 additions & 56 deletions include/rxterm/terminal.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#ifndef RXTERM_TERMINAL_HPP
#define RXTERM_TERMINAL_HPP

#include <iostream>
#include <algorithm>
#include <iostream>
#include <string>

#ifdef _WIN32
#include <csignal>
#include <mutex>
#ifndef NOMINMAX
#define NOMINMAX
Expand All @@ -16,86 +17,196 @@
#include <unistd.h>
#endif

#include <utility>
#include <rxterm/utils.hpp>
#include <utility>

namespace rxterm {
namespace rxterm
{

struct VirtualTerminal {
std::string buffer;
static unsigned width() {
unsigned w;
volatile std::sig_atomic_t& gSignalStatus()
{
static volatile std::sig_atomic_t instance = 0;
return instance;
}

struct VirtualTerminal
{
std::string buffer;
static unsigned width()
{
unsigned w;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO info;
w = (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info))
? info.srWindow.Right - info.srWindow.Left + 1
: 0;
CONSOLE_SCREEN_BUFFER_INFO info;
w = (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info))
? info.srWindow.Right - info.srWindow.Left + 1
: 0;
#else
winsize ws{};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
w = ws.ws_col;
winsize ws{};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
w = ws.ws_col;
#endif
return w;
}
static unsigned height() {
unsigned h;
return w;
}
static unsigned height()
{
unsigned h;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO info;
h = (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info))
? info.srWindow.Bottom - info.srWindow.Top + 1
: 0;
CONSOLE_SCREEN_BUFFER_INFO info;
h = (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info))
? info.srWindow.Bottom - info.srWindow.Top + 1
: 0;
#else
winsize ws{};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
h = ws.ws_row;
#endif
return h;
}
return h;
}

std::string computeTransition(std::string const& next) const {
if(buffer == next) return "";
unsigned const n = std::count(buffer.begin(), buffer.end(), '\n');
return clearLines(n) + "\033[0m" + next;
}
std::string computeTransition(std::string const& next) const
{
if (buffer == next)
return "";
unsigned const n = std::count(buffer.begin(), buffer.end(), '\n');
return clearLines(n) + "\033[0m" + next;
}

static std::string hide() { return "\033[0;8m"; }
static std::string hide()
{
return "\033[0;8m";
}

VirtualTerminal flip(std::string const& next) const {
auto const transition = computeTransition(next);
if(transition.empty()) return *this;
std::cout << transition << hide();
std::flush(std::cout);
return VirtualTerminal{next};
}
VirtualTerminal flip(std::string const& next) const
{
auto const transition = computeTransition(next);
if (transition.empty())
return *this;
std::cout << transition << hide();
std::flush(std::cout);
return VirtualTerminal{ next };
}

struct InitSignalHandlerThread
{
static void globalInit()
{
static InitSignalHandlerThread _;
}

private:
InitSignalHandlerThread()
{

std::signal(SIGINT, [](int signal) { gSignalStatus() = signal; });

std::thread signal_handler_thread([] {
using namespace std::chrono_literals;
for (;;)
{
if (gSignalStatus() != 0)
{

std::exit(gSignalStatus());
}
std::this_thread::sleep_for(100ms);
}
});
signal_handler_thread.detach();
}
};

struct InitAlternateBuffer
{
static void globalInit()
{
static InitAlternateBuffer _;
}

private:
InitAlternateBuffer()
{
// Enter the alternate buffer
std::cout << "\x1b[?1049h";
}
~InitAlternateBuffer()
{
// Exit the alternate buffer
std::cout << "\x1b?1049l";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this supposed to be "\x1b[?1049l" ?

// Returns all attributes to the default state prior to modification
std::cout << "\033[0;m";
}
};

#ifdef _WIN32
static std::once_flag initInstanceFlag;
static DWORD initWindowsTerminal() {
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
// Set output mode to handle virtual terminal sequences
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE) return GetLastError();
DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode)) return GetLastError();
dwMode |= DWORD(ENABLE_VIRTUAL_TERMINAL_PROCESSING);
if (!SetConsoleMode(hOut, dwMode)) return GetLastError();
return ERROR_SUCCESS;
}
struct InitVirtualConsoleMode
{
static void globalInit()
{
static InitVirtualConsoleMode _;
}

VirtualTerminal() {
std::call_once(initInstanceFlag, VirtualTerminal::initWindowsTerminal);
}
VirtualTerminal(const VirtualTerminal&) = default;
VirtualTerminal& operator=(const VirtualTerminal&) = default;
explicit VirtualTerminal(std::string buf) : buffer{std::move(buf)} {};
private:
static void setVirtualTerminalProcessingMode(bool value)
{
// Set output mode to handle virtual terminal sequences
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE)
throw std::system_error(
std::error_code(GetLastError(), std::system_category()));
DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode))
throw std::system_error(
std::error_code(GetLastError(), std::system_category()));
if (value)
{
dwMode |= DWORD(ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
else
{
dwMode &= ~DWORD(ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
if (!SetConsoleMode(hOut, dwMode))
throw std::system_error(
std::error_code(GetLastError(), std::system_category()));
}

InitVirtualConsoleMode()
{
setVirtualTerminalProcessingMode(true);
}
~InitVirtualConsoleMode()
{
setVirtualTerminalProcessingMode(false);
}
};
#endif
};

VirtualTerminal(bool installInterruptSignalHandlerThread = true,
bool useAlternateBuffer = true)
{
#ifdef _WIN32
std::once_flag VirtualTerminal::initInstanceFlag;
InitVirtualConsoleMode::globalInit();
#endif

if (useAlternateBuffer)
{
InitAlternateBuffer::globalInit();
}
if (installInterruptSignalHandlerThread)
{
InitSignalHandlerThread::globalInit();
}
}

VirtualTerminal(const VirtualTerminal&) = default;
VirtualTerminal& operator=(const VirtualTerminal&) = default;
explicit VirtualTerminal(std::string buf) : buffer{ std::move(buf) } {};
};

}

#endif