|
|
1.1 root 1: #!/usr/bin/perl -w
2: #
3: # Copyright (C) 2011 Michael Brown <[email protected]>.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18:
19: =head1 NAME
20:
21: genkeymap.pl
22:
23: =head1 SYNOPSIS
24:
25: genkeymap.pl [options] <keymap name>
26:
27: Options:
28:
29: -f,--from=<name> Set BIOS keymap name (default "us")
30: -h,--help Display brief help message
31: -v,--verbose Increase verbosity
32: -q,--quiet Decrease verbosity
33:
34: =cut
35:
36: # With reference to:
37: #
38: # http://gunnarwrobel.de/wiki/Linux-and-the-keyboard.html
39:
40: use Getopt::Long;
41: use Pod::Usage;
42: use strict;
43: use warnings;
44:
45: use constant BIOS_KEYMAP => "us";
46: use constant BKEYMAP_MAGIC => "bkeymap";
47: use constant MAX_NR_KEYMAPS => 256;
48: use constant NR_KEYS => 128;
49: use constant KG_SHIFT => 0;
50: use constant KG_ALTGR => 1;
51: use constant KG_CTRL => 2;
52: use constant KG_ALT => 3;
53: use constant KG_SHIFTL => 4;
54: use constant KG_KANASHIFT => 4;
55: use constant KG_SHIFTR => 5;
56: use constant KG_CTRLL => 6;
57: use constant KG_CTRLR => 7;
58: use constant KG_CAPSSHIFT => 8;
59: use constant KT_LATIN => 0;
60: use constant KT_FN => 1;
61: use constant KT_SPEC => 2;
62: use constant KT_PAD => 3;
63: use constant KT_DEAD => 4;
64: use constant KT_CONS => 5;
65: use constant KT_CUR => 6;
66: use constant KT_SHIFT => 7;
67: use constant KT_META => 8;
68: use constant KT_ASCII => 9;
69: use constant KT_LOCK => 10;
70: use constant KT_LETTER => 11;
71: use constant KT_SLOCK => 12;
72: use constant KT_SPKUP => 14;
73:
74: my $verbosity = 1;
75: my $from_name = BIOS_KEYMAP;
76:
77: # Read named keymaps using "loadkeys -b"
78: #
79: sub read_keymaps {
80: my $name = shift;
81: my $keymaps = [];
82:
83: # Generate binary keymap
84: open my $pipe, "-|", "loadkeys", "-b", $name
85: or die "Could not load keymap \"".$name."\": $!\n";
86:
87: # Check magic
88: read $pipe, my $magic, length BKEYMAP_MAGIC
89: or die "Could not read from \"".$name."\": $!\n";
90: die "Bad magic value from \"".$name."\"\n"
91: unless $magic eq BKEYMAP_MAGIC;
92:
93: # Read list of included keymaps
94: read $pipe, my $included, MAX_NR_KEYMAPS
95: or die "Could not read from \"".$name."\": $!\n";
96: my @included = unpack ( "C*", $included );
97: die "Missing or truncated keymap list from \"".$name."\"\n"
98: unless @included == MAX_NR_KEYMAPS;
99:
100: # Read each keymap in turn
101: for ( my $keymap = 0 ; $keymap < MAX_NR_KEYMAPS ; $keymap++ ) {
102: if ( $included[$keymap] ) {
103: read $pipe, my $keysyms, ( NR_KEYS * 2 )
104: or die "Could not read from \"".$name."\": $!\n";
105: my @keysyms = unpack ( "S*", $keysyms );
106: die "Missing or truncated keymap ".$keymap." from \"".$name."\"\n"
107: unless @keysyms == NR_KEYS;
108: push @$keymaps, \@keysyms;
109: } else {
110: push @$keymaps, undef;
111: }
112: }
113:
114: close $pipe;
115: return $keymaps;
116: }
117:
118: # Translate keysym value to ASCII
119: #
120: sub keysym_to_ascii {
121: my $keysym = shift;
122:
123: # Non-existent keysyms have no ASCII equivalent
124: return unless $keysym;
125:
126: # Sanity check
127: die "Unexpected keysym ".sprintf ( "0x%04x\n", $keysym )."\n"
128: if $keysym & 0xf000;
129:
130: # Extract type and value
131: my $type = ( $keysym >> 8 );
132: my $value = ( $keysym & 0xff );
133:
134: # Non-simple types have no ASCII equivalent
135: return unless ( ( $type == KT_LATIN ) || ( $type == KT_ASCII ) ||
136: ( $type == KT_LETTER ) );
137:
138: # High-bit-set characters cannot be generated on a US keyboard
139: return if $value & 0x80;
140:
141: return $value;
142: }
143:
144: # Translate ASCII to descriptive name
145: #
146: sub ascii_to_name {
147: my $ascii = shift;
148:
149: if ( $ascii == 0x5c ) {
150: return "'\\\\'";
151: } elsif ( $ascii == 0x27 ) {
152: return "'\\\''";
153: } elsif ( ( $ascii >= 0x20 ) && ( $ascii <= 0x7e ) ) {
154: return sprintf ( "'%c'", $ascii );
155: } elsif ( $ascii <= 0x1a ) {
156: return sprintf ( "Ctrl-%c", ( 0x40 + $ascii ) );
157: } else {
158: return sprintf ( "0x%02x", $ascii );
159: }
160: }
161:
162: # Produce translation table between two keymaps
163: #
164: sub translate_keymaps {
165: my $from = shift;
166: my $to = shift;
167: my $map = {};
168:
169: foreach my $keymap ( 0, 1 << KG_SHIFT, 1 << KG_CTRL ) {
170: for ( my $keycode = 0 ; $keycode < NR_KEYS ; $keycode++ ) {
171: my $from_ascii = keysym_to_ascii ( $from->[$keymap]->[$keycode] )
172: or next;
173: my $to_ascii = keysym_to_ascii ( $to->[$keymap]->[$keycode] )
174: or next;
175: my $new_map = ( ! exists $map->{$from_ascii} );
176: my $update_map =
177: ( $new_map || ( $keycode < $map->{$from_ascii}->{keycode} ) );
178: if ( ( $verbosity > 1 ) &&
179: ( ( $from_ascii != $to_ascii ) ||
180: ( $update_map && ! $new_map ) ) ) {
181: printf STDERR "In keymap %d: %s => %s%s\n", $keymap,
182: ascii_to_name ( $from_ascii ), ascii_to_name ( $to_ascii ),
183: ( $update_map ? ( $new_map ? "" : " (override)" )
184: : " (ignored)" );
185: }
186: if ( $update_map ) {
187: $map->{$from_ascii} = {
188: to_ascii => $to_ascii,
189: keycode => $keycode,
190: };
191: }
192: }
193: }
194: return { map { $_ => $map->{$_}->{to_ascii} } keys %$map };
195: }
196:
197: # Parse command-line options
198: Getopt::Long::Configure ( 'bundling', 'auto_abbrev' );
199: GetOptions (
200: 'verbose|v+' => sub { $verbosity++; },
201: 'quiet|q+' => sub { $verbosity--; },
202: 'from|f=s' => sub { shift; $from_name = shift; },
203: 'help|h' => sub { pod2usage ( 1 ); },
204: ) or die "Could not parse command-line options\n";
205: pod2usage ( 1 ) unless @ARGV == 1;
206: my $to_name = shift;
207:
208: # Read and translate keymaps
209: my $from = read_keymaps ( $from_name );
210: my $to = read_keymaps ( $to_name );
211: my $map = translate_keymaps ( $from, $to );
212:
213: # Generate output
214: ( my $to_name_c = $to_name ) =~ s/\W/_/g;
215: printf "/** \@file\n";
216: printf " *\n";
217: printf " * \"".$to_name."\" keyboard mapping\n";
218: printf " *\n";
219: printf " * This file is automatically generated; do not edit\n";
220: printf " *\n";
221: printf " */\n";
222: printf "\n";
223: printf "FILE_LICENCE ( PUBLIC_DOMAIN );\n";
224: printf "\n";
225: printf "#include <ipxe/keymap.h>\n";
226: printf "\n";
227: printf "/** \"".$to_name."\" keyboard mapping */\n";
228: printf "struct key_mapping ".$to_name_c."_mapping[] __keymap = {\n";
229: foreach my $from_sym ( sort { $a <=> $b } keys %$map ) {
230: my $to_sym = $map->{$from_sym};
231: next if $from_sym == $to_sym;
232: printf "\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n", $from_sym, $to_sym,
233: ascii_to_name ( $from_sym ), ascii_to_name ( $to_sym );
234: }
235: printf "};\n";
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.