diff --git a/meson.build b/meson.build index e9233b4..c23e92e 100644 --- a/meson.build +++ b/meson.build @@ -652,13 +652,6 @@ You can disable the Wayland xkbcli programs with -Denable-wayland=false.''') install_man('tools/xkbcli-list.1') endif - config_scaffold = configuration_data() - config_scaffold.set('XKBEXTRAPATH', XKBCONFIGEXTRAPATH) - config_scaffold.set('DEFAULT_XKB_RULES', get_option('default-rules')) - configure_file(input: 'tools/xkbcli-scaffold-new-layout.py', - output: 'xkbcli-scaffold-new-layout', - configuration: config_scaffold) - test('tool-option-parsing', find_program('test/tool-option-parsing.py'), env: test_env, 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 {{ [ 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 {{ [ 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''' - - - - {variant} - {variant} - {layout} ({variant}) - - - - ''' - else: - variant_template = '' - layout_template = f''' - - - - {layout} - {layout} - {layout} - - {variant_template} - - ''' - else: - layout_template = '' - - if option: - group, section = option - option_template = f''' - - - - {group} - {group} options - - - - - ''' - else: - option_template = '' - - template = dedent(f'''\ - - - - - {layout_template} - {option_template} - ''') - 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()