fixed mouse remap

This commit is contained in:
Devaev Maxim 2021-03-26 21:32:21 +03:00
parent 1674cf70b3
commit a66221a494
8 changed files with 79 additions and 20 deletions

View File

@ -39,6 +39,8 @@ import pygments.formatters
from .. import tools from .. import tools
from ..mouse import MouseRange
from ..plugins import UnknownPluginError from ..plugins import UnknownPluginError
from ..plugins.auth import get_auth_service_class from ..plugins.auth import get_auth_service_class
from ..plugins.hid import get_hid_class from ..plugins.hid import get_hid_class
@ -367,13 +369,13 @@ def _get_config_scheme() -> Dict:
"keymap": Option("/usr/share/kvmd/keymaps/en-us", type=valid_abs_file), "keymap": Option("/usr/share/kvmd/keymaps/en-us", type=valid_abs_file),
"mouse_x_range": { "mouse_x_range": {
"min": Option(-32768, type=valid_hid_mouse_move), "min": Option(MouseRange.MIN, type=valid_hid_mouse_move),
"max": Option(32767, type=valid_hid_mouse_move), "max": Option(MouseRange.MAX, type=valid_hid_mouse_move),
}, },
"mouse_y_range": { "mouse_y_range": {
"min": Option(-32768, type=valid_hid_mouse_move), "min": Option(MouseRange.MIN, type=valid_hid_mouse_move),
"max": Option(32767, type=valid_hid_mouse_move), "max": Option(MouseRange.MAX, type=valid_hid_mouse_move),
}, },
# Dynamic content # Dynamic content

View File

@ -33,6 +33,8 @@ from aiohttp.web import Request
from aiohttp.web import Response from aiohttp.web import Response
from aiohttp.web import WebSocketResponse from aiohttp.web import WebSocketResponse
from ....mouse import MouseRange
from ....plugins.hid import BaseHid from ....plugins.hid import BaseHid
from ....validators import raise_error from ....validators import raise_error
@ -169,11 +171,11 @@ class HidApi:
@exposed_ws("mouse_move") @exposed_ws("mouse_move")
async def __ws_mouse_move_handler(self, _: WebSocketResponse, event: Dict) -> None: async def __ws_mouse_move_handler(self, _: WebSocketResponse, event: Dict) -> None:
try: try:
to_x = valid_hid_mouse_move(event["to"]["x"], *self.__mouse_x_range) to_x = valid_hid_mouse_move(event["to"]["x"])
to_y = valid_hid_mouse_move(event["to"]["y"], *self.__mouse_y_range) to_y = valid_hid_mouse_move(event["to"]["y"])
except Exception: except Exception:
return return
self.__hid.send_mouse_move_event(to_x, to_y) self.__send_mouse_move_event_remapped(to_x, to_y)
@exposed_ws("mouse_relative") @exposed_ws("mouse_relative")
async def __ws_mouse_relative_handler(self, _: WebSocketResponse, event: Dict) -> None: async def __ws_mouse_relative_handler(self, _: WebSocketResponse, event: Dict) -> None:
@ -232,9 +234,9 @@ class HidApi:
@exposed_http("POST", "/hid/events/send_mouse_move") @exposed_http("POST", "/hid/events/send_mouse_move")
async def __events_send_mouse_move_handler(self, request: Request) -> Response: async def __events_send_mouse_move_handler(self, request: Request) -> Response:
to_x = valid_hid_mouse_move(request.query.get("to_x"), *self.__mouse_x_range) to_x = valid_hid_mouse_move(request.query.get("to_x"))
to_y = valid_hid_mouse_move(request.query.get("to_y"), *self.__mouse_y_range) to_y = valid_hid_mouse_move(request.query.get("to_y"))
self.__hid.send_mouse_move_event(to_x, to_y) self.__send_mouse_move_event_remapped(to_x, to_y)
return make_json_response() return make_json_response()
@exposed_http("POST", "/hid/events/send_mouse_relative") @exposed_http("POST", "/hid/events/send_mouse_relative")
@ -245,6 +247,13 @@ class HidApi:
async def __events_send_mouse_wheel_handler(self, request: Request) -> Response: async def __events_send_mouse_wheel_handler(self, request: Request) -> Response:
return self.__process_delta_request(request, self.__hid.send_mouse_wheel_event) return self.__process_delta_request(request, self.__hid.send_mouse_wheel_event)
def __send_mouse_move_event_remapped(self, to_x: int, to_y: int) -> None:
if self.__mouse_x_range != MouseRange.RANGE:
to_x = MouseRange.remap(to_x, *self.__mouse_x_range)
if self.__mouse_y_range != MouseRange.RANGE:
to_y = MouseRange.remap(to_y, *self.__mouse_y_range)
self.__hid.send_mouse_move_event(to_x, to_y)
def __process_delta_request(self, request: Request, handler: Callable[[int, int], None]) -> Response: def __process_delta_request(self, request: Request, handler: Callable[[int, int], None]) -> Response:
delta_x = valid_hid_mouse_delta(request.query.get("delta_x")) delta_x = valid_hid_mouse_delta(request.query.get("delta_x"))
delta_y = valid_hid_mouse_delta(request.query.get("delta_y")) delta_y = valid_hid_mouse_delta(request.query.get("delta_y"))

