"""Add or replace a PowerToys Keyboard Manager global shortcut.""" import json import os # Windows Virtual Key codes — key name (lowercase) → VK int VK_CODES: dict[str, int] = { "lwin": 91, "rwin": 92, "lshift": 160, "rshift": 161, "shift": 160, "lctrl": 162, "rctrl": 163, "ctrl": 162, "lalt": 164, "ralt": 165, "alt": 164, "space": 32, "enter": 13, "tab": 9, "esc": 27, "win": 91, # F-keys "f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123, # Digits "0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, } # Letters A-Z for _ch in "abcdefghijklmnopqrstuvwxyz": VK_CODES[_ch] = ord(_ch.upper()) def _keys_to_vk_string(keys: list[str]) -> str: """Convert a list of key names to a semicolon-separated VK code string. Key names are case-insensitive. Letters are matched as lowercase. Raises ValueError for unknown key names. """ codes = [] for key in keys: normalized = key.lower() if normalized not in VK_CODES: raise ValueError( f"Unknown key name: '{key}'. " f"Use names like 'ctrl', 'alt', 'lctrl', 'lalt', 'shift', 't', 'f1', etc." ) codes.append(str(VK_CODES[normalized])) return ";".join(codes) def _default_config_path() -> str: override = os.environ.get("POWERTOYS_CONFIG") if override: return override user = os.environ.get("USER") or os.environ.get("USERNAME", "") return ( f"/mnt/c/Users/{user}/AppData/Local/Microsoft/PowerToys" f"/Keyboard Manager/default.json" ) def powertoys_shortcut_add( keys: list[str], target_path: str, args: str = "", elevated: bool = False, exact_match: bool = False, start_in_dir: str = "", config_path: str | None = None, ) -> None: """Add or replace a global shortcut in PowerToys Keyboard Manager config. Converts the human-readable key list to VK code string and writes the entry into remapShortcuts.global. If an entry with the same originalKeys already exists, it is replaced. Writes JSON in compact (single-line) format to maintain compatibility with the format PowerToys uses. Args: keys: List of key names (case-insensitive). E.g. ["lctrl", "lalt", "t"]. Supported modifiers: lctrl/rctrl/ctrl, lalt/ralt/alt, lshift/rshift/shift, lwin/rwin/win. Letters: a-z. Digits: 0-9. F-keys: f1-f12. Special: space, enter, tab, esc. target_path: Windows path to the executable. E.g. "C:\\Windows\\System32\\wt.exe". args: Command-line arguments for the program (default ""). elevated: Whether to run the program elevated (default False). exact_match: Whether the shortcut requires an exact key match (default False). start_in_dir: Working directory for the program. Defaults to "" (empty string). config_path: Path to default.json. If None, uses $POWERTOYS_CONFIG or WSL default. """ if config_path is None: config_path = _default_config_path() with open(config_path, "r", encoding="utf-8") as f: data = json.load(f) original_keys_str = _keys_to_vk_string(keys) new_entry = { "originalKeys": original_keys_str, "exactMatch": exact_match, "runProgramElevationLevel": 1 if elevated else 0, "operationType": 1, "runProgramAlreadyRunningAction": 1, "runProgramStartWindowType": 0, "runProgramFilePath": target_path, "runProgramArgs": args, "runProgramStartInDir": start_in_dir, "unicodeText": "*Unsupported*", } global_list: list[dict] = data.setdefault("remapShortcuts", {}).setdefault("global", []) # Replace if same originalKeys already exists replaced = False for i, entry in enumerate(global_list): if entry.get("originalKeys") == original_keys_str: global_list[i] = new_entry replaced = True break if not replaced: global_list.append(new_entry) with open(config_path, "w", encoding="utf-8") as f: json.dump(data, f, separators=(",", ":"), ensure_ascii=False)