Skip to content

Instantly share code, notes, and snippets.

@kolibril13
Created April 29, 2026 09:10
Show Gist options
  • Select an option

  • Save kolibril13/7b09017c6dce1bf2e7c8d706ecbbc66a to your computer and use it in GitHub Desktop.

Select an option

Save kolibril13/7b09017c6dce1bf2e7c8d706ecbbc66a to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# macOS only
"""
DaVinci Resolve keyboard binding editor.
Usage:
python resolve_keybind.py list [filter]
python resolve_keybind.py get <command>
python resolve_keybind.py set <command> <key> [modifiers]
key: single char (x) or special (delete, backspace, f1..f12, etc.)
modifiers: comma-separated ctrl,shift,alt,meta
Examples:
python resolve_keybind.py get editBackspace
python resolve_keybind.py set editBackspace x
python resolve_keybind.py set editRippleCut x ctrl,shift
python resolve_keybind.py list delete
"""
import re
import shutil
import struct
import sys
import uuid
from pathlib import Path
PRESET_PATH = Path.home() / "Library/Preferences/Blackmagic Design/DaVinci Resolve/keyboard.preset.xml"
MODIFIERS = {"ctrl": 0x01, "shift": 0x02, "alt": 0x04, "meta": 0x08}
SPECIAL_KEYS = {
"backspace": 0x01000003, "delete": 0x01000007, "escape": 0x01000000,
"return": 0x01000004, "tab": 0x01000001,
"left": 0x01000012, "right": 0x01000014,
"up": 0x01000013, "down": 0x01000015,
"home": 0x01000010, "end": 0x01000011,
"pageup": 0x01000016, "pagedown": 0x01000017,
**{f"f{i}": 0x0100002F + i for i in range(1, 13)},
}
_SPECIAL_BY_CODE = {v & 0x00FFFFFF: k.capitalize() for k, v in SPECIAL_KEYS.items()}
_U16 = struct.Struct(">H")
_U32 = struct.Struct(">I")
def _scan_strings(data):
"""Yield (end_offset, string) for each UTF-16 BE ASCII run of length >= 3."""
i, n = 0, len(data) - 1
while i < n:
chars, j = [], i
while j < n:
(cp,) = _U16.unpack_from(data, j)
if 32 <= cp <= 126:
chars.append(chr(cp))
j += 2
else:
break
if len(chars) >= 3:
yield j, "".join(chars)
i = j
else:
i += 1
def _parse_bindings(data, offset):
"""Return list of (byte_offset, key_field) for bindings starting at offset."""
if offset + 4 > len(data):
return []
(n,) = _U32.unpack_from(data, offset)
if not (0 < n <= 20):
return []
result = []
for i in range(n):
off = offset + 4 + i * 8
if off + 8 > len(data):
break
(key_field,) = _U32.unpack_from(data, off + 4)
result.append((off, key_field))
return result
def _decode_key(key_field):
mod, key = (key_field >> 24) & 0xFF, key_field & 0x00FFFFFF
parts = [name.capitalize() for name, bit in MODIFIERS.items() if mod & bit]
parts.append(_SPECIAL_BY_CODE.get(key) or (chr(key) if 32 <= key <= 126 else f"0x{key:06x}"))
return "+".join(parts)
def _encode_key(key_str, mods=()):
mod = 0
for m in mods:
if m not in MODIFIERS:
raise ValueError(f"unknown modifier '{m}'. Use: {', '.join(MODIFIERS)}")
mod |= MODIFIERS[m]
k = key_str.lower()
if k in SPECIAL_KEYS:
key_code = SPECIAL_KEYS[k] & 0x00FFFFFF
elif len(key_str) == 1:
key_code = ord(key_str.upper())
else:
raise ValueError(f"unknown key '{key_str}'")
return (mod << 24) | key_code
def _read_blob():
text = PRESET_PATH.read_text(encoding="utf-8")
m = re.search(r"<PresetListBA>([0-9a-f]+)</PresetListBA>", text)
if not m:
raise ValueError("PresetListBA not found in preset file")
return text, bytes.fromhex(m.group(1))
def _write_blob(text, blob):
backup = PRESET_PATH.with_suffix(".xml.bak")
shutil.copy2(PRESET_PATH, backup)
print(f"Backup → {backup}")
new_text = text.replace(
re.search(r"<PresetListBA>[0-9a-f]+</PresetListBA>", text).group(),
f"<PresetListBA>{blob.hex()}</PresetListBA>",
)
PRESET_PATH.write_text(new_text, encoding="utf-8")
print(f"Saved → {PRESET_PATH}")
def _build_blob(command, key_field, preset_name="Custom"):
name_utf16 = preset_name.encode("utf-16-be")
cmd_utf16 = command.encode("utf-16-be")
entry = _U16.pack(len(cmd_utf16)) + cmd_utf16 + _U32.pack(1) + _U32.pack(1) + _U32.pack(key_field)
body = _U32.pack(1) + _U32.pack(1) + entry
return _U32.pack(1) + _U32.pack(1) + _U32.pack(len(name_utf16)) + name_utf16 + _U32.pack(len(body)) + body
def _create_preset(command, key_field):
blob = _build_blob(command, key_field)
xml = (
f'<?xml version="1.0" encoding="UTF-8"?>\n'
f'<SmKeyboardPresetList DbId="{uuid.uuid4()}">\n'
f' <FieldsBlob/>\n'
f' <PresetListBA>{blob.hex()}</PresetListBA>\n'
f'</SmKeyboardPresetList>\n'
)
PRESET_PATH.parent.mkdir(parents=True, exist_ok=True)
PRESET_PATH.write_text(xml, encoding="utf-8")
print(f"Created → {PRESET_PATH}")
def cmd_list(filt=None):
try:
_, blob = _read_blob()
except (FileNotFoundError, ValueError) as e:
sys.exit(f"Error: {e}")
for end, name in _scan_strings(blob):
if filt and filt.lower() not in name.lower():
continue
for _, key_field in _parse_bindings(blob, end):
print(f"{name:<55} {_decode_key(key_field)}")
def cmd_get(command):
try:
_, blob = _read_blob()
except (FileNotFoundError, ValueError) as e:
sys.exit(f"Error: {e}")
found = False
for end, name in _scan_strings(blob):
if name != command:
continue
found = True
bindings = _parse_bindings(blob, end)
if not bindings:
print(f"{name}: (no bindings)")
for i, (off, key_field) in enumerate(bindings):
print(f"{name} [{i}]: {_decode_key(key_field)} (raw @ {off}: {blob[off:off+8].hex()})")
if not found:
print(f"Command '{command}' not found.")
def cmd_set(command, key_field):
if not PRESET_PATH.exists():
_create_preset(command, key_field)
print(f"Set {command} → {_decode_key(key_field)}")
return
try:
text, blob = _read_blob()
except ValueError as e:
sys.exit(f"Error: {e}")
new_blob = bytearray(blob)
for end, name in _scan_strings(blob):
if name != command:
continue
bindings = _parse_bindings(blob, end)
if not bindings:
sys.exit(f"Command '{command}' has no bindings — cannot patch")
off, _ = bindings[0]
new_blob[off + 4:off + 8] = _U32.pack(key_field)
print(f"Set {command} → {_decode_key(key_field)} (raw @ {off}: {new_blob[off:off+8].hex()})")
break
else:
sys.exit(f"Command '{command}' not found in preset")
_write_blob(text, bytes(new_blob))
def main():
args = sys.argv[1:]
if not args:
sys.exit(__doc__)
verb, rest = args[0], args[1:]
if verb == "list":
cmd_list(rest[0] if rest else None)
elif verb == "get":
if not rest:
sys.exit("Usage: get <command>")
cmd_get(rest[0])
elif verb == "set":
if len(rest) < 2:
sys.exit("Usage: set <command> <key> [modifiers]")
mods = [m.strip() for m in rest[2].split(",")] if len(rest) > 2 else []
try:
key_field = _encode_key(rest[1], mods)
except ValueError as e:
sys.exit(str(e))
cmd_set(rest[0], key_field)
else:
sys.exit(__doc__)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment