如何使用 cpp 程序在多个 tmux 终端上获取输入和输出数据?

问题描述 投票:0回答:1

下面的代码在 tmux 中创建两个窗格并倒数到 20。

#include <iostream>
#include <fstream>
#include <thread>
#include <vector>
#include <chrono>
#include <atomic>
#include <mutex>
#include <string>
#include <array>
#include <memory>

// Function to execute a shell command and get the result
std::string exec(const std::string& cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

// Function to be executed by each thread
void threadFunction(const std::string& tty, std::atomic<bool>& running, int threadId) {
    int messageCount = 0;
    std::ofstream ttyStream(tty);
    if (!ttyStream.is_open()) {
        std::cerr << "Failed to open tty: " << tty << std::endl;
        return;
    }
    while (running.load()) {
        ttyStream << "Thread " << threadId << " is running: message " << messageCount++ << std::endl;
        ttyStream.flush();
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

void kill(const std::string& sessionName){
    std::string command = "tmux send-keys -t " + sessionName + " \" tmux kill-server\" Enter";

    // Capture output for debugging
    std::string output = exec(command); // Assuming `exec` returns the output
    std::cerr << "Command output: " << output << std::endl;

    if (std::system(command.c_str()) != 0) {
        std::cerr << "Error executing command: " << command << std::endl;
    }
}

int main() {
    const std::string sessionName = "Client";
    const int numThreads = 2; // Number of threads and panes

    // Create tmux session and panes, and get their TTYs
    std::string createSessionCmd = "tmux new-session -d -s " + sessionName + " -PF '#{pane_id}' 'tail -f /dev/null'";
    std::string pane1 = exec(createSessionCmd);
    pane1.pop_back(); // Remove newline character

    std::string splitPaneCmd = "tmux split-window -t " + sessionName + " -PF '#{pane_id}' 'tail -f /dev/null'";
    std::string pane2 = exec(splitPaneCmd);
    pane2.pop_back(); // Remove newline character

    std::string getTtyCmd1 = "tmux display-message -p -t " + pane1 + " '#{pane_tty}'";
    std::string tty1 = exec(getTtyCmd1);
    tty1.pop_back(); // Remove newline character

    std::string getTtyCmd2 = "tmux display-message -p -t " + pane2 + " '#{pane_tty}'";
    std::string tty2 = exec(getTtyCmd2);
    tty2.pop_back(); // Remove newline character

    std::cout << "TTY1: " << tty1 << std::endl;
    std::cout << "TTY2: " << tty2 << std::endl;

    std::atomic<bool> running(true);

    // Create and run threads
    std::thread t1(threadFunction, tty1, std::ref(running), 1);
    std::thread t2(threadFunction, tty2, std::ref(running), 2);
    
    std::thread t3([&sessionName]() {
        std::this_thread::sleep_for(std::chrono::seconds(20)); // Increase sleep time for testing
    
        std::string command = "tmux new-window -t " + sessionName + " -n thread" + std::to_string(2);
        std::system(command.c_str());
        kill(sessionName);

    });

    t1.detach();
    t2.detach();
    t3.detach();

    

    // Attach to the tmux session
    std::string cmd = "tmux attach -t " + sessionName;
    std::system(cmd.c_str());

    return 0;
}

我面临的问题是窗格无法接受任何输入。我希望线程在指定的窗格上显示消息。但是,如果用户想要发送消息,该窗格可以接受任何输入,那么该输入应该显示在另一个窗格中。这意味着计数消息在接受输入时停止,然后显示消息并从停止的地方开始计数。但在上面的代码中它不能做到这一点,但不仅如此,而且我无法运行任何命令。因此,即使要关闭 tmux 会话,我也必须创建一个新的 tmux 窗口并在新的 tmux 窗口上使用kill-server。

如果我不使用 tty 那么我必须这样做,以便在每个窗格中每当用户输入一串字符时不将其视为命令并将其写入文件中,然后需要有一个新线程来从此文件中读取并在特定窗格中显示该文件。

但是当我不使用 tty 时,每个“输入 + Enter”都会给出输入不是命令的输出,正如您在顶部窗格中看到的那样,显示 cpp 文件运行所需的任何内容“echo 'message' + Enter ”。意味着该消息显示两次

Not using tty and just creating tmux panes using cpp

c++ linux multithreading tmux
1个回答
0
投票

Tmux 是一种客户端服务器架构。每当您创建新的会话/窗口/窗格时,服务器都会向该元素分配新的外部虚拟/终端,就像从其他地方手动启动一样。 这意味着每个 tmux 都有自己的 stdin/stdout,尽管您使用 popen 调用启动每个会话,但与管道的端点无关。这些端点与“tmux 客户端”唯一相关,其命令立即被使用。

尽管如此,您可以:1)通过发送键命令将按键序列发送到您创建的窗口/窗格; 2) 使用手工 IPC(命名管道、tcpip 等)收集和接收“手动启动”(使用发送键)窗格进程的输出。

请记住,作为不同虚拟终端的子进程,每个窗格/窗口将运行不同的进程,其代码可以是您的程序的克隆或第二个外部程序。除非您不为每个 tmux 项编写一个哑 I/O 集线器收发器进程,否则无法让多线程程序处理许多 tmux 项。否则,如果您想使用单个多线程进程并且只需要将屏幕分成几部分,curses 库有可以满足您的范围的窗口。

© www.soinside.com 2019 - 2024. All rights reserved.