防止在控制台上按键写入 Linux C++

prevent write on console at keypress linux c++

本文关键字:Linux C++ 控制台      更新时间:2023-10-16

我正在尝试用C++做一个简单的游戏(Pong)。该游戏是"主机游戏"。我刚刚编写了部分代码,但现在我发现了一个麻烦:我创建了一个 _getch() 和 _kbhit() 函数

    int _getch( ) {
        struct termios oldt, newt;
        int ch;
        tcgetattr( STDIN_FILENO, &oldt );
        newt = oldt;
        newt.c_lflag &= ~( ICANON | ECHO );
        tcsetattr( STDIN_FILENO, TCSANOW, &newt );
        ch = getchar();
        tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
        return ch;
}
int _kbhit() {
        static const int STDIN = 0;
        static bool initialized = false;
        if (! initialized) {
                termios term;
                tcgetattr(STDIN, &term);
                term.c_lflag &= ~ICANON;
                tcsetattr(STDIN, TCSANOW, &term);
                setbuf(stdin, NULL);
                initialized = true;
        }
        int bytesWaiting;
        ioctl(STDIN, FIONREAD, &bytesWaiting);
        return bytesWaiting;
}

但是当我按下一个键时,它会打印在控制台上。我怎样才能防止这种情况发生?

我的完整代码:

#include <iostream> 
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>
#include <stropts.h>
#include <sys/ioctl.h>
using namespace std;
void gotoxy(int x,int y){
       printf("x1b[%d;%df",y,x);
}
int _getch( ) {
        struct termios oldt, newt;
    int ch;
    tcgetattr( STDIN_FILENO, &oldt );
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt );
    ch = getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
    return ch;
}
int _kbhit() {
    static const int STDIN = 0;
    static bool initialized = false;
    if (! initialized) {
        // Use termios to turn off line buffering
        termios term;
        tcgetattr(STDIN, &term);
        term.c_lflag &= ~ICANON;
        tcsetattr(STDIN, TCSANOW, &term);
        setbuf(stdin, NULL);
        initialized = true;
    }
    int bytesWaiting;
    ioctl(STDIN, FIONREAD, &bytesWaiting);
    return bytesWaiting;
}
class Ball{
    public:
};
class Game{
    public:
        void __init(){
            this->pointL = 0;
            this->pointR = 0;
        }
        void addScore(char side){
        if(side == 'l') pointL++;
        else if(side == 'r') pointR++;
        else cout<< "ERROR-2";
            return;
        }
        unsigned int getScore(char side){
            if(side == 'l') return this->pointL;
            else if (side == 'r') return this->pointR;
            else return 0;
        }
        bool isPlaying(){
            return this->playing;
        }
        bool stop(){
            this->playing = false;
            return true;
        }   
    private:
        unsigned int pointL, pointR;
        bool playing;
};
class Player{
    public:
        int pos[5][2];
        int maxX, maxY;
        void __init(int maxX, int maxY, char side){
            //create matrix with all cords of block of wall (r and l)
            this->maxX = maxX;
            this->maxY = maxY;
            if(side == 'l')
                for(int i = 0; i<5; i++){
                    pos[i][0] = 2;
                    pos[i][1] = 2+i;
                    gotoxy(pos[i][0],pos[i][1]);
                    cout<< "|";
                }
            else if(side == 'r')
                for(int i = 0; i<5; i++){
                    pos[i][0] = maxX-4;
                    pos[i][1] = 2+i;
                    gotoxy(pos[i][0],pos[i][1]);
                    cout<< "|";
                }
            else
                cout<<"ERRORE-1";
        }
        void moveUp(){
            gotoxy(pos[4][0],pos[4][1]);
            cout<< " ";
            for(int i = 0; i<5; i++){
                pos[i][1] = (pos[i][1] == 0)?pos[i][1]:pos[i][1]-1;
                gotoxy(pos[i][0],pos[i][1]);
                cout<< "|"; //solid rectangle
            }
        }
        void moveDown(){
            gotoxy(pos[4][0],pos[4][1]);
            cout<< " ";
            for(int i = 0; i<5; i++){
                pos[i][1] = (pos[i][1] == this->maxY)?pos[i][1]:pos[i][1]+1;
                gotoxy(pos[i][0],pos[i][1]);
                cout<< "|"; //solid rectangle
            }
        }
};
int main(){
    int a;
    Game game;
    Player pl1,pl2;
    cout<< "Ridimensiona la finestra come meglio preferisci, quando hai fatto, premi un tasto per continuare";
    _getch();
    struct winsize size;
    ioctl( 0, TIOCSWINSZ, (char *) &size );
    printf("e[2Je[H");
    pl1.__init(size.ws_row, size.ws_col, 'l');
    pl2.__init(size.ws_row, size.ws_col, 'r');
    //asap menu here
    cout<< "TEST: " << size.ws_row;
    char key;
    while(game.isPlaying()){
        if(_kbhit()){
            key = _getch();
            switch(key){ //when i press that keys, it's printed on terminal, how prevent?
                case 'w':
                    pl1.moveUp();
                    break;
                case 's':
                    pl2.moveDown();
                    break;
                case 'q':
                    game.stop();
                    break;
            }   
        }
    }
    return 0;
}

这是一个不回显击键而是将字符写入 stderr 的程序。如果你编译它,比如说,用g++ -o t t.cpp那么你可以用例如 ./t 2>somefile,从而将 stderr 重定向到 somefile

