diff --git a/data/key-mapper-keycodes b/data/key-mapper-keycodes new file mode 100644 index 00000000..27ce68a3 --- /dev/null +++ b/data/key-mapper-keycodes @@ -0,0 +1,265 @@ +// keycodes configuration for key-mapper presets + +// The concept of "reasonable symbolic names" [3] doesn't apply +// when mouse buttons are all over the place. Furthermore this file has +// to work for all devices, which place their keys in different places. +// So create an identity mapping instead to make generating "symbols" files +// easier. Keycode 10 -> "<10>" +// This has the added benefit that keycodes reported by xev can be +// identified in the symbols file. + +// Keycodes reported by evdev are 8 lower than those reported by xev, +// so 10 in this file means 2 in key-mapper. + +default xkb_keycodes "key-mapper" { + minimum = 8; + maximum = 255; + <8> = 8; + <9> = 9; + <10> = 10; + <11> = 11; + <12> = 12; + <13> = 13; + <14> = 14; + <15> = 15; + <16> = 16; + <17> = 17; + <18> = 18; + <19> = 19; + <20> = 20; + <21> = 21; + <22> = 22; + <23> = 23; + <24> = 24; + <25> = 25; + <26> = 26; + <27> = 27; + <28> = 28; + <29> = 29; + <30> = 30; + <31> = 31; + <32> = 32; + <33> = 33; + <34> = 34; + <35> = 35; + <36> = 36; + <37> = 37; + <38> = 38; + <39> = 39; + <40> = 40; + <41> = 41; + <42> = 42; + <43> = 43; + <44> = 44; + <45> = 45; + <46> = 46; + <47> = 47; + <48> = 48; + <49> = 49; + <50> = 50; + <51> = 51; + <52> = 52; + <53> = 53; + <54> = 54; + <55> = 55; + <56> = 56; + <57> = 57; + <58> = 58; + <59> = 59; + <60> = 60; + <61> = 61; + <62> = 62; + <63> = 63; + <64> = 64; + <65> = 65; + <66> = 66; + <67> = 67; + <68> = 68; + <69> = 69; + <70> = 70; + <71> = 71; + <72> = 72; + <73> = 73; + <74> = 74; + <75> = 75; + <76> = 76; + <77> = 77; + <78> = 78; + <79> = 79; + <80> = 80; + <81> = 81; + <82> = 82; + <83> = 83; + <84> = 84; + <85> = 85; + <86> = 86; + <87> = 87; + <88> = 88; + <89> = 89; + <90> = 90; + <91> = 91; + <92> = 92; + <93> = 93; + <94> = 94; + <95> = 95; + <96> = 96; + <97> = 97; + <98> = 98; + <99> = 99; + <100> = 100; + <101> = 101; + <102> = 102; + <103> = 103; + <104> = 104; + <105> = 105; + <106> = 106; + <107> = 107; + <108> = 108; + <109> = 109; + <110> = 110; + <111> = 111; + <112> = 112; + <113> = 113; + <114> = 114; + <115> = 115; + <116> = 116; + <117> = 117; + <118> = 118; + <119> = 119; + <120> = 120; + <121> = 121; + <122> = 122; + <123> = 123; + <124> = 124; + <125> = 125; + <126> = 126; + <127> = 127; + <128> = 128; + <129> = 129; + <130> = 130; + <131> = 131; + <132> = 132; + <133> = 133; + <134> = 134; + <135> = 135; + <136> = 136; + <137> = 137; + <138> = 138; + <139> = 139; + <140> = 140; + <141> = 141; + <142> = 142; + <143> = 143; + <144> = 144; + <145> = 145; + <146> = 146; + <147> = 147; + <148> = 148; + <149> = 149; + <150> = 150; + <151> = 151; + <152> = 152; + <153> = 153; + <154> = 154; + <155> = 155; + <156> = 156; + <157> = 157; + <158> = 158; + <159> = 159; + <160> = 160; + <161> = 161; + <162> = 162; + <163> = 163; + <164> = 164; + <165> = 165; + <166> = 166; + <167> = 167; + <168> = 168; + <169> = 169; + <170> = 170; + <171> = 171; + <172> = 172; + <173> = 173; + <174> = 174; + <175> = 175; + <176> = 176; + <177> = 177; + <178> = 178; + <179> = 179; + <180> = 180; + <181> = 181; + <182> = 182; + <183> = 183; + <184> = 184; + <185> = 185; + <186> = 186; + <187> = 187; + <188> = 188; + <189> = 189; + <190> = 190; + <191> = 191; + <192> = 192; + <193> = 193; + <194> = 194; + <195> = 195; + <196> = 196; + <197> = 197; + <198> = 198; + <199> = 199; + <200> = 200; + <201> = 201; + <202> = 202; + <203> = 203; + <204> = 204; + <205> = 205; + <206> = 206; + <207> = 207; + <208> = 208; + <209> = 209; + <210> = 210; + <211> = 211; + <212> = 212; + <213> = 213; + <214> = 214; + <215> = 215; + <216> = 216; + <217> = 217; + <218> = 218; + <219> = 219; + <220> = 220; + <221> = 221; + <222> = 222; + <223> = 223; + <224> = 224; + <225> = 225; + <226> = 226; + <227> = 227; + <228> = 228; + <229> = 229; + <230> = 230; + <231> = 231; + <232> = 232; + <233> = 233; + <234> = 234; + <235> = 235; + <236> = 236; + <237> = 237; + <238> = 238; + <239> = 239; + <240> = 240; + <241> = 241; + <242> = 242; + <243> = 243; + <244> = 244; + <245> = 245; + <246> = 246; + <247> = 247; + <248> = 248; + <249> = 249; + <250> = 250; + <251> = 251; + <252> = 252; + <253> = 253; + <254> = 254; + <255> = 255; +}; diff --git a/keymapper/config.py b/keymapper/config.py index f0297658..2dd5ef59 100644 --- a/keymapper/config.py +++ b/keymapper/config.py @@ -56,6 +56,10 @@ INITIAL_CONFIG = { "y_scroll_speed": 0.5, }, }, + # if true, will generate symbols and keycodes files and applies them + # to the device (setxkbmap) in order to inject keys that are unknown + # to the system + 'generate_xkb_config': True } diff --git a/keymapper/gui/window.py b/keymapper/gui/window.py index 00d0f51e..70ffdcc2 100644 --- a/keymapper/gui/window.py +++ b/keymapper/gui/window.py @@ -51,6 +51,7 @@ from keymapper.groups import ( ) from keymapper.gui.row import Row, to_string from keymapper.key import Key +from keymapper.gui.xkb import apply_xkb_config from keymapper.gui.reader import reader from keymapper.gui.helper import is_helper_running from keymapper.injection.injector import RUNNING, FAILED, NO_GRAB @@ -677,6 +678,14 @@ class Window: self.show_status(CTX_APPLY, msg) + # TODO test + if custom_mapping.get('generate_xkb_config'): + try: + apply_xkb_config(self.group.key) + except Exception as error: + # since this is optional, ignore all exceptions + logger.error('apply_xkb_config failed: %s', error) + self.show_device_mapping_status() return False diff --git a/keymapper/gui/xkb.py b/keymapper/gui/xkb.py new file mode 100644 index 00000000..ab7a25f5 --- /dev/null +++ b/keymapper/gui/xkb.py @@ -0,0 +1,113 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# key-mapper - GUI for device specific keyboard mappings +# Copyright (C) 2021 sezanzeb +# +# This file is part of key-mapper. +# +# key-mapper is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# key-mapper is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with key-mapper. If not, see . + + +"""Handles calls to setxkbmap. See injection/xkb.py for more info. + +Since the daemon doesn't know about the X session the gui has to do it. +""" + + +import os +import subprocess +import time + +from keymapper.logger import logger +from keymapper.injection.injector import get_udev_name +from keymapper.injection.xkb import get_xkb_symbols_name + + +def get_device_id(device): + """Return the device ID as known to the display server. + + Can be used in setxkbmap. + + Parameters + ---------- + device : string + Device name as found in evtest + """ + try: + names = subprocess.check_output(["xinput", "list", "--name-only"]) + names = names.decode().split("\n") + ids = subprocess.check_output(["xinput", "list", "--id-only"]) + ids = ids.decode().split("\n") + except subprocess.CalledProcessError as error: + # systemd services and ttys can't do that + logger.error(str(error)) + return None + + for name, id in zip(names, ids): + if name == device: + device_id = id + break + else: + return None + + return device_id + + +def apply_xkb_config(group_key): + """Call setxkbmap to apply a different xkb keyboard layout to a device. + + Parameters + ---------- + group_key : string + """ + # TODO test + # needs at least 0.2 seconds for me until the mapping device + # is visible in xinput + mapped_name = get_udev_name(group_key, "mapped") + + for _ in range(5): + time.sleep(0.2) + device_id = get_device_id(mapped_name) + if device_id is not None: + break + else: + logger.error('Failed to get device ID for "%s"', mapped_name) + return + + name = get_xkb_symbols_name(group_key) + path = f"/usr/share/X11/xkb/symbols/{name}" + + if not os.path.exists(path): + logger.debug('Symbols "%s" doen\'t exist, skipping setxkbmap', path) + return + + logger.info("Applying xkb configuration") + + # XkbBadKeyboard: wrong -device id + device_id = get_device_id(mapped_name) + if device_id is None: + return + + cmd = [ + "setxkbmap", + "-keycodes", + "key-mapper-keycodes", + "-symbols", + name, + "-device", + str(device_id), + ] + logger.debug('Running "%s"', " ".join(cmd)) + # TODO disable Popen for setxkbmap in tests + subprocess.Popen(cmd) diff --git a/keymapper/injection/context.py b/keymapper/injection/context.py index 5752664e..ce4cc681 100644 --- a/keymapper/injection/context.py +++ b/keymapper/injection/context.py @@ -22,6 +22,8 @@ """Stores injection-process wide information.""" +from evdev.ecodes import EV_KEY + from keymapper.logger import logger from keymapper.injection.macros.parse import parse, is_this_a_macro from keymapper.system_mapping import system_mapping @@ -129,9 +131,9 @@ class Context: if is_this_a_macro(output): continue - target_code = system_mapping.get(output) + target_code = system_mapping.get_or_allocate(output) if target_code is None: - logger.error('Don\'t know what "%s" is', output) + logger.error('Could not map unknown key "%s"', output) continue for permutation in key.get_permutations(): @@ -156,6 +158,18 @@ class Context: """ return key in self.macros or key in self.key_to_code + def is_written(self, code): + """Check if this code will be written during the injection.""" + # TODO test + if code in self.key_to_code.values(): + return True + + for macro in self.macros.values(): + if code in macro.capabilities[EV_KEY]: + return True + + return False + def maps_joystick(self): """If at least one of the joysticks will serve a special purpose.""" return (self.left_purpose, self.right_purpose) != (NONE, NONE) diff --git a/keymapper/injection/injector.py b/keymapper/injection/injector.py index cc966bf2..7eba00e6 100644 --- a/keymapper/injection/injector.py +++ b/keymapper/injection/injector.py @@ -23,6 +23,7 @@ import asyncio +import traceback import time import multiprocessing @@ -33,6 +34,7 @@ from keymapper.logger import logger from keymapper.groups import classify, GAMEPAD from keymapper.mapping import DISABLE_CODE from keymapper.injection.context import Context +from keymapper.injection.xkb import generate_xkb_config from keymapper.injection.numlock import set_numlock, is_numlock_on, ensure_numlock from keymapper.injection.consumer_control import ConsumerControl @@ -380,6 +382,14 @@ class Injector(multiprocessing.Process): events=self._construct_capabilities(GAMEPAD in self.group.types), ) + if self.mapping.get("generate_xkb_config"): + try: + generate_xkb_config(self.context, self.group.key) # TODO test + except Exception as error: + # since this is optional, catch all exceptions and ignore them + logger.error("generate_xkb_config failed: %s", error) + logger.debug("".join(traceback.format_tb(error.__traceback__)).strip()) + for source in sources: # certain capabilities can have side effects apparently. with an # EV_ABS capability, EV_REL won't move the mouse pointer anymore. diff --git a/keymapper/injection/macros/macro.py b/keymapper/injection/macros/macro.py index 56dc0fc7..04b6a69b 100644 --- a/keymapper/injection/macros/macro.py +++ b/keymapper/injection/macros/macro.py @@ -374,7 +374,7 @@ class Macro: _type_check(macro, [Macro], "m (modify)", 2) modifier = str(modifier) - code = system_mapping.get(modifier) + code = system_mapping.get_or_allocate(modifier) # TODO test if code is None: raise KeyError(f'Unknown modifier "{modifier}"') @@ -419,7 +419,7 @@ class Macro: _type_check_keyname(symbol) symbol = str(symbol) - code = system_mapping.get(symbol) + code = system_mapping.get_or_allocate(symbol) # TODO test self.capabilities[EV_KEY].add(code) async def task(handler): diff --git a/keymapper/injection/xkb.py b/keymapper/injection/xkb.py new file mode 100644 index 00000000..60046987 --- /dev/null +++ b/keymapper/injection/xkb.py @@ -0,0 +1,172 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# key-mapper - GUI for device specific keyboard mappings +# Copyright (C) 2021 sezanzeb +# +# This file is part of key-mapper. +# +# key-mapper is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# key-mapper is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with key-mapper. If not, see . + + +"""Handles xkb config files. + +This is optional and can be disabled via the configuration. If disabled, +outputting keys that are unknown to the system layout is impossible. + +It is optional because broken xkb configs can crash the X session or screw +up the injection, and in ttys xkb configs don't have any effect. setxkbmap +is hard to work with, and xkb configs are horrible. + +It uses setxkbmap to tell the window manager to do stuff differently for +the injected keycodes. + +workflow: +1. injector preparation sees that "a" maps to "b" +2. check which keycode "b" would usually be + 2.a if not in the system_mapping, this keycode is unknown to the + window manager and therefore cannot be used. To fix that, + find an integer code that is not present in system_mapping yet. + 2.b if in the system_mapping, use the existing int code. +3. in the symbols file map that code to "b" +4. the "key-mapper ... mapped" uinput is created by the daemon. Since the + daemon doesn't know anything about the users X session, the GUI calls + setxkbmap instead with the appropriate path generated by helper functions. + +injection: +1. running injection sees that "a" was clicked on the keyboard +2. the injector knows based on the mapping that this maps to + e.g. 48 and injects it +3. the window manager sees code 48 and writes a "b" into the focused + application, because the xkb config tells it to do so + +now it is possible to map "รถ" on an US keyboard + +Resources: +[1] https://wiki.archlinux.org/index.php/Keyboard_input +[2] http://people.uleth.ca/~daniel.odonnell/Blog/custom-keyboard-in-linuxx11 +[3] https://www.x.org/releases/X11R7.7/doc/xorg-docs/input/XKB-Enhancing.html + +Mapping code 10 to a on device_1 and 10 to shift on device_2 may cause issues +when pressing both at the same time, More information can be found in +readme/history.md. That's why the resulting symbols file should match +the existing keyboard layout as much as possible, so that shift stays on the +code that it would usually be. +""" + + +import os + +from keymapper.logger import logger +from keymapper.paths import touch +from keymapper.system_mapping import system_mapping, XKB_KEYCODE_OFFSET + + +SYMBOLS_TEMPLATE = """default xkb_symbols "key-mapper" { + %s +}; +""" + +LINE_TEMPLATE = "key <%d> { [ %s ] };" + + +def get_xkb_symbols_name(device): + """Get the name that can be used for -symbols argument of setxkbmap.""" + return f"key-mapper/{device}".replace(" ", "_") + + +def generate_symbols_lines(context): + """Generate lines to put in symbols files. + + Returns + ------- + string[] + list of 'key <...> {[...]};' strings + """ + symbols = [] + + # because tricky problems appeared during development, add this to + # have some assertion that it works correctly + used_codes = set() + + for name, code in system_mapping.xmodmap_dict.items(): + # TODO if name is a, how to get modified versions of it (A) + # into the symbols file? + code = int(code) + if not context.is_written(code): + # don't include any codes in the symbols file that are + # not used anyway + continue + + logger.spam('"%s" (%s) from xmodmap is used', name, code) + symbols.append(LINE_TEMPLATE % (code + XKB_KEYCODE_OFFSET, name)) + + assert code not in used_codes + used_codes.add(code) + + # TODO store unknown mappings in the context to clarify that it is + # unique per injection + for name, code in system_mapping.get_unknown_mappings().items(): + logger.spam('"%s" (%s) is allocated', name, code) + symbols.append(LINE_TEMPLATE % (code + XKB_KEYCODE_OFFSET, name)) + + assert code not in used_codes + used_codes.add(code) + + return symbols + + +def generate_xkb_config(context, device): + """Generate the needed config file for apply_xkb_config. + + If it is not needed, it will remove any existing xkb symbols config for + that device. + + Parameters + ---------- + context : Context + device : string + Used to name the file in /usr/share/X11/xkb/symbols/key-mapper. + Existing configs with that name will be overwritten. + As indexed in get_devices + """ + # TODO test + name = get_xkb_symbols_name(device) + path = f"/usr/share/X11/xkb/symbols/{name}" + + # remove the old xkb config. If there is no need to apply it then + # there is no need to keep it. The ui will apply it if it is there. + if os.path.exists(path): + os.remove(path) + + if len(context.macros) == 0 and len(context.key_to_code) == 0: + return None + + if len(system_mapping.get_unknown_mappings()) == 0: + # there is no need to change the layout of the device + return None + + logger.info("Unknown characters found, creating xkb configs") + + symbols = generate_symbols_lines(context) + + if len(symbols) == 0: + logger.error("Failed to populate symbols with anything") + return + + touch(path) + with open(path, "w") as f: + logger.info('Writing xkb symbols "%s"', path) + contents = SYMBOLS_TEMPLATE % "\n ".join(symbols) + logger.spam('"%s":\n%s', path, contents.strip()) + f.write(contents) diff --git a/keymapper/system_mapping.py b/keymapper/system_mapping.py index d1badd25..f814a0c0 100644 --- a/keymapper/system_mapping.py +++ b/keymapper/system_mapping.py @@ -31,6 +31,7 @@ from keymapper.logger import logger from keymapper.mapping import DISABLE_NAME, DISABLE_CODE from keymapper.paths import get_config_path, touch from keymapper.utils import is_service +from keymapper.valid_symbols import VALID_XKB_SYMBOLS # xkb uses keycodes that are 8 higher than those from evdev @@ -47,6 +48,13 @@ class SystemMapping: self._mapping = None self._xmodmap = {} self._case_insensitive_mapping = {} + self._allocated_unknowns = {} # int to str # TODO test + self.xmodmap_dict = {} + + # this may contain more entries than _mapping, since _mapping + # stores only one code per character, but a keyboard layout can + # have one character mapped to multiple keys. + self._occupied_keycodes = set() # TODO test def __getattribute__(self, key): """To lazy load system_mapping info only when needed. @@ -75,16 +83,21 @@ class SystemMapping: """Get a mapping of all available names to their keycodes.""" logger.debug("Gathering available keycodes") self.clear() - xmodmap_dict = {} try: xmodmap = subprocess.check_output( ["xmodmap", "-pke"], stderr=subprocess.STDOUT ).decode() + xmodmap = xmodmap self._xmodmap = re.findall(r"(\d+) = (.+)\n", xmodmap + "\n") - xmodmap_dict = self._find_legit_mappings() - if len(xmodmap_dict) == 0: + self.xmodmap_dict = self._find_legit_mappings() + + if len(self.xmodmap_dict) == 0: logger.info("`xmodmap -pke` did not yield any symbol") + + for keycode in self.xmodmap_dict.values(): + self._occupied_keycodes.add(keycode) + except (subprocess.CalledProcessError, FileNotFoundError): # might be within a tty logger.info("Optional `xmodmap` command not found. This is not critical.") @@ -97,9 +110,9 @@ class SystemMapping: touch(path) with open(path, "w") as file: logger.debug('Writing "%s"', path) - json.dump(xmodmap_dict, file, indent=4) + json.dump(self.xmodmap_dict, file, indent=4) - for name, code in xmodmap_dict.items(): + for name, code in self.xmodmap_dict.items(): self._set(name, code) for name, ecode in evdev.ecodes.ecodes.items(): @@ -138,12 +151,68 @@ class SystemMapping: return self._mapping.get(name) + def get_or_allocate(self, character): + """Get a code to inject for that character. + If that character does not exist in the systems keyboard layout, + remember it and return a free code for that to use. + Without modifying the keyboard layout of the display server + injecting the returned code won't do anything. + Parameters + ---------- + character : string + For example F24 or odiaeresis + """ + # TODO test + if character is None: + return None + + character = str(character) + + # check if part of the system layout + from_system_layout = self.get(character) + if from_system_layout is not None: + return from_system_layout + + if character not in VALID_XKB_SYMBOLS: + # not something xkb can do stuff with + return None + + # it's not part of the systems keyboard layout yet, allocate instead + for key, code in self._allocated_unknowns.items(): + # check if already asked to allocate before + if key == character: + return code + + for code in range(1, 256): + # find a free keycode in the range of working keycodes + # TODO test that keys like key_zenkakuhankaku from the linux + # headers are ignored when finding free codes. only the xmodmap + # layout is relevant + if code in self._occupied_keycodes: + continue + + self._allocated_unknowns[character] = code + self._occupied_keycodes.add(code) # TODO test that added + logger.debug('Using %s for "%s"', code, character) + return code + + return None + def clear(self): """Remove all mapped keys. Only needed for tests.""" keys = list(self._mapping.keys()) for key in keys: del self._mapping[key] + def get_unknown_mappings(self): + """Return a mapping of unknown characters to codes. + For example, odiaeresis is unknown on US keyboards. The code + in that case is just any code that was not used by the systems + keyboard layout. + """ + # TODO test + return self._allocated_unknowns + def get_name(self, code): """Get the first matching name for the code.""" for entry in self._xmodmap: diff --git a/keymapper/valid_symbols.py b/keymapper/valid_symbols.py new file mode 100644 index 00000000..74d688c9 --- /dev/null +++ b/keymapper/valid_symbols.py @@ -0,0 +1,1510 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# key-mapper - GUI for device specific keyboard mappings +# Copyright (C) 2021 sezanzeb +# +# This file is part of key-mapper. +# +# key-mapper is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# key-mapper is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with key-mapper. If not, see . + + +"""A set of all human readable symbols xkb understands. + +They are case sensitive. +""" + +# https://gist.github.com/sezanzeb/1236917f509f13d30010c98c9fa8389f +VALID_XKB_SYMBOLS = { + "Cyrillic_ve", + "parenleft", + "SunVideoLowerBrightness", + "KP_6", + "Q", + "uparrow", + "Cyrillic_yu", + "Sinh_ca", + "Cyrillic_YA", + "Thai_ru", + "dead_belowdot", + "XF86Keyboard", + "zacute", + "Greek_beta", + "Greek_PSI", + "Sinh_cha", + "XF86AudioMicMute", + "Shift_Lock", + "onesixth", + "Kana_Lock", + "Cyrillic_zhe_descender", + "Arabic_ddal", + "iacute", + "Greek_kappa", + "W", + "Ooblique", + "dead_stroke", + "Sinh_oo", + "dollar", + "Arabic_comma", + "hebrew_yod", + "V", + "egrave", + "hpDeleteLine", + "Undo", + "Armenian_tyun", + "Armenian_exclam", + "Arabic_keheh", + "Greek_omega", + "includedin", + "partialderivative", + "Arabic_ha", + "Otilde", + "Sinh_ai", + "Georgian_tar", + "Serbian_TSHE", + "Thai_saraaimaimalai", + "Eabovedot", + "F25", + "downarrow", + "Arabic_tehmarbuta", + "doubledagger", + "Armenian_NU", + "kana_RE", + "w", + "Sinh_ng", + "XF86Terminal", + "kana_fullstop", + "Ukrainin_ie", + "Sinh_pha", + "dead_belowmacron", + "Arabic_beh", + "Ugrave", + "plusminus", + "thorn", + "nobreakspace", + "seveneighths", + "Arabic_rreh", + "XF86LaunchA", + "oe", + "nabla", + "Cyrillic_che", + "XF86AudioLowerVolume", + "kana_O", + "uprightcorner", + "KP_Equal", + "scedilla", + "tslash", + "bar", + "Armenian_ligature_ew", + "Byelorussian_SHORTU", + "Scircumflex", + "Num_Lock", + "kana_NO", + "Georgian_in", + "KP_Add", + "braceleft", + "SunFA_Tilde", + "Cyrillic_u_straight", + "Sinh_la", + "F24", + "Ukrainian_ghe_with_upturn", + "Greek_upsilon", + "XF86AddFavorite", + "y", + "Georgian_chin", + "ellipsis", + "leftarrow", + "asciicircum", + "Greek_UPSILON", + "Armenian_apostrophe", + "Georgian_en", + "XF86Spell", + "L6", + "ograve", + "approxeq", + "kana_RU", + "Cyrillic_JE", + "XF86Launch5", + "Cyrillic_EF", + "hebrew_mem", + "Arabic_waw", + "eth", + "Cyrillic_ghe", + "Georgian_gan", + "kana_yo", + "KP_7", + "hebrew_bet", + "Ukrainian_GHE_WITH_UPTURN", + "seconds", + "bott", + "Greek_theta", + "Thai_thothahan", + "Sinh_tha", + "h", + "Armenian_tsa", + "dead_abovedot", + "Arabic_heh_goal", + "XF86LaunchD", + "logicalor", + "Armenian_ben", + "KP_Begin", + "Gabovedot", + "Armenian_SHA", + "Arabic_meem", + "dead_currency", + "Cancel", + "fabovedot", + "Cyrillic_be", + "C", + "XF86Cut", + "q", + "Uacute", + "Cyrillic_LJE", + "thinspace", + "Thai_chochoe", + "Thai_sarauu", + "XF86ModeLock", + "Armenian_VEV", + "Cyrillic_KA_vertstroke", + "XF86Messenger", + "Sinh_fa", + "Greek_ZETA", + "Cyrillic_i_macron", + "Agrave", + "Cyrillic_nje", + "SunFA_Circum", + "Scaron", + "Sinh_ya", + "XF86TouchpadOff", + "Cyrillic_el", + "Scedilla", + "Armenian_DZA", + "rcaron", + "fiveeighths", + "Romaji", + "Sinh_ba", + "Sinh_ri", + "Cyrillic_schwa", + "EuroSign", + "SunPaste", + "diamond", + "XF86WWAN", + "Thai_lekhok", + "Cyrillic_zhe", + "Cyrillic_KA", + "F34", + "Cyrillic_yeru", + "Thai_totao", + "SunFA_Cedilla", + "XF86Reply", + "F31", + "Aogonek", + "kana_SA", + "Armenian_TCHE", + "hebrew_kaph", + "XF86RotationLockToggle", + "Sinh_dhha", + "KP_2", + "Ccircumflex", + "s", + "Cyrillic_SHA", + "Greek_EPSILON", + "XF86Search", + "Ukrainian_yi", + "Escape", + "uacute", + "Armenian_lyun", + "XF86Tools", + "cabovedot", + "ocircumflex", + "Ohorn", + "kana_WO", + "equal", + "Arabic_lam", + "Thai_soso", + "F3", + "Sinh_ka", + "Thai_sarao", + "dead_belowring", + "F33", + "braille_dot_6", + "Thai_saraam", + "Arabic_hamza_above", + "Lcaron", + "Farsi_1", + "Georgian_an", + "tabovedot", + "omacron", + "m", + "logicaland", + "u", + "Multi_key", + "Sinh_na", + "F28", + "hebrew_he", + "SunOpen", + "exclam", + "XF86Support", + "kana_MA", + "dead_doublegrave", + "SeeNkeyinSAXlayout", + "Thai_ngongu", + "Cyrillic_SOFTSIGN", + "Thai_yoying", + "dead_doubleacute", + "T", + "Cyrillic_io", + "Group1", + "Armenian_zhe", + "Arabic_4", + "prolongedsound", + "L4", + "Sinh_lu2", + "Cyrillic_CHE_descender", + "dead_breve", + "hebrew_zade", + "Amacron", + "hstroke", + "Cyrillic_EL", + "zerosubscript", + "Thai_khokhai", + "parenright", + "Thai_thothong", + "XF86Go", + "numbersign", + "greaterthanequal", + "KP_9", + "Thai_maitaikhu", + "Arabic_0", + "hebrew_finalpe", + "Armenian_to", + "Arabic_1", + "ampersand", + "THORN", + "Arabic_ain", + "Eth", + "XF86Meeting", + "Sinh_au", + "Arabic_3", + "j", + "F26", + "Sinh_rii", + "Thai_saraa", + "L9", + "Arabic_percent", + "Home", + "Farsi_6", + "Sinh_u", + "backslash", + "Greek_nu", + "Cyrillic_i", + "XF86ScrollUp", + "jcircumflex", + "cent", + "Cyrillic_shcha", + "umacron", + "Omacron", + "Armenian_VYUN", + "Thai_sosala", + "Cyrillic_es", + "Arabic_alef", + "6", + "KP_Tab", + "KP_Insert", + "Armenian_ra", + "eogonek", + "Eacute", + "leftdoublequotemark", + "Eisu_toggle", + "division", + "Arabic_damma", + "schwa", + "similarequal", + "XF86MonBrightnessCycle", + "Armenian_JE", + "Thai_maitho", + "Thai_thonangmontho", + "grave", + "Greek_GAMMA", + "Return", + "Thai_leksi", + "Cyrillic_HARDSIGN", + "Arabic_question_mark", + "Armenian_LYUN", + "kana_MI", + "B", + "identical", + "dead_invertedbreve", + "Sinh_i2", + "diaeresis", + "Tcedilla", + "lowrightcorner", + "XF86WakeUp", + "F23", + "E", + "Sinh_ttha", + "Pabovedot", + "Greek_IOTA", + "Nosymbol", + "sacute", + "Cyrillic_E", + "Mode_switch", + "hebrew_dalet", + "Yacute", + "asterisk", + "Armenian_ken", + "Thai_dodek", + "kana_TE", + "lstroke", + "Armenian_KE", + "XF86_Ungrab", + "SeeNkey", + "Thai_saraue", + "Thai_maiyamok", + "hpSystem", + "Arabic_dammatan", + "Greek_lambda", + "Touroku", + "Cyrillic_u", + "L7", + "U", + "downtack", + "Arabic_yeh_baree", + "oacute", + "XF86DOS", + "Georgian_don", + "Georgian_on", + "XF86Favorites", + "p", + "braille_dot_4", + "Redo", + "Georgian_phar", + "agrave", + "Caps_Lock", + "KP_3", + "Cyrillic_GHE_bar", + "SunFA_Acute", + "ssharp", + "slash", + "Armenian_HI", + "infinity", + "Thai_sarauee", + "XF86Paste", + "Thai_lakkhangyao", + "Armenian_INI", + "XF86UserPB", + "eightsuperior", + "ibreve", + "XF86Reload", + "XF86MenuPB", + "Armenian_tso", + "Sinh_aee", + "Greek_gamma", + "Armenian_accent", + "udoubleacute", + "L1", + "vertbar", + "kana_MU", + "XF86LightBulb", + "Acircumflex", + "Sinh_ru2", + "Dstroke", + "Armenian_yech", + "n", + "Sinh_luu", + "2", + "Sinh_e", + "Ecircumflex", + "dead_dasia", + "singlelowquotemark", + "overbar", + "Arabic_ra", + "Menu", + "Arabic_yeh", + "Mabovedot", + "XF86New", + "Georgian_har", + "Sinh_luu2", + "SunAudioMute", + "L8", + "hebrew_qoph", + "upstile", + "Cyrillic_SHCHA", + "KP_F3", + "union", + "EuroS", + "Cyrillic_ZE", + "trademark", + "XF86MyComputer", + "Arabic_gaf", + "hebrew_aleph", + "SunCopy", + "ae", + "Sinh_ai2", + "Armenian_men", + "ISO_Next_Group", + "lowleftcorner", + "Zabovedot", + "Rcedilla", + "dagger", + "Arabic_khah", + "Armenian_PYUR", + "Sinh_ddha", + "Sinh_ma", + "F", + "F19", + "Thai_nonen", + "kana_TA", + "guillemotleft", + "eng", + "Ibreve", + "Cyrillic_HA_descender", + "Pause", + "XF86Launch0", + "Arabic_tteh", + "hebrew_ayin", + "kana_RA", + "Cyrillic_U", + "Sinh_au2", + "dcaron", + "Georgian_jhan", + "onethird", + "Gcedilla", + "Thai_maiek", + "Sinh_o", + "XF86ZoomIn", + "kana_KU", + "kana_A", + "XF86Launch9", + "Sinh_mba", + "Arabic_hamzaonyeh", + "dead_iota", + "Ukrainian_I", + "dead_grave", + "Cyrillic_GHE", + "trigraph_C_H", + "Fabovedot", + "Sinh_jnya", + "Thai_saraae", + "Cyrillic_TSE", + "Cyrillic_SHORTI", + "Armenian_CHA", + "Cyrillic_ghe_bar", + "Sinh_nna", + "Arabic_theh", + "Cyrillic_ef", + "Farsi_8", + "KP_4", + "scaron", + "dead_comma", + "kana_NI", + "c", + "8", + "Arabic_sukun", + "dead_belowcomma", + "Armenian_khe", + "degree", + "Greek_MU", + "ncaron", + "Georgian_zhar", + "acircumflex", + "F35", + "colon", + "hpInsertLine", + "Imacron", + "jot", + "babovedot", + "Sinh_aa", + "ISO_Left_Tab", + "XF86MonBrightnessUp", + "KP_Subtract", + "Cyrillic_tse", + "KP_0", + "Utilde", + "Cyrillic_hardsign", + "Thai_saraaa", + "malesymbol", + "kana_SE", + "Arabic_9", + "zerosuperior", + "acute", + "Arabic_kasra", + "Armenian_e", + "Farsi_3", + "crossinglines", + "Armenian_za", + "OE", + "k", + "Igrave", + "Armenian_pyur", + "kana_HO", + "dead_circumflex", + "onesuperior", + "XF86AudioPrev", + "Ukrainian_YI", + "Sinh_ssha", + "KP_Multiply", + "Arabic_7", + "4", + "mu", + "yacute", + "N", + "Sinh_bha", + "Georgian_qar", + "Armenian_tche", + "Tslash", + "enfilledcircbullet", + "none", + "XF86TaskPane", + "kana_NU", + "Cyrillic_U_macron", + "Udiaeresis", + "Ntilde", + "Armenian_FE", + "idotless", + "Cyrillic_ze", + "nacute", + "hebrew_nun", + "Sinh_ee", + "emacron", + "Thai_sarau", + "hpClearLine", + "Georgian_un", + "Greek_OMEGA", + "XF86Finance", + "braille_dot_7", + "rightcaret", + "Cyrillic_pe", + "gcaron", + "Armenian_YECH", + "Cyrillic_dzhe", + "kana_comma", + "XF86PowerOff", + "Armenian_RA", + "5", + "XF86iTouch", + "quote", + "Macedonia_dse", + "semivoicedsound", + "SunStop", + "macron", + "XF86Music", + "gabovedot", + "oslash", + "Armenian_TYUN", + "Sinh_e2", + "Ukrainian_ie", + "Sinh_jha", + "XF86News", + "ISO_Level5_Shift", + "z", + "J", + "dead_horn", + "Greek_iota", + "Arabic_heh", + "Ncedilla", + "function", + "Dabovedot", + "Cyrillic_em", + "Georgian_nar", + "noSymbol", + "idiaeresis", + "Cyrillic_softsign", + "Cyrillic_che_vertstroke", + "Armenian_SE", + "XF86ScrollDown", + "hebrew_resh", + "hebrew_pe", + "Kcedilla", + "udiaeresis", + "paragraph", + "kana_o", + "r", + "aogonek", + "utilde", + "Cyrillic_en_descender", + "XF86MenuKB", + "Cyrillic_u_straight_bar", + "XF86Launch3", + "XF86Market", + "hebrew_lamed", + "kana_YU", + "Armenian_AT", + "Thai_thanthakhat", + "Armenian_TSO", + "Control_R", + "Muhenkan", + "XF86LaunchF", + "Arabic_superscript_alef", + "XF86User1KB", + "KP_Decimal", + "Sinh_pa", + "Serbian_tshe", + "Sinh_ruu2", + "Oacute", + "odoubleacute", + "Arabic_semicolon", + "doubleacute", + "Greek_finalsmallsigma", + "XF86FullScreen", + "abreve", + "apLineDel", + "Super_R", + "Arabic_tatweel", + "SunAgain", + "XF86Stop", + "periodcentered", + "Cyrillic_ER", + "Ukrainian_i", + "VoidSymbol", + "Arabic_veh", + "numerosign", + "Armenian_cha", + "at", + "Armenian_GHAT", + "Aogonekl", + "ugrave", + "F8", + "racute", + "hebrew_shin", + "XF86Back", + "Egrave", + "Sinh_ng2", + "SunFront", + "Thai_leksam", + "Cyrillic_ES", + "kana_tsu", + "F17", + "ISO_Level5_Latch", + "XF86Start", + "Armenian_dza", + "SCHWA", + "Armenian_da", + "Arabic_madonalef", + "hebrew_waw", + "Thai_hohip", + "Thai_fofan", + "Tcaron", + "onequarter", + "cedilla", + "Arabic_feh", + "Cyrillic_DZHE", + "amacron", + "Greek_rho", + "Cyrillic_KA_descender", + "notelementof", + "Zenkaku_Hankaku", + "K", + "XF86Phone", + "Thai_sarai", + "kana_ME", + "9", + "F6", + "F13", + "Cyrillic_a", + "Armenian_vo", + "Cyrillic_EN", + "Greek_SIGMA", + "Shift_R", + "heart", + "XF86Shop", + "kana_SHI", + "Armenian_hyphen", + "Sinh_ndha", + "Nacute", + "kana_conjunctive", + "hcircumflex", + "Cyrillic_DE", + "Find", + "integral", + "f", + "leftsinglequotemark", + "XF86Send", + "uring", + "SunSys_Req", + "Sinh_u2", + "Print", + "hebrew_gimel", + "notequal", + "kana_HA", + "Sinh_nya", + "Itilde", + "kana_yu", + "XF86TouchpadOn", + "Sinh_ae", + "Rcaron", + "Udoubleacute", + "b", + "kana_SO", + "L10", + "ccedilla", + "aacute", + "Ograve", + "hebrew_samech", + "Sinh_gha", + "Y", + "Thai_lekha", + "less", + "SunFA_Grave", + "SunAudioLowerVolume", + "Cyrillic_ha", + "Sinh_nja", + "Arabic_sad", + "iogonek", + "Adiaeresis", + "Greek_delta", + "Cyrillic_shha", + "hpInsertChar", + "rightanglebracket", + "Georgian_zen", + "Georgian_can", + "Kanji", + "ecaron", + "SunCut", + "Greek_KAPPA", + "kana_i", + "ydiaeresis", + "Georgian_khar", + "foursuperior", + "emptyset", + "Sinh_kha", + "kana_KO", + "Arabic_heh_doachashmee", + "Ocircumflex", + "ucircumflex", + "squareroot", + "Cyrillic_che_descender", + "kcedilla", + "enopencircbullet", + "Hiragana", + "Arabic_fullstop", + "braille_dot_9", + "XF86Mail", + "KP_1", + "XF86Clear", + "Hangul", + "Sinh_ja", + "breve", + "threeeighths", + "Armenian_ghat", + "Armenian_TO", + "Thai_thothan", + "Greek_sigma", + "Thai_maihanakat", + "ISO_Level3_Lock", + "Arabic_noon_ghunna", + "Armenian_O", + "KP_Enter", + "kana_U", + "Ydiaeresis", + "Cyrillic_O", + "greater", + "quotedbl", + "Georgian_xan", + "Thai_sarae", + "kana_KA", + "Alt_R", + "threequarters", + "G", + "Armenian_re", + "XF86RFKill", + "g", + "Sinh_thha", + "Aring", + "Cyrillic_A", + "Thai_topatak", + "leftradical", + "ubreve", + "Gbreve", + "Georgian_shin", + "Ecaron", + "Cyrillic_U_straight", + "Thai_lekkao", + "masculine", + "XF86Community", + "rcedilla", + "Thai_kokai", + "Cyrillic_EN_descender", + "Sinh_ha", + "XF86Sleep", + "topt", + "Iogonek", + "Thai_khokhon", + "Greek_eta", + "guilsinglright", + "Thai_nonu", + "upleftcorner", + "Z", + "Cyrillic_ha_descender", + "currency", + "XF86RotationKB", + "Hstroke", + "XF86Travel", + "XF86Display", + "XF86ContrastAdjust", + "Georgian_he", + "Greek_alpha", + "bracketright", + "Iacute", + "Greek_DELTA", + "XF86_Next_VMode", + "Farsi_0", + "Super_L", + "Greek_OMICRON", + "ISO_Group_Latch", + "Arabic_fathatan", + "Sinh_sha", + "Georgian_las", + "XF86LaunchB", + "Armenian_o", + "dead_diaeresis", + "SeeBkey", + "Cyrillic_I_macron", + "Cyrillic_ZHE", + "Macedonia_DSE", + "Sinh_oo2", + "F5", + "hebrew_chet", + "Georgian_kan", + "ncedilla", + "Uring", + "kana_u", + "dstroke", + "trigraph_c_h", + "Greek_tau", + "Georgian_ban", + "Arabic_hamzaunderalef", + "i", + "Thai_sosua", + "kana_E", + "fivesuperior", + "gcircumflex", + "Hyper_L", + "abovedot", + "Thai_chochang", + "minutes", + "kana_KI", + "Armenian_TSA", + "NoSymbol", + "dead_abovering", + "XF86KbdBrightnessUp", + "sixsuperior", + "Sinh_uu2", + "Help", + "radical", + "Armenian_vev", + "kana_I", + "uhorn", + "kana_YO", + "Cyrillic_TE", + "XF86WWW", + "Pointer_EnableKeys", + "Arabic_tah", + "gbreve", + "Katakana", + "Serbian_DJE", + "A", + "Cyrillic_ie", + "ogonek", + "XF86_Prev_VMode", + "Greek_chi", + "Cyrillic_VE", + "Cyrillic_o_bar", + "XF86Close", + "1", + "quoteright", + "Arabic_hamza", + "Henkan_Mode", + "KP_Suptract", + "Idiaeresis", + "Georgian_par", + "ISO_Level3_Shift", + "XF86VendorHome", + "F9", + "Hiragana_Katakana", + "ifonlyif", + "Armenian_DA", + "SeeBkeyinSAXlayout", + "Farsi_9", + "lcedilla", + "v", + "questiondown", + "Macedonia_kje", + "dead_tilde", + "Armenian_MEN", + "P", + "Cyrillic_sha", + "scircumflex", + "femalesymbol", + "copyright", + "kana_HI", + "kana_TO", + "KP_8", + "kana_ya", + "7", + "Sinh_a", + "emdash", + "Farsi_2", + "XF86Excel", + "Arabic_fatha", + "yen", + "kana_WA", + "Cyrillic_ka_vertstroke", + "XF86Bluetooth", + "eabovedot", + "kana_MO", + "DongSign", + "Dcaron", + "Delete", + "Control_L", + "Sabovedot", + "SunUndo", + "group1", + "Sinh_dha", + "XF86HotLinks", + "ordfeminine", + "Greek_epsilon", + "percent", + "KP_Home", + "Arabic_5", + "rightsinglequotemark", + "Umacron", + "kana_TSU", + "guillemotright", + "lacute", + "F32", + "Arabic_maddaonalef", + "Georgian_rae", + "Cyrillic_er", + "Greek_PI", + "Odoubleacute", + "Thai_leksong", + "Georgian_jil", + "F30", + "XF86Launch4", + "Byelorussian_shortu", + "kana_NA", + "Sinh_i", + "Sinh_h2", + "XF86Pictures", + "ISO_Level5_Lock", + "XF86KbdBrightnessDown", + "XF86Eject", + "Georgian_we", + "brokenbar", + "XF86ScreenSaver", + "Armenian_ini", + "KP_Prior", + "hpDeleteChar", + "Arabic_kaf", + "o", + "XF86Calendar", + "Cyrillic_SCHWA", + "F18", + "F29", + "ooblique", + "Greek_phi", + "Armenian_ayb", + "Arabic_ghain", + "Armenian_fe", + "Armenian_sha", + "F15", + "Thai_lekchet", + "elementof", + "intersection", + "Thai_loling", + "Jcircumflex", + "Georgian_tan", + "Ncaron", + "kana_a", + "Eisu_Shift", + "comma", + "permille", + "Cyrillic_CHE_vertstroke", + "Armenian_at", + "Uogonek", + "Thai_leksun", + "Sacute", + "minus", + "Cyrillic_PE", + "kana_middledot", + "XF86LaunchE", + "braille_dot_10", + "Armenian_AYB", + "doublequote", + "XF86KbdLightOnOff", + "dead_abovecomma", + "XF86History", + "SunVideoRaiseBrightness", + "Arabic_noon", + "imacron", + "a", + "Cyrillic_U_straight_bar", + "F16", + "dead_belowcircumflex", + "Thai_maitri", + "Armenian_full_stop", + "Icircumflex", + "Ccaron", + "Cyrillic_ZHE_descender", + "any", + "odiaeresis", + "XF86Hibernate", + "sabovedot", + "Arabic_seen", + "Lcedilla", + "Prior", + "SunFA_Diaeresis", + "Armenian_nu", + "XF86AudioRewind", + "Babovedot", + "XF86AudioPlay", + "X", + "KP_Up", + "I", + "lessthanequal", + "XF86AudioNext", + "R", + "Cyrillic_CHE", + "Greek_LAMDA", + "Cyrillic_HA", + "horizconnector", + "KP_End", + "End", + "approximate", + "Arabic_kasratan", + "caret", + "F22", + "kana_N", + "hebrew_finalnun", + "dead_ogonek", + "Arabic_zah", + "doublelowquotemark", + "Greek_PHI", + "dead_caron", + "XF86MonBrightnessDown", + "ediaeresis", + "Thai_sorusi", + "XF86ToDoList", + "F1", + "XF86OfficeHome", + "period", + "Select", + "ISO_First_Group", + "Armenian_je", + "Greek_CHI", + "otilde", + "Sinh_ee2", + "Thai_leknung", + "Georgian_hie", + "KP_Left", + "KP_Separator", + "F21", + "question", + "trigraph_C_h", + "Armenian_VO", + "onehalf", + "Georgian_ghan", + "club", + "Georgian_cil", + "Arabic_hamza_below", + "XF86HomePage", + "Arabic_alefmaksura", + "ohorn", + "twosuperior", + "Greek_pi", + "guilsinglleft", + "KP_Divide", + "Cyrillic_de", + "F4", + "Sinh_ra", + "rightarrow", + "ninesuperior", + "XF86Launch8", + "adiaeresis", + "endash", + "Greek_lamda", + "Linefeed", + "Armenian_ho", + "XF86AudioForward", + "dead_greek", + "Armenian_RE", + "XF86AudioRaiseVolume", + "XF86RotationPB", + "pabovedot", + "Cyrillic_NJE", + "Right", + "Thai_wowaen", + "Gcaron", + "wacute", + "t", + "Thai_thothung", + "XF86OpenURL", + "leftanglebracket", + "voicedsound", + "icircumflex", + "XF86Q", + "XF86Documents", + "Zacute", + "KP_F4", + "Cyrillic_e", + "Cyrillic_ya", + "KP_F1", + "Henkan", + "Armenian_ZA", + "caron", + "Next", + "Ubreve", + "underscore", + "0", + "aring", + "Racute", + "XF86Video", + "ISO_Last_Group", + "Hangul_Hanja", + "Thai_lekpaet", + "kana_e", + "XF86Book", + "Cyrillic_SHHA", + "Armenian_pe", + "Serbian_dje", + "Arabic_2", + "Armenian_GIM", + "Massyo", + "Greek_TAU", + "XF86ApplicationLeft", + "Oslash", + "Arabic_hamzaonalef", + "leftt", + "XF86User2KB", + "Arabic_jeem", + "itilde", + "kana_FU", + "Sinh_lla", + "L", + "F14", + "XF86Standby", + "Clear", + "Up", + "Arabic_6", + "dead_psili", + "Georgian_san", + "Meta_L", + "kana_KE", + "Arabic_madda_above", + "S", + "Sinh_kunddaliya", + "XF86Away", + "XF86Battery", + "Thai_rorua", + "bracketleft", + "cacute", + "Cacute", + "XF86Xfer", + "F20", + "XF86ApplicationRight", + "multiply", + "Armenian_separation_mark", + "dead_macron", + "Macedonia_gje", + "leftcaret", + "L5", + "ISO_Prev_Group", + "braille_dot_1", + "Arabic_thal", + "sterling", + "Cyrillic_je", + "Armenian_se", + "Georgian_man", + "braceright", + "XF86Forward", + "dead_longsolidusoverlay", + "Cyrillic_o", + "Thai_saraaimaimuan", + "digraph_Ch", + "XF86Game", + "KP_Down", + "XF86_ClearGrab", + "Macedonia_KJE", + "Farsi_5", + "Thai_moma", + "Cyrillic_ka", + "hebrew_taw", + "F11", + "Thai_thophuthao", + "Greek_XI", + "igrave", + "plus", + "l", + "Greek_BETA", + "Alt_L", + "Armenian_KHE", + "gcedilla", + "zabovedot", + "Cyrillic_I", + "Thai_phophan", + "Arabic_shadda", + "kana_SU", + "Thai_chochan", + "Greek_ETA", + "Thai_khokhuat", + "uogonek", + "Armenian_HO", + "XF86AudioPreset", + "XF86Suspend", + "Break", + "Georgian_vin", + "D", + "Thai_baht", + "Arabic_dad", + "Armenian_BEN", + "Atilde", + "Greek_xi", + "F10", + "XF86TouchpadToggle", + "Thai_honokhuk", + "kana_openingbracket", + "XF86AudioMute", + "Uhorn", + "question_mark", + "Greek_mu", + "Georgian_char", + "rightdoublequotemark", + "kana_HE", + "Georgian_hae", + "KP_Next", + "Thai_choching", + "Cyrillic_en", + "XF86SplitScreen", + "XF86AudioPause", + "F27", + "Macedonia_GJE", + "ETH", + "eacute", + "partialderivate", + "XF86RotateWindows", + "XF86Launch2", + "F2", + "threesubscript", + "digraph_ch", + "ezh", + "Lacute", + "Armenian_KEN", + "XF86WebCam", + "Armenian_E", + "Sinh_ii", + "Cyrillic_YU", + "Sinh_tta", + "variation", + "O", + "Armenian_gim", + "Aacute", + "Arabic_jeh", + "Tabovedot", + "hyphen", + "XF86Launch6", + "Sinh_uu", + "hebrew_zain", + "braille_dot_5", + "Greek_THETA", + "F12", + "Cyrillic_lje", + "Iabovedot", + "dead_belowbreve", + "3", + "Arabic_dal", + "Thai_yoyak", + "Greek_omicron", + "mabovedot", + "XF86Save", + "XF86ScrollClick", + "kana_CHI", + "Cyrillic_shorti", + "hebrew_finalmem", + "XF86BrightnessAdjust", + "Thai_lochula", + "Sinh_lu", + "Armenian_vyun", + "Greek_psi", + "Zcaron", + "Hyper_R", + "kana_NE", + "XF86AudioStop", + "lcaron", + "quoteleft", + "Arabic_hamzaonwaw", + "rightt", + "Cyrillic_te", + "twosubscript", + "Ukrainian_IE", + "atilde", + "registered", + "Ccedilla", + "L3", + "Sinh_aa2", + "Thai_khorakhang", + "x", + "Eogonek", + "KP_Delete", + "XF86BackForward", + "Tab", + "Sinh_aee2", + "XF86Open", + "Cabovedot", + "Gcircumflex", + "Execute", + "KP_Right", + "Thai_maichattawa", + "Cyrillic_u_macron", + "Greek_zeta", + "Farsi_yeh", + "KP_5", + "e", + "Thai_nikhahit", + "Greek_NU", + "braille_dot_3", + "Arabic_sheen", + "ccircumflex", + "Thai_khokhwai", + "Cyrillic_ka_descender", + "dead_cedilla", + "XF86Copy", + "dabovedot", + "Down", + "XF86Launch1", + "XF86Explorer", + "Thai_bobaimai", + "H", + "Thai_phinthu", + "apostrophe", + "Cyrillic_YERU", + "L2", + "hebrew_finalzade", + "Arabic_zain", + "XF86LogOff", + "M", + "Armenian_hi", + "Sinh_ae2", + "Sinh_o2", + "Shift_L", + "Sinh_ndda", + "SunProps", + "voidsymbol", + "Farsi_4", + "XF86AudioRecord", + "Cyrillic_O_bar", + "Insert", + "Greek_LAMBDA", + "ENG", + "Scroll_Lock", + "XF86LaunchC", + "ecircumflex", + "XF86MailForward", + "BackSpace", + "Arabic_hah", + "Sinh_al", + "SunFind", + "Arabic_8", + "section", + "Lstroke", + "Armenian_ZHE", + "braille_dot_8", + "Thai_lu", + "XF86WLAN", + "Sinh_sa", + "semicolon", + "Georgian_fi", + "Arabic_teh", + "Thai_phophung", + "Cyrillic_EM", + "therefore", + "XF86Word", + "Cyrillic_BE", + "Farsi_7", + "Thai_popla", + "Armenian_PE", + "zcaron", + "Meta_R", + "horizlinescan5", + "hebrew_tet", + "dead_acute", + "XF86ZoomOut", + "Armenian_question", + "Odiaeresis", + "KP_F2", + "braille_dot_2", + "Cyrillic_IO", + "Arabic_tcheh", + "Armenian_ke", + "kra", + "sevensuperior", + "includes", + "Thai_oang", + "XF86Calculator", + "tcaron", + "Georgian_hoe", + "onesubscript", + "kana_RO", + "Cyrillic_IE", + "threesuperior", + "d", + "Greek_ALPHA", + "Greek_horizbar", + "XF86Launch7", + "AE", + "Thorn", + "Thai_paiyannoi", + "dead_hook", + "F7", + "Sinh_va", + "EZH", + "Wacute", + "Ucircumflex", + "Thai_phosamphao", + "downstile", + "Sinh_nga", + "Arabic_peh", + "Hcircumflex", + "NewSheqelSign", + "kana_YA", + "ccaron", + "Sinh_ga", + "kana_closingbracket", + "asciitilde", + "notsign", + "XF86AudioMedia", + "Left", + "Abreve", + "underbar", + "Sinh_ii2", + "Arabic_qaf", + "ISO_Level3_Latch", + "SunVideoDegauss", + "Ediaeresis", + "space", + "Sinh_dda", + "ntilde", + "Thai_fofa", + "XF86CD", + "exclamdown", + "oneeighth", + "Emacron", + "kana_RI", + "hebrew_finalkaph", + "digraph_CH", + "tcedilla", + "Thai_saraii", + "SunAudioRaiseVolume", + "Thai_dochada", + "Greek_RHO", +} diff --git a/readme/coverage.svg b/readme/coverage.svg index 3f856008..c6421901 100644 --- a/readme/coverage.svg +++ b/readme/coverage.svg @@ -17,7 +17,7 @@ coverage - 93% - 93% + 91% + 91% \ No newline at end of file diff --git a/readme/development.md b/readme/development.md index 4d5c5601..209c2553 100644 --- a/readme/development.md +++ b/readme/development.md @@ -142,7 +142,7 @@ just need to be commited. **Example system startup** 1. systemd loads `key-mapper.service` on boot -2. on login, `key-mapper-autoload.desktop` is executed, which has knowledge +2. on login, `key-mapper-autoload.desktop` is executed, which has knowledge of the current user und doesn't run as root 2.1 it sends the users config directory to the service 2.2 it makes the service stop all ongoing injectings @@ -191,7 +191,7 @@ Is the device a gamepad? Does the GUI show joystick configurations? - if yes, no: adjust `is_gamepad` to loosen up the constraints - if no, yes: adjust `is_gamepad` to tighten up the constraints -Try to do it in such a way that other devices won't break. Also see +Try to do it in such a way that other devices won't break. Also see readme/capabilities.md **It won't offer mapping a button** diff --git a/readme/pylint.svg b/readme/pylint.svg index b69f5a33..f9412338 100644 --- a/readme/pylint.svg +++ b/readme/pylint.svg @@ -17,7 +17,7 @@ pylint - 9.56 - 9.56 + 9.48 + 9.48 \ No newline at end of file diff --git a/setup.py b/setup.py index 7b4e351a..ec627609 100644 --- a/setup.py +++ b/setup.py @@ -113,6 +113,7 @@ setup( ("/usr/share/key-mapper/", glob.glob("data/*")), ("/usr/share/applications/", ["data/key-mapper.desktop"]), ("/usr/share/polkit-1/actions/", ["data/key-mapper.policy"]), + ("/usr/share/X11/xkb/keycodes/", ["data/key-mapper-keycodes"]), ("/usr/lib/systemd/system", ["data/key-mapper.service"]), ("/etc/dbus-1/system.d/", ["data/keymapper.Control.conf"]), ("/etc/xdg/autostart/", ["data/key-mapper-autoload.desktop"]),