summaryrefslogtreecommitdiffstats
path: root/src/util/genkeymap.pl
diff options
context:
space:
mode:
authorMichael Brown2011-03-16 04:22:45 +0100
committerMichael Brown2011-03-16 13:02:59 +0100
commit1febd879e340958c7a4a54823a82784fcb3672ca (patch)
tree2280bd30a465fa8bb2e4a4c25aef0ef85b417279 /src/util/genkeymap.pl
parent[console] Add facility for rudimentary keyboard mapping (diff)
downloadipxe-1febd879e340958c7a4a54823a82784fcb3672ca.tar.gz
ipxe-1febd879e340958c7a4a54823a82784fcb3672ca.tar.xz
ipxe-1febd879e340958c7a4a54823a82784fcb3672ca.zip
[console] Add keymap generator
Inspired by LILO's keytab-lilo.pl, genkeymap.pl uses "loadkeys -b" to obtain a Linux keyboard map, and generates a file keymap_xx.c in hci/keymap. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/util/genkeymap.pl')
-rwxr-xr-xsrc/util/genkeymap.pl224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/util/genkeymap.pl b/src/util/genkeymap.pl
new file mode 100755
index 00000000..89f14695
--- /dev/null
+++ b/src/util/genkeymap.pl
@@ -0,0 +1,224 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=head1 NAME
+
+genkeymap.pl
+
+=head1 SYNOPSIS
+
+genkeymap.pl [options] <keymap name>
+
+Options:
+
+ -f,--from=<name> Set BIOS keymap name (default "us")
+ -h,--help Display brief help message
+ -v,--verbose Increase verbosity
+ -q,--quiet Decrease verbosity
+
+=cut
+
+# With reference to:
+#
+# http://gunnarwrobel.de/wiki/Linux-and-the-keyboard.html
+
+use Getopt::Long;
+use Pod::Usage;
+use strict;
+use warnings;
+
+use constant BIOS_KEYMAP => "us";
+use constant BKEYMAP_MAGIC => "bkeymap";
+use constant MAX_NR_KEYMAPS => 256;
+use constant NR_KEYS => 128;
+use constant KG_SHIFT => 0;
+use constant KG_ALTGR => 1;
+use constant KG_CTRL => 2;
+use constant KG_ALT => 3;
+use constant KG_SHIFTL => 4;
+use constant KG_KANASHIFT => 4;
+use constant KG_SHIFTR => 5;
+use constant KG_CTRLL => 6;
+use constant KG_CTRLR => 7;
+use constant KG_CAPSSHIFT => 8;
+use constant KT_LATIN => 0;
+use constant KT_FN => 1;
+use constant KT_SPEC => 2;
+use constant KT_PAD => 3;
+use constant KT_DEAD => 4;
+use constant KT_CONS => 5;
+use constant KT_CUR => 6;
+use constant KT_SHIFT => 7;
+use constant KT_META => 8;
+use constant KT_ASCII => 9;
+use constant KT_LOCK => 10;
+use constant KT_LETTER => 11;
+use constant KT_SLOCK => 12;
+use constant KT_SPKUP => 14;
+
+my $verbosity = 1;
+my $from_name = BIOS_KEYMAP;
+
+# Read named keymaps using "loadkeys -b"
+#
+sub read_keymaps {
+ my $name = shift;
+ my $keymaps = [];
+
+ # Generate binary keymap
+ open my $pipe, "-|", "loadkeys", "-b", $name
+ or die "Could not load keymap \"".$name."\": $!\n";
+
+ # Check magic
+ read $pipe, my $magic, length BKEYMAP_MAGIC
+ or die "Could not read from \"".$name."\": $!\n";
+ die "Bad magic value from \"".$name."\"\n"
+ unless $magic eq BKEYMAP_MAGIC;
+
+ # Read list of included keymaps
+ read $pipe, my $included, MAX_NR_KEYMAPS
+ or die "Could not read from \"".$name."\": $!\n";
+ my @included = unpack ( "C*", $included );
+ die "Missing or truncated keymap list from \"".$name."\"\n"
+ unless @included == MAX_NR_KEYMAPS;
+
+ # Read each keymap in turn
+ for ( my $keymap = 0 ; $keymap < MAX_NR_KEYMAPS ; $keymap++ ) {
+ if ( $included[$keymap] ) {
+ read $pipe, my $keysyms, ( NR_KEYS * 2 )
+ or die "Could not read from \"".$name."\": $!\n";
+ my @keysyms = unpack ( "S*", $keysyms );
+ die "Missing or truncated keymap ".$keymap." from \"".$name."\"\n"
+ unless @keysyms == NR_KEYS;
+ push @$keymaps, \@keysyms;
+ } else {
+ push @$keymaps, undef;
+ }
+ }
+
+ close $pipe;
+ return $keymaps;
+}
+
+# Translate keysym value to ASCII
+#
+sub keysym_to_ascii {
+ my $keysym = shift;
+
+ # Non-existent keysyms have no ASCII equivalent
+ return unless $keysym;
+
+ # Sanity check
+ die "Unexpected keysym ".sprintf ( "0x%04x\n", $keysym )."\n"
+ if $keysym & 0xf000;
+
+ # Extract type and value
+ my $type = ( $keysym >> 8 );
+ my $value = ( $keysym & 0xff );
+
+ # Non-simple types have no ASCII equivalent
+ return unless ( ( $type == KT_LATIN ) || ( $type == KT_ASCII ) ||
+ ( $type == KT_LETTER ) );
+
+ # High-bit-set characters cannot be generated on a US keyboard
+ return if $value & 0x80;
+
+ return $value;
+}
+
+# Translate ASCII to descriptive name
+#
+sub ascii_to_name {
+ my $ascii = shift;
+
+ if ( $ascii == 0x5c ) {
+ return "'\\\\'";
+ } elsif ( $ascii == 0x27 ) {
+ return "'\\\''";
+ } elsif ( ( $ascii >= 0x20 ) && ( $ascii <= 0x7e ) ) {
+ return sprintf ( "'%c'", $ascii );
+ } elsif ( $ascii <= 0x1a ) {
+ return sprintf ( "Ctrl-%c", ( 0x40 + $ascii ) );
+ } else {
+ return sprintf ( "0x%02x", $ascii );
+ }
+}
+
+# Produce translation table between two keymaps
+#
+sub translate_keymaps {
+ my $from = shift;
+ my $to = shift;
+ my $map = {};
+
+ foreach my $keymap ( 0, 1 << KG_SHIFT, 1 << KG_CTRL ) {
+ for ( my $keycode = 0 ; $keycode < NR_KEYS ; $keycode++ ) {
+ my $from_ascii = keysym_to_ascii ( $from->[$keymap]->[$keycode] )
+ or next;
+ my $to_ascii = keysym_to_ascii ( $to->[$keymap]->[$keycode] )
+ or next;
+ if ( ( $from_ascii != $to_ascii ) && $verbosity > 1 ) {
+ printf STDERR "In keymap %d: %s => %s%s\n", $keymap,
+ ascii_to_name ( $from_ascii ), ascii_to_name ( $to_ascii ),
+ ( $map->{$from_ascii} ? " (ignored)" : "" );
+ }
+ $map->{$from_ascii} ||= $to_ascii;
+ }
+ }
+ return $map;
+}
+
+# Parse command-line options
+Getopt::Long::Configure ( 'bundling', 'auto_abbrev' );
+GetOptions (
+ 'verbose|v+' => sub { $verbosity++; },
+ 'quiet|q+' => sub { $verbosity--; },
+ 'from|f=s' => sub { shift; $from_name = shift; },
+ 'help|h' => sub { pod2usage ( 1 ); },
+) or die "Could not parse command-line options\n";
+pod2usage ( 1 ) unless @ARGV == 1;
+my $to_name = shift;
+
+# Read and translate keymaps
+my $from = read_keymaps ( $from_name );
+my $to = read_keymaps ( $to_name );
+my $map = translate_keymaps ( $from, $to );
+
+# Generate output
+( my $to_name_c = $to_name ) =~ s/\W/_/g;
+printf "/** \@file\n";
+printf " *\n";
+printf " * \"".$to_name."\" keyboard mapping\n";
+printf " *\n";
+printf " * This file is automatically generated; do not edit\n";
+printf " *\n";
+printf " */\n";
+printf "\n";
+printf "FILE_LICENCE ( PUBLIC_DOMAIN );\n";
+printf "\n";
+printf "#include <ipxe/keymap.h>\n";
+printf "\n";
+printf "/** \"".$to_name."\" keyboard mapping */\n";
+printf "struct key_mapping ".$to_name_c."_mapping[] __keymap = {\n";
+foreach my $from_sym ( sort { $a <=> $b } keys %$map ) {
+ my $to_sym = $map->{$from_sym};
+ next if $from_sym == $to_sym;
+ printf "\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n", $from_sym, $to_sym,
+ ascii_to_name ( $from_sym ), ascii_to_name ( $to_sym );
+}
+printf "};\n";