summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Devaev <[email protected]>2024-12-30 18:55:59 +0200
committerMaxim Devaev <[email protected]>2024-12-30 18:55:59 +0200
commitfed3bf1efdea7528919eb9f318c5ada2dcf35410 (patch)
treef1bd27d882b85cf845b92b489b1c494c4d1dd715
parentd52bb34bb9f5bc669f6b915ba585461356128e39 (diff)
pikvm/pikvm#1334: Bad link mode for keyboard events
-rw-r--r--kvmd/apps/kvmd/api/hid.py17
-rw-r--r--kvmd/clients/kvmd.py6
-rw-r--r--kvmd/keyboard/mappings.py2
-rw-r--r--kvmd/plugins/hid/__init__.py9
-rw-r--r--web/kvm/index.html13
-rw-r--r--web/kvm/navbar-system.pug6
-rw-r--r--web/share/js/kvm/keyboard.js8
-rw-r--r--web/share/js/kvm/recorder.js6
-rw-r--r--web/share/js/kvm/session.js3
9 files changed, 53 insertions, 17 deletions
diff --git a/kvmd/apps/kvmd/api/hid.py b/kvmd/apps/kvmd/api/hid.py
index 5ba22abb..98b96313 100644
--- a/kvmd/apps/kvmd/api/hid.py
+++ b/kvmd/apps/kvmd/api/hid.py
@@ -149,16 +149,17 @@ class HidApi:
async def __ws_bin_key_handler(self, _: WsSession, data: bytes) -> None:
try:
key = valid_hid_key(data[1:].decode("ascii"))
- state = valid_bool(data[0])
+ state = bool(data[0] & 0b01)
+ finish = bool(data[0] & 0b10)
except Exception:
return
- self.__hid.send_key_event(key, state)
+ self.__hid.send_key_event(key, state, finish)
@exposed_ws(2)
async def __ws_bin_mouse_button_handler(self, _: WsSession, data: bytes) -> None:
try:
button = valid_hid_mouse_button(data[1:].decode("ascii"))
- state = valid_bool(data[0])
+ state = bool(data[0] & 0b01)
except Exception:
return
self.__hid.send_mouse_button_event(button, state)
@@ -183,7 +184,7 @@ class HidApi:
def __process_ws_bin_delta_request(self, data: bytes, handler: Callable[[Iterable[tuple[int, int]], bool], None]) -> None:
try:
- squash = valid_bool(data[0])
+ squash = bool(data[0] & 0b01)
data = data[1:]
deltas: list[tuple[int, int]] = []
for index in range(0, len(data), 2):
@@ -200,9 +201,10 @@ class HidApi:
try:
key = valid_hid_key(event["key"])
state = valid_bool(event["state"])
+ finish = valid_bool(event.get("finish", False))
except Exception:
return
- self.__hid.send_key_event(key, state)
+ self.__hid.send_key_event(key, state, finish)
@exposed_ws("mouse_button")
async def __ws_mouse_button_handler(self, _: WsSession, event: dict) -> None:
@@ -249,9 +251,10 @@ class HidApi:
key = valid_hid_key(req.query.get("key"))
if "state" in req.query:
state = valid_bool(req.query["state"])
- self.__hid.send_key_event(key, state)
+ finish = valid_bool(req.query.get("finish", False))
+ self.__hid.send_key_event(key, state, finish)
else:
- await self.__hid.send_key_events([(key, True), (key, False)], slow=True)
+ self.__hid.send_key_event(key, True, True)
return make_json_response()
@exposed_http("POST", "/hid/events/send_mouse_button")
diff --git a/kvmd/clients/kvmd.py b/kvmd/clients/kvmd.py
index a0412ce2..d9b38339 100644
--- a/kvmd/clients/kvmd.py
+++ b/kvmd/clients/kvmd.py
@@ -183,10 +183,12 @@ class KvmdClientWs:
self.__communicated = False
async def send_key_event(self, key: str, state: bool) -> None:
- await self.__writer_queue.put(bytes([1, state]) + key.encode("ascii"))
+ mask = (0b01 if state else 0)
+ await self.__writer_queue.put(bytes([1, mask]) + key.encode("ascii"))
async def send_mouse_button_event(self, button: str, state: bool) -> None:
- await self.__writer_queue.put(bytes([2, state]) + button.encode("ascii"))
+ mask = (0b01 if state else 0)
+ await self.__writer_queue.put(bytes([2, mask]) + button.encode("ascii"))
async def send_mouse_move_event(self, to_x: int, to_y: int) -> None:
await self.__writer_queue.put(struct.pack(">bhh", 3, to_x, to_y))
diff --git a/kvmd/keyboard/mappings.py b/kvmd/keyboard/mappings.py
index 8e7d87d1..bfefa712 100644
--- a/kvmd/keyboard/mappings.py
+++ b/kvmd/keyboard/mappings.py
@@ -170,6 +170,8 @@ class WebModifiers:
CTRL_RIGHT = "ControlRight"
CTRLS = set([CTRL_RIGHT, CTRL_RIGHT])
+ ALL = (SHIFTS | ALTS | CTRLS)
+
class X11Modifiers:
SHIFT_LEFT = 65505
diff --git a/kvmd/plugins/hid/__init__.py b/kvmd/plugins/hid/__init__.py
index 447b4d07..5063f53e 100644
--- a/kvmd/plugins/hid/__init__.py
+++ b/kvmd/plugins/hid/__init__.py
@@ -37,6 +37,7 @@ from ...validators.basic import valid_string_list
from ...validators.hid import valid_hid_key
from ...validators.hid import valid_hid_mouse_move
+from ...keyboard.mappings import WebModifiers
from ...mouse import MouseRange
from .. import BasePlugin
@@ -148,10 +149,14 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
if no_ignore_keys or key not in self.__ignore_keys:
if slow:
await asyncio.sleep(0.02)
- self.send_key_event(key, state)
+ self.send_key_event(key, state, False)
- def send_key_event(self, key: str, state: bool) -> None:
+ def send_key_event(self, key: str, state: bool, finish: bool) -> None:
self._send_key_event(key, state)
+ if state and finish and (key not in WebModifiers.ALL and key != "PrintScreen"):
+ # Считаем что PrintScreen это модификатор для Alt+SysRq+...
+ # По-хорошему надо учитывать факт нажатия на Alt, но можно и забить.
+ self._send_key_event(key, False)
self.__bump_activity()
def _send_key_event(self, key: str, state: bool) -> None:
diff --git a/web/kvm/index.html b/web/kvm/index.html
index 1db915ee..c6d3301d 100644
--- a/web/kvm/index.html
+++ b/web/kvm/index.html
@@ -285,7 +285,7 @@
</tr>
</table>
<details>
- <summary>Keyboard &amp; Mouse (HID) settings</summary>
+ <summary>Keyboard &amp; mouse (HID) settings</summary>
<div class="spoiler">
<table class="kv">
<tr>
@@ -401,6 +401,15 @@
</div>
</details>
<table class="kv">
+ <tr>
+ <td>Bad link mode (release keys immediately):</td>
+ <td align="right">
+ <div class="switch-box">
+ <input type="checkbox" id="hid-keyboard-bad-link-switch">
+ <label for="hid-keyboard-bad-link-switch"><span class="switch-inner"></span><span class="switch"></span></label>
+ </div>
+ </td>
+ </tr>
<tr class="feature-disabled" id="hid-connect">
<td>Connect HID to Server:</td>
<td align="right">
@@ -420,7 +429,7 @@
</td>
</tr>
<tr>
- <td>Mute HID input events:</td>
+ <td>Mute all input HID events:</td>
<td align="right">
<div class="switch-box">
<input type="checkbox" id="hid-mute-switch">
diff --git a/web/kvm/navbar-system.pug b/web/kvm/navbar-system.pug
index a3438486..55d70757 100644
--- a/web/kvm/navbar-system.pug
+++ b/web/kvm/navbar-system.pug
@@ -85,7 +85,7 @@ li(id="system-dropdown" class="right")
td Mouse #[a(target="_blank" href="https://docs.pikvm.org/mouse") mode]:
td #[div(id="hid-outputs-mouse-box" class="radio-box")]
details
- summary Keyboard &amp; Mouse (HID) settings
+ summary Keyboard &amp; mouse (HID) settings
div(class="spoiler")
table(class="kv")
tr
@@ -127,12 +127,14 @@ li(id="system-dropdown" class="right")
tr
+menu_switch_notable("page-full-tab-stream-switch", "Expand for the entire tab by default", true, false)
table(class="kv")
+ tr
+ +menu_switch_notable("hid-keyboard-bad-link-switch", "Bad link mode (release keys immediately)", true, false)
tr(id="hid-connect" class="feature-disabled")
+menu_switch_notable("hid-connect-switch", "Connect HID to Server", true, true)
tr(id="hid-jiggler" class="feature-disabled")
+menu_switch_notable("hid-jiggler-switch", "<a href=\"https://docs.pikvm.org/mouse_jiggler\" target=\"_blank\">Mouse jiggler</a>", false, false)
tr
- +menu_switch_notable("hid-mute-switch", "Mute HID input events", true, false)
+ +menu_switch_notable("hid-mute-switch", "Mute all input HID events", true, false)
tr(id="v3-usb-breaker" class="feature-disabled")
+menu_switch_notable_gpio("__v3_usb_breaker__", "Connect main USB to Server",
"Turning off this switch will disconnect the main USB from the server. Are you sure you want to continue?")
diff --git a/web/share/js/kvm/keyboard.js b/web/share/js/kvm/keyboard.js
index 2377d07a..f30dbfe9 100644
--- a/web/share/js/kvm/keyboard.js
+++ b/web/share/js/kvm/keyboard.js
@@ -52,6 +52,7 @@ export function Keyboard(__recordWsEvent) {
window.addEventListener("focusin", __updateOnlineLeds);
window.addEventListener("focusout", __updateOnlineLeds);
+ tools.storage.bindSimpleSwitch($("hid-keyboard-bad-link-switch"), "hid.keyboard.bad_link", false);
tools.storage.bindSimpleSwitch($("hid-keyboard-swap-cc-switch"), "hid.keyboard.swap_cc", false);
};
@@ -140,11 +141,16 @@ export function Keyboard(__recordWsEvent) {
}
let event = {
"event_type": "key",
- "event": {"key": code, "state": state},
+ "event": {
+ "key": code,
+ "state": state,
+ "finish": $("hid-keyboard-bad-link-switch").checked,
+ },
};
if (__ws && !$("hid-mute-switch").checked) {
__ws.sendHidEvent(event);
}
+ delete event.event.finish;
__recordWsEvent(event);
};
diff --git a/web/share/js/kvm/recorder.js b/web/share/js/kvm/recorder.js
index 5d9d1553..cf792bc2 100644
--- a/web/share/js/kvm/recorder.js
+++ b/web/share/js/kvm/recorder.js
@@ -336,7 +336,11 @@ export function Recorder() {
});
return;
- } else if (["key", "mouse_button", "mouse_move", "mouse_wheel", "mouse_relative"].includes(event.event_type)) {
+ } else if (event.event_type === "key") {
+ event.event.finish = $("hid-keyboard-bad-link-switch").checked;
+ __ws.sendHidEvent(event);
+
+ } else if (["mouse_button", "mouse_move", "mouse_wheel", "mouse_relative"].includes(event.event_type)) {
__ws.sendHidEvent(event);
} else if (event.event_type === "mouse_move_random") {
diff --git a/web/share/js/kvm/session.js b/web/share/js/kvm/session.js
index c9839aca..38508f18 100644
--- a/web/share/js/kvm/session.js
+++ b/web/share/js/kvm/session.js
@@ -316,6 +316,9 @@ export function Session() {
if (event_type == "key") {
let data = __ascii_encoder.encode("\x01\x00" + event.key);
data[1] = (event.state ? 1 : 0);
+ if (event.finish === true) { // Optional
+ data[1] |= 0x02;
+ }
ws.send(data);
} else if (event_type == "mouse_button") {