Add host-side per-session input policy controls
Some checks failed
Some checks failed
This commit is contained in:
parent
7e2ef2037c
commit
69c2693984
4 changed files with 211 additions and 1 deletions
|
|
@ -961,6 +961,67 @@ namespace confighttp {
|
|||
send_response(response, output_tree);
|
||||
}
|
||||
|
||||
void setActiveSessionPolicy(resp_https_t response, req_https_t request) {
|
||||
if (!check_content_type(response, request, "application/json")) {
|
||||
return;
|
||||
}
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << request->content.rdbuf();
|
||||
|
||||
try {
|
||||
nlohmann::json output_tree;
|
||||
const nlohmann::json input_tree = nlohmann::json::parse(ss);
|
||||
|
||||
if (!input_tree.contains("session_id") ||
|
||||
!input_tree.contains("allow_keyboard") ||
|
||||
!input_tree.contains("allow_mouse") ||
|
||||
!input_tree.contains("allow_gamepad")) {
|
||||
bad_request(response, request, "Missing required session policy fields");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t session_id = input_tree.at("session_id").get<uint32_t>();
|
||||
const bool allow_keyboard = input_tree.at("allow_keyboard").get<bool>();
|
||||
const bool allow_mouse = input_tree.at("allow_mouse").get<bool>();
|
||||
const bool allow_gamepad = input_tree.at("allow_gamepad").get<bool>();
|
||||
|
||||
bool effective_keyboard;
|
||||
bool effective_mouse;
|
||||
bool effective_gamepad;
|
||||
const bool updated = stream::set_active_session_input_policy(
|
||||
session_id,
|
||||
allow_keyboard,
|
||||
allow_mouse,
|
||||
allow_gamepad,
|
||||
&effective_keyboard,
|
||||
&effective_mouse,
|
||||
&effective_gamepad);
|
||||
|
||||
output_tree["status"] = updated;
|
||||
if (!updated) {
|
||||
output_tree["error"] = "Active session not found";
|
||||
} else {
|
||||
output_tree["session_id"] = session_id;
|
||||
output_tree["policy"] = {
|
||||
{"allow_keyboard", effective_keyboard},
|
||||
{"allow_mouse", effective_mouse},
|
||||
{"allow_gamepad", effective_gamepad},
|
||||
};
|
||||
}
|
||||
|
||||
send_response(response, output_tree);
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SetActiveSessionPolicy: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpair a client.
|
||||
* @param response The HTTP response object.
|
||||
|
|
@ -1612,6 +1673,7 @@ namespace confighttp {
|
|||
server.resource["^/api/clients/unpair$"]["POST"] = unpair;
|
||||
server.resource["^/api/sessions/active$"]["GET"] = getActiveSessions;
|
||||
server.resource["^/api/sessions/ws-token$"]["GET"] = getActiveSessionsWsToken;
|
||||
server.resource["^/api/sessions/policy$"]["POST"] = setActiveSessionPolicy;
|
||||
server.resource["^/api/apps/close$"]["POST"] = closeApp;
|
||||
server.resource["^/api/covers/upload$"]["POST"] = uploadCover;
|
||||
server.resource["^/api/covers/([0-9]+)$"]["GET"] = getCover;
|
||||
|
|
|
|||
|
|
@ -2079,6 +2079,55 @@ namespace stream {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool set_active_session_input_policy(
|
||||
uint32_t session_id,
|
||||
bool allow_keyboard,
|
||||
bool allow_mouse,
|
||||
bool allow_gamepad,
|
||||
bool *effective_allow_keyboard,
|
||||
bool *effective_allow_mouse,
|
||||
bool *effective_allow_gamepad) {
|
||||
auto ref = broadcast.ref();
|
||||
if (!ref) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lg = ref->control_server._sessions.lock();
|
||||
for (auto *session : *ref->control_server._sessions) {
|
||||
if (session->launch_session_id != session_id || session->state.load(std::memory_order_relaxed) != session::state_e::RUNNING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
apply_session_input_policy(session, allow_keyboard, allow_mouse, allow_gamepad, INPUT_POLICY_REASON_HOST_OVERRIDE);
|
||||
|
||||
if (send_session_input_policy(session, INPUT_POLICY_REASON_HOST_OVERRIDE)) {
|
||||
BOOST_LOG(warning) << "Unable to send host policy override to client"sv;
|
||||
}
|
||||
|
||||
auto final_allow_keyboard = session->input_policy.allow_keyboard.load(std::memory_order_relaxed);
|
||||
auto final_allow_mouse = session->input_policy.allow_mouse.load(std::memory_order_relaxed);
|
||||
auto final_allow_gamepad = session->input_policy.allow_gamepad.load(std::memory_order_relaxed);
|
||||
|
||||
if (effective_allow_keyboard) {
|
||||
*effective_allow_keyboard = final_allow_keyboard;
|
||||
}
|
||||
if (effective_allow_mouse) {
|
||||
*effective_allow_mouse = final_allow_mouse;
|
||||
}
|
||||
if (effective_allow_gamepad) {
|
||||
*effective_allow_gamepad = final_allow_gamepad;
|
||||
}
|
||||
|
||||
BOOST_LOG(info)
|
||||
<< "Host input policy override applied [session="sv << session_id << "] kb="sv << final_allow_keyboard
|
||||
<< " mouse="sv << final_allow_mouse << " gamepad="sv << final_allow_gamepad;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace session {
|
||||
std::atomic_uint running_sessions;
|
||||
|
||||
|
|
|
|||
10
src/stream.h
10
src/stream.h
|
|
@ -5,6 +5,7 @@
|
|||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
|
@ -31,6 +32,15 @@ namespace stream {
|
|||
|
||||
nlohmann::json get_active_sessions_info();
|
||||
|
||||
bool set_active_session_input_policy(
|
||||
uint32_t session_id,
|
||||
bool allow_keyboard,
|
||||
bool allow_mouse,
|
||||
bool allow_gamepad,
|
||||
bool *effective_allow_keyboard = nullptr,
|
||||
bool *effective_allow_mouse = nullptr,
|
||||
bool *effective_allow_gamepad = nullptr);
|
||||
|
||||
struct config_t {
|
||||
audio::config_t audio;
|
||||
video::config_t monitor;
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue