SideFX Houdini customization package
git clone https://git.lucas.co/hou-control.git
python3.13libs/hc/hcleader.py (1.4K)
1 import time
2
3 from . import hcstate
4
5
6 _TIMEOUT = 1.0
7 _pending = hcstate.Store("leader_chord", hcstate.PANE)
8
9
10 def chord(editor, action_fn):
11 """Vim-style leader chord. Returns True if action was executed, False if
12 this was the first keypress (leader registered, waiting for second).
13
14 ``editor`` is a ``hou.NetworkEditor`` (or any ``hou.PaneTab``).
15 Houdini creates fresh Python wrappers each time a script fires, so
16 ``id()`` is unreliable and the wrapper itself must not be kept: holding
17 one past the current callback leaves a dangling SWIG pointer, and reading
18 it later segfaults Houdini. ``name()`` is a plain string that stays stable
19 across those wrappers, so that is what gets stored.
20
21 Nothing here may touch HOM off the main thread. An earlier version
22 expired entries from a ``threading.Timer``, whose callback compared the
23 stored ``hou.PaneTab`` keys from a background thread -- HOM is not
24 thread-safe, and that crashed Houdini with SIGSEGV inside SWIG's ``__eq__``.
25 The elapsed-time check below already ignores a stale entry, so no timer is
26 needed: a leftover entry is harmless and the next keypress overwrites it."""
27 now = time.monotonic()
28 old_ts = _pending.get(editor)
29
30 if old_ts and (now - old_ts) < _TIMEOUT:
31 _pending.pop(editor)
32 action_fn()
33 return True
34
35 _pending.set(editor, now)
36 return False