You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改说明

  1. 线程安全的输入记录

    • 新增current_input字符串和input_mutex互斥锁,实时记录用户输入的字符,保证多线程访问时的安全性。
  2. 终端模式处理

    • Windows平台:关闭行输入和默认回显,通过ReadConsole逐个读取字符,自行处理回车、退格操作及字符回显。
    • Linux/Mac平台:使用termios将终端设置为原始模式,关闭行缓冲和默认回显,逐个读取字符并处理回车、退格及回显逻辑,最后恢复终端原始属性。
  3. get_terminal_line简化实现

    • 不再依赖读取屏幕缓冲区的逻辑,直接返回线程安全的当前输入记录,避免了Unix-like系统下无直接屏幕读取API的问题。
  4. 输出干扰规避

    • 使用ANSI转义序列\33[2K\r清除当前行并将光标移至行首,输出内容后重新打印用户当前输入的内容,完全避免输出对输入的干扰。

内容的提问来源于stack exchange,提问作者user23537908

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:49:58