summaryrefslogtreecommitdiffstats
path: root/src/keymap-gen.pl
blob: 56953f8cd0665878d7d143aaa505c63f10ae863e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/perl

use strict;
use warnings;

use Text::CSV;

my %names = (
    linux => [],
    osx => []
);

my %namecolumns = (
    linux => 0,
    osx => 2,
    win32 => 10,
    x11 => 14,
    );

# Base data sources:
#
#  linux:     Linux: linux/input.h                                  (master set)
#    osx:      OS-X: Carbon/HIToolbox/Events.h                      (manually mapped)
# atset1:  AT Set 1: linux/drivers/input/keyboard/atkbd.c           (atkbd_set2_keycode + atkbd_unxlate_table)
# atset2:  AT Set 2: linux/drivers/input/keyboard/atkbd.c           (atkbd_set2_keycode)
# atset3:  AT Set 3: linux/drivers/input/keyboard/atkbd.c           (atkbd_set3_keycode)
#     xt:        XT: linux/drivers/input/keyboard/xt.c              (xtkbd_keycode)
#  xtkbd: Linux RAW: linux/drivers/char/keyboard.c                  (x86_keycodes)
#    usb:   USB HID: linux/drivers/hid/usbhid/usbkbd.c              (usb_kbd_keycode)
#  win32:     Win32: mingw32/winuser.h                              (manually mapped)
# xwinxt:   XWin XT: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h} (xt + manually transcribed)
# xkbdxt:   XKBD XT: xf86-input-keyboard/src/at_scancode.c
#(xt + manually transcribed)
#    x11: X11 keysyms: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h
#
# Derived data sources
#
#    xorgevdev: Xorg +  evdev: linux + an offset
#      xorgkbd: Xorg +    kbd: xkbdxt + an offset
#  xorgxquartz: Xorg +   OS-X: osx + an offset
#     xorgxwin: Xorg + Cygwin: xwinxt + an offset
#          rfb:   XT over RFB: xtkbd + special re-encoding of high bit

my @basemaps = qw(linux osx atset1 atset2 atset3 xt xtkbd usb win32 xwinxt xkbdxt x11);
my @derivedmaps = qw(xorgevdev xorgkbd xorgxquartz xorgxwin rfb);
my @maps = (@basemaps, @derivedmaps);

my %maps;

foreach my $map (@maps) {
    $maps{$map} = [ [], [] ];
}
my %mapcolumns = (
    osx => 3,
    atset1 => 4,
    atset2 => 5,
    atset3 => 6,
    xt => 7,
    xtkbd => 8,
    usb => 9,
    win32 => 11,
    xwinxt => 12,
    xkbdxt => 13,
    x11 => 15
    );

sub help {
    my $msg = shift;
    print $msg;
    print "\n";
    print "Valid keymaps are:\n";
    print "\n";
    foreach my $name (sort { $a cmp $b } keys %maps) {
	print "  $name\n";
    }
    print "\n";
    exit (1);
}

if ($#ARGV != 2) {
    help("syntax: $0 KEYMAPS SRCMAP DSTMAP\n");
}

my $keymaps = shift @ARGV;
my $src = shift @ARGV;
my $dst = shift @ARGV;

help("$src is not a known keymap\n") unless exists $maps{$src};
help("$dst is not a known keymap\n") unless exists $maps{$dst};


open CSV, $keymaps
    or die "cannot read $keymaps: $!";

my $csv = Text::CSV->new();
# Discard column headings
$csv->getline(\*CSV);

