forked from Alepha/Alepha
A console driver from my ISO scratch work.
This commit is contained in:
399
Console.cpp
Normal file
399
Console.cpp
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
static_assert( __cplusplus > 2020'00 );
|
||||||
|
|
||||||
|
#include "console.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ProgramOptions.h"
|
||||||
|
#include "file_help.h"
|
||||||
|
#include "Enum.h"
|
||||||
|
#include "StaticValue.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All of the terminal control code in this library uses ANSI escape sequences (https://en.wikipedia.org/wiki/ANSI_escape_code).
|
||||||
|
* Instead of using termcap and curses, code can use this simpler library instead. The simple fact is that in 2022, there's
|
||||||
|
* probably no terminal software that you're using that does not understand these escape sequences. Truth be told, the
|
||||||
|
* termcap databases are suffering from a tremendous amount of bitrot, as no actual hardware or software in common use
|
||||||
|
* actually uses anything but the ANSI codes.
|
||||||
|
*
|
||||||
|
* Some ANSI engines have a maximum limit to how many "clauses" a CSI sequence can have. To this end, no significant effort
|
||||||
|
* should be put into trying to collapse the sequences for foreground and background into one SGR command. Despite it taking
|
||||||
|
* a few more bytes, it's probably more portable to issue multiple commands.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// It's fairly safe to assume, in 2022, that common ANSI terminal sequences are
|
||||||
|
// universally supported for effectively all cases modern users will care about.
|
||||||
|
|
||||||
|
|
||||||
|
namespace Alepha::Cavorite ::detail:: console
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
|
namespace C
|
||||||
|
{
|
||||||
|
const bool debug= false;
|
||||||
|
|
||||||
|
const int defaultScreenWidthLimit= 100;
|
||||||
|
|
||||||
|
// The Device Status Report should never be longer than this.
|
||||||
|
const int maxLengthOfDSR= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO, this should be in its own lib.
|
||||||
|
namespace storage
|
||||||
|
{
|
||||||
|
StaticValue< std::string > applicationName;
|
||||||
|
|
||||||
|
auto init= enroll <=[]
|
||||||
|
{
|
||||||
|
if( applicationName().empty() ) applicationName()= "ALEPHA";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
void
|
||||||
|
exports::setApplicationName( std::string name )
|
||||||
|
{
|
||||||
|
storage::applicationName()= std::move( name );
|
||||||
|
}
|
||||||
|
const std::string &
|
||||||
|
exports::applicationName()
|
||||||
|
{
|
||||||
|
return storage::applicationName();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto screenWidthEnv() { return applicationName() + "_SCREEN_WIDTH"; }
|
||||||
|
auto screenWidthEnvLimit() { return applicationName() + "_SCREEN_WIDTH_LIMIT"; }
|
||||||
|
auto disableColorsEnv() { return applicationName() + "_DISABLE_COLOR_TEXT"; }
|
||||||
|
auto colorsEnv() { return applicationName() + "_COLORS"; }
|
||||||
|
|
||||||
|
// TODO: Put this in a library
|
||||||
|
int
|
||||||
|
getEnvOrDefault( const std::string env, const int d )
|
||||||
|
{
|
||||||
|
if( getenv( env.c_str() ) )
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return boost::lexical_cast< int >( getenv( env.v_str() ) );
|
||||||
|
}
|
||||||
|
catch( const boost::bad_lexical_cast & ) {}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cachedScreenWidth= evaluate <=[]
|
||||||
|
{
|
||||||
|
const int underlying getEnvOrDefault( screenWidthEnv(), getScreenSize().columns );
|
||||||
|
return std::min( underlying, getEnvOrDefault( screenWidthEnvLimit(), C::defaultScreenWidthLimit ) );
|
||||||
|
};
|
||||||
|
|
||||||
|
using ColorState= Enum< "always"_value, "never"_value, "auto"_value >;
|
||||||
|
std::optional< ColorState > colorState;
|
||||||
|
|
||||||
|
bool
|
||||||
|
colorEnabled()
|
||||||
|
{
|
||||||
|
if( not colorState.has_value() ) return getenv( disableColorsEnv() );
|
||||||
|
|
||||||
|
if( colorState == "never"_value ) return false;
|
||||||
|
if( colorState == "always"_value ) return true;
|
||||||
|
assert( colorState == "auto"_value );
|
||||||
|
|
||||||
|
return ::isatty( 1 ); // Auto means only do this for TTYs.
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticValue< std::map< Style, SGR_String > > colorVariables;
|
||||||
|
|
||||||
|
auto init= enroll <=[]
|
||||||
|
{
|
||||||
|
--"screen-width"_option << affectsHelp << cachedScreenWidth << "Sets the screen width for use in automatic word-wrapping. !default!";
|
||||||
|
--"color"_option << affectsHelp << colorState << "Select the application color preference. If not passed, the environment variable `"
|
||||||
|
<< disableColorsEnv() << "` will be respected. Otherwise, `auto` will detect if a TTY is on stdout. `never` will entirely "
|
||||||
|
<< "disable color output. And `always` will force color output.";
|
||||||
|
--"list-color-variables"_option << []
|
||||||
|
{
|
||||||
|
for( const auto [ name, sgr ]: colorVariables() )
|
||||||
|
{
|
||||||
|
std::cout << name.name << ": ^[[": << sgr.code << "m" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<< "Emit a list with the color variables supported by this application. For use with the `" << colorsEnv()
|
||||||
|
<< "` environment variable.";
|
||||||
|
|
||||||
|
--"dump-color-env-var"_option << []
|
||||||
|
{
|
||||||
|
std::cout << "export " << colorsEnv() << "-\"";
|
||||||
|
for( const auto &[ name, sgr ]: colorVariables() )
|
||||||
|
{
|
||||||
|
if( not first ) std::cout << ":";
|
||||||
|
first= false;
|
||||||
|
std::cout << name.name << "=" << sgr.code;
|
||||||
|
}
|
||||||
|
std::cout << "\"" << std::endl;
|
||||||
|
::exit( EXIT_SUCCESS );
|
||||||
|
}
|
||||||
|
<< "Emit a BASH command which will set the appropriate environment variable to capture the current color settings for this "
|
||||||
|
<< "application.";
|
||||||
|
|
||||||
|
parse_environment_variable_for_color:
|
||||||
|
if( getenv( colorsEnv() ) )
|
||||||
|
{
|
||||||
|
const std::string contents= getenv( colorsEnv() );
|
||||||
|
|
||||||
|
for( const auto var: split( varString, ':' ) )
|
||||||
|
{
|
||||||
|
const auto parsed= split( var, '=' );
|
||||||
|
if( parsed.size() != 2 )
|
||||||
|
{
|
||||||
|
throw std::runtime_error{ "Color environment variable parse error in: `" + var + "`." };
|
||||||
|
}
|
||||||
|
|
||||||
|
const Style name{ parsed.at( 0 ) };
|
||||||
|
const auto value= parsed.at( 1 );
|
||||||
|
|
||||||
|
colorVariables()[ name ]= SGR_String{ value };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
csi( std::ostream &os )
|
||||||
|
{
|
||||||
|
return os << "\e";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Style
|
||||||
|
exports::createStyle( const std::string &name, const SGR_String &sgr )
|
||||||
|
{
|
||||||
|
if( name == "reset" ) throw std::runtime_error( "The `reset` style name is reserved." );
|
||||||
|
Style style{ name };
|
||||||
|
colorVariables().insert( { style, sgr } );
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
exports::sendSGR( std::ostream &os, const SGR_String style )
|
||||||
|
{
|
||||||
|
csi( os ) << style.code << 'm';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
exports::operator << ( std::ostream &os, const Style &s )
|
||||||
|
{
|
||||||
|
if( colorEnabled() and colorVariables().contains( s ) )
|
||||||
|
{
|
||||||
|
sendSGR( os, colorVariables().at( s ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
exports::operator << ( std::ostream &os, decltype( resetStyle ) )
|
||||||
|
{
|
||||||
|
if( colorEnabled )
|
||||||
|
{
|
||||||
|
sendSGR( os, resetTextEffects() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum exports::Console::Mode
|
||||||
|
{
|
||||||
|
cooked, raw, noblock,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct BadScreenStateError : std::runtime_error
|
||||||
|
{
|
||||||
|
BadScreenStateError() : std::runtime_error( "Error in getting terminal dimensions." ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UnknowScreenError : std::runtime_error
|
||||||
|
{
|
||||||
|
UnknowScreenError() : std::runtime_error( "Terminal is unrecognized. Using defaults." ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto
|
||||||
|
rawModeGuard( Console console )
|
||||||
|
{
|
||||||
|
const bool skip= console.getMode() == Console::raw;
|
||||||
|
return AutoRAII
|
||||||
|
{
|
||||||
|
[skip, &console]
|
||||||
|
{
|
||||||
|
if( skip ) return;
|
||||||
|
console.setRaw();
|
||||||
|
},
|
||||||
|
[skip, &console]
|
||||||
|
{
|
||||||
|
if( skip ) return;
|
||||||
|
console.popTermMode();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Console::Impl
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
// TODO: Do we want to make this not gnu libstdc++ specific?
|
||||||
|
__gnu_cxx::stdio_filebuf< char > filebuf;
|
||||||
|
std::ostream stream;
|
||||||
|
std::stack< std::pair< struct termios, decltype( mode ) > > modeStack;
|
||||||
|
ConsoleMode mode= cooked;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
Impl( const int fd )
|
||||||
|
: fd( fd ), filebuf( fd, std::ios::out ), stream( &filebuf )
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
Console::Console( const int fd )
|
||||||
|
: impl( std::make_unique< Impl >( fd ) )
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
Console::csi()
|
||||||
|
{
|
||||||
|
return pimpl().stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Console::Mode
|
||||||
|
Console::getMode() const
|
||||||
|
{
|
||||||
|
return pimpl().mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Console::popTermMode()
|
||||||
|
{
|
||||||
|
tcsetattr( pimpl().fd, TCSAFLUSH, &pimpl().modeStack.top().first );
|
||||||
|
mode= pimpl().modeStack.top().second;
|
||||||
|
pimpl().modeStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct termios
|
||||||
|
setRawModeWithMin( const int fd, const int min )
|
||||||
|
{
|
||||||
|
struct termios next;
|
||||||
|
struct termios now;
|
||||||
|
|
||||||
|
if( tcgetattr( fd, &now ) == -1 ) throw UnknownScreenError{};
|
||||||
|
|
||||||
|
next.c_iflag&= ~( BRKINT | ICRNL | INPCK | ISTRIP | IXON );
|
||||||
|
next.c_oflag&= ~( OPOST );
|
||||||
|
next.c_flag|= CS8;
|
||||||
|
next.c_lflag&= !( ECHO | ICANNON | IEXTEN | ISIG );
|
||||||
|
next.c_cc[ VMIN ]= min;
|
||||||
|
next.c_cc[ VTIME ]= 0;
|
||||||
|
|
||||||
|
if( tcsetattr( pimpl().fd, TCSAFLUSH, &next ) ) throw UnknownScreenException{};
|
||||||
|
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Console::setRaw()
|
||||||
|
{
|
||||||
|
setRawModeWithMin( pimpl().fd, 1 );
|
||||||
|
orig.emplace_back( now, mode );
|
||||||
|
mode= raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Console::setNoblock()
|
||||||
|
{
|
||||||
|
setRawModeWithMin( pimpl().fd, 0 );
|
||||||
|
orig.emplace_back( now, mode );
|
||||||
|
mode= raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::killLineTail() { csi() << 'K'; }
|
||||||
|
void Console::killLineHead() { csi() << "1K"; }
|
||||||
|
void Console::killLine() { csi() << "2K"; }
|
||||||
|
|
||||||
|
void Console::hideCursor() { csi() << "?25l"; }
|
||||||
|
void Console::showCursor() { csi() << "?25h"; }
|
||||||
|
|
||||||
|
void Console::saveHardwareCursor() { csi() << 's'; }
|
||||||
|
void Console::restoreHardwareCursor() { csi() << 'u'; }
|
||||||
|
|
||||||
|
void Console::gotoX( const int x ) { csi() << x << 'G'; }
|
||||||
|
|
||||||
|
void
|
||||||
|
Console::gotoY( const int x )
|
||||||
|
{
|
||||||
|
cursorUp( 1'000'000 );
|
||||||
|
cursorDown( y );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::gotoXY( const int x, const int y ) { csi() << y << ';' << x << 'H'; }
|
||||||
|
|
||||||
|
void Console::cursorUp( const unsigned amt ) { csi() << amt << 'A'; }
|
||||||
|
void Console::cursorDown( const unsigned amt ) { csi() << amt << 'B'; }
|
||||||
|
void Console::cursorRight( const unsigned amt ) { csi() << amt << 'C'; }
|
||||||
|
void Console::cursorLeft( const unsigned amt ) { csi() << amt << 'D'; }
|
||||||
|
|
||||||
|
void Console::clearScreen() { csi() << "2J"; }
|
||||||
|
|
||||||
|
SGR_String exports::resetTextEffects() { return {}; }
|
||||||
|
|
||||||
|
SGR_String exports::setBlink() { return { "5" }; }
|
||||||
|
|
||||||
|
SGR_String
|
||||||
|
exports::setFGColor( const BasicTextColor c )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << '3' << int( c );
|
||||||
|
return { std::move( oss ).str() };
|
||||||
|
}
|
||||||
|
|
||||||
|
SGR_String
|
||||||
|
exports::setBGColor( const BasicTextColor c )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << '4' << int( c );
|
||||||
|
return { std::move( oss ).str() };
|
||||||
|
}
|
||||||
|
|
||||||
|
SGR_String
|
||||||
|
exports::setColor( const BasicTextColor fg, const BasicTextColor bg )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << '3' << fg << ";4" << int( bg );
|
||||||
|
return { std::move( oss ).str() };
|
||||||
|
}
|
||||||
|
|
||||||
|
SGR_String
|
||||||
|
exports::setExtFgColor( const TextColor c )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "38;5;" << int( c );
|
||||||
|
return { std::move( oss ).str() };
|
||||||
|
}
|
||||||
|
|
||||||
|
SGR_String
|
||||||
|
exports::setExtBgColor( const TextColor c )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "48;5;" << int( c );
|
||||||
|
return { std::move( oss ).str() };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SGR_String
|
||||||
|
exports::setExtColor( const TextColor fg, const TextColor bg )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "38;2" << fg << "48;2" << int( bg );
|
||||||
|
return { std::move( oss ).str() };
|
||||||
|
}
|
||||||
|
}
|
||||||
169
Console.h
Normal file
169
Console.h
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
static_assert( __cplusplus > 2020'00 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// These are some terminal/console control primitives.
|
||||||
|
// There are several "modern" terminal assumptions built
|
||||||
|
// into this library.
|
||||||
|
//
|
||||||
|
// As long as this works on most (all?) modern terminal emulators, this should be
|
||||||
|
// fine.
|
||||||
|
|
||||||
|
namespace Alepha::inline Cavorite ::detail:: console
|
||||||
|
{
|
||||||
|
inline namespace exports {}
|
||||||
|
|
||||||
|
namespace exports
|
||||||
|
{
|
||||||
|
struct ScreenSize;
|
||||||
|
struct CursorPosition;
|
||||||
|
|
||||||
|
class Console;
|
||||||
|
|
||||||
|
Console &console() noexcept;
|
||||||
|
|
||||||
|
struct SGR_String
|
||||||
|
{
|
||||||
|
std::string code;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto
|
||||||
|
operator ""_sgr( const char *const p, const std::size_t sz )
|
||||||
|
{
|
||||||
|
return SGR_String{ { p, p + sz } };
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class BasicTextColor : int;
|
||||||
|
enum class TextColor : int;
|
||||||
|
|
||||||
|
struct Style
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
TotalOrder operator <=> ( const Style & ) const= default;
|
||||||
|
};
|
||||||
|
std::ostream &operator << ( std::ostream &, const Style & );
|
||||||
|
|
||||||
|
Style createStyle( const std::string &name, const SGR_String &style );
|
||||||
|
bool styleVarSet( const std::string &name );
|
||||||
|
|
||||||
|
enum ResetStyle { resetStyle };
|
||||||
|
std::ostream &operator << ( std::ostream &, ResetStyle );
|
||||||
|
|
||||||
|
// TODO: Move this to its own library.
|
||||||
|
const std::string &applicationName();
|
||||||
|
void setApplication( std::string name );
|
||||||
|
}
|
||||||
|
|
||||||
|
struct exports::ScreenSize
|
||||||
|
{
|
||||||
|
int rows;
|
||||||
|
int columns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct exports::CursorPosition
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
class exports::Console
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
std::unique_ptr< Impl > impl;
|
||||||
|
Impl &pimpl() noexcept { return *impl; }
|
||||||
|
const Impl &pimpl() const noexcept { return *impl; }
|
||||||
|
|
||||||
|
std::ostream &csi();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// A console object can only be constructed on a raw UNIX file descriptor.
|
||||||
|
explicit Console( int fd );
|
||||||
|
|
||||||
|
enum Mode;
|
||||||
|
|
||||||
|
Mode getMode() const;
|
||||||
|
|
||||||
|
int getScreenWidth();
|
||||||
|
int getScreenHeight();
|
||||||
|
|
||||||
|
// Althought this could be implemented by combining the above observers,
|
||||||
|
// there are more efficient ways to implement this, thus we actually
|
||||||
|
// keep them separate.
|
||||||
|
ScreenSize getScreenSize();
|
||||||
|
|
||||||
|
void hideCursor();
|
||||||
|
void showCursor();
|
||||||
|
|
||||||
|
// Pushes the current mode (raw or normal) onto the stack.
|
||||||
|
void setRaw();
|
||||||
|
|
||||||
|
// A nonblock mode is used to prevent terminal IO from blocking. This helps with keypress driven
|
||||||
|
// programs. This is its own raw mode.
|
||||||
|
void setNoblock();
|
||||||
|
|
||||||
|
// The Console object maintains an internal stack of the last terminal modes it set and how to
|
||||||
|
// revert tothe previous. There is no arbitrary limit on this stack size. Calling this with
|
||||||
|
// no previous modes is a no-op.
|
||||||
|
void popTermMode();
|
||||||
|
|
||||||
|
// Line kill functions keep the cursor where it is, but erase the specified text on the line.
|
||||||
|
void killLine();
|
||||||
|
void killLineHead();
|
||||||
|
void killLineTail();
|
||||||
|
|
||||||
|
// One should avoid calling these, as some internals may also use these hardware functions.
|
||||||
|
// It is better to use `gotoXY` and `getXY` to save/restore cursor positions. One should
|
||||||
|
// maintain a software stack (as a caller) of cursor positions, if necessary.
|
||||||
|
void saveHardwareCursor();
|
||||||
|
void restoreHardwareCursor();
|
||||||
|
|
||||||
|
void gotoX( int x );
|
||||||
|
void gotoY( int y );
|
||||||
|
void gotoXY( int x, int y );
|
||||||
|
|
||||||
|
int getX();
|
||||||
|
int getY();
|
||||||
|
CursorPosition getXY();
|
||||||
|
|
||||||
|
void cursorUp( unsigned amt= 0 );
|
||||||
|
void cursorDown( unsigned amt= 0 );
|
||||||
|
|
||||||
|
void cursorLeft( unsigned amt= 0 );
|
||||||
|
void cursorRight( unsigned amt= 0 );
|
||||||
|
|
||||||
|
void clearScreen(); // `console` library does direct cursor control, so this won't return the cursor to 1,1.
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace exports
|
||||||
|
{
|
||||||
|
[[nodiscard]] SGR_String resetTextEffects();
|
||||||
|
|
||||||
|
[[nodiscard]] SGR_String setBlink();
|
||||||
|
|
||||||
|
[[nodiscard]] SGR_String setFGColor( BasicTextColor fg );
|
||||||
|
[[nodiscard]] SGR_String setBGColor( BasicTextColor bg );
|
||||||
|
[[nodiscard]] SGR_String setColor( BasicTextColor fg, BasicTextColor bg );
|
||||||
|
|
||||||
|
[[nodiscard]] SGR_String setExtFGColor( TextColor fg );
|
||||||
|
[[nodiscard]] SGR_String setExtBGColor( TextColor fg );
|
||||||
|
[[nodiscard]] SGR_String setExtColor( TextColor fg, TextColor bg );
|
||||||
|
|
||||||
|
// Basic color wrapping aliases:
|
||||||
|
[[nodiscard]] inline SGR_String setExtFgColor( const BasicTextColor fg ) { return setExtFgColor( static_cast< TextColor >( fg ) ); }
|
||||||
|
[[nodiscard]] inline SGR_String setExtBgColor( const BasicTextColor bg ) { return setExtBgColor( static_cast< TextColor >( bg ) ); }
|
||||||
|
|
||||||
|
[[nodiscard]] inline SGR_String setExtColor( const TextColor fg, const BasicTextColor bg ) { return setExtColor( ( fg ), static_cast< TextColor >( bg ) ); }
|
||||||
|
[[nodiscard]] inline SGR_String setExtColor( const BasicTextColor fg, const TextColor bg ) { return setExtColor( static_cast< TextColor >( fg ), ( bg ) ); }
|
||||||
|
[[nodiscard]] inline SGR_String setExtColor( const BasicTextColor fg, const BasicTextColor bg ) { return setExtColor( static_cast< TextColor >( fg ), static_cast< TextColor >( bg ) ); }
|
||||||
|
|
||||||
|
[[nodiscard]] SGR_String setFgTrueColor( int rgb );
|
||||||
|
[[nodiscard]] SGR_String setFgTrueColor( int r, int g, int b )
|
||||||
|
|
||||||
|
[[nodiscard]] SGR_String setBgTrueColor( int rgb );
|
||||||
|
[[nodiscard]] SGR_String setBgTrueColor( int r, int g, int b )
|
||||||
|
|
||||||
|
void sendSGR( std::ostream &os, SGR_String );
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user