Неблокирующий стдио

Я работаю над программой, которая будет принимать пользовательский ввод с консоли, а также распечатывать в отдельном потоке. Я хочу избежать ситуаций, когда пользователь набирает что-то на полпути, а появляется printf и печатает себя на курсоре.

Есть ли способ сделать неблокирующий ввод-вывод в c из окна консоли? В идеале, захват нажатия клавиш или что-то в этом роде, чтобы то, что печатает пользователь, не отображалось на экране. Я разрабатываю в Ubuntu, и будет лучше, если мне не придется использовать такие вещи, как ncurses.


person Smashery    schedule 14.08.2009    source источник


Ответы (3)


используя termios, вы можете отключить эхо терминала:

#include <termios.h>

struct termios oflags, nflags;
tcgetattr(fileno(stdin), &oflags);
nflags = oflags;
nflags.c_lflag &= ~ECHO;
nflags.c_lflag |= ECHONL;

if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) {
    /* handle error */
}

затем перед выходом (используйте atexit) вы должны восстановить терминал:

if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) {
    /* handle error */
}
person dfa    schedule 14.08.2009
comment
Спасибо за это. Почему ECHONL? - person Neil Traft; 02.03.2015

Вот пример того, как отключить эхо от C, взятый непосредственно из форум HP (я лично его не проверял):

Хорошо, это должен быть простой пример отключения эха:

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

#define STDIN_FDES 0

struct termios save;

int main(int argc, char *argv[])
{
int cc = 0;
char s_tmp[80],*p = NULL;
struct termios work;

cc = tcgetattr(STDIN_FDES,&save);
work = save;
work.c_lflag &= ~(ECHO);
cc = tcsetattr(STDIN_FDES,TCSANOW,&work);
(void) printf("\nEnter value: ");
(void) fflush(stdout);
p = fgets(s_tmp,sizeof(s_tmp),stdin);
if (p != NULL) (void) printf("Out -> %s\n",p);
cc = tcsetattr(STDIN_FDES,TCSANOW,&save);
return(cc);
}

ПРИМЕЧАНИЕ. Очень важно, чтобы у вас были обработчики сигналов для перехвата сигналов SIGINT, SIGTERM, ... и сброса терминала с использованием исходных терминов, поскольку выигрывает последний tcsetattr(), и это относится к терминальному устройству, а НЕ просто к процессу. Если вы оставите процесс с отключенным эхом, он также будет отключен в оболочке.

В противном случае, если Bash является подходящим подходом, очевидно, вы можете просто сделать stty -echo.

person Mark Rushakoff    schedule 14.08.2009

Отключение эха или использование неблокирующего ввода-вывода не является ответом, если я правильно понимаю ваш вопрос. Скорее, вы хотите, чтобы фоновый поток не прерывал поток пользовательского ввода, верно?

Для этого вам понадобится доступ к необработанным нажатиям клавиш вместо линейного буферизованного ввода. Я не знаю, почему у вас аллергия на ncurses или подобные библиотеки; вот для чего они! Я думаю, вы могли бы сделать это с вызовами termios или ioctl, если вы так катаетесь....

Но чтобы решить проблему с многопоточным выводом TTY, вы можете сделать это:

1) Создайте мьютекс, чтобы контролировать, кто может получить доступ к консоли

В фоновой цепочке для вывода сообщения:

Возьмите мьютекс; написать сообщение; освободить мьютекс; вернулся спать!

В потоке пользовательского ввода:

Захватите мьютекс при обнаружении нового ввода. Сохраняйте эксклюзивный доступ до тех пор, пока пользователь не нажмет Enter, затем освободите мьютекс и дайте фоновому потоку возможность поговорить.

Это помогает?

person Jim Lewis    schedule 14.08.2009
comment
Взгляните на cfmakeraw() для необработанного режима терминала. Обязательно сохраните структуру termios перед вызовом этой функции, чтобы вы могли восстановить ее, когда закончите. - person Rob Jones; 14.08.2009