Files
wehub-resource-sync 91e75e620b
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:19 +08:00

161 lines
4.3 KiB
Python

"""Cross-platform keyboard control via pynput."""
from typing import List, Optional, Union
from pynput.keyboard import Controller as _KBController
from pynput.keyboard import Key as _Key
_kb = _KBController()
# Unified key name → pynput Key map (sourced from computer-server/handlers/windows.py)
_SPECIAL: dict = {
"enter": _Key.enter,
"return": _Key.enter,
"esc": _Key.esc,
"escape": _Key.esc,
"space": _Key.space,
"tab": _Key.tab,
"backspace": _Key.backspace,
"delete": _Key.delete,
"home": _Key.home,
"end": _Key.end,
"pageup": _Key.page_up,
"page_up": _Key.page_up,
"pagedown": _Key.page_down,
"page_down": _Key.page_down,
"up": _Key.up,
"down": _Key.down,
"left": _Key.left,
"right": _Key.right,
"shift": _Key.shift,
"shift_l": _Key.shift_l,
"shift_r": _Key.shift_r,
"ctrl": _Key.ctrl,
"ctrl_l": _Key.ctrl_l,
"ctrl_r": _Key.ctrl_r,
"control": _Key.ctrl,
"alt": _Key.alt,
"alt_l": _Key.alt_l,
"alt_r": _Key.alt_r,
"cmd": _Key.cmd,
"command": _Key.cmd,
"win": _Key.cmd,
"super": _Key.cmd,
"meta": _Key.cmd,
"option": _Key.alt,
"option_l": _Key.alt_l,
"option_r": _Key.alt_r,
"capslock": _Key.caps_lock,
"caps_lock": _Key.caps_lock,
"f1": _Key.f1,
"f2": _Key.f2,
"f3": _Key.f3,
"f4": _Key.f4,
"f5": _Key.f5,
"f6": _Key.f6,
"f7": _Key.f7,
"f8": _Key.f8,
"f9": _Key.f9,
"f10": _Key.f10,
"f11": _Key.f11,
"f12": _Key.f12,
"f13": _Key.f13,
"f14": _Key.f14,
"f15": _Key.f15,
"f16": _Key.f16,
"f17": _Key.f17,
"f18": _Key.f18,
"f19": _Key.f19,
"f20": _Key.f20,
}
# Keys that may not exist on all platforms (e.g. macOS lacks insert, print_screen)
for _name, _attr in [
("insert", "insert"),
("print_screen", "print_screen"),
("pause", "pause"),
("num_lock", "num_lock"),
("scroll_lock", "scroll_lock"),
]:
_val = getattr(_Key, _attr, None)
if _val is not None:
_SPECIAL[_name] = _val
def _resolve(key: str) -> Optional[Union[str, _Key]]:
"""Resolve a key string to a pynput Key or single character."""
if not key:
return None
lk = key.lower()
if lk in _SPECIAL:
return _SPECIAL[lk]
if len(key) == 1:
return key
return None
# ── Key press / release ───────────────────────────────────────────────────────
def key_down(key: str) -> None:
"""Press and hold a key."""
k = _resolve(key)
if k is None:
raise ValueError(f"Unknown key: {key!r}")
_kb.press(k)
def key_up(key: str) -> None:
"""Release a key."""
k = _resolve(key)
if k is None:
raise ValueError(f"Unknown key: {key!r}")
_kb.release(k)
def press_key(key: str) -> None:
"""Press and immediately release a key."""
k = _resolve(key)
if k is None:
raise ValueError(f"Unknown key: {key!r}")
_kb.press(k)
_kb.release(k)
# ── Text typing ───────────────────────────────────────────────────────────────
def type_text(text: str) -> None:
"""Type a string of text (supports Unicode)."""
_kb.type(text)
# ── Hotkeys ───────────────────────────────────────────────────────────────────
def hotkey(keys: List[str]) -> None:
"""Press a combination of keys, e.g. ['ctrl', 'c'].
Modifiers are held while the last key is tapped, then released in reverse
order (standard hotkey behaviour).
"""
resolved = [_resolve(k) for k in keys]
if any(k is None for k in resolved):
bad = [k for k, r in zip(keys, resolved) if r is None]
raise ValueError(f"Unknown keys in hotkey: {bad}")
seq: List[Union[str, _Key]] = [k for k in resolved if k is not None]
if not seq:
return
# Hold all modifiers except the last key
for k in seq[:-1]:
_kb.press(k)
# Tap the action key
last = seq[-1]
_kb.press(last)
_kb.release(last)
# Release modifiers in reverse order
for k in reversed(seq[:-1]):
_kb.release(k)