1
0
forked from Alepha/Alepha
Files
Alepha/Console.h

251 lines
6.9 KiB
C++

static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <string>
#include <memory>
#include <Alepha/TotalOrder.h>
// 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::Hydrogen ::detail:: Console_m
{
inline namespace exports {}
namespace exports
{
struct ScreenSize;
struct CursorPosition;
class Console;
struct SGR_String
{
std::string code;
};
[[nodiscard]] inline SGR_String
operator + ( const SGR_String lhs, const SGR_String rhs )
{
if( lhs.code.empty() ) return rhs;
if( rhs.code.empty() ) return lhs;
return SGR_String{ lhs.code + ';' + rhs.code };
}
inline SGR_String &
operator += ( SGR_String &lhs, const SGR_String rhs )
{
return lhs= lhs + rhs;
}
// Parses sgr token names, like "bold ext:red"
[[nodiscard]] SGR_String operator ""_sgr( const char *p, std::size_t 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 setApplicationName( 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 );
static Console &main();
auto 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();
// Non Colour effects (Mostly sorted by ANSI/ECMA SGR code numeric order)
[[nodiscard]] SGR_String setBold();
[[nodiscard]] SGR_String setFaint();
[[nodiscard]] SGR_String setItalic();
[[nodiscard]] SGR_String setUnderline();
[[nodiscard]] SGR_String setBlink();
[[nodiscard]] SGR_String setStrike();
[[nodiscard]] SGR_String setDoubleUnderline();
[[nodiscard]] SGR_String setFramed();
[[nodiscard]] SGR_String setEncircled();
[[nodiscard]] SGR_String setOverline();
[[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 );
[[nodiscard]] SGR_String setExtUlColor( TextColor ul );
// 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 );
[[nodiscard]] SGR_String setUlTrueColor( int rgb );
[[nodiscard]] SGR_String setUlTrueColor( int r, int g, int b );
void sendSGR( std::ostream &os, SGR_String );
int getConsoleWidth();
}
enum class exports::BasicTextColor : int
{
black= 0,
red= 1,
green= 2,
brown= 3,
blue= 4,
magenta= 5,
cyan= 6,
grey= 7,
};
enum class exports::TextColor : int
{
black= 0,
dim_red= 1,
dim_green= 2,
dim_brown= 3,
dim_blue= 4,
dim_magenta= 5,
dim_cyan= 6,
bright_grey= 7,
// Note that bright and dim grey are reverse, since bright grey is dim white and dim grey si bright black.
// The names are more understandable this way, I think
dim_grey= 8,
bright_red= 9,
bright_green= 10,
bright_brown= 11,
bright_blue= 12,
bright_magenta= 13,
bright_cyan= 14,
white= 15,
rgb_base= 16,
red_radix= 36,
green_radix= 6,
blue_radix= 1,
greyscale_base= 232, // Add N to this to get the greyscale offset.
};
}
namespace Alepha::Hydrogen::inline exports::inline Console_m
{
using namespace detail::Console_m::exports;
}