View File

@ -32,8 +32,11 @@ from typing import Coroutine
from ....logging import get_logger from ....logging import get_logger
from .... import tools
from .... import aiotools from .... import aiotools
from ....mouse import MouseRange
from .errors import RfbError from .errors import RfbError
from .errors import RfbConnectionError from .errors import RfbConnectionError
@ -444,8 +447,8 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
"y": (-4 if buttons & 0x10 else (4 if buttons & 0x8 else 0)), "y": (-4 if buttons & 0x10 else (4 if buttons & 0x8 else 0)),
}, },
move={ move={
"x": round(to_x / self._width * 65535 + -32768), "x": tools.remap(to_x, 0, self._width, *MouseRange.RANGE),
"y": round(to_y / self._height * 65535 + -32768), "y": tools.remap(to_y, 0, self._height, *MouseRange.RANGE),
}, },
) )

34
kvmd/mouse.py Normal file
View File

@ -0,0 +1,34 @@
# ========================================================================== #
# #
# KVMD - The main Pi-KVM daemon. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
# ========================================================================== #
from . import tools
# =====
class MouseRange:
MIN = -32768
MAX = 32767
RANGE = (MIN, MAX)
@classmethod
def remap(cls, value: int, out_min: int, out_max: int) -> int:
return tools.remap(value, cls.MIN, cls.MAX, out_min, out_max)

View File

@ -25,6 +25,8 @@ import struct
from ....keyboard.mappings import KEYMAP from ....keyboard.mappings import KEYMAP
from ....mouse import MouseRange
from .... import tools from .... import tools
@ -143,8 +145,8 @@ class MouseMoveEvent(BaseEvent):
to_y: int to_y: int
def __post_init__(self) -> None: def __post_init__(self) -> None:
assert -32768 <= self.to_x <= 32767 assert MouseRange.MIN <= self.to_x <= MouseRange.MAX
assert -32768 <= self.to_y <= 32767 assert MouseRange.MIN <= self.to_y <= MouseRange.MAX
def make_request(self) -> bytes: def make_request(self) -> bytes:
return _make_request(struct.pack(">Bhh", 0x12, self.to_x, self.to_y)) return _make_request(struct.pack(">Bhh", 0x12, self.to_x, self.to_y))

View File

@ -31,6 +31,8 @@ from typing import Union
from ....keyboard.mappings import OtgKey from ....keyboard.mappings import OtgKey
from ....keyboard.mappings import KEYMAP from ....keyboard.mappings import KEYMAP
from ....mouse import MouseRange
# ===== # =====
class BaseEvent: class BaseEvent:
@ -126,10 +128,10 @@ class MouseMoveEvent(BaseEvent):
to_fixed_y: int = 0 to_fixed_y: int = 0
def __post_init__(self) -> None: def __post_init__(self) -> None:
assert -32768 <= self.to_x <= 32767 assert MouseRange.MIN <= self.to_x <= MouseRange.MAX
assert -32768 <= self.to_y <= 32767 assert MouseRange.MIN <= self.to_y <= MouseRange.MAX
object.__setattr__(self, "to_fixed_x", (self.to_x + 32768) // 2) object.__setattr__(self, "to_fixed_x", MouseRange.remap(self.to_x, 0, MouseRange.MAX))
object.__setattr__(self, "to_fixed_y", (self.to_y + 32768) // 2) object.__setattr__(self, "to_fixed_y", MouseRange.remap(self.to_y, 0, MouseRange.MAX))
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)

View File

@ -32,6 +32,11 @@ from typing import Hashable
from typing import TypeVar from typing import TypeVar
# =====
def remap(value: int, in_min: int, in_max: int, out_min: int, out_max: int) -> int:
return (value - in_min) * (out_max - out_min) // (in_max - in_min) + out_min
# ===== # =====
def efmt(err: Exception) -> str: def efmt(err: Exception) -> str:
return f"{type(err).__name__}: {err}" return f"{type(err).__name__}: {err}"

View File

@ -24,6 +24,8 @@ from typing import Any
from ..keyboard.mappings import KEYMAP from ..keyboard.mappings import KEYMAP
from ..mouse import MouseRange
from . import check_string_in_list from . import check_string_in_list
from .basic import valid_number from .basic import valid_number
@ -42,9 +44,9 @@ def valid_hid_key(arg: Any) -> str:
return check_string_in_list(arg, "Keyboard key", KEYMAP, lower=False) return check_string_in_list(arg, "Keyboard key", KEYMAP, lower=False)
def valid_hid_mouse_move(arg: Any, move_min: int=-32768, move_max: int=32767) -> int: def valid_hid_mouse_move(arg: Any) -> int:
arg = valid_number(arg, name="Mouse move") arg = valid_number(arg, name="Mouse move")
return min(max(move_min, arg), move_max) return min(max(MouseRange.MIN, arg), MouseRange.MAX)
def valid_hid_mouse_button(arg: Any) -> str: def valid_hid_mouse_button(arg: Any) -> str: