× К оглавлению На главную Об авторе

Дата и время публикации:

Теория и практика


1. Теория

В данном режиме терминал производит вывод на экран (stdout) вводимых пользователем символов c устройства ввода (stdin), как показано на рисунке 1.1

Рисунок 1.1

На рисунке 1.1 показан весь процесса ввода/вывода символов в терминале пользователя, в котором участвуют читающий и пишущий процессы на пользовательском уровне, драйвер tty на уровне ядра. Последний обеспечивает буферизацию данных в двух очередях: вывода (output queue) и ввода (input queue). Обе очереди имеют максимальный размер, поэтому по мере заполнения очереди вывода пишущий процесс будет ждать окончания передачи символов на устройство вывода, в то время как считающий процесс будет их терять при достижении максимального размера очереди ввода.

При включенном режиме ECHO обеспечивается возможность непосредственной передачи символов из очереди ввода в очередь вывода драйвера tty без участия пишущего процесса на пользовательском уровне.

Поэтому, в начале работы программы, следует сбрасывать флаг ECHO для того, чтобы предотвратить выдачу символов на экран самой операционной системой.

Потому чтобы

2. Практика

2.1 Управления режимом драйверa tty

Для включения или, как показано в листингe 2.1.2, отключения следует управляющей структуре tty-драйвера termios(3), передать соответствующий флаг.

Определение структуры данных приведено в листинге 2.1.1

Листинг 2.1.1

000 
001 struct termios{
002             tcflag_t  c_iflag   /* Флаги управления очередью ввода */
003             tcflag_t  c_oflag,  /* Флаги управления очередью вывода  */
004             tcflag_t  c_cflag,  /* Флаги управления режимами на выводе */ 
005             tcflag_t  c_lflag,  /* Флаги режимов локальной обработки на вводе */ 
006             cc_t     c_cc[NCCS] /* Специальные символы */
007 };
008 

Режим ECHO относится к локально-обрабатываем на вводе. За его установку и снятие отвечает одноименный флаг ECHO, как показано в листинге 2.1.2

Листинг 2.1.2

000 struct termios tp;
001  
002   if (tcgetattr(STDIN_FILENO, &tp) == -1)
003         perror("tcgetattr");
004 
005     tp.c_lflag &= ~ECHO;       
006   
007   if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tp) == -1)
008         perror("tcsetattr");
009 

В листинге 2.1.3 показан пример реализации включения и выключения режима на контролируемой части экрана в ncurses.

Листинг 2.1.3

    ...
 27 #include "ucurses.h"
 28 #include "ukeys.h"
    ...
106    	        if ( KEYS_CMP(KEYMSG_ECHO) == KEYS_COMPARED ) 
107                  {
108 		    if( UCURSES_FLAGS_CTRL(ucursesp,WITH_ECHO,UCURSES_ENABLE) == OK )
109 		      {			    	
110 		        wprint_eol ( w, 0, "echo enable." );
111                       }
112 		    goto Case_to_print_CLI; 
113                  }
114    	         if ( KEYS_CMP(KEYMSG_NOECHO) == KEYS_COMPARED ) 
115                  {
116 		    if( UCURSES_FLAGS_CTRL(ucursesp,WITH_ECHO,UCURSES_DISABLE) == OK )
117 		      {		    	
118 		        wprint_eol ( w, 0, "echo disable." );
119                       }
120 		    goto Case_to_print_CLI; 
121                  }
    ...

Оператор-выражения UCURSES_FLAGS_CTRL() производит включение режима и принимает флаг WITH_ECHO и состояние UCURSES_ENABLE в строке 108 листинга 2.1.3), а для его отключения – тотже флаг, но состояние UCURSES_DISABLE в строке 40-44 того же листинга. Указанная процедура объявлена в заголовочном файле <ucurses.h> в строке листинга 2.1.3 в виде оператора-выражения. В листинге 2.1.4 приводится фрагмент кода данного заголовочного файла <ucurses.h>.

Листинг 2.1.4

    ...
 61 #define UCURSES_ENABLE     1
 62 #define UCURSES_DISABLE    0
    ...
 64  #define UCURSES_FLAGS_CTRL(wbase,flags,on)({  \
 65     int              __ret   = ERR,            \
 66                      __on    = (on);           \
 67     ucurses_wbase_t* __wbase = (wbase);        \
 68     if( (__wbase) )                            \
 69       {                                        \
 70         int   __flags = (flags);               \
 71         if( ((__flags) & WITH_ECHO) )          \
 72          {                                     \
 73             (__wbase)->ucurses_flags = ((__on) == UCURSES_ENABLE ?       \
 74 				        UCURSES_ECHO_ENBL((__flags)) :  \
 75 					UCURSES_ECHO_DSBL((__flags)) ); \
 76 	   __ret = OK;                     \
 77         }                                  \
 78      }                                     \
 79     __ret;                                 \
 80  })
    ...

Как показано в листинге 2.1.4, при передаче флага WITH_ECHO, в строках 73-75 осуществляется вызов операторы-выражения UCURSES_ECHO_ENBL() и UCURSES_ECHO_DSBL(), которые вызываются в зависимости от выбранного состояния UCURSES_ENABLE или UCURSES_DISABLE, передаваемого в третьем аргументе on оператора-выражения UCURSES_FLAGS_CTRL().

Cоответственно, управленнием режимом непосредственно происходит в операторах-выражениях UCURSES_ECHO_ENBL() и UCURSES_ECHO_DSBL(), которые определены в заголовочном файле <ucurses-modes.h>, фрагмент исходного кода показан в листинге 2.1.5.

Листинг 2.1.5

    ...
 30   #define WITH_ECHO	  0x01
    ...
 34   #define UCURSES_FLAG_ENBL(f,flags)({   \
 35         int           __f = (f);         \
 36         int           __flags = flags;   \
 37         if ( !((__flags) & (__f)) )      \
 38            {                             \
 39              flags |=  (__f);            \
 40   	 }                              \
 41         __flags; })
 42    
 43   #define UCURSES_FLAG_DSBL(f,flags)({   \
 44       int           __f = (f);          \
 45       int           __flags = flags;    \
 46       if ( ((__flags) & (__f)) )        \
 47          {                              \
 48            flags &= ~(__f);             \
 49 	 }                              \
 50       __flags; })   
 51 
 52   #define UCURSES_ECHO_ENBL(flags)({                \
 53 	   int  __ret   = 0;                        \
 54            int  __flags = UCURSES_FLAG_ENBL(WITH_ECHO,flags); \
 55            echo();                                 \
 56        	    __flags; })
 57 
 58   #define UCURSES_ECHO_DSBL(flags)({                \
 59            int  __flags = UCURSES_FLAG_DSBL(WITH_ECHO,flags); \
 60                 noecho();                           \
 61        	       __flags; })
    ...  

В строке 55, оператор-выражение UCURSES_ECHO_ENBL() вызывает библиотечную процедуру echo() библиотеки ncurses, которая включает режим ECHO в свою очередь.

В строке 60, оператор-выражение UCURSES_ECHO_DSBL() вызывает библиотечную процедуру noecho() библиотеки ncurses, которая уже выключает режим ECHO .

На рисунке 2.1 приведены результаты выполнения вышерассмотренной цепочки, которую запускает оператор-выражение UCURSES_FLAGS_CTRL().

Рисунок 1.1

Как видно из результатов на рисунке 2.1 выполнения кода из листинга 2.3, в ncurses процедуры echo() и noecho() контролируют ввод каждого символа пользователем с клавиатуры и его отображение на экране терминала. При этом за чтение символа из потока вывода(stdout) отвечает getch(3) .

Следовательно, перенаправление символа с ввода (stdin) на вывод (stdout) предпочтительней делать на уровне пользователя, чтобы избегать дублирование и случайный выброс символа, что может вызвать нарушение целостности интерфейса у пользователя.

3. Библиография

3.1 echo(3) - Linux man page: curses input options

3.2 termios(3) - Linux man page: get and set terminal attributes, line control, get and set baud rate

3.3 ECHO вывод сообщений и переключения режима отображение команд на экране

3.4 The Single UNIX ® Specification, Version 2. Interface Overview

3.5 tty/no_echo.c from “The Linux Programming Interfase

3.6 Creating menu with Ncurses in C

3.7 the tty layer (2.4)