Linux/Mac下无ncurses实现C++终端同时输入输出
无需ncurses,在Linux/Mac实现终端同时输入输出(避免输入干扰)
现有代码已完成Windows平台的终端输入输出同步逻辑,针对Linux/Mac平台,我们可以通过接管终端输入处理、实时记录当前输入内容的方式实现需求,无需依赖ncurses库。以下是修改后的完整代码及关键说明:
完整代码
#include <iostream> #include <string> #include <thread> #include <atomic> #include <chrono> #include <mutex> #include <cctype> #if defined(_WIN32) #include <windows.h> #include <conio.h> #elif defined(__unix__) || defined(__APPLE__) #include <termios.h> #include <unistd.h> #include <sys/ioctl.h> #endif using namespace std; mutex input_mutex; string current_input; void ReadCin(atomic<bool>& run) { #if defined(_WIN32) HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); DWORD mode; GetConsoleMode(hStdIn, &mode); SetConsoleMode(hStdIn, mode & ~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT); char ch; DWORD bytesRead; while (run.load()) { if (ReadConsoleA(hStdIn, &ch, 1, &bytesRead, nullptr) && bytesRead > 0) { lock_guard<mutex> lock(input_mutex); if (ch == '\r') { cout << endl; if (current_input == "Quit") { run.store(false); } current_input.clear(); } else if (ch == '\b' && !current_input.empty()) { cout << "\b \b"; current_input.pop_back(); } else if (isprint(static_cast<unsigned char>(ch))) { cout << ch; current_input += ch; } cout.flush(); } } SetConsoleMode(hStdIn, mode); #elif defined(__unix__) || defined(__APPLE__) struct termios orig_termios; if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) { return; } struct termios raw = orig_termios; raw.c_lflag &= ~(ICANON | ECHO); raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); return; } char ch; while (run.load() && read(STDIN_FILENO, &ch, 1) == 1) { lock_guard<mutex> lock(input_mutex); if (ch == '\n') { cout << endl; if (current_input == "Quit") { run.store(false); } current_input.clear(); } else if (ch == 127 && !current_input.empty()) // ASCII DEL { cout << "\b \b"; current_input.pop_back(); } else if (isprint(static_cast<unsigned char>(ch))) { cout << ch; current_input += ch; } cout.flush(); } tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); #endif } string get_terminal_line() { lock_guard<mutex> lock(input_mutex); string line = current_input; size_t end = line.find_last_not_of(' '); if (end != string::npos) { line.erase(end + 1); } else { line.clear(); } return line; } void PrintNewLine(atomic<bool>& run) { while (run.load()) { this_thread::sleep_for(chrono::seconds(2)); string line = get_terminal_line(); // 清行并移动到行首,输出内容 cout << "\33[2K\rNew line printed every 2 seconds" << endl; if (!line.empty()) { cout << line; cout.flush(); } } } int main() { atomic<bool> run(true); thread cinThread(ReadCin, ref(run)); thread printThread(PrintNewLine, ref(run)); while (run.load()) { this_thread::sleep_for(chrono::milliseconds(100)); } run.store(false); cinThread.join(); printThread.join(); return 0; }
关键修改说明
线程安全的输入记录
- 新增
current_input字符串和input_mutex互斥锁,实时记录用户输入的字符,保证多线程访问时的安全性。
- 新增
终端模式处理
- Windows平台:关闭行输入和默认回显,通过
ReadConsole逐个读取字符,自行处理回车、退格操作及字符回显。 - Linux/Mac平台:使用
termios将终端设置为原始模式,关闭行缓冲和默认回显,逐个读取字符并处理回车、退格及回显逻辑,最后恢复终端原始属性。
- Windows平台:关闭行输入和默认回显,通过
get_terminal_line简化实现- 不再依赖读取屏幕缓冲区的逻辑,直接返回线程安全的当前输入记录,避免了Unix-like系统下无直接屏幕读取API的问题。
输出干扰规避
- 使用ANSI转义序列
\33[2K\r清除当前行并将光标移至行首,输出内容后重新打印用户当前输入的内容,完全避免输出对输入的干扰。
- 使用ANSI转义序列
内容的提问来源于stack exchange,提问作者user23537908
相关产品推荐
相关产品推荐

