alec

Abstraction Layer for Escape Codes
git clone git://git.dimitrijedobrota.com/alec.git
Log | Files | Refs | README | LICENSE

commit 22c370f54016fa15ea40033194db9ba0e5894acd
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Fri, 29 Dec 2023 18:21:25 +0000

Inital commit, proof of conept

Diffstat:
A.clang-format | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.gitignore | 2++
ACMakeLists.txt | 19+++++++++++++++++++
Ademo/CMakeLists.txt | 8++++++++
Ademo/demo.cpp | 24++++++++++++++++++++++++
Asrc/CMakeLists.txt | 13+++++++++++++
Asrc/alec.hpp | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 445 insertions(+), 0 deletions(-)

diff --git a/.clang-format b/.clang-format @@ -0,0 +1,216 @@ +--- +Language: Cpp +# BasedOnStyle: Microsoft +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 110 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Right +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RequiresClausePosition: OwnLine +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +build +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.25.2) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +project( + Alec + VERSION 0.0.1 + DESCRIPTION "Abstraction Layer for Escape Codes" + HOMEPAGE_URL https://git.dimitrijedobrota.com/alec.git + LANGUAGES CXX +) +enable_testing() + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_subdirectory(src) +add_subdirectory(demo) + diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(demo demo.cpp) +target_link_libraries(demo alec) + +set_target_properties(demo PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" +) diff --git a/demo/demo.cpp b/demo/demo.cpp @@ -0,0 +1,24 @@ +#include "alec.hpp" +#include <iostream> + +int main(void) { + std::cout << ALEC::ABUF_SHOW << ALEC::CURSOR_HIDE; + + std::cout << ALEC::CURSOR_POSITION(1, 1) << ALEC::FOREGROUND(91) << "HELLO!\n"; + + std::cout << ALEC::CURSOR_DOWN(3); + std::cout << ALEC::FOREGROUND(30) << ALEC::BACKGROUND(196) << "WORLD!\n"; + + std::cout << ALEC::BACKGROUND(ALEC::DEFAULT) << "testing 1...\n" << ALEC::FOREGROUND(ALEC::DEFAULT); + std::cout << ALEC::DECOR_SET(ALEC::INVERSE) << "testing 2...\n" << ALEC::DECOR_RESET(ALEC::INVERSE); + + std::cout << ALEC::CURSOR_UP(5) << "Hello there!" << ALEC::CURSOR_SAVE; + std::cout << ALEC::CURSOR_DOWN(10) << "General Kenobi!"; + std::cout << ALEC::CURSOR_POSITION(10, 40) << "no pain no gain" << ALEC::CURSOR_LOAD; + + getchar(); + + std::cout << ALEC::CURSOR_SHOW << ALEC::ABUF_HIDE; + + return 0; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(alec INTERFACE) +target_include_directories(alec INTERFACE .) + +set_target_properties(alec PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + PUBLIC_HEADER alec.hpp +) + +install(TARGETS alec + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION include +) diff --git a/src/alec.hpp b/src/alec.hpp @@ -0,0 +1,163 @@ +#ifndef ALEC_ALEC_H +#define ALEC_ALEC_H + +#include <format> +#include <iostream> + +namespace ALEC { + +enum CTRL { + BELL = 0x07, + BS = 0x08, + HT = 0x09, + LF = 0x0A, + VT = 0x0B, + FF = 0x0C, + CR = 0x0D, + ESC = 0x1B, + DEL = 0x7F, +}; + +enum COLOR { + BLACK = 0, + RED = 1, + GREEN = 2, + YELLOW = 3, + BLUE = 4, + MAGENTA = 5, + CYAN = 6, + WHITE = 7, + DEFAULT = 9, +}; + +enum DECOR { + RESET = 0, + BOLD = 1, + DIM = 2, + ITALIC = 3, + UNDERLINE = 4, + BLINK = 5, + INVERSE = 7, + HIDE = 8, + STRIKE = 9, +}; + +template <typename... Args> class CMD { + public: + constexpr CMD(const char c) : c(c) {} + constexpr std::string operator()(Args... args) const { + return std::format(fmt, (char)CTRL::ESC, args..., c); + } + + private: + const char c; + + static constexpr auto arr = []() { + constexpr std::size_t len = sizeof...(Args) * 3 + 4; + std::array<char, len + 1> arr = {'{', '}', '['}; + for (std::size_t i = 3; i < len - 1; i += 3) { + arr[i] = '{', arr[i + 1] = '}', arr[i + 2] = ';'; + } + arr[len - 2] = '{', arr[len - 1] = '}', arr[len] = 0; + return arr; + }(); + static constexpr auto fmt = std::string_view(arr.data(), arr.size() - 1); +}; + +static constexpr const CMD<int> CURSOR_UP('A'); +static constexpr const CMD<int> CURSOR_DOWN('B'); +static constexpr const CMD<int> CURSOR_FRWD('C'); +static constexpr const CMD<int> CURSOR_BACK('D'); +static constexpr const CMD<int> CURSOR_LINE_NEXT('E'); +static constexpr const CMD<int> CURSOR_LINE_PREV('F'); +static constexpr const CMD<int> CURSOR_COLUMN('G'); +static constexpr const CMD<int> ERASE_DISPLAY('J'); +static constexpr const CMD<int> ERASE_LINE('K'); +static constexpr const CMD<int> SCROLL_UP('S'); +static constexpr const CMD<int> SCROLL_DOWN('T'); + +static constexpr const CMD<int, int> CURSOR_POSITION('H'); + +// palet colors +static constexpr auto FOREGROUND(COLOR color) { + static constexpr const CMD<int> bootstrap('m'); + return bootstrap(30 + color); +} + +static constexpr auto BACKGROUND(COLOR color) { + static constexpr const CMD<int> bootstrap('m'); + return bootstrap(40 + color); +} + +// 256-color palette +static constexpr auto FOREGROUND(int idx) { + static constexpr const CMD<int, int, int> bootstrap('m'); + return bootstrap(38, 5, idx); +} + +static constexpr auto BACKGROUND(int idx) { + static constexpr const CMD<int, int, int> bootstrap('m'); + return bootstrap(48, 5, idx); +} + +// RGB colors +static constexpr auto FOREGROUND(int r, int g, int b) { + static constexpr const CMD<int, int, int, int, int> bootstrap('m'); + return bootstrap(38, 5, r, g, b); +} + +static constexpr auto BACKGROUND(int r, int g, int b) { + static constexpr const CMD<int, int, int, int, int> bootstrap('m'); + return bootstrap(48, 5, r, g, b); +} + +// Set/Reset text decorators +static constexpr auto DECOR_SET(DECOR decor) { + static constexpr const CMD<int> bootstrap('m'); + return bootstrap(static_cast<int>(decor)); +} + +static constexpr auto DECOR_RESET(DECOR decor) { + static constexpr const CMD<int> bootstrap('m'); + return bootstrap(static_cast<int>(decor) + 20); +} + +template <class CharT, class Traits> +static constexpr std::basic_ostream<CharT, Traits> &CURSOR_SAVE(std::basic_ostream<CharT, Traits> &bos) { + static auto sequence = CMD<>('s')(); + return bos << sequence; +} + +template <class CharT, class Traits> +static constexpr std::basic_ostream<CharT, Traits> &CURSOR_LOAD(std::basic_ostream<CharT, Traits> &bos) { + static auto sequence = CMD<>('u')(); + return bos << sequence; +} + +template <class CharT, class Traits> +static constexpr std::basic_ostream<CharT, Traits> &CURSOR_SHOW(std::basic_ostream<CharT, Traits> &bos) { + static auto sequence = CMD<const char *>('h')("?25"); + return bos << sequence; +} + +template <class CharT, class Traits> +static constexpr std::basic_ostream<CharT, Traits> &CURSOR_HIDE(std::basic_ostream<CharT, Traits> &bos) { + static auto sequence = CMD<const char *>('l')("?25"); + return bos << sequence; +} + +template <class CharT, class Traits> +static constexpr std::basic_ostream<CharT, Traits> &ABUF_SHOW(std::basic_ostream<CharT, Traits> &bos) { + static auto sequence = CMD<const char *>('h')("?1049"); + return bos << sequence; +} + +template <class CharT, class Traits> +static constexpr std::basic_ostream<CharT, Traits> &ABUF_HIDE(std::basic_ostream<CharT, Traits> &bos) { + static auto sequence = CMD<const char *>('l')("?1049"); + return bos << sequence; +} + +} // namespace ALEC + +#endif