#!/usr/bin/perl
use warnings 'all';
use strict;
use File::Temp ();
BEGIN {
my $file;
if ($ARGV[1]) {
$file = $ARGV[1];
} else {
$file = './KeyboardNames.pl';
}
do "$file";
}
my $freebsd = '';
my $model = $ARGV[0];
if ($model =~ s/^freebsd-//) {
$freebsd = '-freebsd -backspace del';
}
my $dir = $freebsd ? 'freebsd-keymaps' : 'linux-keymaps';
my $xkbdir;
if ($ARGV[2]) {
$xkbdir = $ARGV[2];
} else {
$xkbdir = "`pwd`/ckb";
}
my $supported;
if ($ARGV[3]) {
$supported = " $ARGV[3] ";
$supported =~ s/ +/() /g;
$supported =~ s/\)\(\)/)/g;
} else {
$supported = 0;
}
# Don't forget to update also the list in keyboard-configuration.config
my $nonlatin = ' af am ara ben bd bg bt by deva et ge gh gr guj guru \
il in iq ir iku kan kh kz la lao lk kg ma mk mm mn mv mal \
np ori pk ru scc sy syr tel th tj tam tib ua uig uz ';
my %keymaps;
sub warning {
print STDERR "WARNING: @_";
}
sub execute {
if (0) {
printf STDERR "%s\n", $_[0];
}
return system ($_[0]);
}
sub ensuredir {
mkdir $_[0];
die "$0: $_[0]: $!\n" if (! -d $_[0]);
}
sub checkrename {
rename $_[0], $_[1] or die "$0: failed to rename $_[0] to $_[1]: $!\n";
}
sub read_temp_kmap {
my $name = $_[0];
open (KMAP, "$dir/$model-$name") or die "$0: $dir/$model-$name: $!\n";
if ($freebsd) {
while (<KMAP>) {
if (/^ *([0-9]+) .*/) {
$keymaps{$name}[$1] = $_;
}
}
} else {
while (<KMAP>) {
if (/^keycode ([0-9]+) =.*/) {
$keymaps{$name}[$1] = $_;
}
}
}
close KMAP;
}
ensuredir $dir;
printf STDERR "Compiling compact keymaps for %s...\n", $model;
for my $layout (values %KeyboardNames::layouts) {
next if ($layout eq 'nec/jp');
next if ($layout eq 'nec_vndr/jp');
my $actual_layout = (($layout eq 'rs') ? 'rs,rs'
: (($layout eq 'lt') ? 'lt,lt'
: (($nonlatin =~ / $layout /)
? "us,$layout" : $layout)));
for my $variant ('', values %{$KeyboardNames::variants{$layout}}) {
my $actual_variant;
if ($actual_layout eq 'rs,rs') {
if ($variant =~ /latin/) {
$actual_variant = "$variant,$variant";
} elsif ($variant eq 'yz') {
$actual_variant = "latinyz,$variant";
} elsif ($variant eq 'alternatequotes') {
$actual_variant = "latinalternatequotes,$variant";
} else {
$actual_variant = "latin,$variant";
}
} elsif ($actual_layout eq 'lt,lt') {
if ($variant eq 'us') {
$actual_variant = "us,basic";
} else {
$actual_variant = "$variant,us";
}
} elsif ($actual_layout =~ /,/) {
$actual_variant = ",$variant";
} else {
$actual_variant = $variant;
}
$actual_variant = "'$actual_variant'";
if (! -f "$dir/$model-$layout:$variant") {
my $cmd = "./ckbcomp $freebsd -compact -I. -I${xkbdir} -rules xorg"
." -model $model"
." -layout $actual_layout -variant $actual_variant"
." >$dir/$model-$layout:$variant.new"
." 2>>ckbcomp.log";
execute ("echo '" .$cmd ."' >>ckbcomp.log");
execute ($cmd) and die "$0: ckbcomp failed\n" if (! -f "$dir/$model-$layout:$variant.new");
checkrename "$dir/$model-$layout:$variant.new", "$dir/$model-$layout:$variant";
}
if (-f "$dir/$model-$layout:$variant") {
if ($supported) {
if (index($supported, " $layout($variant) ") ge 0) {
read_temp_kmap "$layout:$variant";
}
} else {
read_temp_kmap "$layout:$variant";
}
}
}
}
sub subtraction {
my $kmap1 = $keymaps{$_[0]};
my $kmap2 = $keymaps{$_[1]};
my $result = 0;
for my $k (0 .. $#{$kmap1}-1) {
if (defined $kmap1->[$k]) {
if (! defined $kmap2->[$k] || $kmap1->[$k] ne $kmap2->[$k]) {
$result++;
}
} elsif (defined $kmap2->[$k]) {
# kmap1 not a superset of kmap2, so not eligible for reduction
return 10000000;
# I don't think it will be a problem if one additional key
# is defined in kmap1, a key that would be otherwise
# undefined. However, the size reduction when this code is
# removed, is not that big: 127Kb -> 117Kb. (Anton Zinoviev)
}
}
return $result;
}
printf STDERR "Computing differential matrix for %s...\n", $model;
my %kmaps;
my %matrice;
my %reduce;
my $k;
my $k1;
my $k2;
for $k (keys %keymaps) {
$kmaps{$k} = 1;
}
for $k1 (keys %kmaps) {
for $k2 (keys %kmaps) {
$matrice{$k1}{$k2} = subtraction($k1, $k2);
}
}
printf STDERR "Reducing the keymaps for %s...\n", $model;
while (keys %kmaps) {
my $mink1 = '';
my $mink2 = '';
my $minsub = 10000000;
for $k1 (sort keys %kmaps) {
for $k2 (sort keys %kmaps) {
next if ($k1 eq $k2);
if ($matrice{$k1}{$k2} < $minsub) {
$mink1 = $k1;
$mink2 = $k2;
$minsub = $matrice{$k1}{$k2};
}
}
}
if ($mink1 eq '') {
for $k2 (sort keys %kmaps) {
$mink1 = $k2;
last;
}
}
$reduce{$mink1} = $mink2;
delete $kmaps{$mink1};
}
printf STDERR "Dumping the encoded keymaps for %s...\n", $model;
for $k1 (sort keys %reduce) {
my $kmap1 = $keymaps{$k1};
$k2 = $reduce{$k1};
if ($k2 ne '') {
my $kmap2 = $keymaps{$k2};
printf "%s::#include %s\n", $k1, $k2;
for my $k (0 .. $#{$kmap1}-1) {
if (defined $kmap1->[$k]) {
if (! defined $kmap2->[$k] || $kmap1->[$k] ne $kmap2->[$k]) {
printf "%s::%s", $k1, $kmap1->[$k];
}
}
}
} else {
for my $k (0 .. $#{$kmap1}-1) {
if (defined $kmap1->[$k]) {
printf "%s::%s", $k1, $kmap1->[$k];
}
}
}
}
my %options = (
'grp:switch' => [ 'ralt' ],
'grp:lswitch' => [ 'lalt' ],
'grp:win_switch' => [ 'rwin', 'lwin' ],
'grp:lwin_switch' => [ 'rwin' ],
'grp:rwin_switch' => [ 'lwin' ],
'grp:toggle' => [ 'ralt' ],
'grp:shifts_toggle' => [ 'lshift', 'rshift' ],
'grp:ctrls_toggle' => [ 'lctrl', 'rctrl' ],
'grp:alts_toggle' => [ 'ralt', 'lalt' ],
'grp:ctrl_shift_toggle' => [ 'lshift', 'rshift', 'lctrl', 'rctrl' ],
'grp:caps_toggle' => [ 'caps' ],
'grp:caps_switch' => [ 'caps' ],
'grp:shift_caps_toggle' => [ 'caps' ],
'grp:shift_caps_switch' => [ 'caps' ],
'grp:win_menu_switch' => [ 'rwin', 'lwin', 'menu' ],
'grp:alt_caps_toggle' => [ 'caps' ],
'grp:ctrl_alt_toggle' => [ 'lalt', 'ralt', 'lctrl', 'rctrl' ],
'grp:alt_shift_toggle' => [ 'lalt', 'ralt', 'lshift', 'rshift' ],
'grp:alt_space_toggle' => [ 'empty' ],
'grp:menu_toggle' => [ 'menu' ],
'grp:lwin_toggle' => [ 'lwin' ],
'grp:rwin_toggle' => [ 'rwin' ],
'grp:lshift_toggle' => [ 'lshift' ],
'grp:rshift_toggle' => [ 'rshift' ],
'grp:rctrl_switch' => [ 'rctrl' ],
'grp:lctrl_toggle' => [ 'lctrl' ],
'grp:rctrl_toggle' => [ 'rctrl' ],
'grp:lalt_toggle' => [ 'lalt' ],
'grp:sclk_toggle' => [ 'sclk' ],
'grp:lctrl_rctrl_switch' => [ 'rctrl', 'lctrl' ],
'lv3:switch' => [ 'rctrl' ],
'lv3:ralt_switch' => [ 'ralt' ],
'lv3:ralt_switch_multikey' => [ 'ralt' ],
'lv3:ralt_alt' => [ 'ralt' ],
'lv3:lalt_switch' => [ 'lalt' ],
'lv3:alt_switch' => [ 'lalt', 'ralt' ],
'lv3:menu_switch' => [ 'menu' ],
'lv3:win_switch' => [ 'lwin', 'rwin' ],
'lv3:lwin_switch' => [ 'lwin' ],
'lv3:rwin_switch' => [ 'rwin' ],
'lv3:enter_switch' => [ 'empty' ],
'ctrl:nocaps' => [ 'caps', 'lctrl' ],
'ctrl:swapcaps' => [ 'caps', 'lctrl' ],
'compose:ralt' => [ 'ralt' ],
'compose:rwin' => [ 'rwin' ],
'compose:menu' => [ 'menu' ],
'compose:caps' => [ 'caps' ],
'compose:rctrl' => [ 'rctrl' ],
'eurosign:e' => [ 'empty' ],
'eurosign:5' => [ 'empty' ],
'eurosign:2' => [ 'empty' ],
);
my %keycodes = (
'amiga' => 'amiga(de)',
'ataritt' => 'ataritt(de)',
'macintosh' => 'xfree86',
'macintosh_old' => 'macintosh',
'pc105' => 'xfree86',
'sun4' => 'sun(type4_euro)',
'sun5' => 'sun(type5_euro)',
'abnt2' => 'xfree86(abnt2)',
);
my $keycodes = (defined $keycodes {$model}
? $keycodes {$model}
: 'xfree86');
for my $option (sort keys %options) {
my $layout = '';
for my $mod (@{$options{$option}}) {
$layout = $layout . "+stdmodifiers($mod)";
}
$layout =~ s/^\+//;
if ($option =~ /grp:(.*)/) {
$layout = $layout . "+group($1)";
} elsif ($option =~ /ctrl:(.*)/) {
$layout = $layout . "+ctrl($1)";
} elsif ($option =~ /lv3:(.*)/) {
$layout = $layout . "+level3($1)";
} elsif ($option =~ /compose:(.*)/) {
$layout = $layout . "+compose($1)";
} elsif ($option =~ /eurosign:(.*)/) {
$layout = $layout . "+eurosign($1)";
}
my $tmp = new File::Temp(TEMPLATE => 'kbdcompilerXXXXXX');
execute ("./ckbcomp $freebsd -compact -I. -I${xkbdir}"
." -keycodes '$keycodes' -symbols '$layout' >$tmp");
if (! -f $tmp->filename) {
die "$0: ckbcomp failed\n" if (! -f $tmp->filename);
} else {
execute ("grep '^keycode' $tmp | sed 's/^/". $option ."::/'");
}
}