fix: fix console helper
- add annotations for console helper for better user experience. - change build macro to allow using console helper in non-Windows platform. because console color macros is universal and should not be limited in Windows platform.
This commit is contained in:
parent
629a608133
commit
b6c53ac707
|
@ -1,18 +1,23 @@
|
||||||
#include "ConsoleHelper.hpp"
|
#include "ConsoleHelper.hpp"
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
#include "EncodingHelper.hpp"
|
||||||
#include "StringHelper.hpp"
|
#include "StringHelper.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// Include Windows used headers in Windows.
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
#include "WinImportPrefix.hpp"
|
#include "WinImportPrefix.hpp"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include "WinImportSuffix.hpp"
|
#include "WinImportSuffix.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace YYCC::ConsoleHelper {
|
namespace YYCC::ConsoleHelper {
|
||||||
|
|
||||||
|
#pragma region Windows Specific Functions
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
static bool RawEnableColorfulConsole(FILE* fs) {
|
static bool RawEnableColorfulConsole(FILE* fs) {
|
||||||
if (!_isatty(_fileno(fs))) return false;
|
if (!_isatty(_fileno(fs))) return false;
|
||||||
|
|
||||||
|
@ -26,38 +31,30 @@ namespace YYCC::ConsoleHelper {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnableColorfulConsole(FILE* fs) {
|
/*
|
||||||
if (!RawEnableColorfulConsole(stdout)) return false;
|
Reference:
|
||||||
if (!RawEnableColorfulConsole(stderr)) return false;
|
* https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
||||||
return true;
|
* https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
||||||
}
|
|
||||||
|
|
||||||
//template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, char> || std::is_same_v<_Ty, wchar_t>, int> = 0>
|
There is 3 way to make Windows console enable UTF8 mode.
|
||||||
//static bool FetchEOL(std::basic_string<_Ty>& internal_buffer, std::basic_string<_Ty>& result_buffer) {
|
|
||||||
// // try finding EOL in internal buffer
|
|
||||||
// size_t pos;
|
|
||||||
// if constexpr (std::is_same_v<_Ty, char>) internal_buffer.find_first_of('\n');
|
|
||||||
// else internal_buffer.find_first_of(L'\n');
|
|
||||||
|
|
||||||
// // check finding result
|
First one is calling SetConsoleCP and SetConsoleOutputCP.
|
||||||
// if (pos == std::wstring::npos) {
|
The side effect of this is std::cin and std::cout is broken,
|
||||||
// // the whole string do not include EOL, fully appended to return value
|
however there is a patch for this issue.
|
||||||
// result_buffer += internal_buffer;
|
|
||||||
// internal_buffer.clear();
|
Second one is calling _set_mode with _O_U8TEXT or _O_U16TEXT to enable Unicode mode for Windows console.
|
||||||
// // return false mean need more data
|
This also have side effect which is stronger than first one.
|
||||||
// return false;
|
All puts family functions (ASCII-based output functions) will throw assertion exception.
|
||||||
// } else {
|
You only can use putws family functions (wide-char-based output functions).
|
||||||
// // split result
|
However these functions can not be used without calling _set_mode in Windows design.
|
||||||
// // push into result and remain some in internal buffer.
|
|
||||||
// result_buffer.append(internal_buffer, 0u, pos);
|
There still is another method, using WriteConsoleW directly visiting console.
|
||||||
// internal_buffer.erase(0u, pos + 1u); // +1 because EOL take one place.
|
This function family can output correct string without calling any extra functions!
|
||||||
// // return true mean success finding
|
This method is what we adopted.
|
||||||
// return true;
|
*/
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
template<bool _bIsConsole>
|
template<bool _bIsConsole>
|
||||||
static std::string PlainRead(HANDLE hStdIn) {
|
static std::string WinConsoleRead(HANDLE hStdIn) {
|
||||||
using _TChar = std::conditional_t<_bIsConsole, wchar_t, char>;
|
using _TChar = std::conditional_t<_bIsConsole, wchar_t, char>;
|
||||||
|
|
||||||
// Prepare an internal buffer because the read data may not be fully used.
|
// Prepare an internal buffer because the read data may not be fully used.
|
||||||
|
@ -130,34 +127,7 @@ namespace YYCC::ConsoleHelper {
|
||||||
return real_return_buffer;
|
return real_return_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ReadLine() {
|
static void WinConsoleWrite(const std::string& strl) {
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
// get stdin mode
|
|
||||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
// use different method to get according to whether stdin is redirected
|
|
||||||
DWORD dwConsoleMode;
|
|
||||||
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
|
||||||
return PlainRead<true>(hStdIn);
|
|
||||||
} else {
|
|
||||||
return PlainRead<false>(hStdIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif YYCC_OS == YYCC_OS_LINUX
|
|
||||||
|
|
||||||
// in linux, directly use C++ function to fetch.
|
|
||||||
std::string cmd;
|
|
||||||
if (std::getline(std::cin, cmd).fail()) cmd.clear();
|
|
||||||
return cmd;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Write(const char* u8_strl) {}
|
|
||||||
|
|
||||||
static void PlainWrite(const std::string& strl) {
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
// Prepare some Win32 variables
|
// Prepare some Win32 variables
|
||||||
// fetch stdout handle first
|
// fetch stdout handle first
|
||||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
@ -185,8 +155,56 @@ namespace YYCC::ConsoleHelper {
|
||||||
WriteFile(hStdOut, strl.c_str(), static_cast<DWORD>(strl_size), &dwWrittenNumberOfChars, NULL);
|
WriteFile(hStdOut, strl.c_str(), static_cast<DWORD>(strl_size), &dwWrittenNumberOfChars, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#elif YYCC_OS == YYCC_OS_LINUX
|
#endif
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
bool EnableColorfulConsole() {
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
|
if (!RawEnableColorfulConsole(stdout)) return false;
|
||||||
|
if (!RawEnableColorfulConsole(stderr)) return false;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// just return true and do nothing
|
||||||
|
return true
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadLine() {
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
|
// get stdin mode
|
||||||
|
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
// use different method to get according to whether stdin is redirected
|
||||||
|
DWORD dwConsoleMode;
|
||||||
|
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
||||||
|
return WinConsoleRead<true>(hStdIn);
|
||||||
|
} else {
|
||||||
|
return WinConsoleRead<false>(hStdIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// in linux, directly use C++ function to fetch.
|
||||||
|
std::string cmd;
|
||||||
|
if (std::getline(std::cin, cmd).fail()) cmd.clear();
|
||||||
|
return cmd;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RawWrite(const std::string& strl) {
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
|
// call Windows specific writer
|
||||||
|
WinConsoleWrite(strl);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
// in linux, directly use C function to write.
|
// in linux, directly use C function to write.
|
||||||
std::fputs(strl.c_str(), stdout);
|
std::fputs(strl.c_str(), stdout);
|
||||||
|
@ -197,7 +215,7 @@ namespace YYCC::ConsoleHelper {
|
||||||
void Write(const char* u8_fmt, ...) {
|
void Write(const char* u8_fmt, ...) {
|
||||||
va_list argptr;
|
va_list argptr;
|
||||||
va_start(argptr, u8_fmt);
|
va_start(argptr, u8_fmt);
|
||||||
PlainWrite(YYCC::StringHelper::VPrintf(u8_fmt, argptr));
|
RawWrite(YYCC::StringHelper::VPrintf(u8_fmt, argptr));
|
||||||
va_end(argptr);
|
va_end(argptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,10 +224,9 @@ namespace YYCC::ConsoleHelper {
|
||||||
va_start(argptr, u8_fmt);
|
va_start(argptr, u8_fmt);
|
||||||
std::string cache(YYCC::StringHelper::VPrintf(u8_fmt, argptr));
|
std::string cache(YYCC::StringHelper::VPrintf(u8_fmt, argptr));
|
||||||
cache += "\n";
|
cache += "\n";
|
||||||
PlainWrite(cache);
|
RawWrite(cache);
|
||||||
va_end(argptr);
|
va_end(argptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "YYCCInternal.hpp"
|
#include "YYCCInternal.hpp"
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -47,52 +46,64 @@ namespace YYCC::ConsoleHelper {
|
||||||
#define YYCC_COLOR_LIGHT_WHITE(T) "\033[97m" T "\033[0m"
|
#define YYCC_COLOR_LIGHT_WHITE(T) "\033[97m" T "\033[0m"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Try letting terminal support ASCII color schema.
|
* @brief Enable Windows console color support.
|
||||||
* @param fs[in] The stream to be set.
|
* @details This actually is enable virtual console feature for stdout and stderr.
|
||||||
* @return true if success, otherwise false.
|
* @return True if success, otherwise false.
|
||||||
|
* @remarks This function only works on Windows and do nothing on other platforms such as Linux,
|
||||||
|
* because we assume all terminals existing on other platform support color feature as default.
|
||||||
*/
|
*/
|
||||||
bool EnableColorfulConsole(FILE* fs);
|
bool EnableColorfulConsole();
|
||||||
|
|
||||||
/*
|
|
||||||
Reference:
|
|
||||||
* https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
|
||||||
* https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
|
||||||
|
|
||||||
There is 3 way to make Windows console enable UTF8 mode.
|
|
||||||
|
|
||||||
First one is calling SetConsoleCP and SetConsoleOutputCP.
|
|
||||||
The side effect of this is std::cin and std::cout is broken,
|
|
||||||
however there is a patch for this issue.
|
|
||||||
|
|
||||||
Second one is calling _set_mode with _O_U8TEXT or _O_U16TEXT to enable Unicode mode for Windows console.
|
|
||||||
This also have side effect which is stronger than first one.
|
|
||||||
All puts family functions (ASCII-based output functions) will throw assertion exception.
|
|
||||||
You only can use putws family functions (wide-char-based output functions).
|
|
||||||
However these functions can not be used without calling _set_mode in Windows design.
|
|
||||||
|
|
||||||
There still is another method, using WriteConsoleW directly visiting console.
|
|
||||||
This function family can output correct string without calling any extra functions!
|
|
||||||
This method is what we adopted.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief Universal console read function
|
||||||
* @return
|
* @details This function is more like C# Console.ReadLine().
|
||||||
|
* It read user input with UTF8 encoding until reaching EOL.
|
||||||
|
*
|
||||||
|
* This function provide an universal, platform-independent way to read UTF8 string from console,
|
||||||
|
* no matter whether it is redirected.
|
||||||
|
* @return The UTF8 encoded string this function read. EOL is excluded.
|
||||||
|
* @remarks In Windows, this function will try use native Win32 function for reading,
|
||||||
|
* because standard C/C++ function can not handle UTF8 input on Windows normally.
|
||||||
|
* In other platforms, this function will redirect request to std::readline with std::cin,
|
||||||
|
* which is all programmer commonly used method.
|
||||||
|
* It also mean that we assume stdin is encoded by UTF8 on these platforms.
|
||||||
|
*
|
||||||
|
* Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
|
||||||
|
* This action is actually remove all CR chars in result string.
|
||||||
|
*
|
||||||
|
* This function also can be used as ordering user press Enter key by
|
||||||
|
* simply calling this function and ignoring its return value.
|
||||||
*/
|
*/
|
||||||
std::string ReadLine();
|
std::string ReadLine();
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief Universal console write function
|
||||||
* @param u8_fmt The format, or a simple string (format related chars still need escape).
|
* @details This function is more like C# Console.Write().
|
||||||
* @param ...[in] The parameter for formatter.
|
* It write user given UTF8 string into console.
|
||||||
|
*
|
||||||
|
* This function provide an universal, platform-independent way to write UTF8 string into console,
|
||||||
|
* no matter whether it is redirected.
|
||||||
|
* @param u8_fmt[in] The format string.
|
||||||
|
* If you just want to write a pure string, you should escape formatter chars (%) in this string,
|
||||||
|
* because this function always take this parameter as a format string.
|
||||||
|
* @param ...[in] The arguments to be formatted.
|
||||||
|
* @remarks In Windows, this funcion will use native Win32 function for writing,
|
||||||
|
* because standard C/C++ function can not handle UTF8 output on Windows normally.
|
||||||
|
* In other platforms, this function will redirect request to std::fprintf with stdout,
|
||||||
|
* which is all programmer commonly used method.
|
||||||
|
* It also mean that we assume stdout is encoded by UTF8 on these platforms.
|
||||||
*/
|
*/
|
||||||
void Write(const char* u8_fmt, ...);
|
void Write(const char* u8_fmt, ...);
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief Universal console write function with automatic EOL
|
||||||
* @param u8_fmt
|
* @details This function is same as Write(const char*, ...),
|
||||||
* @param
|
* but it will automatically add EOL in output to break line.
|
||||||
|
* This is commonly used.
|
||||||
|
* @param u8_fmt[in] The format string.
|
||||||
|
* If you just want to write a pure string, you should escape formatter chars (%) in this string,
|
||||||
|
* because this function always take this parameter as a format string.
|
||||||
|
* If you want to output plain string, you need escape format
|
||||||
|
* @param ...[in] The arguments to be formatted.
|
||||||
*/
|
*/
|
||||||
void WriteLine(const char* u8_fmt, ...);
|
void WriteLine(const char* u8_fmt, ...);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
|
@ -16,7 +16,7 @@ namespace YYCCTestbench {
|
||||||
|
|
||||||
static void ConsoleTestbench() {
|
static void ConsoleTestbench() {
|
||||||
// Color Test
|
// Color Test
|
||||||
Console::EnableColorfulConsole(stdout);
|
Console::EnableColorfulConsole();
|
||||||
Console::WriteLine("Color Test:");
|
Console::WriteLine("Color Test:");
|
||||||
|
|
||||||
#define TEST_MACRO(col) Console::WriteLine("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col );
|
#define TEST_MACRO(col) Console::WriteLine("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col );
|
||||||
|
|
Loading…
Reference in New Issue
Block a user