my $row;
while ($row = $csv->getline(\*CSV)) {
    my $linux = $row->[1];

    $linux = hex($linux) if $linux =~ /0x/;

    my $to = $maps{linux}->[0];
    my $from = $maps{linux}->[1];
    $to->[$linux] = $linux;
    $from->[$linux] = $linux;

    foreach my $name (keys %namecolumns) {
	my $col = $namecolumns{$name};
	my $val = $row->[$col];

	$val = "" unless defined $val;

	$names{$name}->[$linux] = $val;
    }

    foreach my $name (keys %mapcolumns) {
	my $col = $mapcolumns{$name};
	my $val = $row->[$col];

	next unless defined $val && $val ne "";
	$val = hex($val) if $val =~ /0x/;

	$to = $maps{$name}->[0];
        $from = $maps{$name}->[1];
	$to->[$linux] = $val;
	$from->[$val] = $linux;
    }

    # XXX there are some special cases in kbd to handle
    # Xorg KBD driver is the Xorg KBD XT codes offset by +8
    # The XKBD XT codes are the same as normal XT codes
    # for values <= 83, and completely made up for extended
    # scancodes :-(
    ($to, $from) = @{$maps{xorgkbd}};
    if (defined $maps{xkbdxt}->[0]->[$linux]) {
	$to->[$linux] = $maps{xkbdxt}->[0]->[$linux] + 8;
	$from->[$to->[$linux]] = $linux;
    }

    # Xorg evdev is simply Linux keycodes offset by +8
    ($to, $from) = @{$maps{xorgevdev}};
    $to->[$linux] = $linux + 8;
    $from->[$to->[$linux]] = $linux;

    # Xorg XQuartz is simply OS-X keycodes offset by +8
    ($to, $from) = @{$maps{xorgxquartz}};
    if (defined $maps{osx}->[0]->[$linux]) {
	$to->[$linux] = $maps{osx}->[0]->[$linux] + 8;
	$from->[$to->[$linux]] = $linux;
    }

    # RFB keycodes are XT kbd keycodes with a slightly
    # different encoding of 0xe0 scan codes. RFB uses
    # the high bit of the first byte, instead of the low
    # bit of the second byte.
    ($to, $from) = @{$maps{rfb}};
    my $xtkbd = $maps{xtkbd}->[0]->[$linux];
    if (defined $xtkbd) {
	$to->[$linux] = $xtkbd ? (($xtkbd & 0x100)>>1) | ($xtkbd & 0x7f) : 0;
	$from->[$to->[$linux]] = $linux;
    }

    # Xorg Cygwin is the Xorg Cygwin XT codes offset by +8
    # The Cygwin XT codes are the same as normal XT codes
    # for values <= 83, and completely made up for extended
    # scancodes :-(
    ($to, $from) = @{$maps{xorgxwin}};
    if (defined $maps{xwinxt}->[0]->[$linux]) {
	$to->[$linux] = $maps{xwinxt}->[0]->[$linux] + 8;
	$from->[$to->[$linux]] = $linux;
    }

#    print $linux, "\n";
}

close CSV;

my $srcmap = $maps{$src}->[1];
my $dstmap = $maps{$dst}->[0];

printf "static const guint16 keymap_%s2%s[] = {\n", $src, $dst;

for (my $i = 0 ; $i <= $#{$srcmap} ; $i++) {
    my $linux = $srcmap->[$i] || 0;
    my $j = $dstmap->[$linux];
    next unless $linux && $j;

    my $srcname = $names{$src}->[$linux] if exists $names{$src};
    my $dstname = $names{$dst}->[$linux] if exists $names{$dst};
    my $vianame = $names{linux}->[$linux] unless $src eq "linux" || $dst eq "linux";

    $srcname = "" unless $srcname;
    $dstname = "" unless $dstname;
    $vianame = "" unless $vianame;
    $srcname = " ($srcname)" if $srcname;
    $dstname = " ($dstname)" if $dstname;
    $vianame = " ($vianame)" if $vianame;

    my $comment;
    if ($src ne "linux" && $dst ne "linux") {
	$comment = sprintf "%d%s => %d%s via %d%s", $i, $srcname, $j, $dstname, $linux, $vianame;
    } else {
	$comment = sprintf "%d%s => %d%s", $i, $srcname, $j, $dstname;
    }

    my $data = sprintf "[0x%x] = 0x%x,", $i, $j;

    printf "  %-20s /* %s */\n", $data, $comment;
}

print "};\n";