summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rwxr-xr-xtools/xkbcli-scaffold-new-layout.py326
1 files changed, 0 insertions, 326 deletions
diff --git a/tools/xkbcli-scaffold-new-layout.py b/tools/xkbcli-scaffold-new-layout.py
deleted file mode 100755
index a837b95..0000000
--- a/tools/xkbcli-scaffold-new-layout.py
+++ /dev/null
@@ -1,326 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright © 2020 Red Hat, Inc.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice (including the next
-# paragraph) shall be included in all copies or substantial portions of the
-# Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-
-
-# This script creates the necessary scaffolding to create a custom keyboard
-# layout or option. It does not actually configure anything, it merely creates
-# the required directory structure and file scaffolding for the key
-# configurations to be added by the user.
-#
-
-import argparse
-import logging
-import os
-import re
-import sys
-
-from pathlib import Path
-from textwrap import dedent
-
-
-# Default values are set by meson but to make this usable directly within the
-# git source tree, use some sensible default values and return those where the
-# meson define hasn't been replaced.
-def default_value(key):
- defaults = {
- 'extrapath': ('@XKBEXTRAPATH@', '/etc/xkb'),
- 'rules': ('@DEFAULT_XKB_RULES@', 'evdev'),
- }
-
- mesondefault, default = defaults[key]
- if mesondefault.startswith('@') and mesondefault.endswith('@'):
- return default
- else:
- return mesondefault
-
-
-logging.basicConfig(level=logging.DEBUG)
-logger = logging.getLogger('xkbcli')
-logger.setLevel(logging.INFO)
-
-
-def create_directory_structure(basedir):
- basedir.mkdir(exist_ok=True)
- # Note: we skip geometry
- for d in ['compat', 'keycodes', 'rules', 'symbols', 'types']:
- (basedir / d).mkdir(exist_ok=True)
-
-
-def create_rules_template(base_path, ruleset, layout, option):
- rules = base_path / 'rules' / ruleset
- if rules.exists():
- logger.warning(f'Rules file {rules} already exists, skipping')
- return
-
- with open(rules, 'w') as rulesfile:
- header = dedent(f'''\
- // generated by xkbcli scaffold-new-layout
-
- // Note: no rules file entries are required for for a custom layout
-
- ''')
- rulesfile.write(header)
-
- if option:
- group, section = option
- option_template = dedent(f'''\
- // This section maps XKB option "{group}:{section}" to the '{section}' section in the
- // 'symbols/{group}' file.
- //
- ! option = symbols
- {group}:{section} = +{group}({section})
-
- ''')
- rulesfile.write(option_template)
-
- footer = dedent(f'''\
- // Include the system '{ruleset}' file
- ! include %S/{ruleset}
- ''')
- rulesfile.write(footer)
-
-
-def create_symbols_template(basedir, layout_variant, option):
- if not layout_variant and not option:
- logger.info('No layout or option given, skipping symbols templates')
- return
-
- layout, variant = layout_variant
- layout_file = Path(basedir) / 'symbols' / layout
- if layout_file.exists():
- logger.warning(f'Symbols file {layout_file} already exists, skipping')
- layout_fd = None
- else:
- layout_fd = open(layout_file, 'w')
- layout_fd.write('// generated by xkbcli scaffold-new-sources\n\n')
-
- group, section = option
- options_file = Path(basedir) / 'symbols' / group
-
- # Cater for a potential "custom(variant)" layout and "custom:foo" option,
- # i.e. where both layout and options use the same symbols file
- if options_file == layout_file:
- options_fd = layout_fd
- elif options_file.exists():
- logger.warning(f'File {options_file} already exists, skipping')
- options_fd = None
- else:
- options_fd = open(options_file, 'w')
- options_fd.write('// generated by xkbcli scaffold\n\n')
-
- if layout_fd:
- if variant is None:
- default = 'default '
- variant = 'basic'
- include = ''
- else:
- default = ''
- include = f'include "{layout}(basic)"'
-
- logger.debug(f'Writing "{layout}({variant})" layout template to {layout_file}')
- layout_fd.write(dedent(f'''\
- {default}partial alphanumeric_keys modifier_keys
- xkb_symbols "{variant}" {{
- name[Group1]= "{variant} ({layout})";
-
- {include}
-
- // Example:
- // key <CAPS> {{ [ Escape ] }};
- }};
- '''))
-
- if options_fd:
- logger.debug(f'Writing "{section}" options template to {options_file}')
- options_fd.write(dedent(f'''\
- partial modifier_keys
- xkb_symbols "{section}" {{
- // Example:
- // key <CAPS> {{ [ Escape ] }};
- }};
- '''))
-
- layout_fd.close()
- options_fd.close()
-
-
-def create_registry_template(basedir, ruleset, layout_variant, option):
- xmlpath = Path(basedir) / 'rules' / f'{ruleset}.xml'
- if xmlpath.exists():
- logger.warning(f'XML file {xmlpath} already exists, skipping')
- return
-
- with open(xmlpath, 'w') as xmlfile:
- logger.debug(f'Writing XML file {xmlfile}')
-
- if layout_variant:
- layout, variant = layout_variant
- if variant:
- variant_template = f'''
- <variantList>
- <variant>
- <configItem>
- <name>{variant}</name>
- <shortDescription>{variant}</shortDescription>
- <description>{layout} ({variant})</description>
- </configItem>
- </variant>
- </variantList>
- '''
- else:
- variant_template = ''
- layout_template = f'''
- <layoutList>
- <layout>
- <configItem>
- <name>{layout}</name>
- <shortDescription>{layout}</shortDescription>
- <description>{layout}</description>
- </configItem>
- {variant_template}
- </layout>
- </layoutList>'''
- else:
- layout_template = ''
-
- if option:
- group, section = option
- option_template = f'''
- <optionList>
- <group allowMultipleSelection="true">
- <configItem>
- <name>{group}</name>
- <description>{group} options</description>
- </configItem>
- <option>
- <configItem>
- <name>{group}:{section}</name>
- <description>{group}:{section} description</description>
- </configItem>
- </option>
- </group>
- </optionList>
- '''
- else:
- option_template = ''
-
- template = dedent(f'''\
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE xkbConfigRegistry SYSTEM "xkb.dtd">
- <!-- generated by xkbcli scaffold-new-layout -->
- <xkbConfigRegistry version="1.1">
- {layout_template}
- {option_template}
- </xkbConfigRegistry>''')
- xmlfile.write(template)
-
-
-def main():
- epilog = dedent('''\
- This tool creates the directory structure and template files for
- a custom XKB layout and/or option.
-
- Use the --option and --layout arguments to specify the template names to
- use. These use default values, unset those with the empty string.
-
- Examples:
-
- xkbcli scaffold --layout mylayout --option ''
- xkbcli scaffold --layout '' --option 'custom:foo'
- xkbcli scaffold --system --layout 'us(myvariant)' --option 'custom:foo'
-
- This is a simple tool. If files already exist, the scaffolding skips
- over that file and the result may not be correct.
- ''')
-
- parser = argparse.ArgumentParser(
- description='Create scaffolding to configure custom keymaps',
- formatter_class=argparse.RawDescriptionHelpFormatter,
- epilog=epilog)
- parser.add_argument('-v', '--verbose', action='store_true', default=False,
- help='Enable verbose debugging output')
- group = parser.add_mutually_exclusive_group()
- group.add_argument('--system', action='store_true', default=False,
- help=f'Create scaffolding in {default_value("extrapath")}')
- group.add_argument('--user', action='store_true', default=False,
- help='Create scaffolding in $XDG_CONFIG_HOME/xkb')
- parser.add_argument('--rules', type=str, default=default_value("rules"),
- help=f'Ruleset name (default: "{default_value("rules")}")')
- parser.add_argument('--layout', type=str, default='us(myvariant)',
- help='Add scaffolding for a new layout or variant (default: "us(myvariant)")')
- parser.add_argument('--option', type=str, default='custom:myoption',
- help='Add scaffolding for a new option (default: "custom:myoption")')
-
- args = parser.parse_args()
-
- if args.verbose:
- logger.setLevel(logging.DEBUG)
-
- if args.system:
- basedir = Path(default_value('extrapath'))
- else:
- xdgdir = os.getenv('XDG_CONFIG_HOME')
- if not xdgdir:
- home = os.getenv('HOME')
- if not home:
- logger.error('Unable to resolve base directory from $XDG_CONFIG_HOME or $HOME')
- sys.exit(1)
- xdgdir = Path(home) / '.config'
- basedir = Path(xdgdir) / 'xkb'
-
- if args.option:
- try:
- group, section = args.option.split(':')
- option = (group, section)
- except ValueError:
- logger.error('Option must be specified as "group:name"')
- sys.exit(1)
- else:
- option = None
-
- if args.layout:
- # match either "us" or "us(intl)" style layouts
- # [(] should be \( but flake8 complains about that
- match = re.fullmatch('([a-z]+)([(][a-z]+[)])?', args.layout, flags=re.ASCII)
- l, v = match.group(1, 2)
- if v:
- v = v.strip('()') # regex above includes ( )
- layout_variant = l, v
- else:
- layout_variant = None
-
- try:
- create_directory_structure(basedir)
- create_rules_template(basedir, args.rules, layout_variant, option)
- create_symbols_template(basedir, layout_variant, option)
- create_registry_template(basedir, args.rules, layout_variant, option)
- except PermissionError as e:
- logger.critical(e)
- sys.exit(1)
-
- print(f'XKB scaffolding for layout "{args.layout}" and option "{args.option}" is now in place.')
- print(f'Edit the files in {basedir} to create the actual key mapping.')
-
-
-if __name__ == '__main__':
- main()