diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-09 20:09:38 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-09 20:09:38 +0100 |
| commit | 94e1001173f10d33b5e8d1e32124e089d234a42c (patch) | |
| tree | 990967eddee7b810cfadb58627bbd75a61c92b81 | |
| parent | c47e0f22207302e4dcad9b8480e033fbe85dccd5 (diff) | |
| download | deck-it-94e1001173f10d33b5e8d1e32124e089d234a42c.tar.gz deck-it-94e1001173f10d33b5e8d1e32124e089d234a42c.zip | |
feat: long-press an OBS source to open its properties; fix server detection
| -rwxr-xr-x | deck-it | 9 | ||||
| -rw-r--r-- | deckit/modules/obs.py | 9 | ||||
| -rw-r--r-- | static/deck.css | 2 | ||||
| -rw-r--r-- | static/modules/obs.js | 35 | ||||
| -rw-r--r-- | tests/test_obs.py | 4 |
5 files changed, 48 insertions, 11 deletions
@@ -24,7 +24,7 @@ env_out=$(cd "$DIR" && python3 -m deckit env 2>&1) && eval "$env_out" || echo "C teardown() { pkill -f "^bash $SELF watch" 2>/dev/null pkill -x wayvnc - pkill -f '^python3 -m deckit' 2>/dev/null + pkill -f '[p]ython3 -m deckit' 2>/dev/null pkill -f "^[^ ]*swaybg -o $MON( |$)" 2>/dev/null hyprctl output remove "$MON" >/dev/null 2>&1 local s @@ -176,16 +176,17 @@ mkdir -p -m 700 "$RUN" vnc_setup # wayvnc leaves its socket behind when killed and then refuses to bind, so clear it first. pgrep -x wayvnc >/dev/null || { rm -f "$RUN/vnc.sock"; setsid wayvnc -C "$VNC/config" -u -o "$MON" "$RUN/vnc.sock" >/dev/null 2>&1 & } -pgrep -f '^python3 -m deckit' >/dev/null || (cd "$DIR" && setsid python3 -m deckit >server.log 2>&1 &) +pgrep -f '[p]ython3 -m deckit' >/dev/null || (cd "$DIR" && setsid python3 -m deckit >server.log 2>&1 &) sleep 1 tunnel "$ANDROID_SERIAL" || { echo "Could not set up the tunnel to the tablet"; exit 1; } # Opening the page with the secret stores it as a cookie in the tablet's browser, which unlocks the API. token="$CONF/token" for _ in {1..10}; do [[ -s "$token" ]] && break; sleep 0.5; done -adb shell am start -a android.intent.action.VIEW -d "http://127.0.0.1:$PORT/?t=$(cat "$token")" >/dev/null 2>&1 -sleep 1 open_avnc "$ANDROID_SERIAL" +sleep 1 +# The dashboard last, so it is the foreground app; AVNC is one switch away. +adb shell am start -a android.intent.action.VIEW -d "http://127.0.0.1:$PORT/?t=$(cat "$token")" >/dev/null 2>&1 if [[ "${1:-}" == wifi ]]; then echo "$ANDROID_SERIAL" > "$RUN/serial" diff --git a/deckit/modules/obs.py b/deckit/modules/obs.py index 04c49ef..07ec5d9 100644 --- a/deckit/modules/obs.py +++ b/deckit/modules/obs.py @@ -125,10 +125,17 @@ def make(cfg: dict) -> Module: "sceneItemId": int(body.get("id")), "sceneItemEnabled": bool(body.get("on"))}) + def properties(body): + name = str(body.get("source") or "") + if not name: + raise ModuleError("source required") + req("OpenInputPropertiesDialog", {"inputName": name}) + return Module( name="obs", title="OBS", state=state, actions={"scene": scene, "record": record, "stream": stream, "mute": mute, - "volume": volume, "transition": transition, "trigger": trigger, "source": source}, + "volume": volume, "transition": transition, "trigger": trigger, + "source": source, "properties": properties}, start=lambda: threading.Thread(target=connect_loop, daemon=True).start(), stop=lambda: (stop.set(), client.close()), ) diff --git a/static/deck.css b/static/deck.css index ee2be1f..6f3fa97 100644 --- a/static/deck.css +++ b/static/deck.css @@ -379,7 +379,9 @@ select { border: 1px solid var(--udt-surface2); color: var(--udt-subtext0); } +.tile { position: relative; } .tile .ico { width: 22px; height: 22px; flex: none; } +.tile .gear { position: absolute; right: 6px; top: 4px; font-style: normal; font-size: 11px; opacity: .6; } .tile span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .tile.on { background: var(--udt-accent); diff --git a/static/modules/obs.js b/static/modules/obs.js index 9430ee5..ae9e31b 100644 --- a/static/modules/obs.js +++ b/static/modules/obs.js @@ -52,14 +52,34 @@ function renderTransport(box, s) { box.append(rec, stream, trigger); } -function renderTiles(box, items, onPick) { +function renderTiles(box, items, onPick, onLong) { box.textContent = ""; for (const item of items) { const b = document.createElement("button"); b.className = `tile${item.on ? " on" : ""}`; - b.innerHTML = `${item.icon ? icon(item.icon) : ""}<span></span>`; + b.innerHTML = `${item.icon ? icon(item.icon) : ""}<span></span>${onLong ? '<i class="gear">⚙</i>' : ""}`; b.querySelector("span").textContent = item.label; - b.onclick = () => onPick(item); + let timer = null; + let longFired = false; + if (onLong) { + const host = () => b.closest(".tab-content"); + b.addEventListener("pointerdown", () => { + if (host()) host()._pressing = true; + longFired = false; + timer = setTimeout(() => { longFired = true; onLong(item); }, 600); + }); + const cancel = () => { + if (timer) { clearTimeout(timer); timer = null; } + if (host()) host()._pressing = false; + }; + b.addEventListener("pointerup", cancel); + b.addEventListener("pointercancel", cancel); + b.addEventListener("pointerleave", cancel); + } + b.onclick = () => { + if (longFired) { longFired = false; return; } + onPick(item); + }; box.append(b); } } @@ -129,9 +149,12 @@ export default { renderTiles(el.querySelector(".scenes"), (state.scenes ?? []).map((name) => ({ label: name, icon: "scene", on: name === state.currentScene })), (item) => post("scene", { scene: item.label })); - renderTiles(el.querySelector(".sources"), - (state.sources ?? []).map((src) => ({ label: src.name, icon: src.on ? "eye" : "eyeOff", on: src.on, id: src.id })), - (item) => post("source", { scene: state.currentScene, id: item.id, on: !item.on })); + if (!el._pressing) { + renderTiles(el.querySelector(".sources"), + (state.sources ?? []).map((src) => ({ label: src.name, icon: src.on ? "eye" : "eyeOff", on: src.on, id: src.id })), + (item) => post("source", { scene: state.currentScene, id: item.id, on: !item.on }), + (item) => post("properties", { source: item.label })); + } renderTiles(el.querySelector(".transitions"), (state.transitions ?? []).map((name) => ({ label: name, on: name === state.transition })), (item) => post("transition", { name: item.label })); diff --git a/tests/test_obs.py b/tests/test_obs.py index cd317ed..ecb7cc0 100644 --- a/tests/test_obs.py +++ b/tests/test_obs.py @@ -39,6 +39,10 @@ class Actions(unittest.TestCase): self.m.actions["source"]({"scene": "S", "id": 2, "on": False}) self.assertEqual(Fake.last[0], "SetSceneItemEnabled") + def test_properties_opens_the_source_dialog(self): + self.m.actions["properties"]({"source": "Single Window"}) + self.assertEqual(Fake.last, ("OpenInputPropertiesDialog", {"inputName": "Single Window"})) + def test_state_offline(self): class Off: connected = False |
