koutil
Loading...
Searching...
No Matches
terminal.h
Go to the documentation of this file.
1#ifndef KOUTIL_TERM_TERMINAL_H
2#define KOUTIL_TERM_TERMINAL_H
3
4#include "koutil/term/style.h"
5#include "koutil/util/utils.h"
6#include <cassert>
7#include <csignal>
8#include <cstddef>
9#include <cstdlib>
10#include <functional>
11#include <iostream>
12#include <memory>
13#include <stack>
14#include <string_view>
15
16#if defined(OS_LINUX)
17 #include <sys/ioctl.h>
18 #include <sys/select.h>
19 #include <termios.h>
20 #include <unistd.h>
21#elif defined(OS_WINDOWS)
22 #define WIN32_LEAN_AND_MEAN
23
24 #ifndef NOMINMAX
25 #define NOMINMAX
26 #endif
27
28 #include <io.h>
29 #include <Windows.h>
30#endif
31
32namespace koutil::term {
33
37enum class ColorSupport {
38 COLOR16,
41};
42
46struct Dimensions {
47 std::size_t width;
48 std::size_t height;
49};
50
51class terminal {
52public:
53 enum class Error {
54 NONE,
59 SETUP,
60 };
61
66 static bool init();
67
71 static void register_signals();
72
76 static void rollback();
77
83
88 static Error error();
89
94 static bool has_error();
95
101
103
104private:
105 std::stack<std::function<void()>> m_on_exit;
107 bool m_has_signals = false;
109
110 terminal() = default;
111
112 void exit() {
113 while (!m_on_exit.empty()) {
114 auto& top = m_on_exit.top();
115 top();
116
117 m_on_exit.pop();
118 }
119 }
120
121 static void handle_exit_signal(int /*unused*/) { rollback(); }
122
123 static std::unique_ptr<terminal> s_instance;
124};
125
126std::unique_ptr<terminal> terminal::s_instance = nullptr;
127
129 using namespace std::string_view_literals;
130
131 if (s_instance != nullptr) {
133 return false;
134 }
135
136 s_instance.reset(new terminal());
137
138#if defined(OS_WINDOWS)
139 s_instance->m_color_support = ColorSupport::TRUE_COLOR;
140#else
141 std::string_view colorterm = util::safe_getenv("COLORTERM");
142 if (colorterm.find("24bit") != std::string_view::npos || colorterm.find("truecolor") != std::string_view::npos) {
143 s_instance->m_color_support = ColorSupport::TRUE_COLOR;
144 }
145
146 std::string_view term = util::safe_getenv("TERM");
147 if (colorterm.find("256") != std::string_view::npos || term.find("256") != std::string_view::npos) {
148 s_instance->m_color_support = ColorSupport::COLOR256;
149 }
150#endif
151
152#if defined(OS_WINDOWS)
153 HANDLE console_out = GetStdHandle(STD_OUTPUT_HANDLE);
154 HANDLE console_in = GetStdHandle(STD_INPUT_HANDLE);
155
156 DWORD mode_out = 0;
157 DWORD mode_in = 0;
158
159 if (SetConsoleOutputCP(CP_UTF8) == false) {
161 return false;
162 }
163
164 if (console_in == INVALID_HANDLE_VALUE || !GetConsoleMode(console_in, &mode_in)) {
166 return false;
167 }
168
169 if (console_out == INVALID_HANDLE_VALUE || !GetConsoleMode(console_out, &mode_out)) {
171 return false;
172 }
173
174 s_instance->m_on_exit.push([=] { SetConsoleMode(console_in, mode_in); });
175 s_instance->m_on_exit.push([=] { SetConsoleMode(console_out, mode_out); });
176
177 mode_out |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
178 mode_in |= ENABLE_VIRTUAL_TERMINAL_INPUT | ENABLE_WINDOW_INPUT;
179
180 if (!SetConsoleMode(console_in, mode_in) || !SetConsoleMode(console_out, mode_out)) {
181 s_instance->exit();
182 s_instance->m_error = Error::SETUP;
183 return false;
184 }
185#endif
186 std::at_quick_exit(rollback);
187 std::atexit(rollback);
188
189 s_instance->m_on_exit.emplace([]() {
190 std::cout << reset_all;
191 std::cout.flush();
192 });
193
194 return true;
195}
196
198 assert(s_instance != nullptr);
199
200 if (s_instance->m_has_signals) {
201 return;
202 }
203 s_instance->m_has_signals = true;
204
205 auto add_signal_handler = [](int sig) {
206 auto old_handler = std::signal(sig, handle_exit_signal);
207 s_instance->m_on_exit.emplace([=]() { std::signal(sig, old_handler); });
208 };
209
210 for (auto&& sig : { SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE }) {
211 add_signal_handler(sig);
212 }
213
214#ifdef OS_LINUX
215 add_signal_handler(SIGQUIT);
216
217#endif
218}
219
221
223 assert(s_instance != nullptr);
224 return s_instance->m_color_support;
225}
226
228 assert(s_instance != nullptr);
229 return s_instance->m_error;
230}
231
233 assert(s_instance != nullptr);
234 return s_instance->m_error != Error::NONE;
235}
236
238#if defined(OS_LINUX)
239
240 winsize ws;
241
242 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
243 ws.ws_row = 0;
244 ws.ws_col = 0;
245 }
246 return { .width = ws.ws_col, .height = ws.ws_row };
247
248#elif defined(OS_WINDOWS)
249 CONSOLE_SCREEN_BUFFER_INFO csbi;
250 HANDLE console_out = GetStdHandle(STD_OUTPUT_HANDLE);
251
252 if (console_out == INVALID_HANDLE_VALUE || !GetConsoleScreenBufferInfo(console_out, &csbi)) {
253 return { 0, 0 };
254 }
255
256 return { .width = csbi.srWindow.Right - csbi.srWindow.Left + 1,
257 .height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1 };
258
259#else
260 return { 0, 0 };
261#endif
262}
263}
264
265#endif
Definition terminal.h:51
static std::unique_ptr< terminal > s_instance
Definition terminal.h:123
std::stack< std::function< void()> > m_on_exit
Definition terminal.h:105
static Dimensions query_dimensions()
Queries the dimensions of the terminal window.
Definition terminal.h:237
static Error error()
Retrieves the current error state of the terminal.
Definition terminal.h:227
static bool has_error()
Checks if the terminal has encountered an error.
Definition terminal.h:232
bool m_has_signals
Definition terminal.h:107
static void handle_exit_signal(int)
Definition terminal.h:121
ColorSupport m_color_support
Definition terminal.h:108
void exit()
Definition terminal.h:112
~terminal()
Definition terminal.h:102
static void rollback()
Rolls back changes made to the terminal.
Definition terminal.h:220
static bool init()
Initializes the terminal.
Definition terminal.h:128
static ColorSupport color_support()
Retrieves the level of color support for the terminal.
Definition terminal.h:222
static void register_signals()
Registers signal handlers for the terminal.
Definition terminal.h:197
Error
Definition terminal.h:53
Error m_error
Definition terminal.h:106
Definition color.h:11
std::ostream & reset_all(std::ostream &stream)
Resets all text styles and colors to default.
Definition style.h:109
ColorSupport
Enumerates the levels of color support for the terminal.
Definition terminal.h:37
std::string_view safe_getenv(const char *name)
Definition utils.h:22
Represents the dimensions of the terminal buffer.
Definition terminal.h:46
std::size_t height
Definition terminal.h:48
std::size_t width
Definition terminal.h:47