[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Bind keypress to shell command?



On Mon, May 09, 2005 at 09:31:05AM -0400, Bob Freemer wrote:
> Hi, guys,

well, I'm a gal... but I'll reply anyway :)

> 
> I've searched everywhere on Google and the group for postings about this,
> but can't find anything.  I'm setting up an "appliance" machine and want
> to simply bind shell commands to a keypress on a numeric kepad.  One of
> the functions is to control mpd for MP3 playing, amongst others.
> 
> I DO NOT use X, which precludes things like hotkeys etc.  Simply booting
> an ncurses interface into one of the virtual terminals that would listen
> for a keypress and then execute a shell command would do it, but I've
> searched high and low for such an thing but can't find it.  I can get the
> keycodes from showkey, but that's about it.  Maybe there's a way to do it
> in the inittab?  Can't find anything in the documentation on that though.
> 
> I suppose coding a simple app could do it, but I didn't really want to get
> into that as I'm sure others have faced this type of thing before.

Don't know of any canned solution (others will have to chime in here), 
but instead of spending hours on googling, reading docs and testing,
I'd simply write a little script that does the job...   This has the
added benefit that you can tailor it precisely to your needs.

Below is the outline of how you might do it in Perl.
("The code is the documentation" holds here, mostly -- let me know if
you have a problem with this... :)

Cheers,
Almut


#!/usr/bin/perl

use Term::ReadKey;

my %key_cmd_map = (
    # key        command
    '1' => sub { print `ls` },
    '2' => sub { print `ls -l` },
    '3' => sub { system 'xterm &' },    # (these only in X, of course)
    '4' => sub { system 'xclock &' },
    # ... configure here what to run
);

ReadMode raw;

while ((my $ch = ReadKey 0) ne 'q') {   # 'q' to quit loop
    printf "char: %s, hex: %x\n", $ch, ord $ch;  # debug
    my $cmd = $key_cmd_map{$ch};
    if (ref($cmd) eq "CODE") {          # key handler defined?
        $cmd->();                       # yes: run command
    }
}
ReadMode restore;                       # restore TTY settings


The module Term::ReadKey does the dirty work behind the scenes.
It allows you to read single characters from a terminal (without
hitting enter), which would otherwise be not so trivial...
Keybindings are configured by associating keys with (anonymous)
functions.  Those contain the code to be run.  Use system(), backticks,
open to/from pipe, etc. to run system commands here...

The problem with this simple approach is that it can only handle
single-char keys, like 1, 2, 3, a, b, c,...  For cursor keys, F1-F12,
etc., terminals do generate multi-key sequences (also called "escape
sequences", because they start with the escape key).  This means you'd
have to have numlock activated to use the numeric keypad -- and you
cannot use function keys this way.

But this doesn't have to be a show stopper.  With a simple modification
we can also handle those escape sequences:

#!/usr/bin/perl

use Term::ReadKey;

my %key_cmd_map = (
    # escseq       command
    '[A'  => sub { print "Up\n" },      # those escape sequences should
    '[B'  => sub { print "Down\n" },    # work in the linux console
    '[C'  => sub { print "Right\n" },
    '[D'  => sub { print "Left\n" },
    '[G'  => sub { print "Center\n" },
    '[5~' => sub { print "PgUp\n" },
    '[6~' => sub { print "PgDn\n" },
    '[1~' => sub { print "Home\n" },
    '[4~' => sub { print "End\n" },
    
    '[[A' => sub { print "F1\n" },
    
    '[2~' => sub { $debug = 1 },        # Insert key
    '[3~' => sub { $debug = 0 },        # Delete key
);

ReadMode raw;

my $seq;

while ((my $ch = ReadKey 0) ne 'q') {   # 'q' to quit loop
    $seq .= $ch;                        # append to seq
    $seq = '' if $ch eq "\e";           # clear seq
    print "char: $ch, seq: $seq\n" if $debug;
    my $cmd = $key_cmd_map{$seq};
    if (ref($cmd) eq "CODE") {          # key handler defined?
        $cmd->();                       # yes: run command
        $seq = '';
    }
}
ReadMode restore;                       # restore TTY settings


The idea is, of course, to replace those 'print "Keyname\n"' statements
with the actual commands you want to run.  You get the idea...



Reply to: