diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index 9d6d494a..cfc39499 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -667,9 +667,10 @@ def _get_config_scheme() -> dict: }, "vnc": { - "desired_fps": Option(30, type=valid_stream_fps), - "mouse_output": Option("usb", type=valid_hid_mouse_output), - "keymap": Option("/usr/share/kvmd/keymaps/en-us", type=valid_abs_file), + "desired_fps": Option(30, type=valid_stream_fps), + "mouse_output": Option("usb", type=valid_hid_mouse_output), + "keymap": Option("/usr/share/kvmd/keymaps/en-us", type=valid_abs_file), + "allow_cut_after": Option(3.0, type=valid_float_f0), "server": { "host": Option("", type=valid_ip_or_host, if_empty=""), diff --git a/kvmd/apps/vnc/__init__.py b/kvmd/apps/vnc/__init__.py index f41989d1..1e2c486a 100644 --- a/kvmd/apps/vnc/__init__.py +++ b/kvmd/apps/vnc/__init__.py @@ -71,6 +71,7 @@ def make_memsink_streamer(name: str, fmt: int) -> (MemsinkStreamerClient | None) desired_fps=config.desired_fps, mouse_output=config.mouse_output, keymap_path=config.keymap, + allow_cut_after=config.allow_cut_after, kvmd=KvmdClient(user_agent=user_agent, **config.kvmd._unpack()), streamers=streamers, diff --git a/kvmd/apps/vnc/rfb/__init__.py b/kvmd/apps/vnc/rfb/__init__.py index c145b4b3..d2c6b50b 100644 --- a/kvmd/apps/vnc/rfb/__init__.py +++ b/kvmd/apps/vnc/rfb/__init__.py @@ -22,6 +22,7 @@ import asyncio import ssl +import time from typing import Callable from typing import Coroutine @@ -64,6 +65,7 @@ def __init__( # pylint: disable=too-many-arguments width: int, height: int, name: str, + allow_cut_after: float, vnc_passwds: list[str], vencrypt: bool, none_auth_only: bool, @@ -79,6 +81,7 @@ def __init__( # pylint: disable=too-many-arguments self._width = width self._height = height self.__name = name + self.__allow_cut_after = allow_cut_after self.__vnc_passwds = vnc_passwds self.__vencrypt = vencrypt self.__none_auth_only = none_auth_only @@ -90,6 +93,8 @@ def __init__( # pylint: disable=too-many-arguments self.__fb_cont_updates = False self.__fb_reset_h264 = False + self.__allow_cut_since_ts = 0.0 + self.__lock = asyncio.Lock() # ===== @@ -414,6 +419,7 @@ async def __handshake_init(self) -> None: # ===== async def __main_loop(self) -> None: + self.__allow_cut_since_ts = time.monotonic() + self.__allow_cut_after handlers = { 0: self.__handle_set_pixel_format, 2: self.__handle_set_encodings, @@ -499,7 +505,12 @@ async def __handle_pointer_event(self) -> None: async def __handle_client_cut_text(self) -> None: length = (await self._read_struct("cut text length", "xxx L"))[0] text = await self._read_text("cut text data", length) - await self._on_cut_event(text) + if self.__allow_cut_since_ts > 0 and time.monotonic() >= self.__allow_cut_since_ts: + # We should ignore cut event a few seconds after handshake + # because bVNC, AVNC and maybe some other clients perform + # it right after the connection automatically. + # - https://github.com/pikvm/pikvm/issues/1420 + await self._on_cut_event(text) async def __handle_enable_cont_updates(self) -> None: enabled = bool((await self._read_struct("enabled ContUpdates", "B HH HH"))[0]) diff --git a/kvmd/apps/vnc/server.py b/kvmd/apps/vnc/server.py index e8524a38..5abca7b0 100644 --- a/kvmd/apps/vnc/server.py +++ b/kvmd/apps/vnc/server.py @@ -81,6 +81,7 @@ def __init__( # pylint: disable=too-many-arguments,too-many-locals mouse_output: str, keymap_name: str, symmap: dict[int, dict[int, str]], + allow_cut_after: float, kvmd: KvmdClient, streamers: list[BaseStreamerClient], @@ -100,6 +101,7 @@ def __init__( # pylint: disable=too-many-arguments,too-many-locals tls_timeout=tls_timeout, x509_cert_path=x509_cert_path, x509_key_path=x509_key_path, + allow_cut_after=allow_cut_after, vnc_passwds=list(vnc_credentials), vencrypt=vencrypt, none_auth_only=none_auth_only, @@ -444,6 +446,7 @@ def __init__( # pylint: disable=too-many-arguments,too-many-locals desired_fps: int, mouse_output: str, keymap_path: str, + allow_cut_after: float, kvmd: KvmdClient, streamers: list[BaseStreamerClient], @@ -501,6 +504,7 @@ async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWrit mouse_output=mouse_output, keymap_name=keymap_name, symmap=symmap, + allow_cut_after=allow_cut_after, kvmd=kvmd, streamers=streamers, vnc_credentials=(await self.__vnc_auth_manager.read_credentials())[0],