2011-03-16 04:22:45 +01:00
|
|
|
#!/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
|
2012-07-20 20:55:45 +02:00
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
# 02110-1301, USA.
|
2011-03-16 04:22:45 +01:00
|
|
|
|
|
|
|
=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
|
2012-03-27 21:55:19 +02:00
|
|
|
if ( $keysym & 0xf000 ) {
|
|
|
|
warn "Unexpected keysym ".sprintf ( "0x%04x", $keysym )."\n";
|
|
|
|
return;
|
|
|
|
}
|
2011-03-16 04:22:45 +01:00
|
|
|
|
|
|
|
# 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;
|
2011-03-16 22:31:16 +01:00
|
|
|
my $new_map = ( ! exists $map->{$from_ascii} );
|
|
|
|
my $update_map =
|
|
|
|
( $new_map || ( $keycode < $map->{$from_ascii}->{keycode} ) );
|
|
|
|
if ( ( $verbosity > 1 ) &&
|
|
|
|
( ( $from_ascii != $to_ascii ) ||
|
|
|
|
( $update_map && ! $new_map ) ) ) {
|
2011-03-16 04:22:45 +01:00
|
|
|
printf STDERR "In keymap %d: %s => %s%s\n", $keymap,
|
|
|
|
ascii_to_name ( $from_ascii ), ascii_to_name ( $to_ascii ),
|
2011-03-16 22:31:16 +01:00
|
|
|
( $update_map ? ( $new_map ? "" : " (override)" )
|
|
|
|
: " (ignored)" );
|
|
|
|
}
|
|
|
|
if ( $update_map ) {
|
|
|
|
$map->{$from_ascii} = {
|
|
|
|
to_ascii => $to_ascii,
|
|
|
|
keycode => $keycode,
|
|
|
|
};
|
2011-03-16 04:22:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-03-16 22:31:16 +01:00
|
|
|
return { map { $_ => $map->{$_}->{to_ascii} } keys %$map };
|
2011-03-16 04:22:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# 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";
|