Lectura de un Carácter sin Esperas Mediante POSIX

El siguiente ejemplo muestra como usar el módulo POSIX para construir una función que hace una lectura de un carácter sin buffers intermedios ni esperar por un retorno de carro. Su único propósito es mostrar el uso del módulo POSIX.

lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n HotKey.pm
 1  # HotKey.pm
 2  package HotKey;
 3  use strict;
 4
 5  our @ISA = qw(Exporter);
 6  our @EXPORT = qw(cbreak cooked readkey);
 7
 8  use strict;
 9  use POSIX qw(:termios_h);
10  my ($term, $oterm, $echo, $noecho, $fd_stdin);
11
12  $fd_stdin = fileno(STDIN); # fileno retorna el fd de STDIN
13  $term     = POSIX::Termios->new();
14  $term->getattr($fd_stdin); # Rellenamos los campos de $term
15  $oterm     = $term->getlflag(); # Obtiene el campo c_lflag 
16  # Flags: eco o no; eco kill o no; lectura con buffer o no; 
17  $echo     = ECHO | ECHOK | ICANON; # posibles valores para c_lflag
18  $noecho   = $oterm & ~$echo; # Complento a uno
19
20  sub cbreak {
21    $term->setlflag($noecho);  # no se hace echo
22    $term->setcc(VTIME, 1); # marcharse 1 decima de segundo despues del dato
23    $term->setattr($fd_stdin, TCSANOW);
24  }
25
26  sub cooked {
27    $term->setlflag($oterm);
28    $term->setcc(VTIME, 0);
29    $term->setattr($fd_stdin, TCSANOW);
30  }
31
32  sub readkey {
33    my $key = '';
34    cbreak();
35    sysread(STDIN, $key, 1);
36    cooked();
37    return $key;
38  }
39
40  END { cooked() }
41
42  1;
Además de los modos raw y cooked los caracteres de entrada son procesados según sean los valores de los campos c_iflag y c_lflag.

El campo c_lflag se usa para controlar varias funcionalidades. Se contruye como el OR de las máscaras que aparecen en la tabla 7.2.


Tabla 7.2: Tabla de Modos Locales
Máscara Descripción
ECHO Permitir eco.
ECHOE Eco ERASE como corrector backspace.
ECHOK Eco KILL.
ECHONL Eco <newline>.
ICANON Entrada Canónica (procesado de erase y kill).
IEXTEN Permitir extensiones (implementation-dependent).
ISIG Permitir señales.
NOFLSH Desactivar flush después de interrupt, quit o suspend.
TOSTOP Enviar SIGTTOU a salida en background.
XCASE Presentación Canónica upper/lower. (LEGACY)


Para entender mejor el programa es necesario tener en cuenta los siguientes hechos:

  1. Si ECHO se desactiva no se hará eco de los caracteres de entrada.

  2. Si esta puesto ICANON se permite el procesado canónico. Si no se pone ICANON, las lecturas se satisfacen directamente desde la cola de entrada. Una lectura no se satisface hasta que se hayan recibido VMIN bytes o haya expirado el crono VTIME entre bytes.

  3. A diferencia de lo que ocurre con una lectura desde fichero donde los datos están o no están, en una lectura desde un canal tty aparecen temas relacionados con el tiempo. Por ejemplo, en un teclado ANSI la flecha hacia arriba corresponde a entre eventos como 'el usuario tecleo ESC' frente a 'el usuario tecleo flecha hacia arriba'. Es por eso que en un objeto Termios figuran los campos VTIME (líneas 22 y 28) y VMIN que gobiernan la forma de lectura.

En la sesión que sigue, después de la línea 2 el proceso queda suspendido a la espera de un carácter. El usuario a pulsado la tecla x.

lhp@nereida:~/Lperl/src/perl_networking/ch2$ perl -de 0
  DB<1> use HotKey
  DB<2> $x = readkey()

  DB<3> x $x
0  'x'
  DB<4>

Por razones de eficiencia y generalidad, es mas aconsejable usar el módulo Term::ReadKey.

lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n noecho.pl
 1  use strict;
 2  use Term::ReadKey;
 3  ReadMode('cbreak');
 4  print "Press keys to see their ASCII values.  Use Ctrl-C to quit.\n";
 5
 6  while (1) {
 7      my $char = ReadKey(0);
 8      last unless defined $char;
 9      printf(" Decimal: %d\tHex: %x\n", ord($char), ord($char));
10  }
11
12  ReadMode('normal');

Casiano Rodríguez León
Licencia de Creative Commons
Programación Distribuida y Mejora del Rendimiento
por Casiano Rodríguez León is licensed under a Creative Commons Reconocimiento 3.0 Unported License.

Permissions beyond the scope of this license may be available at http://campusvirtual.ull.es/ocw/course/view.php?id=44.
2012-06-19