home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / perl / info / keypress < prev    next >
Encoding:
Text File  |  1992-02-05  |  11.0 KB  |  336 lines

  1. Newsgroups: comp.lang.perl
  2. Path: convex!tchrist
  3. From: Tom Christiansen <tchrist@convex.COM>
  4. Subject: Re: Related key-press problem [WAS: Re: Can YOU Catch Each Keypress?]
  5. Message-ID: <1992Jan31.135645.5242@convex.com>
  6. Originator: tchrist@pixel.convex.com
  7. Sender: usenet@convex.com (news access account)
  8. Nntp-Posting-Host: pixel.convex.com
  9. Reply-To: tchrist@convex.COM (Tom Christiansen)
  10. Organization: CONVEX Realtime Development, Colorado Springs, CO
  11. References: <1992Jan31.075726.7506@alf.uib.no>
  12. Date: Fri, 31 Jan 1992 13:56:45 GMT
  13. X-Disclaimer: This message was written by a user at CONVEX Computer
  14.               Corp. The opinions expressed are those of the user and
  15.               not necessarily those of CONVEX.
  16. Lines: 318
  17.  
  18. From the keyboard of buboo@alf.uib.no (Ove Ruben R Olsen):
  19. :[... a lot of how to catch the cat.. errh...each key-press deleted ...]
  20. :
  21. :This is a related question:
  22. :
  23. :Maybe I've missed something totally basic, overlooked it in The Book, 
  24. :misread the FAQ... 
  25. :
  26. :I want the following:
  27. :
  28. :     If the a key is pressed, return the value, else continue the program.
  29. :
  30. :This is useful for writing a &deadline that run side by side in a script.
  31. :Yes I'm lazy, I don't wanna fork a separate process for reading the keyboard
  32. :
  33. :I would prefer if this can be done in plain Perl. And not involving stty.
  34. :
  35. :With this we could write tetris, spaceinwaders, whatever... :-))
  36. :
  37. :(I won't write games with this, just a comm. client.)
  38.  
  39. You don't have to exec stty, but if you think you're going to 
  40. be able to get away without using an ioctl or doing anything at all 
  41. that's system dependent (fcntl, select, ...), then you're living
  42. in a dreamworld.  This stuff is simply too system dependent.
  43.  
  44. At the risk of sounding like a broken record, I'm about to quote
  45. things many of you have probably read before.
  46.  
  47. First, from the perl FAQ:
  48.  
  49. 16) How can I detect keyboard input without reading it?
  50.  
  51.     You might check out the Frequently Asked Questions list in comp.unix.* for
  52.     things like this: the answer is essentially the same.  It's very system
  53.     dependent.  Here's one solution that works on BSD systems:
  54.  
  55.         sub key_ready {
  56.             local($rin, $nfd);
  57.             vec($rin, fileno(STDIN), 1) = 1;
  58.             return $nfd = select($rin,undef,undef,0);
  59.         }
  60.  
  61.     A closely related question is how to input a single character from the
  62.     keyboard.  Again, this is a system dependent operation.  The following 
  63.     code that may or may not help you:
  64.  
  65.         $BSD = -f '/vmunix';
  66.         if ($BSD) {
  67.             system "stty cbreak </dev/tty >/dev/tty 2>&1";
  68.         }
  69.         else {
  70.             system "stty", 'cbreak',
  71.             system "stty", 'eol', '^A'; # note: real control A
  72.         }
  73.  
  74.         $key = getc(STDIN);
  75.  
  76.         if ($BSD) {
  77.             system "stty -cbreak </dev/tty >/dev/tty 2>&1";
  78.         }
  79.         else {
  80.             system "stty", 'icanon';
  81.             system "stty", 'eol', '^@'; # ascii null
  82.         }
  83.         print "\n";
  84.  
  85.     You could also handle the stty operations yourself for speed if you're
  86.     going to be doing a lot of them.  This code works to toggle cbreak
  87.     and echo modes on a BSD system:
  88.  
  89.     sub set_cbreak { # &set_cbreak(1) or &set_cbreak(0)
  90.         local($on) = $_[0];
  91.         local($sgttyb,@ary);
  92.         require 'sys/ioctl.pl';
  93.         $sgttyb_t   = 'C4 S' unless $sgttyb_t;
  94.         ioctl(STDIN,$TIOCGETP,$sgttyb) || die "Can't ioctl TIOCGETP: $!";
  95.  
  96.         @ary = unpack($sgttyb_t,$sgttyb);
  97.         if ($on) {
  98.             $ary[4] |= $CBREAK;
  99.             $ary[4] &= ~$ECHO;
  100.         } else {
  101.             $ary[4] &= ~$CBREAK;
  102.             $ary[4] |= $ECHO;
  103.         }
  104.         $sgttyb = pack($sgttyb_t,@ary);
  105.  
  106.         ioctl(STDIN,&TIOCSETP,$sgttyb) || die "Can't ioctl TIOCSETP: $!";
  107.     }
  108.  
  109.     Note that this is one of the few times you actually want to use the
  110.     getc() function; it's in general way too expensive to call for normal
  111.     I/O.  Normally, you just use the <FILE> syntax, or perhaps the read()
  112.     or sysread() functions.
  113.  
  114.  
  115. Now, from the UNIX FAQ:
  116.  
  117. 6)  How do I read characters from the terminal in a shell script?
  118.  
  119.     In sh, use read.  It is most common to use a loop like
  120.  
  121.             while read line
  122.             do
  123.                     ...
  124.             done
  125.  
  126.     In csh, use $< like this:
  127.         
  128.             while ( 1 )
  129.                 set line = "$<"
  130.                 if ( "$line" == "" ) break
  131.                 ...
  132.             end
  133.  
  134.     Unfortunately csh has no way of distinguishing between
  135.     a blank line and an end-of-file.
  136.  
  137.     If you're using sh and want to read a *single* character from
  138.     the terminal, you can try something like
  139.  
  140.             echo -n "Enter a character: "
  141.             stty cbreak         # or  stty raw
  142.             readchar=`dd if=/dev/tty bs=1 count=1 2>/dev/null`
  143.             stty -cbreak
  144.  
  145.             echo "Thank you for typing a $readchar ."
  146.  
  147. 7)  How do I check to see if there are characters to be read without
  148.     actually reading?
  149.  
  150.     Certain versions of UNIX provide ways to check whether
  151.     characters are currently available to be read from a file
  152.     descriptor.  In BSD, you can use select(2).  You can also use
  153.     the FIONREAD ioctl (see tty(4)), which returns the number of
  154.     characters waiting to be read, but only works on terminals,
  155.     pipes and sockets.  In System V Release 3, you can use poll(2),
  156.     but that only works on streams.  In Xenix - and therefore
  157.     Unix SysV r3.2 and later - the rdchk() system call reports
  158.     whether a read() call on a given file descriptor will block.
  159.  
  160.     There is no way to check whether characters are available to be
  161.     read from a FILE pointer.  (You could poke around inside stdio data
  162.     structures to see if the input buffer is nonempty, but that wouldn't
  163.     work since you'd have no way of knowing what will happen the next
  164.     time you try to fill the buffer.)
  165.  
  166.     Sometimes people ask this question with the intention of writing
  167.             if (characters available from fd)
  168.                     read(fd, buf, sizeof buf);
  169.     in order to get the effect of a nonblocking read.  This is not the
  170.     best way to do this, because it is possible that characters will
  171.     be available when you test for availability, but will no longer
  172.     be available when you call read.  Instead, set the O_NDELAY flag
  173.     (which is also called FNDELAY under BSD) using the F_SETFL option
  174.     of fcntl(2).  Older systems (Version 7, 4.1 BSD) don't have O_NDELAY;
  175.     on these systems the closest you can get to a nonblocking read is
  176.     to use alarm(2) to time out the read.
  177.  
  178.  
  179. Does that mean you can't do it?  Of course not: those just showed 
  180. you various ways to do it in various systems.  Here's what I use
  181. in plum.  Note that I here assume the following things:
  182.  
  183. 1) $SYSTEM has the "flavor" of UNIX you're running loaded into it.
  184.  
  185. 2) You've already required the appropriate files for your system
  186.    (sgtty.ph, sys/ioctl.ph, sys/termio.ph, termios.ph, sys/ttycom.ph, 
  187.    and/or sys/ttydev.ph) or otherwise gotten their values loaded.
  188.    
  189. 3) That these .ph files are properly constructed using both h2ph and
  190.    c2ph, and that any files that they themselves require have received
  191.    similar treatements, recursively.  Note that it's important to 
  192.    use c2ph struct definitions if you ever hope to run on more
  193.    than one kind of system without having to modify your code.
  194.  
  195. 4) If your system is a POSIX one, you've loaded $GETIOCTL and 
  196.    $SETIOCTL with values that map into ioctls that retrieve
  197.    a termios struct for you.
  198.  
  199. If all these hold true, these functions may work for you in a reasonably
  200. system-independent fashion. You should call the &init_cbreak function
  201. before calling &cbreak or &cooked, or take a &panic.
  202.  
  203. --tom
  204.  
  205.     sub set_cbreak { &panic("How did I get called?"); } 
  206.  
  207.     sub cbreak { &set_cbreak(1); }
  208.     sub cooked { &set_cbreak(0); }
  209.  
  210.     sub init_cbreak {
  211.     undef &set_cbreak;
  212.     if      ($SYSTEM =~ /^BSD/i) {
  213.         *set_cbreak = *BSD_cbreak;
  214.     } elsif ($SYSTEM =~ /^SysV/i) {
  215.         *set_cbreak = *SYSV_cbreak;
  216.     } elsif ($SYSTEM =~ /POSIX/i) {
  217.         *set_cbreak = *POSIX_cbreak;
  218.     } else {
  219.         *set_cbreak = *DUMB_cbreak;
  220.     } 
  221.     } 
  222.  
  223.  
  224.  
  225.     sub BSD_cbreak {
  226.     local($on) = shift;
  227.     local(@sb);
  228.     local($sgttyb);
  229.     # global $sbttyb_t 
  230.  
  231.     $sgttyb_t = &sgttyb'typedef() unless defined $sgttyb_t;
  232.  
  233.     # native BSD stuff by author (tsc)
  234.  
  235.     ioctl(TTY,&TIOCGETP,$sgttyb)
  236.         || die "Can't ioctl TIOCGETP: $!";
  237.  
  238.     @sb = unpack($sgttyb_t,$sgttyb);
  239.     if ($on) {
  240.         $sb[&sgttyb'sg_flags] |= &CBREAK;
  241.         $sb[&sgttyb'sg_flags] &= ~&ECHO;
  242.     } else {
  243.         $sb[&sgttyb'sg_flags] &= ~&CBREAK;
  244.         $sb[&sgttyb'sg_flags] |= &ECHO;
  245.     }
  246.     $sgttyb = pack($sgttyb_t,@sb);
  247.     ioctl(TTY,&TIOCSETN,$sgttyb)
  248.         || die "Can't ioctl TIOCSETN: $!";
  249.     }
  250.  
  251.     sub SYSV_cbreak {
  252.     # SysV code contributed by Jeff Okamoto <okamoto@hpcc25.corp.hp.com>
  253.  
  254.     local($on) = shift;
  255.     local($termio,@termio);
  256.     # global termio_t ???
  257.  
  258.     $termio_t = &termio'typedef() unless defined $termio_t;
  259.  
  260.     ioctl(TTY,&TCGETA,$termio)
  261.        || die "Can't ioctl TCGETA: $!";
  262.  
  263.     @termio = unpack($termio_t, $termio);
  264.     if ($on) {
  265.         $termio[&termio'c_lflag] &= ~(&ECHO | &ICANON);
  266.         $termio[&termio'c_cc + &VMIN] = 1;
  267.         $termio[&termio'c_cc + &VTIME] = 1;
  268.     } else {
  269.         $termio[&termio'c_lflag] |= (&ECHO | &ICANON);
  270.  
  271.         # In HP-UX, it appears that turning ECHO and ICANON back on is
  272.         # sufficient to re-enable cooked mode.  Therefore I'm not bothering
  273.         # to reset VMIN and VTIME (VEOF and VEOL above).  This might be a
  274.         # problem on other SysV variants.
  275.  
  276.     }
  277.     $termio = pack($termio_t, @termio);
  278.     ioctl(TTY, &TCSETA, $termio)
  279.         || die "Can't ioctl TCSETA: $!";
  280.     
  281.     }
  282.  
  283.     # This is a hack because we don't have tc[gs]etattr.  Instead
  284.     # we must ASSUME that there's an ioctl or 2 that these map to.  
  285.     #
  286.     sub POSIX_cbreak {
  287.     local($on) = shift;
  288.     local(@termios, $termios, $bitmask);
  289.  
  290.     # "file statics" for package cbreak:
  291.     #       $savebits, $save_vtime, $save_vmin, $is_on
  292.  
  293.     $termios_t = &termios'typedef() unless defined $termios_t;
  294.     $termios = pack($termios_t, ());  # for Sun SysVr4, which dies w/o this
  295.  
  296.     ioctl(TTY,&$GETIOCTL,$termios)
  297.         || die "Can't ioctl GETIOCTL ($GETIOCTL): $!";
  298.  
  299.     @termios = unpack($termios_t,$termios);
  300.  
  301.     $bitmask  = &ICANON | &IEXTEN | &ECHO;
  302.     if ($on && $cbreak'ison == 0) {
  303.         $cbreak'ison = 1;
  304.         $cbreak'savebits = $termios[&termios'c_lflag] & $bitmask;
  305.         $termios[&termios'c_lflag] &= ~$bitmask;
  306.         $cbreak'save_vtime = $termios[&termios'c_cc + &VTIME];
  307.         $termios[&termios'c_cc + &VTIME] = 0;
  308.         $cbreak'save_vmin  = $termios[&termios'c_cc + &VMIN];
  309.         $termios[&termios'c_cc + &VMIN] = 1;
  310.     } elsif ( !$on && $cbreak'ison == 1 ) {
  311.         $cbreak'ison = 0;
  312.         $termios[&termios'c_lflag] |= $cbreak'savebits;
  313.         $termios[&termios'c_cc + &VTIME] = $cbreak'save_vtime;
  314.         $termios[&termios'c_cc + &VMIN]  = $cbreak'save_vmin;
  315.     } else {
  316.         return 1;
  317.     } 
  318.     $termios = pack($termios_t,@termios);
  319.     ioctl(TTY,&$SETIOCTL,$termios)
  320.         || die "Can't ioctl SETIOCTL ($SETIOCTL): $!";
  321.     }
  322.  
  323.     # if you're too dumb to be one of the above three, maybe
  324.     # your stty doens't even grok cbreak.  pity.
  325.     sub DUMB_cbreak {
  326.     local($on) = shift;
  327.  
  328.     if ($on) {
  329.         system("stty  cbreak -echo");
  330.     } else {
  331.         system("stty -cbreak  echo");
  332.     }
  333.     } 
  334.  
  335.     1;
  336.