diff options
author | Maxim Devaev <[email protected]> | 2024-12-30 18:55:59 +0200 |
---|---|---|
committer | Maxim Devaev <[email protected]> | 2024-12-30 18:55:59 +0200 |
commit | fed3bf1efdea7528919eb9f318c5ada2dcf35410 (patch) | |
tree | f1bd27d882b85cf845b92b489b1c494c4d1dd715 | |
parent | d52bb34bb9f5bc669f6b915ba585461356128e39 (diff) |
pikvm/pikvm#1334: Bad link mode for keyboard events
-rw-r--r-- | kvmd/apps/kvmd/api/hid.py | 17 | ||||
-rw-r--r-- | kvmd/clients/kvmd.py | 6 | ||||
-rw-r--r-- | kvmd/keyboard/mappings.py | 2 | ||||
-rw-r--r-- | kvmd/plugins/hid/__init__.py | 9 | ||||
-rw-r--r-- | web/kvm/index.html | 13 | ||||
-rw-r--r-- | web/kvm/navbar-system.pug | 6 | ||||
-rw-r--r-- | web/share/js/kvm/keyboard.js | 8 | ||||
-rw-r--r-- | web/share/js/kvm/recorder.js | 6 | ||||
-rw-r--r-- | web/share/js/kvm/session.js | 3 |
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 & Mouse (HID) settings</summary> + <summary>Keyboard & 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 & Mouse (HID) settings + summary Keyboard & 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") { |