hid with granularity prototype

This commit is contained in:
Maxim Devaev 2024-11-03 18:28:28 +02:00
parent 1e277c0f06
commit d93639ba8d
9 changed files with 164 additions and 90 deletions

View File

@ -189,7 +189,11 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
self.__shared_params.name = name self.__shared_params.name = name
elif event_type == "hid_state": elif event_type == "hid_state":
if self._encodings.has_leds_state: if (
self._encodings.has_leds_state
and ("keyboard" in event)
and ("leds" in event["keyboard"])
):
await self._send_leds_state(**event["keyboard"]["leds"]) await self._send_leds_state(**event["keyboard"]["leds"])
# ===== # =====

View File

@ -101,6 +101,19 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
raise NotImplementedError raise NotImplementedError
async def poll_state(self) -> AsyncGenerator[dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
# ==== Granularity table ====
# - enabled -- Full
# - online -- Partial
# - busy -- Partial
# - connected -- Partial, nullable
# - keyboard.online -- Partial
# - keyboard.outputs -- Partial
# - keyboard.leds -- Partial
# - mouse.online -- Partial
# - mouse.outputs -- Partial, follows with absolute
# - mouse.absolute -- Partial, follows with outputs
# ===========================
yield {} yield {}
raise NotImplementedError raise NotImplementedError

View File

@ -217,6 +217,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
mouse_outputs["active"] = active_mouse mouse_outputs["active"] = active_mouse
return { return {
"enabled": True,
"online": online, "online": online,
"busy": bool(state["busy"]), "busy": bool(state["busy"]),
"connected": (bool(outputs2 & 0b01000000) if outputs2 & 0b10000000 else None), "connected": (bool(outputs2 & 0b01000000) if outputs2 & 0b10000000 else None),

View File

@ -141,6 +141,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
state = await self.__server.get_state() state = await self.__server.get_state()
outputs: dict = {"available": [], "active": ""} outputs: dict = {"available": [], "active": ""}
return { return {
"enabled": True,
"online": True, "online": True,
"busy": False, "busy": False,
"connected": None, "connected": None,

View File

@ -104,6 +104,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst
absolute = self.__mouse.is_absolute() absolute = self.__mouse.is_absolute()
leds = await self.__keyboard.get_leds() leds = await self.__keyboard.get_leds()
return { return {
"enabled": True,
"online": state["online"], "online": state["online"],
"busy": False, "busy": False,
"connected": None, "connected": None,

View File

@ -134,6 +134,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
keyboard_state = await self.__keyboard_proc.get_state() keyboard_state = await self.__keyboard_proc.get_state()
mouse_state = await self.__mouse_current.get_state() mouse_state = await self.__mouse_current.get_state()
return { return {
"enabled": True,
"online": True, "online": True,
"busy": False, "busy": False,
"connected": None, "connected": None,

View File

@ -256,23 +256,21 @@
<button class="row33" data-force-hide-menu id="stream-screenshot-button">&bull; Screenshot</button> <button class="row33" data-force-hide-menu id="stream-screenshot-button">&bull; Screenshot</button>
<button class="row33" id="stream-reset-button">Reset stream</button> <button class="row33" id="stream-reset-button">Reset stream</button>
</div> </div>
<div class="feature-disabled" id="hid-outputs"> <hr>
<hr> <table class="kv">
<table class="kv"> <tr class="feature-disabled" id="hid-outputs-keyboard">
<tr class="feature-disabled" id="hid-outputs-keyboard"> <td>Keyboard mode:</td>
<td>Keyboard mode:</td> <td>
<td> <div class="radio-box" id="hid-outputs-keyboard-box"></div>
<div class="radio-box" id="hid-outputs-keyboard-box"></div> </td>
</td> </tr>
</tr> <tr class="feature-disabled" id="hid-outputs-mouse">
<tr class="feature-disabled" id="hid-outputs-mouse"> <td>Mouse <a target="_blank" href="https://docs.pikvm.org/mouse">mode</a>:</td>
<td>Mouse <a target="_blank" href="https://docs.pikvm.org/mouse">mode</a>:</td> <td>
<td> <div class="radio-box" id="hid-outputs-mouse-box"></div>
<div class="radio-box" id="hid-outputs-mouse-box"></div> </td>
</td> </tr>
</tr> </table>
</table>
</div>
<details> <details>
<summary>Keyboard &amp; Mouse (HID) settings</summary> <summary>Keyboard &amp; Mouse (HID) settings</summary>
<div class="spoiler"> <div class="spoiler">

View File

@ -71,15 +71,14 @@ li(id="system-dropdown" class="right")
button(data-force-hide-menu data-show-window="stream-window" class="row33") &bull; Show stream button(data-force-hide-menu data-show-window="stream-window" class="row33") &bull; Show stream
button(data-force-hide-menu id="stream-screenshot-button" class="row33") &bull; Screenshot button(data-force-hide-menu id="stream-screenshot-button" class="row33") &bull; Screenshot
button(id="stream-reset-button" class="row33") Reset stream button(id="stream-reset-button" class="row33") Reset stream
div(id="hid-outputs" class="feature-disabled") hr
hr table(class="kv")
table(class="kv") tr(id="hid-outputs-keyboard", class="feature-disabled")
tr(id="hid-outputs-keyboard", class="feature-disabled") td Keyboard mode:
td Keyboard mode: td #[div(id="hid-outputs-keyboard-box" class="radio-box")]
td #[div(id="hid-outputs-keyboard-box" class="radio-box")] tr(id="hid-outputs-mouse", class="feature-disabled")
tr(id="hid-outputs-mouse", class="feature-disabled") td Mouse #[a(target="_blank" href="https://docs.pikvm.org/mouse") mode]:
td Mouse #[a(target="_blank" href="https://docs.pikvm.org/mouse") mode]: td #[div(id="hid-outputs-mouse-box" class="radio-box")]
td #[div(id="hid-outputs-mouse-box" class="radio-box")]
details details
summary Keyboard &amp; Mouse (HID) settings summary Keyboard &amp; Mouse (HID) settings
div(class="spoiler") div(class="spoiler")

View File

@ -35,6 +35,7 @@ export function Hid(__getGeometry, __recorder) {
/************************************************************************/ /************************************************************************/
var __state = null;
var __keyboard = null; var __keyboard = null;
var __mouse = null; var __mouse = null;
@ -102,8 +103,6 @@ export function Hid(__getGeometry, __recorder) {
/************************************************************************/ /************************************************************************/
self.setSocket = function(ws) { self.setSocket = function(ws) {
tools.el.setEnabled($("hid-reset-button"), ws);
tools.el.setEnabled($("hid-jiggler-switch"), ws);
if (!ws) { if (!ws) {
self.setState(null); self.setState(null);
} }
@ -112,78 +111,135 @@ export function Hid(__getGeometry, __recorder) {
}; };
self.setState = function(state) { self.setState = function(state) {
let has_relative_squash = false;
if (state) { if (state) {
tools.feature.setEnabled($("hid-jiggler"), state.jiggler.enabled); if (!__state) {
$("hid-jiggler-switch").checked = state.jiggler.active; __state = {"keyboard": {}, "mouse": {}};
}
if (state.enabled !== undefined) {
__state.enabled = state.enabled; // Currently unused, always true
}
if (__state.enabled !== undefined) {
for (let key of ["online", "busy", "connected", "jiggler"]) {
if (state[key] !== undefined) {
__state[key] = state[key];
}
}
for (let hid of ["keyboard", "mouse"]) {
if (state[hid] === undefined) {
state[hid] = {}; // Add some stubs for processing
}
for (let key of ["online", "outputs", (hid === "keyboard" ? "leds" : "absolute")]) {
__state[hid][key] = state[hid][key];
}
}
if (state.connected !== undefined) {
tools.feature.setEnabled($("hid-connect"), (__state.connected !== null));
$("hid-connect-switch").checked = !!__state.connected;
}
if (state.jiggler !== undefined) {
tools.feature.setEnabled($("hid-jiggler"), __state.jiggler.enabled);
$("hid-jiggler-switch").checked = __state.jiggler.active;
}
if (state.keyboard.outputs !== undefined) {
__updateKeyboardOutputs(__state.keyboard.outputs);
}
if (state.mouse.outputs !== undefined) {
__updateMouseOutputs(__state.mouse.outputs, __state.mouse.absolute); // Follows together
}
if (
state.keyboard.online !== undefined || state.keyboard.leds !== undefined
|| state.online !== undefined || state.busy !== undefined
) {
__keyboard.setState(__state.keyboard.online, __state.keyboard.leds, __state.online, __state.busy);
}
if (
state.mouse.online !== undefined || state.mouse.absolute !== undefined
|| state.online !== undefined || state.busy !== undefined
) {
__mouse.setState(__state.mouse.online, __state.mouse.absolute, __state.online, __state.busy);
}
if (state.online !== undefined || state.busy !== undefined) {
tools.radio.setEnabled("hid-outputs-keyboard-radio", (__state.online && !__state.busy));
tools.radio.setEnabled("hid-outputs-mouse-radio", (__state.online && !__state.busy));
tools.el.setEnabled($("hid-connect-switch"), (__state.online && !__state.busy));
}
}
} else {
__state = null;
tools.radio.setEnabled("hid-outputs-keyboard-radio", false);
tools.radio.setEnabled("hid-outputs-mouse-radio", false);
tools.el.setEnabled($("hid-connect-switch"), false);
tools.el.setEnabled($("hid-mouse-squash-switch"), false);
tools.el.setEnabled($("hid-mouse-sens-slider"), false);
} }
if (state && state.online) { tools.el.setEnabled($("hid-reset-button"), __state);
let keyboard_outputs = state.keyboard.outputs.available; tools.el.setEnabled($("hid-jiggler-switch"), __state);
let mouse_outputs = state.mouse.outputs.available; };
if (keyboard_outputs.length) {
if ($("hid-outputs-keyboard-box").outputs !== keyboard_outputs) { var __updateKeyboardOutputs = function(outputs) {
let html = ""; let avail = outputs.available;
for (let args of [ if (avail.length > 0) {
["USB", "usb"], let el = $("hid-outputs-keyboard-box");
["PS/2", "ps2"], let avail_json = JSON.stringify(avail);
["Off", "disabled"], if (el.__avail_json !== avail_json) {
]) { let html = "";
if (keyboard_outputs.includes(args[1])) { for (let pair of [
html += tools.radio.makeItem("hid-outputs-keyboard-radio", args[0], args[1]); ["USB", "usb"],
} ["PS/2", "ps2"],
["Off", "disabled"],
]) {
if (avail.includes(pair[1])) {
html += tools.radio.makeItem("hid-outputs-keyboard-radio", pair[0], pair[1]);
} }
$("hid-outputs-keyboard-box").innerHTML = html;
$("hid-outputs-keyboard-box").outputs = keyboard_outputs;
tools.radio.setOnClick("hid-outputs-keyboard-radio", () => __clickOutputsRadio("keyboard"));
} }
tools.radio.setValue("hid-outputs-keyboard-radio", state.keyboard.outputs.active); el.innerHTML = html;
tools.radio.setOnClick("hid-outputs-keyboard-radio", () => __clickOutputsRadio("keyboard"));
el.__avail_json = avail_json;
} }
let has_relative = false; tools.radio.setValue("hid-outputs-keyboard-radio", outputs.active);
if (mouse_outputs.length) { }
if ($("hid-outputs-mouse-box").outputs !== mouse_outputs) { tools.feature.setEnabled($("hid-outputs-keyboard"), (avail.length > 0));
let html = ""; };
for (let args of [
["Absolute", "usb", false], var __updateMouseOutputs = function(outputs, absolute) {
["Abs-Win98", "usb_win98", false], let has_relative = null;
["Relative", "usb_rel", true], let has_relative_squash = null;
["PS/2", "ps2", true], let avail = outputs.available;
["Off", "disabled"], if (avail.length > 0) {
]) { let el = $("hid-outputs-mouse-box");
if (mouse_outputs.includes(args[1])) { let avail_json = JSON.stringify(avail);
html += tools.radio.makeItem("hid-outputs-mouse-radio", args[0], args[1]); if (el.__avail_json !== avail_json) {
has_relative = (has_relative || args[2]); has_relative = false;
} let html = "";
for (let pair of [
["Absolute", "usb", false],
["Abs-Win98", "usb_win98", false],
["Relative", "usb_rel", true],
["PS/2", "ps2", true],
["Off", "disabled", false],
]) {
if (avail.includes(pair[1])) {
html += tools.radio.makeItem("hid-outputs-mouse-radio", pair[0], pair[1]);
has_relative = (has_relative || pair[2]);
} }
$("hid-outputs-mouse-box").innerHTML = html;
$("hid-outputs-mouse-box").outputs = mouse_outputs;
tools.radio.setOnClick("hid-outputs-mouse-radio", () => __clickOutputsRadio("mouse"));
} }
tools.radio.setValue("hid-outputs-mouse-radio", state.mouse.outputs.active); el.innerHTML = html;
has_relative_squash = ["usb_rel", "ps2"].includes(state.mouse.outputs.active); tools.radio.setOnClick("hid-outputs-mouse-radio", () => __clickOutputsRadio("mouse"));
} else { el.__avail_json = avail_json;
has_relative = !state.mouse.absolute;
has_relative_squash = has_relative;
} }
tools.feature.setEnabled($("hid-outputs"), (keyboard_outputs.length || mouse_outputs.length)); tools.radio.setValue("hid-outputs-mouse-radio", outputs.active);
tools.feature.setEnabled($("hid-outputs-keyboard"), keyboard_outputs.length); has_relative_squash = (["usb_rel", "ps2"].includes(outputs.active));
tools.feature.setEnabled($("hid-outputs-mouse"), mouse_outputs.length); } else {
has_relative = !absolute;
has_relative_squash = has_relative;
}
if (has_relative !== null) {
tools.feature.setEnabled($("hid-mouse-squash"), has_relative); tools.feature.setEnabled($("hid-mouse-squash"), has_relative);
tools.feature.setEnabled($("hid-mouse-sens"), has_relative); tools.feature.setEnabled($("hid-mouse-sens"), has_relative);
tools.feature.setEnabled($("hid-connect"), (state.connected !== null));
$("hid-connect-switch").checked = !!state.connected;
}
tools.radio.setEnabled("hid-outputs-keyboard-radio", (state && state.online && !state.busy));
tools.radio.setEnabled("hid-outputs-mouse-radio", (state && state.online && !state.busy));
tools.el.setEnabled($("hid-mouse-squash-switch"), (has_relative_squash && !state.busy));
tools.el.setEnabled($("hid-mouse-sens-slider"), (has_relative_squash && !state.busy));
tools.el.setEnabled($("hid-connect-switch"), (state && state.online && !state.busy));
if (state) {
__keyboard.setState(state.keyboard.online, state.keyboard.leds, state.online, state.busy);
__mouse.setState(state.mouse.online, state.mouse.absolute, state.online, state.busy);
} }
tools.feature.setEnabled($("hid-outputs-mouse"), (avail.length > 0));
tools.el.setEnabled($("hid-mouse-squash-switch"), has_relative_squash);
tools.el.setEnabled($("hid-mouse-sens-slider"), has_relative_squash);
}; };
var __releaseAll = function() { var __releaseAll = function() {