You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

genkeymap.pl 6.5KB

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