package keyboard; # $Id: keyboard.pm,v 1.3 2003/08/19 09:59:21 rider Exp $ use common; use detect_devices; use run_program; use log; use c; my $KMAP_MAGIC = 0x8B39C07F; my %lang2keyboard = ( 'be' => 'by:80', 'be_BY'=> 'by:80', 'de_AT'=> 'de_nodeadkeys:70 de:50', 'de_BE'=> 'be:70 de_nodeadkeys:60 de:50', 'de_CH'=> 'ch_de:70 ch_fr:25 de_nodeadkeys:20 de:15', 'de_DE'=> 'de_nodeadkeys:70 de:50', 'en' => 'us:90 us_intl:50', 'en_US'=> 'us:90 us_intl:50', 'en_GB'=> 'uk:89 us:60 us_intl:50', 'en_IE'=> 'uk:89 us:60 us_intl:50', 'eo' => 'us_intl:89 dvorak:20', 'fr_BE'=> 'be:85 fr:5', #'fr_CA'=> 'qc:85 fr:5', 'fr_CH'=> 'ch_fr:70 ch_de:15 fr:10', 'fr_FR'=> 'fr:90', 'kk'=>'kz:90', 'kk_KZ'=>'kz:90', 'pt_BR'=> 'br:80 la:20 pt:10 us_intl:30', 'pt_PT'=> 'pt:80', 'ru' => 'ruwin_ct_sh:80 ruwin_cplk:50 ruwin_alt:40 ruwin_ctrl:30 ru:10 ru_cplk:20 ru_alt:30 ru_ctrl:40 ru_yawerty:20', 'ru_RU'=> 'ruwin_ct_sh:80 ruwin_cplk:50 ruwin_alt:40 ruwin_ctrl:30 ru:10 ru_cplk:20 ru_alt:30 ru_ctrl:40 ru_yawerty:20', 'uk' => 'ua:85 rua:75 ru:50 ru_yawerty:40', 'uk_UA'=> 'ua:85 rua:75 ru:50 ru_yawerty:40', ); my %keyboards = ( arch() =~ /^sparc/ ? ( "de" => [ __("German"), "sunt5-de-latin1", "de" ], "dvorak" => [ __("Dvorak"), "sundvorak", "dvorak" ], "fr" => [ __("French"), "sunt5-fr-latin1", "fr" ], "ru" => [ __("Russian"), "sunt5-ru", "ru" ], # TODO: check the console map "uk" => [ __("UK keyboard"), "sunt5-uk", "gb" ], "us" => [ __("US keyboard"), "sunkeymap", "us" ], ) : ( "be" => [ __("Belgian"), "be2-latin1", "be" ], "br" => [ __("Brazilian (ABNT-2)"), "br-abnt2", "br" ], "by" => [ __("Belarusian"), "by", "byru" ], "ch_de" => [ __("Swiss (German layout)"), "sg-latin1", "de_CH" ], "ch_fr" => [ __("Swiss (French layout)"), "fr_CH-latin1", "fr_CH" ], "de" => [ __("German"), "de-latin1", "de" ], "de_nodeadkeys" => [ __("German (no dead keys)"), "de-latin1-nodeadkeys", "de(nodeadkeys)" ], "fr" => [ __("French"), "fr-latin1", "fr" ], #There is no XKB korean file yet; but using xmodmap one disables # some functioanlity; "us" used for XKB until this is fixed "kz" => [ __("Kazakh"), "kazakh", "kz" ], "pt" => [ __("Portuguese"), "pt-latin1", "pt" ], "qc" => [ __("Canadian (Quebec)"), "qc-latin1","ca_enhanced" ], "ru" => [ __("Russian(Ctrl-Shift)"), "ru_ct_sh", "ru" ], "ru_cplk" => [ __("Russian, CapsLock"), "ru_cplk", "ru" ], "ru_alt" => [ __("Russian, RghtAlt"), "ru_alt", "ru" ], "ru_ctrl" => [ __("Russian, RghtCtrl"), "ru_ctrl", "ru" ], "ruwin_ct_sh" => [ __("Russian Win, Ctrl-Shift"), "ruwin_ct_sh", "ru" ], "ruwin_cplk" => [ __("Russian Win, CapsLock"), "ruwin_cplk", "ru" ], "ruwin_alt" => [ __("Russian Win, RightAlt"), "ruwin_alt", "ru" ], "ruwin_ctrl" => [ __("Russian Win, RightCtrl"), "ruwin_ctrl", "ru" ], "ru_yawerty" => [ __("Russian (Yawerty)"),"ru-yawerty","ru_yawerty" ], # TODO: console map # TODO: console map "ua" => [ __("Ukrainian"), "ua", "ua" ], #fixme, rider "rua" => [ __("Ukrainian with Russian (rua)"), "ua", "ua,ru" ], "uk" => [ __("UK keyboard"), "uk", "gb" ], "us" => [ __("US keyboard"), "us", "us" ], "us_intl" => [ __("US keyboard (international)"), "us-latin1", "us_intl" ], ), ); sub keyboards { keys %keyboards } sub keyboard2text { $keyboards{$_[0]} && $keyboards{$_[0]}[0] } #sub keyboard2kmap { $keyboards{$_[0]} && $keyboards{$_[0]}[1] } sub keyboard2xkb { $keyboards{$_[0]} && $keyboards{$_[0]}[2] } sub keyboard2xkb_addon { $keyboards{$_[0]} && $keyboards{$_[0]}[1] } sub keyboard2kmap { my $addon=$_[1]; $addon =~ s/[^.]+\.(.+)/$1/ or $addon = ""; if ($keyboards{$_[0]}){ if ($addon){ return $keyboards{$_[0]}[1]."-$addon"; }else{ return $keyboards{$_[0]}[1]; } } } sub loadkeys_files { my ($warn) = @_; my $archkbd = arch() =~ /^sparc/ ? "sun" : arch() =~ /i.86/ ? "i386" : arch() =~ /ppc/ ? "mac" : arch(); my $p = "/usr/lib/kbd/keymaps/$archkbd"; my $post = ".kmap.gz"; my %trans = ("cz-latin2" => "cz-lat2"); my %find_file; foreach my $dir (all($p)) { $find_file{$dir} = ''; foreach (all("$p/$dir")) { $find_file{$_} && $warn and warn "file $_ is both in $find_file{$_} and $dir\n"; $find_file{$_} = "$p/$dir/$_"; } } my (@l, %l); foreach (values %keyboards) { local $_ = $trans{$_->[1]} || $_->[1]; my $l = $find_file{"$_$post"} || $find_file{first(/(..)/) . $post}; print STDERR "unknown $_\n" if $warn && !$l; $l or next; push @l, $l; foreach (`zgrep include $l | grep "^include"`) { /include\s+"(.*)"/ or die "bad line $_"; @l{grep { -e $_ } ("$p/$1.inc.gz")} = (); } } @l, keys %l, grep { -e $_ } map { "$p/$_.inc.gz" } qw(compose euro windowkeys linux-keys-bare); } sub unpack_keyboards { my ($k) = @_ or return; [ grep { my $b = $keyboards{$_->[0]}; $b or log::l("bad keyboard $_->[0] in %keyboard::lang2keyboard"); $b; } map { [ split ':' ] } split ' ', $k ]; } sub lang2keyboards { my ($l) = @_; my $li = unpack_keyboards($lang2keyboard{substr($l, 0, 5)}) || [ $keyboards{$l} && $l || "us" ]; $li->[0][1] ||= 100; $li; } sub lang2keyboard { my ($l) = @_; my $kb = lang2keyboards($l)->[0][0]; $keyboards{$kb} ? $kb : "us"; } sub load { my ($keymap) = @_; return if $::testing; my ($magic, @keymaps) = unpack "I i" . c::MAX_NR_KEYMAPS() . "a*", $keymap; $keymap = pop @keymaps; $magic != $KMAP_MAGIC and die "failed to read kmap magic"; local *F; sysopen F, "/dev/console", 2 or die "failed to open /dev/console: $!"; my $count = 0; foreach (0 .. c::MAX_NR_KEYMAPS() - 1) { $keymaps[$_] or next; my @keymap = unpack "s" . c::NR_KEYS() . "a*", $keymap; $keymap = pop @keymap; my $key = -1; foreach my $value (@keymap) { $key++; c::KTYP($value) != c::KT_SPEC() or next; ioctl(F, c::KDSKBENT(), pack("CCS", $_, $key, $value)) or die "keymap ioctl failed ($_ $key $value): $!"; } $count++; } } sub xmodmap_file { my ($keyboard) = @_; my $f = "$ENV{SHARE_PATH}/xmodmap/xmodmap.$keyboard"; if (! -e $f) { eval { require packdrake; my $packer = new packdrake("$ENV{SHARE_PATH}/xmodmap.cz2", quiet => 1); $packer->extract_archive("/tmp", "xmodmap.$keyboard"); }; $f = "/tmp/xmodmap.$keyboard"; } -e $f && $f; } sub setup { return if arch() =~ /^sparc/; if (arch() =~ /ppc/ && !$::testing && $ENV{DISPLAY}) { log::l("Fixing Mac keyboard"); run_program::run('xmodmap', "-e", "keycode 59 = BackSpace" ); run_program::run('xmodmap', "-e", "keycode 131 = Shift_R" ); run_program::run('xmodmap', "-e", "add shift = Shift_R" ); return; } my ($keyboard) = @_; my $o = $keyboards{$keyboard} or return; log::l("loading keymap $o->[1]"); if (-e (my $f = "$ENV{SHARE_PATH}/keymaps/$o->[1].bkmap")) { load(scalar cat_($f)); } else { local *F; if (my $pid = open F, "-|") { local $/ = undef; eval { load(join('', )) }; waitpid $pid, 0; } else { eval { require packdrake; my $packer = new packdrake("$ENV{SHARE_PATH}/keymaps.cz2", quiet => 1); $packer->extract_archive(undef, "$o->[1].bkmap"); }; c::_exit(0); } } my $f = xmodmap_file(substr($keyboard,0,2)); #hack for all lang related keyboards eval { run_program::run('xmodmap', $f) } if $f && !$::testing && $ENV{DISPLAY}; } sub write { my ($prefix, $keyboard, $charset, $isNotDelete,$lang ) = @_; my $config = read_raw($prefix); put_in_hash($config, { KEYTABLE => keyboard2kmap($keyboard,$lang), KBCHARSET => $charset, }); add2hash_($config, { DISABLE_WINDOWS_KEY => bool2yesno(detect_devices::isLaptop()), BACKSPACE => $isNotDelete ? "BackSpace" : "Delete", }); setVarsInSh("$prefix/etc/sysconfig/keyboard", $config); run_program::rooted($prefix, "dumpkeys > /etc/sysconfig/console/default.kmap") or log::l("dumpkeys failed"); if (arch() =~ /ppc/) { my $s = "dev.mac_hid.keyboard_sends_linux_keycodes = 1"; substInFile { $_ = '' if /^\Qdev.mac_hid.keyboard_sends_linux_keycodes/; $_ .= $s if eof; } "$prefix/etc/sysctl.conf"; } } sub read_raw { my ($prefix) = @_; my %config = getVarsFromSh("$prefix/etc/sysconfig/keyboard"); \%config; } sub read { my ($prefix) = @_; my $keytable = read_raw($prefix)->{KEYTABLE}; keyboard2kmap($_) eq $keytable and return $_ foreach keys %keyboards; $keyboards{$keytable} && $keytable; } 1;