Почему терминал зависает при запуске программы на C?

#c #linux #terminal

#c #linux #терминал

Вопрос:

Я пытаюсь прочитать ввод от пользователя в необработанном режиме терминала.

Вот функция, которая включает режим raw и отключает некоторые escape-символы и все. Он также ссылается на disableRawMode, который используется для повторного входа в канонический режим после считывания входных данных:

 void enableRawMode() {
     if (tcgetattr(STDIN_FILENO, amp;orig_termios) == -1) 
        die("tcgetattr");
    atexit(disableRawMode);
    struct termios raw = orig_termios;
    raw.c_iflag amp;= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    raw.c_oflag amp;= ~(OPOST);
    raw.c_cflag |= (CS8);
    raw.c_lflag amp;= ~(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 0;
    raw.c_cc[VTIME] = 1;
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, amp;raw) == -1)
        die("tcsetattr");
}
  

Существуют также некоторые другие функции (editorReadKey amp; editorprocessKeypress) для считывания нажатия клавиши и обработки ввода символов:

 char editorReadKey() {
    int nread;
    char c;
    while ((nread = read(STDIN_FILENO, amp;c, 1) != 1)) {
        if (nread == -1 amp;amp; errno != EAGAIN) 
            die("read");
    }
}

void editorProcessKeypress() {
    char c = editorReadKey();
    switch (c) {
    case CTRL_KEY('q'):
        exit(0);
        break;
    }
}
  

Но когда я вызываю функции в основной программе:

 int main(void) {    
    enableRawMode();
    while (1) {
        editorRefreshScreen();
        editorProcessKeypress();
    }
    return 0;
}
  

Мой терминал просто зависает, и я должен перезапустить его, чтобы он снова заработал. Какие-либо причины, по которым это может происходить?

Комментарии:

1. Существует разница между «при компиляции» и «при запуске».

2. Если вы вставляете вывод в бесконечный цикл внутри main() , как часто вы видите этот вывод?

3. Извините за это. Так получилось, что английский не является моим родным языком, и я все еще привыкаю к нему. Отредактировал вопрос.

4. @Yunnosch Я использую бесконечный цикл только потому, что хочу считывать входные данные до нажатия кнопки «q». В этом случае, я полагаю, цикл завершится.

5. Вы ничего не возвращаете editorReadKey() , и компилятор C просто принимает это. Это означает, что ни одна из ваших операций с ключами никогда не срабатывает, поэтому ваш код никогда ничего не делает и поэтому выглядит замороженным. Всегда создавайте код на C с -Wall

Ответ №1:

Возможно, этот пример делает то, что вы хотите. Вывод минимален, поскольку в этом суть 🙂

 terminal info saved
raw mode will be set now
Enter 'q' to exit!

terminal configuration restored
  

Программа просто сохраняет конфигурацию терминала, переходит в режим raw и считывает данные с ввода до тех пор, пока не будет прочитан «q» или пользователь не нажмет control-C. Затем терминал сбрасывается и конфигурация восстанавливается.

 #include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

typedef struct termios Termios;

void        editorProcessKeypress();
char        editorReadKey();
void        editorRefreshScreen();
void        enableRawMode(Termios*);
void        gameOver();
int         getMode(Termios*);
int         setMode(Termios*);

int main(void)
{ 
    Termios original_termios;
    Termios raw;
    if ( getMode(amp;original_termios) < 0)
    {
        fprintf(stderr,"Could not get terminal info");
        return(-1);
    }
    printf("terminal info savedn");

    enableRawMode(amp;raw);

    printf("Enter 'q' to exit!n");
    editorRefreshScreen();
    char in = editorReadKey();

    while( in != 'q' )
    {
           editorRefreshScreen();
           in = editorReadKey();
    };

    if ( setMode(amp;original_termios) < 0)
    {
        fprintf(stderr,"Could not reset terminal. Please run 'tput reset'!");
        return(-1);
    }
    printf("nterminal configuration restoredn");
    return 0;
};

void enableRawMode(Termios* raw)
{
    raw->c_iflag amp;= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    raw->c_oflag amp;= ~(OPOST);
    raw->c_cflag |= (CS8);
    raw->c_lflag amp;= ~(ECHO | ICANON | IEXTEN ) | ISIG;
    raw->c_cc[VINTR] = 3;
    raw->c_cc[VMIN] = 0;
    raw->c_cc[VTIME] = 10;

    signal(SIGINT, gameOver);

    printf("raw mode will be set nown");
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, raw) < 0)
        printf("raw mode NOT set!!n");
    fflush(stdin);
};

char editorReadKey() {
    int nread;
    char c = 0;
    while ((nread = read(STDIN_FILENO, amp;c, 1) != 1)) {
        if (nread == -1 amp;amp; errno != EAGAIN) 
            return 0;
    };
    return c;
};


void        editorRefreshScreen()
{
    return;
};


void        gameOver()
{
    Termios reset;
    reset.c_iflag =  ~IGNCR | ICRNL | INPCK  | ISTRIP | IXON | BRKINT | ICANON;
    reset.c_oflag = ECHO | OPOST;
    reset.c_lflag = ECHO | ICANON | IEXTEN;
    tcsetattr(STDIN_FILENO, TCSANOW, amp;reset);
    printf("n^C: Aborting... Issue a 'tput reset' if terminal not okn");
    exit(1);
};


int         getMode( Termios* term )
{
        return tcgetattr(STDIN_FILENO, term);
};


int         setMode(Termios* term)
{
        return tcsetattr(STDIN_FILENO, TCSANOW, term);
};