Add host-side per-session input policy controls
Some checks failed
ci-bundle.yml / Add host-side per-session input policy controls (push) Failing after 0s
ci-copr.yml / Add host-side per-session input policy controls (push) Failing after 0s
ci-homebrew.yml / Add host-side per-session input policy controls (push) Failing after 0s

This commit is contained in:
Joey Yakimowich-Payne 2026-02-12 08:43:39 -07:00
commit 69c2693984
4 changed files with 211 additions and 1 deletions

View file

@ -151,7 +151,7 @@
<div class="card my-4">
<div class="card-body">
<h2 id="input_status">Live Input Status</h2>
<p>Per-session input activity and policy. Indicators update twice per second.</p>
<p>Per-session input activity and policy. Use the checkboxes to override keyboard, mouse, and gamepad permissions for each active client.</p>
</div>
<div v-if="activeSessions.length === 0" class="card-body pt-0">
<em>No active streaming sessions.</em>
@ -187,6 +187,41 @@
</span>
</div>
</div>
<div class="d-flex align-items-center flex-wrap gap-3 mt-2">
<span class="text-muted" style="font-size: 0.8em">Host override:</span>
<label class="d-flex align-items-center gap-1" style="font-size: 0.8em">
<input
class="form-check-input mt-0"
type="checkbox"
:checked="s.policy.allow_keyboard"
:disabled="isSessionPolicyBusy(s.session_id)"
@change="updateSessionPolicy(s, 'allow_keyboard', $event.target.checked)"
>
KB
</label>
<label class="d-flex align-items-center gap-1" style="font-size: 0.8em">
<input
class="form-check-input mt-0"
type="checkbox"
:checked="s.policy.allow_mouse"
:disabled="isSessionPolicyBusy(s.session_id)"
@change="updateSessionPolicy(s, 'allow_mouse', $event.target.checked)"
>
Mouse
</label>
<label class="d-flex align-items-center gap-1" style="font-size: 0.8em">
<input
class="form-check-input mt-0"
type="checkbox"
:checked="s.policy.allow_gamepad"
:disabled="isSessionPolicyBusy(s.session_id)"
@change="updateSessionPolicy(s, 'allow_gamepad', $event.target.checked)"
>
Gamepad
</label>
<span v-if="isSessionPolicyBusy(s.session_id)" class="text-muted" style="font-size: 0.8em">Saving...</span>
<span v-else-if="sessionPolicyError[s.session_id]" class="text-danger" style="font-size: 0.8em">{{ sessionPolicyError[s.session_id] }}</span>
</div>
</li>
</ul>
</div>
@ -284,6 +319,8 @@
sessionInterval: null,
sessionSocket: null,
sessionReconnectTimer: null,
sessionPolicyBusy: {},
sessionPolicyError: {},
restartPressed: false,
showApplyMessage: false,
platform: "",
@ -455,6 +492,58 @@
}
},
methods: {
isSessionPolicyBusy(sessionId) {
return this.sessionPolicyBusy[sessionId] === true;
},
updateSessionPolicy(session, field, value) {
if (!session || !session.policy || session.session_id === undefined || session.session_id === null) {
return;
}
if (!["allow_keyboard", "allow_mouse", "allow_gamepad"].includes(field)) {
return;
}
const sessionId = session.session_id;
const nextPolicy = {
allow_keyboard: field === "allow_keyboard" ? value : !!session.policy.allow_keyboard,
allow_mouse: field === "allow_mouse" ? value : !!session.policy.allow_mouse,
allow_gamepad: field === "allow_gamepad" ? value : !!session.policy.allow_gamepad,
};
this.sessionPolicyBusy[sessionId] = true;
this.sessionPolicyError[sessionId] = "";
fetch("./api/sessions/policy", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
session_id: sessionId,
allow_keyboard: nextPolicy.allow_keyboard,
allow_mouse: nextPolicy.allow_mouse,
allow_gamepad: nextPolicy.allow_gamepad,
})
})
.then((r) => r.json())
.then((r) => {
if (!r || r.status !== true || !r.policy) {
throw new Error((r && r.error) ? r.error : "Failed to update session input policy");
}
session.policy.allow_keyboard = !!r.policy.allow_keyboard;
session.policy.allow_mouse = !!r.policy.allow_mouse;
session.policy.allow_gamepad = !!r.policy.allow_gamepad;
})
.catch((e) => {
this.sessionPolicyError[sessionId] = (e && e.message) ? e.message : "Failed to update session input policy";
this.refreshActiveSessions();
})
.finally(() => {
this.sessionPolicyBusy[sessionId] = false;
});
},
connectActiveSessionsSocket() {
fetch("./api/sessions/ws-token")
.then((r) => r.json())