http://qs1969.pair.com?node_id=671388
Category: miscellaneous
Author/Contact Info halfcountplus@intergate.com
Description: I have seen a few people wanting to use the F-keys, PgUp, End, (etc) in non-GUI apps. I once did too. Unfortunately this requires changing terminal modes with the poorly documented Term::ReadKey, but if you still want to go ahead, my short script will read the multicharacter KEYS and remember them with names you give them (eg, F7). These keys are identified by reducing the number of unique elements in 3,4, or 5 byte KEYS (remember, 1 character = 1 byte!) according to the chart at the beginning. You can hex these raw character values and use them in regex with \x.

So it can be done, but most people really won't want to ;) IMPORTANT: if you don't exit cleanly (w/ ENTER) the terminal probably won't return to normal mode, and your command line will now be unresponsive...

Update: I noticed recently there is a "Curses" module for perl on CPAN which probably makes this a lot simpler -- ie. i'm wrong, Term::ReadKey is neither the only nor the best option. However, Term::ReadLine::Gnu recognizes these multi-character escape sequences in "bind_keyseq".
#!/usr/bin/perl
# will register multicharacter keys (eg F3, End, PgUp, <-) and allow y
+ou
# to name them, then return those names when pressed again.

# ReadKey in cbreak returns key values like this:
#byte:  1       2       3       4       5
#1char          X       X       X       X       normal
#3char  \e      0               X       X       F1-4, home, end
#3char  \e      [               X       X       arrows
#4char  \e      [               ~       X       PgUp, PgDn
#5char  \e      [                       ~       F5-12
#       eg. a is "a", PgUp is "\e[5~"
#       \e is ESC

use warnings;
use Term::ReadKey;

sub idkey {
        my @key = (); $C="@_";
        if ($C =~ /^\e/) {
                $C = ReadKey();
                if ($C =~ /^\[/) {
                        until ($C eq "~" || $C =~ /[A-D]/) {
                                $C = ReadKey(); if ($C ne "~") {push @
+key,$C;}
                        }   
                } else {push @key,$C;$C = ReadKey(); push @key,$C;}
                my $x = "@key";
                foreach (%mcks) {
                        if (exists($mcks{$_}) && $mcks{$_} eq "$x") {r
+eturn "$_";}
                }   
                ReadMode('normal');
                print "name? "; $nm = <>; chomp $nm;
                $mcks{$nm} = "$x";
                return @key;
        } else {return "@_";}
}    

%mcks=();    

while (1) {
        ReadMode('cbreak');
        print "Press a key. (ENTER quits)\n";
        $C = ReadKey();
        if ($C eq "\n") {ReadMode('normal');exit;}
        @key = idkey("$C");
        print "key:@key\n";
}
Replies are listed 'Best First'.
Re: PgUp Home
by zentara (Archbishop) on Mar 01, 2008 at 17:55 UTC
    I know you said commandline, but if you can use Gtk2, you might like this. It's much cleaner.
    #! /usr/bin/perl -w use strict; use Gtk2::Gdk::Keysyms; use Glib qw/TRUE FALSE/; use Gtk2 -init; my $window = Gtk2::Window->new ('toplevel'); $window->signal_connect (delete_event => sub { Gtk2->main_quit }); $window->signal_connect('key-press-event' => \&show_key); my $label = Gtk2::Label->new(); $label->set_markup("<span foreground=\"blue\" size=\"x-large\">Ty +pe something on the keyboard!</span>"); $window->add ($label); $window->show_all; $window->set_position ('center-always'); Gtk2->main; sub show_key { my ($widget,$event,$parameter)= @_; my $key_nr = $event->keyval(); #run trough the available key names, and get the values of each, #compare this with $event->keyval(), when you get a match exit the + loop foreach my $key (keys %Gtk2::Gdk::Keysyms){ my $key_compare = $Gtk2::Gdk::Keysyms{$key}; if($key_compare == $key_nr){ #print ("You typed $key \n"); $label->set_markup("<span foreground=\"blue\" size=\"x-large\" +>You typed a</span><span foreground=\"red\" size=\"30000\"><i><tt> $k +ey</tt></i></span><span foreground=\"blue\" size=\"x-large\"> which h +as a numeric value of </span><span foreground=\"red\" size=\"30000\"> +<i><tt> $key_nr!</tt></i></span>"); $widget->set_title("key pressed: $key -> numeric value: $key_n +r"); last; } } #good practice to let the event propagate, should we need it somew +here else return FALSE; }

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Myself, i have no perl/Gtk2 installed, but i agree with your approach. Who really needs numeric keypad functionality or something on the console when Tk or Gtk will do this nicer and easier in X?

      Still, there can always be something learned from trying. But if the gods have ears may this save someone else a silly amount of time & everyone can move on.

      Or maybe a better Term::Readkey might come to exist.
        As far as commanline goes, GLib will readkeys too. See Readkey with timer using Glib. So you can get the keycodes with Glib in a commandlinr app, the only thing you need to do is find out where in the Gtk2 headers, they translate the keycodes to keyboard names like F2, etc. It's probably in a header file somewhere. In any event, it's alot easier to use an event-loop system, like Glib, Curses, POE, etc., than to use a while(1) loop.

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: PgUp Home
by eserte (Deacon) on Mar 01, 2008 at 23:45 UTC
    To be really compatible, you need to look at the termcap entries, because the key values vary between different terminals. Or just use Curses?
      I am pretty sure this will work for 99%+ of users.

      The script might provide a starting point if you really need to test some unusual keyboard. Beyond the obvious limits stated in the chart, the A-D in the "until" statement is a cheat to account for the arrow keys, which are : "\e[A" "\e[B" "\e[C" "\e[D" in the standard. A regular deal: I presume you are going to get the same answers i did, but i don't completely provide them.

      I have seen termcap entries and no way I will look at them again.