win95 runtime switching

This commit is contained in:
Maxim Devaev
2021-08-15 17:34:42 +03:00
parent 9afa6efbe4
commit df098bd075
12 changed files with 110 additions and 52 deletions

View File

@@ -7,7 +7,7 @@ platform = atmelavr
board = micro board = micro
framework = arduino framework = arduino
lib_deps = lib_deps =
git+https://github.com/mdevaev/HID#f12b7d633f7437552707d6ccd411204cf36601f2 git+https://github.com/mdevaev/HID#a8877bc878a1a2f39d993a3defa750b6ec1b2ee0
git+https://github.com/Harvie/ps2dev#v0.0.3 git+https://github.com/Harvie/ps2dev#v0.0.3
digitalWriteFast@1.0.0 digitalWriteFast@1.0.0
extra_scripts = extra_scripts =
@@ -25,7 +25,7 @@ build_flags =
-DHID_SET_USB_KBD -DHID_SET_USB_KBD
-DHID_SET_USB_MOUSE_ABS -DHID_SET_USB_MOUSE_ABS
# ----- The USB ABS fix for Windows 98 (https://github.com/pikvm/pikvm/issues/159) ----- # ----- The USB ABS fix for Windows 98 (https://github.com/pikvm/pikvm/issues/159) -----
# -DHID_USB_ABS_WIN98_FIX # -DHID_WITH_USB_WIN98
# ----- PS2 keyboard only ----- # ----- PS2 keyboard only -----
# -DHID_WITH_PS2 # -DHID_WITH_PS2
# -DHID_SET_PS2_KBD # -DHID_SET_PS2_KBD

View File

