configs

configs
git clone https://git.prime8.dev/configs.git
Log | Files | Refs | README

toshy_config.py (322763B)


      1 # -*- coding: utf-8 -*-
      2 __version__ = '20260101'
      3 ###############################################################################
      4 ############################   Welcome to Toshy!   ############################
      5 ###
      6 ###  This is a highly customized fork of the config file that powers
      7 ###  Kinto.sh, by Ben Reaves
      8 ###      (https://kinto.sh)
      9 ###
     10 ###  All credit for the basis of this config goes to Ben Reaves.
     11 ###      (https://github.com/rbreaves/kinto/)
     12 ###
     13 ###  Much assistance was provided by Josh Goebel, the developer of the
     14 ###  `xkeysnail` fork `keyszer`, which is now forked into `xwaykeyz` to
     15 ###  provide support for some (most?) Wayland environments.
     16 ###      (http://github.com/joshgoebel/keyszer)
     17 ###
     18 ###############################################################################
     19 
     20 import re
     21 import os
     22 import sys
     23 import time
     24 import shutil
     25 import asyncio
     26 import inspect
     27 import subprocess
     28 
     29 from subprocess import DEVNULL
     30 from typing import Any, Callable, Optional, Union, List, Dict, Tuple
     31 
     32 from xwaykeyz.config_api import *
     33 from xwaykeyz.lib.key_context import KeyContext
     34 from xwaykeyz.lib.logger import debug, error
     35 from xwaykeyz.models.modifier import Modifier
     36 
     37 
     38 ###################################################################################################
     39 ###  SLICE_MARK_START: keymapper_api  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
     40 
     41 # Keymapper-specific config settings - REMOVE OR SET TO DEFAULTS FOR DISTRIBUTION
     42 dump_diagnostics_key(Key.F15)   # default key: F15
     43 emergency_eject_key(Key.F16)    # default key: F16
     44 
     45 timeouts(
     46     multipurpose        = 1,        # default: 1 sec
     47     suspend             = 1,        # default: 1 sec, try 0.1 sec for touchpads/trackpads
     48 )
     49 
     50 # Delays often needed for Wayland and/or virtual machines or slow systems
     51 throttle_delays(
     52     key_pre_delay_ms    = 8,      # default: 0 ms, range: 0-150 ms, suggested: 1-50 ms
     53     key_post_delay_ms   = 12,      # default: 0 ms, range: 0-150 ms, suggested: 1-100 ms
     54 )
     55 
     56 devices_api(
     57     # Only the specified devices will be "grabbed" and watched for during
     58     # device connections/disconnections.
     59     only_devices = [
     60         # 'Example Disconnected Keyboard',
     61         # 'Example Connected Keyboard',
     62     ]
     63 )
     64 
     65 ###########################################################
     66 # Use this ONLY if you want near zero CPU usage for held,
     67 # repeating keys. This will bypass the new repeating keys
     68 # cache mechanism that reduces CPU usage for repeats to
     69 # very low levels (but not zero), in xwaykeyz>=1.11.0.
     70 #
     71 # This override will BREAK shortcuts like Emacs-style
     72 # cursor movement, which will not be able to repeat.
     73 #
     74 # A warning will appear in verbose debug logging.
     75 ###########################################################
     76 # ignore_repeating_keys(True)
     77 
     78 
     79 ###  SLICE_MARK_END: keymapper_api  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
     80 ###################################################################################################
     81 
     82 
     83 
     84 ###################################################################################################
     85 # How to add an alias to an existing modifier definition (VERY EXPERIMENTAL!!!)
     86 # Some of these are disabled because I'm not sure they would apply correctly
     87 # to all of the different keyboard types. But virtualized Command and Left Ctrl,
     88 # Left Option aliases should make sense for all keyboard types.
     89 # WARNING: It is not advisable to start using these in actual input
     90 # combos in keymaps. These aliases may be removed in the future.
     91 try:
     92     ###########################################################################
     93     # # Virtualized (left) Control key (in Terminals)
     94 
     95     LC_aliases: List[str]           = Modifier.L_CONTROL.aliases
     96     LC_keys: List[Key]              = Modifier.L_CONTROL.keys
     97     # use slice assignment instead of insert()
     98     LC_aliases[:0]                  = ['VLCtrl', 'VirtLCtrl']
     99     # debug(f"{LC_aliases             = }")
    100     # debug(f"{LC_keys                = }")
    101 
    102     ###########################################################################
    103     # # Virtualized (left/right) Control keys (in GUI apps)
    104 
    105     # L_META_aliases: List[str]       = Modifier.L_META.aliases
    106     # L_META_keys: List[Key]          = Modifier.L_META.keys
    107     # # use slice assignment instead of insert()
    108     # L_META_aliases[:0]              = ['VLCtrl', 'Virt_LCtrl']
    109     # debug(f"{L_META_aliases         = }")
    110     # debug(f"{L_META_keys            = }")
    111 
    112     # R_META_aliases: List[str]       = Modifier.R_META.aliases
    113     # R_META_keys: List[Key]          = Modifier.R_META.keys
    114     # # use slice assignment instead of insert()
    115     # R_META_aliases[:0]              = ['VRCtrl', 'Virt_RCtrl']
    116     # debug(f"{R_META_aliases         = }")
    117     # debug(f"{R_META_keys            = }")
    118 
    119     ###########################################################################
    120     # Virtualized Option/Alt keys (left/right)
    121 
    122     LA_aliases: List[str]           = Modifier.L_ALT.aliases
    123     LA_keys: List[Key]              = Modifier.L_ALT.keys
    124     # use slice assignment instead of insert()
    125     LA_aliases[:0]                  = ['VLOpt', 'VirtLOpt']
    126     # debug(f"{LA_aliases             = }")
    127     # debug(f"{LA_keys                = }")
    128 
    129     # RA_aliases: List[str]       = Modifier.R_ALT.aliases
    130     # RA_keys: List[Key]          = Modifier.R_ALT.keys
    131     # # use slice assignment instead of insert()
    132     # RA_aliases[:0]              = ['VROpt', 'VirtROpt']
    133     # debug(f"{RA_aliases         = }")
    134     # debug(f"{RA_keys            = }")
    135 
    136     ###########################################################################
    137     # Virtualized Command key (all keyboard types)
    138 
    139     R_CONTROL_aliases: List[str]    = Modifier.R_CONTROL.aliases
    140     R_CONTROL_keys: List[Key]       = Modifier.R_CONTROL.keys
    141     # use slice assignment instead of insert()
    142     R_CONTROL_aliases[:0]           = ['VCmd', 'VirtCmd']
    143     # debug(f"{R_CONTROL_aliases      = }")
    144     # debug(f"{R_CONTROL_keys         = }")
    145 
    146 except AttributeError as e:
    147     error(f"Problem adding alias to modifier:\n\t{e}")
    148 ###################################################################################################
    149 
    150 
    151 
    152 ###################################################################################################
    153 # Some important setup work necessary to make custom preferences,
    154 # notifications and Synergy log monitoring work.
    155 home_dir = os.path.expanduser('~')
    156 icons_dir = os.path.join(home_dir, '.local', 'share', 'icons')
    157 
    158 # get the path of this file (not the main module loading it)
    159 config_globals = inspect.stack()[1][0].f_globals
    160 current_folder_path = os.path.dirname(os.path.abspath(config_globals["__config__"]))
    161 sys.path.insert(0, current_folder_path)
    162 
    163 # Local imports after path has been set
    164 from toshy_common.env_context import EnvironmentInfo
    165 from toshy_common.machine_context import get_machine_id_hash
    166 from toshy_common.notification_manager import NotificationManager
    167 from toshy_common.settings_class import Settings
    168 
    169 assets_path         = os.path.join(current_folder_path, 'assets')
    170 icon_file_active    = os.path.join(assets_path, "toshy_app_icon_rainbow.svg")
    171 icon_file_grayscale = os.path.join(assets_path, "toshy_app_icon_rainbow_inverse_grayscale.svg")
    172 icon_file_inverse   = os.path.join(assets_path, "toshy_app_icon_rainbow_inverse.svg")
    173 
    174 # Toshy config file
    175 TOSHY_PART      = 'config'   # CUSTOMIZE TO SPECIFIC TOSHY COMPONENT! (gui, tray, config)
    176 TOSHY_PART_NAME = 'Toshy Config file'
    177 APP_VERSION     = __version__
    178 
    179 # Settings object used to tweak preferences "live" between gui, tray and config.
    180 cnfg = Settings(current_folder_path)
    181 cnfg.watch_database()           # activate watchdog observer on the sqlite3 db file
    182 cnfg.watch_shared_devices()     # Look for network KVM apps and watch logs (on server only)
    183 # debug("")
    184 debug(cnfg, ctx="CG")
    185 
    186 
    187 
    188 #############################  ENVIRONMENT  ##############################
    189 ###                                                                    ###
    190 ###                                                                    ###
    191 ###      ███████ ███    ██ ██    ██ ██ ██████   ██████  ███    ██      ###
    192 ###      ██      ████   ██ ██    ██ ██ ██   ██ ██    ██ ████   ██      ###
    193 ###      █████   ██ ██  ██ ██    ██ ██ ██████  ██    ██ ██ ██  ██      ###
    194 ###      ██      ██  ██ ██  ██  ██  ██ ██   ██ ██    ██ ██  ██ ██      ###
    195 ###      ███████ ██   ████   ████   ██ ██   ██  ██████  ██   ████      ###
    196 ###                                                                    ###
    197 ###                                                                    ###
    198 ##########################################################################
    199 # Set up some useful environment variables
    200 
    201 ###################################################################################################
    202 ###  SLICE_MARK_START: env_overrides  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
    203 
    204 # MANUALLY set any environment information if the auto-identification isn't working:
    205 OVERRIDE_DISTRO_ID              = None
    206 OVERRIDE_DISTRO_VER             = None
    207 OVERRIDE_VARIANT_ID             = None
    208 OVERRIDE_SESSION_TYPE           = None
    209 OVERRIDE_DESKTOP_ENV            = None
    210 OVERRIDE_DE_MAJ_VER             = None
    211 OVERRIDE_WINDOW_MGR             = None
    212 
    213 wlroots_compositors             = [
    214     # Comma-separated list of Wayland desktop environments or window managers
    215     # that should try to use the 'wlroots' window context provider. Use the
    216     # 'WINDOW_MGR' name that appears when running `toshy-env`, or 'DESKTOP_ENV'
    217     # if the window manager name is not identified.
    218     # 'obscurewm',
    219     # 'unknown-wm',
    220 
    221 ]
    222 
    223 ###  SLICE_MARK_END: env_overrides  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
    224 ###################################################################################################
    225 
    226 # Leave all of this alone! Don't try to override values here.
    227 DISTRO_ID                       = None
    228 DISTRO_VER                      = None
    229 VARIANT_ID                      = None
    230 SESSION_TYPE                    = None
    231 DESKTOP_ENV                     = None
    232 DE_MAJ_VER                      = None
    233 WINDOW_MGR                      = None
    234 
    235 env_ctxt_getter = EnvironmentInfo()
    236 env_ctxt: Dict[str, str] = env_ctxt_getter.get_env_info()
    237 
    238 DISTRO_ID       = locals().get('OVERRIDE_DISTRO_ID')    or env_ctxt.get('DISTRO_ID',    'keymissing')
    239 DISTRO_VER      = locals().get('OVERRIDE_DISTRO_VER')   or env_ctxt.get('DISTRO_VER',   'keymissing')
    240 VARIANT_ID      = locals().get('OVERRIDE_VARIANT_ID')   or env_ctxt.get('VARIANT_ID',   'keymissing')
    241 SESSION_TYPE    = locals().get('OVERRIDE_SESSION_TYPE') or env_ctxt.get('SESSION_TYPE', 'keymissing')
    242 DESKTOP_ENV     = locals().get('OVERRIDE_DESKTOP_ENV')  or env_ctxt.get('DESKTOP_ENV',  'keymissing')
    243 DE_MAJ_VER      = locals().get('OVERRIDE_DE_MAJ_VER')   or env_ctxt.get('DE_MAJ_VER',   'keymissing')
    244 WINDOW_MGR      = locals().get('OVERRIDE_WINDOW_MGR')   or env_ctxt.get('WINDOW_MGR',   'keymissing')
    245 
    246 # debug("")
    247 debug(  f'Toshy config sees this environment:'
    248         f'\n\t{DISTRO_ID        = }'
    249         f'\n\t{DISTRO_VER       = }'
    250         f'\n\t{VARIANT_ID       = }'
    251         f'\n\t{SESSION_TYPE     = }'
    252         f'\n\t{DESKTOP_ENV      = }'
    253         f'\n\t{DE_MAJ_VER       = }'
    254         f'\n\t{WINDOW_MGR       = }', ctx="CG")
    255 
    256 
    257 # NOTE: List kind of stabilized, so moved all into keymapper wlroots window context method
    258 # during the transition from using "desktop environment" to using "window manager".
    259 known_wlroots_compositors = [
    260     # 'hyprland',
    261     # 'labwc',
    262     # 'miracle-wm',
    263     # 'miriway',
    264     # 'miriway-shell',    # actual process name for 'miriway' compositor?
    265     # 'niri',
    266     # 'qtile',
    267     # 'river',
    268     # 'sway',
    269     # 'wayfire',
    270 ]
    271 
    272 # Make sure the 'wlroots_compositors' list variable exists before checking it.
    273 # Older config files won't have it in the 'env_overrides' slice.
    274 wlroots_compositors = locals().get('wlroots_compositors', [])
    275 
    276 all_wlroots_compositors = known_wlroots_compositors + wlroots_compositors
    277 
    278 # Direct the keymapper to try to use `wlroots` window context for
    279 # all DEs/WMs in user list, if list is not empty.
    280 if wlroots_compositors and WINDOW_MGR in wlroots_compositors:
    281     debug(f"Will use 'wlroots' context provider for '{WINDOW_MGR}' WM", ctx="CG")
    282     debug("File an issue on GitHub repo if this works for your WM.", ctx="CG")
    283     _wl_compositor = 'wlroots'
    284 elif wlroots_compositors and DESKTOP_ENV in wlroots_compositors:
    285     debug(f"Will use 'wlroots' context provider for '{DESKTOP_ENV}' DE", ctx="CG")
    286     debug("File an issue on GitHub repo if this works for your DE.", ctx="CG")
    287     _wl_compositor = 'wlroots'
    288 elif WINDOW_MGR in known_wlroots_compositors:
    289     debug(f"WM '{WINDOW_MGR}' is in known 'wlroots' compositor list.", ctx="CG")
    290     _wl_compositor = 'wlroots'
    291 elif DESKTOP_ENV in known_wlroots_compositors:
    292     debug(f"DE '{DESKTOP_ENV}' is in known 'wlroots' compositor list.", ctx="CG")
    293     _wl_compositor = 'wlroots'
    294 # elif (SESSION_TYPE, DESKTOP_ENV) == ('wayland', 'lxqt') and WINDOW_MGR == 'kwin_wayland':
    295 #     # The Toshy KWin script must be installed in the LXQt/KWin environment for this to work!
    296 #     debug(f"DE is LXQt, WM is '{WINDOW_MGR}', using 'kwin_wayland' window context method.", ctx="CG")
    297 #     _desktop_env = 'kde'
    298 # elif (SESSION_TYPE, DESKTOP_ENV) == ('wayland', 'lxqt') and WINDOW_MGR in all_wlroots_compositors:
    299 #     debug(f"DE is LXQt, WM is '{WINDOW_MGR}', using 'wlroots' window context method.", ctx="CG")
    300 #     _desktop_env = 'wlroots'
    301 else:
    302     _wl_compositor = WINDOW_MGR
    303 
    304 try:
    305     # Help the keymapper select the correct window context provider object
    306     environ_api(session_type = SESSION_TYPE, wl_compositor = _wl_compositor) # type: ignore
    307 except NameError:
    308     error(f"The API function 'environ_api' is not defined yet. Wrong keymapper branch?")
    309     pass
    310 
    311 
    312 # Global variable to store the local machine ID at runtime, for machine-specific keymaps.
    313 # Allows syncing a single config file between different machines without overlapping the
    314 # hardware/media key overrides, or any other machine-specific customization.
    315 # Get the ID for each machine with the `toshy-machine-id` command, for use in `if` conditions.
    316 MACHINE_ID = get_machine_id_hash()
    317 
    318 
    319 
    320 #################  VARIABLES  ####################
    321 ###                                            ###
    322 ###                                            ###
    323 ###      ██    ██  █████  ██████  ███████      ###
    324 ###      ██    ██ ██   ██ ██   ██ ██           ###
    325 ###      ██    ██ ███████ ██████  ███████      ###
    326 ###       ██  ██  ██   ██ ██   ██      ██      ###
    327 ###        ████   ██   ██ ██   ██ ███████      ###
    328 ###                                            ###
    329 ###                                            ###
    330 ##################################################
    331 # Establish important global variables here
    332 
    333 
    334 STARTUP_TIMESTAMP = time.time()     # only gets evaluated once for each run of keymapper
    335 
    336 # Variable to hold the keyboard type
    337 KBTYPE = None
    338 
    339 # Short names for the `xwaykeyz/keyszer` string and Unicode processing helper functions
    340 ST = to_US_keystrokes           # was 'to_keystrokes' originally
    341 UC = unicode_keystrokes
    342 ignore_combo = ComboHint.IGNORE
    343 
    344 ###############################################################################
    345 # This is a "trick" to negate the need to put quotes around all the key labels
    346 # inside the "lists of dicts" to be given to the matchProps() function.
    347 # Makes the variables evaluate to equivalent strings inside the dicts.
    348 # Provides for nice syntax highlighting and visual separation of key:value.
    349 clas        = 'clas'        # key label for matchProps() arg to match: wm_class
    350 name        = 'name'        # key label for matchProps() arg to match: wm_name
    351 devn        = 'devn'        # key label for matchProps() arg to match: device_name
    352 not_clas    = 'not_clas'    # key label for matchProps() arg to NEGATIVE match: wm_class
    353 not_name    = 'not_name'    # key label for matchProps() arg to NEGATIVE match: wm_name
    354 not_devn    = 'not_devn'    # key label for matchProps() arg to NEGATIVE match: device_name
    355 numlk       = 'numlk'       # key label for matchProps() arg to match: numlock_on
    356 capslk      = 'capslk'      # key label for matchProps() arg to match: capslock_on
    357 cse         = 'cse'         # key label for matchProps() arg to enable: case sensitivity
    358 lst         = 'lst'         # key label for matchProps() arg to pass in a [list] of {dicts}
    359 dbg         = 'dbg'         # key label for matchProps() arg to set debugging info string
    360 
    361 # global variables for the isDoubleTap() function
    362 tapTime1 = time.time()
    363 tapInterval = 0.24
    364 tapCount = 0
    365 last_dt_combo = None
    366 
    367 # Set this variable to False to disable the alert that appears
    368 # when using Apple logo shortcut (Shift+Option+K)
    369 applelogoalert_enabled = True   # Default: True
    370 
    371 
    372 
    373 
    374 ######################  LISTS  #######################
    375 ###                                                ###
    376 ###                                                ###
    377 ###      ██      ██ ███████ ████████ ███████       ###
    378 ###      ██      ██ ██         ██    ██            ###
    379 ###      ██      ██ ███████    ██    ███████       ###
    380 ###      ██      ██      ██    ██         ██       ###
    381 ###      ███████ ██ ███████    ██    ███████       ###
    382 ###                                                ###
    383 ###                                                ###
    384 ######################################################
    385 
    386 
    387 def toRgxStr(lst_of_str) -> str:
    388     """
    389     Convert a list of strings into single casefolded regex pattern string.
    390     """
    391     def raise_TypeError(): raise TypeError(f"\n\n###  toRgxStr wants a list of strings  ###\n")
    392     if not isinstance(lst_of_str, list): raise_TypeError()
    393     if any([not isinstance(x, str) for x in lst_of_str]): raise_TypeError()
    394     lst_of_str_clean = [str(x).replace('^','').replace('$','') for x in lst_of_str]
    395     return "|".join('^'+x.casefold()+'$' for x in lst_of_str_clean)
    396 
    397 
    398 def negRgx(rgx_str):
    399     """
    400     Convert positive match regex pattern into negative lookahead regex pattern.
    401     """
    402     # remove any ^$
    403     rgx_str_strip = str(rgx_str).replace('^','').replace('$','')
    404     # add back ^$, but only around ENTIRE STRING (ignore any vertical bars/pipes)
    405     rgx_str_add = str('^'+rgx_str_strip+'$')
    406     # convert ^$ to complicated negative lookahead pattern that actually works
    407     neg_rgx_str = str(rgx_str_add).replace('^','^(?:(?!^').replace('$','$).)*$')
    408     return neg_rgx_str
    409 
    410 
    411 def create_list_of_dicts(lst: List[str]):
    412     """
    413     Convert a simple list of class names into a list of dictionaries,
    414     each dictionary containing a single key 'clas' with the regex pattern
    415     for exact match. Strips any leading '^' or trailing '$' from each item
    416     in the list to ensure clean, standardized entries.
    417     """
    418     cleaned_list = [name.strip('^$') for name in lst]  # Remove any leading '^' or trailing '$'
    419     return [{'clas': f"^{item}$"} for item in cleaned_list]
    420 
    421 
    422 terminals = [
    423     "alacritty",
    424     # "com.mitchellh.ghostty",          # Replaced with double-wildcard ghostty pattern
    425     "com.raggesilver.BlackBox",
    426     "com.system76.CosmicTerm",
    427     "contour",
    428     "cutefish-terminal",
    429     "deepin-terminal",
    430     "dev.warp.Warp",
    431     "eterm",
    432     ".*ghostty.*",
    433     # "ghostty",                        # Replaced with double-wildcard ghostty pattern
    434     # "ghostty-debug",                  # Replaced with double-wildcard ghostty pattern
    435     "gnome-terminal",
    436     "gnome-terminal-server",
    437     "guake",
    438     "hyper",
    439     "io.elementary.terminal",
    440     "kinto-gui.py",
    441     "kitty",
    442     "Kgx",
    443     "konsole",
    444     "lxterminal",
    445     "mate-terminal",
    446     "MateTerminal",
    447     "org.codeberg.dnkl.foot.desktop",
    448     "org.gnome.Console",
    449     # "org.gnome.Ptyxis.*",             # Replaced by double-wildcard Ptyxis pattern
    450     "org.gnome.Terminal",
    451     "org.kde.konsole",
    452     "org.kde.yakuake",
    453     "org.wezfurlong.wezterm",
    454     ".*Ptyxis.*",
    455     "roxterm",
    456     "qterminal",
    457     "st",
    458     "sakura",
    459     "station",
    460     "tabby",
    461     "terminator",
    462     "terminology",
    463     "termite",
    464     "Termius",
    465     "tilda",
    466     "tilix",
    467     "urxvt",
    468     "Wave",
    469     "wezterm",
    470     "wezterm-gui",
    471     "xfce4-terminal",
    472     "xterm",
    473     "yakuake",
    474 ]
    475 terminals                       = [x.casefold() for x in terminals]
    476 termStr                         = toRgxStr(terminals)
    477 
    478 # DEPRECATED
    479 # This is only for use with 'remotes_lod', otherwise regex pattern string is used.
    480 terminals_lod = create_list_of_dicts(terminals)
    481 
    482 vscodes = [
    483     "code",
    484     "code - oss",
    485     "code-oss",
    486     "cursor",           # New VSCode variant with A.I.
    487     "vscodium",
    488 ]
    489 vscodes                         = [x.casefold() for x in vscodes]
    490 vscodeStr                       = toRgxStr(vscodes)
    491 
    492 # DEPRECATED: Converted back to simple list
    493 # This is only for use with 'vscodes_and_remotes_lod', otherwise regex pattern string is used.
    494 vscodes_lod = create_list_of_dicts(vscodes)
    495 
    496 sublimes = [
    497     "sublime_text",
    498     "subl",
    499 ]
    500 sublimes                        = [x.casefold() for x in sublimes]
    501 sublimeStr                      = toRgxStr(sublimes)
    502 
    503 JDownloader_lod = [
    504     {clas: "^.*jdownloader.*$"},
    505     {clas: "^java-lang-Thread$", name: "^JDownloader.*$"}   # Happens after auto-update of app
    506 ]
    507 
    508 # Transmission app can have several different app class strings
    509 transmissions = [
    510     'Transmission-gtk',
    511     'Transmission-qt',
    512     'com.transmissionbt.Transmission.*',    # Flatpak has a stupid random app class suffix
    513 ]
    514 transmissions                   = [x.casefold() for x in transmissions]
    515 transmissionStr                 = toRgxStr(transmissions)
    516 
    517 
    518 # Add remote desktop clients & VM software here
    519 # Ideally we'd only exclude the client window,
    520 # but that may not be easily done.
    521 # (Can be done now with `keyszer`, as long as main window has a
    522 # different WM_NAME than client windows. See `remotes_lod` below.)
    523 remotes = [
    524     "Anydesk",
    525     "cs2",
    526     "csgo",
    527     "Gnome-boxes",
    528     "gnome-connections",
    529     "org.gnome.Boxes",
    530     "org.remmina.Remmina",
    531     "Nxplayer.bin",
    532     "remmina",
    533     "qemu-system-.*",
    534     "qemu",
    535     "Spicy",
    536     "Virt-manager",
    537     "VirtualBox",
    538     "VirtualBox Machine",
    539     "xfreerdp",
    540     "Wfica",
    541 ]
    542 remotes = [x.casefold() for x in remotes]
    543 remoteStr = toRgxStr(remotes)
    544 
    545 # DEPRECATED: Converted back to simple list format
    546 # Add remote desktop clients & VM software here
    547 remotes_lod = [
    548     {clas: "^Anydesk$"                       },
    549     {clas: "^Gnome-boxes$"                   },
    550     {clas: "^gnome-connections$"             },
    551     {clas: "^org.remmina.Remmina$",
    552         not_name: "^Remmina Remote Desktop Client$|^Remote Connection Profile$"},
    553     {clas: "^Nxplayer.bin$"                  },
    554     {clas: "^remmina$"                       },
    555     {clas: "^qemu-system-.*$"                },
    556     {clas: "^qemu$"                          },
    557     {clas: "^Spicy$"                         },
    558     {clas: "^Virt-manager$"                  },
    559     {clas: "^VirtualBox$"                    },
    560     {clas: "^VirtualBox Machine$"            },
    561     {clas: "^xfreerdp$"                      },
    562     {clas: "^Wfica$"                         },
    563 ]
    564 
    565 terms_and_remotes_lst = terminals + remotes
    566 terms_and_remotes_Str = toRgxStr(terms_and_remotes_lst)
    567 
    568 # DEPRECATED: Converted back to simple list
    569 terminals_and_remotes_lod = [
    570     {lst: terminals_lod                  },
    571     {lst: remotes_lod                    },
    572 ]
    573 
    574 # DEPRECATED by 'vscodes_and_remotes_lod' "list of dicts" below
    575 # vscodes.extend(remotes)
    576 # vscodeStr_ext = toRgxStr(vscodes)
    577 
    578 vscodes_and_remotes_lst = vscodes + remotes
    579 vscodes_and_remotes_Str = toRgxStr(vscodes_and_remotes_lst)
    580 
    581 # DEPRECATED: Converted back to simple list
    582 vscodes_and_remotes_lod = [
    583     {lst: vscodes_lod                    },
    584     {lst: remotes_lod                    },
    585 ]
    586 
    587 browsers_chrome = [
    588     "Brave-browser",
    589     "Chromium",
    590     "Chromium-browser",
    591     "Falkon",
    592     "Google-chrome",
    593     "Io.github.ungoogled_software.ungoogled_chromium",
    594     "microsoft-edge",
    595     "microsoft-edge-dev",
    596     "org.deepin.browser",
    597     "org.kde.falkon",
    598     ".*ungoogled_chromium",
    599     "Vivaldi.*",
    600 ]
    601 browsers_chrome         = [x.casefold() for x in browsers_chrome]
    602 browsers_chromeStr      = "|".join('^'+x+'$' for x in browsers_chrome)
    603 
    604 browsers_firefox = [
    605     "firedragon",               # Garuda Firefox fork
    606     "Firefox",
    607     "firefox_.*",   # Catch "firefox_firefox" (WTF?), seen on Ubuntu Cinnamon 25.10 in X11 session
    608     "firefox-esr",
    609     "Firefox Developer Edition",
    610     "firefoxdeveloperedition",
    611     "firefox-nightly.*",
    612     "floorp",
    613     "LibreWolf",
    614     "Mullvad Browser",
    615     "Navigator",
    616     "org.mozilla.firefox",
    617     "Waterfox",
    618     "zen-browser",
    619     "zen",
    620     "zen-alpha",
    621     "zen-beta",
    622     "zen-bin",
    623     "zen-twilight",
    624 ]
    625 browsers_firefox        = [x.casefold() for x in browsers_firefox]
    626 browsers_firefoxStr     = "|".join('^'+x+'$' for x in browsers_firefox)
    627 
    628 browsers_all = [
    629     # unpack Chrome and Firefox lists into this general browser list
    630     *browsers_chrome,
    631     *browsers_firefox,
    632     "Discord",
    633     "Epiphany",
    634     "org.gnome.Epiphany",
    635 ]
    636 browsers_all            = [x.casefold() for x in browsers_all]
    637 browsers_allStr         = "|".join('^'+x+'$' for x in browsers_all)
    638 
    639 
    640 # NOTE: Do not be tempted to convert simple app class lists into a "list of dicts"
    641 # If the list contains only app classes, the regex pattern string is much faster.
    642 filemanagers = [
    643     "caja",
    644     "com.system76.CosmicFiles",
    645     "dde-file-manager",
    646     "dolphin",
    647     "io.elementary.files",
    648     "krusader",
    649     "nautilus",
    650     "nemo",
    651     "org.gnome.nautilus",
    652     "org.kde.dolphin",
    653     "org.kde.krusader",
    654     "pcmanfm",
    655     "pcmanfm-qt",
    656     "peony-qt",
    657     "spacefm",
    658     "thunar",
    659 ]
    660 filemanagers = [x.casefold() for x in filemanagers]
    661 filemanagerStr = "|".join('^'+x+'$' for x in filemanagers)
    662 
    663 
    664 ### dialogs_Escape_lod = send these windows the Escape key for Cmd+W
    665 dialogs_Escape_lod = [
    666     {clas: "^Angry.*IP.*Scanner$",
    667         name: "^IP.*address.*details.*$|^Preferences.*$|^Scan.*Statistics.*$|^Edit.*openers.*$"},
    668     # TODO: add or change Atoms class to "pm.mirko.Atoms" if the app gets updated
    669     # TODO: remove "atoms" from "name: " entry patterns if "Shortcuts" dialog gets updated
    670     # Reference: https://github.com/AtomsDevs/Atoms/issues/61
    671     {clas: "^atoms$", name: "^Preferences$|^Shortcuts$|^About$|^atoms$"},
    672     {clas: "^com.github.rafostar.Clapper$", name: "^Preferences$"},
    673     {clas: "^epiphany$|^org.gnome.Epiphany$", name: "^Preferences$"},
    674     {clas: "^gnome-text-editor$|^org.gnome.TextEditor$", name: "^Preferences$"},
    675     {clas: "^io.github.celluloid_player.Celluloid$", name: "^Preferences$"},
    676     {clas: "^konsole$|^org.kde.konsole$", name: "^Configure.*Konsole$|^Edit Profile.*Konsole$"},
    677     {clas: "^krusader$|^org.kde.krusader$", name: "^Properties.*Krusader$"},
    678     {clas: "^.*nautilus$", name: "^.*Properties$|^Preferences$|^Create Archive$|^Rename.*Files$"},
    679     # {clas: "^org.gnome.Ptyxis.*$", name: "^Preferences$"},
    680     {clas: "^.*Ptyxis.*$", name: "^Preferences$"},
    681     {clas: "^org.gnome.Shell.Extensions$"},
    682     {clas: "^org.kde.Dolphin$", name: "^Configure.*Dolphin$|^Properties.*Dolphin$"},
    683     {clas: "^org.kde.falkon$|^Falkon$", name: "^Preferences.*Falkon$"},
    684     {clas: "^org.kde.kdialog$"},
    685     {clas: "^org.kde.KWrite$", name: "^Configure.*KWrite$"},
    686     {clas: "^org.gnome.Software$", not_name: "^Software$"},
    687     {clas: transmissionStr, not_name: "^Transmission$"},
    688     {clas: "^xfce4-terminal$", name: "^Terminal Preferences$"},
    689     {clas: "^zenity$|^qarma$"}
    690 ]
    691 
    692 ### dialogs_CloseWin_lod = send these windows the "Close window" combo for Cmd+W
    693 dialogs_CloseWin_lod = [
    694     {clas: "^Angry.*IP.*Scanner$", name: "^Fetchers.*$|^Edit.*favorites.*$"},
    695     {clas: "^com.mattjakeman.ExtensionManager$|^extension-manager$", not_name: "^Extension Manager$"},
    696     {clas: "^fr.handbrake.ghb$", not_name: "^HandBrake$"},
    697     {clas: "^Gnome-control-center$", not_name: "^Settings$"},
    698     {clas: "^gnome-terminal.*$", name: "^Preferences.*$"},
    699     {clas: "^gnome-terminal-pref.*$", name: "^Preferences.*$"},
    700     {clas: "^org.kde.kate$", name: "^Configure.*Kate$"},
    701     {clas: "^pcloud$"},
    702     {clas: "^systemsettings$", name: "^Download New.*System Settings$"},
    703     {clas: "^Totem$", not_name: "^Videos$"},
    704 ]
    705 
    706 
    707 ###################################################################################################
    708 ###  SLICE_MARK_START: kbtype_override  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
    709 
    710 keyboards_UserCustom_dct = {
    711     # Add your keyboard device here if its type is misidentified.
    712     # Valid types to map device to: Apple, Windows, IBM, Chromebook (case sensitive)
    713     # Example:
    714     'My Keyboard Device Name': 'Apple',
    715 }
    716 
    717 ###  SLICE_MARK_END: kbtype_override  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
    718 ###################################################################################################
    719 
    720 # Create a "UserCustom" keyboard dictionary with casefolded keys
    721 kbds_UserCustom_dct_cf = {k.casefold(): v for k, v in keyboards_UserCustom_dct.items()}
    722 
    723 
    724 # Lists of keyboard device names, to match keyboard type
    725 keyboards_IBM = [
    726     # Add specific IBM-style keyboard device names to this list
    727     'IBM Enhanced (101/102-key) Keyboard',
    728     'IBM Rapid Access Keyboard',
    729     'IBM Space Saver II',
    730     'IBM Model M',
    731     'IBM Model F',
    732 ]
    733 keyboards_Chromebook = [
    734     # Add specific Chromebook keyboard device names to this list
    735     'Google.*Keyboard',
    736 ]
    737 keyboards_Windows = [
    738     # Add specific Windows/PC keyboard device names to this list
    739     'AT Translated Set 2 keyboard',
    740 ]
    741 keyboards_Apple = [
    742     # Add specific Apple/Mac keyboard device names to this list
    743     'HP TouchPad Wireless Keyboard',    # Missing some keys, but Apple type probably best default
    744     'Magic Keyboard with Numeric Keypad',
    745     'Magic Keyboard',
    746     'Mitsumi Electric Apple Extended USB Keyboard',
    747     'MX Keys Mac Keyboard',
    748     'Griffin Technology, Inc. iMate, USB To ADB Adaptor',
    749     '.*USB to ADB Adapt.*'  # ADB device is pretty much guaranteed to be an Apple (or NeXT) keyboard
    750 ]
    751 
    752 kbtype_lists = {
    753     'IBM':          keyboards_IBM,
    754     'Chromebook':   keyboards_Chromebook,
    755     'Windows':      keyboards_Windows,
    756     'Apple':        keyboards_Apple
    757 }
    758 
    759 # List of all known keyboard devices from all lists
    760 all_keyboards       = [kb for kbtype in kbtype_lists.values() for kb in kbtype]
    761 
    762 # keyboard lists compiled to regex objects (replacing spaces with wildcards)
    763 kbds_IBM_rgx        = [re.compile(kb.replace(" ", ".*"), re.I) for kb in keyboards_IBM]
    764 kbds_Chromebook_rgx = [re.compile(kb.replace(" ", ".*"), re.I) for kb in keyboards_Chromebook]
    765 kbds_Windows_rgx    = [re.compile(kb.replace(" ", ".*"), re.I) for kb in keyboards_Windows]
    766 kbds_Apple_rgx      = [re.compile(kb.replace(" ", ".*"), re.I) for kb in keyboards_Apple]
    767 
    768 # Dict mapping keyboard type keywords onto
    769 kbtype_lists_rgx    = {
    770     'IBM':          kbds_IBM_rgx,
    771     'Chromebook':   kbds_Chromebook_rgx,
    772     'Windows':      kbds_Windows_rgx,
    773     'Apple':        kbds_Apple_rgx
    774 }
    775 
    776 # List of all known keyboard devices from all lists
    777 all_kbds_rgx        = re.compile(toRgxStr(all_keyboards), re.I)
    778 
    779 not_win_type_rgx    = re.compile("IBM|Chromebook|Apple", re.I)
    780 
    781 
    782 # Suggested location for customizing lists and variables for use with the "when" conditions.
    783 ###################################################################################################
    784 ###  SLICE_MARK_START: user_custom_lists  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
    785 
    786 
    787 
    788 ###  SLICE_MARK_END: user_custom_lists  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
    789 ###################################################################################################
    790 
    791 
    792 
    793 ###################################  CUSTOM FUNCTIONS  ####################################
    794 ###                                                                                     ###
    795 ###                                                                                     ###
    796 ###      ███████ ██    ██ ███    ██  ██████ ████████ ██  ██████  ███    ██ ███████      ###
    797 ###      ██      ██    ██ ████   ██ ██         ██    ██ ██    ██ ████   ██ ██           ###
    798 ###      █████   ██    ██ ██ ██  ██ ██         ██    ██ ██    ██ ██ ██  ██ ███████      ###
    799 ###      ██      ██    ██ ██  ██ ██ ██         ██    ██ ██    ██ ██  ██ ██      ██      ###
    800 ###      ██       ██████  ██   ████  ██████    ██    ██  ██████  ██   ████ ███████      ###
    801 ###                                                                                     ###
    802 ###                                                                                     ###
    803 ###########################################################################################
    804 
    805 
    806 # Instantiate a useful notification object class instance, to make notifications easier
    807 ntfy = NotificationManager(icon_file_active, title='Toshy Alert (Config)')
    808 
    809 
    810 def isKBtype(kbtype: str, map=None):
    811     # guard against failure to give valid type arg (we don't need to casefold anything with this)
    812     if kbtype not in ['IBM', 'Chromebook', 'Windows', 'Apple']:
    813         raise ValueError(f"Invalid type given to isKBtype() function: '{kbtype}'"
    814                 f'\n\t Valid keyboard types (case sensitive): IBM | Chromebook | Windows | Apple')
    815     def _isKBtype(ctx: KeyContext):
    816         # debug(f"KBTYPE: '{KBTYPE}' | isKBtype check from map: '{map}'")
    817         return kbtype == KBTYPE
    818     return _isKBtype
    819 
    820 
    821 kbtype_cache_dct = {}
    822 
    823 
    824 def getKBtype():
    825     """
    826     ### Get the keyboard type string for the current device
    827 
    828     #### Valid Types
    829 
    830     - IBM | Chromebook | Windows | Apple
    831 
    832     #### Hierarchy of validations:
    833 
    834     - Check if a forced override of keyboard type is applied by user preference.
    835     - Check cache dictionary for device name stored from previous run of function.
    836     - Check if the device name is in the keyboards_UserCustom_dct dictionary.
    837     - Check if the device name matches any keyboard type list.
    838     - Check if any keyboard type string is found in the device name string.
    839     - Check if the device name indicates a "Windows" keyboard by excluding other types.
    840     """
    841 
    842     valid_kbtypes = ['IBM', 'Chromebook', 'Windows', 'Apple']
    843 
    844     def _getKBtype(ctx: KeyContext):
    845         # debug(f"Entering getKBtype with override value: '{cnfg.override_kbtype}'")
    846         global KBTYPE
    847         kbd_dev_name = ctx.device_name
    848 
    849         def log_kbtype(msg, cache_dev):
    850             debug(f"KBTYPE: '{KBTYPE}' | {msg}: '{kbd_dev_name}'")
    851             if cache_dev:
    852                 kbtype_cache_dct[kbd_dev_name] = (KBTYPE, msg)
    853 
    854         # If user wants to override, apply override and return.
    855         # Breaks per-device adaptatation capability while engaged!
    856         if cnfg.override_kbtype in valid_kbtypes:
    857             KBTYPE = cnfg.override_kbtype
    858             log_kbtype(f"WARNING: Override applied! Dev", cache_dev=False)
    859             return
    860 
    861         # Check in the kbtype cache dict for the device
    862         if kbd_dev_name in kbtype_cache_dct:
    863             KBTYPE, cached_msg = kbtype_cache_dct[kbd_dev_name]
    864             log_kbtype(f'(CACHED) {cached_msg}', cache_dev=False)
    865             return
    866 
    867         kbd_dev_name_cf = ctx.device_name.casefold()
    868 
    869         # Check if there is a custom type for the device
    870         custom_kbtype = kbds_UserCustom_dct_cf.get(kbd_dev_name_cf, '')
    871         if custom_kbtype and custom_kbtype in valid_kbtypes:
    872             KBTYPE = custom_kbtype
    873             log_kbtype('Custom type for dev', cache_dev=True)
    874             return
    875 
    876         # Check against the keyboard type lists
    877         for kbtype, regex_lst in kbtype_lists_rgx.items():
    878             for rgx in regex_lst:
    879                 if rgx.search(kbd_dev_name_cf):
    880                     KBTYPE = kbtype
    881                     log_kbtype('Rgx matched on dev', cache_dev=True)
    882                     return
    883 
    884         # Check if any keyboard type string is found in the device name
    885         for kbtype in ['IBM', 'Chromebook', 'Windows', 'Apple']:
    886             if kbtype.casefold() in kbd_dev_name_cf:
    887                 KBTYPE = kbtype
    888                 log_kbtype('Type in dev name', cache_dev=True)
    889                 return
    890 
    891         # Check if the device name indicates a "Windows" keyboard
    892         if ('windows' not in kbd_dev_name_cf
    893             and not not_win_type_rgx.search(kbd_dev_name_cf)
    894             and not all_kbds_rgx.search(kbd_dev_name_cf) ):
    895             KBTYPE = 'Windows'
    896             log_kbtype('Default type for dev', cache_dev=True)
    897             return
    898 
    899         # Default to None if no matching keyboard type is found
    900         KBTYPE = 'unidentified'
    901         error(f"KBTYPE: '{KBTYPE}' | Dev fell through all checks: '{kbd_dev_name}'")
    902 
    903     return _getKBtype  # Return the inner function
    904 
    905 
    906 def isDoubleTap(dt_combo):
    907     """
    908     VERY EXPERIMENTAL!!!
    909 
    910     Simplistic detection of double-tap of a key or combo.
    911 
    912     BLOCKS single-tap function, if used with a single key as the input, but the
    913     'normal' (non-modifier) key of a combo will still be usable when used by
    914     itself as a non-double-tapped key press.
    915 
    916     Example: 'RC-CapsLock' will respond when "Cmd" key (under Toshy remapping)
    917     is held and CapsLock key is double-tapped. Nothing will happen if
    918     Cmd+CapsLock is pressed without double-tapping CapsLock key within the
    919     configured time interval. But the CapsLock key will still work by itself.
    920 
    921     If double-tap input "combo" is just 'CapsLock', the functioning of a single-tap
    922     CapsLock key press will be BLOCKED. Nothing will happen unless the key is
    923     double-tapped within the configured time interval.
    924 
    925     Only cares about the 'real' key in a combo of Mods+key, like in the example
    926     above with 'RC-CapsLock'.
    927 
    928     The proper way to do this would be inside the keymapper, in the async event loop
    929     that deals with input/output functions.
    930     """
    931     def _isDoubleTap():
    932         global tapTime1
    933         global tapInterval
    934         global tapCount
    935         global last_dt_combo
    936         _tapTime = time.time()
    937         # This first "if" block has a logic defect, if a different key in the
    938         # same keymap is also set up to send the same "dt_combo" value.
    939         if tapCount == 1 and last_dt_combo != dt_combo:
    940             debug(f'## isDoubleTap: \n\tDifferent combo: \n\t{last_dt_combo, dt_combo=}')
    941             last_dt_combo = None
    942             tapCount = 0
    943         # 2nd tap beyond time interval? Treat as new double-tap cycle.
    944         if tapCount == 1 and _tapTime - tapTime1 >= tapInterval:
    945             debug(f'## isDoubleTap: \n\tTime diff (too long): \n\t{_tapTime - tapTime1=}')
    946             tapCount = 0
    947         # Try to keep held key from producing repeats of dt_combo.
    948         # If repeat rate very slow or delay very short, this won't work well.
    949         if tapCount == 1 and _tapTime - tapTime1 < 0.07:
    950             debug(f'## isDoubleTap: \n\tTime diff (too short): \n\t{_tapTime - tapTime1=}')
    951             tapCount = 0
    952             return None
    953         # 2nd tap within interval window? Reset cycle & send dt_combo.
    954         if tapCount == 1 and _tapTime - tapTime1 < tapInterval:
    955             debug(f'## isDoubleTap: \n\tTime diff (just right): \n\t{_tapTime - tapTime1=}')
    956             tapCount = 0
    957             tapTime1 = 0.0
    958             return dt_combo
    959         # New cycle? Set count = 1, tapTime1 = now. Send nothing.
    960         if tapCount == 0:
    961             debug(f'## isDoubleTap: \n\tTime diff (1st cycle): \n\t{_tapTime - tapTime1=}')
    962             last_dt_combo = dt_combo
    963             tapCount = 1
    964             tapTime1 = _tapTime
    965             return None
    966     return _isDoubleTap
    967 
    968 
    969 total_matchProps_iterations = 0
    970 MAX_MATCHPROPS_ITERATIONS = 1000
    971 MAX_MATCHPROPS_ITERATIONS_REACHED = False
    972 MATCHPROPS_FULL_DEBUG = False  # Set True to disable short-circuits and enable full debug logging
    973 
    974 
    975 # Correct syntax to reject all positional parameters: put `*,` at beginning
    976 def matchProps(*,
    977     # string parameters (positive matching)
    978     clas: str = None, name: str = None, devn: str = None,
    979     # string parameters (negative matching)
    980     not_clas: str = None, not_name: str = None, not_devn: str = None,
    981     # bool parameters
    982     numlk: bool = None, capslk: bool = None, cse: bool = None,
    983     # list of dicts of parameters (positive)
    984     lst: List[Dict[str, Union[str, bool]]] = None,
    985     # list of dicts of parameters (negative)
    986     not_lst: List[Dict[str, Union[str, bool]]] = None,
    987     dbg: str = None,    # debugging info (such as: which modmap/keymap?)
    988 ) -> Callable[[KeyContext], bool]:
    989     """
    990     ### Match all given properties to current window context.       \n
    991     - Parameters must be _named_, no positional arguments.          \n
    992     - All parameters optional, but at least one must be given.      \n
    993     - Defaults to case insensitive matching of:                     \n
    994         - WM_CLASS, WM_NAME, device_name                            \n
    995     - To negate/invert regex pattern match use:                     \n
    996         - `not_clas` `not_name` `not_devn` params or...             \n
    997         - "^(?:(?!^pattern$).)*$"                                   \n
    998     - To force case insensitive pattern match use:                  \n
    999         - "^(?i:pattern)$" or...                                    \n
   1000         - "^(?i)pattern$"                                           \n
   1001 
   1002     ### Accepted Parameters:                                        \n
   1003     `clas` = WM_CLASS    (regex/string) [xprop WM_CLASS]            \n
   1004     `name` = WM_NAME     (regex/string) [xprop _NET_WM_NAME]        \n
   1005     `devn` = Device Name (regex/string) [xwaykeyz --list-devices]   \n
   1006     `not_clas` = `clas` but inverted, matches when "not"            \n
   1007     `not_name` = `name` but inverted, matches when "not"            \n
   1008     `not_devn` = `devn` but inverted, matches when "not"            \n
   1009     `numlk`    = Num Lock LED state         (bool)                  \n
   1010     `capslk`   = Caps Lock LED state        (bool)                  \n
   1011     `cse`      = Case Sensitive matching    (bool)                  \n
   1012     `lst`      = List of dicts of the above arguments               \n
   1013     `not_lst`  = `lst` but inverted, matches when "not"             \n
   1014     `dbg`      = Debugging info             (string)                \n
   1015 
   1016     ### Negative match parameters:
   1017     - `not_clas`|`not_name`|`not_devn`                              \n
   1018     Parameters take same regex patterns as `clas`|`name`|`devn`     \n
   1019     but result in a True condition only if pattern is NOT found.    \n
   1020     Negative parameters cannot be used together with the normal     \n
   1021     positive matching equivalent parameter in same instance.        \n
   1022 
   1023     ### List of Dicts parameter: `lst`|`not_lst`
   1024     A [list] of {dicts} with each dict containing 1 to 6 of the     \n
   1025     named parameters above, to be processed recursively as args.    \n
   1026     A dict can also contain a single `lst` or `not_lst` argument.   \n
   1027 
   1028     ### Debugging info parameter: `dbg`
   1029     A string that will print as part of logging output. Use to      \n
   1030     help identify origin of logging output.                         \n
   1031     -                                                               \n
   1032     """
   1033     # Reference for successful negative lookahead pattern, and
   1034     # explanation of why it works:
   1035     # https://stackoverflow.com/questions/406230/\
   1036         # regular-expression-to-match-a-line-that-doesnt-contain-a-word
   1037 
   1038     global MAX_MATCHPROPS_ITERATIONS_REACHED
   1039     global total_matchProps_iterations
   1040 
   1041     # Return `False` immediately if screen does not have focus (e.g. Synergy),
   1042     # but only after the guard clauses have had a chance to evaluate on
   1043     # all possible uses of the function that may exist in the config.
   1044     if MAX_MATCHPROPS_ITERATIONS_REACHED and not cnfg.screen_has_focus:
   1045         # return False    # Returning a boolean here causes as exception. Must return a callable!
   1046         return lambda _: False
   1047 
   1048     if total_matchProps_iterations >= MAX_MATCHPROPS_ITERATIONS:
   1049         MAX_MATCHPROPS_ITERATIONS_REACHED = True
   1050         bypass_guard_clauses = True
   1051     else:
   1052         total_matchProps_iterations += 1
   1053         current_timestamp = time.time()
   1054 
   1055         # 'STARTUP_TIMESTAMP' is a global variable, set when config is executed
   1056         time_elapsed = current_timestamp - STARTUP_TIMESTAMP
   1057 
   1058         # Bypass all guard clauses if more than a few seconds have passed since keymapper
   1059         # started and loaded the config file. Inputs never change until keymapper
   1060         # restarts and reloads the config file, so we don't need to keep checking.
   1061         bypass_guard_clauses = time_elapsed > 6
   1062 
   1063     logging_enabled = False
   1064 
   1065     allowed_params  = (clas, name, devn, not_clas, not_name, not_devn,
   1066                         numlk, capslk, cse, lst, not_lst, dbg)
   1067     lst_dct_params  = (clas, name, devn, not_clas, not_name, not_devn,
   1068                         numlk, capslk, cse)
   1069     string_params   = (clas, name, devn, not_clas, not_name, not_devn, dbg)
   1070 
   1071     # This was using up a lot of CPU time, actually. Bad idea.
   1072     # dct_param_strs  = list(inspect.signature(matchProps).parameters.keys())
   1073 
   1074     # Static list of parameter names. Using this instead of `inspect` cuts CPU
   1075     # usage considerably, for reasons I don't yet understand. Apparently the
   1076     # keymapper is actually running the entire function again on each key
   1077     # press and release, rather than just re-evaluating the inner closure.
   1078     dct_param_strs = [
   1079         'clas', 'name', 'devn', 'not_clas', 'not_name', 'not_devn',
   1080         'numlk', 'capslk', 'cse', 'lst', 'not_lst', 'dbg'
   1081     ]
   1082 
   1083     if not MAX_MATCHPROPS_ITERATIONS_REACHED or not bypass_guard_clauses:
   1084         if all([x is None for x in allowed_params]):
   1085             raise ValueError(f"\n\n(EE) matchProps(): Received no valid argument\n")
   1086         if any([x not in (True, False, None) for x in (numlk, capslk, cse)]):
   1087             raise TypeError(f"\n\n(EE) matchProps(): Params 'numlk|capslk|cse' are bools\n")
   1088         if any([x is not None and not isinstance(x, str) for x in string_params]):
   1089             raise TypeError(    f"\n\n(EE) matchProps(): These parameters must be strings:"
   1090                                 f"\n\t'clas|name|devn|not_clas|not_name|not_devn|dbg'\n")
   1091         if clas and not_clas or name and not_name or devn and not_devn or lst and not_lst:
   1092             raise ValueError(   f"\n\n(EE) matchProps(): Do not mix positive and "
   1093                                 f"negative match params for same property\n")
   1094 
   1095     # consolidate positive and negative matching params into new vars
   1096     # only one should be in use at a time (checked above)
   1097     _lst = not_lst if lst is None else lst
   1098     _clas = not_clas if clas is None else clas
   1099     _name = not_name if name is None else name
   1100     _devn = not_devn if devn is None else devn
   1101 
   1102     # process lists of conditions
   1103     if _lst is not None:
   1104 
   1105         if not MAX_MATCHPROPS_ITERATIONS_REACHED or not bypass_guard_clauses:
   1106             if any([x is not None for x in lst_dct_params]):
   1107                 raise TypeError(f"\n\n(EE) matchProps(): Param 'lst|not_lst' must be used alone\n")
   1108             if not isinstance(_lst, list) or not all(isinstance(item, dict) for item in _lst):
   1109                 raise TypeError(
   1110                     f"\n\n(EE) matchProps(): Param 'lst|not_lst' wants a [list] of {{dicts}}\n")
   1111             # verify that every {dict} in [list of dicts] only contains valid parameter names
   1112             for dct in _lst:
   1113                 for param in list(dct.keys()):
   1114                     if param not in dct_param_strs:
   1115                         error(f"matchProps(): Invalid parameter: '{param}'")
   1116                         error(f"Invalid parameter is in this dict: \n\t{dct}")
   1117                         error(f"Dict is in this list:")
   1118                         for item in _lst:
   1119                             print(f"\t{item}")
   1120                         raise ValueError(
   1121                             f"\n(EE) matchProps(): Invalid parameter found in dict in list. "
   1122                             f"See log output before traceback.\n")
   1123 
   1124         def _matchProps_Lst(ctx: KeyContext):
   1125             if not cnfg.screen_has_focus:
   1126                 return False
   1127             if not_lst is not None:
   1128                 if logging_enabled: print(f"## _matchProps_Lst()[not_lst] ## {dbg=}")
   1129                 return not any(matchProps(**dct)(ctx) for dct in not_lst)
   1130             else:
   1131                 if logging_enabled: print(f"## _matchProps_Lst()[lst] ## {dbg=}")
   1132                 return any(matchProps(**dct)(ctx) for dct in lst)
   1133 
   1134         return _matchProps_Lst      # outer function returning inner function
   1135 
   1136     # compile case insensitive regex object for given params, unless cse=True
   1137     if _clas is not None: clas_rgx = re.compile(_clas, 0 if cse else re.I)
   1138     if _name is not None: name_rgx = re.compile(_name, 0 if cse else re.I)
   1139     if _devn is not None: devn_rgx = re.compile(_devn, 0 if cse else re.I)
   1140 
   1141     def _matchProps(ctx: KeyContext):
   1142         if not cnfg.screen_has_focus:
   1143             return False
   1144 
   1145         nt_err = 'ERR: matchProps: NoneType in ctx.'
   1146 
   1147         # Full debug mode: use original cond_list approach for complete visibility
   1148         if MATCHPROPS_FULL_DEBUG:
   1149             cond_list = []
   1150             if numlk is not None:
   1151                 cond_list.append(numlk is ctx.numlock_on)
   1152             if capslk is not None:
   1153                 cond_list.append(capslk is ctx.capslock_on)
   1154             if _devn is not None:
   1155                 devn_match = re.search(devn_rgx, ctx.device_name or nt_err + 'device_name')
   1156                 cond_list.append(not devn_match if not_devn is not None else devn_match)
   1157             if _clas is not None:
   1158                 clas_match = re.search(clas_rgx, ctx.wm_class or nt_err + 'wm_class')
   1159                 cond_list.append(not clas_match if not_clas is not None else clas_match)
   1160             if _name is not None:
   1161                 name_match = re.search(name_rgx, ctx.wm_name or nt_err + 'wm_name')
   1162                 cond_list.append(not name_match if not_name is not None else name_match)
   1163             print(f'####  CND_LST ({all(cond_list)})  ####  {dbg=}')
   1164             for elem in cond_list:
   1165                 print('##', re.sub(r'^.*span=.*\), ', '', str(elem)).replace('>',''))
   1166             print('-------------------------------------------------------------------')
   1167             return all(cond_list)
   1168 
   1169         # Optimized path: short-circuit on first failure
   1170         # Order: cheapest checks first, then most selective (devn), then clas, then name
   1171 
   1172         # Bool checks - nearly free (identity comparison)
   1173         if numlk is not None and numlk is not ctx.numlock_on:
   1174             return False
   1175         if capslk is not None and capslk is not ctx.capslock_on:
   1176             return False
   1177 
   1178         # Device check - most selective, eliminates most keystrokes from other devices
   1179         if _devn is not None:
   1180             devn_match = re.search(devn_rgx, ctx.device_name or nt_err + 'device_name')
   1181             # XOR: fail if (negative match requested) == (match found)
   1182             if (not_devn is not None) == bool(devn_match):
   1183                 return False
   1184 
   1185         # Class check - moderately selective, often complex regex patterns
   1186         if _clas is not None:
   1187             clas_match = re.search(clas_rgx, ctx.wm_class or nt_err + 'wm_class')
   1188             if (not_clas is not None) == bool(clas_match):
   1189                 return False
   1190 
   1191         # Name check - least commonly used
   1192         if _name is not None:
   1193             name_match = re.search(name_rgx, ctx.wm_name or nt_err + 'wm_name')
   1194             if (not_name is not None) == bool(name_match):
   1195                 return False
   1196 
   1197         return True
   1198 
   1199     return _matchProps      # outer function returning inner function
   1200 
   1201 
   1202 # Boolean variable to toggle Enter key state between F2 and Enter
   1203 # True = Enter key sends F2, False = Enter key sends Enter
   1204 _enter_is_F2 = True                 # DON'T CHANGE THIS! Must be set to True here.
   1205 _enter_F2_last_app_class = None     # Track which app set the state to be False
   1206 
   1207 
   1208 def iEF2(combo_if_true, latch_or_combo_if_false,
   1209                 keep_value_if_true=False, keep_value_if_false=False):
   1210     """
   1211     Formerly 'is_Enter_F2'
   1212     Send a different combo for the Enter key based on the state of the `_enter_is_F2` variable,
   1213     or latch the variable to True or False to control the Enter key output on the next use.
   1214 
   1215     Store the app class of the file manager that sets the state variable to be False, and
   1216     reset the state variable to True if the current app class does not match.
   1217 
   1218     Args:
   1219         combo_if_true:              The combo to send if _enter_is_F2 is True.
   1220         latch_or_combo_if_false:    The combo to send if _enter_is_F2 is False, or
   1221                                     a Boolean to latch _enter_is_F2 to a specific value.
   1222         keep_value_if_true (opt.):  If True, _enter_is_F2 will be kept True if it is currently True.
   1223                                     If False, _enter_is_F2 will be set to False if it is currently True.
   1224         keep_value_if_false (opt.): If True, _enter_is_F2 will be kept False if it is currently False.
   1225                                     If False, _enter_is_F2 will be set to True if it is currently False.
   1226 
   1227     Returns:
   1228         A function that, when called, returns the appropriate combo based on the current
   1229         state of _enter_is_F2 and the provided parameters, and updates _enter_is_F2
   1230         based on the provided parameters. The current app class is obtained by checking
   1231         the context object.
   1232 
   1233     This enables a simulation of the macOS Finder's "Enter to rename" capability, allowing
   1234     for complex control over the Enter key's behavior in various scenarios.
   1235     """
   1236     def _is_Enter_F2(ctx: KeyContext):
   1237         global _enter_is_F2, _enter_F2_last_app_class
   1238 
   1239         # If last app class that set state to False is not None, and does not match current
   1240         # app, reset the state var to True for initial behavior in new file manager windows.
   1241         if _enter_F2_last_app_class and _enter_F2_last_app_class != ctx.wm_class:
   1242             _enter_is_F2 = True
   1243             _enter_F2_last_app_class = None
   1244 
   1245         combo_list = [combo_if_true]
   1246         if latch_or_combo_if_false in (True, False):    # Latch variable to the provided bool value
   1247             _enter_is_F2 = latch_or_combo_if_false
   1248         elif _enter_is_F2:                              # If Enter is F2 now, set to be Enter next
   1249             if keep_value_if_true is False:
   1250                 _enter_is_F2 = False
   1251         else:                                           # If Enter is Enter now, set to be F2 next
   1252             combo_list = [latch_or_combo_if_false]
   1253             if keep_value_if_false is False:
   1254                 _enter_is_F2 = True
   1255 
   1256         # At this point we know whether the state var was set to True or False
   1257         if not _enter_is_F2:
   1258             # Record which app set state var to False
   1259             _enter_F2_last_app_class = ctx.wm_class
   1260 
   1261         # If state var is currently True, clear last app.
   1262         elif _enter_is_F2:
   1263             _enter_F2_last_app_class = None
   1264 
   1265         debug(f"_is_Enter_F2:               {combo_list      = }")
   1266         debug(f"_is_Enter_F2:               {_enter_is_F2    = }")
   1267         debug(f"_enter_F2_last_app_class:   {_enter_F2_last_app_class = }")
   1268 
   1269         return combo_list
   1270     return _is_Enter_F2
   1271 
   1272 
   1273 def iEF2NT():
   1274     """Feed `is_Enter_F2` function `None` and `True` as arguments, with short name"""
   1275     return iEF2(None, True)
   1276 
   1277 
   1278 def macro_tester():
   1279     """Type out a macro with useful info and a Unicode test.
   1280         WARNING: Safe only for use in apps that accept text blocks/typing of many characters."""
   1281     def _macro_tester(ctx: KeyContext):
   1282         return [
   1283                     C("Enter"),
   1284                     ST(f"Class: '{ctx.wm_class}'"), C("Enter"),
   1285                     ST(f"Title: '{ctx.wm_name}'"), C("Enter"),
   1286                     ST(f"Keybd: '{ctx.device_name}'"), C("Enter"),
   1287                     ST(f"Keyboard type: '{KBTYPE}'"), C("Enter"),
   1288                     ST("Next test should come out on ONE LINE!"), C("Enter"),
   1289                     ST("Unicode and Shift Test: 🌹—€—\u2021—ÿ—\U00002021 12345 !@#$% |\\ !!!!!!"),
   1290                     C("Enter"), C("Enter"),
   1291         ]
   1292     return _macro_tester
   1293 
   1294 
   1295 def is_valid_command(command):
   1296     """Check if the command path is valid and executable"""
   1297     return command and os.path.isfile(command) and os.access(command, os.X_OK)
   1298 
   1299 
   1300 # Result will be None if DE is not in list OR if 'kdialog' not available.
   1301 # kdialog_cmd = shutil.which('kdialog') if DESKTOP_ENV.casefold() in ['kde', 'lxqt'] else None
   1302 # DISABLING KDIALOG BECAUSE IT KIND OF SUCKS QUITE A BIT COMPARED TO ZENITY/QARMA
   1303 kdialog_cmd = shutil.which('kdialog') if DESKTOP_ENV.casefold() in ['kdialog_is_lame'] else None
   1304 
   1305 
   1306 zenity_is_qarma = False
   1307 
   1308 zenity_cmd = shutil.which('zenity-gtk')
   1309 if not zenity_cmd:
   1310     zenity_cmd = shutil.which('qarma')
   1311     if zenity_cmd:
   1312         zenity_is_qarma = True
   1313 if not zenity_cmd:
   1314     zenity_cmd = shutil.which('zenity')
   1315 
   1316 debug(f"Zenity command path: '{zenity_cmd}'")
   1317 
   1318 zenity_icon_option = None
   1319 
   1320 if zenity_cmd:
   1321     try:
   1322         zenity_help_output = subprocess.check_output([zenity_cmd, '--help-info'])
   1323         help_text = str(zenity_help_output)
   1324         if '--icon=' in help_text:
   1325             zenity_icon_option = '--icon=toshy_app_icon_rainbow'
   1326         elif '--icon-name=' in help_text:
   1327             zenity_icon_option = '--icon-name=toshy_app_icon_rainbow'
   1328     except subprocess.CalledProcessError:
   1329         pass  # zenity --help-info failed, assume icon is not supported
   1330 else:
   1331     error('ERR: Zenity command is missing! Diagnostic dialog not available!')
   1332 
   1333 
   1334 def notify_context():
   1335     """pop up a dialog with context info"""
   1336     def _notify_context(ctx: KeyContext):
   1337 
   1338         dialog_cmd              = None
   1339         nwln_str                = '\n'
   1340 
   1341         if kdialog_cmd:
   1342             dialog_cmd          = kdialog_cmd
   1343             nwln_str            = '<br>'
   1344         elif zenity_cmd:
   1345             dialog_cmd          = zenity_cmd
   1346             nwln_str            = '<br>' if zenity_is_qarma else '\n'
   1347         elif not zenity_cmd and not kdialog_cmd:
   1348             error('ERR: Diagnostic dialog not available. Necessary commands missing.')
   1349             return
   1350 
   1351         if not is_valid_command(dialog_cmd):
   1352             error(f"ERR: Dialog command not valid: '{dialog_cmd}'")
   1353             return
   1354 
   1355         # fix a problem with zenity and <tags> in text
   1356         def escape_markup(text: str):
   1357             return text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
   1358 
   1359         ctx_clas        = ctx.wm_class
   1360         ctx_name        = ctx.wm_name
   1361         ctx_devn        = ctx.device_name
   1362 
   1363         # ------ following are all True/False
   1364         ctx_term        = matchProps(clas=termStr           )(ctx)
   1365         ctx_rmte        = matchProps(clas=remoteStr         )(ctx)
   1366         ctx_fmgr        = matchProps(clas=filemanagerStr    )(ctx)
   1367         ctx_brws        = matchProps(clas=browsers_allStr   )(ctx)
   1368         ctx_vscd        = matchProps(clas=vscodeStr         )(ctx)
   1369 
   1370         if matchProps(lst=dialogs_CloseWin_lod)(ctx) or matchProps(lst=dialogs_Escape_lod)(ctx):
   1371             ctx_dlgs        = True
   1372         else:
   1373             ctx_dlgs        = False
   1374 
   1375         message         = (
   1376             f"<tt>"
   1377             f"<b>Class:</b> '{escape_markup(ctx_clas)}' {nwln_str}"
   1378             f"<b>Title:</b> '{escape_markup(ctx_name)}' {nwln_str}"
   1379             f"{nwln_str}"
   1380             f"<b>Input keyboard name:</b> '{ctx_devn}' {nwln_str}"
   1381             f"<b>Device seen as type:</b> '{KBTYPE}' {nwln_str}"
   1382             f"{nwln_str}"
   1383             f"<b>Toshy config file sees this environment:</b>  {nwln_str}"
   1384             f"<b> • DISTRO_ID ____________</b> '{DISTRO_ID      }' {nwln_str}"
   1385             f"<b> • DISTRO_VER ___________</b> '{DISTRO_VER     }' {nwln_str}"
   1386             f"<b> • VARIANT_ID ___________</b> '{VARIANT_ID     }' {nwln_str}"
   1387             f"<b> • SESSION_TYPE _________</b> '{SESSION_TYPE   }' {nwln_str}"
   1388             f"<b> • DESKTOP_ENV __________</b> '{DESKTOP_ENV    }' {nwln_str}"
   1389             f"<b> • DE_MAJ_VER ___________</b> '{DE_MAJ_VER     }' {nwln_str}"
   1390             f"<b> • WINDOW_MGR ___________</b> '{WINDOW_MGR     }' {nwln_str}"
   1391             f"{nwln_str}"
   1392             f"<b>Do any app class groups match on this window?:</b>  {nwln_str}"
   1393             f"<b> • Terminals ____________</b> '{ctx_term}' {nwln_str}"
   1394             f"<b> • Remotes/VMs __________</b> '{ctx_rmte}' {nwln_str}"
   1395             f"<b> • File Managers ________</b> '{ctx_fmgr}' {nwln_str}"
   1396             f"<b> • Web Browsers _________</b> '{ctx_brws}' {nwln_str}"
   1397             f"<b> • VSCode(s) ____________</b> '{ctx_vscd}' {nwln_str}"
   1398             f"<b> • Dialogs ______________</b> '{ctx_dlgs}' {nwln_str}"
   1399             f"{nwln_str}"
   1400             f"<b> __________________________________________________ </b>{nwln_str}"
   1401             f"<i>Keyboard shortcuts (Ctrl+C/Cmd+C) may not work here.</i>{nwln_str}"
   1402             f"<i>Select text with mouse. Triple-click to select all. </i>{nwln_str}"
   1403             f"<i>Right-click with mouse and choose 'Copy' from menu. </i>{nwln_str}"
   1404             f"</tt>"
   1405         )
   1406 
   1407         zenity_cmd_lst = [  zenity_cmd, '--info', '--no-wrap',
   1408                             '--title=Toshy Context Info',
   1409                             '--text=' + message ]
   1410 
   1411         # insert the icon argument if it's supported
   1412         if zenity_icon_option is not None:
   1413             zenity_cmd_lst.insert(3, zenity_icon_option)
   1414 
   1415         kdialog_cmd_lst = [kdialog_cmd, '--msgbox', message, '--title', 'Toshy Context Info']
   1416         # Add icon if needed: kdialog_cmd_lst += ['--icon', '/path/to/icon']
   1417         # Figure out why icon argument doesn't work. Need a proper icon theme folder?
   1418         # DONE: Figured out that Kdialog does not support custom icons at all!
   1419         kdialog_cmd_lst += ['--icon', 'toshy_app_icon_rainbow']
   1420 
   1421         if dialog_cmd == kdialog_cmd:
   1422             subprocess.Popen(kdialog_cmd_lst, cwd=icons_dir, stderr=DEVNULL, stdout=DEVNULL)
   1423         elif dialog_cmd == zenity_cmd:
   1424             subprocess.Popen(zenity_cmd_lst, cwd=icons_dir, stderr=DEVNULL, stdout=DEVNULL)
   1425 
   1426         # Optionally, also send a system notification:
   1427         # ntfy.send_notification(message)
   1428     return _notify_context
   1429 
   1430 
   1431 def is_pre_GNOME_45(de_ver):
   1432     """Utility function to check if GNOME version is older than GNOME 45"""
   1433     if DESKTOP_ENV != 'gnome':
   1434         # No need to go further if we aren't even in GNOME
   1435         return False
   1436     pre_G45_ver_lst = [44, 43, 42, 41, 40, 3]
   1437     return str(de_ver).isdigit() and int(de_ver) in pre_G45_ver_lst
   1438 
   1439 
   1440 def toggle_and_show_capslock_state(ctx: KeyContext):
   1441     """
   1442     Show the (coming) state of CapsLock LED in a notification pop-up dialog.
   1443     Then return the CapsLock key combo to toggle the CapsLock LED state.
   1444 
   1445     No need to return inner closure because not used in conditionals.
   1446     Do not use () to 'call' the function from output macro. Not needed.
   1447 
   1448     Example usage:
   1449     C("CapsLock"): toggle_and_show_capslock_state, # Toggle CapsLock, show notification
   1450     """
   1451 
   1452     # Logic reversed because notification is constructed before combo is returned.
   1453     message = 'CapsLock is ON' if not ctx.capslock_on else 'CapsLock is OFF'
   1454     icon = 'input-caps-on-symbolic' if not ctx.capslock_on else 'window-close'
   1455     ntfy.send_notification(message, icon, 'low', False)
   1456     return C("CapsLock")
   1457 
   1458 
   1459 def toggle_and_show_numlock_state(ctx: KeyContext):
   1460     """
   1461     Show the (coming) state of NumLock LED in a notification pop-up dialog.
   1462     Then return the NumLock key combo to toggle the NumLock LED state.
   1463 
   1464     Only shows notification and toggles if 'Forced Numpad' pref is disabled.
   1465     Like the isNumlockClearKey() function, returns Escape combo if 'Forced
   1466     Numpad' feature is enabled. 'Forced Numpad' must be disabled to use
   1467     NumLock key normally and see the notifications.
   1468 
   1469     No need to return inner closure because not used in conditionals.
   1470     Do not use () to 'call' the function from output macro. Not needed.
   1471 
   1472     Example usage:
   1473     C("NumLock"): toggle_and_show_numlock_state, # Toggle NumLock, show notification
   1474     """
   1475 
   1476     if cnfg.forced_numpad:
   1477         debug(f'Force Numpad is ON: NumLock key is "Clear" (sends Escape)')
   1478         return C("Esc")
   1479     else:
   1480         # Logic reversed because notification is constructed before combo is returned.
   1481         message = 'NumLock is ON' if not ctx.numlock_on else 'NumLock is OFF'
   1482         icon = 'input-num-on' if not ctx.numlock_on else 'window-close'
   1483         ntfy.send_notification(message, icon, 'low', False)
   1484         return C("NumLock")
   1485 
   1486 
   1487 
   1488 # Suggested location for adding custom functions for personal use.
   1489 ###################################################################################################
   1490 ###  SLICE_MARK_START: user_custom_functions  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   1491 
   1492 def open_launcher(ctx):
   1493     subprocess.Popen(['vicinae', 'toggle'], stdout=DEVNULL, stderr=DEVNULL)
   1494     return None
   1495 
   1496 ###  SLICE_MARK_END: user_custom_functions  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   1497 ###################################################################################################
   1498 
   1499 
   1500 
   1501 ###################################  MULTI-TAP  ####################################
   1502 ###                                                                              ###
   1503 ###                                                                              ###
   1504 ###    ███    ███ ██    ██ ██      ████████ ██       ████████  █████  ██████     ###
   1505 ###    ████  ████ ██    ██ ██         ██    ██          ██    ██   ██ ██   ██    ###
   1506 ###    ██ ████ ██ ██    ██ ██         ██    ██ █████    ██    ███████ ██████     ###
   1507 ###    ██  ██  ██ ██    ██ ██         ██    ██          ██    ██   ██ ██         ###
   1508 ###    ██      ██  ██████  ███████    ██    ██          ██    ██   ██ ██         ###
   1509 ###                                                                              ###
   1510 ###                                                                              ###
   1511 ####################################################################################
   1512 # Functions to support proper asyncio time-based multi-tap actions, without
   1513 # blocking the single-tap usage of the same combo (unless desired).
   1514 #
   1515 # Until this EXPERIMENTAL feature moves into the keymapper, the API function
   1516 # `multitap_config()` will need to be called from a section lower down, like
   1517 # the "user_apps" editable slice, if user wants custom multi-tap timings.
   1518 
   1519 
   1520 # Multi-tap configuration storage
   1521 _MULTITAP_CONFIG = {
   1522     'tap_interval': 0.25,     # Default: 250ms between taps
   1523     'min_tap_delay': 0.07,    # Default: 70ms repeat protection
   1524 }
   1525 
   1526 
   1527 def get_output():
   1528     """Get the main keymapper's output instance"""
   1529     try:
   1530         # Access the transform module's _output at runtime
   1531         if 'xwaykeyz.transform' in sys.modules:
   1532             transform_module = sys.modules['xwaykeyz.transform']
   1533             if hasattr(transform_module, '_output'):
   1534                 debug("## multitap: Using main keymapper's _output instance")
   1535                 return transform_module._output
   1536     except Exception as e:
   1537         debug(f"## multitap: Error accessing output: {e}")
   1538     return None
   1539 
   1540 
   1541 def process_multitap_command(command, ctx):
   1542     """Simplified recursive command processor based on handle_commands logic"""
   1543     debug(f"## multitap: Processing command: {type(command)}")
   1544 
   1545     if callable(command):
   1546         # Handle functions like ST(), notify_context, etc.
   1547         cmd_param_cnt = len(inspect.signature(command).parameters)
   1548         debug(f"## multitap: Callable with {cmd_param_cnt} parameters")
   1549 
   1550         if cmd_param_cnt == 0:
   1551             result = command()
   1552         else:
   1553             result = command(ctx)
   1554 
   1555         debug(f"## multitap: Callable returned: {type(result)}")
   1556 
   1557         # Recursively process the result
   1558         if result is not None:
   1559             process_multitap_command(result, ctx)
   1560 
   1561     elif isinstance(command, list):
   1562         # Recursively process each item in the list
   1563         debug(f"## multitap: Processing list with {len(command)} items")
   1564         for i, item in enumerate(command):
   1565             debug(f"## multitap: Processing list item {i+1}: {type(item)}")
   1566             process_multitap_command(item, ctx)
   1567 
   1568     else:
   1569         # Handle direct objects (Combo, Key, etc.)
   1570         output = get_output()
   1571         if output and hasattr(command, '__class__'):
   1572             class_name = command.__class__.__name__
   1573             debug(f"## multitap: Direct object class: {class_name}")
   1574 
   1575             if 'Combo' in class_name:
   1576                 output.send_combo(command)
   1577                 debug(f"## multitap: Sent Combo object")
   1578             elif 'Key' in class_name:
   1579                 output.send_key(command)
   1580                 debug(f"## multitap: Sent Key object")
   1581             elif command is not None:
   1582                 debug(f"## multitap: Unknown command type: {class_name}")
   1583         elif not output:
   1584             debug(f"## multitap: No output available for command: {type(command)}")
   1585         else:
   1586             debug(f"## multitap: Command has no __class__: {command}")
   1587 
   1588 
   1589 # Per-combo state tracking using action tuple as key
   1590 tap_states: Dict[tuple, Dict[str, Any]] = {}
   1591 
   1592 event_loop: Optional[asyncio.AbstractEventLoop] = None
   1593 
   1594 
   1595 def get_loop() -> Optional[asyncio.AbstractEventLoop]:
   1596     global event_loop
   1597     if event_loop is None or event_loop.is_closed():
   1598         try:
   1599             event_loop = asyncio.get_running_loop()
   1600         except RuntimeError:
   1601             event_loop = None
   1602     return event_loop
   1603 
   1604 
   1605 def multitap_config(tap_interval=None, min_tap_delay=None):
   1606     """
   1607     Configure global multi-tap timing settings.
   1608 
   1609     Args:
   1610         tap_interval: Maximum time between taps in seconds (0.15 to 1.5)
   1611         min_tap_delay: Minimum time between taps to avoid repeats (0.05 to 0.5)
   1612 
   1613     Example:
   1614         multitap_config(
   1615             tap_interval=0.3,    # 300ms between taps
   1616             min_tap_delay=0.10   # 100ms repeat protection
   1617         )
   1618     """
   1619     global _MULTITAP_CONFIG
   1620 
   1621     if tap_interval is not None:
   1622         if isinstance(tap_interval, (int, float)) and 0.15 <= tap_interval <= 1.5:
   1623             _MULTITAP_CONFIG['tap_interval'] = float(tap_interval)
   1624             debug(f"## multitap_config: Set tap_interval to {tap_interval}s")
   1625         else:
   1626             debug(f"## multitap_config: Invalid tap_interval {tap_interval}, must be 0.15-1.5 sec")
   1627 
   1628     if min_tap_delay is not None:
   1629         if isinstance(min_tap_delay, (int, float)) and 0.05 <= min_tap_delay <= 0.5:
   1630             _MULTITAP_CONFIG['min_tap_delay'] = float(min_tap_delay)
   1631             debug(f"## multitap_config: Set min_tap_delay to {min_tap_delay}s")
   1632         else:
   1633             debug(f"## multitap_config: Invalid min_tap_delay {min_tap_delay}, must be 0.05-0.5 sec")
   1634 
   1635     # Ensure ignore time is less than interval time
   1636     if _MULTITAP_CONFIG['min_tap_delay'] >= _MULTITAP_CONFIG['tap_interval']:
   1637         original_delay = _MULTITAP_CONFIG['min_tap_delay']
   1638         _MULTITAP_CONFIG['min_tap_delay'] = _MULTITAP_CONFIG['tap_interval'] * 0.25
   1639         debug(f"## multitap_config: min_tap_delay ({original_delay}s) >= tap_interval, "
   1640                 f"adjusted to {_MULTITAP_CONFIG['min_tap_delay']:.3f}s")
   1641 
   1642 
   1643 def isMultiTap( tap_1_action: Optional[Callable] = None,
   1644                 tap_2_action: Optional[Callable] = None,
   1645                 tap_3_action: Optional[Callable] = None,
   1646                 tap_4_action: Optional[Callable] = None,
   1647                 tap_5_action: Optional[Callable] = None,
   1648                 tap_interval: float = None,
   1649                 min_tap_delay: float = None) -> Callable:
   1650     """
   1651     Multi-tap handler that supports 1-5 taps with asyncio.
   1652 
   1653     Args:
   1654         tap_1_action: Function to call on single tap (can be None to block single-tap)
   1655         tap_2_action: Function to call on double tap
   1656         tap_3_action: Function to call on triple tap
   1657         tap_4_action: Function to call on quadruple tap
   1658         tap_5_action: Function to call on quintuple tap
   1659         tap_interval: Max time between taps (None = use global config)
   1660         min_tap_delay: Min time between taps to avoid key repeat (None = use global config)
   1661 
   1662     Returns:
   1663         Function that handles the tap detection
   1664     """
   1665 
   1666     # Use global config values if not explicitly provided
   1667     if tap_interval is None:
   1668         tap_interval = _MULTITAP_CONFIG['tap_interval']
   1669     if min_tap_delay is None:
   1670         min_tap_delay = _MULTITAP_CONFIG['min_tap_delay']
   1671 
   1672     # Use action tuple as unique identifier, converting lists to tuples for hashability
   1673     def make_hashable(action):
   1674         if isinstance(action, list):
   1675             return tuple(make_hashable(item) for item in action)
   1676         return action
   1677 
   1678     action_key = (
   1679         make_hashable(tap_1_action),
   1680         make_hashable(tap_2_action),
   1681         make_hashable(tap_3_action),
   1682         make_hashable(tap_4_action),
   1683         make_hashable(tap_5_action)
   1684     )
   1685 
   1686     def execute_action_for_tap_count(   tap_count: int,
   1687                                         captured_ctx,
   1688                                         tap_1_action,
   1689                                         tap_2_action,
   1690                                         tap_3_action,
   1691                                         tap_4_action,
   1692                                         tap_5_action):
   1693         """Execute the appropriate action based on tap count."""
   1694         actions = {
   1695             1: tap_1_action,
   1696             2: tap_2_action,
   1697             3: tap_3_action,
   1698             4: tap_4_action,
   1699             5: tap_5_action
   1700         }
   1701 
   1702         action = actions.get(tap_count)
   1703         if action is not None:
   1704             try:
   1705                 debug(f"## isMultiTap: Executing {tap_count}-tap action for {action_key}")
   1706                 process_multitap_command(action, captured_ctx)
   1707                 debug(f"## isMultiTap: Completed {tap_count}-tap action for {action_key}")
   1708             except Exception as e:
   1709                 debug(f"## isMultiTap: Error executing {tap_count}-tap action: {e}")
   1710         else:
   1711             debug(f"## isMultiTap: No action defined for {tap_count} taps on {action_key}")
   1712 
   1713     def finalize_taps(action_key: tuple, captured_ctx):
   1714         """Called when tap sequence is finalized."""
   1715         if action_key in tap_states:
   1716             state = tap_states[action_key]
   1717             tap_count = state['count']
   1718             debug(f"## isMultiTap: Finalizing {tap_count} taps for {action_key}")
   1719 
   1720             # Get the actions before cleaning up state
   1721             stored_tap_1_action = state['tap_1_action']
   1722             stored_tap_2_action = state['tap_2_action']
   1723             stored_tap_3_action = state['tap_3_action']
   1724             stored_tap_4_action = state['tap_4_action']
   1725             stored_tap_5_action = state['tap_5_action']
   1726 
   1727             # Clean up state
   1728             del tap_states[action_key]
   1729 
   1730             # Execute appropriate action with captured context
   1731             execute_action_for_tap_count(   tap_count,
   1732                                             captured_ctx,
   1733                                             stored_tap_1_action,
   1734                                             stored_tap_2_action,
   1735                                             stored_tap_3_action,
   1736                                             stored_tap_4_action,
   1737                                             stored_tap_5_action)
   1738 
   1739     def _isMultiTap(ctx) -> None:
   1740         loop = get_loop()
   1741         if loop is None:
   1742             debug(f"## isMultiTap: No event loop available for {action_key}")
   1743             return None
   1744 
   1745         current_time = time.time()
   1746 
   1747         # Initialize or get existing state
   1748         if action_key not in tap_states:
   1749             tap_states[action_key] = {
   1750                 'count': 0,
   1751                 'last_tap_time': 0.0,
   1752                 'finalize_handle': None,
   1753                 'captured_ctx': ctx,
   1754                 # Store the individual actions in the state
   1755                 'tap_1_action': tap_1_action,
   1756                 'tap_2_action': tap_2_action,
   1757                 'tap_3_action': tap_3_action,
   1758                 'tap_4_action': tap_4_action,
   1759                 'tap_5_action': tap_5_action,
   1760             }
   1761 
   1762         state = tap_states[action_key]
   1763         time_since_last = current_time - state['last_tap_time']
   1764 
   1765         # Check if this tap is too soon (key repeat protection)
   1766         if state['count'] > 0 and time_since_last < min_tap_delay:
   1767             debug(  f"## isMultiTap: Ignoring repeat for {action_key} "
   1768                     f"(too soon: {time_since_last:.3f}s)")
   1769             return None
   1770 
   1771         # Check if this tap is too late (start new sequence)
   1772         if state['count'] > 0 and time_since_last >= tap_interval:
   1773             debug(  f"## isMultiTap: Too late for {action_key} "
   1774                     f"(gap: {time_since_last:.3f}s), finalizing previous")
   1775             # Finalize the previous sequence with its captured context
   1776             finalize_taps(action_key, state['captured_ctx'])
   1777             # Start new sequence with current context
   1778             tap_states[action_key] = {
   1779                 'count': 0,
   1780                 'last_tap_time': 0.0,
   1781                 'finalize_handle': None,
   1782                 'captured_ctx': ctx,
   1783                 # Store the individual actions in the state
   1784                 'tap_1_action': tap_1_action,
   1785                 'tap_2_action': tap_2_action,
   1786                 'tap_3_action': tap_3_action,
   1787                 'tap_4_action': tap_4_action,
   1788                 'tap_5_action': tap_5_action,
   1789             }
   1790             state = tap_states[action_key]
   1791 
   1792         # Cancel any pending finalization
   1793         finalize_handle: Optional[asyncio.Handle] = state['finalize_handle']
   1794         if finalize_handle is not None:
   1795             finalize_handle.cancel()
   1796             state['finalize_handle'] = None
   1797 
   1798         # Increment tap count
   1799         state['count'] += 1
   1800         state['last_tap_time'] = current_time
   1801 
   1802         debug(f"## isMultiTap: Tap #{state['count']} for {action_key}")
   1803 
   1804         # If we've exceeded max taps (5), ignore subsequent taps
   1805         if state['count'] > 5:
   1806             debug(f"## isMultiTap: Ignoring tap beyond maximum (tap #{state['count']})")
   1807             return None
   1808 
   1809         # Schedule finalization after the interval for all tap counts
   1810         if state['tap_1_action'] or state['count'] > 1:
   1811             captured_ctx = state['captured_ctx']
   1812             handle: asyncio.Handle = loop.call_later(
   1813                 tap_interval,
   1814                 lambda: finalize_taps(action_key, captured_ctx)  # Pass captured context
   1815             )
   1816             state['finalize_handle'] = handle
   1817             debug(f"## isMultiTap: Scheduled finalization for {action_key} in {tap_interval}s")
   1818 
   1819         # Return None since we're handling actions asynchronously
   1820         return None
   1821 
   1822     return _isMultiTap
   1823 
   1824 
   1825 
   1826 #################################  MODMAPS  ####################################
   1827 ###                                                                          ###
   1828 ###                                                                          ###
   1829 ###      ███    ███  ██████  ██████  ███    ███  █████  ██████  ███████      ###
   1830 ###      ████  ████ ██    ██ ██   ██ ████  ████ ██   ██ ██   ██ ██           ###
   1831 ###      ██ ████ ██ ██    ██ ██   ██ ██ ████ ██ ███████ ██████  ███████      ###
   1832 ###      ██  ██  ██ ██    ██ ██   ██ ██  ██  ██ ██   ██ ██           ██      ###
   1833 ###      ██      ██  ██████  ██████  ██      ██ ██   ██ ██      ███████      ###
   1834 ###                                                                          ###
   1835 ###                                                                          ###
   1836 ################################################################################
   1837 ### Modmaps turn a key into a different key as long as the modmap is active
   1838 ### The modified key can be used in shortcut combos as the new key
   1839 
   1840 
   1841 # DO NOT REMOVE THIS MODMAP AND KEYMAP!
   1842 # Special modmap to trigger the evaluation of the keyboard type when
   1843 # any modifier key is pressed
   1844 modmap("Trigger Modmap: Keyboard Type", {
   1845     # This modmap must have all modifier keys inside it, so they will
   1846     # all trigger the re-evaluation of the keyboard type.
   1847     # The accompanying keymap can be empty and still accomplish
   1848     # the same purpose of triggering a re-evaluation of the
   1849     # keyboard type when any non-modifier key is pressed.
   1850     Key.LEFT_META:              Key.LEFT_META,
   1851     Key.RIGHT_META:             Key.RIGHT_META,
   1852     Key.LEFT_ALT:               Key.LEFT_ALT,
   1853     Key.RIGHT_ALT:              Key.RIGHT_ALT,
   1854     Key.LEFT_CTRL:              Key.LEFT_CTRL,
   1855     Key.RIGHT_CTRL:             Key.RIGHT_CTRL,
   1856     Key.LEFT_SHIFT:             Key.LEFT_SHIFT,
   1857     Key.RIGHT_SHIFT:            Key.RIGHT_SHIFT,
   1858 }, when = lambda ctx: getKBtype()(ctx) )    # THIS CONDITIONAL MUST NEVER EVALUATE TO TRUE!
   1859 # Special keymap to trigger the evaluation of the keyboard type when
   1860 # any non-modifier key is pressed
   1861 keymap("Trigger Keymap: Keyboard Type", {
   1862     # Nothing needed here.
   1863 }, when = lambda ctx: getKBtype()(ctx) )
   1864 
   1865 
   1866 def get_iEF2_context():
   1867     """Reset Enter-to-rename state variable to True, if the current
   1868         application does not match on the file manager app class string"""
   1869     def _get_iEF2_context(ctx: KeyContext):
   1870         global _enter_is_F2, _enter_F2_last_app_class
   1871         if not _enter_is_F2 and not matchProps(clas=filemanagerStr)(ctx):
   1872             debug("Resetting Enter-to-rename state variable to True.")
   1873             _enter_is_F2 = True
   1874             _enter_F2_last_app_class = None
   1875 
   1876         return False
   1877 
   1878     return _get_iEF2_context
   1879 
   1880 
   1881 # DO NOT REMOVE THIS MODMAP AND KEYMAP!
   1882 # Special modmap to trigger the evaluation of the window context when
   1883 # any modifier key is pressed
   1884 modmap("Trigger Modmap: Enter-to-Rename Context", {
   1885     # This modmap must have all modifier keys inside it, so they will
   1886     # all trigger the re-evaluation of the window context.
   1887     # The accompanying keymap can be empty and still accomplish
   1888     # the same purpose of triggering a re-evaluation of the
   1889     # window context when any non-modifier key is pressed.
   1890     Key.LEFT_META:              Key.LEFT_META,
   1891     Key.RIGHT_META:             Key.RIGHT_META,
   1892     Key.LEFT_ALT:               Key.LEFT_ALT,
   1893     Key.RIGHT_ALT:              Key.RIGHT_ALT,
   1894     Key.LEFT_CTRL:              Key.LEFT_CTRL,
   1895     Key.RIGHT_CTRL:             Key.RIGHT_CTRL,
   1896     Key.LEFT_SHIFT:             Key.LEFT_SHIFT,
   1897     Key.RIGHT_SHIFT:            Key.RIGHT_SHIFT,
   1898 }, when = lambda ctx: get_iEF2_context()(ctx) )    # THIS CONDITIONAL MUST NEVER EVALUATE TO TRUE!
   1899 # Special keymap to trigger the evaluation of the window context when
   1900 # any non-modifier key is pressed
   1901 keymap("Trigger Keymap: Enter-to-Rename Context", {
   1902     # Nothing needed here.
   1903 }, when = lambda ctx: get_iEF2_context()(ctx) )
   1904 
   1905 
   1906 modmap("Cond modmap - Media Arrows Fix",{
   1907     # Fix arrow keys with media functions instead of PgUp/PgDn/Home/End
   1908     Key.PLAYPAUSE:              Key.PAGE_UP,
   1909     Key.STOPCD:                 Key.PAGE_DOWN,
   1910     Key.PREVIOUSSONG:           Key.HOME,
   1911     Key.NEXTSONG:               Key.END,
   1912 }, when = lambda ctx:
   1913     cnfg.media_arrows_fix and
   1914     cnfg.screen_has_focus and
   1915     matchProps(not_clas=remoteStr)(ctx)
   1916 )
   1917 
   1918 
   1919 ###################################################################################################
   1920 ###  SLICE_MARK_START: exclude_kpad_devs  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   1921 
   1922 # List of devices to add to the device exclusion list below this slice
   1923 
   1924 exclude_kpad_devs_UserCustom_lst = [
   1925     # Example syntax:
   1926     # 'My Keyboard Device',
   1927 
   1928 ]
   1929 
   1930 ###  SLICE_MARK_END: exclude_kpad_devs  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   1931 ###################################################################################################
   1932 
   1933 
   1934 # DEPRECATED in favor of simple list:
   1935 # # List of devices with keypads to exclude from Forced Numpad and GTK3 fix modmaps
   1936 # exclude_kpad_devs_lod = [
   1937 #     {devn: 'Razer Razer Naga X'},
   1938 #     *exclude_kpad_devs_UserCustom_lod,
   1939 # ]
   1940 
   1941 # Ensure the DEPRECATED variable exists and is a list before attempting to access it
   1942 # (New users WILL NOT have it in the marked slice above)
   1943 exclude_kpad_devs_UserCustom_lod = locals().get('exclude_kpad_devs_UserCustom_lod', [])
   1944 
   1945 # Ensure the NEW variable exists and is a list before attempting to access it
   1946 # (Existing users MIGHT NOT have it in the marked slice above)
   1947 exclude_kpad_devs_UserCustom_lst = locals().get('exclude_kpad_devs_UserCustom_lod', [])
   1948 
   1949 DEPRECATED_exclude_kpad_devs_UserCustom_lst = [
   1950     device['devn'] for device in exclude_kpad_devs_UserCustom_lod
   1951     if isinstance(exclude_kpad_devs_UserCustom_lod, list)
   1952 ]
   1953 
   1954 # List of devices with keypads to exclude from Forced Numpad and GTK3 fix modmaps
   1955 exclude_kpad_devs_lst = [
   1956     'Razer Razer Naga X',
   1957     *DEPRECATED_exclude_kpad_devs_UserCustom_lst,
   1958     *exclude_kpad_devs_UserCustom_lst,
   1959 ]
   1960 exclude_kpad_devs_Str = toRgxStr(exclude_kpad_devs_lst)
   1961 
   1962 modmap("Cond modmap - Forced Numpad feature",{
   1963     # Make numpad be a numpad regardless of Numlock state (like an Apple keyboard in macOS)
   1964     Key.KP1:                    Key.KEY_1,
   1965     Key.KP2:                    Key.KEY_2,
   1966     Key.KP3:                    Key.KEY_3,
   1967     Key.KP4:                    Key.KEY_4,
   1968     Key.KP5:                    Key.KEY_5,
   1969     Key.KP6:                    Key.KEY_6,
   1970     Key.KP7:                    Key.KEY_7,
   1971     Key.KP8:                    Key.KEY_8,
   1972     Key.KP9:                    Key.KEY_9,
   1973     Key.KP0:                    Key.KEY_0,
   1974     Key.KPDOT:                  Key.DOT,
   1975     Key.KPENTER:                Key.ENTER,
   1976 }, when = lambda ctx:
   1977     cnfg.forced_numpad and
   1978     cnfg.screen_has_focus and
   1979     matchProps(not_devn=exclude_kpad_devs_Str)(ctx) and
   1980     matchProps(not_clas=remoteStr)(ctx)
   1981 )
   1982 
   1983 
   1984 modmap("Cond modmap - GTK3 numpad nav keys fix", {
   1985     # Make numpad nav keys work correctly in GTK3 apps
   1986     # Key.KP5:                    Key.X,                # GTK3 numpad fix - TEST TO SEE IF WORKING
   1987     # Numpad PgUp/PgDn/Home/End keys
   1988     Key.KP9:                    Key.PAGE_UP,
   1989     Key.KP3:                    Key.PAGE_DOWN,
   1990     Key.KP7:                    Key.HOME,
   1991     Key.KP1:                    Key.END,
   1992     # Numpad arrow keys
   1993     Key.KP8:                    Key.UP,
   1994     Key.KP2:                    Key.DOWN,
   1995     Key.KP4:                    Key.LEFT,
   1996     Key.KP6:                    Key.RIGHT,
   1997     # Numpad Insert/Delete/Enter keys
   1998     Key.KP0:                    Key.INSERT,
   1999     Key.KPDOT:                  Key.DELETE,
   2000     Key.KPENTER:                Key.ENTER,
   2001 }, when = lambda ctx:
   2002     not cnfg.forced_numpad and
   2003     cnfg.screen_has_focus and
   2004     matchProps(numlk=False)(ctx) and
   2005     matchProps(not_devn=exclude_kpad_devs_Str)(ctx) and
   2006     matchProps(not_clas=remoteStr)(ctx)
   2007 )
   2008 
   2009 
   2010 # multipurpose_modmap("Optional Tweaks",
   2011 #     # {Key.ENTER:                 [Key.ENTER, Key.RIGHT_CTRL]     # Enter2Cmd
   2012 #     # {Key.CAPSLOCK:              [Key.ESC, Key.RIGHT_CTRL]       # Caps2Esc
   2013 #     # {Key.LEFT_META:             [Key.ESC, Key.RIGHT_CTRL]       # Caps2Esc - Chromebook
   2014 #     {                                                             # Placeholder
   2015 # })
   2016 
   2017 multipurpose_modmap("Enter2Cmd", {
   2018     Key.ENTER:                  [Key.ENTER, Key.RIGHT_CTRL]     # Enter2Cmd
   2019 }, when = lambda ctx:
   2020     cnfg.Enter2Ent_Cmd and
   2021     cnfg.screen_has_focus and
   2022     matchProps(not_clas=remoteStr)(ctx)
   2023 )
   2024 
   2025 multipurpose_modmap("Caps2Esc - not Chromebook kbd", {
   2026     Key.CAPSLOCK:               [Key.ESC, Key.RIGHT_CTRL]       # Caps2Esc - not Chromebook
   2027 }, when = lambda ctx:
   2028     cnfg.Caps2Esc_Cmd and
   2029     cnfg.screen_has_focus and
   2030     not isKBtype('Chromebook')(ctx) and
   2031     matchProps(not_clas=remoteStr)(ctx)
   2032 )
   2033 
   2034 multipurpose_modmap("Caps2Esc - Chromebook kbd", {
   2035     Key.LEFT_META:               [Key.ESC, Key.RIGHT_CTRL]       # Caps2Esc - Chromebook
   2036 }, when = lambda ctx:
   2037     cnfg.Caps2Esc_Cmd and
   2038     cnfg.screen_has_focus and
   2039     isKBtype('Chromebook')(ctx) and
   2040     matchProps(not_clas=remoteStr)(ctx)
   2041 )
   2042 
   2043 
   2044 # THIS IS ALL SUPERCEDED BY THE NEW SOLUTION OF MONITORING THE SYNERGY LOG FILE!
   2045 # Fix for avoiding modmapping when using Synergy keyboard/mouse sharing.
   2046 # Synergy doesn't set a wm_class, so this may cause issues with other
   2047 # applications that also don't set the wm_class.
   2048 # Enable only if you use Synergy. (This is only for use with xkeysnail.)
   2049 # define_conditional_modmap(lambda wm_class: wm_class == '', {})
   2050 ###########################
   2051 # Suggestion (untested):
   2052 # For use with keyszer, to avoid catching GNOME Shell task switcher (which has
   2053 # an empty WM_CLASS but sets a WM_NAME):
   2054 # modmap("Synergy fix", {}, when = lambda ctx: ctx.wm_class == '' and ctx.wm_name == '')
   2055 # If Synergy actually sets a WM_NAME like GNOME Shell does, you'd need to use
   2056 # one of these alternatives:
   2057 # modmap("Synergy fix", {}, when = lambda ctx: ctx.wm_class == '' and ctx.wm_name == 'SYNERGY_WM_NAME')
   2058 # Or:
   2059 # modmap("Synergy fix", {}, when = lambda ctx: ctx.wm_class == '' and not ctx.wm_name == 'gnome-shell')
   2060 # PROBLEM: When GNOME desktop has focus, it sets no window info at all (no class, no name/title)
   2061 
   2062 
   2063 # [Global GUI conditional modmaps] Change modifier keys as in xmodmap
   2064 modmap("Cond modmap - GUI - Caps2Cmd - not Cbk kdb", {
   2065     Key.CAPSLOCK:               Key.RIGHT_CTRL,                 # Caps2Cmd
   2066 }, when = lambda ctx:
   2067     cnfg.Caps2Cmd and
   2068     cnfg.screen_has_focus and
   2069     not isKBtype('Chromebook')(ctx) and
   2070     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2071 )
   2072 modmap("Cond modmap - GUI - Caps2Cmd - Cbk kdb", {
   2073     Key.LEFT_META:              Key.RIGHT_CTRL,                 # Caps2Cmd - Chromebook
   2074 }, when = lambda ctx:
   2075     cnfg.Caps2Cmd and
   2076     cnfg.screen_has_focus and
   2077     isKBtype('Chromebook')(ctx) and
   2078     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2079 )
   2080 modmap("Cond modmap - GUI - IBM kbd - multi_lang OFF", {
   2081     # - IBM
   2082     Key.RIGHT_ALT:              Key.RIGHT_CTRL,                 # IBM - Multi-language (Remove)
   2083     Key.RIGHT_CTRL:             Key.RIGHT_ALT,                  # IBM - Multi-language (Remove)
   2084 }, when = lambda ctx:
   2085     not cnfg.multi_lang and
   2086     cnfg.screen_has_focus and
   2087     isKBtype('IBM', map='mmap GUI IBM ML-OFF')(ctx) and
   2088     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2089 )
   2090 modmap("Cond modmap - GUI - IBM kbd", {
   2091     # - IBM
   2092     Key.CAPSLOCK:               Key.LEFT_META,                  # IBM
   2093     Key.LEFT_CTRL:              Key.LEFT_ALT,                   # IBM
   2094     Key.LEFT_ALT:               Key.RIGHT_CTRL,                 # IBM
   2095 }, when = lambda ctx:
   2096     cnfg.screen_has_focus and
   2097     isKBtype('IBM', map='mmap GUI IBM')(ctx) and
   2098     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2099 )
   2100 modmap("Cond modmap - GUI - Cbk kbd - multi_lang OFF", {
   2101     # - Chromebook
   2102     Key.RIGHT_ALT:              Key.RIGHT_CTRL,                 # Chromebook - Multi-language (Remove)
   2103     Key.RIGHT_CTRL:             Key.RIGHT_ALT,                  # Chromebook - Multi-language (Remove)
   2104 }, when = lambda ctx:
   2105     not cnfg.multi_lang and
   2106     cnfg.screen_has_focus and
   2107     isKBtype('Chromebook', map='mmap GUI Cbk ML-OFF')(ctx) and
   2108     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2109 )
   2110 modmap("Cond modmap - GUI - Cbk kbd", {
   2111     # - Chromebook
   2112     Key.LEFT_CTRL:              Key.LEFT_ALT,                   # Chromebook
   2113     Key.LEFT_ALT:               Key.RIGHT_CTRL,                 # Chromebook
   2114 }, when = lambda ctx:
   2115     cnfg.screen_has_focus and
   2116     isKBtype('Chromebook', map='mmap GUI Cbk')(ctx) and
   2117     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2118 )
   2119 modmap("Cond modmap - GUI - Win kbd - multi_lang OFF", {
   2120     # - Default Mac/Win
   2121     # - Default Win
   2122     Key.RIGHT_ALT:              Key.RIGHT_CTRL,                 # WinMac - Multi-language (Remove)
   2123     Key.RIGHT_META:             Key.RIGHT_ALT,                  # WinMac - Multi-language (Remove)
   2124     Key.RIGHT_CTRL:             Key.RIGHT_META,                 # WinMac - Multi-language (Remove)
   2125 }, when = lambda ctx:
   2126     not cnfg.multi_lang and
   2127     cnfg.screen_has_focus and
   2128     isKBtype('Windows', map='mmap GUI Win ML-OFF')(ctx) and
   2129     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2130 )
   2131 modmap("Cond modmap - GUI - Win kbd", {
   2132     # - Default Mac/Win
   2133     # - Default Win
   2134     Key.LEFT_CTRL:              Key.LEFT_META,                  # WinMac
   2135     Key.LEFT_META:              Key.LEFT_ALT,                   # WinMac
   2136     Key.LEFT_ALT:               Key.RIGHT_CTRL,                 # WinMac
   2137 }, when = lambda ctx:
   2138     cnfg.screen_has_focus and
   2139     isKBtype('Windows', map='mmap GUI Win')(ctx) and
   2140     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2141 )
   2142 modmap("Cond modmap - GUI - Mac kbd - multi_lang OFF", {
   2143     # - Mac Only
   2144     Key.RIGHT_META:             Key.RIGHT_CTRL,                 # Mac - Multi-language (Remove)
   2145     Key.RIGHT_CTRL:             Key.RIGHT_META,                 # Mac - Multi-language (Remove)
   2146 }, when = lambda ctx:
   2147     not cnfg.multi_lang and
   2148     cnfg.screen_has_focus and
   2149     isKBtype('Apple', map='mmap GUI Apple ML-OFF')(ctx) and
   2150     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2151 )
   2152 modmap("Cond modmap - GUI - Mac kbd", {
   2153     # - Mac Only
   2154     Key.LEFT_CTRL:              Key.LEFT_META,                  # Mac
   2155     Key.LEFT_META:              Key.RIGHT_CTRL,                 # Mac
   2156 }, when = lambda ctx:
   2157     cnfg.screen_has_focus and
   2158     isKBtype('Apple', map='mmap GUI Apple')(ctx) and
   2159     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   2160 )
   2161 
   2162 
   2163 # [Global Terminals conditional modmaps] Change modifier keys in certain applications
   2164 modmap("Cond modmap - Terms - IBM kbd - multi_lang OFF", {
   2165     # - IBM - Multi-language
   2166     Key.RIGHT_ALT:              Key.RIGHT_CTRL,                 # IBM - Multi-language (Remove)
   2167 }, when = lambda ctx:
   2168     not cnfg.multi_lang and
   2169     cnfg.screen_has_focus and
   2170     isKBtype('IBM', map='mmap terms IBM ML-OFF')(ctx) and
   2171     matchProps(clas=termStr)(ctx)
   2172 )
   2173 modmap("Cond modmap - Terms - IBM kbd", {
   2174     # - IBM
   2175     Key.CAPSLOCK:               Key.LEFT_ALT,                   # IBM
   2176     # Left Ctrl stays Left Ctrl
   2177     Key.LEFT_ALT:               Key.RIGHT_CTRL,                 # IBM
   2178     # Right Meta does not exist on IBM keyboards
   2179     Key.RIGHT_CTRL:             Key.RIGHT_ALT,                  # IBM
   2180 }, when = lambda ctx:
   2181     cnfg.screen_has_focus and
   2182     isKBtype('IBM', map='mmap terms IBM')(ctx) and
   2183     matchProps(clas=termStr)(ctx)
   2184 )
   2185 modmap("Cond modmap - Terms - Cbk kbd - multi_lang OFF", {
   2186     # - Chromebook
   2187     Key.RIGHT_ALT:              Key.RIGHT_CTRL,                 # Chromebook - Multi-language (Remove)
   2188 }, when = lambda ctx:
   2189     not cnfg.multi_lang and
   2190     cnfg.screen_has_focus and
   2191     isKBtype('Chromebook', map='mmap terms Cbk ML-OFF')(ctx) and
   2192     matchProps(clas=termStr)(ctx)
   2193 )
   2194 modmap("Cond modmap - Terms - Cbk kbd", {
   2195     # - Chromebook
   2196     # Left Ctrl Stays Left Ctrl
   2197     Key.LEFT_META:              Key.LEFT_ALT,                   # Chromebook
   2198     Key.LEFT_ALT:               Key.RIGHT_CTRL,                 # Chromebook
   2199     # Right Meta does not exist on chromebooks
   2200     Key.RIGHT_CTRL:             Key.RIGHT_ALT,                  # Chromebook
   2201 }, when = lambda ctx:
   2202     cnfg.screen_has_focus and
   2203     isKBtype('Chromebook', map='mmap terms Cbk')(ctx) and
   2204     matchProps(clas=termStr)(ctx)
   2205 )
   2206 modmap("Cond modmap - Terms - Win kbd - multi_lang OFF", {
   2207     # - Default Mac/Win
   2208     # - Default Win
   2209     Key.RIGHT_ALT:              Key.RIGHT_CTRL,                 # WinMac - Multi-language (Remove)
   2210     Key.RIGHT_META:             Key.RIGHT_ALT,                  # WinMac - Multi-language (Remove)
   2211     Key.RIGHT_CTRL:             Key.LEFT_CTRL,                  # WinMac - Multi-language (Remove)
   2212 }, when = lambda ctx:
   2213     not cnfg.multi_lang and
   2214     cnfg.screen_has_focus and
   2215     isKBtype('Windows', map='mmap terms Win ML-OFF')(ctx) and
   2216     matchProps(clas=termStr)(ctx)
   2217 )
   2218 modmap("Cond modmap - Terms - Win kbd", {
   2219     # - Default Mac/Win
   2220     # - Default Win
   2221     Key.LEFT_CTRL:              Key.LEFT_CTRL,                  # WinMac
   2222     Key.LEFT_META:              Key.LEFT_ALT,                   # WinMac
   2223     Key.LEFT_ALT:               Key.RIGHT_CTRL,                 # WinMac
   2224 }, when = lambda ctx:
   2225     cnfg.screen_has_focus and
   2226     isKBtype('Windows', map='mmap terms Win')(ctx) and
   2227     matchProps(clas=termStr)(ctx)
   2228 )
   2229 modmap("Cond modmap - Terms - Mac kbd - multi_lang OFF", {
   2230     # - Mac Only
   2231     # Left Ctrl Stays Left Ctrl
   2232     Key.RIGHT_META:             Key.RIGHT_CTRL,                 # Mac - Multi-language (Remove)
   2233     Key.RIGHT_CTRL:             Key.LEFT_CTRL,                  # Mac - Multi-language (Remove)
   2234 }, when = lambda ctx:
   2235     not cnfg.multi_lang and
   2236     cnfg.screen_has_focus and
   2237     isKBtype('Apple', map='mmap terms Apple ML-OFF')(ctx) and
   2238     matchProps(clas=termStr)(ctx)
   2239 )
   2240 modmap("Cond modmap - Terms - Mac kbd", {
   2241     # - Mac Only
   2242     # Left Ctrl Stays Left Ctrl
   2243     Key.LEFT_CTRL:              Key.LEFT_CTRL,                  # Mac (self-modmap)
   2244     Key.LEFT_ALT:               Key.LEFT_ALT,                   # Mac (self-modmap)
   2245     Key.LEFT_META:              Key.RIGHT_CTRL,                 # Mac
   2246     Key.RIGHT_ALT:              Key.RIGHT_ALT,                  # Mac (self-modmap)
   2247 }, when = lambda ctx:
   2248     cnfg.screen_has_focus and
   2249     isKBtype('Apple', map='mmap terms Apple')(ctx) and
   2250     matchProps(clas=termStr)(ctx)
   2251 )
   2252 
   2253 
   2254 
   2255 # Suggested location for adding custom modmaps for personal use.
   2256 ###################################################################################################
   2257 ###  SLICE_MARK_START: user_custom_modmaps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   2258 
   2259 
   2260 
   2261 ###  SLICE_MARK_END: user_custom_modmaps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   2262 ###################################################################################################
   2263 
   2264 
   2265 
   2266 ##########################  FORCED NUMPAD  ############################
   2267 ###                                                                 ###
   2268 ###                                                                 ###
   2269 ###      ███    ██ ██    ██ ███    ███ ██████   █████  ██████       ###
   2270 ###      ████   ██ ██    ██ ████  ████ ██   ██ ██   ██ ██   ██      ###
   2271 ###      ██ ██  ██ ██    ██ ██ ████ ██ ██████  ███████ ██   ██      ###
   2272 ###      ██  ██ ██ ██    ██ ██  ██  ██ ██      ██   ██ ██   ██      ###
   2273 ###      ██   ████  ██████  ██      ██ ██      ██   ██ ██████       ###
   2274 ###                                                                 ###
   2275 ###                                                                 ###
   2276 #######################################################################
   2277 
   2278 # Force the numpad to always be a numpad, like a Mac keyboard on macOS
   2279 # Numlock key becomes "Clear" key for use with calculator (sends Escape)
   2280 # Toggle feature on/off with Option+Numlock (Fn+Numlock might work on
   2281 # Apple keyboards that have Fn key)
   2282 
   2283 
   2284 def toggle_forced_numpad():
   2285     """Toggle the Forced Numpad feature on or off."""
   2286     cnfg.forced_numpad = not cnfg.forced_numpad
   2287     cnfg.save_settings()
   2288     ntfy.forced_numpad(cnfg.forced_numpad)
   2289 
   2290 
   2291 def isNumlockClearKey():
   2292     """NumLock key is Clear (Esc) if Forced Numpad feature is enabled."""
   2293     return C("Esc") if cnfg.forced_numpad else C("Numlock")
   2294 
   2295 
   2296 
   2297 ###########################  OPTSPECIALCHARS  ##############################
   2298 ###                                                                      ###
   2299 ###                                                                      ###
   2300 ###       ██████  ██████  ████████ ███████ ██████  ███████  ██████       ###
   2301 ###      ██    ██ ██   ██    ██    ██      ██   ██ ██      ██            ###
   2302 ###      ██    ██ ██████     ██    ███████ ██████  █████   ██            ###
   2303 ###      ██    ██ ██         ██         ██ ██      ██      ██            ###
   2304 ###       ██████  ██         ██    ███████ ██      ███████  ██████       ###
   2305 ###                                                                      ###
   2306 ###                                                                      ###
   2307 ############################################################################
   2308 
   2309 ###########   START OF OPTION KEY SPECIAL CHARACTER ENTRY SCHEME    #############
   2310 #################################################################################
   2311 ### Full list of special characters on Apple US and ABC Extended keyboard layouts:
   2312 ### https://github.org/RedBearAK/optspecialchars
   2313 
   2314 
   2315 def apple_logo_alert():
   2316     """Show a notification about needing Baskerville Old Face font for displaying Apple logo"""
   2317     if applelogoalert_enabled:
   2318         ntfy.apple_logo()
   2319 
   2320 
   2321 
   2322 ######################################################################################
   2323 ###                                                                                ###
   2324 ###                                                                                ###
   2325 ###      ██████  ███████  █████  ██████      ██   ██ ███████ ██    ██ ███████      ###
   2326 ###      ██   ██ ██      ██   ██ ██   ██     ██  ██  ██       ██  ██  ██           ###
   2327 ###      ██   ██ █████   ███████ ██   ██     █████   █████     ████   ███████      ###
   2328 ###      ██   ██ ██      ██   ██ ██   ██     ██  ██  ██         ██         ██      ###
   2329 ###      ██████  ███████ ██   ██ ██████      ██   ██ ███████    ██    ███████      ###
   2330 ###                                                                                ###
   2331 ###                                                                                ###
   2332 ######################################################################################
   2333 
   2334 # variables to store dead keys diacritic accent character Unicode address
   2335 ac_Chr_main = None
   2336 _ac_Chr_copy = None
   2337 
   2338 
   2339 def set_dead_key_char(hex_unicode_addr):
   2340     """
   2341     Set the value of the dead keys accent character
   2342     variable, and its alternate. Does not clear the
   2343     value of the alternate variable if input is
   2344     falsy (such as None or 0x0000 or 0). This allows
   2345     the value to be used after the tripwire keymap
   2346     clears the main variable value.
   2347     """
   2348     def _set_dead_key_char():
   2349         global ac_Chr_main
   2350         global _ac_Chr_copy
   2351         # if hex_unicode_addr is None or hex_unicode_addr == 0x0000:
   2352         #     pass
   2353         # else:
   2354         if hex_unicode_addr:
   2355             _ac_Chr_copy = hex_unicode_addr
   2356         ac_Chr_main = hex_unicode_addr
   2357     #
   2358     return _set_dead_key_char
   2359 
   2360 
   2361 def get_dead_key_char():
   2362     """Get the value of the alternate dead key accent character
   2363         variable, and print/type the resulting Unicode character."""
   2364     def _get_dead_key_char():
   2365         global _ac_Chr_copy
   2366         return UC(_ac_Chr_copy)
   2367     #
   2368     return _get_dead_key_char
   2369 
   2370 
   2371 def setDK(*args, **kwargs):
   2372     """wrapper for `set_dead_key_char` function, to provide shorter name"""
   2373     return set_dead_key_char(*args, **kwargs)
   2374 
   2375 
   2376 def getDK():
   2377     """wrapper for `get_dead_key_char` function, to provide shorter name"""
   2378     return get_dead_key_char()
   2379 
   2380 
   2381 # setDK = set_dead_key_char
   2382 # getDK = get_dead_key_char
   2383 
   2384 deadkeys_ABC = [
   2385     # Dead keys on ABC Extended keyboard layout (25, plus substitutes for problematic chars)
   2386     0x0060,                     # Dead Keys Accent: Grave
   2387     0x02C6,                     # Dead Keys Accent: Circumflex
   2388     0x02D9,                     # Dead Keys Accent: Dot Above
   2389     0x00B4,                     # Dead Keys Accent: Acute
   2390 
   2391     ###  Combining Double Grave has issues (spacing behavior) - substituting {U+02F5}
   2392     0x030F,                     # Dead Keys Accent: Combining Double Grave
   2393     ###  Substitute for Double Grave: Modifier Letter Middle Double Grave Accent
   2394     0x02F5,                     # Dead Keys Accent: Double Grave - substitute for {U+030F}
   2395 
   2396     0x00A8,                     # Dead Keys Accent: Umlaut/Diaeresis
   2397     0x02BC,                     # Dead Keys Accent: Apostrophe/Horn
   2398     0x002C,                     # Dead Keys Accent: Comma Below
   2399     0x00AF,                     # Dead Keys Accent: Macron/Line Above
   2400 
   2401     ###  Combining Inverted Breve has issues (spacing behavior) - substituting {U+1D16}
   2402     0x0311,                     # Dead Keys Accent: Combining Inverted Breve
   2403     ###  Substitute for Inverted Breve: Latin Small Letter Top Half O
   2404     0x1D16,                     # Dead Keys Accent: Inverted Breve - substitute for {U+0311}
   2405 
   2406     ###  Combining Tilde Below has issues (spacing behavior) - substituting {U+02F7}
   2407     0x0330,                     # Dead Keys Accent: Combining Tilde Below
   2408     ###  Substitute for Tilde Below: Modifier Letter Low Tilde
   2409 
   2410     0x02F7,                     # Dead Keys Accent: Tilde Below
   2411     0x2038,                     # Dead Keys Accent: Caret/Circumflex Below
   2412     0x02CD,                     # Dead Keys Accent: Low Macron/Line Below
   2413     0x02DD,                     # Dead Keys Accent: Double Acute
   2414     0x02DA,                     # Dead Keys Accent: Ring Above
   2415     0x002D,                     # Dead Keys Accent: Stroke/Hyphen-Minus
   2416     0x2116,                     # Dead Keys Accent: Numero Sign
   2417     0x02C0,                     # Dead Keys Accent: Hook Above/Glottal Stop
   2418     0x002E,                     # Dead Keys Accent: Dot Below
   2419     0x00B8,                     # Dead Keys Accent: Cedilla/Cedille
   2420     0x02C7,                     # Dead Keys Accent: Caron/hacek
   2421     0x02D8,                     # Dead Keys Accent: Breve
   2422     0x02DC,                     # Dead Keys Accent: Tilde
   2423     0x02DB,                     # Dead Keys Accent: Ogonek
   2424     0x0294,                     # Dead Keys Accent: Hook
   2425 ]
   2426 
   2427 deadkeys_US = [
   2428     # Dead keys on standard US keyboard layout (5)
   2429     0x0060,                     # Dead Keys Accent: Grave
   2430     0x00B4,                     # Dead Keys Accent: Acute
   2431     0x00A8,                     # Dead Keys Accent: Umlaut/Diaeresis
   2432     0x02C6,                     # Dead Keys Accent: Circumflex
   2433     0x02DC,                     # Dead Keys Accent: Tilde
   2434 ]
   2435 
   2436 # Join the two dead keys lists together
   2437 deadkeys_list = []
   2438 deadkeys_list.extend(deadkeys_ABC)
   2439 deadkeys_list.extend(deadkeys_US)
   2440 
   2441 
   2442 #####################################
   2443 ###   DEAD KEYS KEYMAPS - START   ###
   2444 #####################################
   2445 # Dead Keys conditional keymaps
   2446 # only active when the dead key variable matches
   2447 # and the layout variable matches (US or ABC)
   2448 
   2449 #################################################
   2450 ###  DEAD KEYS KEYMAPS - ABC EXTENDED LAYOUT  ###
   2451 #################################################
   2452 
   2453 keymap("DK-ABC - Grave", {
   2454     # Option+Grave              {U+0060}
   2455     # Valid keys:
   2456     # a e i n o u v w y
   2457     # A E I N O U V W Y
   2458     C("A"):                     UC(0x00E0),                     # à Latin Small Letter A with Grave
   2459     C("E"):                     UC(0x00E8),                     # è Latin Small Letter E with Grave
   2460     C("I"):                     UC(0x00EC),                     # ì Latin Small Letter I with Grave
   2461     C("N"):                     UC(0x01F9),                     # ǹ Latin Small Letter N with Grave
   2462     C("O"):                     UC(0x00F2),                     # ò Latin Small Letter O with Grave
   2463     C("U"):                     UC(0x00F9),                     # ù Latin Small Letter U with Grave
   2464     C("V"):                     UC(0x01DC),                     # ǜ Latin Small Letter U w/Diaeresis and Grave
   2465     C("W"):                     UC(0x1E81),                     # ẁ Latin Small Letter W with Grave
   2466     C("Y"):                     UC(0x1EF3),                     # ỳ Latin Small Letter Y with Grave
   2467     C("Shift-A"):               UC(0x00C0),                     # À Latin Capital Letter A with Grave
   2468     C("Shift-E"):               UC(0x00C8),                     # È Latin Capital Letter E with Grave
   2469     C("Shift-I"):               UC(0x00CC),                     # Ì Latin Capital Letter I with Grave
   2470     C("Shift-N"):               UC(0x01F8),                     # Ǹ Latin Capital Letter N with Grave
   2471     C("Shift-O"):               UC(0x00D2),                     # Ò Latin Capital Letter O with Grave
   2472     C("Shift-U"):               UC(0x00D9),                     # Ù Latin Capital Letter U with Grave
   2473     C("Shift-V"):               UC(0x01DB),                     # Ǜ Latin Capital Letter U w/Diaeresis and Grave
   2474     C("Shift-W"):               UC(0x1E80),                     # Ẁ Latin Capital Letter W with Grave
   2475     C("Shift-Y"):               UC(0x1EF2),                     # Ỳ Latin Capital Letter Y with Grave
   2476 }, when = lambda _: ac_Chr_main == 0x0060 and cnfg.optspec_layout == 'ABC')
   2477 
   2478 keymap("DK-ABC - Circumflex", {
   2479     # Option+6                  {U+02C6}
   2480     # Valid keys:
   2481     # a c e g h i j m n o s u w y z
   2482     # A C E G H I J M N O S U W Y Z
   2483     C("A"):                     UC(0x00E2),                     # â Latin Small Letter A with Circumflex
   2484     C("C"):                     UC(0x0109),                     # ĉ Latin Small Letter C with Circumflex
   2485     C("E"):                     UC(0x00EA),                     # ê Latin Small Letter E with Circumflex
   2486     C("G"):                     UC(0x011D),                     # ĝ Latin Small Letter G with Circumflex
   2487     C("H"):                     UC(0x0125),                     # ĥ Latin Small Letter H with Circumflex
   2488     C("I"):                     UC(0x00EE),                     # î Latin Small Letter I with Circumflex
   2489     C("J"):                     UC(0x0135),                     # ĵ Latin Small Letter J with Circumflex
   2490     C("M"):                    [UC(0x006D),UC(0x0302)],         # m̂ Latin Small Letter M with Circumflex
   2491     C("N"):                    [UC(0x006E),UC(0x0302)],         # n̂ Latin Small Letter N with Circumflex
   2492     C("O"):                     UC(0x00F4),                     # ô Latin Small Letter O with Circumflex
   2493     C("S"):                     UC(0x015D),                     # ŝ Latin Small Letter S with Circumflex
   2494     C("U"):                     UC(0x00FB),                     # û Latin Small Letter U with Circumflex
   2495     C("W"):                     UC(0x0175),                     # ŵ Latin Small Letter W with Circumflex
   2496     C("Y"):                     UC(0x0177),                     # ŷ Latin Small Letter Y with Circumflex
   2497     C("Z"):                     UC(0x1E91),                     # ẑ Latin Small Letter Z with Circumflex
   2498     C("Shift-A"):               UC(0x00C2),                     # Â Latin Capital Letter A with Circumflex
   2499     C("Shift-C"):               UC(0x0108),                     # Ĉ Latin Capital Letter C with Circumflex
   2500     C("Shift-E"):               UC(0x00CA),                     # Ê Latin Capital Letter E with Circumflex
   2501     C("Shift-G"):               UC(0x011C),                     # Ĝ Latin Capital Letter G with Circumflex
   2502     C("Shift-H"):               UC(0x0124),                     # Ĥ Latin Capital Letter H with Circumflex
   2503     C("Shift-I"):               UC(0x00CE),                     # Î Latin Capital Letter I with Circumflex
   2504     C("Shift-J"):               UC(0x0134),                     # Ĵ Latin Capital Letter J with Circumflex
   2505     C("Shift-M"):              [UC(0x004D),UC(0x0302)],         # M̂ Latin Capital Letter M with Circumflex
   2506     C("Shift-N"):              [UC(0x004E),UC(0x0302)],         # N̂ Latin Capital Letter N with Circumflex
   2507     C("Shift-O"):               UC(0x00D4),                     # Ô Latin Capital Letter O with Circumflex
   2508     C("Shift-S"):               UC(0x015C),                     # Ŝ Latin Capital Letter S with Circumflex
   2509     C("Shift-U"):               UC(0x00DB),                     # Û Latin Capital Letter U with Circumflex
   2510     C("Shift-W"):               UC(0x0174),                     # Ŵ Latin Capital Letter W with Circumflex
   2511     C("Shift-Y"):               UC(0x0176),                     # Ŷ Latin Capital Letter Y with Circumflex
   2512     C("Shift-Z"):               UC(0x1E90),                     # Ẑ Latin Capital Letter Z with Circumflex
   2513 }, when = lambda _: ac_Chr_main == 0x02C6 and cnfg.optspec_layout == 'ABC')
   2514 
   2515 keymap("DK-ABC - Dot Above", {
   2516     # Option+W                  {U+02D9}
   2517     # Valid keys:
   2518     # a b c d e f g h i m n o p r s t w x y z
   2519     # A B C D E F G H I M N O P R S T W X Y Z
   2520     C("A"):                     UC(0x0227),                     # ȧ Latin Small Letter A with Dot Above
   2521     C("B"):                     UC(0x1E03),                     # ḃ Latin Small Letter B with Dot Above
   2522     C("C"):                     UC(0x010B),                     # ċ Latin Small Letter C with Dot Above
   2523     C("D"):                     UC(0x1E0B),                     # ḋ Latin Small Letter D with Dot Above
   2524     C("E"):                     UC(0x0117),                     # ė Latin Small Letter E with Dot Above
   2525     C("F"):                     UC(0x1E1F),                     # ḟ Latin Small Letter F with Dot Above
   2526     C("G"):                     UC(0x0121),                     # ġ Latin Small Letter G with Dot Above
   2527     C("H"):                     UC(0x1E23),                     # ḣ Latin Small Letter H with Dot Above
   2528     C("I"):                     UC(0x0131),                     # ı Latin Small Letter Dotless I
   2529     C("M"):                     UC(0x1E41),                     # ṁ Latin Small Letter M with Dot Above
   2530     C("N"):                     UC(0x1E45),                     # ṅ Latin Small Letter N with Dot Above
   2531     C("O"):                     UC(0x022F),                     # ȯ Latin Small Letter O with Dot Above
   2532     C("P"):                     UC(0x1E57),                     # ṗ Latin Small Letter P with Dot Above
   2533     C("R"):                     UC(0x1E59),                     # ṙ Latin Small Letter R with Dot Above
   2534     C("S"):                     UC(0x1E61),                     # ṡ Latin Small Letter S with Dot Above
   2535     C("T"):                     UC(0x1E6B),                     # ṫ Latin Small Letter T with Dot Above
   2536     C("W"):                     UC(0x1E87),                     # ẇ Latin Small Letter W with Dot Above
   2537     C("X"):                     UC(0x1E8B),                     # ẋ Latin Small Letter X with Dot Above
   2538     C("Y"):                     UC(0x1E8F),                     # ẏ Latin Small Letter Y with Dot Above
   2539     C("Z"):                     UC(0x017C),                     # ż Latin Small Letter Z with Dot Above
   2540     C("Shift-A"):               UC(0x0226),                     # Ȧ Latin Capital Letter A with Dot Above
   2541     C("Shift-B"):               UC(0x1E02),                     # Ḃ Latin Capital Letter B with Dot Above
   2542     C("Shift-C"):               UC(0x010A),                     # Ċ Latin Capital Letter C with Dot Above
   2543     C("Shift-D"):               UC(0x1E0A),                     # Ḋ Latin Capital Letter D with Dot Above
   2544     C("Shift-E"):               UC(0x0116),                     # Ė Latin Capital Letter E with Dot Above
   2545     C("Shift-F"):               UC(0x1E1E),                     # Ḟ Latin Capital Letter F with Dot Above
   2546     C("Shift-G"):               UC(0x0120),                     # Ġ Latin Capital Letter G with Dot Above
   2547     C("Shift-H"):               UC(0x1E22),                     # Ḣ Latin Capital Letter H with Dot Above
   2548     C("Shift-I"):               UC(0x0130),                     # İ Latin Capital Letter I with Dot Above
   2549     C("Shift-M"):               UC(0x1E40),                     # Ṁ Latin Capital Letter M with Dot Above
   2550     C("Shift-N"):               UC(0x1E44),                     # Ṅ Latin Capital Letter N with Dot Above
   2551     C("Shift-O"):               UC(0x022E),                     # Ȯ Latin Capital Letter O with Dot Above
   2552     C("Shift-P"):               UC(0x1E56),                     # Ṗ Latin Capital Letter P with Dot Above
   2553     C("Shift-R"):               UC(0x1E58),                     # Ṙ Latin Capital Letter R with Dot Above
   2554     C("Shift-S"):               UC(0x1E60),                     # Ṡ Latin Capital Letter S with Dot Above
   2555     C("Shift-T"):               UC(0x1E6A),                     # Ṫ Latin Capital Letter T with Dot Above
   2556     C("Shift-W"):               UC(0x1E86),                     # Ẇ Latin Capital Letter W with Dot Above
   2557     C("Shift-X"):               UC(0x1E8A),                     # Ẋ Latin Capital Letter X with Dot Above
   2558     C("Shift-Y"):               UC(0x1E8E),                     # Ẏ Latin Capital Letter Y with Dot Above
   2559     C("Shift-Z"):               UC(0x017B),                     # Ż Latin Capital Letter Z with Dot Above
   2560 }, when = lambda _: ac_Chr_main == 0x02D9 and cnfg.optspec_layout == 'ABC')
   2561 
   2562 keymap("DK-ABC - Acute", {
   2563     # Option+E                  {U+00B4}
   2564     # Valid keys:
   2565     # a c e g i m n o p r s w y z
   2566     # A C E G I M N O P R S W Y Z
   2567     C("A"):                     UC(0x00E1),                     # á Latin Small Letter A with Acute
   2568     C("C"):                     UC(0x0107),                     # ć Latin Small Letter C with Acute
   2569     C("E"):                     UC(0x00E9),                     # é Latin Small Letter E with Acute
   2570     C("G"):                     UC(0x01F5),                     # ǵ Latin Small Letter G with Acute
   2571     C("I"):                     UC(0x00ED),                     # í Latin Small Letter I with Acute
   2572     C("M"):                     UC(0x1E3F),                     # ḿ Latin Small Letter M with Acute
   2573     C("N"):                     UC(0x0144),                     # ń Latin Small Letter N with Acute
   2574     C("O"):                     UC(0x00F3),                     # ó Latin Small Letter O with Acute
   2575     C("P"):                     UC(0x1E55),                     # ṕ Latin Small Letter P with Acute
   2576     C("R"):                     UC(0x0155),                     # ŕ Latin Small Letter R with Acute
   2577     C("S"):                     UC(0x015B),                     # ś Latin Small Letter S with Acute
   2578     C("W"):                     UC(0x1E83),                     # ẃ Latin Small Letter W with Acute
   2579     C("Y"):                     UC(0x00FD),                     # ý Latin Small Letter Y with Acute
   2580     C("Z"):                     UC(0x017A),                     # ź Latin Small Letter Z with Acute
   2581     C("Shift-A"):               UC(0x00C1),                     # Á Latin Capital Letter A with Acute
   2582     C("Shift-C"):               UC(0x0106),                     # Ć Latin Capital Letter C with Acute
   2583     C("Shift-E"):               UC(0x00C9),                     # É Latin Capital Letter E with Acute
   2584     C("Shift-G"):               UC(0x01F4),                     # Ǵ Latin Capital Letter G with Acute
   2585     C("Shift-I"):               UC(0x00CD),                     # Í Latin Capital Letter I with Acute
   2586     C("Shift-M"):               UC(0x1E3E),                     # Ḿ Latin Capital Letter M with Acute
   2587     C("Shift-N"):               UC(0x0143),                     # Ń Latin Capital Letter N with Acute
   2588     C("Shift-O"):               UC(0x00D3),                     # Ó Latin Capital Letter O with Acute
   2589     C("Shift-P"):               UC(0x1E54),                     # Ṕ Latin Capital Letter P with Acute
   2590     C("Shift-R"):               UC(0x0154),                     # Ŕ Latin Capital Letter R with Acute
   2591     C("Shift-S"):               UC(0x015A),                     # Ś Latin Capital Letter S with Acute
   2592     C("Shift-W"):               UC(0x1E82),                     # Ẃ Latin Capital Letter W with Acute
   2593     C("Shift-Y"):               UC(0x00DD),                     # Ý Latin Capital Letter Y with Acute
   2594     C("Shift-Z"):               UC(0x0179),                     # Ź Latin Capital Letter Z with Acute
   2595 }, when = lambda _: ac_Chr_main == 0x00B4 and cnfg.optspec_layout == 'ABC')
   2596 
   2597 keymap("DK-ABC - Double Grave", {
   2598     # Shift+Option+Y            {U+030F} [uses {U+02F5} Modifier Letter Middle Double Grave Accent]
   2599     # Valid keys:
   2600     # a e i o r u
   2601     # A E I O R U
   2602     C("A"):                     UC(0x0201),                     # ȁ Latin Small Letter A with Double Grave
   2603     C("E"):                     UC(0x0205),                     # ȅ Latin Small Letter E with Double Grave
   2604     C("I"):                     UC(0x0209),                     # ȉ Latin Small Letter I with Double Grave
   2605     C("O"):                     UC(0x020D),                     # ȍ Latin Small Letter O with Double Grave
   2606     C("R"):                     UC(0x0211),                     # ȑ Latin Small Letter R with Double Grave
   2607     C("U"):                     UC(0x0215),                     # ȕ Latin Small Letter U with Double Grave
   2608     C("Shift-A"):               UC(0x0200),                     # Ȁ Latin Capital Letter A with Double Grave
   2609     C("Shift-E"):               UC(0x0204),                     # Ȅ Latin Capital Letter E with Double Grave
   2610     C("Shift-I"):               UC(0x0208),                     # Ȉ Latin Capital Letter I with Double Grave
   2611     C("Shift-O"):               UC(0x020C),                     # Ȍ Latin Capital Letter O with Double Grave
   2612     C("Shift-R"):               UC(0x0210),                     # Ȑ Latin Capital Letter R with Double Grave
   2613     C("Shift-U"):               UC(0x0214),                     # Ȕ Latin Capital Letter U with Double Grave
   2614 }, when=lambda _: ac_Chr_main in [0x030F, 0x02F5] and cnfg.optspec_layout == 'ABC')
   2615 
   2616 keymap("DK-ABC - Umlaut/Diaeresis", {
   2617     # Option+U                  {U+00A8}
   2618     # Valid keys:
   2619     # a e h i o t u w x y
   2620     # A E H I O T U W X Y
   2621     C("A"):                     UC(0x00E4),                     # ä Latin Small Letter A with Diaeresis
   2622     C("E"):                     UC(0x00EB),                     # ë Latin Small Letter E with Diaeresis
   2623     C("H"):                     UC(0x1E27),                     # ḧ Latin Small Letter H with Diaeresis
   2624     C("I"):                     UC(0x00EF),                     # ï Latin Small Letter I with Diaeresis
   2625     C("O"):                     UC(0x00F6),                     # ö Latin Small Letter O with Diaeresis
   2626     C("T"):                     UC(0x1E97),                     # ẗ Latin Small Letter T with Diaeresis
   2627     C("U"):                     UC(0x00FC),                     # ü Latin Small Letter U with Diaeresis
   2628     C("W"):                     UC(0x1E85),                     # ẅ Latin Small Letter W with Diaeresis
   2629     C("X"):                     UC(0x1E8D),                     # ẍ Latin Small Letter X with Diaeresis
   2630     C("Y"):                     UC(0x00FF),                     # ÿ Latin Small Letter Y with Diaeresis
   2631     C("Shift-A"):               UC(0x00C4),                     # Ä Latin Capital Letter A with Diaeresis
   2632     C("Shift-E"):               UC(0x00CB),                     # Ë Latin Capital Letter E with Diaeresis
   2633     C("Shift-H"):               UC(0x1E26),                     # Ḧ Latin Capital Letter H with Diaeresis
   2634     C("Shift-I"):               UC(0x00CF),                     # Ï Latin Capital Letter I with Diaeresis
   2635     C("Shift-O"):               UC(0x00D6),                     # Ö Latin Capital Letter O with Diaeresis
   2636     C("Shift-T"):              [UC(0x0054),UC(0x0308)],         # T̈ Latin Capital Letter T with Diaeresis
   2637     C("Shift-U"):               UC(0x00DC),                     # Ü Latin Capital Letter U with Diaeresis
   2638     C("Shift-W"):               UC(0x1E84),                     # Ẅ Latin Capital Letter W with Diaeresis
   2639     C("Shift-X"):               UC(0x1E8C),                     # Ẍ Latin Capital Letter X with Diaeresis
   2640     C("Shift-Y"):               UC(0x0178),                     # Ÿ Latin Capital Letter Y with Diaeresis
   2641 }, when = lambda _: ac_Chr_main == 0x00A8 and cnfg.optspec_layout == 'ABC')
   2642 
   2643 keymap("DK-ABC - Apostrophe/Horn", {
   2644     # Option+I                  {U+02BC}
   2645     # Valid keys:
   2646     # o u
   2647     # O U
   2648     C("O"):                     UC(0x01A1),                     # ơ Latin Small Letter O with Horn
   2649     C("U"):                     UC(0x01B0),                     # ư Latin Small Letter U with Horn
   2650     C("Shift-O"):               UC(0x01A0),                     # Ơ Latin Capital Letter O with Horn
   2651     C("Shift-U"):               UC(0x01AF),                     # Ư Latin Capital Letter U with Horn
   2652 }, when = lambda _: ac_Chr_main == 0x02BC and cnfg.optspec_layout == 'ABC')
   2653 
   2654 keymap("DK-ABC - Comma Below", {
   2655     # Option+P                  {U+002C}
   2656     # Valid keys:
   2657     # s t
   2658     # S T
   2659     C("S"):                     UC(0x0219),                     # ș Latin Small Letter S with Comma Below
   2660     C("T"):                     UC(0x021B),                     # ț Latin Small Letter T with Comma Below
   2661     C("Shift-S"):               UC(0x0218),                     # Ș Latin Capital Letter S with Comma Below
   2662     C("Shift-T"):               UC(0x021A),                     # Ț Latin Capital Letter T with Comma Below
   2663 }, when = lambda _: ac_Chr_main == 0x002C and cnfg.optspec_layout == 'ABC')
   2664 
   2665 keymap("DK-ABC - Macron/Line Above", {
   2666     # Option+A                  {U+00AF}
   2667     # Valid keys:
   2668     # a e g i l o r s v y z
   2669     # A E G I L O R S V Y Z
   2670     C("A"):                     UC(0x0101),                     # ā Latin Small Letter A with Macron
   2671     C("E"):                     UC(0x0113),                     # ē Latin Small Letter E with Macron
   2672     C("G"):                     UC(0x1E21),                     # ḡ Latin Small Letter G with Macron
   2673     C("I"):                     UC(0x012B),                     # ī Latin Small Letter I with Macron
   2674     C("L"):         [UC(0x006C),UC(0x0304),UC(0x0323)],         # ḹ Latin Small Letter L w/Macron and Dot Below
   2675     C("O"):                     UC(0x014D),                     # ō Latin Small Letter O with Macron
   2676     C("R"):         [UC(0x0072),UC(0x0304),UC(0x0323)],         # ṝ Latin Small Letter R w/Macron and Dot Below
   2677     C("S"):                    [UC(0x0073),UC(0x0304)],         # s̄ Latin Small Letter S with Macron
   2678     C("V"):                     UC(0x01D6),                     # ǖ Latin Small Letter U with Diaeresis and Macron
   2679     C("Y"):                     UC(0x0233),                     # ȳ Latin Small Letter Y with Macron
   2680     C("Z"):                    [UC(0x007A),UC(0x0304)],         # z̄ Latin Small Letter Z with Macron
   2681     C("Shift-A"):               UC(0x0100),                     # Ā Latin Capital Letter A with Macron
   2682     C("Shift-E"):               UC(0x0112),                     # Ē Latin Capital Letter E with Macron
   2683     C("Shift-G"):               UC(0x1E20),                     # Ḡ Latin Capital Letter G with Macron
   2684     C("Shift-I"):               UC(0x012A),                     # Ī Latin Capital Letter I with Macron
   2685     C("Shift-L"):   [UC(0x004C),UC(0x0304),UC(0x0323)],         # Ḹ Latin Capital Letter L w/Macron and Dot Below
   2686     C("Shift-O"):               UC(0x014C),                     # Ō Latin Capital Letter O with Macron
   2687     C("Shift-R"):   [UC(0x0052),UC(0x0304),UC(0x0323)],         # Ṝ Latin Capital Letter R w/Macron and Dot Below
   2688     C("Shift-S"):              [UC(0x0053),UC(0x0304)],         # S̄ Latin Capital Letter S with Macron
   2689     C("Shift-V"):               UC(0x01D5),                     # Ǖ Latin Capital Letter U with Diaeresis and Macron
   2690     C("Shift-Y"):               UC(0x0232),                     # Ȳ Latin Capital Letter Y with Macron
   2691     C("Shift-Z"):              [UC(0x005A),UC(0x0304)],         # Z̄ Latin Capital Letter Z with Macron
   2692 }, when = lambda _: ac_Chr_main == 0x00AF and cnfg.optspec_layout == 'ABC')
   2693 
   2694 keymap("DK-ABC - Inverted Breve", {
   2695     # Shift+Option+S            {U+0311}    [uses {U+1D16} as a substitute]
   2696     # Valid keys:
   2697     # a e i o r u
   2698     # A E I O R U
   2699     C("A"):                     UC(0x0203),                     # ȃ Latin Small Letter A with Inverted Breve
   2700     C("E"):                     UC(0x0207),                     # ȇ Latin Small Letter E with Inverted Breve
   2701     C("I"):                     UC(0x020B),                     # ȋ Latin Small Letter I with Inverted Breve
   2702     C("O"):                     UC(0x020F),                     # ȏ Latin Small Letter O with Inverted Breve
   2703     C("R"):                     UC(0x0213),                     # ȓ Latin Small Letter R with Inverted Breve
   2704     C("U"):                     UC(0x0217),                     # ȗ Latin Small Letter U with Inverted Breve
   2705     C("Shift-A"):               UC(0x0202),                     # Ȃ Latin Capital Letter A with Inverted Breve
   2706     C("Shift-E"):               UC(0x0206),                     # Ȇ Latin Capital Letter E with Inverted Breve
   2707     C("Shift-I"):               UC(0x020A),                     # Ȋ Latin Capital Letter I with Inverted Breve
   2708     C("Shift-O"):               UC(0x020E),                     # Ȏ Latin Capital Letter O with Inverted Breve
   2709     C("Shift-R"):               UC(0x0212),                     # Ȓ Latin Capital Letter R with Inverted Breve
   2710     C("Shift-U"):               UC(0x0216),                     # Ȗ Latin Capital Letter U with Inverted Breve
   2711 }, when = lambda _: ac_Chr_main in [0x0311, 0x1D16] and cnfg.optspec_layout == 'ABC')
   2712 
   2713 keymap("DK-ABC - Tilde Below", {
   2714     # Shift+Option+F            {U+0330}    [uses {U+02F7} as a substitute]
   2715     # Valid keys:
   2716     # e i u
   2717     # E I U
   2718     C("E"):                     UC(0x1E1B),                     # ḛ Latin Small Letter E with Tilde Below
   2719     C("I"):                     UC(0x1E2D),                     # ḭ Latin Small Letter I with Tilde Below
   2720     C("U"):                     UC(0x1E75),                     # ṵ Latin Small Letter U with Tilde Below
   2721     C("Shift-E"):               UC(0x1E1A),                     # Ḛ Latin Capital Letter E with Tilde Below
   2722     C("Shift-I"):               UC(0x1E2C),                     # Ḭ Latin Capital Letter I with Tilde Below
   2723     C("Shift-U"):               UC(0x1E74),                     # Ṵ Latin Capital Letter U with Tilde Below
   2724 }, when = lambda _: ac_Chr_main in [0x0330, 0x02F7] and cnfg.optspec_layout == 'ABC')
   2725 
   2726 keymap("DK-ABC - Caret/Circumflex Below", {
   2727     # Shift+Option+G            {U+2038}
   2728     # Valid keys:
   2729     # d e l n t u
   2730     # D E L N T U
   2731     C("D"):                     UC(0x1E13),                     # ḓ Latin Small Letter D with Circumflex Below
   2732     C("E"):                     UC(0x1E19),                     # ḙ Latin Small Letter E with Circumflex Below
   2733     C("L"):                     UC(0x1E3D),                     # ḽ Latin Small Letter L with Circumflex Below
   2734     C("N"):                     UC(0x1E4B),                     # ṋ Latin Small Letter N with Circumflex Below
   2735     C("T"):                     UC(0x1E71),                     # ṱ Latin Small Letter T with Circumflex Below
   2736     C("U"):                     UC(0x1E77),                     # ṷ Latin Small Letter U with Circumflex Below
   2737     C("Shift-D"):               UC(0x1E12),                     # Ḓ Latin Capital Letter D with Circumflex Below
   2738     C("Shift-E"):               UC(0x1E18),                     # Ḙ Latin Capital Letter E with Circumflex Below
   2739     C("Shift-L"):               UC(0x1E3C),                     # Ḽ Latin Capital Letter L with Circumflex Below
   2740     C("Shift-N"):               UC(0x1E4A),                     # Ṋ Latin Capital Letter N with Circumflex Below
   2741     C("Shift-T"):               UC(0x1E70),                     # Ṱ Latin Capital Letter T with Circumflex Below
   2742     C("Shift-U"):               UC(0x1E76),                     # Ṷ Latin Capital Letter U with Circumflex Below
   2743 }, when = lambda _: ac_Chr_main == 0x2038 and cnfg.optspec_layout == 'ABC')
   2744 
   2745 keymap("DK-ABC - Low Macron/Line Below", {
   2746     # Option+H                  {U+02CD}
   2747     # Valid keys:
   2748     # b d h k l n r t z
   2749     # B D H K L N R T Z
   2750     C("B"):                     UC(0x1E07),                     # ḇ Latin Small Letter B with Line Below
   2751     C("D"):                     UC(0x1E0F),                     # ḏ Latin Small Letter D with Line Below
   2752     C("H"):                     UC(0x1E96),                     # ẖ Latin Small Letter H with Line Below
   2753     C("K"):                     UC(0x1E35),                     # ḵ Latin Small Letter K with Line Below
   2754     C("L"):                     UC(0x1E3B),                     # ḻ Latin Small Letter L with Line Below
   2755     C("N"):                     UC(0x1E49),                     # ṉ Latin Small Letter N with Line Below
   2756     C("R"):                     UC(0x1E5F),                     # ṟ Latin Small Letter R with Line Below
   2757     C("T"):                     UC(0x1E6F),                     # ṯ Latin Small Letter T with Line Below
   2758     C("Z"):                     UC(0x1E95),                     # ẕ Latin Small Letter Z with Line Below
   2759     C("Shift-B"):               UC(0x1E06),                     # Ḇ Latin Capital Letter B with Line Below
   2760     C("Shift-D"):               UC(0x1E0E),                     # Ḏ Latin Capital Letter D with Line Below
   2761     C("Shift-H"):              [UC(0x0048),UC(0x0331)],         # H̱ Latin Capital Letter H with Line Below
   2762     C("Shift-K"):               UC(0x1E34),                     # Ḵ Latin Capital Letter K with Line Below
   2763     C("Shift-L"):               UC(0x1E3A),                     # Ḻ Latin Capital Letter L with Line Below
   2764     C("Shift-N"):               UC(0x1E48),                     # Ṉ Latin Capital Letter N with Line Below
   2765     C("Shift-R"):               UC(0x1E5E),                     # Ṟ Latin Capital Letter R with Line Below
   2766     C("Shift-T"):               UC(0x1E6E),                     # Ṯ Latin Capital Letter T with Line Below
   2767     C("Shift-Z"):               UC(0x1E94),                     # Ẕ Latin Capital Letter Z with Line Below
   2768 }, when = lambda _: ac_Chr_main == 0x02CD and cnfg.optspec_layout == 'ABC')
   2769 
   2770 keymap("DK-ABC - Double Acute", {
   2771     # Option+J                  {U+02DD}
   2772     # Valid keys:
   2773     # o u
   2774     # O U
   2775     C("O"):                     UC(0x0151),                     # ő Latin Small Letter O with Double Acute
   2776     C("U"):                     UC(0x0171),                     # ű Latin Small Letter U with Double Acute
   2777     C("Shift-O"):               UC(0x0150),                     # Ő Latin Capital Letter O with Double Acute
   2778     C("Shift-U"):               UC(0x0170),                     # Ű Latin Capital Letter U with Double Acute
   2779 }, when = lambda _: ac_Chr_main == 0x02DD and cnfg.optspec_layout == 'ABC')
   2780 
   2781 keymap("DK-ABC - Ring Above", {
   2782     # Option+K                  {U+02DA}
   2783     # Valid keys:
   2784     # a e o u w y
   2785     # A E O U W Y
   2786     C("A"):                     UC(0x00E5),                     # å Latin Small Letter A with Ring Above
   2787     C("E"):                    [UC(0x0065),UC(0x030A)],         # e̊ Latin Small Letter E with Ring Above
   2788     C("O"):                    [UC(0x006F),UC(0x030A)],         # o̊ Latin Small Letter O with Ring Above
   2789     C("U"):                     UC(0x016F),                     # ů Latin Small Letter U with Ring Above
   2790     C("W"):                     UC(0x1E98),                     # ẘ Latin Small Letter W with Ring Above
   2791     C("Y"):                     UC(0x1E99),                     # ẙ Latin Small Letter Y with Ring Above
   2792     C("Shift-A"):               UC(0x00C5),                     # Å Latin Capital Letter A with Ring Above
   2793     C("Shift-E"):              [UC(0x0045),UC(0x030A)],         # E̊ Latin Capital Letter E with Ring Above
   2794     C("Shift-O"):              [UC(0x004F),UC(0x030A)],         # O̊ Latin Capital Letter O with Ring Above
   2795     C("Shift-U"):               UC(0x016E),                     # Ů Latin Capital Letter U with Ring Above
   2796     C("Shift-W"):              [UC(0x0057),UC(0x030A)],         # W̊ Latin Capital Letter W with Ring Above
   2797     C("Shift-Y"):              [UC(0x0059),UC(0x030A)],         # Y̊ Latin Capital Letter Y with Ring Above
   2798 }, when = lambda _: ac_Chr_main == 0x02DA and cnfg.optspec_layout == 'ABC')
   2799 
   2800 keymap("DK-ABC - Stroke/Hyphen-Minus", {
   2801     # Option+L                  {U+002D}
   2802     # Valid keys:
   2803     # b d g h i l o t u z
   2804     #   D G H I L O T   Z
   2805     C("B"):                     UC(0x0180),                     # ƀ Latin Small Letter B with Stroke
   2806     C("D"):                     UC(0x0111),                     # đ Latin Small Letter D with Stroke
   2807     C("G"):                     UC(0x01E5),                     # ǥ Latin Small Letter G with Stroke
   2808     C("H"):                     UC(0x0127),                     # ħ Latin Small Letter H with Stroke
   2809     C("I"):                     UC(0x0268),                     # ɨ Latin Small Letter I with Stroke
   2810     C("L"):                     UC(0x0142),                     # ł Latin Small Letter L with Stroke
   2811     C("O"):                     UC(0x0275),                     # ɵ Latin Small Letter Barred O
   2812     C("T"):                     UC(0x0167),                     # ŧ Latin Small Letter T with Stroke
   2813     C("U"):                     UC(0x0289),                     # ʉ Latin Small Letter U Bar
   2814     C("Z"):                     UC(0x01B6),                     # ƶ Latin Small Letter Z with Stroke
   2815     C("Shift-D"):               UC(0x0110),                     # Đ Latin Capital Letter D with Stroke
   2816     C("Shift-G"):               UC(0x01E4),                     # Ǥ Latin Capital Letter G with Stroke
   2817     C("Shift-H"):               UC(0x0126),                     # Ħ Latin Capital Letter H with Stroke
   2818     C("Shift-I"):               UC(0x0197),                     # Ɨ Latin Capital Letter I with Stroke
   2819     C("Shift-L"):               UC(0x0141),                     # Ł Latin Capital Letter L with Stroke
   2820     C("Shift-O"):               UC(0x019F),                     # Ɵ Latin Capital Letter O with Middle Tilde
   2821     C("Shift-T"):               UC(0x0166),                     # Ŧ Latin Capital Letter T with Stroke
   2822     C("Shift-Z"):               UC(0x01B5),                     # Ƶ Latin Capital Letter Z with Stroke
   2823 }, when = lambda _: ac_Chr_main == 0x002D and cnfg.optspec_layout == 'ABC')
   2824 
   2825 keymap("DK-ABC - Numero Sign", {
   2826     # Shift+Option+Semicolon    {U+2116}
   2827     # Valid keys:
   2828     # 2 3 5 6 7 8 (digits with Option)
   2829     # 2 3 5 6   8 (digits with Shift+Option)
   2830     # a c e g h j k m n q r s u v w y z (letters with Option)
   2831     # A C E G H J K M N Q R   U   W Y Z (letters with Shift+Option)
   2832     C("3"):                     UC(0x025B),                     # ɛ  Latin Small Letter Open E
   2833     C("5"):                     UC(0x01BD),                     # ƽ  Latin Small Letter Tone Five
   2834     C("2"):                     UC(0x01A8),                     # ƨ  Latin Small Letter Tone Two
   2835     C("6"):                     UC(0x0185),                     # ƅ  Latin Small Letter Tone Six
   2836     C("7"):                     UC(0x204A),                     # ⁊  Tironian Sign Et
   2837     C("8"):                     UC(0x0223),                     # ȣ  Latin Small Letter Ou
   2838     C("Shift-2"):               UC(0x01A7),                     # Ƨ  Latin Capital Letter Tone Two
   2839     C("Shift-3"):               UC(0x0190),                     # Ɛ  Latin Capital Letter Open E
   2840     C("Shift-5"):               UC(0x01BC),                     # Ƽ  Latin Capital Letter Tone Five
   2841     C("Shift-6"):               UC(0x0184),                     # Ƅ  Latin Capital Letter Tone Six
   2842     C("Shift-8"):               UC(0x0222),                     # Ȣ  Latin Capital Letter Ou
   2843     C("a"):                     UC(0x0259),                     # ə  Latin Small Letter Schwa
   2844     C("c"):                     UC(0x0254),                     # ɔ  Latin Small Letter Open O
   2845     C("e"):                     UC(0x01DD),                     # ǝ  Latin Small Letter Turned E
   2846     C("g"):                     UC(0x0263),                     # ɣ  Latin Small Letter Gamma
   2847     C("h"):                     UC(0x0195),                     # ƕ  Latin Small Letter Hv
   2848     C("j"):                     UC(0x019E),                     # ƞ  Latin Small Letter N with Long Right Leg
   2849     C("k"):                     UC(0x0138),                     # ĸ  Latin Small Letter Kra
   2850     C("m"):                     UC(0x026F),                     # ɯ  Latin Small Letter Turned M
   2851     C("n"):                     UC(0x014B),                     # ŋ  Latin Small Letter Eng
   2852     C("q"):                     UC(0x01A3),                     # ƣ  Latin Small Letter Oi
   2853     C("r"):                     UC(0x0280),                     # ʀ  Latin Letter Small Capital R
   2854     C("s"):                     UC(0x017F),                     # ſ  Latin Small Letter Long S
   2855     C("u"):                     UC(0x028A),                     # ʊ  Latin Small Letter Upsilon
   2856     C("v"):                     UC(0x028C),                     # ʌ  Latin Small Letter Turned V
   2857     C("w"):                     UC(0x01BF),                     # ƿ  Latin Letter Wynn
   2858     C("y"):                     UC(0x021D),                     # ȝ  Latin Small Letter Yogh
   2859     C("z"):                     UC(0x0292),                     # ʒ  Latin Small Letter Ezh
   2860     C("Shift-A"):               UC(0x018F),                     # Ə  Latin Capital Letter Schwa
   2861     C("Shift-C"):               UC(0x0186),                     # Ɔ  Latin Capital Letter Open O
   2862     C("Shift-E"):               UC(0x018E),                     # Ǝ  Latin Capital Letter Reversed E
   2863     C("Shift-G"):               UC(0x0194),                     # Ɣ  Latin Capital Letter Gamma
   2864     C("Shift-H"):               UC(0x01F6),                     # Ƕ  Latin Capital Letter Hwair
   2865     C("Shift-J"):               UC(0x0220),                     # Ƞ  Latin Capital Letter N with Long Right Leg
   2866     C("Shift-K"):              [UC(0x004B),UC(0x2019)],         # K’ Latin Capital Letter K with Apostrophe
   2867     C("Shift-M"):               UC(0x019C),                     # Ɯ  Latin Capital Letter Turned M
   2868     C("Shift-N"):               UC(0x014A),                     # Ŋ  Latin Capital Letter Eng
   2869     C("Shift-Q"):               UC(0x01A2),                     # Ƣ  Latin Capital Letter Oi
   2870     C("Shift-R"):               UC(0x01A6),                     # Ʀ  Latin Letter Yr
   2871     C("Shift-U"):               UC(0x01B1),                     # Ʊ  Latin Capital Letter Upsilon
   2872     C("Shift-W"):               UC(0x01F7),                     # Ƿ  Latin Capital Letter Wynn
   2873     C("Shift-Y"):               UC(0x021C),                     # Ȝ  Latin Capital Letter Yogh
   2874     C("Shift-Z"):               UC(0x01B7),                     # Ʒ  Latin Capital Letter Ezh
   2875 }, when = lambda _: ac_Chr_main == 0x2116 and cnfg.optspec_layout == 'ABC')
   2876 
   2877 keymap("DK-ABC - Hook Above/Glottal Stop", {
   2878     # Option+Z                  {U+02C0}
   2879     # Valid keys:
   2880     # a e i o u y
   2881     # A E I O U Y
   2882     C("A"):                     UC(0x1EA3),                     # ả  Latin Small Letter A with Hook Above
   2883     C("E"):                     UC(0x1EBB),                     # ẻ  Latin Small Letter E with Hook Above
   2884     C("I"):                     UC(0x1EC9),                     # ỉ  Latin Small Letter I with Hook Above
   2885     C("O"):                     UC(0x1ECF),                     # ỏ  Latin Small Letter O with Hook Above
   2886     C("U"):                     UC(0x1EE7),                     # ủ  Latin Small Letter U with Hook Above
   2887     C("Y"):                     UC(0x1EF7),                     # ỷ  Latin Small Letter Y with Hook Above
   2888     C("Shift-A"):               UC(0x1EA2),                     # Ả  Latin Small Letter A with Hook Above
   2889     C("Shift-E"):               UC(0x1EBA),                     # Ẻ  Latin Small Letter E with Hook Above
   2890     C("Shift-I"):               UC(0x1EC8),                     # Ỉ  Latin Small Letter I with Hook Above
   2891     C("Shift-O"):               UC(0x1ECE),                     # Ỏ  Latin Small Letter O with Hook Above
   2892     C("Shift-U"):               UC(0x1EE6),                     # Ủ  Latin Small Letter U with Hook Above
   2893     C("Shift-Y"):               UC(0x1EF6),                     # Ỷ  Latin Small Letter Y with Hook Above
   2894 }, when = lambda _: ac_Chr_main == 0x02C0 and cnfg.optspec_layout == 'ABC')
   2895 
   2896 keymap("DK-ABC - Dot Below", {
   2897     # Option+X                  {U+002E}
   2898     # Valid keys:
   2899     # a b d e h i k l m n o r s t u v w y z
   2900     # A B D E H I K L M N O R S T U V W Y Z
   2901     C("A"):                     UC(0x1EA1),                     # ạ Latin Small Letter A with Dot Below
   2902     C("B"):                     UC(0x1E05),                     # ḅ Latin Small Letter B with Dot Below
   2903     C("D"):                     UC(0x1E0D),                     # ḍ Latin Small Letter D with Dot Below
   2904     C("E"):                     UC(0x1EB9),                     # ẹ Latin Small Letter E with Dot Below
   2905     C("H"):                     UC(0x1E25),                     # ḥ Latin Small Letter H with Dot Below
   2906     C("I"):                     UC(0x1ECB),                     # ị Latin Small Letter I with Dot Below
   2907     C("K"):                     UC(0x1E33),                     # ḳ Latin Small Letter K with Dot Below
   2908     C("L"):                     UC(0x1E37),                     # ḷ Latin Small Letter L with Dot Below
   2909     C("M"):                     UC(0x1E43),                     # ṃ Latin Small Letter M with Dot Below
   2910     C("N"):                     UC(0x1E47),                     # ṇ Latin Small Letter N with Dot Below
   2911     C("O"):                     UC(0x1ECD),                     # ọ Latin Small Letter O with Dot Below
   2912     C("R"):                     UC(0x1E5B),                     # ṛ Latin Small Letter R with Dot Below
   2913     C("S"):                     UC(0x1E63),                     # ṣ Latin Small Letter S with Dot Below
   2914     C("T"):                     UC(0x1E6D),                     # ṭ Latin Small Letter T with Dot Below
   2915     C("U"):                     UC(0x1EE5),                     # ụ Latin Small Letter U with Dot Below
   2916     C("V"):                     UC(0x1E7F),                     # ṿ Latin Small Letter V with Dot Below
   2917     C("W"):                     UC(0x1E89),                     # ẉ Latin Small Letter W with Dot Below
   2918     C("Y"):                     UC(0x1EF5),                     # ỵ Latin Small Letter Y with Dot Below
   2919     C("Z"):                     UC(0x1E93),                     # ẓ Latin Small Letter Z with Dot Below
   2920     C("Shift-A"):               UC(0x1EA0),                     # Ạ Latin Capital Letter A with Dot Below
   2921     C("Shift-B"):               UC(0x1E04),                     # Ḅ Latin Capital Letter B with Dot Below
   2922     C("Shift-D"):               UC(0x1E0C),                     # Ḍ Latin Capital Letter D with Dot Below
   2923     C("Shift-E"):               UC(0x1EB8),                     # Ẹ Latin Capital Letter E with Dot Below
   2924     C("Shift-H"):               UC(0x1E24),                     # Ḥ Latin Capital Letter H with Dot Below
   2925     C("Shift-I"):               UC(0x1ECA),                     # Ị Latin Capital Letter I with Dot Below
   2926     C("Shift-K"):               UC(0x1E32),                     # Ḳ Latin Capital Letter K with Dot Below
   2927     C("Shift-L"):               UC(0x1E36),                     # Ḷ Latin Capital Letter L with Dot Below
   2928     C("Shift-M"):               UC(0x1E42),                     # Ṃ Latin Capital Letter M with Dot Below
   2929     C("Shift-N"):               UC(0x1E46),                     # Ṇ Latin Capital Letter N with Dot Below
   2930     C("Shift-O"):               UC(0x1ECC),                     # Ọ Latin Capital Letter O with Dot Below
   2931     C("Shift-R"):               UC(0x1E5A),                     # Ṛ Latin Capital Letter R with Dot Below
   2932     C("Shift-S"):               UC(0x1E62),                     # Ṣ Latin Capital Letter S with Dot Below
   2933     C("Shift-T"):               UC(0x1E6C),                     # Ṭ Latin Capital Letter T with Dot Below
   2934     C("Shift-U"):               UC(0x1EE4),                     # Ụ Latin Capital Letter U with Dot Below
   2935     C("Shift-V"):               UC(0x1E7E),                     # Ṿ Latin Capital Letter V with Dot Below
   2936     C("Shift-W"):               UC(0x1E88),                     # Ẉ Latin Capital Letter W with Dot Below
   2937     C("Shift-Y"):               UC(0x1EF4),                     # Ỵ Latin Capital Letter Y with Dot Below
   2938     C("Shift-Z"):               UC(0x1E92),                     # Ẓ Latin Capital Letter Z with Dot Below
   2939 }, when = lambda _: ac_Chr_main == 0x002E and cnfg.optspec_layout == 'ABC')
   2940 
   2941 keymap("DK-ABC - Cedilla/Cedille", {
   2942     # Option+C                  {U+00B8}
   2943     # Valid keys:
   2944     # c d e g h k l n r s t z
   2945     # C D E G H K L N R S T Z
   2946     C("C"):                     UC(0x00E7),                     # ç Latin Small Letter C with Cedilla
   2947     C("D"):                     UC(0x1E11),                     # ḑ Latin Small Letter D with Cedilla
   2948     C("E"):                     UC(0x0229),                     # ȩ Latin Small Letter E with Cedilla
   2949     C("G"):                     UC(0x0123),                     # ģ Latin Small Letter G with Cedilla
   2950     C("H"):                     UC(0x1E29),                     # ḩ Latin Small Letter H with Cedilla
   2951     C("K"):                     UC(0x0137),                     # ķ Latin Small Letter K with Cedilla
   2952     C("L"):                     UC(0x013C),                     # ļ Latin Small Letter L with Cedilla
   2953     C("N"):                     UC(0x0146),                     # ņ Latin Small Letter N with Cedilla
   2954     C("R"):                     UC(0x0157),                     # ŗ Latin Small Letter R with Cedilla
   2955     C("S"):                     UC(0x015F),                     # ş Latin Small Letter S with Cedilla
   2956     C("T"):                     UC(0x0163),                     # ţ Latin Small Letter T with Cedilla
   2957     C("Z"):                    [UC(0x007A),UC(0x0327)],         # z̧ Latin Small Letter Z with Cedilla
   2958     C("Shift-C"):               UC(0x00C7),                     # Ç Latin Capital Letter C with Cedilla
   2959     C("Shift-D"):               UC(0x1E10),                     # Ḑ Latin Capital Letter D with Cedilla
   2960     C("Shift-E"):               UC(0x0228),                     # Ȩ Latin Capital Letter E with Cedilla
   2961     C("Shift-G"):               UC(0x0122),                     # Ģ Latin Capital Letter G with Cedilla
   2962     C("Shift-H"):               UC(0x1E28),                     # Ḩ Latin Capital Letter H with Cedilla
   2963     C("Shift-K"):               UC(0x0136),                     # Ķ Latin Capital Letter K with Cedilla
   2964     C("Shift-L"):               UC(0x013B),                     # Ļ Latin Capital Letter L with Cedilla
   2965     C("Shift-N"):               UC(0x0145),                     # Ņ Latin Capital Letter N with Cedilla
   2966     C("Shift-R"):               UC(0x0156),                     # Ŗ Latin Capital Letter R with Cedilla
   2967     C("Shift-S"):               UC(0x015E),                     # Ş Latin Capital Letter S with Cedilla
   2968     C("Shift-T"):               UC(0x0162),                     # Ţ Latin Capital Letter T with Cedilla
   2969     C("Shift-Z"):              [UC(0x005A),UC(0x0327)],         # Z̧ Latin Capital Letter Z with Cedilla
   2970 }, when = lambda _: ac_Chr_main == 0x00B8 and cnfg.optspec_layout == 'ABC')
   2971 
   2972 keymap("DK-ABC - Caron/hacek", {
   2973     # Option+V                  {U+02C7}
   2974     # Valid keys:
   2975     # a c d e g h i j k l n o r s t u v x z
   2976     # A C D E G H I J K L N O R S T U V X Z
   2977     C("A"):                     UC(0x01CE),                     # ǎ Latin Small Letter A with Caron
   2978     C("C"):                     UC(0x010D),                     # č Latin Small Letter C with Caron
   2979     C("D"):                     UC(0x010F),                     # ď Latin Small Letter D with Caron
   2980     C("E"):                     UC(0x011B),                     # ě Latin Small Letter E with Caron
   2981     C("G"):                     UC(0x01E7),                     # ǧ Latin Small Letter G with Caron
   2982     C("H"):                     UC(0x021F),                     # ȟ Latin Small Letter H with Caron
   2983     C("I"):                     UC(0x01D0),                     # ǐ Latin Small Letter I with Caron
   2984     C("J"):                     UC(0x01F0),                     # ǰ Latin Small Letter J with Caron
   2985     C("K"):                     UC(0x01E9),                     # ǩ Latin Small Letter K with Caron
   2986     C("L"):                     UC(0x013E),                     # ľ Latin Small Letter L with Caron
   2987     C("N"):                     UC(0x0148),                     # ň Latin Small Letter N with Caron
   2988     C("O"):                     UC(0x01D2),                     # ǒ Latin Small Letter O with Caron
   2989     C("R"):                     UC(0x0159),                     # ř Latin Small Letter R with Caron
   2990     C("S"):                     UC(0x0161),                     # š Latin Small Letter S with Caron
   2991     C("T"):                     UC(0x0165),                     # ť Latin Small Letter T with Caron
   2992     C("U"):                     UC(0x01D4),                     # ǔ Latin Small Letter U with Caron
   2993     C("V"):                     UC(0x01DA),                     # ǚ Latin Small Letter U w/Diaeresis and Caron
   2994     C("X"):                    [UC(0x0292),UC(0x030C)],         # ǯ Latin Small Letter Ezh with Caron
   2995     C("Z"):                     UC(0x017E),                     # ž Latin Small Letter Z with Caron
   2996     C("Shift-A"):               UC(0x01CD),                     # Ǎ Latin Capital Letter A with Caron
   2997     C("Shift-C"):               UC(0x010C),                     # Č Latin Capital Letter C with Caron
   2998     C("Shift-D"):               UC(0x010E),                     # Ď Latin Capital Letter D with Caron
   2999     C("Shift-E"):               UC(0x011A),                     # Ě Latin Capital Letter E with Caron
   3000     C("Shift-G"):               UC(0x01E6),                     # Ǧ Latin Capital Letter G with Caron
   3001     C("Shift-H"):               UC(0x021E),                     # Ȟ Latin Capital Letter H with Caron
   3002     C("Shift-I"):               UC(0x01CF),                     # Ǐ Latin Capital Letter I with Caron
   3003     C("Shift-J"):              [UC(0x004A),UC(0x030C)],         # J̌ Latin Capital Letter J with Caron
   3004     C("Shift-K"):               UC(0x01E8),                     # Ǩ Latin Capital Letter K with Caron
   3005     C("Shift-L"):               UC(0x013D),                     # Ľ Latin Capital Letter L with Caron
   3006     C("Shift-N"):               UC(0x0147),                     # Ň Latin Capital Letter N with Caron
   3007     C("Shift-O"):               UC(0x01D1),                     # Ǒ Latin Capital Letter O with Caron
   3008     C("Shift-R"):               UC(0x0158),                     # Ř Latin Capital Letter R with Caron
   3009     C("Shift-S"):               UC(0x0160),                     # Š Latin Capital Letter S with Caron
   3010     C("Shift-T"):               UC(0x0164),                     # Ť Latin Capital Letter T with Caron
   3011     C("Shift-U"):               UC(0x01D3),                     # Ǔ Latin Capital Letter U with Caron
   3012     C("Shift-V"):               UC(0x01D9),                     # Ǚ Latin Capital Letter U w/Diaeresis and Caron
   3013     C("Shift-X"):              [UC(0x01B7),UC(0x030C)],         # Ǯ Latin Capital Letter Ezh with Caron
   3014     C("Shift-Z"):               UC(0x017D),                     # Ž Latin Capital Letter Z with Caron
   3015 }, when = lambda _: ac_Chr_main == 0x02C7 and cnfg.optspec_layout == 'ABC')
   3016 
   3017 keymap("DK-ABC - Breve", {
   3018     # Option+B                  {U+02D8}
   3019     # Valid keys:
   3020     # a e g h i o u
   3021     # A E G H I O U
   3022     C("A"):                     UC(0x0103),                     # ă Latin Small Letter A with Breve
   3023     C("E"):                     UC(0x0115),                     # ĕ Latin Small Letter E with Breve
   3024     C("G"):                     UC(0x011F),                     # ğ Latin Small Letter G with Breve
   3025     C("H"):                     UC(0x1E2B),                     # ḫ Latin Small Letter H with Breve Below
   3026     C("I"):                     UC(0x012D),                     # ĭ Latin Small Letter I with Breve
   3027     C("O"):                     UC(0x014F),                     # ŏ Latin Small Letter O with Breve
   3028     C("U"):                     UC(0x016D),                     # ŭ Latin Small Letter U with Breve
   3029     C("Shift-A"):               UC(0x0102),                     # Ă Latin Capital Letter A with Breve
   3030     C("Shift-E"):               UC(0x0114),                     # Ĕ Latin Capital Letter E with Breve
   3031     C("Shift-G"):               UC(0x011E),                     # Ğ Latin Capital Letter G with Breve
   3032     C("Shift-H"):               UC(0x1E2A),                     # Ḫ Latin Capital Letter H with Breve Below
   3033     C("Shift-I"):               UC(0x012C),                     # Ĭ Latin Capital Letter I with Breve
   3034     C("Shift-O"):               UC(0x014E),                     # Ŏ Latin Capital Letter O with Breve
   3035     C("Shift-U"):               UC(0x016C),                     # Ŭ Latin Capital Letter U with Breve
   3036 }, when = lambda _: ac_Chr_main == 0x02D8 and cnfg.optspec_layout == 'ABC')
   3037 
   3038 keymap("DK-ABC - Tilde", {
   3039     # Option+N                  {U+02DC}
   3040     # Valid keys:
   3041     # a e i n o u v y
   3042     # A E I N O U V Y
   3043     C("A"):                     UC(0x00E3),                     # ã Latin Small Letter A with Tilde
   3044     C("E"):                     UC(0x1EBD),                     # ẽ Latin Small Letter E with Tilde
   3045     C("I"):                     UC(0x0129),                     # ĩ Latin Small Letter I with Tilde
   3046     C("N"):                     UC(0x00F1),                     # ñ Latin Small Letter N with Tilde
   3047     C("O"):                     UC(0x00F5),                     # õ Latin Small Letter O with Tilde
   3048     C("U"):                     UC(0x0169),                     # ũ Latin Small Letter U with Tilde
   3049     C("V"):                     UC(0x1E7D),                     # ṽ Latin Small Letter V with Tilde
   3050     C("Y"):                     UC(0x1EF9),                     # ỹ Latin Small Letter Y with Tilde
   3051     C("Shift-A"):               UC(0x00C3),                     # Ã Latin Capital Letter A with Tilde
   3052     C("Shift-E"):               UC(0x1EBC),                     # Ẽ Latin Capital Letter E with Tilde
   3053     C("Shift-I"):               UC(0x0128),                     # Ĩ Latin Capital Letter I with Tilde
   3054     C("Shift-N"):               UC(0x00D1),                     # Ñ Latin Capital Letter N with Tilde
   3055     C("Shift-O"):               UC(0x00D5),                     # Õ Latin Capital Letter O with Tilde
   3056     C("Shift-U"):               UC(0x0168),                     # Ũ Latin Capital Letter U with Tilde
   3057     C("Shift-V"):               UC(0x1E7C),                     # Ṽ Latin Capital Letter V with Tilde
   3058     C("Shift-Y"):               UC(0x1EF8),                     # Ỹ Latin Capital Letter Y with Tilde
   3059 }, when = lambda _: ac_Chr_main == 0x02DC and cnfg.optspec_layout == 'ABC')
   3060 
   3061 keymap("DK-ABC - Ogonek", {
   3062     # Option+M                  {U+02DB}
   3063     # Valid keys:
   3064     # a e i o u
   3065     # A E I O U
   3066     C("A"):                     UC(0x0105),                     # ą Latin Small Letter A with Ogonek
   3067     C("E"):                     UC(0x0119),                     # ę Latin Small Letter E with Ogonek
   3068     C("I"):                     UC(0x012F),                     # į Latin Small Letter I with Ogonek
   3069     C("O"):                     UC(0x01EB),                     # ǫ Latin Small Letter O with Ogonek
   3070     C("U"):                     UC(0x0173),                     # ų Latin Small Letter U with Ogonek
   3071     C("Shift-A"):               UC(0x0104),                     # Ą Latin Capital Letter A with Ogonek
   3072     C("Shift-E"):               UC(0x0118),                     # Ę Latin Capital Letter E with Ogonek
   3073     C("Shift-I"):               UC(0x012E),                     # Į Latin Capital Letter I with Ogonek
   3074     C("Shift-O"):               UC(0x01EA),                     # Ǫ Latin Capital Letter O with Ogonek
   3075     C("Shift-U"):               UC(0x0172),                     # Ų Latin Capital Letter U with Ogonek
   3076 }, when = lambda _: ac_Chr_main == 0x02DB and cnfg.optspec_layout == 'ABC')
   3077 
   3078 keymap("DK-ABC - Hook", {
   3079     # Shift+Option+Dot          {U+0294}
   3080     # Valid keys:
   3081     # b c d f g h i k n p q r s t u x y z
   3082     # B C D F G   I K N P   R S T U X Y Z
   3083     C("B"):                     UC(0x0253),                     # ɓ Latin Small Letter B with Hook
   3084     C("C"):                     UC(0x0188),                     # ƈ Latin Small Letter C with Hook
   3085     C("D"):                     UC(0x0257),                     # ɗ Latin Small Letter D with Hook
   3086     C("F"):                     UC(0x0192),                     # ƒ Latin Small Letter F with Hook (function symbol)
   3087     C("G"):                     UC(0x0260),                     # ɠ Latin Small Letter G with Hook
   3088     C("H"):                     UC(0x0266),                     # ɦ Latin Small Letter H with Hook
   3089     C("I"):                     UC(0x0269),                     # ɩ Latin Small Letter Iota
   3090     C("K"):                     UC(0x0199),                     # ƙ Latin Small Letter K with Hook
   3091     C("N"):                     UC(0x0272),                     # ɲ Latin Small Letter N with Left Hook
   3092     C("P"):                     UC(0x01A5),                     # ƥ Latin Small Letter P with Hook
   3093     C("Q"):                     UC(0x02A0),                     # ʠ Latin Small Letter Q with Hook
   3094     C("R"):                     UC(0x0288),                     # ʈ Latin Small Letter T with Retroflex Hook
   3095     C("S"):                     UC(0x0283),                     # ʃ Latin Small Letter Esh
   3096     C("T"):                     UC(0x01AD),                     # ƭ Latin Small Letter T with Hook
   3097     C("U"):                     UC(0x028B),                     # ʋ Latin Small Letter V with Hook
   3098     C("X"):                     UC(0x0256),                     # ɖ Latin Small Letter D with Tail
   3099     C("Y"):                     UC(0x01B4),                     # ƴ Latin Small Letter Y with Hook
   3100     C("Z"):                     UC(0x0225),                     # ȥ Latin Small Letter Z with Hook
   3101     C("Shift-B"):               UC(0x0181),                     # Ɓ Latin Capital Letter B with Hook
   3102     C("Shift-C"):               UC(0x0187),                     # Ƈ Latin Capital Letter C with Hook
   3103     C("Shift-D"):               UC(0x018A),                     # Ɗ Latin Capital Letter D with Hook
   3104     C("Shift-F"):               UC(0x0191),                     # Ƒ Latin Capital Letter F with Hook
   3105     C("Shift-G"):               UC(0x0193),                     # Ɠ Latin Capital Letter G with Hook
   3106     C("Shift-I"):               UC(0x0196),                     # Ɩ Latin Capital Letter Iota
   3107     C("Shift-K"):               UC(0x0198),                     # Ƙ Latin Capital Letter K with Hook
   3108     C("Shift-N"):               UC(0x019D),                     # Ɲ Latin Capital Letter N with Left Hook
   3109     C("Shift-P"):               UC(0x01A4),                     # Ƥ Latin Capital Letter P with Hook
   3110     C("Shift-R"):               UC(0x01AE),                     # Ʈ Latin Capital Letter T with Retroflex Hook
   3111     C("Shift-S"):               UC(0x01A9),                     # Ʃ Latin Capital Letter Esh
   3112     C("Shift-T"):               UC(0x01AC),                     # Ƭ Latin Capital Letter T with Hook
   3113     C("Shift-U"):               UC(0x01B2),                     # Ʋ Latin Capital Letter V with Hook
   3114     C("Shift-X"):               UC(0x0189),                     # Ɖ Latin Capital Letter African D
   3115     C("Shift-Y"):               UC(0x01B3),                     # Ƴ Latin Capital Letter Y with Hook
   3116     C("Shift-Z"):               UC(0x0224),                     # Ȥ Latin Capital Letter Z with Hook
   3117 }, when = lambda _: ac_Chr_main == 0x0294 and cnfg.optspec_layout == 'ABC')
   3118 
   3119 
   3120 #######################################
   3121 ###  DEAD KEYS KEYMAPS - US LAYOUT  ###
   3122 #######################################
   3123 keymap("DK-US - Grave", {
   3124     # Valid keys:
   3125     # a e i o u
   3126     # A E I O U
   3127     C("A"):                     UC(0x00E0),                     # à Latin Small a with Grave
   3128     C("E"):                     UC(0x00E8),                     # è Latin Small e with Grave
   3129     C("I"):                     UC(0x00EC),                     # ì Latin Small i with Grave
   3130     C("O"):                     UC(0x00F2),                     # ò Latin Small o with Grave
   3131     C("U"):                     UC(0x00F9),                     # ù Latin Small u with Grave
   3132     C("Shift-A"):               UC(0x00C0),                     # À Latin Capital A with Grave
   3133     C("Shift-E"):               UC(0x00C8),                     # È Latin Capital E with Grave
   3134     C("Shift-I"):               UC(0x00CC),                     # Ì Latin Capital I with Grave
   3135     C("Shift-O"):               UC(0x00D2),                     # Ò Latin Capital O with Grave
   3136     C("Shift-U"):               UC(0x00D9),                     # Ù Latin Capital U with Grave
   3137 }, when = lambda _: ac_Chr_main == 0x0060 and cnfg.optspec_layout == 'US')
   3138 
   3139 keymap("DK-US - Acute", {
   3140     # Valid keys:
   3141     # a e i o u
   3142     # A E I O U
   3143     C("A"):                     UC(0x00E1),                     # á Latin Small a with Acute
   3144     C("E"):                     UC(0x00E9),                     # é Latin Small e with Acute
   3145     C("I"):                     UC(0x00ED),                     # í Latin Small i with Acute
   3146     C("O"):                     UC(0x00F3),                     # ó Latin Small o with Acute
   3147     C("U"):                     UC(0x00FA),                     # ú Latin Small u with Acute
   3148     C("Shift-A"):               UC(0x00C1),                     # Á Latin Capital A with Acute
   3149     C("Shift-E"):               UC(0x00C9),                     # É Latin Capital E with Acute
   3150     C("Shift-I"):               UC(0x00CD),                     # Í Latin Capital I with Acute
   3151     C("Shift-O"):               UC(0x00D3),                     # Ó Latin Capital O with Acute
   3152     C("Shift-U"):               UC(0x00DA),                     # Ú Latin Capital U with Acute
   3153 }, when = lambda _: ac_Chr_main == 0x00B4 and cnfg.optspec_layout == 'US')
   3154 
   3155 keymap("DK-US - Umlaut", {
   3156     # Valid keys:
   3157     # a e i o u y
   3158     # A E I O U Y
   3159     C("A"):                     UC(0x00E4),                     # ä Latin Small a with Umlaut
   3160     C("E"):                     UC(0x00EB),                     # ë Latin Small e with Umlaut
   3161     C("I"):                     UC(0x00EF),                     # ï Latin Small i with Umlaut
   3162     C("O"):                     UC(0x00F6),                     # ö Latin Small o with Umlaut
   3163     C("U"):                     UC(0x00FC),                     # ü Latin Small u with Umlaut
   3164     C("Y"):                     UC(0x00FF),                     # ÿ Latin Small y with Umlaut
   3165     C("Shift-A"):               UC(0x00C4),                     # Ä Latin Capital A with Umlaut
   3166     C("Shift-E"):               UC(0x00CB),                     # Ë Latin Capital E with Umlaut
   3167     C("Shift-I"):               UC(0x00CF),                     # Ï Latin Capital I with Umlaut
   3168     C("Shift-O"):               UC(0x00D6),                     # Ö Latin Capital O with Umlaut
   3169     C("Shift-U"):               UC(0x00DC),                     # Ü Latin Capital U with Umlaut
   3170     C("Shift-Y"):               UC(0x0178),                     # Ÿ Latin Capital Y with Umlaut
   3171 }, when = lambda _: ac_Chr_main == 0x00A8 and cnfg.optspec_layout == 'US')
   3172 
   3173 keymap("DK-US - Circumflex", {
   3174     # Valid keys:
   3175     # a e i o u
   3176     # A E I O U
   3177     C("A"):                     UC(0x00E2),                     # â Latin Small a with Circumflex
   3178     C("E"):                     UC(0x00EA),                     # ê Latin Small e with Circumflex
   3179     C("I"):                     UC(0x00EE),                     # î Latin Small i with Circumflex
   3180     C("O"):                     UC(0x00F4),                     # ô Latin Small o with Circumflex
   3181     C("U"):                     UC(0x00FB),                     # û Latin Small u with Circumflex
   3182     C("Shift-A"):               UC(0x00C2),                     # Â Latin Capital A with Circumflex
   3183     C("Shift-E"):               UC(0x00CA),                     # Ê Latin Capital E with Circumflex
   3184     C("Shift-I"):               UC(0x00CE),                     # Î Latin Capital I with Circumflex
   3185     C("Shift-O"):               UC(0x00D4),                     # Ô Latin Capital O with Circumflex
   3186     C("Shift-U"):               UC(0x00DB),                     # Û Latin Capital U with Circumflex
   3187 }, when = lambda _: ac_Chr_main == 0x02C6 and cnfg.optspec_layout == 'US')
   3188 
   3189 keymap("DK-US - Tilde", {
   3190     # Valid keys:
   3191     # a n o
   3192     # A N O
   3193     C("A"):                     UC(0x00E3),                     # ã Latin Small a with Tilde
   3194     C("N"):                     UC(0x00F1),                     # ñ Latin Small n with Tilde
   3195     C("O"):                     UC(0x00F5),                     # õ Latin Small o with Tilde
   3196     C("Shift-A"):               UC(0x00C3),                     # Ã Latin Capital A with Tilde
   3197     C("Shift-N"):               UC(0x00D1),                     # Ñ Latin Capital N with Tilde
   3198     C("Shift-O"):               UC(0x00D5),                     # Õ Latin Capital O with Tilde
   3199 }, when = lambda _: ac_Chr_main == 0x02DC and cnfg.optspec_layout == 'US')
   3200 
   3201 
   3202 
   3203 #################################################################
   3204 ###                                                           ###
   3205 ###                                                           ###
   3206 ###      ███████ ███████  ██████  █████  ██████  ███████      ###
   3207 ###      ██      ██      ██      ██   ██ ██   ██ ██           ###
   3208 ###      █████   ███████ ██      ███████ ██████  █████        ###
   3209 ###      ██           ██ ██      ██   ██ ██      ██           ###
   3210 ###      ███████ ███████  ██████ ██   ██ ██      ███████      ###
   3211 ###                                                           ###
   3212 ###                                                           ###
   3213 #################################################################
   3214 keymap("Escape actions for dead keys - Overrides for ABC Extended", {
   3215 
   3216     ###  activate other dead keys correctly while one is active
   3217 
   3218     # US layout dead keys (not here, see keymap below)
   3219 
   3220     # ABC Extended layout dead keys
   3221     C("Alt-Grave"):     [getDK(),UC(0x0060),C("Shift-Left"),setDK(0x0060)], # Dead Key Accent: Grave
   3222     C("Alt-6"):         [getDK(),UC(0x02C6),C("Shift-Left"),setDK(0x02C6)], # Dead Key Accent: Circumflex
   3223     C("Alt-W"):         [getDK(),UC(0x02D9),C("Shift-Left"),setDK(0x02D9)], # Dead Key Accent: Dot Above
   3224     C("Alt-E"):         [getDK(),UC(0x00B4),C("Shift-Left"),setDK(0x00B4)], # Dead Key Accent: Acute
   3225 
   3226     # C("Shift-Alt-Y"):   [getDK(),UC(0x030F),C("Shift-Left"),setDK(0x030F)], # Dead Key Accent: Combining Double Grave
   3227     # Double Grave accent acts odd when using the Combining Double Grave {U+030F}
   3228     # Substituting {U+02F5}: ˵ Modifier Letter Middle Double Grave Accent
   3229     C("Shift-Alt-Y"):   [getDK(),UC(0x02F5),C("Shift-Left"),setDK(0x02F5)], # Dead Key Accent: Double Grave (substitute)
   3230 
   3231     C("Alt-U"):         [getDK(),UC(0x00A8),C("Shift-Left"),setDK(0x00A8)], # Dead Key Accent: Umlaut/Diaeresis
   3232     C("Alt-I"):         [getDK(),UC(0x02BC),C("Shift-Left"),setDK(0x02BC)], # Dead Key Accent: Apostrophe/Horn
   3233     C("Alt-P"):         [getDK(),UC(0x002C),C("Shift-Left"),setDK(0x002C)], # Dead Key Accent: Comma Below
   3234     C("Alt-A"):         [getDK(),UC(0x00AF),C("Shift-Left"),setDK(0x00AF)], # Dead Key Accent: Macron/Line Above
   3235 
   3236     # C("Shift-Alt-S"):   [getDK(),UC(0x0311),C("Shift-Left"),setDK(0x0311)], # Dead Key Accent: Combining Inverted Breve
   3237     C("Shift-Alt-S"):   [getDK(),UC(0x1D16),C("Shift-Left"),setDK(0x1D16)], # Dead Key Accent: Combining Inverted Breve
   3238 
   3239     # C("Shift-Alt-F"):   [getDK(),UC(0x0330),C("Shift-Left"),setDK(0x0330)], # Dead Key Accent: Combining Tilde Below
   3240     # Tilde Below accent acts odd when using the Combining Tilde Below {U+0330}
   3241     # Substituting {U+02F7}: ˷ Modifier Letter Low Tilde
   3242     C("Shift-Alt-F"):   [getDK(),UC(0x02F7),C("Shift-Left"),setDK(0x02F7)], # Dead Key Accent: Tilde Below
   3243     C("Shift-Alt-G"):   [getDK(),UC(0x2038),C("Shift-Left"),setDK(0x2038)], # Dead Key Accent: Caret/Circumflex Below
   3244 
   3245     C("Alt-H"):         [getDK(),UC(0x02CD),C("Shift-Left"),setDK(0x02CD)], # Dead Key Accent: Low Macron/Line Below
   3246     C("Alt-J"):         [getDK(),UC(0x02DD),C("Shift-Left"),setDK(0x02DD)], # Dead Key Accent: Double Acute
   3247     C("Alt-K"):         [getDK(),UC(0x02DA),C("Shift-Left"),setDK(0x02DA)], # Dead Key Accent: Ring Above
   3248     C("Alt-L"):         [getDK(),UC(0x002D),C("Shift-Left"),setDK(0x002D)], # Dead Key Accent: Stroke/Hyphen-Minus
   3249 
   3250     C("Shift-Alt-Semicolon"): [getDK(),UC(0x2116),C("Shift-Left"),setDK(0x2116)], # Dead Key Accent: Numero Sign
   3251 
   3252     C("Alt-Z"):         [getDK(),UC(0x02C0),C("Shift-Left"),setDK(0x02C0)], # Dead Key Accent: Hook Above/Glottal Stop
   3253     C("Alt-X"):         [getDK(),UC(0x002E),C("Shift-Left"),setDK(0x002E)], # Dead Key Accent: Dot Below
   3254     C("Alt-C"):         [getDK(),UC(0x00B8),C("Shift-Left"),setDK(0x00B8)], # Dead Key Accent: Cedilla/Cedille
   3255     C("Alt-V"):         [getDK(),UC(0x02C7),C("Shift-Left"),setDK(0x02C7)], # Dead Key Accent: Caron/hacek
   3256     C("Alt-B"):         [getDK(),UC(0x02D8),C("Shift-Left"),setDK(0x02D8)], # Dead Key Accent: Breve
   3257     C("Alt-N"):         [getDK(),UC(0x02DC),C("Shift-Left"),setDK(0x02DC)], # Dead Key Accent: Tilde
   3258     C("Alt-M"):         [getDK(),UC(0x02DB),C("Shift-Left"),setDK(0x02DB)], # Dead Key Accent: Ogonek
   3259 
   3260     C("Shift-Alt-Dot"): [getDK(),UC(0x0294),C("Shift-Left"),setDK(0x0294)], # Dead Key Accent: Hook
   3261 
   3262 }, when = lambda _: ac_Chr_main in deadkeys_list and cnfg.optspec_layout == 'ABC')
   3263 
   3264 keymap("Escape actions for dead keys", {
   3265     # special case shortcuts that should cancel dead keys
   3266     C("Esc"):           [getDK(),setDK(None)],                              # Leave accent char if dead keys Escaped
   3267     C("Space"):         [getDK(),setDK(None)],                              # Leave accent char if user hits Space
   3268     C("Delete"):        [getDK(),setDK(None)],                              # Leave accent char if user hits Delete
   3269     C("Backspace"):     [getDK(),C("Backspace"),setDK(None)],               # Delete character if user hits Backspace
   3270     C("Tab"):           [getDK(),C("Tab"),setDK(None)],                     # Leave accent char, insert Tab
   3271     C("Enter"):         [getDK(),C("Enter"),setDK(None)],                   # Leave accent char, Enter key
   3272     C("Up"):            [getDK(),C("Up"),setDK(None)],                      # Leave accent char, up arrow
   3273     C("Down"):          [getDK(),C("Down"),setDK(None)],                    # Leave accent char, down arrow
   3274     C("Left"):          [getDK(),C("Left"),setDK(None)],                    # Leave accent char, left arrow
   3275     C("Right"):         [getDK(),C("Right"),setDK(None)],                   # Leave accent char, right arrow
   3276     C("RC-Tab"):        [getDK(),bind,C("Alt-Tab"),setDK(None)],            # Leave accent char, task switch
   3277     C("Shift-RC-Tab"):  [getDK(),bind,C("Shift-Alt-Tab"),setDK(None)],      # Leave accent char, task switch (reverse)
   3278     C("RC-Grave"):      [getDK(),bind,C("Alt-Grave"),setDK(None)],          # Leave accent char, in-app window switch
   3279     C("Shift-RC-Tab"):  [getDK(),bind,C("Shift-Alt-Grave"),setDK(None)],    # Leave accent char, in-app window switch (reverse)
   3280 
   3281     # common shortcuts that should also cancel dead keys
   3282     C("RC-a"):                  [getDK(),C("C-a"),setDK(None)],             # Leave accent char, select all
   3283     C("RC-z"):                  [getDK(),C("C-z"),setDK(None)],             # Leave accent char, undo
   3284     C("RC-x"):                  [getDK(),C("C-x"),setDK(None)],             # Leave accent char, cut
   3285     C("RC-c"):                  [getDK(),C("C-c"),setDK(None)],             # Leave accent char, copy
   3286     C("RC-v"):                  [getDK(),C("C-v"),setDK(None)],             # Leave accent char, paste
   3287 
   3288     ###  activate other dead keys correctly while one is active
   3289 
   3290     # US layout dead keys
   3291     C("Alt-Grave"):     [getDK(),UC(0x0060),C("Shift-Left"),setDK(0x0060)], # Dead Key Accent: Grave
   3292     C("Alt-E"):         [getDK(),UC(0x00B4),C("Shift-Left"),setDK(0x00B4)], # Dead Key Accent: Acute
   3293     C("Alt-U"):         [getDK(),UC(0x00A8),C("Shift-Left"),setDK(0x00A8)], # Dead Key Accent: Umlaut/Diaeresis
   3294     C("Alt-I"):         [getDK(),UC(0x02C6),C("Shift-Left"),setDK(0x02C6)], # Dead Key Accent: Circumflex
   3295     C("Alt-N"):         [getDK(),UC(0x02DC),C("Shift-Left"),setDK(0x02DC)], # Dead Key Accent: Tilde
   3296 
   3297     # ABC Extended layout dead keys (not here, see keymap above)
   3298 
   3299 
   3300     # cancel dead keys with number keys row
   3301     C("Grave"):                 [getDK(),C("Grave"),setDK(None)],
   3302     C("Key_1"):                 [getDK(),C("Key_1"),setDK(None)],
   3303     C("Key_2"):                 [getDK(),C("Key_2"),setDK(None)],
   3304     C("Key_3"):                 [getDK(),C("Key_3"),setDK(None)],
   3305     C("Key_4"):                 [getDK(),C("Key_4"),setDK(None)],
   3306     C("Key_5"):                 [getDK(),C("Key_5"),setDK(None)],
   3307     C("Key_6"):                 [getDK(),C("Key_6"),setDK(None)],
   3308     C("Key_7"):                 [getDK(),C("Key_7"),setDK(None)],
   3309     C("Key_8"):                 [getDK(),C("Key_8"),setDK(None)],
   3310     C("Key_9"):                 [getDK(),C("Key_9"),setDK(None)],
   3311     C("Key_0"):                 [getDK(),C("Key_0"),setDK(None)],
   3312     C("Minus"):                 [getDK(),C("Minus"),setDK(None)],
   3313     C("Equal"):                 [getDK(),C("Equal"),setDK(None)],
   3314     C("Shift-Grave"):           [getDK(),C("Shift-Grave"),setDK(None)],
   3315     C("Shift-Key_1"):           [getDK(),C("Shift-Key_1"),setDK(None)],
   3316     C("Shift-Key_2"):           [getDK(),C("Shift-Key_2"),setDK(None)],
   3317     C("Shift-Key_3"):           [getDK(),C("Shift-Key_3"),setDK(None)],
   3318     C("Shift-Key_4"):           [getDK(),C("Shift-Key_4"),setDK(None)],
   3319     C("Shift-Key_5"):           [getDK(),C("Shift-Key_5"),setDK(None)],
   3320     C("Shift-Key_6"):           [getDK(),C("Shift-Key_6"),setDK(None)],
   3321     C("Shift-Key_7"):           [getDK(),C("Shift-Key_7"),setDK(None)],
   3322     C("Shift-Key_8"):           [getDK(),C("Shift-Key_8"),setDK(None)],
   3323     C("Shift-Key_9"):           [getDK(),C("Shift-Key_9"),setDK(None)],
   3324     C("Shift-Key_0"):           [getDK(),C("Shift-Key_0"),setDK(None)],
   3325     C("Shift-Minus"):           [getDK(),C("Shift-Minus"),setDK(None)],
   3326     C("Shift-Equal"):           [getDK(),C("Shift-Equal"),setDK(None)],
   3327 
   3328     # cancel dead keys with any letter on the keyboard that isn't supported by the dead key
   3329     C("A"):                     [getDK(),C("A"),setDK(None)],
   3330     C("B"):                     [getDK(),C("B"),setDK(None)],
   3331     C("C"):                     [getDK(),C("C"),setDK(None)],
   3332     C("D"):                     [getDK(),C("D"),setDK(None)],
   3333     C("E"):                     [getDK(),C("E"),setDK(None)],
   3334     C("F"):                     [getDK(),C("F"),setDK(None)],
   3335     C("G"):                     [getDK(),C("G"),setDK(None)],
   3336     C("H"):                     [getDK(),C("H"),setDK(None)],
   3337     C("I"):                     [getDK(),C("I"),setDK(None)],
   3338     C("J"):                     [getDK(),C("J"),setDK(None)],
   3339     C("K"):                     [getDK(),C("K"),setDK(None)],
   3340     C("L"):                     [getDK(),C("L"),setDK(None)],
   3341     C("M"):                     [getDK(),C("M"),setDK(None)],
   3342     C("N"):                     [getDK(),C("N"),setDK(None)],
   3343     C("O"):                     [getDK(),C("O"),setDK(None)],
   3344     C("P"):                     [getDK(),C("P"),setDK(None)],
   3345     C("Q"):                     [getDK(),C("Q"),setDK(None)],
   3346     C("R"):                     [getDK(),C("R"),setDK(None)],
   3347     C("S"):                     [getDK(),C("S"),setDK(None)],
   3348     C("T"):                     [getDK(),C("T"),setDK(None)],
   3349     C("U"):                     [getDK(),C("U"),setDK(None)],
   3350     C("V"):                     [getDK(),C("V"),setDK(None)],
   3351     C("W"):                     [getDK(),C("W"),setDK(None)],
   3352     C("X"):                     [getDK(),C("X"),setDK(None)],
   3353     C("Y"):                     [getDK(),C("Y"),setDK(None)],
   3354     C("Z"):                     [getDK(),C("Z"),setDK(None)],
   3355     C("Shift-A"):               [getDK(),C("Shift-A"),setDK(None)],
   3356     C("Shift-B"):               [getDK(),C("Shift-B"),setDK(None)],
   3357     C("Shift-C"):               [getDK(),C("Shift-C"),setDK(None)],
   3358     C("Shift-D"):               [getDK(),C("Shift-D"),setDK(None)],
   3359     C("Shift-E"):               [getDK(),C("Shift-E"),setDK(None)],
   3360     C("Shift-F"):               [getDK(),C("Shift-F"),setDK(None)],
   3361     C("Shift-G"):               [getDK(),C("Shift-G"),setDK(None)],
   3362     C("Shift-H"):               [getDK(),C("Shift-H"),setDK(None)],
   3363     C("Shift-I"):               [getDK(),C("Shift-I"),setDK(None)],
   3364     C("Shift-J"):               [getDK(),C("Shift-J"),setDK(None)],
   3365     C("Shift-K"):               [getDK(),C("Shift-K"),setDK(None)],
   3366     C("Shift-L"):               [getDK(),C("Shift-L"),setDK(None)],
   3367     C("Shift-M"):               [getDK(),C("Shift-M"),setDK(None)],
   3368     C("Shift-N"):               [getDK(),C("Shift-N"),setDK(None)],
   3369     C("Shift-O"):               [getDK(),C("Shift-O"),setDK(None)],
   3370     C("Shift-P"):               [getDK(),C("Shift-P"),setDK(None)],
   3371     C("Shift-Q"):               [getDK(),C("Shift-Q"),setDK(None)],
   3372     C("Shift-R"):               [getDK(),C("Shift-R"),setDK(None)],
   3373     C("Shift-S"):               [getDK(),C("Shift-S"),setDK(None)],
   3374     C("Shift-T"):               [getDK(),C("Shift-T"),setDK(None)],
   3375     C("Shift-U"):               [getDK(),C("Shift-U"),setDK(None)],
   3376     C("Shift-V"):               [getDK(),C("Shift-V"),setDK(None)],
   3377     C("Shift-W"):               [getDK(),C("Shift-W"),setDK(None)],
   3378     C("Shift-X"):               [getDK(),C("Shift-X"),setDK(None)],
   3379     C("Shift-Y"):               [getDK(),C("Shift-Y"),setDK(None)],
   3380     C("Shift-Z"):               [getDK(),C("Shift-Z"),setDK(None)],
   3381 
   3382     # cancel dead keys with other punctuation keys
   3383     C("Left_Brace"):            [getDK(),C("Left_Brace"),setDK(None)],
   3384     C("Right_Brace"):           [getDK(),C("Right_Brace"),setDK(None)],
   3385     C("Backslash"):             [getDK(),C("Backslash"),setDK(None)],
   3386     C("Semicolon"):             [getDK(),C("Semicolon"),setDK(None)],
   3387     C("Apostrophe"):            [getDK(),C("Apostrophe"),setDK(None)],
   3388     C("Comma"):                 [getDK(),C("Comma"),setDK(None)],
   3389     C("Dot"):                   [getDK(),C("Dot"),setDK(None)],
   3390     C("Slash"):                 [getDK(),C("Slash"),setDK(None)],
   3391     C("Shift-Left_Brace"):      [getDK(),C("Shift-Left_Brace"),setDK(None)],
   3392     C("Shift-Right_Brace"):     [getDK(),C("Shift-Right_Brace"),setDK(None)],
   3393     C("Shift-Backslash"):       [getDK(),C("Shift-Backslash"),setDK(None)],
   3394     C("Shift-Semicolon"):       [getDK(),C("Shift-Semicolon"),setDK(None)],
   3395     C("Shift-Apostrophe"):      [getDK(),C("Shift-Apostrophe"),setDK(None)],
   3396     C("Shift-Comma"):           [getDK(),C("Shift-Comma"),setDK(None)],
   3397     C("Shift-Dot"):             [getDK(),C("Shift-Dot"),setDK(None)],
   3398     C("Shift-Slash"):           [getDK(),C("Shift-Slash"),setDK(None)],
   3399 
   3400 }, when = lambda _: ac_Chr_main in deadkeys_list)
   3401 
   3402 keymap("Disable Dead Keys Tripwire",{
   3403     # Nothing needs to be here. Tripwire keymap to disable active dead keys keymap(s)
   3404 }, when = lambda _: setDK(None)())
   3405 
   3406 
   3407 
   3408 ##############################################################################
   3409 ###                                                                        ###
   3410 ###                                                                        ###
   3411 ###       █████  ██████   ██████     ███    ███  █████  ██ ███    ██       ###
   3412 ###      ██   ██ ██   ██ ██          ████  ████ ██   ██ ██ ████   ██       ###
   3413 ###      ███████ ██████  ██          ██ ████ ██ ███████ ██ ██ ██  ██       ###
   3414 ###      ██   ██ ██   ██ ██          ██  ██  ██ ██   ██ ██ ██  ██ ██       ###
   3415 ###      ██   ██ ██████   ██████     ██      ██ ██   ██ ██ ██   ████       ###
   3416 ###                                                                        ###
   3417 ###                                                                        ###
   3418 ##############################################################################
   3419 # Main keymap for special characters on the ABC Extended layout
   3420 keymap("OptSpecialChars - ABC", {
   3421 
   3422     # Number keys row with Option
   3423     ######################################################
   3424     C("Alt-Grave"):     [UC(0x0060),C("Shift-Left"),setDK(0x0060)],     # Dead Key Accent: Grave
   3425 
   3426     C("Alt-1"):                 UC(0x00A1),                     # ¡ Inverted Exclamation Mark
   3427     C("Alt-2"):                 UC(0x2122),                     # ™ Trade Mark Sign Emoji
   3428     C("Alt-3"):                 UC(0x00A3),                     # £ British Pound currency symbol
   3429     C("Alt-4"):                 UC(0x00A2),                     # ¢ Cent currency symbol
   3430     C("Alt-5"):                 UC(0x00A7),                     # § Section symbol
   3431 
   3432     C("Alt-6"):         [UC(0x02C6),C("Shift-Left"),setDK(0x02C6)],     # Dead Key Accent: Circumflex
   3433 
   3434     C("Alt-7"):                 UC(0x00B6),                     # ¶ Paragraph mark (Pilcrow) symbol
   3435     C("Alt-8"):                 UC(0x2022),                     # • Bullet Point symbol (solid)
   3436     C("Alt-9"):                 UC(0x00AA),                     # ª Feminine Ordinal Indicator
   3437     C("Alt-0"):                 UC(0x00BA),                     # º Masculine Ordinal Indicator
   3438     C("Alt-Minus"):             UC(0x2013),                     # – En Dash punctuation mark
   3439     C("Alt-Equal"):             UC(0x2260),                     # ≠ Not Equal To symbol
   3440 
   3441     # Number keys row with Shift+Option
   3442     ######################################################
   3443     C("Shift-Alt-Grave"):       UC(0x0300),                     # ` Combining Grave Accent
   3444     C("Shift-Alt-1"):           UC(0x2044),                     # ⁄ Fraction Slash
   3445     C("Shift-Alt-2"):           UC(0x20AC),                     # € Euro currency symbol
   3446     C("Shift-Alt-3"):           UC(0x2039),                     # ‹ Single Left-Pointing Angle Quotation mark
   3447     C("Shift-Alt-4"):           UC(0x203A),                     # › Single Right-Pointing Angle Quotation mark
   3448     C("Shift-Alt-5"):           UC(0x2020),                     # † Simple dagger (cross) symbol
   3449     C("Shift-Alt-6"):           UC(0x0302),                     #  ̂ Combining Circumflex Accent
   3450     C("Shift-Alt-7"):           UC(0x2021),                     # ‡ Double dagger (cross) symbol
   3451     C("Shift-Alt-8"):           UC(0x00B0),                     # ° Degree Sign
   3452     C("Shift-Alt-9"):           UC(0x00B7),                     # · Middle Dot (interpunct/middot)
   3453     C("Shift-Alt-0"):           UC(0x201A),                     # ‚ Single low-9 quotation mark
   3454     C("Shift-Alt-Minus"):       UC(0x2014),                     # — Em Dash punctuation mark
   3455     C("Shift-Alt-Equal"):       UC(0x00B1),                     # ± Plus Minus mathematical symbol
   3456 
   3457     # Tab key row with Option
   3458     ######################################################
   3459     C("Alt-Q"):                 UC(0x0153),                     # œ Small oe (oethel) ligature
   3460 
   3461     C("Alt-W"):         [UC(0x02D9),C("Shift-Left"),setDK(0x02D9)],     # Dead Key Accent: Dot Above
   3462     C("Alt-E"):         [UC(0x00B4),C("Shift-Left"),setDK(0x00B4)],     # Dead Key Accent: Acute
   3463 
   3464     C("Alt-R"):                 UC(0x00AE),                     # ® Registered Trade Mark Sign
   3465     C("Alt-T"):                 UC(0x00FE),                     # þ Latin Small Letter Thorn
   3466     C("Alt-Y"):                 UC(0x00A5),                     # ¥ Japanese Yen currency symbol
   3467 
   3468     C("Alt-U"):         [UC(0x00A8),C("Shift-Left"),setDK(0x00A8)],     # Dead Key Accent: Umlaut/Diaeresis
   3469     C("Alt-I"):         [UC(0x02BC),C("Shift-Left"),setDK(0x02BC)],     # Dead Key Accent: Apostrophe/Horn
   3470 
   3471     C("Alt-O"):                 UC(0x00F8),                     # ø Latin Small Letter o with Stroke
   3472 
   3473     C("Alt-P"):         [UC(0x002C),C("Shift-Left"),setDK(0x002C)],     # Dead Key Accent: Comma Below
   3474 
   3475     C("Alt-Left_Brace"):        UC(0x201C),                     # “ Left Double Quotation Mark
   3476     C("Alt-Right_Brace"):       UC(0x2018),                     # ‘ Left Single Quotation Mark
   3477     C("Alt-Backslash"):         UC(0x00AB),                     # « Left-Pointing Double Angle Quotation Mark
   3478 
   3479     # Tab key row with Shift+Option
   3480     ######################################################
   3481     C("Shift-Alt-Q"):           UC(0x0152),                     # Œ Capital OE (Oethel) ligature
   3482     C("Shift-Alt-W"):           UC(0x0307),                     # ˙ Combining Dot Above
   3483     C("Shift-Alt-E"):           UC(0x0301),                     #  ́ Combining Acute Accent
   3484     C("Shift-Alt-R"):           UC(0x2030),                     # ‰ Per mille symbol (zero over zero-zero)
   3485     C("Shift-Alt-T"):           UC(0x00DE),                     # Þ Latin Capital Letter Thorn
   3486 
   3487     # C("Shift-Alt-Y"):           UC(0x02F5), # UC(0x030F),       # ̏  Combining Double Grave Accent
   3488     # Spacing issues when using Combining Double Grave {U+030F}
   3489     # Substituting {U+02F5}: ˵ Modifier Letter Middle Double Grave Accent for initial presentation
   3490     C("Shift-Alt-Y"):   [UC(0x02F5),C("Shift-Left"),setDK(0x02F5)],     # Dead Key Accent: Double Grave
   3491 
   3492     C("Shift-Alt-U"):           UC(0x0308),                     #  ̈ Combining Diaeresis/Umlaut
   3493     C("Shift-Alt-I"):           UC(0x031B),                     # ̛ Combining Horn (Apostrophe)
   3494     C("Shift-Alt-O"):           UC(0x00D8),                     # Ø Latin Capital Letter O with Stroke
   3495     C("Shift-Alt-P"):           UC(0x0326),                     #  ̦ Combining Comma Below
   3496     C("Shift-Alt-Left_Brace"):  UC(0x201D),                     # ” Right Double Quotation Mark
   3497     C("Shift-Alt-Right_Brace"): UC(0x2019),                     # ’ Right Single Quotation Mark
   3498     C("Shift-Alt-Backslash"):   UC(0x00BB),                     # » Right-Pointing Double Angle Quotation Mark
   3499 
   3500     # CapsLock key row with Option
   3501     ######################################################
   3502 
   3503     C("Alt-A"):         [UC(0x00AF),C("Shift-Left"),setDK(0x00AF)],     # Dead Key Accent: Macron/Line Above
   3504 
   3505     C("Alt-S"):                 UC(0x00DF),                     # ß German Eszett/beta (Sharfes/Sharp S)
   3506     C("Alt-D"):                 UC(0x00F0),                     # ð Latin Small Letter Eth
   3507     C("Alt-F"):                 UC(0x0192),                     # ƒ Function/florin currency symbol
   3508     C("Alt-G"):                 UC(0x00A9),                     # © Copyright Sign
   3509 
   3510     C("Alt-H"):         [UC(0x02CD),C("Shift-Left"),setDK(0x02CD)],     # Dead Key Accent: Low Macron/Line Below
   3511     C("Alt-J"):         [UC(0x02DD),C("Shift-Left"),setDK(0x02DD)],     # Dead Key Accent: Double Acute
   3512     C("Alt-K"):         [UC(0x02DA),C("Shift-Left"),setDK(0x02DA)],     # Dead Key Accent: Ring Above
   3513     C("Alt-L"):         [UC(0x002D),C("Shift-Left"),setDK(0x002D)],     # Dead Key Accent: Stroke/Hyphen-Minus
   3514 
   3515     C("Alt-Semicolon"):         UC(0x2026),                     # … Horizontal ellipsis
   3516     C("Alt-Apostrophe"):        UC(0x00E6),                     # æ Small ae ligature
   3517 
   3518     # CapsLock key row with Shift+Option
   3519     ######################################################
   3520     C("Shift-Alt-A"):           UC(0x0304),                     #  ̄ Combining Macron/Line Below
   3521 
   3522     # C("Shift-Alt-S"): [UC(0x0311),C("Shift-Left"),setDK(0x0311)],   # Dead Key Accent: Combining Inverted Breve
   3523     # Combining Inverted Breve has spacing problems
   3524     # Substituting {U+1D16}: ᴖ Latin Small Letter Top Half O
   3525     C("Shift-Alt-S"):   [UC(0x1D16),C("Shift-Left"),setDK(0x1D16)],     # Dead Key Accent: Inverted Breve
   3526 
   3527     C("Shift-Alt-D"):           UC(0x00D0),                     # Ð Latin Capital Letter Eth
   3528 
   3529     # C("Shift-Alt-F"):   [UC(0x0330),C("Shift-Left"),setDK(0x0330)],     # Dead Key Accent: Tilde Below
   3530     # Combining Tilde Below has spacing problems
   3531     # Substituting {U+02F7}: ˷ Modifier Letter Low Tilde
   3532     C("Shift-Alt-F"):   [UC(0x02F7),C("Shift-Left"),setDK(0x02F7)],     # Dead Key Accent: Tilde Below
   3533     C("Shift-Alt-G"):   [UC(0x2038),C("Shift-Left"),setDK(0x2038)],     # Dead Key Accent: Caret/Circumflex Below
   3534 
   3535     C("Shift-Alt-H"):           UC(0x0331),                     # ̱  Combining Macron/Line Below
   3536     C("Shift-Alt-J"):           UC(0x030B),                     #  ̋ Combining Double Acute Accent
   3537     C("Shift-Alt-K"):           UC(0x030A),                     #  ̊ Combining Ring Above
   3538     C("Shift-Alt-L"):           UC(0x0335),                     #  ̵ Combining Short Stroke Overlay
   3539 
   3540     C("Shift-Alt-Semicolon"):  [UC(0x2116),C("Shift-Left"),setDK(0x2116)],      # Dead Key Accent: Numero Sign
   3541 
   3542     C("Shift-Alt-Apostrophe"):  UC(0x00C6),                     # Æ Capital AE ligature
   3543 
   3544     # Shift keys row with Option
   3545     ######################################################
   3546 
   3547     C("Alt-Z"): [UC(0x02C0),C("Shift-Left"),setDK(0x02C0)],     # Dead Key Accent: Hook Above/Glottal Stop
   3548     C("Alt-X"): [UC(0x002E),C("Shift-Left"),setDK(0x002E)],     # Dead Key Accent: Dot Below
   3549     C("Alt-C"): [UC(0x00B8),C("Shift-Left"),setDK(0x00B8)],     # Dead Key Accent: Cedilla/Cedille
   3550     C("Alt-V"): [UC(0x02C7),C("Shift-Left"),setDK(0x02C7)],     # Dead Key Accent: Caron/hacek
   3551     C("Alt-B"): [UC(0x02D8),C("Shift-Left"),setDK(0x02D8)],     # Dead Key Accent: Breve
   3552     C("Alt-N"): [UC(0x02DC),C("Shift-Left"),setDK(0x02DC)],     # Dead Key Accent: Tilde
   3553     C("Alt-M"): [UC(0x02DB),C("Shift-Left"),setDK(0x02DB)],     # Dead Key Accent: Ogonek
   3554 
   3555     C("Alt-Comma"):             UC(0x2264),                     # ≤ Less Than or Equal To symbol
   3556     C("Alt-Dot"):               UC(0x2265),                     # ≥ Greater Than or Equal To symbol
   3557     C("Alt-Slash"):             UC(0x00F7),                     # ÷ Obelus/Division symbol
   3558 
   3559     # Shift keys row with Shift+Option
   3560     ######################################################
   3561     C("Shift-Alt-Z"):           UC(0x0309),                     # ̉  Combining Hook Above (hoi)
   3562     C("Shift-Alt-X"):           UC(0x0323),                     # ̣  Combining Dot Below (nang)
   3563     C("Shift-Alt-C"):           UC(0x0327),                     #  ̧ Combining Cedilla
   3564     C("Shift-Alt-V"):           UC(0x030C),                     #  ̌ Combining Caron/hacek
   3565     C("Shift-Alt-B"):           UC(0x0306),                     #  ̆ Combining Breve
   3566     C("Shift-Alt-N"):           UC(0x0303),                     #  ̃ Combining Tilde
   3567     C("Shift-Alt-M"):           UC(0x0328),                     #  ̨ Combining Ogonek (nasal hook)
   3568     C("Shift-Alt-Comma"):       UC(0x201E),                     # „ Double Low-9 Quotation Mark
   3569 
   3570     C("Shift-Alt-Dot"): [UC(0x0294),C("Shift-Left"),setDK(0x0294)],     # Dead Key Accent: Hook
   3571 
   3572     C("Shift-Alt-Slash"):       UC(0x00BF),                     # ¿ Inverted Question mark
   3573 
   3574 }, when = lambda ctx:
   3575     cnfg.screen_has_focus and
   3576     cnfg.optspec_layout == 'ABC' and
   3577     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   3578 )
   3579 
   3580 
   3581 
   3582 ######################################################################
   3583 ###                                                                ###
   3584 ###                                                                ###
   3585 ###      ██    ██ ███████     ███    ███  █████  ██ ███    ██      ###
   3586 ###      ██    ██ ██          ████  ████ ██   ██ ██ ████   ██      ###
   3587 ###      ██    ██ ███████     ██ ████ ██ ███████ ██ ██ ██  ██      ###
   3588 ###      ██    ██      ██     ██  ██  ██ ██   ██ ██ ██  ██ ██      ###
   3589 ###       ██████  ███████     ██      ██ ██   ██ ██ ██   ████      ###
   3590 ###                                                                ###
   3591 ###                                                                ###
   3592 ######################################################################
   3593 # Main keymap for special characters on the standard US layout
   3594 keymap("OptSpecialChars - US", {
   3595 
   3596     # Number keys row with Option
   3597     ######################################################
   3598     C("Alt-Grave"): [UC(0x0060), C("Shift-Left"), setDK(0x0060)],       # Dead Key Accent: Grave
   3599 
   3600     C("Alt-1"):                 UC(0x00A1),                     # ¡ Inverted Exclamation Mark
   3601     C("Alt-2"):                 UC(0x2122),                     # ™ Trade Mark Sign Emoji
   3602     C("Alt-3"):                 UC(0x00A3),                     # £ British Pound currency symbol
   3603     C("Alt-4"):                 UC(0x00A2),                     # ¢ Cent currency symbol
   3604     C("Alt-5"):                 UC(0x221E),                     # ∞ Infinity mathematical symbol
   3605     C("Alt-6"):                 UC(0x00A7),                     # § Section symbol
   3606     C("Alt-7"):                 UC(0x00B6),                     # ¶ Paragraph mark (Pilcrow) symbol
   3607     C("Alt-8"):                 UC(0x2022),                     # • Bullet Point symbol (solid)
   3608     C("Alt-9"):                 UC(0x00AA),                     # ª Feminine Ordinal Indicator
   3609     C("Alt-0"):                 UC(0x00BA),                     # º Masculine Ordinal Indicator
   3610     C("Alt-Minus"):             UC(0x2013),                     # – En Dash punctuation mark
   3611     C("Alt-Equal"):             UC(0x2260),                     # ≠ Not Equal To symbol
   3612 
   3613     # Number keys row with Shift+Option
   3614     ######################################################
   3615     C("Shift-Alt-Grave"):       UC(0x0060),                     # ` Grave Accent (non-combining)
   3616     C("Shift-Alt-1"):           UC(0x2044),                     # ⁄ Fraction Slash
   3617     C("Shift-Alt-2"):           UC(0x20AC),                     # € Euro currency symbol
   3618     C("Shift-Alt-3"):           UC(0x2039),                     # ‹ Single Left-Pointing Angle Quotation mark
   3619     C("Shift-Alt-4"):           UC(0x203A),                     # › Single Right-Pointing Angle Quotation mark
   3620     C("Shift-Alt-5"):           UC(0xFB01),                     # fi Latin Small Ligature Fi
   3621     C("Shift-Alt-6"):           UC(0xFB02),                     # fl Latin Small Ligature Fl
   3622     C("Shift-Alt-7"):           UC(0x2021),                     # ‡ Double dagger (cross) symbol
   3623     C("Shift-Alt-8"):           UC(0x00B0),                     # ° Degree Sign
   3624     C("Shift-Alt-9"):           UC(0x00B7),                     # · Middle Dot (interpunct/middot)
   3625     C("Shift-Alt-0"):           UC(0x201A),                     # ‚ Single low-9 quotation mark
   3626     C("Shift-Alt-Minus"):       UC(0x2014),                     # — Em Dash punctuation mark
   3627     C("Shift-Alt-Equal"):       UC(0x00B1),                     # ± Plus Minus mathematical symbol
   3628 
   3629     # Tab key row with Option
   3630     ######################################################
   3631     C("Alt-Q"):                 UC(0x0153),                     # œ Small oe (oethel) ligature
   3632     C("Alt-W"):                 UC(0x2211),                     # ∑ N-Ary Summation (sigma) notation
   3633 
   3634     C("Alt-E"):         [UC(0x00B4), C("Shift-Left"), setDK(0x00B4)],   # Dead Key Accent: Acute
   3635 
   3636     C("Alt-R"):                 UC(0x00AE),                     # ® Registered Trade Mark Sign
   3637     C("Alt-T"):                 UC(0x2020),                     # † Simple dagger (cross) symbol
   3638     C("Alt-Y"):                 UC(0x00A5),                     # ¥ Japanese Yen currency symbol
   3639 
   3640     C("Alt-U"):         [UC(0x00A8), C("Shift-Left"), setDK(0x00A8)],   # Dead Key Accent: Umlaut/Diaeresis
   3641 
   3642     C("Alt-I"):         [UC(0x02C6), C("Shift-Left"), setDK(0x02C6)],   # Dead Key Accent: Circumflex
   3643 
   3644     C("Alt-O"):                 UC(0x00F8),                     # ø Latin Small Letter o with Stroke
   3645     C("Alt-P"):                 UC(0x03C0),                     # π Greek Small Letter Pi
   3646     C("Alt-Left_Brace"):        UC(0x201C),                     # “ Left Double Quotation Mark
   3647     C("Alt-Right_Brace"):       UC(0x2018),                     # ‘ Left Single Quotation Mark
   3648     C("Alt-Backslash"):         UC(0x00AB),                     # « Left-Pointing Double Angle Quotation Mark
   3649 
   3650     # Tab key row with Shift+Option
   3651     ######################################################
   3652     C("Shift-Alt-Q"):           UC(0x0152),                     # Œ Capital OE (Oethel) ligature
   3653     C("Shift-Alt-W"):           UC(0x201E),                     # „ Double Low-9 Quotation mark
   3654     C("Shift-Alt-E"):           UC(0x00B4),                     # ´ Acute Accent diacritic (non-combining)
   3655     C("Shift-Alt-R"):           UC(0x2030),                     # ‰ Per mille symbol (zero over zero-zero)
   3656     C("Shift-Alt-T"):           UC(0x02C7),                     # ˇ Caron/hacek diacritic (non-combining)
   3657     C("Shift-Alt-Y"):           UC(0x00C1),                     # Á Latin Capital Letter A with Acute
   3658     C("Shift-Alt-U"):           UC(0x00A8),                     # ¨ Diaeresis/Umlaut (non-combining)
   3659     C("Shift-Alt-I"):           UC(0x02C6),                     # ˆ Circumflex Accent (non-combining)
   3660     C("Shift-Alt-O"):           UC(0x00D8),                     # Ø Latin Capital Letter O with Stroke
   3661     C("Shift-Alt-P"):           UC(0x220F),                     # ∏ N-Ary Product mathematical symbol
   3662     C("Shift-Alt-Left_Brace"):  UC(0x201D),                     # ” Right Double Quotation Mark
   3663     C("Shift-Alt-Right_Brace"): UC(0x2019),                     # ’ Right Single Quotation Mark
   3664     C("Shift-Alt-Backslash"):   UC(0x00BB),                     # » Right-Pointing Double Angle Quotation Mark
   3665 
   3666     # CapsLock key row with Option
   3667     ######################################################
   3668     C("Alt-A"):                 UC(0x00E5),                     # å Small Letter a with Ring Above
   3669     C("Alt-S"):                 UC(0x00DF),                     # ß German Eszett/beta (Sharfes/Sharp S)
   3670     C("Alt-D"):                 UC(0x2202),                     # ∂ Partial Differential
   3671     C("Alt-F"):                 UC(0x0192),                     # ƒ Function/florin currency symbol
   3672     C("Alt-G"):                 UC(0x00A9),                     # © Copyright Sign
   3673     C("Alt-H"):                 UC(0x02D9),                     # ˙ Dot Above diacritic (non-combining)
   3674     C("Alt-J"):                 UC(0x2206),                     # ∆ Increment, laplace operator symbol
   3675     C("Alt-K"):                 UC(0x02DA),                     # ˚ Ring Above diacritic (non-combining)
   3676     C("Alt-L"):                 UC(0x00AC),                     # ¬ Not Sign angled dash symbol
   3677     C("Alt-Semicolon"):         UC(0x2026),                     # … Horizontal ellipsis
   3678     C("Alt-Apostrophe"):        UC(0x00E6),                     # æ Small ae ligature
   3679 
   3680     # CapsLock key row with Shift+Option
   3681     ######################################################
   3682     C("Shift-Alt-A"):           UC(0x00C5),                     # Å Capital Letter A with Ring Above
   3683     C("Shift-Alt-S"):           UC(0x00CD),                     # Í Latin Capital Letter I with Acute
   3684     C("Shift-Alt-D"):           UC(0x00CE),                     # Î Latin Capital Letter I with Circumflex
   3685     C("Shift-Alt-F"):           UC(0x00CF),                     # Ï Latin Capital Letter I with Diaeresis
   3686     C("Shift-Alt-G"):           UC(0x02DD),                     # ˝ Double Acute Accent (non-combining)
   3687     C("Shift-Alt-H"):           UC(0x00D3),                     # Ó Latin Capital Letter O with Acute
   3688     C("Shift-Alt-J"):           UC(0x00D4),                     # Ô Latin Capital Letter O with Circumflex
   3689     #########################################################################################################
   3690     # The Apple logo is at {U+F8FF} in a Unicode Private Use Area. Only at that location in Mac fonts.
   3691     # Symbol exists at {U+F000} in Baskerville Old Face font.
   3692     C("Shift-Alt-K"):   [apple_logo_alert,UC(0xF000)],          #  Apple logo [req's Baskerville Old Face font]
   3693     C("Shift-Alt-L"):           UC(0x00D2),                     # Ò Latin Capital Letter O with Grave
   3694     C("Shift-Alt-Semicolon"):   UC(0x00DA),                     # Ú Latin Capital Letter U with Acute
   3695     C("Shift-Alt-Apostrophe"):  UC(0x00C6),                     # Æ Capital AE ligature
   3696 
   3697     # Shift keys row with Option
   3698     ######################################################
   3699     C("Alt-Z"):                 UC(0x03A9),                     # Ω Greek Capital Letter Omega
   3700     C("Alt-X"):                 UC(0x2248),                     # ≈ Almost Equal To symbol
   3701     C("Alt-C"):                 UC(0x00E7),                     # ç Small Letter c with Cedilla
   3702     C("Alt-V"):                 UC(0x221A),                     # √ Square Root radical sign
   3703     C("Alt-B"):                 UC(0x222B),                     # ∫ Integral mathematical symbol
   3704 
   3705     C("Alt-N"): [UC(0x02DC), C("Shift-Left"), setDK(0x02DC)],   # Dead Key Accent: Tilde
   3706 
   3707     C("Alt-M"):                 UC(0x00B5),                     # µ Micro (mu) symbol
   3708     C("Alt-Comma"):             UC(0x2264),                     # ≤ Less Than or Equal To symbol
   3709     C("Alt-Dot"):               UC(0x2265),                     # ≥ Greater Than or Equal To symbol
   3710     C("Alt-Slash"):             UC(0x00F7),                     # ÷ Obelus/Division symbol
   3711 
   3712     # Shift keys row with Shift+Option
   3713     ######################################################
   3714     C("Shift-Alt-Z"):           UC(0x00B8),                     # ¸ Spacing Cedilla diacritic (non-combining)
   3715     C("Shift-Alt-X"):           UC(0x02DB),                     # ˛ Ogonek diacritic (non-combining)
   3716     C("Shift-Alt-C"):           UC(0x00C7),                     # Ç Capital Letter C with Cedilla
   3717     C("Shift-Alt-V"):           UC(0x25CA),                     # ◊ Lozenge (diamond) shape symbol
   3718     C("Shift-Alt-B"):           UC(0x0131),                     # ı Latin Small Letter Dotless i
   3719     C("Shift-Alt-N"):           UC(0x02DC),                     # ˜ Small Tilde character
   3720     C("Shift-Alt-M"):           UC(0x00C2),                     # Â Latin Capital Letter A with Circumflex
   3721     C("Shift-Alt-Comma"):       UC(0x00AF),                     # ¯ Macron/overline/overbar (non-combining)
   3722     C("Shift-Alt-Dot"):         UC(0x02D8),                     # ˘ Breve diacritic (non-combining)
   3723     C("Shift-Alt-Slash"):       UC(0x00BF),                     # ¿ Inverted Question mark
   3724 
   3725 }, when = lambda ctx:
   3726     cnfg.screen_has_focus and
   3727     cnfg.optspec_layout == 'US' and
   3728     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   3729 )
   3730 
   3731 
   3732 
   3733 ####################################  USER APPS  #####################################
   3734 ###                                                                                ###
   3735 ###                                                                                ###
   3736 ###      ██    ██ ███████ ███████ ██████       █████  ██████  ██████  ███████      ###
   3737 ###      ██    ██ ██      ██      ██   ██     ██   ██ ██   ██ ██   ██ ██           ###
   3738 ###      ██    ██ ███████ █████   ██████      ███████ ██████  ██████  ███████      ###
   3739 ###      ██    ██      ██ ██      ██   ██     ██   ██ ██      ██           ██      ###
   3740 ###       ██████  ███████ ███████ ██   ██     ██   ██ ██      ██      ███████      ###
   3741 ###                                                                                ###
   3742 ###                                                                                ###
   3743 ######################################################################################
   3744 ### This is a good location in the config file for adding new custom keymaps for
   3745 ### user applications and custom function keys. Watch out that you don't override
   3746 ### any "general" shortcuts like Cmd+Z/X/C/V that may be defined below this section.
   3747 ### Changes made between the "slice" marks will be retained by the Toshy installer
   3748 ### if you reinstall and it finds matching start/end markers for each section.
   3749 
   3750 ###################################################################################################
   3751 ###  SLICE_MARK_START: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   3752 
   3753 
   3754 keymap("User hardware keys", {
   3755     # PUT UNIVERSAL REMAPS FOR HARDWARE KEYS HERE
   3756     # KEYMAP WILL BE ACTIVE IN ALL DESKTOP ENVIRONMENTS/DISTROS
   3757 
   3758     C("RC-Space"): open_launcher,  # Command+Space to open Albert launcher
   3759 
   3760 }, when = lambda ctx:
   3761     cnfg.screen_has_focus and
   3762     matchProps(not_clas=remoteStr)(ctx)
   3763 )
   3764 
   3765 ###  SLICE_MARK_END: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
   3766 ###################################################################################################
   3767 
   3768 
   3769 # HOW TO SWAP CMD+SPACE AND CTRL+SPACE (SPOTLIGHT EQUIVALENT VS INPUT SWITCHING)
   3770 # Copy this entire block into the "user_apps" editable slice markers, and
   3771 # uncomment everything below this text. Fix the output shortcuts to work on your
   3772 # chosen desktop environment.
   3773 #
   3774 # keymap("User overrides terminals", {
   3775 #     C("LC-Space"):              [iEF2NT(),C("THE-REAL-COMBO-FOR-SOME-LAUNCHER")],    # Spotlight equivalent
   3776 #     C("Shift-LC-Space"):        None,    # block the default general terminals shortcut for input switching
   3777 # }, when = lambda ctx:
   3778 #       cnfg.screen_has_focus and
   3779 #       matchProps(clas=termStr)(ctx)
   3780 # )
   3781 # keymap("User overrides general", {
   3782 #     C("Super-Space"):           [iEF2NT(),C("THE-REAL-COMBO-FOR-SOME-LAUNCHER")],    # Spotlight equivalent
   3783 #     C("Shift-Super-Space"):     None,    # block the default general GUI shortcut for reverse input switching
   3784 #     ### Keyboard input source (language/layout) switching
   3785 #     C("RC-Space"):              [bind,C("THE-REAL-COMBO-FOR-INPUT-SWITCHING")],    # input switch forward
   3786 #     C("Shift-RC-Space"):        [bind,C("THE-REAL-COMBO-FOR-REVERSE-INPUT-SWITCHING")],    # input switch reverse (OPTIONAL)
   3787 # }, when = lambda ctx:
   3788 #       cnfg.screen_has_focus and
   3789 #       matchProps(clas=remoteStr)(ctx)
   3790 # )
   3791 
   3792 
   3793 
   3794 #################################  MISC APPS  #####################################
   3795 ###                                                                             ###
   3796 ###                                                                             ###
   3797 ###      ███    ███ ██ ███████  ██████      █████  ██████  ██████  ███████      ###
   3798 ###      ████  ████ ██ ██      ██          ██   ██ ██   ██ ██   ██ ██           ###
   3799 ###      ██ ████ ██ ██ ███████ ██          ███████ ██████  ██████  ███████      ###
   3800 ###      ██  ██  ██ ██      ██ ██          ██   ██ ██      ██           ██      ###
   3801 ###      ██      ██ ██ ███████  ██████     ██   ██ ██      ██      ███████      ###
   3802 ###                                                                             ###
   3803 ###                                                                             ###
   3804 ###################################################################################
   3805 # Miscellaneous apps that need a few fixes
   3806 
   3807 keymap("Thunderbird email client", {
   3808     C("Alt-RC-I"):              C("Shift-C-I"),                 # Dev tools
   3809     # Enable Cmd+Option+Left/Right for tab navigation
   3810     C("RC-Alt-Left"):          [bind,C("C-Page_Up")],           # Go to prior tab (macOS Thunderbird tab nav shortcut)
   3811     C("RC-Alt-Right"):         [bind,C("C-Page_Down")],         # Go to next tab (macOS Thunderbird tab nav shortcut)
   3812 }, when = lambda ctx:
   3813     cnfg.screen_has_focus and
   3814     matchProps(clas="^thunderbird.*$|^org.mozilla.thunderbird$")(ctx) )
   3815 
   3816 keymap("Angry IP Scanner", {
   3817     C("RC-comma"):              C("Shift-C-P"),                 # Open preferences
   3818     C("RC-i"):                  C("Alt-Enter"),                 # Get info (details)
   3819     C("RC-h"):                  C("C-h"),                       # Go to next live host (override hide window)
   3820     C("Shift-RC-i"):            C("C-i"),                       # Invert selection
   3821 }, when = lambda ctx:
   3822     cnfg.screen_has_focus and
   3823     matchProps(clas="^Angry.*IP.*Scanner$")(ctx) )
   3824 
   3825 keymap("Transmission bittorrent client", {
   3826     C("RC-i"):                  C("Alt-Enter"),                 # Open properties (Get Info) dialog
   3827     C("RC-comma"):             [C("Alt-e"),C("p")],             # Open preferences (settings) dialog
   3828 }, when = lambda ctx:
   3829     cnfg.screen_has_focus and
   3830     matchProps(clas=transmissionStr)(ctx) )
   3831 
   3832 keymap("JDownloader", {
   3833     # Fixes for tab navigation done here instead of in the main tab nav fix keymaps,
   3834     # because we have to use a "list of dicts" to match some  JDownloader windows.
   3835     C("Shift-RC-Left_Brace"):  [bind,C("C-Shift-Tab")],         # Tab nav: Go to prior tab (left)
   3836     C("Shift-RC-Right_Brace"): [bind,C("C-Tab")],               # Tab nav: Go to next tab (right)
   3837     C("Shift-RC-Left"):        [bind,C("C-Shift-Tab")],         # Tab nav: Go to prior tab (left)
   3838     C("Shift-RC-Right"):       [bind,C("C-Tab")],               # Tab nav: Go to next tab (right)
   3839 
   3840     C("RC-i"):                  C("Alt-Enter"),                 # Open properties
   3841     C("RC-Backspace"):          C("Delete"),                    # Remove download from list
   3842     C("RC-Comma"):              C("C-P"),                       # Open preferences (settings)
   3843 }, when = lambda ctx:
   3844     cnfg.screen_has_focus and
   3845     matchProps(lst=JDownloader_lod)(ctx) )
   3846 
   3847 keymap("Totem video player", {
   3848     C("RC-dot"):                C("C-q"),                       # Stop (quit player, there is no "Stop" function)
   3849 }, when = lambda ctx:
   3850     cnfg.screen_has_focus and
   3851     matchProps(clas="^totem$")(ctx) )
   3852 
   3853 keymap("GNOME image viewer", {
   3854     C("RC-i"):                  C("Alt-Enter"),                 # Image properties
   3855 }, when = lambda ctx:
   3856     cnfg.screen_has_focus and
   3857     matchProps(clas="^eog$")(ctx) )
   3858 
   3859 keymap("LibreOffice Writer", {
   3860     C("RC-comma"):              C("Alt-F12"),                   # Tools > Options (preferences dialog)
   3861 }, when = lambda ctx:
   3862     cnfg.screen_has_focus and
   3863     matchProps(clas="^libreoffice-writer$")(ctx) )
   3864 
   3865 
   3866 
   3867 ########################################  FINDER MODS  #############################################
   3868 ###                                                                                              ###
   3869 ###                                                                                              ###
   3870 ###     ███████ ██ ███    ██ ██████  ███████ ██████      ███    ███  ██████  ██████  ███████     ###
   3871 ###     ██      ██ ████   ██ ██   ██ ██      ██   ██     ████  ████ ██    ██ ██   ██ ██          ###
   3872 ###     █████   ██ ██ ██  ██ ██   ██ █████   ██████      ██ ████ ██ ██    ██ ██   ██ ███████     ###
   3873 ###     ██      ██ ██  ██ ██ ██   ██ ██      ██   ██     ██  ██  ██ ██    ██ ██   ██      ██     ###
   3874 ###     ██      ██ ██   ████ ██████  ███████ ██   ██     ██      ██  ██████  ██████  ███████     ###
   3875 ###                                                                                              ###
   3876 ###                                                                                              ###
   3877 ####################################################################################################
   3878 
   3879 ###  START OF FILE MANAGER GROUP OF KEYMAPS - FINDER MODS  ###
   3880 
   3881 # Keybindings overrides for Caja
   3882 # (overrides some bindings from general file manager code block below)
   3883 keymap("Overrides for Caja - Finder Mods", {
   3884     C("RC-Super-o"):            C("Shift-C-Enter"),             # Open in new tab
   3885     # C("RC-Super-o"):            C("Shift-C-W"),                 # Open in new window
   3886 }, when = lambda ctx:
   3887     cnfg.screen_has_focus and
   3888     matchProps(clas="^caja$")(ctx) )
   3889 
   3890 # Keybindings overrides for COSMIC Files
   3891 # (overrides some bindings from general file manager code block below)
   3892 keymap("Overrides for COSMIC Files - Finder Mods", {
   3893     # No shortcuts yet to change the view modes?
   3894     # TODO: Add Grid/List view mode shortcuts when available (if different from general FMs)
   3895     # Tab nav uses Ctrl+Tab/Shift+Ctrl+Tab
   3896     C("Shift-RC-Left_Brace"):  [bind,C("Shift-C-Tab")],         # Go to prior tab (left)
   3897     C("Shift-RC-Right_Brace"): [bind,C("C-Tab")],               # Go to next tab (right)
   3898     C("Shift-RC-Left"):        [bind,C("Shift-C-Tab")],         # Go to prior tab (left)
   3899     C("Shift-RC-Right"):       [bind,C("C-Tab")],               # Go to next tab (right)
   3900     # COSMIC uses nonstandard shortcut (Space) for file/folder Properties dialog
   3901     C("RC-i"):                  C("Space"),                     # Get info (properties) [MacOS shortcut]
   3902     C("Alt-Enter"):             C("Space"),                     # Get info (properties) [Linux shortcut]
   3903 }, when = lambda ctx:
   3904     cnfg.screen_has_focus and
   3905     matchProps(clas="^com.system76.CosmicFiles$")(ctx) )
   3906 
   3907 # Keybindings overrides for DDE (Deepin) File Manager
   3908 # (overrides some bindings from general file manager code block below)
   3909 keymap("Overrides for DDE File Manager - Finder Mods", {
   3910     C("RC-i"):                  C("C-i"),                       # File properties dialog (Get Info)
   3911     C("RC-comma"):              None,                           # Disable preferences shortcut (no shortcut available)
   3912     C("RC-Up"):                 C("C-Up"),                      # Go Up dir
   3913     C("Shift-RC-Left_Brace"):  [bind,C("C-Shift-Tab")],         # Go to prior tab
   3914     C("Shift-RC-Right_Brace"): [bind,C("C-Tab")],               # Go to next tab
   3915     C("Shift-RC-Left"):        [bind,C("C-Shift-Tab")],         # Go to prior tab
   3916     C("Shift-RC-Right"):       [bind,C("C-Tab")],               # Go to next tab
   3917 }, when = lambda ctx:
   3918     cnfg.screen_has_focus and
   3919     matchProps(clas="^dde-file-manager$")(ctx) )
   3920 
   3921 ##########################  DOLPHIN KEYMAPS - BEGIN  ##########################
   3922 # Keybindings overrides for Dolphin (KDE file manager)
   3923 # (overrides some bindings from general file manager code block below)
   3924 
   3925 keymap("Overrides for Dolphin - Finder Mods pre-KF6", {
   3926     # KDE Frameworks 6 assigns F10 to "Open Main Manu" so this is only valid for KF5 now
   3927     # (Reference https://planet.kde.org/felix-ernst-2023-10-13-f10-for-accessibility-in-kf6/)
   3928     C("Shift-RC-n"):            iEF2(C("F10"), False),          # Create new folder (F10), toggle Enter to be Enter (pre-KF6!)
   3929 }, when = lambda ctx:
   3930     cnfg.screen_has_focus and
   3931     DESKTOP_ENV == 'kde' and DE_MAJ_VER in ['5', '4', '3'] and
   3932     matchProps(clas="^dolphin$|^org.kde.dolphin$")(ctx)
   3933 )
   3934 
   3935 # Dolphin dialog names where Enter should always be Enter (not F2/Enter toggle)
   3936 dlgs_Dolphin_Enter_is_Enter = [
   3937     "Edit Places Entry.*",
   3938     "Create New File.*",
   3939     "Folder Already Exists.*",
   3940     "File Already Exists.*",
   3941 ]
   3942 # Convert list to regex pattern string
   3943 dlgs_Dolphin_Enter_is_Enter_Str = toRgxStr(dlgs_Dolphin_Enter_is_Enter)
   3944 
   3945 keymap("Overrides for Dolphin dialogs - Finder Mods", {
   3946     C("Enter"):                 C("Enter"),                     # Override Enter to be Enter (never F2) for dialogs
   3947 }, when = lambda ctx:
   3948     cnfg.screen_has_focus and
   3949     matchProps(clas="^dolphin$|^org.kde.dolphin$", name=dlgs_Dolphin_Enter_is_Enter_Str)(ctx)
   3950 )
   3951 
   3952 keymap("Overrides for Dolphin - Finder Mods", {
   3953     C("RC-r"):                  C("F5"),                        # Refresh folder view with Cmd+R (like browsers)
   3954     C("RC-KEY_2"):              C("C-KEY_3"),                   # View as List (Detailed)
   3955     C("RC-KEY_3"):              C("C-KEY_2"),                   # View as List (Compact)
   3956     ##########################################################################################
   3957     ### "Open in new window" (or new tab) requires manually setting custom shortcut of Ctrl+Shift+o
   3958     ### in Dolphin's keyboard shortcuts. There is no default shortcut set for this function.
   3959     ##########################################################################################
   3960     C("RC-Super-o"):            C("Shift-C-o"),                 # Open in new window (or new tab, user's choice, see above)
   3961     C("Shift-RC-n"):            iEF2(C("Shift-C-n"), False),    # Create new folder, toggle Enter to be Enter (KF6 and later)
   3962     C("RC-comma"):              C("Shift-C-comma"),             # Open preferences dialog
   3963 }, when = lambda ctx:
   3964     cnfg.screen_has_focus and
   3965     matchProps(clas="^dolphin$|^org.kde.dolphin$")(ctx) )
   3966 
   3967 #
   3968 ###########################  DOLPHIN KEYMAPS - END  ###########################
   3969 
   3970 
   3971 # Keybindings overrides for elementary OS Files (Pantheon)
   3972 # (overrides some bindings from general file manager code block below)
   3973 keymap("Overrides for Pantheon - Finder Mods", {
   3974     C("RC-Super-o"):            C("Shift-Enter"),               # Open folder in new tab
   3975     C("RC-comma"):              None,                           # Disable preferences shortcut since none available
   3976 }, when = lambda ctx:
   3977     cnfg.screen_has_focus and
   3978     matchProps(clas="^io.elementary.files$")(ctx) )
   3979 
   3980 # Keybindings overrides for Krusader (alternative/old KDE file manager)
   3981 # (overrides some bindings from general file manager code block below)
   3982 keymap("Overrides for Krusader - Finder Mods", {
   3983     C("Shift-RC-Dot"):          C("Alt-Dot"),                   # Toggle hidden files visibility ("dot" files)
   3984     # C("RC-n"):                  None,                           # Enable this to block Cmd+N (new window is not possible?)
   3985     C("RC-Shift-n"):            iEF2(C("F7"), False),           # New folder (F7)
   3986     C("RC-KEY_1"):              None,                           # View as Icons [Not available, blocked]
   3987     C("RC-KEY_2"):              C("Alt-Shift-D"),               # View as List (Detailed)
   3988     C("RC-KEY_3"):              C("Alt-Shift-B"),               # View as List (Compact)
   3989     C("RC-KEY_4"):              None,                           # View as Gallery [Not available, blocked]
   3990     C("RC-Backspace"):         [C("Delete"), sleep(0.5), C("Tab"), sleep(0.5), C("Enter")],    # Delete file/folder (auto-confirm)
   3991     # C("RC-Backspace"):          C("Delete"),                    # Delete file/folder (no auto-confirm)
   3992 }, when = lambda ctx:
   3993     cnfg.screen_has_focus and
   3994     matchProps(clas="^org.kde.krusader$|^krusader$")(ctx) )
   3995 
   3996 # Keybindings overrides for Nautilus
   3997 # (overrides some bindings from general file manager code block below)
   3998 keymap("Overrides for Nautilus Create Archive dialog - Finder Mods", {
   3999     C("Enter"):                 C("Enter"),                     # Use Enter as Enter in the Create Archive dialog
   4000 }, when = lambda ctx:
   4001     cnfg.screen_has_focus and
   4002     matchProps(clas="^org.gnome.nautilus$|^nautilus$", name="Create Archive")(ctx) )
   4003 
   4004 keymap("Overrides for Nautilus - Finder Mods", {
   4005     # Optional "new window at home folder" in Nautilus
   4006     # C("RC-N"):                  C("C-Alt-Space"),               # macOS Finder search window shortcut Cmd+Option+Space
   4007     # For the above shortcut to work, a custom shortcut bound to Ctrl+Alt+Space must be set up in the
   4008     # Settings app in GNOME to run command: "nautilus --new-window /home/USER" [ replace "USER" ]
   4009     C("RC-KEY_1"):              C("C-KEY_2"),                   # View as Icons
   4010     C("RC-KEY_2"):              C("C-KEY_1"),                   # View as List (Detailed)
   4011     # C("RC-Super-o"):            C("Shift-Enter"),               # Open in new window (disable line below)
   4012     C("RC-Super-o"):            C("C-Enter"),                   # Open in new tab (disable line above)
   4013     C("RC-comma"):              C("C-comma"),                   # Overrides "Open preferences dialog" shortcut below
   4014     C("RC-F"):                  C("C-F"),                       # Don't toggle Enter key, pass Cmd+F
   4015 }, when = lambda ctx:
   4016     cnfg.screen_has_focus and
   4017     matchProps(clas="^org.gnome.nautilus$|^nautilus$")(ctx) )
   4018 
   4019 # Keybindings overrides for Nemo
   4020 # (overrides some bindings from general file manager code block below)
   4021 keymap("Overrides for Nemo - Finder Mods", {
   4022     C("RC-Backspace"):          iEF2(C("Delete"), False),       # Set Enter to Enter for Cmd+Delete confirmation
   4023 }, when = lambda ctx:
   4024     cnfg.screen_has_focus and
   4025     matchProps(clas="^nemo$")(ctx) )
   4026 
   4027 # Keybindings overrides for PCManFM and PCManFM-Qt
   4028 # (overrides some bindings from general file manager code block below)
   4029 keymap("Overrides for PCManFM-Qt - Finder Mods - LXQt desktop", {
   4030     C("Enter"):                 C("Enter"),                     # Use Enter as Enter on the LXQt desktop
   4031 }, when = lambda ctx:
   4032     cnfg.screen_has_focus and
   4033     matchProps(clas="^pcmanfm-qt$", name="^pcmanfm-desktop.*$")(ctx) )
   4034 keymap("Overrides for PCManFM-Qt - Finder Mods", {
   4035     C("RC-Backspace"):          C("Delete"),                    # Move to Trash (delete, bypass dialog)
   4036     # Change folder view
   4037     C("RC-KEY_1"):  [C("Alt-V"), sleep(0.1), C("V"), sleep(0.1), C("I")],   # View as Icons
   4038     C("RC-KEY_2"):  [C("Alt-V"), sleep(0.1), C("V"), sleep(0.1), C("D")],   # View as List (Detailed)
   4039     C("RC-KEY_3"):  [C("Alt-V"), sleep(0.1), C("V"), sleep(0.1), C("C")],   # View as List (Compact)
   4040     C("RC-KEY_4"):  [C("Alt-V"), sleep(0.1), C("V"), sleep(0.1), C("T")],   # View as Thumbnails
   4041 }, when = lambda ctx:
   4042     cnfg.screen_has_focus and
   4043     matchProps(clas="^pcmanfm-qt$")(ctx) )
   4044 
   4045 keymap("Overrides for PCManFM - Finder Mods", {
   4046     C("RC-KEY_2"):              C("C-KEY_4"),                   # View as List (Detailed) [Not in PCManFM-Qt]
   4047     C("RC-Backspace"):         [C("Delete"),C("Space")],        # Move to Trash (delete, bypass dialog)
   4048     C("RC-F"):                  C("C-F"),                       # Don't toggle Enter key state, pass Cmd+F (Ctrl+F)
   4049 }, when = lambda ctx:
   4050     cnfg.screen_has_focus and
   4051     matchProps(clas="^pcmanfm$|^pcmanfm-qt$")(ctx) )
   4052 
   4053 keymap("Overrides for Peony-Qt - Finder Mods", {
   4054     C("RC-Comma"):              None,                           # Block Cmd+Comma (doesn't work in Peony)
   4055     C("RC-Equal"):              C("Shift-C-Equal"),             # Enlarge icons
   4056     C("Shift-RC-Left_Brace"):  [bind,C("C-Shift-Tab")],         # Go to prior tab
   4057     C("Shift-RC-Right_Brace"): [bind,C("C-Tab")],               # Go to next tab
   4058     C("Shift-RC-Left"):        [bind,C("C-Shift-Tab")],         # Go to prior tab
   4059     C("Shift-RC-Right"):       [bind,C("C-Tab")],               # Go to next tab
   4060 }, when = lambda ctx:
   4061     cnfg.screen_has_focus and
   4062     matchProps(clas="^peony-qt$")(ctx) )
   4063 
   4064 # Keybindings overrides for SpaceFM
   4065 # (overrides some bindings from general file manager code block below)
   4066 keymap("Overrides for SpaceFM Find Files dialog - Finder Mods", {
   4067     C("Enter"):                 C("Enter"),                     # Use Enter as Enter in the Find dialog
   4068     C("Esc"):                   C("Alt-F4"),                    # Close Find Files dialog with Escape
   4069     C("RC-W"):                  C("Alt-F4"),                    # Close Find Files dialog with Cmd+W
   4070 }, when = lambda ctx:
   4071     cnfg.screen_has_focus and
   4072     matchProps(clas="^SpaceFM$", name="Find Files")(ctx) )
   4073 
   4074 keymap("Overrides for SpaceFM - Finder Mods", {
   4075     C("RC-Page_Up"):           [bind,C("C-Shift-Tab")],         # Go to prior tab
   4076     C("RC-Page_Down"):         [bind,C("C-Tab")],               # Go to next tab
   4077     C("Shift-RC-Left_Brace"):  [bind,C("C-Shift-Tab")],         # Go to prior tab
   4078     C("Shift-RC-Right_Brace"): [bind,C("C-Tab")],               # Go to next tab
   4079     C("Shift-RC-Left"):        [bind,C("C-Shift-Tab")],         # Go to prior tab
   4080     C("Shift-RC-Right"):       [bind,C("C-Tab")],               # Go to next tab
   4081     C("Shift-RC-N"):            iEF2(C("C-F"), False),          # Switch Enter to Enter. New folder is Ctrl+F(???)
   4082     # Need to catch WM_NAME of "Find Files" and override Enter key state back to being Enter. See above keymap.
   4083     C("RC-F"):                  None,                           # No direct shortcut available and menu macros don't work in SpaceFM.
   4084     C("RC-Backspace"):         [C("Delete"),C("Space")],        # Move to Trash (delete, bypass dialog)
   4085     C("RC-comma"):             [C("Alt-V"),C("p")],             # Overrides "Open preferences dialog" shortcut below
   4086     # This shortcut ^^^^^^^^^^^^^^^ is not fully working in SpaceFM. Opens "View" menu but not Preferences.
   4087     # SpaceFM is doing some nasty binding that blocks all shortcuts, including Alt+Tab, while any menu is open.
   4088 }, when = lambda ctx:
   4089     cnfg.screen_has_focus and
   4090     matchProps(clas="^spacefm$")(ctx) )
   4091 
   4092 # Keybindings overrides for Thunar
   4093 # (overrides some bindings from general file manager code block below)
   4094 keymap("Overrides for Thunar - Finder Mods", {
   4095     C("RC-Super-o"):            C("Shift-C-P"),                 # Open in new tab
   4096     C("RC-comma"):             [C("Alt-E"),C("E")],             # Overrides "Open preferences dialog" shortcut below
   4097     C("RC-F"):                  C("C-F"),                       # Don't toggle Enter key, pass Cmd+F (Ctrl+F)
   4098 }, when = lambda ctx:
   4099     cnfg.screen_has_focus and
   4100     matchProps(clas="^thunar$")(ctx) )
   4101 
   4102 # Keybindings overrides for GNOME XDG "Save As" and "Open File" dialogs
   4103 file_open_save_dialogs = [
   4104     {
   4105         clas: "^xdg-desktop-portal-gnome$|^Firefox.*$|^LibreWolf$|^Waterfox$|^zen.*$",
   4106         name: "^Open File$|^Save As$"
   4107     },
   4108 ]
   4109 keymap("XDG file dialogs", {
   4110     C("RC-Left"):               C("Alt-Left"),                  # Go Back
   4111     C("RC-Right"):              C("Alt-Right"),                 # Go Forward
   4112     C("RC-Up"):                 C("Alt-Up"),                    # Go Up dir
   4113     C("RC-Down"):               C("Enter"),                     # Go Down dir (open folder/file) [universal]
   4114 }, when = lambda ctx:
   4115     cnfg.screen_has_focus and
   4116     matchProps(lst=file_open_save_dialogs)(ctx)
   4117 )
   4118 
   4119 ####################################################################################################
   4120 ##  Keybindings for Linux general file managers group:
   4121 ##
   4122 ##  Currently supported Linux file managers (file browsers):
   4123 ##
   4124 ##  Caja File Browser       (MATE file manager, fork of Nautilus)
   4125 ##  COSMIC Files            (Pop!_OS COSMIC desktop environment file manager)
   4126 ##  DDE File Manager        (Deepin Linux file manager)
   4127 ##  Dolphin                 (KDE file manager)
   4128 ##  Krusader                (Alternative/old KDE file manager)
   4129 ##  Nautilus                (GNOME file manager, may be named "Files")
   4130 ##  Nemo                    (Cinnamon file manager, fork of Nautilus, may be named "Files")
   4131 ##  Pantheon Files          (elementary OS file manager, may be named "Files")
   4132 ##  PCManFM                 (LXDE file manager)
   4133 ##  PCManFM-Qt              (LXQt file manager)
   4134 ##  Peony-Qt                (UKUI file manager, found in Ubuntu Kylin)
   4135 ##  SpaceFM                 (Fork of PCManFM file manager)
   4136 ##  Thunar File Manager     (Xfce file manager)
   4137 ##
   4138 ##  GNOME XDG file dialogs ("Open File" and "Save As" windows in apps like Firefox)
   4139 ##
   4140 ####################################################################################################
   4141 
   4142 keymap("General File Managers - Finder Mods", {
   4143     ###########################################################################################################
   4144     ###  Show Properties (Get Info) | Open Settings/Preferences | Toggle hidden files visibility            ###
   4145     ###########################################################################################################
   4146     C("RC-i"):                  C("Alt-Enter"),                 # File properties dialog (Get Info)
   4147     C("RC-comma"):             [C("Alt-E"),C("N")],             # Open preferences dialog
   4148     C("Shift-RC-dot"):          C("C-H"),                       # Toggle hidden files visibility ("dot" files)
   4149     ###########################################################################################################
   4150     ###  Navigation                                                                                         ###
   4151     ###########################################################################################################
   4152 
   4153     # C("Shift-RC-Left_Brace"):   C("C-Page_Up"),                 # Go to prior tab
   4154     # C("Shift-RC-Right_Brace"):  C("C-Page_Down"),               # Go to next tab
   4155 
   4156     C("Shift-RC-Left_Brace"):   [bind,C("C-Page_Up")],          # Go to prior tab
   4157     C("Shift-RC-Right_Brace"):  [bind,C("C-Page_Down")],        # Go to next tab
   4158 
   4159     # C("Shift-RC-Left"):         C("C-Page_Up"),                 # Go to prior tab
   4160     # C("Shift-RC-Right"):        C("C-Page_Down"),               # Go to next tab
   4161 
   4162     C("Shift-RC-Left"):         [bind,C("C-Page_Up")],          # Go to prior tab
   4163     C("Shift-RC-Right"):        [bind,C("C-Page_Down")],        # Go to next tab
   4164 
   4165     C("RC-Left_Brace"):         C("Alt-Left"),                  # Go Back
   4166     C("RC-Right_Brace"):        C("Alt-Right"),                 # Go Forward
   4167 
   4168     # C("RC-Left"):               C("Alt-Left"),                  # Go Back
   4169     # C("RC-Right"):              C("Alt-Right"),                 # Go Forward
   4170     # C("RC-Up"):                 C("Alt-Up"),                    # Go Up dir
   4171     # C("RC-Down"):               C("Enter"),                     # Go Down dir (open folder/file) [universal]
   4172     # EXPERIMENTAL: Attempt to get wordwise Cmd+Left/Right to work while renaming, but otherwise be navigation
   4173     C("RC-Left"):           iEF2(C("Alt-Left"), C("Home"), True, True),      # Go Back
   4174     C("RC-Right"):          iEF2(C("Alt-Right"), C("End"), True, True),      # Go Forward
   4175     C("RC-Up"):             iEF2(C("Alt-Up"), True),            # Go Up dir
   4176     C("RC-Down"):           iEF2(C("Enter"), True),             # Go Down dir (open folder/file) [universal]
   4177 
   4178     # C("RC-Down"):               C("Alt-Down"),                  # Go Down dir (only works on folders) [not universal]
   4179     # C("RC-Down"):               C("C-O"),                       # Go Down dir (open folder/file) [not universal]
   4180     ###########################################################################################################
   4181     ###  Open in New Window | Move to Trash                                                                 ###
   4182     ###########################################################################################################
   4183     C("RC-Super-o"):            C("Shift-C-o"),                 # Open in new window (or tab, depends) [not universal]
   4184     C("RC-Backspace"):          C("Delete"),	                # Move to Trash (delete)
   4185     C("RC-Delete"):             None,                           # Block Ctrl+Delete from performing any action (error in macOS)
   4186     ###########################################################################################################
   4187     ###  ENTER-KEY-TO-RENAME CUSTOM FUNCTION SHORTCUTS                                                      ###
   4188     ###########################################################################################################
   4189     C("Enter"):             iEF2(C("F2"),C("Enter")),           # Send F2 to rename files, unless var is False
   4190     C("Shift-RC-N"):        iEF2(C("Shift-C-N"), False),        # New folder, set Enter to Enter
   4191     C("RC-L"):              iEF2(C("C-L"), False),              # Set Enter to Enter for Location field
   4192     C("RC-F"):              iEF2(C("C-F"), False),              # Set Enter to Enter for Find field
   4193     C("Esc"):               iEF2(C("Esc"), True),               # Send Escape, set Enter to be F2 next
   4194     # C("Tab"):               iEF2(C("Tab"), C("Tab"), True, True),       # Set Enter to Enter after using Tab key
   4195     C("Shift-RC-Space"):    iEF2(C("Shift-C-Space"), False),    # Set Enter to Enter for alternate overview shortcut
   4196     C("Shift-RC-Enter"):        C("Enter"),                             # alternative "Enter" key for unusual cases
   4197 }, when = lambda ctx:
   4198     cnfg.screen_has_focus and
   4199     matchProps(clas=filemanagerStr)(ctx) )
   4200 
   4201 
   4202 
   4203 ###################################  BROWSERS  #####################################
   4204 ###                                                                              ###
   4205 ###                                                                              ###
   4206 ###      ██████  ██████   ██████  ██     ██ ███████ ███████ ██████  ███████      ###
   4207 ###      ██   ██ ██   ██ ██    ██ ██     ██ ██      ██      ██   ██ ██           ###
   4208 ###      ██████  ██████  ██    ██ ██  █  ██ ███████ █████   ██████  ███████      ###
   4209 ###      ██   ██ ██   ██ ██    ██ ██ ███ ██      ██ ██      ██   ██      ██      ###
   4210 ###      ██████  ██   ██  ██████   ███ ███  ███████ ███████ ██   ██ ███████      ###
   4211 ###                                                                              ###
   4212 ###                                                                              ###
   4213 ####################################################################################
   4214 
   4215 keymap("Firefox Browsers Overrides", {
   4216 
   4217     C("C-comma"):              [C("C-t"), sleep(0.2),
   4218                                 ST("about:preferences"),
   4219                                 sleep(0.2), C("Enter")],        # Open preferences (Settings)
   4220 
   4221     # Open private window with Cmd+Shift+N like other browsers
   4222     C("Shift-RC-N"):            C("Shift-C-P"),
   4223 
   4224     # Wordwise shortcuts (overrides of general wordwise)
   4225     C("RC-Backspace"):         [C("Shift-Home"),
   4226                                 C("Backspace")],                # Delete Line Left of Cursor
   4227     C("RC-Delete"):            [C("Shift-End"), C("Delete")],   # Delete Line Right of Cursor
   4228 
   4229     # Specific numbered tab navigation - removed from general browsers keymap
   4230     C("RC-1"):                  C("Alt-1"),                     # Jump to Tab #1
   4231     C("RC-2"):                  C("Alt-2"),                     # Jump to Tab #2
   4232     C("RC-3"):                  C("Alt-3"),                     # Jump to Tab #3
   4233     C("RC-4"):                  C("Alt-4"),                     # Jump to Tab #4
   4234     C("RC-5"):                  C("Alt-5"),                     # Jump to Tab #5
   4235     C("RC-6"):                  C("Alt-6"),                     # Jump to Tab #6
   4236     C("RC-7"):                  C("Alt-7"),                     # Jump to Tab #7
   4237     C("RC-8"):                  C("Alt-8"),                     # Jump to Tab #8
   4238     C("RC-9"):                  C("Alt-9"),                     # Jump to last tab
   4239 
   4240     # Block shortcuts that might get confused with Shift+Cmd+[Left/Right]_Brace
   4241     C("Shift-RC-Minus"):        ignore_combo,                   # Ignore alternate zoom out shortcut
   4242     C("Shift-RC-Equal"):        ignore_combo,                   # Ignore alternate zoom in shortcut
   4243 
   4244 }, when = lambda ctx:
   4245     cnfg.screen_has_focus and
   4246     matchProps(clas=browsers_firefoxStr)(ctx) )
   4247 
   4248 # Zotero is a Firefox based research app
   4249 keymap("Zotero", {
   4250     # (override the Shift+Opt+Cmd+I,I diagnostic dialog shortcut in Zotero)
   4251     C("Shift-Alt-RC-i"):        C("Shift-Alt-C-i"),             # Import from clipboard
   4252 }, when = lambda ctx:
   4253     cnfg.screen_has_focus and
   4254     matchProps(clas="^.*Zotero.*$")(ctx) )
   4255 
   4256 keymap("Vivaldi browser - Settings dialog", {
   4257     C("Esc"):                   C("Alt-F4"),                    # Close Settings dialog with Escape
   4258 }, when = lambda ctx:
   4259     cnfg.screen_has_focus and
   4260     matchProps(clas="^Vivaldi.*$", name="^Vivaldi Settings:.*Vivaldi$")(ctx) )
   4261 
   4262 # Vivaldi is a Chromium based web browser
   4263 keymap("Overrides for Vivaldi browser", {
   4264 
   4265     # These get caught by the shell in KDE Plasma!!! (Show Desktop and Go to Desktop 1 shortcuts):
   4266     # C("RC-comma"):              C("C-F12"),                     # Open preferences (Settings)
   4267     # C("RC-Slash"):              C("C-F1"),                      # Keyboard Cheat Sheet (shortcuts)
   4268 
   4269     # This only opens the normal Chrome-like settings page, not the special Vivaldi settings dialog:
   4270     # C("C-comma"):              [C("C-t"), sleep(0.1),
   4271     #                             ST("chrome://settings"),
   4272     #                             sleep(0.1), C("Enter")],        # Open preferences (Settings)
   4273 
   4274     # Alternatives using macros to avoid issues with KDE Plasma global shortcuts (see above):
   4275     C("RC-Slash"):             [C("F10"),
   4276                                 sleep(0.2), C("H"),
   4277                                 sleep(0.2), C("K")],            # Keyboard Cheat Sheet (shortcuts)
   4278 
   4279     C("C-comma"):              [C("F10"),
   4280                                 sleep(0.2), C("S")],            # Open preferences (Settings)
   4281 
   4282 }, when = lambda ctx:
   4283     cnfg.screen_has_focus and
   4284     matchProps(clas="^Vivaldi.*$")(ctx) )
   4285 
   4286 # Falkon is a Chromium based web browser
   4287 keymap("Overrides for Falkon browser", {
   4288     C("RC-comma"):              C("Shift-C-comma"),             # Open preferences (Settings)
   4289 }, when = lambda ctx:
   4290     cnfg.screen_has_focus and
   4291     matchProps(clas="^org.kde.falkon$|^Falkon$")(ctx) )
   4292 
   4293 keymap("Chrome Browsers Overrides", {
   4294     # C("C-comma"):              [C("Alt-e"), C("s"),C("Enter")], # Open preferences (Settings)
   4295     C("C-comma"):              [C("C-t"), sleep(0.2),
   4296                                 ST("chrome://settings"),
   4297                                 sleep(0.2), C("Enter")],        # Open preferences (Settings)
   4298     C("RC-q"):                  C("Alt-F4"),                    # Quit Chrome(s) browsers with Cmd+Q
   4299     # C("RC-Left"):               C("Alt-Left"),                  # Page nav: Back to prior page in history (conflict with wordwise)
   4300     # C("RC-Right"):              C("Alt-Right"),                 # Page nav: Forward to next page in history (conflict with wordwise)
   4301     C("RC-Left_Brace"):         C("Alt-Left"),                  # Page nav: Back to prior page in history
   4302     C("RC-Right_Brace"):        C("Alt-Right"),                 # Page nav: Forward to next page in history
   4303 
   4304     C("RC-y"):                  C("C-H"),                       # Browser History
   4305     C("Alt-RC-u"):              C("C-U"),                       # View Page Source
   4306     C("Shift-RC-j"):            C("C-J"),                       # Show Downloads view
   4307 }, when = lambda ctx:
   4308     cnfg.screen_has_focus and
   4309     matchProps(clas=browsers_chromeStr)(ctx) )
   4310 
   4311 # Keybindings for General Web Browsers
   4312 keymap("General Web Browsers", {
   4313 
   4314     C("RC-Q"):                  C("C-Q"),                       # Close all browsers Instances
   4315     C("Alt-RC-I"):              C("Shift-C-I"),                 # Dev tools
   4316     C("Alt-RC-J"):              C("Shift-C-J"),                 # Dev tools
   4317 
   4318     # Enable Cmd+Shift+Braces for tab navigation (redundant with General GUI)
   4319     # C("Shift-RC-Left_Brace"):   C("C-Page_Up"),                 # Go to prior tab
   4320     # C("Shift-RC-Right_Brace"):  C("C-Page_Down"),               # Go to next tab
   4321 
   4322     # Enable Cmd+Option+Left/Right for tab navigation
   4323     C("RC-Alt-Left"):           C("C-Page_Up"),                 # Go to prior tab
   4324     C("RC-Alt-Right"):          C("C-Page_Down"),               # Go to next tab
   4325 
   4326     # Enable Ctrl+PgUp/PgDn for tab navigation
   4327     C("Super-Page_Up"):         C("C-Page_Up"),                 # Go to prior tab
   4328     C("Super-Page_Down"):       C("C-Page_Down"),               # Go to next tab
   4329 
   4330     # Use Cmd+Braces keys for tab navigation instead of page navigation
   4331     # C("C-Left_Brace"):        C("C-Page_Up"),
   4332     # C("C-Right_Brace"):       C("C-Page_Down"),
   4333 
   4334 }, when = lambda ctx:
   4335     cnfg.screen_has_focus and
   4336     matchProps(clas=browsers_allStr)(ctx) )
   4337 
   4338 
   4339 
   4340 ############################################  CODE EDITORS  ##############################################
   4341 ###                                                                                                    ###
   4342 ###                                                                                                    ###
   4343 ###       ██████  ██████  ██████  ███████     ███████ ██████  ██ ████████  ██████  ██████  ███████     ###
   4344 ###      ██      ██    ██ ██   ██ ██          ██      ██   ██ ██    ██    ██    ██ ██   ██ ██          ###
   4345 ###      ██      ██    ██ ██   ██ █████       █████   ██   ██ ██    ██    ██    ██ ██████  ███████     ###
   4346 ###      ██      ██    ██ ██   ██ ██          ██      ██   ██ ██    ██    ██    ██ ██   ██      ██     ###
   4347 ###       ██████  ██████  ██████  ███████     ███████ ██████  ██    ██     ██████  ██   ██ ███████     ###
   4348 ###                                                                                                    ###
   4349 ###                                                                                                    ###
   4350 ##########################################################################################################
   4351 
   4352 
   4353 # Keybindings for IntelliJ
   4354 keymap("Jetbrains", {
   4355     # General
   4356     C("C-Key_0"):               C("Alt-Key_0"),                 # Open corresponding tool window
   4357     C("C-Key_1"):               C("Alt-Key_1"),                 # Open corresponding tool window
   4358     C("C-Key_2"):               C("Alt-Key_2"),                 # Open corresponding tool window
   4359     C("C-Key_3"):               C("Alt-Key_3"),                 # Open corresponding tool window
   4360     C("C-Key_4"):               C("Alt-Key_4"),                 # Open corresponding tool window
   4361     C("C-Key_5"):               C("Alt-Key_5"),                 # Open corresponding tool window
   4362     C("C-Key_6"):               C("Alt-Key_6"),                 # Open corresponding tool window
   4363     C("C-Key_7"):               C("Alt-Key_7"),                 # Open corresponding tool window
   4364     C("C-Key_8"):               C("Alt-Key_8"),                 # Open corresponding tool window
   4365     C("C-Key_9"):               C("Alt-Key_9"),                 # Open corresponding tool window
   4366     C("Super-Grave"):           C("C-Grave"),                   # Quick switch current scheme
   4367     C("C-Comma"):               C("C-Alt-s"),                   # Open Settings dialog
   4368     C("C-Semicolon"):           C("C-Alt-Shift-s"),             # Open Project Structure dialog
   4369     # Debugging
   4370     C("C-Alt-r"):               C("F9"),                        # Resume program
   4371     # Search/Replace
   4372     C("C-g"):                   C("F3"),                        # Find next
   4373     C("C-Shift-F3"):            C("Shift-F3"),                  # Find previous
   4374     C("Super-g"):               C("Alt-j"),                     # Select next occurrence
   4375     C("C-Super-g"):             C("C-Alt-Shift-j"),             # Select all occurrences
   4376     C("Super-Shift-g"):         C("Alt-Shift-j"),               # Unselect occurrence
   4377     # Editing
   4378     # C("Super-Space"):           C("C-Space"),                   # Basic code completion (conflicts with input switching)
   4379     # C("Super-Shift-Space"):     C("Shift-C-Space"),             # Smart code completion (conflicts with input switching)
   4380     C("Super-j"):               C("C-q"),                       # Quick documentation lookup
   4381     C("C-n"):                   C("Alt-Insert"),                # Generate code...
   4382     C("Super-o"):               C("C-o"),                       # Override methods
   4383     C("Super-i"):               C("C-i"),                       # Implement methods
   4384     C("Alt-Up"):                C("C-w"),                       # Extend selection
   4385     C("Alt-Down"):              C("C-Shift-w"),                 # Shrink selection
   4386     C("Super-Shift-q"):         C("Alt-q"),                     # Context info
   4387     C("Super-Alt-o"):           C("C-Alt-o"),                   # Optimize imports
   4388     C("Super-Alt-i"):           C("C-Alt-i"),                   # Auto-indent line(s)
   4389     C("C-Backspace"):           C("C-y"),                       # Delete line at caret
   4390     C("Super-Shift-j"):         C("C-Shift-j"),                 # Smart line join
   4391     C("Alt-Delete"):            C("C-Delete"),                  # Delete to word end
   4392     C("Alt-Backspace"):         C("C-Backspace"),               # Delete to word start
   4393     C("C-Shift-Equal"):         C("C-KPPLUS"),                  # Expand code block
   4394     C("C-Minus"):               C("C-KPMINUS"),                 # Collapse code block
   4395     C("C-Shift-Equal"):         C("C-Shift-KPPLUS"),            # Expand all
   4396     C("C-Shift-Minus"):         C("C-Shift-KPMINUS"),           # Collapse all
   4397     C("C-w"):                   C("C-F4"),                      # Close active editor tab
   4398     # Refactoring
   4399     C("C-Delete"):              C("Alt-Delete"),                # Safe Delete
   4400     C("C-T"):                   C("C-Alt-Shift-t"),             # Refactor this
   4401     # Navigation
   4402     C("C-o"):                   C("C-n"),                       # Go to class
   4403     C("C-Shift-o"):             C("C-Shift-n"),                 # Go to file
   4404     C("C-Alt-o"):               C("C-Alt-Shift-n"),             # Go to symbol
   4405     C("Super-Right"):           C("Alt-Right"),                 # Go to next editor tab
   4406     C("Super-Left"):            C("Alt-Left"),                  # Go to previous editor tab
   4407     C("C-l"):                   C("C-g"),                       # Go to line
   4408     C("Alt-Space"):             C("C-Shift-i"),                 # Open quick definition lookup
   4409     C("C-Y"):                   C("C-Shift-i"),                 # Open quick definition lookup
   4410     C("Super-Shift-b"):         C("C-Shift-b"),                 # Go to type declaration
   4411     C("Super-Up"):              C("Alt-Up"),                    # Go to previous
   4412     C("Super-Down"):            C("Alt-Down"),                  # Go to next method
   4413     C("C-Left_Brace"):          C("Alt-Shift-Left"),            # Go back
   4414     C("C-Right_Brace"):         C("Alt-Shift-Right"),           # Go forward
   4415     C("Super-h"):               C("C-h"),                       # Type hierarchy
   4416     C("Super-Alt-h"):           C("C-Alt-h"),                   # Call hierarchy
   4417     C("C-Down"):                C("C-Enter"),                   # Edit source/View source
   4418     C("Alt-Home"):              C("Alt-Home"),                  # Show navigation bar
   4419     C("F2"):                    C("F11"),                       # Toggle bookmark
   4420     C("Super-F3"):              C("C-F11"),                     # Toggle bookmark with mnemonic
   4421     C("Super-Key_0"):           C("C-Key_0"),                   # Go to numbered bookmark
   4422     C("Super-Key_1"):           C("C-Key_1"),                   # Go to numbered bookmark
   4423     C("Super-Key_2"):           C("C-Key_2"),                   # Go to numbered bookmark
   4424     C("Super-Key_3"):           C("C-Key_3"),                   # Go to numbered bookmark
   4425     C("Super-Key_4"):           C("C-Key_4"),                   # Go to numbered bookmark
   4426     C("Super-Key_5"):           C("C-Key_5"),                   # Go to numbered bookmark
   4427     C("Super-Key_6"):           C("C-Key_6"),                   # Go to numbered bookmark
   4428     C("Super-Key_7"):           C("C-Key_7"),                   # Go to numbered bookmark
   4429     C("Super-Key_8"):           C("C-Key_8"),                   # Go to numbered bookmark
   4430     C("Super-Key_9"):           C("C-Key_9"),                   # Go to numbered bookmark
   4431     C("C-F3"):                  C("Shift-F11"),                 # Show bookmarks
   4432     # Compile and Run
   4433     C("Super-Alt-r"):           C("Alt-Shift-F10"),             # Select configuration and run
   4434     C("Super-Alt-d"):           C("Alt-Shift-F9"),              # Select configuration and debug
   4435     C("Super-r"):               C("Shift-F10"),                 # Run
   4436     C("Super-d"):               C("Shift-F9"),                  # Debug
   4437     C("Super-Shift-r"):         C("C-Shift-F10"),               # Run context configuration from editor
   4438     C("Super-Shift-d"):         C("C-Shift-F9"),                # Debug context configuration from editor
   4439     # VCS/Local History
   4440     C("Super-v"):               C("Alt-Grave"),                 # VCS quick popup
   4441     C("Super-c"):               C("C-c"),                       # Sigints - interrupt
   4442 }, when = lambda ctx:
   4443     cnfg.screen_has_focus and
   4444     matchProps(clas="^jetbrains-(?!.*toolbox).*$")(ctx) )
   4445 
   4446 keymap("Wordwise - not vscode", {
   4447     # Wordwise remaining - for Everything but VS Code
   4448     C("Alt-Left"):              C("C-Left"),                    # Left of Word
   4449     C("Alt-Shift-Left"):        C("C-Shift-Left"),              # Select Left of Word
   4450     C("Alt-Right"):             C("C-Right"),                   # Right of Word
   4451     C("Alt-Shift-Right"):       C("C-Shift-Right"),             # Select Right of Word
   4452     C("Alt-Shift-g"):           C("C-Shift-g"),                 # View source control
   4453     # ** VS Code fix **
   4454     #   Electron issue precludes normal keybinding fix.
   4455     #   Alt menu auto-focus/toggle gets in the way.
   4456     #
   4457     #   refer to ./xkeysnail-config/vscode_keybindings.json
   4458     # **
   4459     #
   4460     # ** Firefox fix **
   4461     #   User will need to set "ui.key.menuAccessKeyFocuses"
   4462     #   under about:config to false.
   4463     #
   4464     #   https://superuser.com/questions/770301/pentadactyl-how-to-disable-menu-bar-toggle-by-alt
   4465     # **
   4466     #
   4467 }, when = lambda ctx:
   4468     cnfg.screen_has_focus and
   4469     matchProps(not_clas=vscodes_and_remotes_Str)(ctx)
   4470 )
   4471 
   4472 # Keybindings for VS Code and variants
   4473 keymap("VSCodes overrides for Chromebook/IBM - Sublime", {
   4474     C("C-Alt-g"):               C("C-f2"),                      # Chromebook/IBM - Sublime - find_all_under
   4475 }, when = lambda ctx:
   4476     cnfg.screen_has_focus and
   4477     cnfg.ST3_in_VSCode and
   4478     (   isKBtype('Chromebook', map="vscodes ovr cbook - sublime")(ctx) or
   4479         isKBtype('IBM', map="vscodes ovr ibm - sublime")(ctx) ) and
   4480     matchProps(clas=vscodeStr)(ctx)
   4481 )
   4482 keymap("VSCodes overrides for not Chromebook/IBM - Sublime", {
   4483     C("Super-C-g"):             C("C-f2"),                      # Default - Sublime - find_all_under
   4484 }, when = lambda ctx:
   4485     cnfg.screen_has_focus and
   4486     cnfg.ST3_in_VSCode and
   4487     not ( isKBtype('Chromebook', map="vscodes ovr not cbook - sublime")(ctx) or
   4488     isKBtype('IBM', map="vscodes ovr not ibm - sublime")(ctx) ) and
   4489     matchProps(clas=vscodeStr)(ctx)
   4490 )
   4491 keymap("VSCodes overrides for Chromebook/IBM", {
   4492     C("Alt-c"):                 C("C-c"),                       #  Chromebook/IBM - Terminal - Sigint
   4493     C("Alt-x"):                 C("C-x"),                       #  Chromebook/IBM - Terminal - Exit nano
   4494 }, when = lambda ctx:
   4495     cnfg.screen_has_focus and
   4496     (   isKBtype('Chromebook', map="vscodes ovr cbook")(ctx) or
   4497         isKBtype('IBM', map="vscodes ovr ibm")(ctx) ) and
   4498     matchProps(clas=vscodeStr)(ctx)
   4499 )
   4500 keymap("VSCodes overrides for not Chromebook/IBM", {
   4501     C("Super-c"):               C("C-c"),                       # Default - Terminal - Sigint
   4502     C("Super-x"):               C("C-x"),                       # Default - Terminal - Exit nano
   4503 }, when = lambda ctx:
   4504     cnfg.screen_has_focus and
   4505     not (   isKBtype('Chromebook', map="vscodes ovr not cbook")(ctx) or
   4506             isKBtype('IBM', map="vscodes ovr not ibm")(ctx) ) and
   4507     matchProps(clas=vscodeStr)(ctx)
   4508 )
   4509 keymap("VSCodes", {
   4510     # C("Super-Space"):           C("C-Space"),                  # Basic code completion (conflicts with input switching)
   4511 
   4512     # Override the global Cmd+Dot (Escape/cancel) shortcut for QuickFix in VSCode(s)
   4513     C("RC-Dot"):                C("C-Dot"),                     # QuickFix, overriding global shortcut
   4514 
   4515     # Override the global copy shortcut, to allow Cmd+C to work properly in the embedded
   4516     # VSCode terminal, without an internal VSCode keyboard shortcut remap.
   4517     C("RC-C"):                 C("C-Insert"),                   # Copy
   4518 
   4519     # This override unfortunately seems to paste from the "wrong" clipboard. Only pastes content
   4520     # that was copied from within the VSCode window, not content copied from other app windows:
   4521     # C("RC-V"):                 C("Shift-Insert"),               # Paste
   4522 
   4523     # In-app terminal operations (Cmd+J is "Toggle Panel Visibility" for problems/output/debug/terminal panel)
   4524     C("Super-Grave"):           C("C-Grave"),                   # Terminal: Toggle Terminal
   4525     C("Shift-Super-Grave"):     C("Shift-C-Grave"),             # Terminal: Create New Terminal
   4526 
   4527     # Find dialog options
   4528     C("Alt-RC-C"):              C("Alt-C"),                     # Find: toggle "Match Case"
   4529     C("Alt-RC-W"):              C("Alt-W"),                     # Find: toggle "Match Whole Word"
   4530     C("Alt-RC-R"):              C("Alt-R"),                     # Find: toggle "Use Regular Expression"
   4531     C("Alt-RC-L"):              C("Alt-L"),                     # Find: toggle "Find in Selection"
   4532     C("Alt-RC-P"):              C("Alt-P"),                     # Replace: toggle "Preserve Case"
   4533     C("RC-Enter"):              C("C-Alt-Enter"),               # Find: Replace All
   4534 
   4535     C("Alt-RC-Z"):              C("Alt-Z"),                     # View: toggle "Word Wrap"
   4536 
   4537     C("Alt-RC-Up"):             C("Shift-Alt-Up"),              # Insert cursor above
   4538     C("Alt-RC-Down"):           C("Shift-Alt-Down"),            # Insert cursor below
   4539 
   4540     C("Shift-Super-Right"):     C("Shift-Alt-Right"),           # Expand Selection (increase logical scope of smart selection)
   4541     C("Shift-Super-Left"):      C("Shift-Alt-Left"),            # Shrink Selection (reduce logical scope of smart selection)
   4542 
   4543     C("Shift-Super-RC-Right"):  C("Shift-Alt-Right"),           # Expand Selection (increase logical scope of smart selection) [alt]
   4544     C("Shift-Super-RC-Left"):   C("Shift-Alt-Left"),            # Shrink Selection (reduce logical scope of smart selection) [alt]
   4545 
   4546     # On Mac, Cmd+G and Shift+Cmd+G are for Find Next/Previous.
   4547     # Correct combo on Mac for Source Control sidebar panel is physical Shift+Ctrl+G
   4548     C("Shift-Super-g"):         C("Shift-C-g"),                 # Show Source Control
   4549 
   4550     # Wordwise remaining - for VS Code
   4551     # Alt-F19 hack fixes Alt menu activation
   4552     C("Alt-Left"):             [C("Alt-F19"),C("C-Left")],          # Left of Word
   4553     C("Alt-Right"):            [C("Alt-F19"),C("C-Right")],         # Right of Word
   4554     C("Alt-Shift-Left"):       [C("Alt-F19"),C("C-Shift-Left")],    # Select Left of Word
   4555     C("Alt-Shift-Right"):      [C("Alt-F19"),C("C-Shift-Right")],   # Select Right of Word
   4556 
   4557     # To make this work, assign these shortcuts to "deleteWordPartLeft" and "deleteWordPartRight" shortcuts
   4558     C("Shift-Alt-Backspace"):   C("Shift-Alt-Backspace"),        # Delete word left of cursor (override GenGUI)
   4559     C("Shift-Alt-Delete"):      C("Shift-Alt-Delete"),           # Delete word right of cursor (override GenGUI)
   4560     C("RC-Backspace"):         [C("Shift-Home"), C("Delete")],  # Delete entire line left of cursor
   4561     C("RC-Delete"):            [C("Shift-End"), C("Delete")],   # Delete entire line right of cursor
   4562 
   4563     # C("C-PAGE_DOWN"):           ignore_combo,                   # cancel next_view
   4564     # C("C-PAGE_UP"):             ignore_combo,                   # cancel prev_view
   4565     C("C-Alt-Left"):            C("C-PAGE_UP"),                 # next_view
   4566     C("C-Alt-Right"):           C("C-PAGE_DOWN"),               # prev_view
   4567     C("Shift-RC-Left_Brace"):   C("C-PAGE_UP"),                 # next_view
   4568     C("Shift-RC-Right_Brace"):  C("C-PAGE_DOWN"),               # prev_view
   4569 
   4570     # VS Code Shortcuts
   4571     C("C-g"):                   ignore_combo,                   # cancel Go to Line...
   4572     C("Super-g"):               C("C-g"),                       # Go to Line...
   4573     C("F3"):                    ignore_combo,                   # cancel Find next
   4574     C("C-h"):                   ignore_combo,                   # cancel replace
   4575     C("C-Alt-f"):               C("C-h"),                       # replace
   4576     C("C-Shift-h"):             ignore_combo,                   # cancel replace_next
   4577     C("C-Alt-e"):               C("C-Shift-h"),                 # replace_next
   4578     C("f3"):                    ignore_combo,                   # cancel find_next
   4579     C("C-g"):                   C("f3"),                        # find_next
   4580     C("Shift-f3"):              ignore_combo,                   # cancel find_prev
   4581     C("C-Shift-g"):             C("Shift-f3"),                  # find_prev
   4582 }, when = lambda ctx:
   4583     cnfg.screen_has_focus and
   4584     matchProps(clas=vscodeStr)(ctx)
   4585 )
   4586 
   4587 # Keybindings for Sublime Text
   4588 keymap("Sublime Text overrides for Chromebook/IBM", {
   4589     C("Alt-c"):                 C("C-c"),                       #  Chromebook/IBM - Terminal - Sigint
   4590     C("Alt-x"):                 C("C-x"),                       #  Chromebook/IBM - Terminal - Exit nano
   4591     C("Alt-Refresh"):           ignore_combo,                   # Chromebook/IBM - cancel find_all_under
   4592     C("Alt-C-g"):               C("Alt-Refresh"),               # Chromebook/IBM - find_all_under
   4593 }, when = lambda ctx:
   4594     cnfg.screen_has_focus and
   4595     (   isKBtype('Chromebook', map="sublime ovr cbook")(ctx) or
   4596         isKBtype('IBM', map="sublime ovr ibm")(ctx) ) and
   4597     matchProps(clas=sublimeStr)(ctx)
   4598 )
   4599 keymap("Sublime Text overrides for not Chromebook/IBM", {
   4600     # C("Super-c"):               C("C-c"),                       # Default - Terminal - Sigint
   4601     # C("Super-x"):               C("C-x"),                       # Default - Terminal - Exit nano
   4602     C("Alt-f3"):                ignore_combo,                   # Default - cancel find_all_under
   4603     C("Super-C-g"):             C("Alt-f3"),                    # Default - find_all_under
   4604 }, when = lambda ctx:
   4605     cnfg.screen_has_focus and
   4606     not (   isKBtype('Chromebook', map="sublime ovr not cbook")(ctx) or
   4607             isKBtype('IBM', map="sublime ovr not ibm")(ctx) ) and
   4608     matchProps(clas=sublimeStr)(ctx)
   4609 )
   4610 keymap("Sublime Text", {
   4611     # C("Super-c"):               C("C-c"),                       # Default - Terminal - Sigint
   4612     # C("Super-x"):               C("C-x"),                       # Default - Terminal - Exit nano
   4613     # C("Alt-c"):                 C("C-c"),                       #  Chromebook/IBM - Terminal - Sigint
   4614     # C("Alt-x"):                 C("C-x"),                       #  Chromebook/IBM - Terminal - Exit nano
   4615     # C("Super-Space"):           C("C-Space"),                   # Basic code completion (conflicts with input switching)
   4616     C("C-Super-up"):            C("Alt-o"),                     # Switch file
   4617     C("Super-RC-f"):            C("f11"),                       # toggle_full_screen
   4618     C("C-Alt-v"):              [C("C-k"), C("C-v")],            # paste_from_history
   4619     # C("C-up"):                  ignore_combo,                   # cancel scroll_lines up
   4620     C("C-Alt-up"):              C("C-up"),                      # scroll_lines up
   4621     # C("C-down"):                ignore_combo,                   # cancel scroll_lines down
   4622     C("C-Alt-down"):            C("C-down"),                    # scroll_lines down
   4623     C("Super-Shift-up"):        C("Alt-Shift-up"),              # multi-cursor up
   4624     C("Super-Shift-down"):      C("Alt-Shift-down"),            # multi-cursor down
   4625     C("C-PAGE_DOWN"):           ignore_combo,                   # cancel next_view
   4626     C("C-PAGE_UP"):             ignore_combo,                   # cancel prev_view
   4627     C("C-Shift-left_brace"):    C("C-PAGE_DOWN"),               # next_view
   4628     C("C-Shift-right_brace"):   C("C-PAGE_UP"),                 # prev_view
   4629     C("C-Alt-right"):           C("C-PAGE_DOWN"),               # next_view
   4630     C("C-Alt-left"):            C("C-PAGE_UP"),                 # prev_view
   4631     C("insert"):                ignore_combo,                   # cancel toggle_overwrite
   4632     C("C-Alt-o"):               C("insert"),                    # toggle_overwrite
   4633     C("Alt-c"):                 ignore_combo,                   # cancel toggle_case_sensitive
   4634     C("C-Alt-c"):               C("Alt-c"),                     # toggle_case_sensitive
   4635     C("C-h"):                   ignore_combo,                   # cancel replace
   4636     C("C-Alt-f"):               C("C-h"),                       # replace
   4637     C("C-Shift-h"):             ignore_combo,                   # cancel replace_next
   4638     C("C-Alt-e"):               C("C-Shift-h"),                 # replace_next
   4639     C("f3"):                    ignore_combo,                   # cancel find_next
   4640     C("C-g"):                   C("f3"),                        # find_next
   4641     C("Shift-f3"):              ignore_combo,                   # cancel find_prev
   4642     C("C-Shift-g"):             C("Shift-f3"),                  # find_prev
   4643     C("C-f3"):                  ignore_combo,                   # cancel find_under
   4644     C("Super-Alt-g"):           C("C-f3"),                      # find_under
   4645     C("C-Shift-f3"):            ignore_combo,                   # cancel find_under_prev
   4646     C("Super-Alt-Shift-g"):     C("C-Shift-f3"),                # find_under_prev
   4647     C("Alt-f3"):                ignore_combo,                   # Default - cancel find_all_under
   4648     C("Super-C-g"):             C("Alt-f3"),                    # Default - find_all_under
   4649     # C("Alt-Refresh"):           ignore_combo,                   # Chromebook/IBM - cancel find_all_under
   4650     # C("Alt-C-g"):               C("Alt-Refresh"),               # Chromebook/IBM - find_all_under
   4651     C("C-Shift-up"):            ignore_combo,                   # cancel swap_line_up
   4652     C("Super-Alt-up"):          C("C-Shift-up"),                # swap_line_up
   4653     C("C-Shift-down"):          ignore_combo,                   # cancel swap_line_down
   4654     C("Super-Alt-down"):        C("C-Shift-down"),              # swap_line_down
   4655     C("C-Pause"):               ignore_combo,                   # cancel cancel_build
   4656     C("Super-c"):               C("C-Pause"),                   # cancel_build
   4657     C("f9"):                    ignore_combo,                   # cancel sort_lines case_s false
   4658     C("f5"):                    C("f9"),                        # sort_lines case_s false
   4659     C("Super-f9"):              ignore_combo,                   # cancel sort_lines case_s true
   4660     C("Super-f5"):              C("Super-f9"),                  # sort_lines case_s true
   4661     C("Alt-Shift-Key_1"):       ignore_combo,                   # cancel set_layout
   4662     C("C-Alt-Key_1"):           C("Alt-Shift-Key_1"),           # set_layout
   4663     C("Alt-Shift-Key_2"):       ignore_combo,                   # cancel set_layout
   4664     C("C-Alt-Key_2"):           C("Alt-Shift-Key_2"),           # set_layout
   4665     C("Alt-Shift-Key_3"):       ignore_combo,                   # cancel set_layout
   4666     C("C-Alt-Key_3"):           C("Alt-Shift-Key_3"),           # set_layout
   4667     C("Alt-Shift-Key_4"):       ignore_combo,                   # cancel set_layout
   4668     C("C-Alt-Key_4"):           C("Alt-Shift-Key_4"),           # set_layout
   4669     C("Alt-Shift-Key_8"):       ignore_combo,                   # cancel set_layout
   4670     C("C-Alt-Shift-Key_2"):     C("Alt-Shift-Key_8"),           # set_layout
   4671     C("Alt-Shift-Key_9"):       ignore_combo,                   # cancel set_layout
   4672     C("C-Alt-Shift-Key_3"):     C("Alt-Shift-Key_9"),           # set_layout
   4673     C("Alt-Shift-Key_5"):       ignore_combo,                   # cancel set_layout
   4674     C("C-Alt-Shift-Key_5"):     C("Alt-Shift-Key_5"),           # set_layout
   4675     # C(""):                    ignore_combo,                   # cancel
   4676     # C(""):                    C(""),                          #
   4677 }, when = lambda ctx:
   4678     cnfg.screen_has_focus and
   4679     matchProps(clas=sublimeStr)(ctx) )
   4680 
   4681 keymap("Kate Advanced Text Editor", {
   4682     C("RC-Comma"):              C("Shift-C-Comma"),             # Open settings/preferences
   4683     C("RC-g"):                  C("F3"),                        # Find next
   4684     C("Super-g"):               C("C-g"),                       # Go to line
   4685 }, when = lambda ctx:
   4686     cnfg.screen_has_focus and
   4687     matchProps(clas="^org.kde.kate$")(ctx) )
   4688 
   4689 keymap("Linux Mint xed text editor", {
   4690     C("RC-T"):                  C("C-N"),                       # Open new tab (new file)
   4691 }, when = lambda ctx:
   4692     cnfg.screen_has_focus and
   4693     matchProps(clas="^xed$")(ctx) )
   4694 
   4695 keymap("KWrite text editor - Close Document dialog", {
   4696     C("RC-d"):                  C("Alt-d"),                     # [D]iscard file without saving (from Close Document dialog)
   4697     C("RC-s"):                  C("Alt-s"),                     # Save file (from Close Document dialog)
   4698 }, when = lambda ctx:
   4699     cnfg.screen_has_focus and
   4700     matchProps(clas="^kwrite$|^org.kde.Kwrite$", name="^Close Document.*KWrite$")(ctx) )
   4701 keymap("KWrite text editor", {
   4702     C("RC-comma"):              C("Shift-C-comma"),             # Open preferences dialog
   4703     C("RC-t"):                  C("C-n"),                       # New tab (new document appears in tab)
   4704     C("Super-g"):               C("C-g"),                       # Go to line with physical Ctrl+G
   4705     C("RC-g"):                  C("F3"),                        # Find next instance (Cmd+G)
   4706     C("Shift-RC-g"):            C("Shift-F3"),                  # Find previous instance (Shift+Cmd+G)
   4707     # wordwise shortcuts
   4708     C("RC-Backspace"):          C("C-k"),                       # Delete line (Cmd+Delete)
   4709 }, when = lambda ctx:
   4710     cnfg.screen_has_focus and
   4711     matchProps(clas="^kwrite$|^org.kde.Kwrite$")(ctx) )
   4712 
   4713 keymap("GNOME Text Editor", {
   4714     C("RC-Slash"):              None,                           # Block Cmd+Slash from doing "Select All"
   4715     C("RC-Alt-f"):              C("C-h"),                       # Search and replace within the document
   4716 }, when = lambda ctx:
   4717     cnfg.screen_has_focus and
   4718     matchProps(clas="^gnome-text-editor$|^org.gnome.TextEditor$")(ctx) )
   4719 
   4720 
   4721 ###########################  DIALOG FIXES  ###########################
   4722 ###                                                                ###
   4723 ###                                                                ###
   4724 ###      ██████  ██  █████  ██       ██████   ██████  ███████      ###
   4725 ###      ██   ██ ██ ██   ██ ██      ██    ██ ██       ██           ###
   4726 ###      ██   ██ ██ ███████ ██      ██    ██ ██   ███ ███████      ###
   4727 ###      ██   ██ ██ ██   ██ ██      ██    ██ ██    ██      ██      ###
   4728 ###      ██████  ██ ██   ██ ███████  ██████   ██████  ███████      ###
   4729 ###                                                                ###
   4730 ###                                                                ###
   4731 ######################################################################
   4732 ### Fixes for the problem of modal dialogs and other "child"
   4733 ### windows failing to close with Cmd+W.
   4734 ### Many dialogs respond to the Escape key, others may require the
   4735 ### "Close window" shortcut (normally Alt+F4 but not always) to close.
   4736 ###
   4737 ### Cmd+W can't just be always mapped to the "Close window" shortcut for all apps
   4738 ### because some apps will "quit" rather than just closing a tab.
   4739 ###
   4740 ### To add window conditions to the list, search for the list names in
   4741 ### the "LISTS" section near the top of this config file.
   4742 ### dialogs_Escape_lod = send these windows the Escape key for Cmd+W
   4743 ### dialogs_CloseWin_lod = send these windows the "Close window" shortcut for Cmd+W
   4744 
   4745 keymap("Cmd+W dialog fix - send Escape", {
   4746     C("RC-W"):                  iEF2(C("Esc"), True),
   4747 }, when = lambda ctx:
   4748     cnfg.screen_has_focus and
   4749     matchProps(lst=dialogs_Escape_lod)(ctx)
   4750 )
   4751 
   4752 # This keymap for Manjaro GNOME will override the same shortcut from the keymap just below it,
   4753 # sending Super+Q to close the dialogs in the matchProps list, instead of sending Alt+F4.
   4754 if DISTRO_ID == 'manjaro'  and DESKTOP_ENV == 'gnome':
   4755     keymap("Cmd+W dialog fix - Super+Q Manjaro GNOME", {
   4756         C("RC-W"):                  iEF2(C("Super-Q"), True),
   4757     }, when = lambda ctx:
   4758         cnfg.screen_has_focus and
   4759         matchProps(lst=dialogs_CloseWin_lod)(ctx)
   4760     )
   4761 
   4762 keymap("Cmd+W dialog fix - Alt+F4", {
   4763     C("RC-W"):                  iEF2(C("Alt-F4"), True),
   4764 }, when = lambda ctx:
   4765     cnfg.screen_has_focus and
   4766     matchProps(lst=dialogs_CloseWin_lod)(ctx)
   4767 )
   4768 
   4769 
   4770 
   4771 ###########################  TAB NAV FIXES  #############################
   4772 ###                                                                   ###
   4773 ###                                                                   ###
   4774 ###      ████████  █████  ██████      ███    ██  █████  ██    ██      ###
   4775 ###         ██    ██   ██ ██   ██     ████   ██ ██   ██ ██    ██      ###
   4776 ###         ██    ███████ ██████      ██ ██  ██ ███████ ██    ██      ###
   4777 ###         ██    ██   ██ ██   ██     ██  ██ ██ ██   ██  ██  ██       ###
   4778 ###         ██    ██   ██ ██████      ██   ████ ██   ██   ████        ###
   4779 ###                                                                   ###
   4780 ###                                                                   ###
   4781 #########################################################################
   4782 ### Various fixes for supporting tab navigation shortcuts like Shift+Cmd+Braces
   4783 
   4784 
   4785 tab_UI_fix_CtrlShiftTab_lst = [
   4786     "com.raggesilver.BlackBox",
   4787     "com.system76.CosmicTerm",
   4788     "org.gnome.Console|Console",
   4789     "deepin-terminal",
   4790     "hyper",
   4791     "kitty",
   4792     "Kgx",
   4793 ]
   4794 tab_UI_fix_CtrlShiftTab_Str = toRgxStr(tab_UI_fix_CtrlShiftTab_lst)
   4795 
   4796 tab_UI_fix_CtrlAltPgUp_lst = [
   4797     "gedit",
   4798     "xed",
   4799 ]
   4800 tab_UI_fix_CtrlAltPgUp_Str = toRgxStr(tab_UI_fix_CtrlAltPgUp_lst)
   4801 
   4802 # Tab navigation overrides for tabbed UI apps that use Ctrl+Shift+Tab/Ctrl+Tab instead of Ctrl+PgUp/PgDn
   4803 keymap("Tab Nav fix for apps that use Ctrl+Shift+Tab/Ctrl+Tab", {
   4804     C("Shift-RC-Left_Brace"):  [bind,C("C-Shift-Tab")],         # Tab nav: Go to prior tab (left)
   4805     C("Shift-RC-Right_Brace"): [bind,C("C-Tab")],               # Tab nav: Go to next tab (right)
   4806     C("Shift-RC-Left"):        [bind,C("C-Shift-Tab")],         # Tab nav: Go to prior tab (left)
   4807     C("Shift-RC-Right"):       [bind,C("C-Tab")],               # Tab nav: Go to next tab (right)
   4808 }, when = lambda ctx:
   4809     cnfg.screen_has_focus and
   4810     matchProps(clas=tab_UI_fix_CtrlShiftTab_Str)(ctx)
   4811 )
   4812 
   4813 # Tab navigation overrides for tabbed UI apps that use Ctrl+Alt+PgUp/PgDn instead of Ctrl+PgUp/PgDn
   4814 keymap("Tab Nav fix for apps that use Ctrl+Alt+PgUp/PgDn", {
   4815     C("Shift-RC-Left_Brace"):  [bind,C("C-Alt-Page_Up")],       # Go to prior tab (Left)
   4816     C("Shift-RC-Right_Brace"): [bind,C("C-Alt-Page_Down")],     # Go to next tab (Right)
   4817 }, when = lambda ctx:
   4818     cnfg.screen_has_focus and
   4819     matchProps(clas=tab_UI_fix_CtrlAltPgUp_Str)(ctx)
   4820 )
   4821 
   4822 keymap("Konsole tab switching", {
   4823     # Ctrl Tab - In App Tab Switching
   4824     C("Shift-LC-Tab") :        [bind,C("Shift-Left")],
   4825     C("LC-Tab") :              [bind,C("Shift-Right")],
   4826     C("LC-Grave") :            [bind,C("Shift-Left")],
   4827     # Konsole tab switching in KDE4 (not needed in KDE5)
   4828     C("Shift-RC-Left_Brace"):  [bind,C("Shift-Left")],          # Go to prior tab (Left)
   4829     C("Shift-RC-Right_Brace"): [bind,C("Shift-Right")],         # Go to next tab (Right)
   4830 }, when = lambda ctx:
   4831     cnfg.screen_has_focus and
   4832     matchProps(clas="^konsole$|^org.kde.Konsole$")(ctx) )
   4833 
   4834 keymap("Elementary Terminal tab switching", {
   4835     # Ctrl Tab - In App Tab Switching
   4836     C("Shift-LC-Tab") :        [bind,C("Shift-C-Left")],        # Go to prior tab (Left)
   4837     C("LC-Grave") :            [bind,C("Shift-C-Left")],        # Go to prior tab (Left)
   4838     C("LC-Tab") :              [bind,C("Shift-C-Right")],       # Go to next tab (Right)
   4839 }, when = lambda ctx:
   4840     cnfg.screen_has_focus and
   4841     matchProps(clas="^Io.elementary.terminal$|^kitty$")(ctx) )
   4842 
   4843 
   4844 
   4845 ######################################  TERMINALS  #######################################
   4846 ###                                                                                    ###
   4847 ###                                                                                    ###
   4848 ###      ████████ ███████ ██████  ███    ███ ██ ███    ██  █████  ██      ███████      ###
   4849 ###         ██    ██      ██   ██ ████  ████ ██ ████   ██ ██   ██ ██      ██           ###
   4850 ###         ██    █████   ██████  ██ ████ ██ ██ ██ ██  ██ ███████ ██      ███████      ###
   4851 ###         ██    ██      ██   ██ ██  ██  ██ ██ ██  ██ ██ ██   ██ ██           ██      ###
   4852 ###         ██    ███████ ██   ██ ██      ██ ██ ██   ████ ██   ██ ███████ ███████      ###
   4853 ###                                                                                    ###
   4854 ###                                                                                    ###
   4855 ##########################################################################################
   4856 
   4857 keymap("Alacritty terminal", {
   4858     C("RC-K"):                  C("C-L"),                       # clear log
   4859 }, when = lambda ctx:
   4860     cnfg.screen_has_focus and
   4861     matchProps(clas="^alacritty$")(ctx) )
   4862 
   4863 
   4864 keymap("Contour terminal overrides", {
   4865     C("RC-minus"):              C("Shift-C-minus"),             # Decrease font size (override general terminals remap)
   4866 }, when = lambda ctx:
   4867     cnfg.screen_has_focus and
   4868     matchProps(clas="^contour$")(ctx) )
   4869 
   4870 
   4871 keymap("COSMIC Terminal overrides", {
   4872     # There are already tab nav fixes in the usual place.
   4873     C("RC-equal"):              C("C-equal"),                   # Increase font size (override general terminals remap)
   4874 }, when = lambda ctx:
   4875     cnfg.screen_has_focus and
   4876     matchProps(clas="^com.system76.CosmicTerm$")(ctx) )
   4877 
   4878 
   4879 keymap("Deepin Terminal overrides", {
   4880     C("RC-w"):                  C("Alt-w"),                     # Close only current tab, instead of all other tabs
   4881     C("RC-j"):                  None,                           # Block Cmd+J from remapping to vertical split (Ctrl+Shift+J)
   4882     C("RC-minus"):              C("C-minus"),                   # Decrease font size/zoom out
   4883     C("RC-equal"):              C("C-equal"),                   # Increase font size/zoom in
   4884 }, when = lambda ctx:
   4885     cnfg.screen_has_focus and
   4886     matchProps(clas="^deepin-terminal$")(ctx) )
   4887 
   4888 keymap("Ghostty terminal overrides", {
   4889     C("RC-Equal"): C(
   4890         "C-Equal"
   4891     ),              # Increase font size [override general terminals remap]
   4892     C("RC-D"):                  C("RC-Shift-O"),                # Open right split
   4893     C("RC-Shift-D"):            C("RC-Shift-E"),                # Open down split
   4894     C("RC-Alt-I"):              C("RC-Shift-I"),                # Open inspector
   4895     C("RC-K"):                  C("C-L"),                       # clear log
   4896 }, when = lambda ctx:
   4897     cnfg.screen_has_focus and
   4898     matchProps(clas="^.*ghostty.*$")(ctx)
   4899 )
   4900 
   4901 keymap("Hyper terminal tab switching", {
   4902     C("RC-Equal"):              C("C-Equal"),                   # Increase font size [override general terminals remap]
   4903     C("Shift-LC-Tab"):         [bind,C("Shift-C-Tab")],         # Tab nav: Go to prior tab (left) [override general remap]
   4904     C("LC-Tab"):               [bind,C("C-Tab")],               # Tab nav: Go to next tab (right) [override general remap]
   4905 }, when = lambda ctx:
   4906     cnfg.screen_has_focus and
   4907     matchProps(clas="^hyper$")(ctx) )
   4908 
   4909 keymap("Kitty terminal - not tab nav", {
   4910     C("RC-L"):                  C("C-L"),                       # Clear log
   4911     C("RC-K"):                  C("C-L"),                       # Clear log (macOS)
   4912 }, when = lambda ctx:
   4913     cnfg.screen_has_focus and
   4914     matchProps(clas="^kitty$")(ctx) )
   4915 
   4916 keymap("Konsole terminal - not tab nav", {
   4917     C("RC-comma"):              C("Shift-C-comma"),             # Open Preferences dialog
   4918     C("RC-0"):                  C("C-Alt-0"),                   # Reset font size
   4919     C("RC-K"):                  C("Shift-C-K"),                 # Clear Scrollback and Reset
   4920 }, when = lambda ctx:
   4921     cnfg.screen_has_focus and
   4922     matchProps(clas="^Konsole$|^org.kde.Konsole$")(ctx) )
   4923 
   4924 keymap("Terminology terminal", {
   4925     C("RC-w"):                  C("Shift-C-End"),               # Close focused tab
   4926     C("RC-c"):                  C("Alt-w"),                     # Copy selection to primary buffer
   4927     C("RC-v"):                  C("Alt-Enter"),                 # Paste primary buffer
   4928     C("RC-0"):                  C("C-Alt-0"),                   # Reset font size
   4929     C("RC-Minus"):              C("C-Alt-Minus"),               # Decrease font size
   4930     C("RC-Equal"):              C("C-Alt-Equal"),               # Increase font size
   4931 }, when = lambda ctx:
   4932     cnfg.screen_has_focus and
   4933     matchProps(clas="^terminology$")(ctx) )
   4934 
   4935 keymap("Wave terminal", {
   4936     C("RC-t"):                  C("Alt-t"),                     # Open a new tab
   4937     C("RC-n"):                  C("Alt-n"),                     # Open a new terminal block
   4938     C("Shift-RC-n"):            C("Shift-Alt-n"),               # Open a new window
   4939     C("RC-w"):                  C("Alt-w"),                     # Close the current block
   4940     C("Shift-RC-w"):            C("Shift-Alt-w"),               # Close the current tab
   4941 }, when = lambda ctx:
   4942     cnfg.screen_has_focus and
   4943     matchProps(clas="^Wave$")(ctx) )
   4944 
   4945 keymap("Xfce4 terminal", {
   4946     C("RC-comma"):      [C("Alt-e"), sleep(0.1), C("e")],       # Open Preferences dialog
   4947 }, when = lambda ctx:
   4948     cnfg.screen_has_focus and
   4949     matchProps(clas="^xfce4-terminal$")(ctx) )
   4950 
   4951 
   4952 # Overrides to General Terminals shortcuts for specific distros (or are they really just desktop environments?)
   4953 
   4954 if DISTRO_ID in ['fedora', 'almalinux'] and DESKTOP_ENV == 'gnome':
   4955     keymap("GenTerms overrides: Fedora GNOME", {
   4956         C("RC-H"):                  C("Super-h"),                   # Hide Window/Minimize app (gnome/fedora)
   4957     }, when = lambda ctx:
   4958         cnfg.screen_has_focus and
   4959         matchProps(clas=termStr)(ctx)
   4960     )
   4961 
   4962 if DISTRO_ID == 'pop':
   4963     keymap("GenTerms overrides: Pop!_OS", {
   4964         C("LC-Right"):              [bind,C("Super-C-Up")],         # SL - Change workspace (pop)
   4965         C("LC-Left"):               [bind,C("Super-C-Down")],       # SL - Change workspace (pop)
   4966     }, when = lambda ctx:
   4967         cnfg.screen_has_focus and
   4968         matchProps(clas=termStr)(ctx)
   4969     )
   4970 
   4971 if DISTRO_ID in ['ubuntu', 'fedora'] and DESKTOP_ENV == 'gnome':
   4972     keymap("GenTerms overrides: Ubuntu/Fedora", {
   4973         C("LC-RC-Q"):               C("Super-L"),                   # Lock screen (ubuntu/fedora)
   4974         C("LC-Right"):              [bind,C("Super-Page_Up")],      # SL - Change workspace (ubuntu/fedora)
   4975         C("LC-Left"):               [bind,C("Super-Page_Down")],    # SL - Change workspace (ubuntu/fedora)
   4976     }, when = lambda ctx:
   4977         cnfg.screen_has_focus and
   4978         matchProps(clas=termStr)(ctx)
   4979     )
   4980 
   4981 
   4982 # Overrides to General Terminals shortcuts for specific desktop environments
   4983 
   4984 if DESKTOP_ENV == 'budgie':
   4985     keymap("GenTerms overrides: Budgie", {
   4986         C("LC-Right"):              [bind,C("C-Alt-Right")],        # Default SL - Change workspace (budgie)
   4987         C("LC-Left"):               [bind,C("C-Alt-Left")],         # Default SL - Change workspace (budgie)
   4988     }, when = lambda ctx:
   4989         cnfg.screen_has_focus and
   4990         matchProps(clas=termStr)(ctx)
   4991     )
   4992 
   4993 # On Pop!_OS 22.04, System76 seems to have changed the name of the DE from 'cosmic' to 'pop'.
   4994 # Probably to avoid overlapping with the new COSMIC desktop environment in Pop!_OS 24.04 and later.
   4995 if DESKTOP_ENV in ['cosmic', 'pop']:
   4996     keymap("GenTerms overrides: COSMIC", {
   4997         C("LC-RC-F"):               C("Super-M"),                   # Maximize window toggle (overrides General terminals)
   4998     }, when = lambda ctx:
   4999         cnfg.screen_has_focus and
   5000         matchProps(clas=termStr)(ctx)
   5001     )
   5002 
   5003 if DESKTOP_ENV == 'gnome':
   5004     keymap("GenTerms overrides: GNOME", {
   5005         ### Keyboard input source (language/layout) switching in GNOME
   5006         C("LC-Space"):             [bind,C("Super-Space")],         # keyboard input source (layout) switching (gnome)
   5007         C("Shift-LC-Space"):       [bind,C("Super-Shift-Space")],   # keyboard input source (layout) switching (reverse) (gnome)
   5008     }, when = lambda ctx:
   5009         cnfg.screen_has_focus and
   5010         matchProps(clas=termStr)(ctx)
   5011     )
   5012 
   5013 if DESKTOP_ENV == 'kde':
   5014     keymap("GenTerms overrides: KDE", {
   5015         ### Keyboard input source (language/layout) switching in KDE Plasma
   5016         C("LC-Space"):              [bind,C("Super-Alt-L")],        # keyboard input source (layout) switching (Last-Used) (kde)
   5017         C("Shift-LC-Space"):        [bind,C("Super-Alt-K")],        # keyboard input source (layout) switching (Next) (kde)
   5018         C("RC-H"):                  C("Super-Page_Down"),           # Hide Window/Minimize app (KDE Plasma)
   5019 
   5020         # C("LC-RC-f"):               C("Alt-F10"),                   # Toggle window maximized state (pre-Plasma 6)
   5021         # F10 key was designated an accessibility key for opening the window/app menu in KDE.
   5022         # The shortcut for toggling window maximization state is now Meta+PgUp (so, Super-Page_Up).
   5023         C("LC-RC-f"):               C("Super-Page_Up"),             # Toggle window maximized state
   5024 
   5025         # Workspace (virtual desktop) navigation
   5026         C("LC-Left"):               C("C-Super-Left"),              # Switch one desktop to the left
   5027         C("LC-Right"):              C("C-Super-Right"),             # Switch one desktop to the right
   5028 
   5029     }, when = lambda ctx:
   5030         cnfg.screen_has_focus and
   5031         matchProps(clas=termStr)(ctx)
   5032     )
   5033 
   5034 if DESKTOP_ENV == 'pantheon':
   5035     keymap("GenTerms overrides: elementary OS", {
   5036         C("LC-Right"):              [bind,C("Super-Right")],        # SL - Change workspace (elementary)
   5037         C("LC-Left"):               [bind,C("Super-Left")],         # SL - Change workspace (elementary)
   5038     }, when = lambda ctx:
   5039         cnfg.screen_has_focus and
   5040         matchProps(clas=termStr)(ctx)
   5041     )
   5042 
   5043 if DESKTOP_ENV == 'sway':
   5044     keymap("GenTerms overrides: swaywm", {
   5045         C("RC-Q"):                  C("Shift-C-Q"),                 # Override sway GenGUI Cmd+Q
   5046     }, when = lambda ctx:
   5047         cnfg.screen_has_focus and
   5048         matchProps(clas=termStr)(ctx)
   5049     )
   5050 
   5051 if DESKTOP_ENV == 'xfce':
   5052     keymap("GenTerms overrides: Xfce4", {
   5053         C("RC-Grave"):             [bind,C("Super-Tab")],           # xfce4 Switch within app group
   5054         C("Shift-RC-Grave"):       [bind,C("Super-Shift-Tab")],     # xfce4 Switch within app group
   5055         C("LC-Right"):             [bind,C("C-Alt-Home")],          # SL - Change workspace xfce4
   5056         C("LC-Left"):              [bind,C("C-Alt-End")],           # SL - Change workspace xfce4
   5057     }, when = lambda ctx:
   5058         cnfg.screen_has_focus and
   5059         matchProps(clas=termStr)(ctx)
   5060     )
   5061 
   5062 
   5063 # Active in all apps in the terminals list
   5064 keymap("General Terminals", {
   5065 
   5066     ### wordwise overrides of general GUI block
   5067     C("Alt-Backspace"):         C("C-w"),                       # Wordwise delete word left of cursor in terminals
   5068     # C("Alt-Backspace"):         C("Alt-Shift-Backspace"),       # Wordwise delete word left of cursor in terminals
   5069     C("Alt-Delete"):           [C("Esc"),C("d")],               # Wordwise delete word right of cursor in terminals
   5070     C("RC-Backspace"):          C("C-u"),                       # Wordwise delete line left of cursor in terminals
   5071     C("RC-Delete"):             C("C-k"),                       # Wordwise delete line right of cursor in terminals
   5072 
   5073     ### Tab navigation
   5074     C("Shift-RC-Left"):        [bind,C("C-Page_Up")],           # Tab nav: Go to prior tab (Left)
   5075     C("Shift-RC-Right"):       [bind,C("C-Page_Down")],         # Tab nav: Go to next tab (Right)
   5076 
   5077     C("LC-RC-f"):               C("Alt-F10"),                   # Toggle window maximized state (gnome?)
   5078 
   5079     # Ctrl Tab - In App Tab Switching
   5080     C("LC-Tab") :               C("C-PAGE_DOWN"),
   5081     C("Shift-LC-Tab") :         C("C-PAGE_UP"),
   5082     C("LC-Grave") :             C("C-PAGE_UP"),
   5083     # C("Alt-Tab"):               ignore_combo,                   # Default - Cmd Tab - App Switching Default
   5084     # C("RC-Tab"):                C("Alt-Tab"),                   # Default - Cmd Tab - App Switching Default
   5085     # C("Shift-RC-Tab"):          C("Alt-Shift-Tab"),             # Default - Cmd Tab - App Switching Default
   5086 
   5087     # Converts Cmd to use Ctrl-Shift
   5088     C("RC-MINUS"):              C("C-MINUS"),                   # Reduce font size
   5089     C("RC-EQUAL"):              C("C-Shift-EQUAL"),             # Increase font size
   5090     # C("RC-BACKSPACE"):          C("C-Shift-BACKSPACE"),         # Conflicts with wordwise shortcut above
   5091     C("RC-W"):                  C("C-Shift-W"),                 # Close tab/window
   5092     # C("RC-E"):                  C("C-Shift-E"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5093     # C("RC-R"):                  C("C-Shift-R"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5094     C("RC-T"):                  C("C-Shift-t"),                 # Open new tab in many terminals
   5095     # C("RC-Y"):                  C("C-Shift-Y"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5096     # C("RC-U"):                  C("C-Shift-U"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5097     # C("RC-I"):                  C("C-Shift-I"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5098     # C("RC-O"):                  C("C-Shift-O"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5099     # C("RC-P"):                  C("C-Shift-P"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5100     C("RC-LEFT_BRACE"):         C("C-Shift-LEFT_BRACE"),
   5101     C("RC-RIGHT_BRACE"):        C("C-Shift-RIGHT_BRACE"),
   5102     # If focused app is terminal, this stops Cmd+A from working in GNOME overview search field
   5103     # C("RC-A"):                  C("C-Shift-A"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5104     C("RC-S"):                  C("C-Shift-S"),                 # Save file in some terminals
   5105     C("RC-D"):                  C("C-Shift-D"),                 # Split horizontal in some terminals
   5106     C("RC-F"):                  C("C-Shift-F"),                 # Find text in some terminals
   5107     # C("RC-G"):                  C("C-Shift-G"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5108     # C("RC-H"):                  C("C-Shift-H"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5109     # C("RC-J"):                  C("C-Shift-J"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5110     # C("RC-K"):                  C("C-Shift-K"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5111     C("RC-L"):                  C("C-Shift-L"),                 # Clear screen in some terminals
   5112     # C("RC-SEMICOLON"):          C("C-Shift-SEMICOLON"),         # No function - RE-ENABLE IF SOMEONE REPORTS
   5113     # C("RC-APOSTROPHE"):         C("C-Shift-APOSTROPHE"),        # No function - RE-ENABLE IF SOMEONE REPORTS
   5114     # C("RC-GRAVE"):              C("C-Shift-GRAVE"),             # Conflicts with General GUI window switching
   5115     # C("RC-Z"):                  C("C-Shift-Z"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5116     # C("RC-X"):                  C("C-Shift-X"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5117     C("RC-C"):                  C("C-Shift-C"),                 # Copy text in many terminals
   5118     C("RC-V"):                  C("C-Shift-V"),                 # Paste text in many terminals
   5119     # C("RC-B"):                  C("C-Shift-B"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5120     C("RC-N"):                  C("C-Shift-N"),                 # Open new window in many terminals
   5121     # C("RC-M"):                  C("C-Shift-M"),                 # No function - RE-ENABLE IF SOMEONE REPORTS
   5122     # C("RC-COMMA"):              C("C-Shift-COMMA"),             # Open Preferences (Replaced by per-app remaps if needed)
   5123     C("RC-Dot"):                C("C-c"),                       # Mimic macOS Cmd+Dot to cancel command
   5124     # C("RC-SLASH"):              C("C-Shift-SLASH"),             # No function - RE-ENABLE IF SOMEONE REPORTS
   5125     # C("RC-KPASTERISK"):         C("C-Shift-KPASTERISK"),        # No function - RE-ENABLE IF SOMEONE REPORTS
   5126 
   5127     # Allow access to Linux TTY virtual consoles with the usual physical key locations (General Terminals)
   5128     C("LC-RC-F1"):              C("C-Alt-F1"),                  # Go to TTY virtual console 1
   5129     C("LC-RC-F2"):              C("C-Alt-F2"),                  # Go to TTY virtual console 2
   5130     C("LC-RC-F3"):              C("C-Alt-F3"),                  # Go to TTY virtual console 3
   5131     C("LC-RC-F4"):              C("C-Alt-F4"),                  # Go to TTY virtual console 4
   5132     C("LC-RC-F5"):              C("C-Alt-F5"),                  # Go to TTY virtual console 5
   5133     C("LC-RC-F6"):              C("C-Alt-F6"),                  # Go to TTY virtual console 6
   5134     C("LC-RC-F7"):              C("C-Alt-F7"),                  # Go to TTY virtual console 7
   5135 
   5136 }, when = lambda ctx:
   5137     cnfg.screen_has_focus and
   5138     matchProps(clas=termStr)(ctx)
   5139 )
   5140 
   5141 
   5142 
   5143 #############################  GENERAL GUI  ################################
   5144 ###                                                                      ###
   5145 ###                                                                      ###
   5146 ###       ██████  ███████ ███    ██ ███████ ██████   █████  ██           ###
   5147 ###      ██       ██      ████   ██ ██      ██   ██ ██   ██ ██           ###
   5148 ###      ██   ███ █████   ██ ██  ██ █████   ██████  ███████ ██           ###
   5149 ###      ██    ██ ██      ██  ██ ██ ██      ██   ██ ██   ██ ██           ###
   5150 ###       ██████  ███████ ██   ████ ███████ ██   ██ ██   ██ ███████      ###
   5151 ###                                                                      ###
   5152 ###                                                                      ###
   5153 ############################################################################
   5154 
   5155 # Note: terminals extends to remotes as well
   5156 keymap("Cmd+Dot not in terminals", {
   5157     C("RC-Dot"):                C("Esc"),                       # Mimic macOS Cmd+dot = Escape key (not in terminals)
   5158 }, when = lambda ctx:
   5159     cnfg.screen_has_focus and
   5160     matchProps(not_clas=terms_and_remotes_Str)(ctx)
   5161 )
   5162 
   5163 
   5164 # Overrides to General GUI shortcuts for specific keyboard types
   5165 keymap("GenGUI overrides: Chromebook/IBM", {
   5166     # In-App Tab switching
   5167     C("Alt-Tab"):              [iEF2NT(),bind,C("C-Tab")],          # Chromebook/IBM - In-App Tab switching
   5168     C("Alt-Shift-Tab"):        [iEF2NT(),bind,C("C-Shift-Tab")],    # Chromebook/IBM - In-App Tab switching
   5169     C("Alt-Grave") :           [iEF2NT(),bind,C("C-Shift-Tab")],    # Chromebook/IBM - In-App Tab switching
   5170     C("RAlt-Backspace"):        C("Delete"),                        # Chromebook/IBM - Delete
   5171     C("LAlt-Backspace"):        C("C-Backspace"),                   # Chromebook/IBM - Delete Left Word of Cursor
   5172 }, when = lambda ctx:
   5173     cnfg.screen_has_focus and
   5174     (   isKBtype('Chromebook', map="gengui ovr cbook")(ctx) or
   5175         isKBtype('IBM', map="gengui ovr ibm")(ctx) ) and
   5176     matchProps(not_clas=remoteStr)(ctx)
   5177 )
   5178 keymap("GenGUI overrides: not Chromebook", {
   5179     # In-App Tab switching
   5180     C("Super-Tab"):            [iEF2NT(),bind,C("C-Tab")],          # Default not-chromebook
   5181     C("Super-Shift-Tab"):      [iEF2NT(),bind,C("Shift-C-Tab")],    # Default not-chromebook
   5182     C("Alt-Backspace"):         C("C-Backspace"),                   # Default not-chromebook
   5183 }, when = lambda ctx:
   5184     cnfg.screen_has_focus and
   5185     not isKBtype('Chromebook', map="gengui ovr not cbook")(ctx) and
   5186     matchProps(not_clas=remoteStr)(ctx)
   5187 )
   5188 
   5189 
   5190 # Overrides to General GUI shortcuts for specific distros
   5191 
   5192 if DISTRO_ID in ['almalinux', 'rhel', 'rocky'] and DESKTOP_ENV == 'xfce':
   5193     keymap("GenGUI overrides: Alma/Rocky Xfce4", {
   5194         C("RC-Space"):             [iEF2NT(),C("Alt-F3")],       # Launch App Finder xfce4 (AlmaLinux/Rocky)
   5195     }, when = lambda ctx:
   5196         cnfg.screen_has_focus and
   5197         matchProps(not_clas=remoteStr)(ctx)
   5198     )
   5199 
   5200 if DISTRO_ID == 'debian' and DESKTOP_ENV == 'xfce':
   5201     keymap("GenGUI overrides: Debian Xfce4", {
   5202         C("RC-Space"):             [iEF2NT(),C("Alt-F1")],     # Launch Application Menu xfce4 (Debian)
   5203     }, when = lambda ctx:
   5204         cnfg.screen_has_focus and
   5205         matchProps(not_clas=remoteStr)(ctx)
   5206     )
   5207 
   5208 if DISTRO_ID in ['fedora', 'almalinux'] and DESKTOP_ENV == 'gnome':
   5209     keymap("GenGUI overrides: Fedora GNOME", {
   5210         C("Super-RC-Q"):            C("Super-L"),                   # Lock screen (fedora)
   5211         C("RC-H"):                  C("Super-h"),                   # Default SL - Minimize app (gnome/budgie/popos/fedora) not-deepin
   5212         C("Super-Right"):          [bind,C("Super-Page_Up")],       # SL - Change workspace (ubuntu/fedora)
   5213         C("Super-Left"):           [bind,C("Super-Page_Down")],     # SL - Change workspace (ubuntu/fedora)
   5214     }, when = lambda ctx:
   5215         cnfg.screen_has_focus and
   5216         matchProps(not_clas=remoteStr)(ctx)
   5217     )
   5218 
   5219 if DISTRO_ID == 'manjaro' and DESKTOP_ENV == 'gnome':
   5220     keymap("GenGUI overrides: Manjaro GNOME", {
   5221         C("RC-Q"):              C("Super-Q"),                       # Close window
   5222     }, when = lambda ctx:
   5223         cnfg.screen_has_focus and
   5224         matchProps(not_clas=remoteStr)(ctx)
   5225     )
   5226 
   5227 if DISTRO_ID == 'manjaro' and DESKTOP_ENV == 'xfce':
   5228     keymap("GenGUI overrides: Manjaro Xfce", {
   5229         C("RC-Space"):             [iEF2NT(),C("Alt-F1")],          # Open Whisker Menu with Cmd+Space
   5230     }, when = lambda ctx:
   5231         cnfg.screen_has_focus and
   5232         matchProps(not_clas=remoteStr)(ctx)
   5233     )
   5234 
   5235 if DISTRO_ID == 'manjaro':
   5236     keymap("GenGUI overrides: Manjaro", {
   5237         C("Super-RC-f"):              C("Super-PAGE_UP"),             # SL- Maximize app manjaro
   5238     }, when = lambda ctx:
   5239         cnfg.screen_has_focus and
   5240         matchProps(not_clas=remoteStr)(ctx)
   5241     )
   5242 
   5243 if DISTRO_ID == 'mint' and DESKTOP_ENV == 'xfce':
   5244     keymap("GenGUI overrides: Mint Xfce4", {
   5245         C("RC-Space"):             [iEF2NT(),C("Super-Space")],     # Launch Application Menu xfce4 (Linux Mint)
   5246     }, when = lambda ctx:
   5247         cnfg.screen_has_focus and
   5248         matchProps(not_clas=remoteStr)(ctx)
   5249     )
   5250 
   5251 if DISTRO_ID == 'neon':
   5252     keymap("GenGUI overrides: KDE Neon", {
   5253         C("RC-Super-f"):            C("Super-Page_Up"),             # SL - Toggle maximized window state (kde_neon)
   5254         C("RC-H"):                  C("Super-Page_Down"),           # SL - Minimize app (kde_neon)
   5255                                                                     # SL - Default SL - Change workspace (kde_neon)
   5256     }, when = lambda ctx:
   5257         cnfg.screen_has_focus and
   5258         matchProps(not_clas=remoteStr)(ctx)
   5259     )
   5260 
   5261 if DISTRO_ID == 'pop':
   5262     keymap("GenGUI overrides: Pop!_OS", {
   5263         C("RC-Space"):             [iEF2NT(),C("Super-slash")],     # "Launch and switch applications" (pop)
   5264         C("RC-H"):                  C("Super-h"),                   # Default SL - Minimize app (gnome/budgie/popos/fedora) not-deepin
   5265         C("Super-Right"):          [bind,C("Super-C-Up")],          # SL - Change workspace (pop)
   5266         C("Super-Left"):           [bind,C("Super-C-Down")],        # SL - Change workspace (pop)
   5267         C("RC-Q"):                  C("Super-q"),                   # SL - Close Apps (pop)
   5268     }, when = lambda ctx:
   5269         cnfg.screen_has_focus and
   5270         matchProps(not_clas=remoteStr)(ctx)
   5271     )
   5272 
   5273 if DISTRO_ID == 'ubuntu':
   5274     keymap("GenGUI overrides: Ubuntu", {
   5275         C("Super-RC-Q"):            C("Super-L"),                   # Lock screen (ubuntu)
   5276         C("Super-Right"):          [bind,C("Super-Page_Up")],       # SL - Change workspace (ubuntu)
   5277         C("Super-Left"):           [bind,C("Super-Page_Down")],     # SL - Change workspace (ubuntu)
   5278     }, when = lambda ctx:
   5279         cnfg.screen_has_focus and
   5280         matchProps(not_clas=remoteStr)(ctx)
   5281     )
   5282 
   5283 
   5284 # Overrides to General GUI shortcuts for specific desktop environments
   5285 
   5286 if DESKTOP_ENV == 'budgie':
   5287     keymap("GenGUI overrides: Budgie", {
   5288         C("RC-Space"):             [iEF2NT(),Key.LEFT_META],        # Open panel-main-menu (Budgie menu)
   5289         C("Super-Right"):           C("C-Alt-Right"),               # Change workspace (budgie)
   5290         C("Super-Left"):            C("C-Alt-Left"),                # Change workspace (budgie)
   5291         C("RC-H"):                  C("Super-h"),                   # Minimize app (gnome/budgie/popos/fedora) not-deepin
   5292     }, when = lambda ctx:
   5293         cnfg.screen_has_focus and
   5294         matchProps(not_clas=remoteStr)(ctx)
   5295     )
   5296 
   5297 if DESKTOP_ENV == 'cinnamon':
   5298     keymap("GenGUI overrides: Cinnamon", {
   5299         C("RC-Space"):             [iEF2NT(),C("C-Esc")],           # Right click, configure Mint menu shortcut to Ctrl+Esc
   5300     }, when = lambda ctx:
   5301         cnfg.screen_has_focus and
   5302         matchProps(not_clas=remoteStr)(ctx)
   5303     )
   5304 
   5305 # On Pop!_OS 22.04, System76 seems to have changed the name of the DE from 'cosmic' to 'pop'.
   5306 # Probably to avoid overlapping with the new COSMIC desktop environment in Pop!_OS 24.04 and later.
   5307 if DESKTOP_ENV in ['cosmic', 'pop']:
   5308     keymap("GenGUI overrides: COSMIC", {
   5309         # No shortcuts settings panel seems to be available at this time (July 30, 2024),
   5310         # so we can't "fix" this during Toshy install to not use the Meta/Super key.
   5311         C("RC-Space"):             [Key.LEFT_META,iEF2NT()],        # Launcher or Workspaces or Applications (user choice)
   5312         C("RC-Q"):                  C("Super-Q"),                   # Close window/Quit (overrides Alt+F4 from General GUI)
   5313         C("Super-RC-F"):            C("Super-M"),                   # Maximize window toggle (overrides General GUI)
   5314     }, when = lambda ctx:
   5315         cnfg.screen_has_focus and
   5316         matchProps(not_clas=remoteStr)(ctx)
   5317     )
   5318 
   5319 if DESKTOP_ENV == 'dde':
   5320     keymap("GenGUI overrides: DDE", {
   5321         C("RC-Space"):             [iEF2NT(),Key.LEFT_META],        # Open Launcher menu (Deeping Desktop Environment)
   5322     }, when = lambda ctx:
   5323         cnfg.screen_has_focus and
   5324         matchProps(not_clas=remoteStr)(ctx)
   5325     )
   5326 
   5327 if DESKTOP_ENV == 'deepin':
   5328     keymap("GenGUI overrides: Deepin", {
   5329         C("RC-H"):                  C("Super-n"),                   # Minimize app (deepin)
   5330         C("Alt-RC-Space"):          C("Super-e"),                   # Open Finder - (deepin)
   5331     }, when = lambda ctx:
   5332         cnfg.screen_has_focus and
   5333         matchProps(not_clas=remoteStr)(ctx)
   5334     )
   5335 
   5336 if DESKTOP_ENV == 'enlightenment':
   5337     keymap("GenGUI overrides: Enlightenment", {
   5338         C("RC-q"):                  C("C-Alt-x"),                   # Close window (Cmd+Q)
   5339         # C("RC-Space"):             [iEF2NT(),C("C-Alt-m")],         # enlightenment main menu (override in "User Apps" slice if necessary)
   5340         C("RC-Space"):             [iEF2NT(),C("C-Alt-Space")],     # enlightenment main menu (override in "User Apps" slice if necessary)
   5341     }, when = lambda ctx:
   5342         cnfg.screen_has_focus and
   5343         matchProps(not_clas=remoteStr)(ctx)
   5344     )
   5345 
   5346 if DESKTOP_ENV == 'gnome':
   5347     if is_pre_GNOME_45(DE_MAJ_VER):
   5348         # This keymap, if invoked, must come before the other GNOME overrides in the next keymap, not after.
   5349         keymap("GenGUI overrides: pre-GNOME 45 fix", {
   5350             C("RC-Space"):             [iEF2NT(),C("Super-s")],         # Override GNOME 45+ Shift+Ctrl+Space remap
   5351         }, when = lambda ctx:
   5352             cnfg.screen_has_focus and
   5353             matchProps(not_clas=remoteStr)(ctx)
   5354         )
   5355     keymap("GenGUI overrides: GNOME", {
   5356         C("RC-Space"):             [iEF2NT(),C("Shift-C-Space")],   # Show GNOME overview/app launcher
   5357         C("RC-F3"):                 C("Super-d"),                   # Default SL - Show Desktop (gnome/kde,elementary)
   5358         C("RC-Super-f"):            C("Alt-F10"),                   # Default SL - Maximize app (gnome/kde)
   5359         C("RC-H"):                  C("Super-h"),                   # Default SL - Minimize app (gnome/budgie/popos/fedora) not-deepin
   5360         # Screenshot shortcuts for GNOME 42+
   5361         C("RC-Shift-Key_3"):        C("Shift-Print"),               # Take a screenshot immediately (gnome)
   5362         C("RC-Shift-Key_4"):        C("Alt-Print"),                 # Take a screenshot of a window (gnome)
   5363         C("RC-Shift-Key_5"):        C("Print"),                     # Take a screenshot interactively (gnome)
   5364     }, when = lambda ctx:
   5365         cnfg.screen_has_focus and
   5366         matchProps(not_clas=remoteStr)(ctx)
   5367     )
   5368 
   5369 if DESKTOP_ENV == 'hyprland':
   5370     keymap("GenGUI overrides: Hyprland", {
   5371         # C("RC-Space"):             [iEF2NT(),Key.LEFT_META],        # Open Launcher with Cmd+Space
   5372         C("RC-Space"):             [C("Super-d"), iEF2NT()],        # Open Launcher with Cmd+Space
   5373     }, when = lambda ctx:
   5374         cnfg.screen_has_focus and
   5375         matchProps(not_clas=remoteStr)(ctx)
   5376     )
   5377 
   5378 if DESKTOP_ENV == 'icewm':
   5379     keymap("GenGUI overrides: IceWM", {
   5380         C("RC-Space"):             [iEF2NT(),Key.LEFT_META],        # IceWM: Win95Keys=1 (Meta shows menu)
   5381     }, when = lambda ctx:
   5382         cnfg.screen_has_focus and
   5383         matchProps(not_clas=remoteStr)(ctx)
   5384     )
   5385 
   5386 if DESKTOP_ENV == 'kde':
   5387     keymap("GenGUI overrides: KDE", {
   5388 
   5389         # Application launcher menu remap
   5390         # C("RC-Space"):             [iEF2NT(),C("Alt-F1")],          # Application Launcher Menu
   5391 
   5392         # krunner drop-down (similar to Spotlight) remap
   5393         C("RC-Space"):             [iEF2NT(),C("Alt-Space")],          # Invoke krunner drop-down
   5394 
   5395         C("RC-F3"):                 C("Super-d"),                   # Show Desktop (gnome/kde,elementary)
   5396         C("RC-H"):                  C("Super-Page_Down"),           # Minimize app (KDE Plasma)
   5397 
   5398         # Workspace (virtual desktop) navigation
   5399         C("Super-Left"):            C("C-Super-Left"),              # Switch one desktop to the left
   5400         C("Super-Right"):           C("C-Super-Right"),             # Switch one desktop to the right
   5401 
   5402         # C("Super-RC-f"):            C("Alt-F10"),                   # Toggle window maximized state (pre-Plasma 6)
   5403         # F10 key was designated an accessibility key for opening the window/app menu in KDE.
   5404         # The shortcut for toggling window maximization state is now Meta+PgUp (so, Super-Page_Up).
   5405         C("Super-RC-f"):            C("Super-Page_Up"),             # Toggle window maximized state
   5406 
   5407         # Screenshot shortcuts for KDE Plasma desktops (Spectacle app)
   5408         C("RC-Shift-Key_3"):        C("Shift-Print"),               # Take a screenshot immediately (kde)
   5409         C("RC-Shift-Key_4"):        C("Alt-Print"),                 # Take a screenshot of a window (kde)
   5410         C("RC-Shift-Key_5"):        C("Print"),                     # Take a screenshot interactively (kde)
   5411 
   5412         ### Keyboard input source (language/layout) switching in KDE Plasma
   5413         C("Super-Space"):          [bind,C("Super-Alt-L")],         # keyboard input source (layout) switching (Last-Used) (kde)
   5414         C("Shift-Super-Space"):    [bind,C("Super-Alt-K")],         # keyboard input source (layout) switching (Next) (kde)
   5415 
   5416     }, when = lambda ctx:
   5417         cnfg.screen_has_focus and
   5418         matchProps(not_clas=remoteStr)(ctx)
   5419     )
   5420 
   5421 if DESKTOP_ENV == 'mate' and DISTRO_ID == 'mint':
   5422     keymap("GenGUI overrides: MATE on Mint", {
   5423         # Right click, configure Mint menu shortcut to match `Alt+Space` shortcut to make this work
   5424         C("RC-Space"):             [iEF2NT(), C("Alt-Space")],       # Open Mint app menu
   5425     }, when = lambda ctx:
   5426         cnfg.screen_has_focus and
   5427         matchProps(not_clas=remoteStr)(ctx)
   5428     )
   5429 
   5430 if DESKTOP_ENV == 'mate' and DISTRO_ID == 'ubuntu':
   5431     keymap("GenGUI overrides: MATE on Ubuntu", {
   5432         # No way to reconfigure the shortcut for Brisk Menu Launcher?
   5433         C("RC-Space"):             [iEF2NT(), Key.LEFT_META],       # Open Brisk Menu Launcher
   5434     }, when = lambda ctx:
   5435         cnfg.screen_has_focus and
   5436         matchProps(not_clas=remoteStr)(ctx)
   5437     )
   5438 
   5439 if DESKTOP_ENV == 'miracle-wm':
   5440     keymap("GenGUI overrides: MiracleWM", {
   5441         # C("RC-Space"):             [iEF2NT(),Key.LEFT_META],        # Open Launcher with Cmd+Space
   5442         C("RC-Space"):             [C("Super-d"), iEF2NT()],        # Open Launcher with Cmd+Space
   5443     }, when = lambda ctx:
   5444         cnfg.screen_has_focus and
   5445         matchProps(not_clas=remoteStr)(ctx)
   5446     )
   5447 
   5448 if DESKTOP_ENV == 'pantheon':
   5449     keymap("GenGUI overrides: Pantheon", {
   5450         C("RC-F3"):                 C("Super-d"),                   # Show Desktop (gnome/kde,elementary)
   5451         # C("RC-Space"):             [iEF2NT(),C("Super-Space")],     # Launch Application Menu (elementary)
   5452         C("RC-Space"):             [iEF2NT(),C("Alt-F2")],          # Launch Application Menu (elementary OS 8)
   5453         C("Super-RC-f"):            C("Super-Up"),                  # Maximize app elementary
   5454     }, when = lambda ctx:
   5455         cnfg.screen_has_focus and
   5456         matchProps(not_clas=remoteStr)(ctx)
   5457     )
   5458 
   5459 if DESKTOP_ENV == 'sway':
   5460     keymap("GenGUI overrides: swaywm", {
   5461         C("RC-Space"):             [iEF2NT(),C("Super-d")],         # Open sway launcher
   5462         C("RC-Q"):                  C("C-Q"),                       # Override General GUI Alt+F4 remap
   5463     }, when = lambda ctx:
   5464         cnfg.screen_has_focus and
   5465         matchProps(not_clas=remoteStr)(ctx)
   5466     )
   5467 
   5468 if DESKTOP_ENV == 'trinity':
   5469     keymap("GenGUI overrides: Trinity desktop", {
   5470         C("RC-Space"):             [iEF2NT(),Key.LEFT_META],        # Trinity desktop (Q4OS)
   5471     }, when = lambda ctx:
   5472         cnfg.screen_has_focus and
   5473         matchProps(not_clas=remoteStr)(ctx)
   5474     )
   5475 
   5476 if DESKTOP_ENV == 'unity':
   5477     keymap("GenGUI overrides: Unity desktop", {
   5478         C("RC-Space"):             [iEF2NT(),Key.LEFT_META],        # Trinity desktop (Q4OS)
   5479     }, when = lambda ctx:
   5480         cnfg.screen_has_focus and
   5481         matchProps(not_clas=remoteStr)(ctx)
   5482     )
   5483 
   5484 if DESKTOP_ENV == 'xfce' and DISTRO_ID == 'zorin':
   5485     keymap("GenGUI overrides: Zorin Xfce", {
   5486         C("RC-Space"):             [iEF2NT(),C("Alt-Pause")],     # "Launch and switch applications" (Xfce on Zorin)
   5487     }, when = lambda ctx:
   5488         cnfg.screen_has_focus and
   5489         matchProps(not_clas=remoteStr)(ctx)
   5490     )
   5491 
   5492 if DESKTOP_ENV == 'xfce':
   5493     keymap("GenGUI overrides: Xfce4", {
   5494         C("RC-Grave"):             [bind,C("Super-Tab")],           # xfce4 Switch within app group
   5495         C("Shift-RC-Grave"):       [bind,C("Super-Shift-Tab")],     # xfce4 Switch within app group
   5496         C("RC-Space"):             [iEF2NT(),C("C-Esc")],           # Launch Application Menu xfce4 (Xubuntu)
   5497         C("RC-F3"):                 C("C-Alt-d"),                   # SL- Show Desktop xfce4
   5498         C("RC-H"):                  C("Alt-F9"),                    # SL - Minimize app xfce4
   5499         # Screenshot shortcuts for Xfce desktops (xfce4-screenshooter app)
   5500         C("RC-Shift-Key_3"):        C("Print"),                     # Take a screenshot immediately (xfce4)
   5501         C("RC-Shift-Key_4"):        C("Alt-Print"),                 # Take a screenshot of a window (xfce4)
   5502         C("RC-Shift-Key_5"):        C("Shift-Print"),               # Take a screenshot interactively (xfce4)
   5503     }, when = lambda ctx:
   5504         cnfg.screen_has_focus and
   5505         matchProps(not_clas=remoteStr)(ctx)
   5506     )
   5507 
   5508 
   5509 # None referenced here originally
   5510 # - but remote clients and VM software ought to be set here
   5511 # These are the typical remaps for ALL GUI based apps
   5512 keymap("General GUI", {
   5513 
   5514     C("Alt-Numlock"):           toggle_forced_numpad,           # Turn the Forced Numpad feature on and off
   5515     C("Fn-Numlock"):            toggle_forced_numpad,           # Turn the Forced Numpad feature on and off
   5516     C("Numlock"):               isNumlockClearKey,              # Numlock key is "Clear" (Esc) if cnfg.forced_numpad is True
   5517 
   5518     C("Shift-RC-Left_Brace"):   C("C-Page_Up"),                 # Tab navigation: Go to prior (left) tab
   5519     C("Shift-RC-Right_Brace"):  C("C-Page_Down"),               # Tab navigation: Go to next (right) tab
   5520     C("RC-Space"):             [iEF2NT(),C("Alt-F1")],          # Default SL - Launch Application Menu (gnome/kde)
   5521     C("RC-F3"):                 C("Super-d"),                   # Default SL - Show Desktop (gnome/kde,elementary)
   5522     C("RC-Super-f"):            C("Alt-F10"),                   # Default SL - Maximize app (gnome/kde)
   5523     C("RC-Q"):                  C("Alt-F4"),                    # Default SL - not-popos
   5524     C("Alt-Tab"):               ignore_combo,                   # Default - Cmd Tab - App Switching Default
   5525 
   5526     C("RC-Tab"):            [iEF2NT(),bind, C("Alt-Tab")],           # Default - Cmd Tab - App Switching Default
   5527     C("Shift-RC-Tab"):      [iEF2NT(),bind, C("Alt-Shift-Tab")],     # Default - Cmd Tab - App Switching Default
   5528 
   5529     C("RC-Grave"):          [iEF2NT(),bind, C("Alt-Grave")],         # Default not-xfce4 - Cmd ` - Same App Switching
   5530     C("Shift-RC-Grave"):    [iEF2NT(),bind, C("Alt-Shift-Grave")],   # Default not-xfce4 - Cmd ` - Same App Switching
   5531 
   5532     # Fn to Alt style remaps
   5533     C("RAlt-Enter"):            C("insert"),                    # Insert
   5534 
   5535     # emacs style
   5536     C("Super-a"):               C("Home"),                      # Beginning of Line
   5537     C("Super-e"):               C("End"),                       # End of Line
   5538     C("Super-b"):               C("Left"),
   5539     C("Super-f"):               C("Right"),
   5540     C("Super-n"):               C("Down"),
   5541     C("Super-p"):               C("Up"),
   5542     C("Super-k"):              [C("Shift-End"), C("Backspace")],
   5543     C("Super-d"):               C("Delete"),
   5544 
   5545     # This is better done with a native custom shortcut in each DE
   5546     # C("Alt-RC-Space"):          C(""),                          # Open Finder - Placeholder not-deepin
   5547 
   5548     # Wordwise
   5549     C("RC-Left"):               C("Home"),                      # Beginning of Line
   5550     C("Shift-RC-Left"):         C("Shift-Home"),                # Select all to Beginning of Line
   5551     C("RC-Right"):              C("End"),                       # End of Line
   5552     C("Shift-RC-Right"):        C("Shift-End"),                 # Select all to End of Line
   5553     C("RC-Up"):                 C("C-Home"),                    # Beginning of File
   5554     C("Shift-RC-Up"):           C("C-Shift-Home"),              # Select all to Beginning of File
   5555     C("RC-Down"):               C("C-End"),                     # End of File
   5556     C("Shift-RC-Down"):         C("C-Shift-End"),               # Select all to End of File
   5557     C("Super-Backspace"):       C("C-Backspace"),               # Delete Left Word of Cursor
   5558     C("Super-Delete"):          C("C-Delete"),                  # Delete Right Word of Cursor
   5559     C("RC-Backspace"):          C("C-Shift-Backspace"),         # Delete Entire Line Left of Cursor
   5560     C("Alt-Delete"):            C("C-Delete"),                  # Delete Right Word of Cursor
   5561     C("Shift-Alt-Backspace"):   C("C-Backspace"),               # Delete word left of cursor
   5562     C("Shift-Alt-Delete"):      C("C-Delete"),                  # Delete word right of cursor
   5563 
   5564     # Allow access to Linux TTY virtual consoles with the usual physical key locations (General GUI)
   5565     C("Super-RC-F1"):           C("C-Alt-F1"),                  # Go to TTY virtual console 1
   5566     C("Super-RC-F2"):           C("C-Alt-F2"),                  # Go to TTY virtual console 2
   5567     C("Super-RC-F3"):           C("C-Alt-F3"),                  # Go to TTY virtual console 3
   5568     C("Super-RC-F4"):           C("C-Alt-F4"),                  # Go to TTY virtual console 4
   5569     C("Super-RC-F5"):           C("C-Alt-F5"),                  # Go to TTY virtual console 5
   5570     C("Super-RC-F6"):           C("C-Alt-F6"),                  # Go to TTY virtual console 6
   5571     C("Super-RC-F7"):           C("C-Alt-F7"),                  # Go to TTY virtual console 7
   5572 
   5573     # C("RC-Left"):               C("C-LEFT_BRACE"),              # Firefox-nw - Back
   5574     # C("RC-Right"):              C("C-RIGHT_BRACE"),             # Firefox-nw - Forward
   5575     # C("RC-Left"):               C("Alt-LEFT"),                  # Chrome-nw - Back
   5576     # C("RC-Right"):              C("Alt-RIGHT"),                 # Chrome-nw - Forward
   5577     # C(""):                      ignore_combo,                   # cancel
   5578     # C(""):                      C(""),                          #
   5579 
   5580 }, when = lambda ctx:
   5581     cnfg.screen_has_focus and
   5582     matchProps(not_clas=remoteStr)(ctx)
   5583 )
   5584 
   5585 
   5586 keymap("Diagnostics (isMultiTap)", {
   5587 
   5588     C("Shift-Alt-RC-i"): isMultiTap(
   5589                             # tap_1_action=None,  # Block single tap
   5590                             tap_1_action=C("Shift-Alt-C-i"),    # Keep original single-tap combo
   5591                             tap_2_action=notify_context,
   5592                         ),
   5593 
   5594     C("Shift-Alt-RC-h"): isMultiTap(
   5595                             tap_1_action=C("Shift-Alt-RC-h"),   # Keep original single-tap combo
   5596                             tap_2_action=notify_context,
   5597                             tap_3_action=lambda: print("\nTriple tap!\n"),  # Shows in terminal
   5598                         ),
   5599 
   5600     C("Shift-Alt-RC-t"): isMultiTap(
   5601                             tap_1_action=C("C-n"),          # Test single-tap by opening new window
   5602                             tap_2_action=macro_tester,      # Types out a long test macro text
   5603                             tap_3_action=[
   5604                                 ST("You tapped Shift-Alt-C-t 3 times!!!"),
   5605                                 C("Enter"), C("Enter")],
   5606                             tap_4_action=[
   5607                                 ST("You tapped Shift-Alt-C-t 4 times!!!!"),
   5608                                 C("Enter"), C("Enter")],
   5609                             tap_5_action=[
   5610                                 ST("You tapped Shift-Alt-C-t 5 times!!!!!"),
   5611                                 C("Enter"), C("Enter")],
   5612                         ),
   5613 
   5614 }, when = lambda ctx: ctx is ctx)
   5615 
   5616 
   5617 # keymap("Diagnostics (isDoubleTap)", {
   5618 #     C("Shift-Alt-RC-i"):        isDoubleTap(notify_context),    # Diagnostic dialog (primary)
   5619 #     C("Shift-Alt-RC-h"):        isDoubleTap(notify_context),    # Diagnostic dialog (alternate)
   5620 #     C("Shift-Alt-RC-t"):        isDoubleTap(macro_tester),      # Type out test macro
   5621 # }, when = lambda ctx: ctx is ctx )