3 # Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; either version 2 of the
8 # License, or any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 genkeymap.pl [options] <keymap name>
30 -f,--from=<name> Set BIOS keymap name (default "us")
31 -h,--help Display brief help message
32 -v,--verbose Increase verbosity
33 -q,--quiet Decrease verbosity
39 # http://gunnarwrobel.de/wiki/Linux-and-the-keyboard.html
46 use constant BIOS_KEYMAP
=> "us";
47 use constant BKEYMAP_MAGIC
=> "bkeymap";
48 use constant MAX_NR_KEYMAPS
=> 256;
49 use constant NR_KEYS
=> 128;
50 use constant KG_SHIFT
=> 0;
51 use constant KG_ALTGR
=> 1;
52 use constant KG_CTRL
=> 2;
53 use constant KG_ALT
=> 3;
54 use constant KG_SHIFTL
=> 4;
55 use constant KG_KANASHIFT
=> 4;
56 use constant KG_SHIFTR
=> 5;
57 use constant KG_CTRLL
=> 6;
58 use constant KG_CTRLR
=> 7;
59 use constant KG_CAPSSHIFT
=> 8;
60 use constant KT_LATIN
=> 0;
61 use constant KT_FN
=> 1;
62 use constant KT_SPEC
=> 2;
63 use constant KT_PAD
=> 3;
64 use constant KT_DEAD
=> 4;
65 use constant KT_CONS
=> 5;
66 use constant KT_CUR
=> 6;
67 use constant KT_SHIFT
=> 7;
68 use constant KT_META
=> 8;
69 use constant KT_ASCII
=> 9;
70 use constant KT_LOCK
=> 10;
71 use constant KT_LETTER
=> 11;
72 use constant KT_SLOCK
=> 12;
73 use constant KT_SPKUP
=> 14;
76 my $from_name = BIOS_KEYMAP
;
78 # Read named keymaps using "loadkeys -b"
84 # Generate binary keymap
85 open my $pipe, "-|", "loadkeys", "-b", $name
86 or die "Could not load keymap \"".$name."\": $!\n";
89 read $pipe, my $magic, length BKEYMAP_MAGIC
90 or die "Could not read from \"".$name."\": $!\n";
91 die "Bad magic value from \"".$name."\"\n"
92 unless $magic eq BKEYMAP_MAGIC
;
94 # Read list of included keymaps
95 read $pipe, my $included, MAX_NR_KEYMAPS
96 or die "Could not read from \"".$name."\": $!\n";
97 my @included = unpack ( "C*", $included );
98 die "Missing or truncated keymap list from \"".$name."\"\n"
99 unless @included == MAX_NR_KEYMAPS
;
101 # Read each keymap in turn
102 for ( my $keymap = 0 ; $keymap < MAX_NR_KEYMAPS
; $keymap++ ) {
103 if ( $included[$keymap] ) {
104 read $pipe, my $keysyms, ( NR_KEYS
* 2 )
105 or die "Could not read from \"".$name."\": $!\n";
106 my @keysyms = unpack ( "S*", $keysyms );
107 die "Missing or truncated keymap ".$keymap." from \"".$name."\"\n"
108 unless @keysyms == NR_KEYS
;
109 push @
$keymaps, \
@keysyms;
111 push @
$keymaps, undef;
119 # Translate keysym value to ASCII
121 sub keysym_to_ascii
{
124 # Non-existent keysyms have no ASCII equivalent
125 return unless $keysym;
128 if ( $keysym & 0xf000 ) {
129 warn "Unexpected keysym ".sprintf ( "0x%04x", $keysym )."\n";
133 # Extract type and value
134 my $type = ( $keysym >> 8 );
135 my $value = ( $keysym & 0xff );
137 # Non-simple types have no ASCII equivalent
138 return unless ( ( $type == KT_LATIN
) || ( $type == KT_ASCII
) ||
139 ( $type == KT_LETTER
) );
141 # High-bit-set characters cannot be generated on a US keyboard
142 return if $value & 0x80;
147 # Translate ASCII to descriptive name
152 if ( $ascii == 0x5c ) {
154 } elsif ( $ascii == 0x27 ) {
156 } elsif ( ( $ascii >= 0x20 ) && ( $ascii <= 0x7e ) ) {
157 return sprintf ( "'%c'", $ascii );
158 } elsif ( $ascii <= 0x1a ) {
159 return sprintf ( "Ctrl-%c", ( 0x40 + $ascii ) );
161 return sprintf ( "0x%02x", $ascii );
165 # Produce translation table between two keymaps
167 sub translate_keymaps
{
172 foreach my $keymap ( 0, 1 << KG_SHIFT
, 1 << KG_CTRL
) {
173 for ( my $keycode = 0 ; $keycode < NR_KEYS
; $keycode++ ) {
174 my $from_ascii = keysym_to_ascii
( $from->[$keymap]->[$keycode] )
176 my $to_ascii = keysym_to_ascii
( $to->[$keymap]->[$keycode] )
178 my $new_map = ( ! exists $map->{$from_ascii} );
180 ( $new_map || ( $keycode < $map->{$from_ascii}->{keycode
} ) );
181 if ( ( $verbosity > 1 ) &&
182 ( ( $from_ascii != $to_ascii ) ||
183 ( $update_map && ! $new_map ) ) ) {
184 printf STDERR
"In keymap %d: %s => %s%s\n", $keymap,
185 ascii_to_name
( $from_ascii ), ascii_to_name
( $to_ascii ),
186 ( $update_map ?
( $new_map ?
"" : " (override)" )
190 $map->{$from_ascii} = {
191 to_ascii
=> $to_ascii,
197 return { map { $_ => $map->{$_}->{to_ascii
} } keys %$map };
200 # Parse command-line options
201 Getopt
::Long
::Configure
( 'bundling', 'auto_abbrev' );
203 'verbose|v+' => sub { $verbosity++; },
204 'quiet|q+' => sub { $verbosity--; },
205 'from|f=s' => sub { shift; $from_name = shift; },
206 'help|h' => sub { pod2usage
( 1 ); },
207 ) or die "Could not parse command-line options\n";
208 pod2usage
( 1 ) unless @ARGV == 1;
211 # Read and translate keymaps
212 my $from = read_keymaps
( $from_name );
213 my $to = read_keymaps
( $to_name );
214 my $map = translate_keymaps
( $from, $to );
217 ( my $to_name_c = $to_name ) =~ s/\W/_/g;
218 printf "/** \@file\n";
220 printf " * \"".$to_name."\" keyboard mapping\n";
222 printf " * This file is automatically generated; do not edit\n";
226 printf "FILE_LICENCE ( PUBLIC_DOMAIN );\n";
228 printf "#include <ipxe/keymap.h>\n";
230 printf "/** \"".$to_name."\" keyboard mapping */\n";
231 printf "struct key_mapping ".$to_name_c."_mapping[] __keymap = {\n";
232 foreach my $from_sym ( sort { $a <=> $b } keys %$map ) {
233 my $to_sym = $map->{$from_sym};
234 next if $from_sym == $to_sym;
235 printf "\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n", $from_sym, $to_sym,
236 ascii_to_name
( $from_sym ), ascii_to_name
( $to_sym );