@@ -102,6 +102,8 @@ static void _initOutputs() {
outputs |= PROTO::OUTPUTS1::MOUSE::USB_REL; outputs |= PROTO::OUTPUTS1::MOUSE::USB_REL;
# elif defined(HID_WITH_PS2) && defined(HID_SET_PS2_MOUSE) # elif defined(HID_WITH_PS2) && defined(HID_SET_PS2_MOUSE)
outputs |= PROTO::OUTPUTS1::MOUSE::PS2; outputs |= PROTO::OUTPUTS1::MOUSE::PS2;
# elif defined(HID_WITH_USB) && defined(HID_WITH_USB_WIN98) && defined(HID_SET_USB_MOUSE_WIN98)
outputs |= PROTO::OUTPUTS1::MOUSE::USB_WIN98;
# endif # endif
# ifdef HID_DYNAMIC # ifdef HID_DYNAMIC
@@ -122,7 +124,8 @@ static void _initOutputs() {
uint8_t mouse = outputs & PROTO::OUTPUTS1::MOUSE::MASK; uint8_t mouse = outputs & PROTO::OUTPUTS1::MOUSE::MASK;
switch (mouse) { switch (mouse) {
# ifdef HID_WITH_USB # ifdef HID_WITH_USB
case PROTO::OUTPUTS1::MOUSE::USB_ABS: _usb_mouse_abs = new UsbMouseAbsolute(); break; case PROTO::OUTPUTS1::MOUSE::USB_ABS:
case PROTO::OUTPUTS1::MOUSE::USB_WIN98: _usb_mouse_abs = new UsbMouseAbsolute(); break;
case PROTO::OUTPUTS1::MOUSE::USB_REL: _usb_mouse_rel = new UsbMouseRelative(); break; case PROTO::OUTPUTS1::MOUSE::USB_REL: _usb_mouse_rel = new UsbMouseRelative(); break;
# endif # endif
} }
@@ -140,7 +143,12 @@ static void _initOutputs() {
switch (mouse) { switch (mouse) {
# ifdef HID_WITH_USB # ifdef HID_WITH_USB
case PROTO::OUTPUTS1::MOUSE::USB_ABS: _usb_mouse_abs->begin(); break; case PROTO::OUTPUTS1::MOUSE::USB_ABS:
# ifdef HID_WITH_USB_WIN98
case PROTO::OUTPUTS1::MOUSE::USB_WIN98:
# endif
_usb_mouse_abs->begin(mouse == PROTO::OUTPUTS1::MOUSE::USB_WIN98);
break;
case PROTO::OUTPUTS1::MOUSE::USB_REL: _usb_mouse_rel->begin(); break; case PROTO::OUTPUTS1::MOUSE::USB_REL: _usb_mouse_rel->begin(); break;
# endif # endif
} }
@@ -286,7 +294,11 @@ static void _sendResponse(uint8_t code) {
} }
if (_usb_mouse_abs) { if (_usb_mouse_abs) {
response[1] |= _usb_mouse_abs->getOfflineAs(PROTO::PONG::MOUSE_OFFLINE); response[1] |= _usb_mouse_abs->getOfflineAs(PROTO::PONG::MOUSE_OFFLINE);
response[2] |= PROTO::OUTPUTS1::MOUSE::USB_ABS; if (_usb_mouse_abs->isWin98FixEnabled()) {
response[2] |= PROTO::OUTPUTS1::MOUSE::USB_WIN98;
} else {
response[2] |= PROTO::OUTPUTS1::MOUSE::USB_ABS;
}
} else if (_usb_mouse_rel) { } else if (_usb_mouse_rel) {
response[1] |= _usb_mouse_rel->getOfflineAs(PROTO::PONG::MOUSE_OFFLINE); response[1] |= _usb_mouse_rel->getOfflineAs(PROTO::PONG::MOUSE_OFFLINE);
response[2] |= PROTO::OUTPUTS1::MOUSE::USB_REL; response[2] |= PROTO::OUTPUTS1::MOUSE::USB_REL;
@@ -299,6 +311,9 @@ static void _sendResponse(uint8_t code) {
# endif # endif
# ifdef HID_WITH_USB # ifdef HID_WITH_USB
response[3] |= PROTO::OUTPUTS2::HAS_USB; response[3] |= PROTO::OUTPUTS2::HAS_USB;
# ifdef HID_WITH_USB_WIN98
response[3] |= PROTO::OUTPUTS2::HAS_USB_WIN98;
# endif
# endif # endif
# ifdef HID_WITH_PS2 # ifdef HID_WITH_PS2
response[3] |= PROTO::OUTPUTS2::HAS_PS2; response[3] |= PROTO::OUTPUTS2::HAS_PS2;

View File

@@ -53,18 +53,20 @@ namespace PROTO {
const uint8_t PS2 = 0b00000011; const uint8_t PS2 = 0b00000011;
}; };
namespace MOUSE { namespace MOUSE {
const uint8_t MASK = 0b00111000; const uint8_t MASK = 0b00111000;
const uint8_t USB_ABS = 0b00001000; const uint8_t USB_ABS = 0b00001000;
const uint8_t USB_REL = 0b00010000; const uint8_t USB_REL = 0b00010000;
const uint8_t PS2 = 0b00011000; const uint8_t PS2 = 0b00011000;
const uint8_t USB_WIN98 = 0b00100000;
}; };
}; };
namespace OUTPUTS2 { // Complex response namespace OUTPUTS2 { // Complex response
const uint8_t CONNECTABLE = 0b10000000; const uint8_t CONNECTABLE = 0b10000000;
const uint8_t CONNECTED = 0b01000000; const uint8_t CONNECTED = 0b01000000;
const uint8_t HAS_USB = 0b00000001; const uint8_t HAS_USB = 0b00000001;
const uint8_t HAS_PS2 = 0b00000010; const uint8_t HAS_PS2 = 0b00000010;
const uint8_t HAS_USB_WIN98 = 0b00000100;
} }
namespace CMD { namespace CMD {

View File

@@ -149,11 +149,13 @@ class UsbMouseAbsolute {
public: public:
UsbMouseAbsolute() {} UsbMouseAbsolute() {}
void begin() { void begin(bool win98_fix) {
_mouse.begin(); _mouse.begin();
#ifdef HID_USB_ABS_WIN98_FIX _mouse.setWin98FixEnabled(win98_fix);
_mouse.setWin98Fix(true); }
#endif
bool isWin98FixEnabled() {
return _mouse.isWin98FixEnabled();
} }
void clear() { void clear() {

View File

@@ -179,12 +179,21 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
if outputs1 & 0b10000000: # Dynamic if outputs1 & 0b10000000: # Dynamic
if outputs2 & 0b00000001: # USB if outputs2 & 0b00000001: # USB
keyboard_outputs["available"].extend(["usb", "disabled"]) keyboard_outputs["available"].append("usb")
mouse_outputs["available"].extend(["usb", "usb_rel", "disabled"]) mouse_outputs["available"].extend(["usb", "usb_rel"])
if outputs2 & 0b00000100: # USB WIN98
mouse_outputs["available"].append("usb_win98")
if outputs2 & 0b00000010: # PS/2 if outputs2 & 0b00000010: # PS/2
keyboard_outputs["available"].extend(["ps2", "disabled"]) keyboard_outputs["available"].append("ps2")
mouse_outputs["available"].extend(["ps2", "disabled"]) mouse_outputs["available"].append("ps2")
if keyboard_outputs["available"]:
keyboard_outputs["available"].append("disabled")
if mouse_outputs["available"]:
mouse_outputs["available"].append("disabled")
active_keyboard = get_active_keyboard(outputs1) active_keyboard = get_active_keyboard(outputs1)
if active_keyboard in keyboard_outputs["available"]: if active_keyboard in keyboard_outputs["available"]:

View File

@@ -63,10 +63,11 @@ class SetKeyboardOutputEvent(BaseEvent):
# ===== # =====
_MOUSE_NAMES_TO_CODES = { _MOUSE_NAMES_TO_CODES = {
"disabled": 0b00000000, "disabled": 0b00000000,
"usb": 0b00001000, "usb": 0b00001000,
"usb_rel": 0b00010000, "usb_rel": 0b00010000,
"ps2": 0b00011000, "ps2": 0b00011000,
"usb_win98": 0b00100000,
} }
_MOUSE_CODES_TO_NAMES = tools.swapped_kvs(_MOUSE_NAMES_TO_CODES) _MOUSE_CODES_TO_NAMES = tools.swapped_kvs(_MOUSE_NAMES_TO_CODES)

View File

@@ -27,7 +27,6 @@ from typing import AsyncGenerator
from typing import Optional from typing import Optional
from typing import Any from typing import Any
from .... import tools
from .... import aiomulti from .... import aiomulti
from ....yamlconf import Option from ....yamlconf import Option
@@ -59,30 +58,31 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
self.__udc = UsbDeviceController(udc) self.__udc = UsbDeviceController(udc)
win98_fix = mouse.pop("absolute_win98_fix")
common = { common = {
"udc": self.__udc, "udc": self.__udc,
"noop": noop, "noop": noop,
"notifier": self.__notifier, "notifier": self.__notifier,
} }
self.__keyboard_proc = KeyboardProcess(**common, **keyboard) self.__keyboard_proc = KeyboardProcess(**common, **keyboard)
self.__mouse_current = self.__mouse_proc = MouseProcess(**common, **mouse) self.__mouse_current = self.__mouse_proc = MouseProcess(**common, **mouse)
self.__mouse_alt_proc: Optional[MouseProcess] = None self.__mouse_alt_proc: Optional[MouseProcess] = None
self.__output_to_mouse: Dict[str, MouseProcess] = {} self.__mouses: Dict[str, MouseProcess] = {}
self.__mouse_to_output: Dict[MouseProcess, str] = {}
if mouse_alt["device_path"]: if mouse_alt["device_path"]:
self.__mouse_alt_proc = MouseProcess( self.__mouse_alt_proc = MouseProcess(
absolute=(not mouse["absolute"]), absolute=(not mouse["absolute"]),
absolute_win98_fix=mouse["absolute_win98_fix"],
**common, **common,
**mouse_alt, **mouse_alt,
) )
self.__output_to_mouse = { self.__mouses = {
"usb": (self.__mouse_proc if mouse["absolute"] else self.__mouse_alt_proc), "usb": (self.__mouse_proc if mouse["absolute"] else self.__mouse_alt_proc),
"usb_rel": (self.__mouse_alt_proc if mouse["absolute"] else self.__mouse_proc), "usb_rel": (self.__mouse_alt_proc if mouse["absolute"] else self.__mouse_proc),
} }
self.__mouse_to_output = tools.swapped_kvs(self.__output_to_mouse) if win98_fix:
# На самом деле мультимышка и win95 не зависят друг от друга,
# но так было проще реализовать переключение режимов
self.__mouses["usb_win98"] = self.__mouses["usb"]
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> Dict:
@@ -139,8 +139,8 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
}, },
"mouse": { "mouse": {
"outputs": { "outputs": {
"available": list(self.__output_to_mouse), "available": list(self.__mouses),
"active": (self.__mouse_to_output[self.__mouse_current] if self.__mouse_alt_proc else ""), "active": self.__get_current_mouse_mode(),
}, },
**mouse_state, **mouse_state,
}, },
@@ -157,7 +157,9 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
async def reset(self) -> None: async def reset(self) -> None:
self.__keyboard_proc.send_reset_event() self.__keyboard_proc.send_reset_event()
self.__mouse_current.send_reset_event() self.__mouse_proc.send_reset_event()
if self.__mouse_alt_proc:
self.__mouse_alt_proc.send_reset_event()
async def cleanup(self) -> None: async def cleanup(self) -> None:
try: try:
@@ -188,12 +190,23 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
def set_params(self, keyboard_output: Optional[str]=None, mouse_output: Optional[str]=None) -> None: def set_params(self, keyboard_output: Optional[str]=None, mouse_output: Optional[str]=None) -> None:
_ = keyboard_output _ = keyboard_output
if mouse_output != self.__mouse_to_output[self.__mouse_current]: if mouse_output in self.__mouses and mouse_output != self.__get_current_mouse_mode():
if mouse_output in self.__output_to_mouse: self.__mouse_current.send_clear_event()
self.__mouse_current.send_clear_event() self.__mouse_current = self.__mouses[mouse_output]
self.__mouse_current = self.__output_to_mouse[mouse_output] self.__mouse_current.set_win98_fix(mouse_output == "usb_win98")
self.__notifier.notify() self.__notifier.notify()
def clear_events(self) -> None: def clear_events(self) -> None:
self.__keyboard_proc.send_clear_event() self.__keyboard_proc.send_clear_event()
self.__mouse_current.send_clear_event() self.__mouse_proc.send_clear_event()
if self.__mouse_alt_proc:
self.__mouse_alt_proc.send_clear_event()
# =====
def __get_current_mouse_mode(self) -> str:
if len(self.__mouses) == 0:
return ""
if self.__mouse_current.is_absolute():
return ("usb_win98" if self.__mouse_current.get_win98_fix() else "usb")
return "usb_rel"

View File

@@ -124,14 +124,23 @@ class MouseButtonEvent(BaseEvent):
class MouseMoveEvent(BaseEvent): class MouseMoveEvent(BaseEvent):
to_x: int to_x: int
to_y: int to_y: int
win98_fix: bool = False
to_fixed_x: int = 0 to_fixed_x: int = 0
to_fixed_y: int = 0 to_fixed_y: int = 0
def __post_init__(self) -> None: def __post_init__(self) -> None:
assert MouseRange.MIN <= self.to_x <= MouseRange.MAX assert MouseRange.MIN <= self.to_x <= MouseRange.MAX
assert MouseRange.MIN <= self.to_y <= MouseRange.MAX assert MouseRange.MIN <= self.to_y <= MouseRange.MAX
object.__setattr__(self, "to_fixed_x", MouseRange.remap(self.to_x, 0, MouseRange.MAX)) to_fixed_x = MouseRange.remap(self.to_x, 0, MouseRange.MAX)
object.__setattr__(self, "to_fixed_y", MouseRange.remap(self.to_y, 0, MouseRange.MAX)) to_fixed_y = MouseRange.remap(self.to_y, 0, MouseRange.MAX)
if self.win98_fix:
# https://github.com/pikvm/pikvm/issues/159
# For some reason, the correct implementation of this fix
# is a shift to the left, and not to the right, as in VirtualBox
to_fixed_x <<= 1
to_fixed_y <<= 1
object.__setattr__(self, "to_fixed_x", to_fixed_x)
object.__setattr__(self, "to_fixed_y", to_fixed_y)
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)

View File

@@ -42,7 +42,6 @@ from .events import make_mouse_report
class MouseProcess(BaseDeviceProcess): class MouseProcess(BaseDeviceProcess):
def __init__(self, **kwargs: Any) -> None: def __init__(self, **kwargs: Any) -> None:
self.__absolute: bool = kwargs.pop("absolute") self.__absolute: bool = kwargs.pop("absolute")
self.__absolute_win98_fix: bool = kwargs.pop("absolute_win98_fix")
self.__horizontal_wheel: bool = kwargs.pop("horizontal_wheel") self.__horizontal_wheel: bool = kwargs.pop("horizontal_wheel")
super().__init__( super().__init__(
@@ -55,6 +54,16 @@ class MouseProcess(BaseDeviceProcess):
self.__pressed_buttons = 0 self.__pressed_buttons = 0
self.__x = 0 # For absolute self.__x = 0 # For absolute
self.__y = 0 self.__y = 0
self.__win98_fix = False
def is_absolute(self) -> bool:
return self.__absolute
def set_win98_fix(self, enabled: bool) -> None:
self.__win98_fix = enabled
def get_win98_fix(self) -> bool:
return self.__win98_fix
def cleanup(self) -> None: def cleanup(self) -> None:
self._stop() self._stop()
@@ -82,7 +91,7 @@ class MouseProcess(BaseDeviceProcess):
def send_move_event(self, to_x: int, to_y: int) -> None: def send_move_event(self, to_x: int, to_y: int) -> None:
if self.__absolute: if self.__absolute:
self._queue_event(MouseMoveEvent(to_x, to_y)) self._queue_event(MouseMoveEvent(to_x, to_y, self.__win98_fix))
def send_relative_event(self, delta_x: int, delta_y: int) -> None: def send_relative_event(self, delta_x: int, delta_y: int) -> None:
if not self.__absolute: if not self.__absolute:
@@ -144,12 +153,6 @@ class MouseProcess(BaseDeviceProcess):
assert relative_event is None assert relative_event is None
move_x = self.__x move_x = self.__x
move_y = self.__y move_y = self.__y
if self.__absolute_win98_fix:
# https://github.com/pikvm/pikvm/issues/159
# For some reason, the correct implementation of this fix
# is a shift to the left, and not to the right, as in VirtualBox
move_x <<= 1
move_y <<= 1
else: else:
assert self.__x == self.__y == 0 assert self.__x == self.__y == 0
if relative_event is not None: if relative_event is not None:

View File

@@ -37,7 +37,7 @@ def valid_hid_keyboard_output(arg: Any) -> str:
def valid_hid_mouse_output(arg: Any) -> str: def valid_hid_mouse_output(arg: Any) -> str:
return check_string_in_list(arg, "Mouse output", ["usb", "usb_rel", "ps2", "disabled"]) return check_string_in_list(arg, "Mouse output", ["usb", "usb_win98", "usb_rel", "ps2", "disabled"])
def valid_hid_key(arg: Any) -> str: def valid_hid_key(arg: Any) -> str:

View File

@@ -10,6 +10,9 @@ kvmd:
device: /dev/null device: /dev/null
mouse: mouse:
device: /dev/null device: /dev/null
# absolute_win98_fix: true
# mouse_alt:
# device: /dev/null
noop: true noop: true
msd: msd:

View File

@@ -133,6 +133,7 @@ export function Hid(__getResolution) {
let html = ""; let html = "";
for (let args of [ for (let args of [
["USB", "usb", false], ["USB", "usb", false],
["USB Win98", "usb_win98", false],
["USB Relative", "usb_rel", true], ["USB Relative", "usb_rel", true],
["PS/2", "ps2", true], ["PS/2", "ps2", true],
["Off", "disabled"], ["Off", "disabled"],