posts:bugs: Update URL for my utf_8 iText patch
[blog.git] / posts / Xmodmap / gen_sampler.py
1 #!/usr/bin/env python3
2 #
3 # Copyright (C) 2010-2012  W. Trevor King
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU 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, see <http://www.gnu.org/licenses/>.
17
18 """Generate a unicode sampler from an Xmodmap file
19 """
20
21 ENCODING = 'utf-8'
22 XKEYSIMDEF_H = "/usr/include/X11/keysymdef.h"
23
24
25 def sample(lines, keysyms):
26     """Convert lines from an Xmodmap file to sampler lines."""
27     for line in lines:
28         # parse line
29         line = line.strip()
30         if not line.startswith('keycode '):
31             continue
32         fields = line.split()
33         # keycode  10 = 1 exclam U2081 onehalf
34         fields = fields + [None] * (7-len(fields))
35         k,keycode,eq,a,b,c,d = fields
36         assert k == 'keycode', k
37         assert eq == '=', eq
38         keycode = int(keycode)
39         if keycode > 100:
40             continue  # control code, not very interesting
41
42         # convert keys to unicode
43         keys = [a,b,c,d]
44         for i,key in enumerate(keys):
45             if key == None:
46                 continue
47             if key in keysyms:
48                 keys[i] = keysyms[key]
49                 continue
50             assert key.startswith('U'), key
51             codepoint = int(key[len('U'):], 16)
52             keys[i] = chr(codepoint)
53
54         line = (' '.join([key or ' ' for key in keys])).rstrip()
55         if len(line) > 0:
56             yield line
57
58 def read_keysyms(file=None):
59     """Read keysymdef.h and extract keysym -> unicode mappings."""
60     if file == None:
61         file = XKEYSIMDEF_H
62     keysyms = {}
63     for line in open(file, 'r').readlines():
64         if not line.startswith('#define XK_'):
65             continue
66         # #define XK_space                         0x0020  /* U+0020 SPACE */
67         fields = line.split()
68         assert len(fields) >= 3, fields
69         keysym = fields[1]
70         code = fields[2]
71         assert keysym.startswith('XK_'), keysym
72         assert code.startswith('0x'), code
73         keysym = keysym[len('XK_'):]
74         if len(fields) >= 5:
75             codepoint = fields[4]
76             if not codepoint.startswith('U+'):
77                 codepoint = None
78             else:
79                 codepoint = int(codepoint[len('U+'):], 16)
80                 codepoint = chr(codepoint)
81         else:
82             codepoint = None  # keysym to unicode mapping not well defined
83         keysyms[keysym] = codepoint
84     return keysyms
85
86
87 if __name__ == '__main__':
88     import io
89     import sys
90     import time
91
92     xmodmap_filename = sys.argv[1]
93
94     keysyms = read_keysyms()
95     lines = open(xmodmap_filename, 'r').readlines()
96     #lines = sample(lines, keysyms)
97     lines = sorted(sample(lines, keysyms))
98     out = io.TextIOWrapper(sys.stdout.buffer, ENCODING)
99     out.write('These characters are bound in my current .Xmodmap\n')
100     out.write('({})\n'.format(time.asctime()))
101     out.write('\n')
102     for line in lines:
103         out.write(line)
104         out.write('\n')