aztlan667 has asked for the wisdom of the Perl Monks concerning the following question:

Greetings, I am trying to create a script that will read in a series of characters and look for a certain sequence of characters. I am having a hard time reading in control characters like <CTRL> and <ALT>. I'm basically am trying to create a keylogger that will look for a sequence like "<CTRL>+<ALT>+B" and execute another script when that happens. I've tried using the following code:

my $key_buf; my $sequence = qr/some_keys/g; system "stty cbreak < /dev/tty > /dev/tty 2>&1"; while (($key=getc) ) { $key_buf .= $key; if ($key_buf =~ $my_sequence) { system "xmessage 'OH YA!'"; last; } last if $key eq "q"; } system "stty -cbreak < /dev/tty > /dev/tty 2>&1";

I've also tried using Term::ReadKey and Term::InKey, but I've had no success in capturing the control characters. Any suggestions or anything I'm missing?

Thank you.

Replies are listed 'Best First'.
Re: Capturing control characters
by almut (Canon) on Feb 10, 2009 at 02:59 UTC

    The general problem here is that modifier keys like <CTRL> and <ALT> are being interpreted by the tty itself.  You could try something like this, however:

    #!/usr/bin/perl use Term::ReadKey; my $my_sequence = qr/\x1b\x02\z/; # <CTRL>+<ALT>+B my $key; my $key_buf; ReadMode 4; while ($key ne 'q') { $key = ReadKey(); printf "key=\\x%02x\n", ord($key); # debug $key_buf .= $key; if ($key_buf =~ $my_sequence) { system "xmessage 'OH YA!'"; last; } }; ReadMode 0;

    This has its limits, too (for example I wouldn't know how to tell apart <CTRL>+<ALT>+B from <CTRL>+<ALT>+<SHIFT>+B), but maybe it's sufficient for your purpose.

    <CTRL>+A .. <CTRL>+Z typically maps to 1..26 (ord($key), that is); and <ALT>+... typically generates a two-byte sequence with a leading ESC (27 or \x1b). Other keys (cursor keys, etc.) generate three-byte sequences...

    Printing out the sequence you get when you hit a certain key/modifier combination will tell you which substring to match (you don't necessarily need a regex for this, btw; index() would do).  But in case portability is an issue, be aware that terminals sometimes differ with respect to the sequences they generate — in particular the multi-byte ones...

    Update: the loop exit condition ($key eq 'q') is probably too simple for production use (it would also exit on hitting <ALT>+q, for example, due to the second byte of the escape sequence being 'q')...  Fixing this is left as an exercise for the reader :)

Re: Capturing control characters
by Joost (Canon) on Feb 10, 2009 at 00:00 UTC
      I'll tak a look at IPC::Open3. I agree with the sentiment towards keylogging. This isn't a keylogger, per se. It's just a script that will execute some code once a certain key combination has been entered. The end users are fully aware of its presence, and its pretty much the whole reason for using the system.