How can I read a single character from the keyboard under UNIX and DOS?


    A closely related question to the no-echo question is how to input a
    single character from the keyboard.  Again, this is a system dependent
    operation.  The following code that may or may not help you.  It should
    work on both SysV and BSD flavors of UNIX:

        $BSD = -f '/vmunix';
        if ($BSD) {
            system "stty cbreak </dev/tty >/dev/tty 2>&1";
        }
        else {
            system "stty", '-icanon',
            system "stty", 'eol', "\001"; 
        }

        $key = getc(STDIN);

        if ($BSD) {
            system "stty -cbreak </dev/tty >/dev/tty 2>&1";
        }
        else {
            system "stty", 'icanon';
            system "stty", 'eol', '^@'; # ascii null
        }
        print "\n";

    You could also handle the stty operations yourself for speed if you're
    going to be doing a lot of them.  This code works to toggle cbreak
    and echo modes on a BSD system:

    sub set_cbreak { # &set_cbreak(1) or &set_cbreak(0)
        local($on) = $_[0];
        local($sgttyb,@ary);
        require 'sys/ioctl.ph';
        $sgttyb_t   = 'C4 S' unless $sgttyb_t;  # c2ph: &sgttyb'typedef()

        ioctl(STDIN,&TIOCGETP,$sgttyb) || die "Can't ioctl TIOCGETP: $!";

        @ary = unpack($sgttyb_t,$sgttyb);
        if ($on) {
            $ary[4] |= &CBREAK;
            $ary[4] &= ~&ECHO;
        } else {
            $ary[4] &= ~&CBREAK;
            $ary[4] |= &ECHO;
        }
        $sgttyb = pack($sgttyb_t,@ary);

        ioctl(STDIN,&TIOCSETP,$sgttyb) || die "Can't ioctl TIOCSETP: $!";
    }

    Note that this is one of the few times you actually want to use the
    getc() function; it's in general way too expensive to call for normal
    I/O.  Normally, you just use the <FILE> syntax, or perhaps the read()
    or sysread() functions.

    For perspectives on more portable solutions, use anon ftp to retrieve
    the file /pub/perl/info/keypress from convex.com.

    For DOS systems, Dan Carson <dbc@tc.fluke.COM> reports:

    To put the PC in "raw" mode, use ioctl with some magic numbers gleaned
    from msdos.c (Perl source file) and Ralf Brown's interrupt list (comes
    across the net every so often):

        $old_ioctl = ioctl(STDIN,0,0);     # Gets device info
        $old_ioctl &= 0xff;
        ioctl(STDIN,1,$old_ioctl | 32);    # Writes it back, setting bit 5

    Then to read a single character:

        sysread(STDIN,$c,1);               # Read a single character

    And to put the PC back to "cooked" mode:

        ioctl(STDIN,1,$old_ioctl);         # Sets it back to cooked mode.


    So now you have $c.  If ord($c) == 0, you have a two byte code, which
    means you hit a special key.  Read another byte (sysread(STDIN,$c,1)),
    and that value tells you what combination it was according to this
    table:

        # PC 2-byte keycodes = ^@ + the following:

        # HEX     KEYS
        # ---     ----
        # 0F      SHF TAB
        # 10-19   ALT QWERTYUIOP
        # 1E-26   ALT ASDFGHJKL
        # 2C-32   ALT ZXCVBNM
        # 3B-44   F1-F10
        # 47-49   HOME,UP,PgUp
        # 4B      LEFT
        # 4D      RIGHT
        # 4F-53   END,DOWN,PgDn,Ins,Del
        # 54-5D   SHF F1-F10
        # 5E-67   CTR F1-F10
        # 68-71   ALT F1-F10
        # 73-77   CTR LEFT,RIGHT,END,PgDn,HOME
        # 78-83   ALT 1234567890-=
        # 84      CTR PgUp

    This is all trial and error I did a long time ago, I hope I'm reading the
    file that worked.