alecAbstraction Layer for Escape Codes |
git clone git://git.dimitrijedobrota.com/alec.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | b6aefc9b942a0f464448415670b8679298a51782 |
parent | d705309b300eaf8f7c60a6eb6774f21e5634789c |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Tue, 20 May 2025 21:47:04 +0200 |
Cleanup
M | include/alec/alec.hpp | | | +++++++++++++++++++++++++++++++++++++++ ------------------------------------------- |
M | include/alec/terminal.hpp | | | ++++++++++++++++++++++++++++++++++++++++ ------------------------------------------ |
2 files changed, 102 insertions(+), 111 deletions(-)
diff --git a/ include/alec/alec.hpp b/ include/alec/alec.hpp
@@ -14,26 +14,24 @@
namespace alec
#define ENUM_COLOR \
black, red, green, yellow, blue, magenta, cyan, white, unused_1, def
BASED_DECLARE_ENUM(color, based::bi8, 0, ENUM_COLOR)
BASED_DEFINE_ENUM(color, based::bi8, 0, ENUM_COLOR)
BASED_DECLARE_ENUM(color, based::bu8, 0, ENUM_COLOR)
BASED_DEFINE_ENUM(color, based::bu8, 0, ENUM_COLOR)
#undef ENUM_COLOR
#define ENUM_DECOR \
reset, bold, dim, italic, underline, blink, unused_1, inverse, hide, strike
BASED_DECLARE_ENUM(decor, based::bi8, 0, ENUM_DECOR)
BASED_DEFINE_ENUM(decor, based::bi8, 0, ENUM_DECOR)
BASED_DECLARE_ENUM(decor, based::bu8, 0, ENUM_DECOR)
BASED_DEFINE_ENUM(decor, based::bu8, 0, ENUM_DECOR)
#undef ENUM_DECOR
#define ENUM_MOTION end, begin, whole
BASED_DECLARE_ENUM(motion, based::bi8, 0, ENUM_MOTION)
BASED_DEFINE_ENUM(motion, based::bi8, 0, ENUM_MOTION)
BASED_DECLARE_ENUM(motion, based::bu8, 0, ENUM_MOTION)
BASED_DEFINE_ENUM(motion, based::bu8, 0, ENUM_MOTION)
#undef ENUM_MOTION
namespace detail
{
namespace helper
{
template<std::size_t n>
static constexpr std::size_t size(based::string_literal<n> /*val*/)
{
@@ -79,10 +77,10 @@
static constexpr char* append(char* ptr, T val)
static constexpr std::string make(auto... args)
{
std::string res((helper::size(args) + ... + 2), 0);
std::string res((size(args) + ... + 2), 0);
res[0] = 0x1B, res[1] = '[';
auto* ptr = res.data() + 2; // NOLINT
((ptr = helper::append(ptr, args)), ...);
((ptr = append(ptr, args)), ...);
return res;
}
@@ -91,19 +89,17 @@
struct escape_t
{
static constexpr const auto value = []()
{
std::array<char, (helper::size(args) + ... + 3)> arr = {};
std::array<char, (size(args) + ... + 3)> arr = {};
arr[0] = 0x1B, arr[1] = '[';
auto* ptr = arr.data() + 2;
((ptr = helper::append(ptr, args)), ...);
((ptr = append(ptr, args)), ...);
return arr;
}();
static constexpr auto data = value.data();
};
} // namespace helper
template<auto... args>
static constexpr auto escape = alec::detail::helper::escape_t<args...>::data;
static constexpr auto escape = alec::detail::escape_t<args...>::data;
template<based::string_literal... strs>
static constexpr auto escape_literal = escape<strs...>;
@@ -147,11 +143,11 @@
template<unsigned col>
static constexpr auto cursor_column_v = detail::escape<col, 'G'>;
// Erase functions
template<motion::type motion>
static constexpr auto erase_display_v = detail::escape<motion.value, 'J'>;
template<motion::enum_type motion>
static constexpr auto erase_display_v = detail::escape<motion(), 'J'>;
template<motion::type motion>
static constexpr auto erase_line_v = detail::escape<motion.value, 'K'>;
template<motion::enum_type motion>
static constexpr auto erase_line_v = detail::escape<motion(), 'K'>;
// Scroll up/down
template<unsigned cnt>
@@ -166,13 +162,11 @@
static constexpr auto cursor_position_v = detail::escape<row, ';', col, 'H'>;
// color
// palet colors
template<color::type color>
static constexpr auto foreground_v<color> =
detail::escape<color.value + 30, 'm'>;
template<color::enum_type color>
static constexpr auto foreground_v<color> = detail::escape<color() + 30, 'm'>;
template<color::type color>
static constexpr auto background_v<color> =
detail::escape<color.value + 40, 'm'>;
template<color::enum_type color>
static constexpr auto background_v<color> = detail::escape<color() + 40, 'm'>;
// 256-color palette
template<based::bu8 idx>
@@ -193,11 +187,11 @@
static constexpr auto background_v<red, green, blue> =
detail::escape<48, ';', 2, ';', red, ';', green, ';', blue, 'm'>;
// Set/reset text decorators
template<decor::type decor>
static constexpr auto decor_set_v = detail::escape<decor.value, 'm'>;
template<decor::enum_type decor>
static constexpr auto decor_set_v = detail::escape<decor(), 'm'>;
template<decor::type decor>
static constexpr auto decor_reset_v = detail::escape<decor.value + 20, 'm'>;
template<decor::enum_type decor>
static constexpr auto decor_reset_v = detail::escape<decor() + 20, 'm'>;
// Save/restore cursor position;
static constexpr auto cursor_save_v = detail::escape<'s'>;
@@ -237,90 +231,90 @@
static constexpr auto paste_disable_v = detail::escape_literal<"?2004l">;
// Move cursor up/down/frwd/back
static constexpr auto cursor_up(unsigned cnt)
{
return detail::helper::make(cnt, 'A');
return detail::make(cnt, 'A');
}
static constexpr auto cursor_down(unsigned cnt)
{
return detail::helper::make(cnt, 'B');
return detail::make(cnt, 'B');
}
static constexpr auto cursor_frwd(unsigned cnt)
{
return detail::helper::make(cnt, 'C');
return detail::make(cnt, 'C');
}
static constexpr auto cursor_back(unsigned cnt)
{
return detail::helper::make(cnt, 'D');
return detail::make(cnt, 'D');
}
// Move cursor to the next/prev line
static constexpr auto cursor_line_next(unsigned cnt)
{
return detail::helper::make(cnt, 'E');
return detail::make(cnt, 'E');
}
static constexpr auto cursor_line_prev(unsigned cnt)
{
return detail::helper::make(cnt, 'F');
return detail::make(cnt, 'F');
}
// Set cursor to specific column
static constexpr auto cursor_column(unsigned col)
{
return detail::helper::make(col, 'G');
return detail::make(col, 'G');
}
// Erase functions
static constexpr auto erase_display(motion::type motion)
static constexpr auto erase_display(motion::enum_type motion)
{
return detail::helper::make(motion.value, 'J');
return detail::make(motion(), 'J');
}
static constexpr auto erase_line(motion::type motion)
static constexpr auto erase_line(motion::enum_type motion)
{
return detail::helper::make(motion.value, 'K');
return detail::make(motion(), 'K');
}
// Scroll up/down
static constexpr auto scroll_up(unsigned cnt)
{
return detail::helper::make(cnt, 'S');
return detail::make(cnt, 'S');
}
static constexpr auto scroll_down(unsigned cnt)
{
return detail::helper::make(cnt, 'T');
return detail::make(cnt, 'T');
}
// Set cursor to a specific position
static constexpr auto cursor_position(unsigned row, unsigned col)
{
return detail::helper::make(row, ';', col, 'H');
return detail::make(row, ';', col, 'H');
}
// color
// palet colors
static constexpr auto foreground(color::type color)
static constexpr auto foreground(color::enum_type color)
{
return detail::helper::make(color.value + 30, 'm');
return detail::make(color() + 30, 'm');
}
static constexpr auto background(color::type color)
static constexpr auto background(color::enum_type color)
{
return detail::helper::make(color.value + 40, 'm');
return detail::make(color() + 40, 'm');
}
// 256-color palette
static constexpr auto foreground(based::bu8 idx)
{
return detail::helper::make(38, ';', 5, ';', idx, 'm');
return detail::make(38, ';', 5, ';', idx, 'm');
}
static constexpr auto background(based::bu8 idx)
{
return detail::helper::make(48, ';', 5, ';', idx, 'm');
return detail::make(48, ';', 5, ';', idx, 'm');
}
// RGB colors
@@ -328,36 +322,36 @@
static constexpr auto foreground(
based::bu8 red, based::bu8 green, based::bu8 blue
)
{
return detail::helper::make(38, ';', 2, ';', red, ';', green, ';', blue, 'm');
return detail::make(38, ';', 2, ';', red, ';', green, ';', blue, 'm');
}
static constexpr auto background(
based::bu8 red, based::bu8 green, based::bu8 blue
)
{
return detail::helper::make(48, ';', 2, ';', red, ';', green, ';', blue, 'm');
return detail::make(48, ';', 2, ';', red, ';', green, ';', blue, 'm');
}
// Set/reset text decorators
static constexpr auto decor_set(decor::type decor)
static constexpr auto decor_set(decor::enum_type decor)
{
return detail::helper::make(decor.value, 'm');
return detail::make(decor(), 'm');
}
static constexpr auto decor_reset(decor::type decor)
static constexpr auto decor_reset(decor::enum_type decor)
{
return detail::helper::make(decor.value + 20, 'm');
return detail::make(decor() + 20, 'm');
}
// Set screen modes
static constexpr auto screen_mode_set(unsigned mode)
{
return detail::helper::make('=', mode, 'h');
return detail::make('=', mode, 'h');
}
static constexpr auto screen_mode_reset(unsigned mode)
{
return detail::helper::make('=', mode, 'l');
return detail::make('=', mode, 'l');
}
} // namespace alec
diff --git a/ include/alec/terminal.hpp b/ include/alec/terminal.hpp
@@ -4,6 +4,9 @@
#include <optional>
#include <stdexcept>
#include <based/enum/enum.hpp>
#include <based/enum/enum_flag.hpp>
#include <based/types/types.hpp>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
@@ -20,18 +23,13 @@
public:
}
};
enum error_code_t // NOLINT
{
FDNTERM,
TERMIOSRD,
TERMIOSWR,
BUFFULL,
CHARRD,
IOCTL,
SCREENSZ
};
#define ENUM_ERROR_CODE \
fdnterm, termiosrd, termioswr, buffull, charrd, ioctl, screensz
BASED_DECLARE_ENUM(error_code_t, based::bi8, 0, ENUM_ERROR_CODE)
BASED_DEFINE_ENUM(error_code_t, based::bi8, 0, ENUM_ERROR_CODE)
#undef ENUM_ERROR_CODE
template<error_code_t e>
template<error_code_t::enum_type e>
class error : public runtime_error
{
public:
@@ -41,26 +39,26 @@
public:
}
private:
static std::string error_get_message(error_code_t error)
static std::string error_get_message(error_code_t::enum_type error)
{
switch (error) {
case error_code_t::FDNTERM:
switch (error()) {
case error_code_t::fdnterm():
return "File descriptor is not associated with a terminal";
case error_code_t::TERMIOSRD:
case error_code_t::termiosrd():
return "Can't read termios";
case error_code_t::TERMIOSWR:
case error_code_t::termioswr():
return "Can't write termios";
case error_code_t::BUFFULL:
case error_code_t::buffull():
return "Buffer is full";
case error_code_t::CHARRD:
case error_code_t::charrd():
return "Can't read character";
case error_code_t::IOCTL:
case error_code_t::ioctl():
return "ioctl error";
case error_code_t::SCREENSZ:
case error_code_t::screensz():
return "Can't determine the screen size";
default:
return "alec error, should not happen...";
}
return "alec error, should not happen...";
}
};
@@ -72,11 +70,11 @@
public:
, m_orig_termios()
{
if (isatty(m_fd) == 0) {
throw error<error_code_t::FDNTERM>();
throw error<error_code_t::fdnterm>();
}
if (tcgetattr(m_fd, &m_orig_termios) == -1) {
throw error<error_code_t::TERMIOSRD>();
throw error<error_code_t::termiosrd>();
}
struct termios raw = m_orig_termios;
@@ -92,7 +90,7 @@
public:
/* put terminal in raw mode after flushing */
if (tcsetattr(m_fd, TCSAFLUSH, &raw) < 0) {
throw error<error_code_t::TERMIOSWR>();
throw error<error_code_t::termioswr>();
}
}
@@ -137,7 +135,7 @@
private:
{
ssize_t scnt = -1;
if ((m_end + 1) % m_buffer.size() == m_start) {
throw error<error_code_t::BUFFULL>();
throw error<error_code_t::buffull>();
}
if (m_start <= m_end) {
@@ -147,7 +145,7 @@
private:
}
if (scnt == -1) {
throw error<error_code_t::CHARRD>();
throw error<error_code_t::charrd>();
}
const auto cnt = static_cast<size_t>(scnt);
@@ -191,61 +189,60 @@
inline std::pair<std::uint16_t, std::uint16_t> get_screen_size()
#elif defined(TIOCGWINSZ)
struct winsize tts = {};
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &tts) == -1) { // NOLINT
throw error<error_code_t::IOCTL>();
throw error<error_code_t::ioctl>();
}
return {tts.ws_col, tts.ws_row};
#endif /* TIOCGSIZE */
throw error<error_code_t::SCREENSZ>();
throw error<error_code_t::screensz>();
}
#define ENUM_EVENT_TYPE none, key, resize, mouse
#define ENUM_EVENT_MOD none, alt, ctrl, shift, motion
class event
{
public:
enum class Type : std::uint8_t
{
NONE = 0,
KEY = 1,
RESIZE = 2,
MOUSE = 3,
};
BASED_DECLARE_ENUM(type, based::bu8, 0, ENUM_EVENT_TYPE)
BASED_DECLARE_ENUM_FLAG(mod, based::bu8, ENUM_EVENT_MOD)
enum class Mod : std::uint8_t
{
ALT = 1,
CTRL = 2,
SHIFT = 4,
MOTION = 8,
};
event(Type type, uint8_t mod_mask, uint8_t key) // NOLINT
event(type::enum_type type, uint8_t mod_mask, uint8_t key) // NOLINT
: m_type(type)
, m_mod_mask(mod_mask)
, m_key(key)
{
}
const auto& type() const { return m_type; }
auto& type() { return m_type; }
[[nodiscard]] const auto& type() const { return m_type; }
[[nodiscard]] auto& type() { return m_type; }
const auto& key() const { return m_key; }
auto& key() { return m_key; }
[[nodiscard]] const auto& key() const { return m_key; }
[[nodiscard]] auto& key() { return m_key; }
const auto& mod_mask() const { return m_mod_mask; }
auto& mod_mask() { return m_mod_mask; }
[[nodiscard]] const auto& mod_mask() const { return m_mod_mask; }
[[nodiscard]] auto& mod_mask() { return m_mod_mask; }
bool is_set(uint8_t mask) const { return mask == (m_mod_mask & mask); }
[[nodiscard]] bool is_set(uint8_t mask) const
{
return mask == (m_mod_mask & mask);
}
private:
Type m_type = Type::NONE;
type::enum_type m_type = type::none;
uint8_t m_mod_mask = 0;
uint8_t m_key = 0;
};
BASED_DEFINE_ENUM_FLAG_CLASS(event, mod, based::bu8, ENUM_EVENT_MOD)
#undef ENUM_EVENT_MOD
BASED_DEFINE_ENUM_CLASS(event, type, based::bu8, 0, ENUM_EVENT_TYPE)
#undef ENUM_EVENT_TYPE
inline event get_event()
{
const auto chr = get_buffer().value().read();
return {chr != 0 ? event::Type::KEY : event::Type::NONE, 0, chr};
return {chr != 0 ? event::type::key : event::type::none, 0, chr};
}
} // namespace alec