Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / util / genkeymap.pl
1 #!/usr/bin/perl -w
2 #
3 # Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
4 #
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.
9 #
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.
14 #
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
18 # 02110-1301, USA.
19
20 =head1 NAME
21
22 genkeymap.pl
23
24 =head1 SYNOPSIS
25
26 genkeymap.pl [options] <keymap name>
27
28 Options:
29
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
34
35 =cut
36
37 # With reference to:
38 #
39 # http://gunnarwrobel.de/wiki/Linux-and-the-keyboard.html
40
41 use Getopt::Long;
42 use Pod::Usage;
43 use strict;
44 use warnings;
45
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;
74
75 my $verbosity = 1;
76 my $from_name = BIOS_KEYMAP;
77
78 # Read named keymaps using "loadkeys -b"
79 #
80 sub read_keymaps {
81   my $name = shift;
82   my $keymaps = [];
83
84   # Generate binary keymap
85   open my $pipe, "-|", "loadkeys", "-b", $name
86       or die "Could not load keymap \"".$name."\": $!\n";
87
88   # Check magic
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;
93
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;
100
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;
110     } else {
111       push @$keymaps, undef;
112     }
113   }
114
115   close $pipe;
116   return $keymaps;
117 }
118
119 # Translate keysym value to ASCII
120 #
121 sub keysym_to_ascii {
122   my $keysym = shift;
123
124   # Non-existent keysyms have no ASCII equivalent
125   return unless $keysym;
126
127   # Sanity check
128   if ( $keysym & 0xf000 ) {
129     warn "Unexpected keysym ".sprintf ( "0x%04x", $keysym )."\n";
130     return;
131   }
132
133   # Extract type and value
134   my $type = ( $keysym >> 8 );
135   my $value = ( $keysym & 0xff );
136
137   # Non-simple types have no ASCII equivalent
138   return unless ( ( $type == KT_LATIN ) || ( $type == KT_ASCII ) ||
139                   ( $type == KT_LETTER ) );
140
141   # High-bit-set characters cannot be generated on a US keyboard
142   return if $value & 0x80;
143
144   return $value;
145 }
146
147 # Translate ASCII to descriptive name
148 #
149 sub ascii_to_name {
150   my $ascii = shift;
151
152   if ( $ascii == 0x5c ) {
153     return "'\\\\'";
154   } elsif ( $ascii == 0x27 ) {
155     return "'\\\''";
156   } elsif ( ( $ascii >= 0x20 ) && ( $ascii <= 0x7e ) ) {
157     return sprintf ( "'%c'", $ascii );
158   } elsif ( $ascii <= 0x1a ) {
159     return sprintf ( "Ctrl-%c", ( 0x40 + $ascii ) );
160   } else {
161     return sprintf ( "0x%02x", $ascii );
162   }
163 }
164
165 # Produce translation table between two keymaps
166 #
167 sub translate_keymaps {
168   my $from = shift;
169   my $to = shift;
170   my $map = {};
171
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] )
175           or next;
176       my $to_ascii = keysym_to_ascii ( $to->[$keymap]->[$keycode] )
177           or next;
178       my $new_map = ( ! exists $map->{$from_ascii} );
179       my $update_map =
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)" )
187                              : " (ignored)" );
188       }
189       if ( $update_map ) {
190         $map->{$from_ascii} = {
191           to_ascii => $to_ascii,
192           keycode => $keycode,
193         };
194       }
195     }
196   }
197   return { map { $_ => $map->{$_}->{to_ascii} } keys %$map };
198 }
199
200 # Parse command-line options
201 Getopt::Long::Configure ( 'bundling', 'auto_abbrev' );
202 GetOptions (
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;
209 my $to_name = shift;
210
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 );
215
216 # Generate output
217 ( my $to_name_c = $to_name ) =~ s/\W/_/g;
218 printf "/** \@file\n";
219 printf " *\n";
220 printf " * \"".$to_name."\" keyboard mapping\n";
221 printf " *\n";
222 printf " * This file is automatically generated; do not edit\n";
223 printf " *\n";
224 printf " */\n";
225 printf "\n";
226 printf "FILE_LICENCE ( PUBLIC_DOMAIN );\n";
227 printf "\n";
228 printf "#include <ipxe/keymap.h>\n";
229 printf "\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 );
237 }
238 printf "};\n";