Mads
Multi-Agent Distributed System
Loading...
Searching...
No Matches
goback.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cstddef>
5#include <iostream>
6
7#if defined(_WIN32)
8#ifndef NOMINMAX
9#define NOMINMAX
10#endif
11#define WIN32_LEAN_AND_MEAN
12#include <windows.h>
13#endif
14
15namespace Mads {
16
17class GoBack {
18public:
19 explicit GoBack(std::size_t lines) : _lines(lines) {}
20
21 std::size_t lines() const noexcept { return _lines; }
22
23private:
24 std::size_t _lines;
25
26 friend std::ostream &operator<<(std::ostream &os, const GoBack &cmd) {
27 if (cmd._lines == 0U) {
28 return os;
29 }
30
31#if defined(_WIN32)
32 HANDLE handle = invalid_handle();
33 if (&os == &std::cout) {
34 handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
35 } else if (&os == &std::cerr || &os == &std::clog) {
36 handle = ::GetStdHandle(STD_ERROR_HANDLE);
37 }
38
39 if (handle != invalid_handle()) {
40 if (try_enable_vt(handle)) {
41 write_ansi(os, cmd._lines);
42 return os;
43 }
44
45 if (clear_with_console_api(handle, cmd._lines)) {
46 return os;
47 }
48 }
49#endif
50
51 write_ansi(os, cmd._lines);
52 return os;
53 }
54
55#if defined(_WIN32)
56 static HANDLE invalid_handle() noexcept {
57 return INVALID_HANDLE_VALUE;
58 }
59
60 static bool try_enable_vt(HANDLE handle) {
61 DWORD mode = 0;
62 if (!::GetConsoleMode(handle, &mode)) {
63 return false;
64 }
65
66 if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0) {
67 return true;
68 }
69
70 return ::SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
71 }
72
73 static bool clear_with_console_api(HANDLE handle, std::size_t lines) {
74 CONSOLE_SCREEN_BUFFER_INFO info{};
75 if (!::GetConsoleScreenBufferInfo(handle, &info)) {
76 return false;
77 }
78
79 const SHORT width = info.dwSize.X;
80 if (width <= 0) {
81 return false;
82 }
83
84 SHORT cursor_y = info.dwCursorPosition.Y;
85 SHORT first_row = static_cast<SHORT>(std::max<int>(0, static_cast<int>(cursor_y) - static_cast<int>(lines)));
86
87 for (SHORT row = static_cast<SHORT>(cursor_y - 1); row >= first_row; --row) {
88 DWORD written = 0;
89 COORD row_start{0, row};
90 if (!::FillConsoleOutputCharacterA(handle, ' ', static_cast<DWORD>(width), row_start, &written)) {
91 return false;
92 }
93 if (!::FillConsoleOutputAttribute(handle, info.wAttributes, static_cast<DWORD>(width), row_start, &written)) {
94 return false;
95 }
96 if (row == 0) {
97 break;
98 }
99 }
100
101 COORD target{0, first_row};
102 if (!::SetConsoleCursorPosition(handle, target)) {
103 return false;
104 }
105
106 return true;
107 }
108#endif
109
110 static void write_ansi(std::ostream &os, std::size_t lines) {
111 for (std::size_t i = 0; i < lines; ++i) {
112 os << "\x1b[1A\x1b[2K";
113 if (i + 1U < lines) {
114 os << '\r';
115 }
116 }
117 os << '\r' << std::flush;
118 }
119};
120
121inline GoBack goback(std::size_t lines, bool really = true) {
122 if (really) {
123 return GoBack(lines);
124 }
125 return GoBack(0);
126}
127
128} // namespace Mads
std::size_t lines() const noexcept
Definition goback.hpp:21
GoBack(std::size_t lines)
Definition goback.hpp:19
friend std::ostream & operator<<(std::ostream &os, const GoBack &cmd)
Definition goback.hpp:26
Definition agent.hpp:67
GoBack goback(std::size_t lines, bool really=true)
Definition goback.hpp:121