3 # Copyright 2015 Daiki Ueno <dueno@src.gnome.org>
4 # 2016 Parag Nemade <pnemade@redhat.com>
5 # 2017 Alan <alan@boum.org>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU Lesser General Public License as
9 # published by the Free Software Foundation; either version 2 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this program; if not, see
19 # <http://www.gnu.org/licenses/>.
28 import xml
.etree
.ElementTree
31 gi
.require_version('GnomeDesktop', '3.0') # NOQA: E402
32 from gi
.repository
import GnomeDesktop
34 ESCAPE_PATTERN
= re
.compile(r
'\\u\{([0-9A-Fa-f]+?)\}')
35 ISO_PATTERN
= re
.compile(r
'[A-E]([0-9]+)')
37 LOCALE_TO_XKB_OVERRIDES
= {
50 def parse_single_key(value
):
52 return chr(int(m
.group(1), 16))
53 value
= ESCAPE_PATTERN
.sub(unescape
, value
)
57 def parse_rows(keymap
):
59 for _map
in keymap
.iter('map'):
60 value
= _map
.get('to')
61 key
= [parse_single_key(value
)]
63 if not ISO_PATTERN
.match(iso
):
64 sys
.stderr
.write('invalid ISO key name: %s\n' % iso
)
66 if not iso
[0] in unsorted_rows
:
67 unsorted_rows
[iso
[0]] = []
68 unsorted_rows
[iso
[0]].append((int(iso
[1:]), key
))
70 longPress
= _map
.get('longPress')
72 for value
in longPress
.split(' '):
73 subkey
= parse_single_key(value
)
77 for k
, v
in sorted(list(unsorted_rows
.items()),
81 for key
in sorted(v
, key
=lambda x
: x
):
88 def convert_xml(tree
):
90 for xml_keyboard
in tree
.iter("keyboard"):
91 locale_full
= xml_keyboard
.get("locale")
92 locale
, sep
, end
= locale_full
.partition("-t-")
93 root
["locale"] = locale
94 for xml_name
in tree
.iter("name"):
95 name
= xml_name
.get("value")
99 for index
, keymap
in enumerate(tree
.iter('keyMap')):
100 # FIXME: heuristics here
101 modifiers
= keymap
.get('modifiers')
105 elif 'shift' in modifiers
.split(' '):
111 level
["level"] = modifiers
113 level
["rows"] = parse_rows(keymap
)
114 root
["levels"].append(level
)
118 def locale_to_xkb(locale
, name
):
119 if locale
in sorted(LOCALE_TO_XKB_OVERRIDES
.keys()):
120 xkb
= LOCALE_TO_XKB_OVERRIDES
[locale
]
121 logging
.debug("override for %s → %s",
126 raise KeyError("layout %s explicitely disabled in overrides"
128 xkb_names
= sorted(name_to_xkb
.keys())
129 if name
in xkb_names
:
130 return name_to_xkb
[name
]
132 logging
.debug("name %s failed" % name
)
133 for sub_name
in name
.split(' '):
134 if sub_name
in xkb_names
:
135 xkb
= name_to_xkb
[sub_name
]
136 logging
.debug("dumb mapping failed but match with locale word: "
138 locale
, name
, xkb
, sub_name
)
141 logging
.debug("sub_name failed")
142 for xkb_name
in xkb_names
:
143 for xkb_sub_name
in xkb_name
.split(' '):
144 if xkb_sub_name
.strip('()') == name
:
145 xkb
= name_to_xkb
[xkb_name
]
146 logging
.debug("dumb mapping failed but match with xkb word: "
148 locale
, name
, xkb
, xkb_name
)
150 raise KeyError("failed to find XKB mapping for %s" % locale
)
153 def convert_file(source_file
, destination_path
):
154 logging
.info("Parsing %s", source_file
)
156 itree
= xml
.etree
.ElementTree
.ElementTree()
157 itree
.parse(source_file
)
159 root
= convert_xml(itree
)
162 xkb_name
= locale_to_xkb(root
["locale"], root
["name"])
163 except KeyError as e
:
166 destination_file
= os
.path
.join(destination_path
, xkb_name
+ ".json")
168 with
open(destination_file
, 'w', encoding
="utf-8") as dest_fd
:
169 json
.dump(root
, dest_fd
, ensure_ascii
=False, indent
=2, sort_keys
=True)
171 logging
.debug("written %s", destination_file
)
174 def load_xkb_mappings():
175 xkb
= GnomeDesktop
.XkbInfo()
176 layouts
= xkb
.get_all_layouts()
179 for layout
in layouts
:
180 name
= xkb
.get_layout_info(layout
).display_name
181 name_to_xkb
[name
] = layout
186 locale
.setlocale(locale
.LC_ALL
, "C")
187 name_to_xkb
= load_xkb_mappings()
190 if __name__
== "__main__":
191 if "DEBUG" in os
.environ
:
192 logging
.basicConfig(level
=logging
.DEBUG
)
194 if len(sys
.argv
) < 2:
195 print("supply a CLDR keyboard file")
198 if len(sys
.argv
) < 3:
199 print("supply an output directory")
203 destination
= sys
.argv
[2]
204 if os
.path
.isfile(source
):
205 convert_file(source
, destination
)
206 elif os
.path
.isdir(source
):
207 for path
in glob
.glob(source
+ "/*-t-k0-android.xml"):
208 convert_file(path
, destination
)