在第二个终端中,您可以在每次击键后执行cat somefile以控制程序写入的内容。(警告:less somefile在我的 cygwin 安装上不起作用,因为文件增长只出现在更少的块中(我告诉它通过按"F"等待输入))。

由于所有击键都转发到控制程序Control-C或其他通过其控制终端向正在运行的程序发送信号的方法不起作用。您必须从不同的终端杀死它。

基本思想是 read() 在原始模式下阻塞,直到输入可用,因此不需要单独的kbhit()

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
using namespace std;
int main()
{
    struct termios oldt;
    if( tcgetattr( 0, &oldt ) ) 
    { 
        fprintf(stderr, "Error getting term attribsn"); 
    }
    cfmakeraw(&oldt);
    if( tcsetattr(0, TCSANOW, &oldt) ) 
    {
        fprintf(stderr, "Error setting term attribsn");
    }
    char inp;
    while(true)
    {
        int bytesRead = read(0, &inp, 1);
        if( bytesRead <= 0)
        {
            fprintf(stderr, "oops, bytes read return val is %dn", bytesRead);
        }
        else
        {
            write(2, &inp, 1);
        }
    }
}

您可以使用:

#include <iostream>
#include <stdexcept>
#include <termios.h>
#include <unistd.h>
// A class for mofdifying the behavour of a terminal.
class Terminal
{
    // Types
    // =====
    public:
    typedef speed_t speed_type;
    // Terminal
    // ========
    public:
    // Initialize the terminal file descriptor and store the attributes of the terminal.
    Terminal(int fd)
    :   m_fd(fd), m_restore(get(fd))
    {}
    // Restore the orignal attributes of the terminal
    ~Terminal() {
        set(m_fd, m_restore, false);
    }
    Terminal(const Terminal&) = delete;
    const Terminal& operator = (const Terminal&) = delete;
    int fd() const { return m_fd; }
    void restore() { set(m_fd, m_restore); }
    protected:
    // Get attributes of a terminal
    static termios get(const int fd) {
        termios attributes;
        if(tcgetattr(fd, &attributes) < 0) {
            throw std::runtime_error("Terminal");
        }
        return attributes;
    }
    // Set attributes of a terminal
    static void set(const int fd, const termios& attributes, bool exception = true) {
        if(tcsetattr(fd, TCSANOW, &attributes) < 0 && exception) {
            throw std::runtime_error("Terminal");
        }
    }
    // Set attributes of a terminal
    static void set(const int fd, int action, const termios& attributes, bool exception = true) {
        if(tcsetattr(fd, action, &attributes) < 0 && exception) {
            throw std::runtime_error("Terminal");
        }
    }
    private:
    int m_fd;
    termios m_restore;
};
// A class for mofdifying the input behavour of a terminal.
class StdInputTerminal : public Terminal
{
    // Constants
    // =========
    public:
    enum Attributes {
        Blocking = 0x01,
        Echo = 0x02
    };
    // Static
    // ======
    public:
    // Clear available input in std::cin
    static void clear() {
        termios attributes = disable_attributes(Blocking);
        while(std::cin)
            std::cin.get();
        std::cin.clear();
        set(fileno(stdin), attributes);
    }
    // StdInputTerminal
    // ================
    public:
    // Initialize with 'stdin'
    StdInputTerminal()
    :   Terminal(fileno(stdin))
    {}
    public:
    // Disable attributes specified by any combination of Attributes flags
    void disable(unsigned flags) { disable_attributes(flags); }
    // Disable blocking
    void disable_blocking() { disable_attributes(Blocking); }
    protected:
    // Set attributes of the terminal
    static termios disable_attributes(unsigned flags) {
        const int fd = fileno(stdin);
        termios attributes = get(fd);
        termios a = attributes;
        if(flags & Blocking) {
            a.c_lflag &= ~ICANON;
            a.c_cc[VMIN] = 0;
            a.c_cc[VTIME] = 0;
        }
        if(flags & Echo) {
            a.c_lflag &= ~ECHO;
        }
        set(fd, a);
        return attributes;
    }
};

// Sample Usage
// ============
int kbhit() {
    StdInputTerminal terminal;
    terminal.disable(StdInputTerminal::Blocking | StdInputTerminal::Echo);
    int result = 0;
    char c;
    if( ! std::cin.get(c))
        std::cin.clear();
    else
    {
        result = c;
        std::cin.unget();
    }
    return result;
}
int getch() {
    StdInputTerminal terminal;
    terminal.disable(StdInputTerminal::Blocking | StdInputTerminal::Echo);
    char result = 0;
    while(true) {
        std::cin.get(result);
        if(std::cin.eof()) {
            std::cin.clear();
            usleep(100000);
        }
        else break;
    }
    if(std::cin.fail()) {
        std::cin.clear();
        result = 0;
    }
    return result;
}

// Test
// ====
int main()
{
    std::cout << "Enter a single sample character: ";
    char c;
    std::cin.get(c);
    std::cin.unget();
    if(kbhit()) {
        int ch = getch();
        std::cout << "Entered character: " << char(ch);
        if(kbhit()) {
            int ch = getch();
            std::cout << " Newline " << char(ch);
            if(kbhit()) {
                std::string line;
                getline(std::cin, line);
                std::cout << "nToo many sample characters: " << line << 'n';
            }
        }
    }
}

(请原谅不完整的课程,你的是实验性的)