diff options
author | FeralChild64 <unknown> | 2022-10-14 13:22:04 +0300 |
---|---|---|
committer | kcgen <1557255+kcgen@users.noreply.github.com> | 2022-10-22 21:15:34 +0300 |
commit | ffc06cae7a0665c08f363c4cb2c6a364fdccd640 (patch) | |
tree | 457b21b0adda1b36abe761521f9f04c42dbfcdd0 | |
parent | 8dbfbf9e7a6c9ef4ad476967a2d705731457ecc6 (diff) |
Fix mouse code formatting
-rw-r--r-- | include/mouse.h | 169 | ||||
-rw-r--r-- | src/hardware/mouse/mouse.cpp | 754 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_common.cpp | 195 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_common.h | 156 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_config.cpp | 543 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_config.h | 98 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_interfaces.cpp | 813 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_interfaces.h | 195 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_manymouse.cpp | 648 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_manymouse.h | 104 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_queue.cpp | 388 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_queue.h | 101 | ||||
-rw-r--r-- | src/hardware/mouse/mouseif_dos_driver.cpp | 2705 | ||||
-rw-r--r-- | src/hardware/mouse/mouseif_ps2_bios.cpp | 600 | ||||
-rw-r--r-- | src/hardware/mouse/mouseif_virtual_machines.cpp | 315 | ||||
-rw-r--r-- | src/hardware/serialport/serialmouse.cpp | 812 | ||||
-rw-r--r-- | src/hardware/serialport/serialmouse.h | 122 |
17 files changed, 4328 insertions, 4390 deletions
diff --git a/include/mouse.h b/include/mouse.h index ad93aa28c..12b3b5e51 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -30,7 +30,7 @@ // Initialization, configuration // *************************************************************************** -void MOUSE_Init(Section * ); +void MOUSE_Init(Section *); void MOUSE_AddConfigSection(const config_ptr_t &); // *************************************************************************** @@ -38,35 +38,35 @@ void MOUSE_AddConfigSection(const config_ptr_t &); // *************************************************************************** enum class MouseInterfaceId : uint8_t { - DOS, // emulated DOS mouse driver - PS2, // PS/2 mouse (this includes VMware mouse protocol) - COM1, // serial mouse - COM2, - COM3, - COM4, - - First = DOS, - Last = COM4, - None = UINT8_MAX + DOS, // emulated DOS mouse driver + PS2, // PS/2 mouse (this includes VMware mouse protocol) + COM1, // serial mouse + COM2, + COM3, + COM4, + + First = DOS, + Last = COM4, + None = UINT8_MAX }; -constexpr uint8_t num_mouse_interfaces = static_cast<uint8_t>(MouseInterfaceId::Last) + 1; - +constexpr uint8_t num_mouse_interfaces = static_cast<uint8_t>(MouseInterfaceId::Last) + + 1; enum class MouseMapStatus : uint8_t { - HostPointer, - Mapped, // single physical mouse mapped to emulated port - Disconnected, // physical mouse used to be mapped, but got unplugged - Disabled + HostPointer, + Mapped, // single physical mouse mapped to emulated port + Disconnected, // physical mouse used to be mapped, but got unplugged + Disabled }; // *************************************************************************** // Notifications from external subsystems - all should go via these methods // *************************************************************************** -void MOUSE_EventMoved(const float x_rel, const float y_rel, +void MOUSE_EventMoved(const float x_rel, const float y_rel, const uint16_t x_abs, const uint16_t y_abs); -void MOUSE_EventMoved(const float x_rel, const float y_rel, +void MOUSE_EventMoved(const float x_rel, const float y_rel, const MouseInterfaceId device_id); void MOUSE_EventButton(const uint8_t idx, const bool pressed); @@ -74,8 +74,7 @@ void MOUSE_EventButton(const uint8_t idx, const bool pressed, const MouseInterfaceId device_id); void MOUSE_EventWheel(const int16_t w_rel); -void MOUSE_EventWheel(const int16_t w_rel, - const MouseInterfaceId device_id); +void MOUSE_EventWheel(const int16_t w_rel, const MouseInterfaceId device_id); void MOUSE_NotifyBooting(); @@ -91,7 +90,8 @@ void MOUSE_NewScreenParams(const uint16_t clip_x, const uint16_t clip_y, // Information for the GFX subsystem // *************************************************************************** -bool MOUSE_IsUsingSeamlessDriver(); // if driver with seamless pointer support is running +bool MOUSE_IsUsingSeamlessDriver(); // if driver with seamless pointer support + // is running bool MOUSE_IsUsingSeamlessSetting(); // if user selected seamless mode is in effect // *************************************************************************** @@ -126,98 +126,91 @@ class MousePhysical; class MouseInterfaceInfoEntry final { public: - - bool IsEmulated() const; - bool IsMapped() const; - bool IsMapped(const uint8_t device_idx) const; - bool IsMappedDeviceDisconnected() const; - - MouseInterfaceId GetInterfaceId() const; - MouseMapStatus GetMapStatus() const; - const std::string &GetMappedDeviceName() const; - int8_t GetSensitivityX() const; // -99 to +99 - int8_t GetSensitivityY() const; // -99 to +99 - uint16_t GetMinRate() const; // 10-500, 0 for none - uint16_t GetRate() const; // current rate, 10-500, 0 for N/A + bool IsEmulated() const; + bool IsMapped() const; + bool IsMapped(const uint8_t device_idx) const; + bool IsMappedDeviceDisconnected() const; + + MouseInterfaceId GetInterfaceId() const; + MouseMapStatus GetMapStatus() const; + const std::string &GetMappedDeviceName() const; + int8_t GetSensitivityX() const; // -99 to +99 + int8_t GetSensitivityY() const; // -99 to +99 + uint16_t GetMinRate() const; // 10-500, 0 for none + uint16_t GetRate() const; // current rate, 10-500, 0 for N/A private: + friend class MouseInterface; + MouseInterfaceInfoEntry(const MouseInterfaceId interface_id); - friend class MouseInterface; - MouseInterfaceInfoEntry(const MouseInterfaceId interface_id); - - const uint8_t idx; - const MouseInterface &Interface() const; - const MousePhysical &MappedPhysical() const; + const uint8_t idx; + const MouseInterface &Interface() const; + const MousePhysical &MappedPhysical() const; }; class MousePhysicalInfoEntry final { public: - - bool IsMapped() const; - bool IsDeviceDisconnected() const; - const std::string &GetDeviceName() const; + bool IsMapped() const; + bool IsDeviceDisconnected() const; + const std::string &GetDeviceName() const; private: + friend class ManyMouseGlue; + MousePhysicalInfoEntry(const uint8_t idx); - friend class ManyMouseGlue; - MousePhysicalInfoEntry(const uint8_t idx); - - const uint8_t idx; - const MousePhysical &Physical() const; + const uint8_t idx; + const MousePhysical &Physical() const; }; class MouseControlAPI final { public: + // Always destroy the object once it is not needed anymore + // (configuration tool finishes it's job) and we are returning + // to normal code execution! - // Always destroy the object once it is not needed anymore - // (configuration tool finishes it's job) and we are returning - // to normal code execution! - - MouseControlAPI(); - ~MouseControlAPI(); + MouseControlAPI(); + ~MouseControlAPI(); - // Empty list = performs operation on all emulated interfaces - typedef std::vector<MouseInterfaceId> ListIDs; + // Empty list = performs operation on all emulated interfaces + typedef std::vector<MouseInterfaceId> ListIDs; - // Do not use the references after object gets destroyed - const std::vector<MouseInterfaceInfoEntry> &GetInfoInterfaces() const; - const std::vector<MousePhysicalInfoEntry> &GetInfoPhysical(); + // Do not use the references after object gets destroyed + const std::vector<MouseInterfaceInfoEntry> &GetInfoInterfaces() const; + const std::vector<MousePhysicalInfoEntry> &GetInfoPhysical(); - static bool IsNoMouseMode(); - static bool CheckInterfaces(const ListIDs &list_ids); - static bool PatternToRegex(const std::string &pattern, std::regex ®ex); + static bool IsNoMouseMode(); + static bool CheckInterfaces(const ListIDs &list_ids); + static bool PatternToRegex(const std::string &pattern, std::regex ®ex); - bool ProbeForMapping(uint8_t &device_id); // For interactive mapping in MOUSECTL.COM only! + bool ProbeForMapping(uint8_t &device_id); // For interactive mapping in + // MOUSECTL.COM only! - bool Map(const MouseInterfaceId interface_id, const uint8_t device_idx); - bool Map(const MouseInterfaceId interface_id, const std::regex ®ex); - bool UnMap(const ListIDs &list_ids); + bool Map(const MouseInterfaceId interface_id, const uint8_t device_idx); + bool Map(const MouseInterfaceId interface_id, const std::regex ®ex); + bool UnMap(const ListIDs &list_ids); - bool OnOff(const ListIDs &list_ids, const bool enable); - bool Reset(const ListIDs &list_ids); + bool OnOff(const ListIDs &list_ids, const bool enable); + bool Reset(const ListIDs &list_ids); - // Valid sensitivity values are from -99 to +99 - bool SetSensitivity(const ListIDs &list_ids, - const int8_t sensitivity_x, - const int8_t sensitivity_y); - bool SetSensitivityX(const ListIDs &list_ids, - const int8_t sensitivity_x); - bool SetSensitivityY(const ListIDs &list_ids, - const int8_t sensitivity_y); + // Valid sensitivity values are from -99 to +99 + bool SetSensitivity(const ListIDs &list_ids, const int8_t sensitivity_x, + const int8_t sensitivity_y); + bool SetSensitivityX(const ListIDs &list_ids, const int8_t sensitivity_x); + bool SetSensitivityY(const ListIDs &list_ids, const int8_t sensitivity_y); - bool ResetSensitivity(const ListIDs &list_ids); - bool ResetSensitivityX(const ListIDs &list_ids); - bool ResetSensitivityY(const ListIDs &list_ids); + bool ResetSensitivity(const ListIDs &list_ids); + bool ResetSensitivityX(const ListIDs &list_ids); + bool ResetSensitivityY(const ListIDs &list_ids); - static const std::vector<uint16_t> &GetValidMinRateList(); - static const std::string &GetValidMinRateStr(); - bool SetMinRate(const MouseControlAPI::ListIDs &list_ids, - const uint16_t value_hz); - bool ResetMinRate(const MouseControlAPI::ListIDs &list_ids); + static const std::vector<uint16_t> &GetValidMinRateList(); + static const std::string &GetValidMinRateStr(); + bool SetMinRate(const MouseControlAPI::ListIDs &list_ids, + const uint16_t value_hz); + bool ResetMinRate(const MouseControlAPI::ListIDs &list_ids); private: - MouseControlAPI(const MouseControlAPI &) = delete; - MouseControlAPI &operator=(const MouseControlAPI &) = delete; + MouseControlAPI(const MouseControlAPI &) = delete; + MouseControlAPI &operator=(const MouseControlAPI &) = delete; }; #endif // DOSBOX_MOUSE_H diff --git a/src/hardware/mouse/mouse.cpp b/src/hardware/mouse/mouse.cpp index 4db2275aa..6db7b6e52 100644 --- a/src/hardware/mouse/mouse.cpp +++ b/src/hardware/mouse/mouse.cpp @@ -24,8 +24,8 @@ #include "mouse_queue.h" #include <algorithm> -#include <string> #include <sstream> +#include <string> #include <vector> #include "callback.h" @@ -36,13 +36,12 @@ CHECK_NARROWING(); - bool seamless_driver = false; bool seamless_setting = false; static Bitu int74_ret_callback = 0; -static MouseQueue &queue = MouseQueue::GetInstance(); +static MouseQueue &queue = MouseQueue::GetInstance(); static ManyMouseGlue &manymouse = ManyMouseGlue::GetInstance(); // *************************************************************************** @@ -51,82 +50,84 @@ static ManyMouseGlue &manymouse = ManyMouseGlue::GetInstance(); static Bitu int74_exit() { - SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback))); - reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback)); + SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback))); + reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback)); - return CBRET_NONE; + return CBRET_NONE; } static Bitu int74_handler() { - MouseEvent ev; - queue.FetchEvent(ev); - - // Handle DOS events - if (ev.request_dos) { - - uint8_t mask = 0; - if (ev.dos_moved) { - mask = MOUSEDOS_UpdateMoved(); - - // Taken from DOSBox X: HERE within the IRQ 12 handler is the - // appropriate place to redraw the cursor. OSes like Windows 3.1 - // expect real-mode code to do it in response to IRQ 12, not - // "out of the blue" from the SDL event handler like the - // original DOSBox code did it. Doing this allows the INT 33h - // emulation to draw the cursor while not causing Windows 3.1 to - // crash or behave erratically. - if (mask) - MOUSEDOS_DrawCursor(); - } - if (ev.dos_button) - mask = static_cast<uint8_t>(mask | MOUSEDOS_UpdateButtons(ev.dos_buttons)); - if (ev.dos_wheel) - mask = static_cast<uint8_t>(mask | MOUSEDOS_UpdateWheel()); - - // If DOS driver's client is not interested in this particular - // type of event - skip it - if (!MOUSEDOS_HasCallback(mask)) - return int74_exit(); - - CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback))); - CPU_Push16(RealOff(static_cast<RealPt>(CALLBACK_RealPointer(int74_ret_callback)) + 7)); - - return MOUSEDOS_DoCallback(mask, ev.dos_buttons); - } - - // Handle PS/2 and BIOS mouse events - if (ev.request_ps2 && mouse_shared.active_bios) { - CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback))); - CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback))); - - MOUSEPS2_UpdatePacket(); - return MOUSEBIOS_DoCallback(); - } - - // No mouse emulation module is interested in event - return int74_exit(); + MouseEvent ev; + queue.FetchEvent(ev); + + // Handle DOS events + if (ev.request_dos) { + uint8_t mask = 0; + if (ev.dos_moved) { + mask = MOUSEDOS_UpdateMoved(); + + // Taken from DOSBox X: HERE within the IRQ 12 handler + // is the appropriate place to redraw the cursor. OSes + // like Windows 3.1 expect real-mode code to do it in + // response to IRQ 12, not "out of the blue" from the + // SDL event handler like the original DOSBox code did + // it. Doing this allows the INT 33h emulation to draw + // the cursor while not causing Windows 3.1 to crash or + // behave erratically. + if (mask) + MOUSEDOS_DrawCursor(); + } + if (ev.dos_button) + mask = static_cast<uint8_t>( + mask | MOUSEDOS_UpdateButtons(ev.dos_buttons)); + if (ev.dos_wheel) + mask = static_cast<uint8_t>(mask | MOUSEDOS_UpdateWheel()); + + // If DOS driver's client is not interested in this particular + // type of event - skip it + if (!MOUSEDOS_HasCallback(mask)) + return int74_exit(); + + CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback))); + CPU_Push16(RealOff(static_cast<RealPt>(CALLBACK_RealPointer( + int74_ret_callback)) + + 7)); + + return MOUSEDOS_DoCallback(mask, ev.dos_buttons); + } + + // Handle PS/2 and BIOS mouse events + if (ev.request_ps2 && mouse_shared.active_bios) { + CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback))); + CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback))); + + MOUSEPS2_UpdatePacket(); + return MOUSEBIOS_DoCallback(); + } + + // No mouse emulation module is interested in event + return int74_exit(); } Bitu int74_ret_handler() { - queue.StartTimerIfNeeded(); - return CBRET_NONE; + queue.StartTimerIfNeeded(); + return CBRET_NONE; } - // *************************************************************************** // Information for the GFX subsystem // *************************************************************************** bool MOUSE_IsUsingSeamlessDriver() { - return seamless_driver; + return seamless_driver; } bool MOUSE_IsUsingSeamlessSetting() { - return seamless_setting; + return seamless_setting; } // *************************************************************************** @@ -138,444 +139,433 @@ void MOUSE_NewScreenParams(const uint16_t clip_x, const uint16_t clip_y, const bool fullscreen, const uint16_t x_abs, const uint16_t y_abs) { - mouse_video.clip_x = clip_x; - mouse_video.clip_y = clip_y; + mouse_video.clip_x = clip_x; + mouse_video.clip_y = clip_y; - // Protection against strange window sizes, - // to prevent division by 0 in some places - mouse_video.res_x = std::max(res_x, static_cast<uint16_t>(2)); - mouse_video.res_y = std::max(res_y, static_cast<uint16_t>(2)); + // Protection against strange window sizes, + // to prevent division by 0 in some places + mouse_video.res_x = std::max(res_x, static_cast<uint16_t>(2)); + mouse_video.res_y = std::max(res_y, static_cast<uint16_t>(2)); - mouse_video.fullscreen = fullscreen; + mouse_video.fullscreen = fullscreen; - MOUSEVMM_NewScreenParams(x_abs, y_abs); - MOUSE_NotifyStateChanged(); + MOUSEVMM_NewScreenParams(x_abs, y_abs); + MOUSE_NotifyStateChanged(); } void MOUSE_NotifyResetDOS() { - queue.ClearEventsDOS(); + queue.ClearEventsDOS(); } void MOUSE_NotifyStateChanged() { - const auto old_seamless_driver = seamless_driver; - const auto old_seamless_setting = seamless_setting; + const auto old_seamless_driver = seamless_driver; + const auto old_seamless_setting = seamless_setting; - const auto is_mapping_in_effect = manymouse.IsMappingInEffect(); + const auto is_mapping_in_effect = manymouse.IsMappingInEffect(); - static bool already_warned = false; - if (!already_warned && is_mapping_in_effect && - (mouse_shared.active_vmm || mouse_config.seamless)) { - LOG_WARNING("MOUSE: Mapping disables seamless pointer integration"); - already_warned = true; - } + static bool already_warned = false; + if (!already_warned && is_mapping_in_effect && + (mouse_shared.active_vmm || mouse_config.seamless)) { + LOG_WARNING("MOUSE: Mapping disables seamless pointer integration"); + already_warned = true; + } - // Prepare suggestions to the GFX subsystem - seamless_driver = mouse_shared.active_vmm && - !mouse_video.fullscreen && - !is_mapping_in_effect; - seamless_setting = mouse_config.seamless && - !mouse_video.fullscreen && - !is_mapping_in_effect; + // Prepare suggestions to the GFX subsystem + seamless_driver = mouse_shared.active_vmm && !mouse_video.fullscreen && + !is_mapping_in_effect; + seamless_setting = mouse_config.seamless && !mouse_video.fullscreen && + !is_mapping_in_effect; - // If state has really changed, update GFX subsystem - if (seamless_driver != old_seamless_driver || - seamless_setting != old_seamless_setting) - GFX_UpdateMouseState(); + // If state has really changed, update GFX subsystem + if (seamless_driver != old_seamless_driver || + seamless_setting != old_seamless_setting) + GFX_UpdateMouseState(); } void MOUSE_NotifyDisconnect(const MouseInterfaceId interface_id) { - auto interface = MouseInterface::Get(interface_id); - if (interface) - interface->NotifyDisconnect(); + auto interface = MouseInterface::Get(interface_id); + if (interface) + interface->NotifyDisconnect(); } void MOUSE_NotifyFakePS2() { - const auto interface = MouseInterface::GetPS2(); + const auto interface = MouseInterface::GetPS2(); - if (interface && interface->IsUsingEvents()) { - MouseEvent ev; - ev.request_ps2 = true; - queue.AddEvent(ev); - } + if (interface && interface->IsUsingEvents()) { + MouseEvent ev; + ev.request_ps2 = true; + queue.AddEvent(ev); + } } void MOUSE_NotifyBooting() { - for (auto &interface : mouse_interfaces) - interface->NotifyBooting(); + for (auto &interface : mouse_interfaces) + interface->NotifyBooting(); } - -void MOUSE_EventMoved(const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) +void MOUSE_EventMoved(const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) { - // Drop unneeded events - if (!mouse_is_captured && !seamless_driver && !seamless_setting) - return; + // Drop unneeded events + if (!mouse_is_captured && !seamless_driver && !seamless_setting) + return; - // From the GUI we are getting mouse movement data in two - // distinct formats: - // - // - relative; this one has a chance to be raw movements, - // it has to be fed to PS/2 mouse emulation, serial port - // mouse emulation, etc.; any guest side software accessing - // these mouse interfaces will most likely implement it's - // own mouse acceleration/smoothing/etc. - // - absolute; this follows host OS mouse behavior and should - // be fed to VMware seamless mouse emulation and similar - // interfaces - // - // Our DOS mouse driver (INT 33h) is a bit special, as it can - // act both ways (seamless and non-seamless mouse pointer), - // so it needs data in both formats. + // From the GUI we are getting mouse movement data in two + // distinct formats: + // + // - relative; this one has a chance to be raw movements, + // it has to be fed to PS/2 mouse emulation, serial port + // mouse emulation, etc.; any guest side software accessing + // these mouse interfaces will most likely implement it's + // own mouse acceleration/smoothing/etc. + // - absolute; this follows host OS mouse behavior and should + // be fed to VMware seamless mouse emulation and similar + // interfaces + // + // Our DOS mouse driver (INT 33h) is a bit special, as it can + // act both ways (seamless and non-seamless mouse pointer), + // so it needs data in both formats. - // Notify mouse interfaces + // Notify mouse interfaces - MouseEvent ev; - for (auto &interface : mouse_interfaces) - if (interface->IsUsingHostPointer()) - interface->NotifyMoved(ev, x_rel, y_rel, x_abs, y_abs); - queue.AddEvent(ev); + MouseEvent ev; + for (auto &interface : mouse_interfaces) + if (interface->IsUsingHostPointer()) + interface->NotifyMoved(ev, x_rel, y_rel, x_abs, y_abs); + queue.AddEvent(ev); } -void MOUSE_EventMoved(const float x_rel, - const float y_rel, +void MOUSE_EventMoved(const float x_rel, const float y_rel, const MouseInterfaceId interface_id) { - auto interface = MouseInterface::Get(interface_id); - if (interface && interface->IsUsingEvents()) { - MouseEvent ev; - interface->NotifyMoved(ev, x_rel, y_rel, 0, 0); - queue.AddEvent(ev); - } + auto interface = MouseInterface::Get(interface_id); + if (interface && interface->IsUsingEvents()) { + MouseEvent ev; + interface->NotifyMoved(ev, x_rel, y_rel, 0, 0); + queue.AddEvent(ev); + } } -void MOUSE_EventButton(const uint8_t idx, - const bool pressed) +void MOUSE_EventButton(const uint8_t idx, const bool pressed) { - MouseEvent ev; - for (auto &interface : mouse_interfaces) - if (interface->IsUsingHostPointer()) - interface->NotifyButton(ev, idx, pressed); - queue.AddEvent(ev); + MouseEvent ev; + for (auto &interface : mouse_interfaces) + if (interface->IsUsingHostPointer()) + interface->NotifyButton(ev, idx, pressed); + queue.AddEvent(ev); } -void MOUSE_EventButton(const uint8_t idx, - const bool pressed, +void MOUSE_EventButton(const uint8_t idx, const bool pressed, const MouseInterfaceId interface_id) { - auto interface = MouseInterface::Get(interface_id); - if (interface && interface->IsUsingEvents()) { - MouseEvent ev; - interface->NotifyButton(ev, idx, pressed); - queue.AddEvent(ev); - } + auto interface = MouseInterface::Get(interface_id); + if (interface && interface->IsUsingEvents()) { + MouseEvent ev; + interface->NotifyButton(ev, idx, pressed); + queue.AddEvent(ev); + } } void MOUSE_EventWheel(const int16_t w_rel) { - MouseEvent ev; - for (auto &interface : mouse_interfaces) - if (interface->IsUsingHostPointer()) - interface->NotifyWheel(ev, w_rel); - queue.AddEvent(ev); + MouseEvent ev; + for (auto &interface : mouse_interfaces) + if (interface->IsUsingHostPointer()) + interface->NotifyWheel(ev, w_rel); + queue.AddEvent(ev); } -void MOUSE_EventWheel(const int16_t w_rel, - const MouseInterfaceId interface_id) +void MOUSE_EventWheel(const int16_t w_rel, const MouseInterfaceId interface_id) { - auto interface = MouseInterface::Get(interface_id); - if (interface && interface->IsUsingEvents()) { - MouseEvent ev; - interface->NotifyWheel(ev, w_rel); - queue.AddEvent(ev); - } + auto interface = MouseInterface::Get(interface_id); + if (interface && interface->IsUsingEvents()) { + MouseEvent ev; + interface->NotifyWheel(ev, w_rel); + queue.AddEvent(ev); + } } // *************************************************************************** // MOUSECTL.COM / GUI configurator interface // *************************************************************************** -static std::vector<MouseInterface *> get_relevant_interfaces(const std::vector<MouseInterfaceId> &list_ids) +static std::vector<MouseInterface *> get_relevant_interfaces( + const std::vector<MouseInterfaceId> &list_ids) { - std::vector<MouseInterface *> list_tmp = {}; + std::vector<MouseInterface *> list_tmp = {}; - if (list_ids.empty()) - // If command does not specify interfaces, - // assume we are interested in all of them - list_tmp = mouse_interfaces; - else - for (const auto &interface_id : list_ids) { - auto interface = MouseInterface::Get(interface_id); - if (interface) - list_tmp.push_back(interface); - } + if (list_ids.empty()) + // If command does not specify interfaces, + // assume we are interested in all of them + list_tmp = mouse_interfaces; + else + for (const auto &interface_id : list_ids) { + auto interface = MouseInterface::Get(interface_id); + if (interface) + list_tmp.push_back(interface); + } - // Filter out not emulated ones - std::vector<MouseInterface *> list_out = {}; - for (const auto &interface : list_tmp) - if (interface->IsEmulated()) - list_out.push_back(interface); + // Filter out not emulated ones + std::vector<MouseInterface *> list_out = {}; + for (const auto &interface : list_tmp) + if (interface->IsEmulated()) + list_out.push_back(interface); - return list_out; + return list_out; } MouseControlAPI::MouseControlAPI() { - manymouse.StartConfigAPI(); + manymouse.StartConfigAPI(); } MouseControlAPI::~MouseControlAPI() { - manymouse.StopConfigAPI(); - MOUSE_NotifyStateChanged(); + manymouse.StopConfigAPI(); + MOUSE_NotifyStateChanged(); } bool MouseControlAPI::IsNoMouseMode() { - return mouse_config.no_mouse; + return mouse_config.no_mouse; } const std::vector<MouseInterfaceInfoEntry> &MouseControlAPI::GetInfoInterfaces() const { - return mouse_info.interfaces; + return mouse_info.interfaces; } const std::vector<MousePhysicalInfoEntry> &MouseControlAPI::GetInfoPhysical() { - manymouse.RescanIfSafe(); - return mouse_info.physical; + manymouse.RescanIfSafe(); + return mouse_info.physical; } bool MouseControlAPI::CheckInterfaces(const MouseControlAPI::ListIDs &list_ids) { - const auto list = get_relevant_interfaces(list_ids); - - if (list_ids.empty() && list.empty()) - return false; // no emulated mouse interfaces - if (list_ids.empty()) - return true; // OK, requested all emulated interfaces - if (list_ids.size() != list.size()) - return false; // at least one requested interface is not emulated - - return true; -} - -bool MouseControlAPI::PatternToRegex(const std::string &pattern, - std::regex ®ex) -{ - // Convert DOS wildcard pattern to a regular expression - std::stringstream pattern_regex; - pattern_regex << std::hex; - for (const auto character : pattern) { - if (character < 0x20 || character > 0x7E) { - return false; - } - if (character == '?') - pattern_regex << "."; - else if (character == '*') - pattern_regex << ".*"; - else if ((character >= '0' && character <= '9') || - (character >= 'a' && character <= 'z') || - (character >= 'A' && character <= 'Z')) - pattern_regex << character; - else - pattern_regex << "\\x" << static_cast<int>(character); - } - - // Return a case-insensitive regular expression - regex = std::regex(pattern_regex.str(), std::regex_constants::icase); - return true; + const auto list = get_relevant_interfaces(list_ids); + + if (list_ids.empty() && list.empty()) + return false; // no emulated mouse interfaces + if (list_ids.empty()) + return true; // OK, requested all emulated interfaces + if (list_ids.size() != list.size()) + return false; // at least one requested interface is not emulated + + return true; +} + +bool MouseControlAPI::PatternToRegex(const std::string &pattern, std::regex ®ex) +{ + // Convert DOS wildcard pattern to a regular expression + std::stringstream pattern_regex; + pattern_regex << std::hex; + for (const auto character : pattern) { + if (character < 0x20 || character > 0x7E) { + return false; + } + if (character == '?') + pattern_regex << "."; + else if (character == '*') + pattern_regex << ".*"; + else if ((character >= '0' && character <= '9') || + (character >= 'a' && character <= 'z') || + (character >= 'A' && character <= 'Z')) + pattern_regex << character; + else + pattern_regex << "\\x" << static_cast<int>(character); + } + + // Return a case-insensitive regular expression + regex = std::regex(pattern_regex.str(), std::regex_constants::icase); + return true; } bool MouseControlAPI::ProbeForMapping(uint8_t &device_id) { - if (mouse_config.no_mouse) - return false; + if (mouse_config.no_mouse) + return false; - manymouse.RescanIfSafe(); - return manymouse.ProbeForMapping(device_id); + manymouse.RescanIfSafe(); + return manymouse.ProbeForMapping(device_id); } -bool MouseControlAPI::Map(const MouseInterfaceId interface_id, - const uint8_t device_idx) +bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const uint8_t device_idx) { - if (mouse_config.no_mouse) - return false; + if (mouse_config.no_mouse) + return false; - auto mouse_interface = MouseInterface::Get(interface_id); - if (!mouse_interface) - return false; + auto mouse_interface = MouseInterface::Get(interface_id); + if (!mouse_interface) + return false; - return mouse_interface->ConfigMap(device_idx); + return mouse_interface->ConfigMap(device_idx); } -bool MouseControlAPI::Map(const MouseInterfaceId interface_id, - const std::regex ®ex) +bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const std::regex ®ex) { - if (mouse_config.no_mouse) - return false; + if (mouse_config.no_mouse) + return false; - manymouse.RescanIfSafe(); - const auto idx = manymouse.GetIdx(regex); - if (idx >= mouse_info.physical.size()) - return false; + manymouse.RescanIfSafe(); + const auto idx = manymouse.GetIdx(regex); + if (idx >= mouse_info.physical.size()) + return false; - return Map(interface_id, idx); + return Map(interface_id, idx); } bool MouseControlAPI::UnMap(const MouseControlAPI::ListIDs &list_ids) { - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigUnMap(); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigUnMap(); - return !list.empty(); + return !list.empty(); } -bool MouseControlAPI::OnOff(const MouseControlAPI::ListIDs &list_ids, - const bool enable) +bool MouseControlAPI::OnOff(const MouseControlAPI::ListIDs &list_ids, const bool enable) { - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigOnOff(enable); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigOnOff(enable); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::Reset(const MouseControlAPI::ListIDs &list_ids) { - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigReset(); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigReset(); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::SetSensitivity(const MouseControlAPI::ListIDs &list_ids, const int8_t sensitivity_x, const int8_t sensitivity_y) { - if (sensitivity_x > mouse_predefined.sensitivity_user_max || - sensitivity_x < -mouse_predefined.sensitivity_user_max || - sensitivity_y > mouse_predefined.sensitivity_user_max || - sensitivity_y < -mouse_predefined.sensitivity_user_max) - return false; + if (sensitivity_x > mouse_predefined.sensitivity_user_max || + sensitivity_x < -mouse_predefined.sensitivity_user_max || + sensitivity_y > mouse_predefined.sensitivity_user_max || + sensitivity_y < -mouse_predefined.sensitivity_user_max) + return false; - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigSetSensitivity(sensitivity_x, sensitivity_y); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigSetSensitivity(sensitivity_x, sensitivity_y); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::SetSensitivityX(const MouseControlAPI::ListIDs &list_ids, const int8_t sensitivity_x) { - if (sensitivity_x > mouse_predefined.sensitivity_user_max || - sensitivity_x < -mouse_predefined.sensitivity_user_max) - return false; + if (sensitivity_x > mouse_predefined.sensitivity_user_max || + sensitivity_x < -mouse_predefined.sensitivity_user_max) + return false; - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigSetSensitivityX(sensitivity_x); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigSetSensitivityX(sensitivity_x); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::SetSensitivityY(const MouseControlAPI::ListIDs &list_ids, const int8_t sensitivity_y) { - if (sensitivity_y > mouse_predefined.sensitivity_user_max || - sensitivity_y < -mouse_predefined.sensitivity_user_max) - return false; + if (sensitivity_y > mouse_predefined.sensitivity_user_max || + sensitivity_y < -mouse_predefined.sensitivity_user_max) + return false; - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigSetSensitivityY(sensitivity_y); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigSetSensitivityY(sensitivity_y); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::ResetSensitivity(const MouseControlAPI::ListIDs &list_ids) { - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigResetSensitivity(); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigResetSensitivity(); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::ResetSensitivityX(const MouseControlAPI::ListIDs &list_ids) { - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigResetSensitivityX(); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigResetSensitivityX(); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::ResetSensitivityY(const MouseControlAPI::ListIDs &list_ids) { - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigResetSensitivityY(); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigResetSensitivityY(); - return !list.empty(); + return !list.empty(); } const std::vector<uint16_t> &MouseControlAPI::GetValidMinRateList() { - return MouseConfig::GetValidMinRateList(); + return MouseConfig::GetValidMinRateList(); } const std::string &MouseControlAPI::GetValidMinRateStr() { - static std::string out_str = ""; + static std::string out_str = ""; - if (out_str.empty()) { - const auto &valid_list = GetValidMinRateList(); + if (out_str.empty()) { + const auto &valid_list = GetValidMinRateList(); - bool first = true; - for (const auto &rate : valid_list) { - if (first) - first = false; - else - out_str += std::string(", "); - out_str += std::to_string(rate); - } - } + bool first = true; + for (const auto &rate : valid_list) { + if (first) + first = false; + else + out_str += std::string(", "); + out_str += std::to_string(rate); + } + } - return out_str; + return out_str; } bool MouseControlAPI::SetMinRate(const MouseControlAPI::ListIDs &list_ids, const uint16_t value_hz) { - const auto &valid_list = GetValidMinRateList(); - if (std::find(valid_list.begin(), valid_list.end(), value_hz) == valid_list.end()) - return false; // invalid value + const auto &valid_list = GetValidMinRateList(); + if (std::find(valid_list.begin(), valid_list.end(), value_hz) == + valid_list.end()) + return false; // invalid value - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigSetMinRate(value_hz); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigSetMinRate(value_hz); - return !list.empty(); + return !list.empty(); } bool MouseControlAPI::ResetMinRate(const MouseControlAPI::ListIDs &list_ids) { - auto list = get_relevant_interfaces(list_ids); - for (auto &interface : list) - interface->ConfigResetMinRate(); + auto list = get_relevant_interfaces(list_ids); + for (auto &interface : list) + interface->ConfigResetMinRate(); - return !list.empty(); + return !list.empty(); } // *************************************************************************** @@ -584,81 +574,79 @@ bool MouseControlAPI::ResetMinRate(const MouseControlAPI::ListIDs &list_ids) void MOUSE_SetConfigSeamless(const bool seamless) { - // Called during SDL initialization - mouse_config.seamless = seamless; - MOUSE_NotifyStateChanged(); + // Called during SDL initialization + mouse_config.seamless = seamless; + MOUSE_NotifyStateChanged(); - // Just in case it is also called later - for (auto &interface : mouse_interfaces) - interface->UpdateConfig(); + // Just in case it is also called later + for (auto &interface : mouse_interfaces) + interface->UpdateConfig(); - // Start mouse emulation if ready - mouse_shared.ready_config_sdl = true; - MOUSE_Startup(); + // Start mouse emulation if ready + mouse_shared.ready_config_sdl = true; + MOUSE_Startup(); } void MOUSE_SetConfigNoMouse() { - // NOTE: if it is decided to not allow enabling/disabling - // this during runtime, add button click releases for all - // the mouse buttons - mouse_config.no_mouse = true; + // NOTE: if it is decided to not allow enabling/disabling + // this during runtime, add button click releases for all + // the mouse buttons + mouse_config.no_mouse = true; - // Start mouse emulation if ready - mouse_shared.ready_config_sdl = true; - MOUSE_Startup(); + // Start mouse emulation if ready + mouse_shared.ready_config_sdl = true; + MOUSE_Startup(); } void MOUSE_Startup() { - if (mouse_shared.started || - !mouse_shared.ready_startup_sequence || - !mouse_shared.ready_config_mouse || - !mouse_shared.ready_config_sdl) - return; - - // Callback for ps2 irq - auto call_int74 = CALLBACK_Allocate(); - CALLBACK_Setup(call_int74, &int74_handler, CB_IRQ12, "int 74"); - // pseudocode for CB_IRQ12: - // sti - // push ds - // push es - // pushad - // callback int74_handler - // ps2 or user callback if requested - // otherwise jumps to CB_IRQ12_RET - // push ax - // mov al, 0x20 - // out 0xa0, al - // out 0x20, al - // pop ax - // cld - // retf - - int74_ret_callback = CALLBACK_Allocate(); - CALLBACK_Setup(int74_ret_callback, &int74_ret_handler, CB_IRQ12_RET, "int 74 ret"); - // pseudocode for CB_IRQ12_RET: - // cli - // mov al, 0x20 - // out 0xa0, al - // out 0x20, al - // callback int74_ret_handler - // popad - // pop es - // pop ds - // iret - - // (MOUSE_IRQ > 7) ? (0x70 + MOUSE_IRQ - 8) : (0x8 + MOUSE_IRQ); - RealSetVec(0x74, CALLBACK_RealPointer(call_int74)); - - MouseInterface::InitAllInstances(); - mouse_shared.started = true; + if (mouse_shared.started || !mouse_shared.ready_startup_sequence || + !mouse_shared.ready_config_mouse || !mouse_shared.ready_config_sdl) + return; + + // Callback for ps2 irq + auto call_int74 = CALLBACK_Allocate(); + CALLBACK_Setup(call_int74, &int74_handler, CB_IRQ12, "int 74"); + // pseudocode for CB_IRQ12: + // sti + // push ds + // push es + // pushad + // callback int74_handler + // ps2 or user callback if requested + // otherwise jumps to CB_IRQ12_RET + // push ax + // mov al, 0x20 + // out 0xa0, al + // out 0x20, al + // pop ax + // cld + // retf + + int74_ret_callback = CALLBACK_Allocate(); + CALLBACK_Setup(int74_ret_callback, &int74_ret_handler, CB_IRQ12_RET, "int 74 ret"); + // pseudocode for CB_IRQ12_RET: + // cli + // mov al, 0x20 + // out 0xa0, al + // out 0x20, al + // callback int74_ret_handler + // popad + // pop es + // pop ds + // iret + + // (MOUSE_IRQ > 7) ? (0x70 + MOUSE_IRQ - 8) : (0x8 + MOUSE_IRQ); + RealSetVec(0x74, CALLBACK_RealPointer(call_int74)); + + MouseInterface::InitAllInstances(); + mouse_shared.started = true; } void MOUSE_Init(Section * /*sec*/) { - // Start mouse emulation if ready - mouse_shared.ready_startup_sequence = true; - MOUSE_Startup(); + // Start mouse emulation if ready + mouse_shared.ready_startup_sequence = true; + MOUSE_Startup(); } diff --git a/src/hardware/mouse/mouse_common.cpp b/src/hardware/mouse/mouse_common.cpp index fb1350dad..cad02c5e7 100644 --- a/src/hardware/mouse/mouse_common.cpp +++ b/src/hardware/mouse/mouse_common.cpp @@ -29,9 +29,9 @@ CHECK_NARROWING(); // Common variables // *************************************************************************** -MouseInfo mouse_info; +MouseInfo mouse_info; MouseShared mouse_shared; -MouseVideo mouse_video; +MouseVideo mouse_video; // *************************************************************************** // Common helper calculations @@ -39,119 +39,120 @@ MouseVideo mouse_video; float MOUSE_GetBallisticsCoeff(const float speed) { - // This routine provides a function for mouse ballistics - // (cursor acceleration), to be reused by various mouse interfaces. - // Since this is a DOS emulator, the acceleration model is - // based on a historic PS/2 mouse scaling specification, desribed - // for exampel here: - // - https://wiki.osdev.org/Mouse_Input - - // Input: mouse speed - // Output: acceleration coefficient (1.0f for speed >= 6.0f) - - // NOTE: If we don't have raw mouse input, stay with flat profile; - // in such case the acceleration is already handled by the host OS, - // adding our own could lead to hard to predict (most likely - // undesirable) effects - - constexpr float a = 0.017153417f; - constexpr float b = 0.382477002f; - constexpr float lowest = 0.5f; - - // Normal PS/2 mouse 2:1 scaling algorithm is just a substitution: - // 0 => 0, 1 => 1, 2 => 1, 3 => 3, 4 => 6, 5 => 9, other x => x * 2 - // and the same for negatives. But we want smooth cursor movement, - // therefore we use approximation model (least square regression, - // 3rd degree polynomial, on points -6, -5, ..., 0, ... , 5, 6, - // here scaled to give f(6.0) = 6.0). Polynomial would be: - // - // f(x) = a*(x^3) + b*(x^1) = x*(a*(x^2) + b) - // - // This C++ function provides not the full polynomial, but rather - // a coefficient (0.0f ... 1.0f) calculated from supplied speed, - // by which the relative mouse measurement should be multiplied - - if (speed > -6.0f && speed < 6.0f) - return std::max((a * speed * speed + b), lowest); - else - return 1.0f; - - // Please consider this algorithm as yet another nod to the past, - // one more small touch of 20th century PC computing history :) + // This routine provides a function for mouse ballistics + // (cursor acceleration), to be reused by various mouse interfaces. + // Since this is a DOS emulator, the acceleration model is + // based on a historic PS/2 mouse scaling specification, desribed + // for exampel here: + // - https://wiki.osdev.org/Mouse_Input + + // Input: mouse speed + // Output: acceleration coefficient (1.0f for speed >= 6.0f) + + // NOTE: If we don't have raw mouse input, stay with flat profile; + // in such case the acceleration is already handled by the host OS, + // adding our own could lead to hard to predict (most likely + // undesirable) effects + + constexpr float a = 0.017153417f; + constexpr float b = 0.382477002f; + constexpr float lowest = 0.5f; + + // Normal PS/2 mouse 2:1 scaling algorithm is just a substitution: + // 0 => 0, 1 => 1, 2 => 1, 3 => 3, 4 => 6, 5 => 9, other x => x * 2 + // and the same for negatives. But we want smooth cursor movement, + // therefore we use approximation model (least square regression, + // 3rd degree polynomial, on points -6, -5, ..., 0, ... , 5, 6, + // here scaled to give f(6.0) = 6.0). Polynomial would be: + // + // f(x) = a*(x^3) + b*(x^1) = x*(a*(x^2) + b) + // + // This C++ function provides not the full polynomial, but rather + // a coefficient (0.0f ... 1.0f) calculated from supplied speed, + // by which the relative mouse measurement should be multiplied + + if (speed > -6.0f && speed < 6.0f) + return std::max((a * speed * speed + b), lowest); + else + return 1.0f; + + // Please consider this algorithm as yet another nod to the past, + // one more small touch of 20th century PC computing history :) } uint8_t MOUSE_GetDelayFromRateHz(const uint16_t rate_hz) { - assert(rate_hz); - const auto tmp = std::lround(1000.0f / MOUSE_ClampRateHz(rate_hz)); - return static_cast<uint8_t>(tmp); + assert(rate_hz); + const auto tmp = std::lround(1000.0f / MOUSE_ClampRateHz(rate_hz)); + return static_cast<uint8_t>(tmp); } float MOUSE_ClampRelativeMovement(const float rel) { - // Enforce sane upper limit of relative mouse movement - return std::clamp(rel, -2048.0f, 2048.0f); + // Enforce sane upper limit of relative mouse movement + return std::clamp(rel, -2048.0f, 2048.0f); } uint16_t MOUSE_ClampRateHz(const uint16_t rate_hz) { - constexpr uint16_t rate_min = 10; - constexpr uint16_t rate_max = 500; + constexpr uint16_t rate_min = 10; + constexpr uint16_t rate_max = 500; - return std::clamp(rate_hz, rate_min, rate_max); + return std::clamp(rate_hz, rate_min, rate_max); } // *************************************************************************** // Mouse speed calculation // *************************************************************************** -MouseSpeedCalculator::MouseSpeedCalculator(const float scaling) : - ticks_start(PIC_Ticks), - scaling(scaling * 1000.0f) // to convert from units/ms to units/s -{ -} +MouseSpeedCalculator::MouseSpeedCalculator(const float scaling) + : ticks_start(PIC_Ticks), + scaling(scaling * 1000.0f) // to convert from units/ms to units/s +{} float MouseSpeedCalculator::Get() const { - return speed; + return speed; } void MouseSpeedCalculator::Update(const float delta) { - // For the measurement require at least certain amount - // of PIC ticks; if too much time passes without meaningful - // movement, consider mouse speed to be 0 - constexpr uint32_t min_ticks = 40; - constexpr uint32_t max_ticks = 400; - - // Calculate time from the beginning of measurement - const auto diff_ticks = PIC_Ticks - ticks_start; - - // Try to calculate cursor speed - if (PIC_Ticks >= ticks_start) { - if (diff_ticks > max_ticks) - // Do not wait any more for the movement, consider speed to be 0 - speed = 0.0f; - else { - // Update distance travelled by the cursor - distance += delta; - - // Make sure enough time passed for accurate speed calculation - if (diff_ticks < min_ticks) - return; - - // Calculate cursor speed - speed = scaling * distance / static_cast<float>(diff_ticks); - } - } - - // Note: if PIC_Ticks < ticks_start, we have a counter overflow - this - // can only happen if emulator is run for many weeks at a time. In such - // case we just assume previous speed is still valid, for simplicity. - - // Start new measurement - distance = 0.0f; - ticks_start = PIC_Ticks; + // For the measurement require at least certain amount + // of PIC ticks; if too much time passes without meaningful + // movement, consider mouse speed to be 0 + constexpr uint32_t min_ticks = 40; + constexpr uint32_t max_ticks = 400; + + // Calculate time from the beginning of measurement + const auto diff_ticks = PIC_Ticks - ticks_start; + + // Try to calculate cursor speed + if (PIC_Ticks >= ticks_start) { + if (diff_ticks > max_ticks) + // Do not wait any more for the movement, consider speed + // to be 0 + speed = 0.0f; + else { + // Update distance travelled by the cursor + distance += delta; + + // Make sure enough time passed for accurate speed + // calculation + if (diff_ticks < min_ticks) + return; + + // Calculate cursor speed + speed = scaling * distance / static_cast<float>(diff_ticks); + } + } + + // Note: if PIC_Ticks < ticks_start, we have a counter overflow - this + // can only happen if emulator is run for many weeks at a time. In such + // case we just assume previous speed is still valid, for simplicity. + + // Start new measurement + distance = 0.0f; + ticks_start = PIC_Ticks; } // *************************************************************************** @@ -160,24 +161,24 @@ void MouseSpeedCalculator::Update(const float delta) MouseButtons12 &MouseButtons12::operator=(const MouseButtons12 &other) { - data = other.data; - return *this; + data = other.data; + return *this; } MouseButtons345 &MouseButtons345::operator=(const MouseButtons345 &other) { - data = other.data; - return *this; + data = other.data; + return *this; } MouseButtonsAll &MouseButtonsAll::operator=(const MouseButtonsAll &other) { - data = other.data; - return *this; + data = other.data; + return *this; } MouseButtons12S &MouseButtons12S::operator=(const MouseButtons12S &other) { - data = other.data; - return *this; + data = other.data; + return *this; } diff --git a/src/hardware/mouse/mouse_common.h b/src/hardware/mouse/mouse_common.h index ff9a7e0bb..a44779c84 100644 --- a/src/hardware/mouse/mouse_common.h +++ b/src/hardware/mouse/mouse_common.h @@ -29,44 +29,43 @@ class MouseShared { public: - bool active_bios = false; // true = BIOS has a registered callback - bool active_dos = false; // true = DOS driver has a functioning callback - bool active_vmm = false; // true = VMware-compatible driver is active + bool active_bios = false; // true = BIOS has a registered callback + bool active_dos = false; // true = DOS driver has a functioning callback + bool active_vmm = false; // true = VMware-compatible driver is active - bool dos_cb_running = false; // true = DOS callback is running + bool dos_cb_running = false; // true = DOS callback is running - // Readiness for initialization - bool ready_startup_sequence = false; - bool ready_config_mouse = false; - bool ready_config_sdl = false; + // Readiness for initialization + bool ready_startup_sequence = false; + bool ready_config_mouse = false; + bool ready_config_sdl = false; - bool started = false; + bool started = false; }; class MouseVideo { public: - bool fullscreen = true; + bool fullscreen = true; - uint16_t res_x = 640; // resolution to which guest image is scaled, - uint16_t res_y = 400; // excluding black borders + uint16_t res_x = 640; // resolution to which guest image is scaled, + uint16_t res_y = 400; // excluding black borders - uint16_t clip_x = 0; // clipping = size of black border (one side) - uint16_t clip_y = 0; + uint16_t clip_x = 0; // clipping = size of black border (one side) + uint16_t clip_y = 0; }; class MouseInfo { public: - std::vector<MouseInterfaceInfoEntry> interfaces = {}; - std::vector<MousePhysicalInfoEntry> physical = {}; + std::vector<MouseInterfaceInfoEntry> interfaces = {}; + std::vector<MousePhysicalInfoEntry> physical = {}; }; -extern MouseInfo mouse_info; // information which can be shared externally +extern MouseInfo mouse_info; // information which can be shared externally extern MouseShared mouse_shared; // shared internal information -extern MouseVideo mouse_video; // video information - resolution, clipping, etc. +extern MouseVideo mouse_video; // video information - resolution, clipping, etc. extern bool mouse_is_captured; - // *************************************************************************** // Common helper calculations // *************************************************************************** @@ -83,24 +82,22 @@ uint16_t MOUSE_ClampRateHz(const uint16_t rate_hz); class MouseSpeedCalculator final { public: + MouseSpeedCalculator(const float scaling); - MouseSpeedCalculator(const float scaling); - - float Get() const; - void Update(const float delta); + float Get() const; + void Update(const float delta); private: + MouseSpeedCalculator() = delete; + MouseSpeedCalculator(const MouseSpeedCalculator &) = delete; + MouseSpeedCalculator &operator=(const MouseSpeedCalculator &) = delete; - MouseSpeedCalculator() = delete; - MouseSpeedCalculator(const MouseSpeedCalculator &) = delete; - MouseSpeedCalculator &operator=(const MouseSpeedCalculator &) = delete; + uint32_t ticks_start = 0; - uint32_t ticks_start = 0; + const float scaling; - const float scaling; - - float distance = 0.0f; - float speed = 0.0f; + float distance = 0.0f; + float speed = 0.0f; }; // *************************************************************************** @@ -111,61 +108,61 @@ private: // (DOS driver) functions 0x03 / 0x05 / 0x06 and it's callback interface union MouseButtons12 { - // For storing left and right buttons only - uint8_t data = 0; + // For storing left and right buttons only + uint8_t data = 0; - bit_view<0, 1> left; - bit_view<1, 1> right; + bit_view<0, 1> left; + bit_view<1, 1> right; - MouseButtons12() : data(0) {} - MouseButtons12(const uint8_t data) : data(data) {} - MouseButtons12(const MouseButtons12 &other) : data(other.data) {} - MouseButtons12 &operator=(const MouseButtons12 &other); + MouseButtons12() : data(0) {} + MouseButtons12(const uint8_t data) : data(data) {} + MouseButtons12(const MouseButtons12 &other) : data(other.data) {} + MouseButtons12 &operator=(const MouseButtons12 &other); }; union MouseButtons345 { - // For storing middle and extra buttons - uint8_t data = 0; + // For storing middle and extra buttons + uint8_t data = 0; - bit_view<2, 1> middle; - bit_view<3, 1> extra_1; - bit_view<4, 1> extra_2; + bit_view<2, 1> middle; + bit_view<3, 1> extra_1; + bit_view<4, 1> extra_2; - MouseButtons345() : data(0) {} - MouseButtons345(const uint8_t data) : data(data) {} - MouseButtons345(const MouseButtons345 &other) : data(other.data) {} - MouseButtons345 &operator=(const MouseButtons345 &other); + MouseButtons345() : data(0) {} + MouseButtons345(const uint8_t data) : data(data) {} + MouseButtons345(const MouseButtons345 &other) : data(other.data) {} + MouseButtons345 &operator=(const MouseButtons345 &other); }; union MouseButtonsAll { - // For storing all 5 mouse buttons - uint8_t data = 0; - - bit_view<0, 1> left; - bit_view<1, 1> right; - bit_view<2, 1> middle; - bit_view<3, 1> extra_1; - bit_view<4, 1> extra_2; - - MouseButtonsAll() : data(0) {} - MouseButtonsAll(const uint8_t data) : data(data) {} - MouseButtonsAll(const MouseButtonsAll &other) : data(other.data) {} - MouseButtonsAll &operator=(const MouseButtonsAll &other); + // For storing all 5 mouse buttons + uint8_t data = 0; + + bit_view<0, 1> left; + bit_view<1, 1> right; + bit_view<2, 1> middle; + bit_view<3, 1> extra_1; + bit_view<4, 1> extra_2; + + MouseButtonsAll() : data(0) {} + MouseButtonsAll(const uint8_t data) : data(data) {} + MouseButtonsAll(const MouseButtonsAll &other) : data(other.data) {} + MouseButtonsAll &operator=(const MouseButtonsAll &other); }; union MouseButtons12S { - // To be used where buttons 3/4/5 are squished - // into a virtual middle button - uint8_t data = 0; - - bit_view<0, 1> left; - bit_view<1, 1> right; - bit_view<2, 1> middle; - - MouseButtons12S() : data(0) {} - MouseButtons12S(const uint8_t data) : data(data) {} - MouseButtons12S(const MouseButtons12S &other) : data(other.data) {} - MouseButtons12S &operator=(const MouseButtons12S &other); + // To be used where buttons 3/4/5 are squished + // into a virtual middle button + uint8_t data = 0; + + bit_view<0, 1> left; + bit_view<1, 1> right; + bit_view<2, 1> middle; + + MouseButtons12S() : data(0) {} + MouseButtons12S(const uint8_t data) : data(data) {} + MouseButtons12S(const MouseButtons12S &other) : data(other.data) {} + MouseButtons12S &operator=(const MouseButtons12S &other); }; // *************************************************************************** @@ -173,15 +170,14 @@ union MouseButtons12S { // *************************************************************************** struct MouseEvent { - bool request_dos = false; // if DOS mouse driver needs an event - bool request_ps2 = false; // if PS/2 mouse emulation needs an event + bool request_dos = false; // if DOS mouse driver needs an event + bool request_ps2 = false; // if PS/2 mouse emulation needs an event - bool dos_moved = false; - bool dos_button = false; - bool dos_wheel = false; + bool dos_moved = false; + bool dos_button = false; + bool dos_wheel = false; - MouseButtons12S dos_buttons = 0; + MouseButtons12S dos_buttons = 0; }; - #endif // DOSBOX_MOUSE_COMMON_H diff --git a/src/hardware/mouse/mouse_config.cpp b/src/hardware/mouse/mouse_config.cpp index 89cd4e3da..6f147beee 100644 --- a/src/hardware/mouse/mouse_config.cpp +++ b/src/hardware/mouse/mouse_config.cpp @@ -16,8 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "mouse_common.h" #include "mouse_config.h" +#include "mouse_common.h" #include "mouse_interfaces.h" #include "checks.h" @@ -31,7 +31,6 @@ CHECK_NARROWING(); - // TODO - IntelliMouse Explorer emulation is currently deactivated - there is // probably no way to test it. The IntelliMouse 3.0 software can use it, but // it seems to require physical PS/2 mouse registers to work correctly, @@ -39,307 +38,313 @@ CHECK_NARROWING(); // #define ENABLE_EXPLORER_MOUSE - -MouseConfig mouse_config; +MouseConfig mouse_config; MousePredefined mouse_predefined; static const std::vector<std::string> list_models_ps2 = { - "standard", - "intellimouse", + "standard", + "intellimouse", #ifdef ENABLE_EXPLORER_MOUSE - "explorer", + "explorer", #endif }; static const std::vector<std::string> list_models_com = { - "2button", - "3button", - "wheel", - "msm", - "2button+msm", - "3button+msm", - "wheel+msm", + "2button", + "3button", + "wheel", + "msm", + "2button+msm", + "3button+msm", + "wheel+msm", }; static const std::vector<uint16_t> list_rates = { -// Commented out values are probably not interesting -// for the end user as "boosted" sampling rate -// 10", // PS/2 mouse -// 20", // PS/2 mouse -// 30", // bus/InPort mouse - 40, // PS/2 mouse, approx. limit for 1200 baud serial mouse -// 50, // bus/InPort mouse - 60, // PS/2 mouse, used by Microsoft Mouse Driver 8.20 - 80, // PS/2 mouse, approx. limit for 2400 baud serial mouse - 100, // PS/2 mouse, bus/InPort mouse, used by CuteMouse 2.1b4 - 125, // USB mouse (basic, non-gaming), Bluetooth mouse - 160, // approx. limit for 4800 baud serial mouse - 200, // PS/2 mouse, bus/InPort mouse - 250, // USB mouse (gaming) - 330, // approx. limit for 9600 baud serial mouse - 500, // USB mouse (gaming) - -// Todays gaming USB mice are capable of even higher sampling -// rates (like 1000 Hz), but such values are way higher than -// anything DOS games were designed for; most likely such rates -// would only result in emulation slowdowns and compatibility -// issues. + // Commented out values are probably not interesting + // for the end user as "boosted" sampling rate + // 10", // PS/2 mouse + // 20", // PS/2 mouse + // 30", // bus/InPort mouse + 40, // PS/2 mouse, approx. limit for 1200 baud serial mouse + // 50, // bus/InPort mouse + 60, // PS/2 mouse, used by Microsoft Mouse Driver 8.20 + 80, // PS/2 mouse, approx. limit for 2400 baud serial mouse + 100, // PS/2 mouse, bus/InPort mouse, used by CuteMouse 2.1b4 + 125, // USB mouse (basic, non-gaming), Bluetooth mouse + 160, // approx. limit for 4800 baud serial mouse + 200, // PS/2 mouse, bus/InPort mouse + 250, // USB mouse (gaming) + 330, // approx. limit for 9600 baud serial mouse + 500, // USB mouse (gaming) + + // Todays gaming USB mice are capable of even higher sampling + // rates (like 1000 Hz), but such values are way higher than + // anything DOS games were designed for; most likely such rates + // would only result in emulation slowdowns and compatibility + // issues. }; bool MouseConfig::ParseSerialModel(const std::string &model_str, - MouseModelCOM &model, - bool &auto_msm) + MouseModelCOM &model, bool &auto_msm) { - if (model_str == list_models_com[0]) { - model = MouseModelCOM::Microsoft; - auto_msm = false; - return true; - } else if (model_str == list_models_com[1]) { - model = MouseModelCOM::Logitech; - auto_msm = false; - return true; - } else if (model_str == list_models_com[2]) { - model = MouseModelCOM::Wheel; - auto_msm = false; - return true; - } else if (model_str == list_models_com[3]) { - model = MouseModelCOM::MouseSystems; - auto_msm = false; - return true; - } else if (model_str == list_models_com[4]) { - model = MouseModelCOM::Microsoft; - auto_msm = true; - return true; - } else if (model_str == list_models_com[5]) { - model = MouseModelCOM::Logitech; - auto_msm = true; - return true; - } else if (model_str == list_models_com[6]) { - model = MouseModelCOM::Wheel; - auto_msm = true; - return true; - } - - return false; + if (model_str == list_models_com[0]) { + model = MouseModelCOM::Microsoft; + auto_msm = false; + return true; + } else if (model_str == list_models_com[1]) { + model = MouseModelCOM::Logitech; + auto_msm = false; + return true; + } else if (model_str == list_models_com[2]) { + model = MouseModelCOM::Wheel; + auto_msm = false; + return true; + } else if (model_str == list_models_com[3]) { + model = MouseModelCOM::MouseSystems; + auto_msm = false; + return true; + } else if (model_str == list_models_com[4]) { + model = MouseModelCOM::Microsoft; + auto_msm = true; + return true; + } else if (model_str == list_models_com[5]) { + model = MouseModelCOM::Logitech; + auto_msm = true; + return true; + } else if (model_str == list_models_com[6]) { + model = MouseModelCOM::Wheel; + auto_msm = true; + return true; + } + + return false; } const std::vector<uint16_t> &MouseConfig::GetValidMinRateList() { - return list_rates; + return list_rates; } static void config_read(Section *section) { - assert(section); - const Section_prop *conf = dynamic_cast<Section_prop *>(section); - assert(conf); - if (!conf) - return; - - // Settings changeable during runtime - - mouse_config.dos_immediate = conf->Get_bool("dos_mouse_immediate"); - mouse_config.raw_input = conf->Get_bool("mouse_raw_input"); - GFX_SetMouseRawInput(mouse_config.raw_input); - - // Settings below should be read only once - - if (mouse_shared.ready_config_mouse) { - MOUSE_NotifyStateChanged(); - return; - } - - // Default mouse sensitivity - - auto sensitivity_from_str = [](const std::string &str) { - const int32_t base_value = 50; - int32_t ret_val = base_value; // default - bool invalid_input = false; - - if (str.find('.') != std::string::npos) { - // Parameter supplied in a form of floating point - float tmp_float = 1.0f; - try { - tmp_float = std::stof(str); - } catch (...) { - invalid_input = true; - tmp_float = 1.0f; - } - // We need 'tmp_float' to be positive for further calculations - if (tmp_float < 0.0f) { - tmp_float = -tmp_float; - ret_val = -1; - } else if (tmp_float > 0.0f) - ret_val = 1; - else - ret_val = 0; - // Now calculate user steps as a logarithm - if (ret_val != 0) { - tmp_float = std::log(tmp_float) / std::log(2.0f); // log tmp_float at base 2.0f - tmp_float *= mouse_predefined.sensitivity_double_steps; - tmp_float += static_cast<float>(base_value); - const auto tmp_rounded = std::lround(std::max(tmp_float, 1.0f)); - ret_val = clamp_to_int8(ret_val * tmp_rounded); - } - } else if (!str.empty()) { - // Parameter supplied in a form of integer - try { - ret_val = std::stoi(str); - } catch (...) { - invalid_input = true; - ret_val = base_value; - } - } - - if (invalid_input) - LOG_ERR("MOUSE: Invalid sensitivity value"); - - const auto min = clamp_to_int8(-mouse_predefined.sensitivity_user_max); - const auto max = clamp_to_int8(mouse_predefined.sensitivity_user_max); - return std::clamp(clamp_to_int8(ret_val), min, max); - }; - - PropMultiVal *prop_multi = conf->GetMultiVal("mouse_sensitivity"); - const std::string xsens = prop_multi->GetSection()->Get_string("xsens"); - const std::string ysens = prop_multi->GetSection()->Get_string("ysens"); - - mouse_config.sensitivity_x = sensitivity_from_str(xsens); - if (ysens.empty()) - mouse_config.sensitivity_y = mouse_config.sensitivity_x; - else - mouse_config.sensitivity_y = sensitivity_from_str(ysens); - - // DOS driver configuration - - mouse_config.dos_driver = conf->Get_bool("dos_mouse_driver"); - - // PS/2 AUX port mouse configuration - - std::string prop_str = conf->Get_string("ps2_mouse_model"); - if (prop_str == list_models_ps2[0]) - mouse_config.model_ps2 = MouseModelPS2::Standard; - if (prop_str == list_models_ps2[1]) - mouse_config.model_ps2 = MouseModelPS2::IntelliMouse; + assert(section); + const Section_prop *conf = dynamic_cast<Section_prop *>(section); + assert(conf); + if (!conf) + return; + + // Settings changeable during runtime + + mouse_config.dos_immediate = conf->Get_bool("dos_mouse_immediate"); + mouse_config.raw_input = conf->Get_bool("mouse_raw_input"); + GFX_SetMouseRawInput(mouse_config.raw_input); + + // Settings below should be read only once + + if (mouse_shared.ready_config_mouse) { + MOUSE_NotifyStateChanged(); + return; + } + + // Default mouse sensitivity + + auto sensitivity_from_str = [](const std::string &str) { + const int32_t base_value = 50; + int32_t ret_val = base_value; // default + bool invalid_input = false; + + if (str.find('.') != std::string::npos) { + // Parameter supplied in a form of floating point + float tmp_float = 1.0f; + try { + tmp_float = std::stof(str); + } catch (...) { + invalid_input = true; + tmp_float = 1.0f; + } + // We need 'tmp_float' to be positive for further calculations + if (tmp_float < 0.0f) { + tmp_float = -tmp_float; + ret_val = -1; + } else if (tmp_float > 0.0f) + ret_val = 1; + else + ret_val = 0; + // Now calculate user steps as a logarithm + if (ret_val != 0) { + tmp_float = std::log(tmp_float) / std::log(2.0f); + tmp_float *= mouse_predefined.sensitivity_double_steps; + tmp_float += static_cast<float>(base_value); + const auto tmp_rounded = std::lround( + std::max(tmp_float, 1.0f)); + ret_val = clamp_to_int8(ret_val * tmp_rounded); + } + } else if (!str.empty()) { + // Parameter supplied in a form of integer + try { + ret_val = std::stoi(str); + } catch (...) { + invalid_input = true; + ret_val = base_value; + } + } + + if (invalid_input) + LOG_ERR("MOUSE: Invalid sensitivity value"); + + const auto min = clamp_to_int8(-mouse_predefined.sensitivity_user_max); + const auto max = clamp_to_int8(mouse_predefined.sensitivity_user_max); + return std::clamp(clamp_to_int8(ret_val), min, max); + }; + + PropMultiVal *prop_multi = conf->GetMultiVal("mouse_sensitivity"); + const std::string xsens = prop_multi->GetSection()->Get_string("xsens"); + const std::string ysens = prop_multi->GetSection()->Get_string("ysens"); + + mouse_config.sensitivity_x = sensitivity_from_str(xsens); + if (ysens.empty()) + mouse_config.sensitivity_y = mouse_config.sensitivity_x; + else + mouse_config.sensitivity_y = sensitivity_from_str(ysens); + + // DOS driver configuration + + mouse_config.dos_driver = conf->Get_bool("dos_mouse_driver"); + + // PS/2 AUX port mouse configuration + + std::string prop_str = conf->Get_string("ps2_mouse_model"); + if (prop_str == list_models_ps2[0]) + mouse_config.model_ps2 = MouseModelPS2::Standard; + if (prop_str == list_models_ps2[1]) + mouse_config.model_ps2 = MouseModelPS2::IntelliMouse; #ifdef ENABLE_EXPLORER_MOUSE - if (prop_str == list_models_ps2[2]) - mouse_config.model_ps2 = MouseModelPS2::Explorer; + if (prop_str == list_models_ps2[2]) + mouse_config.model_ps2 = MouseModelPS2::Explorer; #endif - // Serial (COM port) mice configuration - - auto set_model_com = [](const std::string &model_str, - MouseModelCOM& model_var, - bool &model_auto) { - if (model_str == list_models_com[0] || model_str == list_models_com[4]) - model_var = MouseModelCOM::Microsoft; - if (model_str == list_models_com[1] || model_str == list_models_com[5]) - model_var = MouseModelCOM::Logitech; - if (model_str == list_models_com[2] || model_str == list_models_com[6]) - model_var = MouseModelCOM::Wheel; - if (model_str == list_models_com[3]) - model_var = MouseModelCOM::MouseSystems; - - if (model_str == list_models_com[4] || - model_str == list_models_com[5] || - model_str == list_models_com[6]) - model_auto = true; - else - model_auto = false; - }; - - prop_str = conf->Get_string("com_mouse_model"); - set_model_com(prop_str, - mouse_config.model_com, - mouse_config.model_com_auto_msm); - - // Start mouse emulation if ready - mouse_shared.ready_config_mouse = true; - MOUSE_Startup(); + // Serial (COM port) mice configuration + + auto set_model_com = [](const std::string &model_str, + MouseModelCOM &model_var, + bool &model_auto) { + if (model_str == list_models_com[0] || + model_str == list_models_com[4]) + model_var = MouseModelCOM::Microsoft; + if (model_str == list_models_com[1] || + model_str == list_models_com[5]) + model_var = MouseModelCOM::Logitech; + if (model_str == list_models_com[2] || + model_str == list_models_com[6]) + model_var = MouseModelCOM::Wheel; + if (model_str == list_models_com[3]) + model_var = MouseModelCOM::MouseSystems; + + if (model_str == list_models_com[4] || + model_str == list_models_com[5] || + model_str == list_models_com[6]) + model_auto = true; + else + model_auto = false; + }; + + prop_str = conf->Get_string("com_mouse_model"); + set_model_com(prop_str, mouse_config.model_com, mouse_config.model_com_auto_msm); + + // Start mouse emulation if ready + mouse_shared.ready_config_mouse = true; + MOUSE_Startup(); } static void config_init(Section_prop &secprop) { - constexpr auto always = Property::Changeable::Always; - constexpr auto only_at_start = Property::Changeable::OnlyAtStart; - - Prop_bool *prop_bool = nullptr; - // XXX Prop_int *prop_int = nullptr; - Prop_string *prop_str = nullptr; - PropMultiVal *prop_multi = nullptr; - - // General configuration - - prop_multi = secprop.AddMultiVal("mouse_sensitivity", only_at_start, ","); - prop_multi->Set_help("Default mouse sensitivity.\n" - "Integer values work exponentially, add 10 to double the effect.\n" - "Alternatively, put 1.0 for base sensitivity, 1.5 do double sensitivity, etc.\n" - "Negative values reverse mouse direction, 0 disables the movement completely.\n" - "The optional second parameter specifies vertical sensitivity (e.g. 1.5,3.0).\n" - "Setting can be adjusted in runtime (also per mouse interface) using internal\n" - "MOUSECTL.COM tool, available on drive Z:."); - prop_multi->SetValue("1.0"); - prop_str = prop_multi->GetSection()->Add_string("xsens", only_at_start, "1.0"); - prop_str = prop_multi->GetSection()->Add_string("ysens", only_at_start, "1.0"); - - prop_bool = secprop.Add_bool("mouse_raw_input", always, true); - prop_bool->Set_help("Enable to bypass your operating system's mouse acceleration and sensitivity\n" - "settings. Works in fullscreen or when the mouse is captured in window mode."); - - // DOS driver configuration - - prop_bool = secprop.Add_bool("dos_mouse_driver", only_at_start, true); - assert(prop_bool); - prop_bool->Set_help("Enable built-in DOS mouse driver.\n" - "Notes:\n" - " Disable if you intend to use original MOUSE.COM driver in emulated DOS.\n" - " When guest OS is booted, built-in driver gets disabled automatically."); - - prop_bool = secprop.Add_bool("dos_mouse_immediate", always, false); - assert(prop_bool); - prop_bool->Set_help("Updates mouse movement counters immediately, without waiting for interrupt.\n" - "May improve gameplay, especially in fast paced games (arcade, FPS, etc.) - as\n" - "for some games it effectively boosts the mouse sampling rate to 1000 Hz, without\n" - "increasing interrupt overhead.\n" - "Might cause compatibility issues. List of known incompatible games:\n" - " - Ultima Underworld: The Stygian Abyss\n" - " - Ultima Underworld II: Labyrinth of Worlds\n" - "Please file a bug with the project if you find another game that fails when\n" - "this is enabled, we will update this list."); - - // Physical mice configuration - - prop_str = secprop.Add_string("ps2_mouse_model", only_at_start, "intellimouse"); - assert(prop_str); - prop_str->Set_values(list_models_ps2); - prop_str->Set_help("PS/2 AUX port mouse model:\n" - // TODO - Add option "none" - " standard: 3 buttons, standard PS/2 mouse.\n" - " intellimouse: 3 buttons + wheel, Microsoft IntelliMouse.\n" + constexpr auto always = Property::Changeable::Always; + constexpr auto only_at_start = Property::Changeable::OnlyAtStart; + + Prop_bool *prop_bool = nullptr; + // XXX Prop_int *prop_int = nullptr; + Prop_string *prop_str = nullptr; + PropMultiVal *prop_multi = nullptr; + + // General configuration + + prop_multi = secprop.AddMultiVal("mouse_sensitivity", only_at_start, ","); + prop_multi->Set_help( + "Default mouse sensitivity.\n" + "Integer values work exponentially, add 10 to double the effect.\n" + "Alternatively, put 1.0 for base sensitivity, 1.5 do double sensitivity, etc.\n" + "Negative values reverse mouse direction, 0 disables the movement completely.\n" + "The optional second parameter specifies vertical sensitivity (e.g. 1.5,3.0).\n" + "Setting can be adjusted in runtime (also per mouse interface) using internal\n" + "MOUSECTL.COM tool, available on drive Z:."); + prop_multi->SetValue("1.0"); + prop_str = prop_multi->GetSection()->Add_string("xsens", only_at_start, "1.0"); + prop_str = prop_multi->GetSection()->Add_string("ysens", only_at_start, "1.0"); + + prop_bool = secprop.Add_bool("mouse_raw_input", always, true); + prop_bool->Set_help( + "Enable to bypass your operating system's mouse acceleration and sensitivity\n" + "settings. Works in fullscreen or when the mouse is captured in window mode."); + + // DOS driver configuration + + prop_bool = secprop.Add_bool("dos_mouse_driver", only_at_start, true); + assert(prop_bool); + prop_bool->Set_help( + "Enable built-in DOS mouse driver.\n" + "Notes:\n" + " Disable if you intend to use original MOUSE.COM driver in emulated DOS.\n" + " When guest OS is booted, built-in driver gets disabled automatically."); + + prop_bool = secprop.Add_bool("dos_mouse_immediate", always, false); + assert(prop_bool); + prop_bool->Set_help( + "Updates mouse movement counters immediately, without waiting for interrupt.\n" + "May improve gameplay, especially in fast paced games (arcade, FPS, etc.) - as\n" + "for some games it effectively boosts the mouse sampling rate to 1000 Hz, without\n" + "increasing interrupt overhead.\n" + "Might cause compatibility issues. List of known incompatible games:\n" + " - Ultima Underworld: The Stygian Abyss\n" + " - Ultima Underworld II: Labyrinth of Worlds\n" + "Please file a bug with the project if you find another game that fails when\n" + "this is enabled, we will update this list."); + + // Physical mice configuration + + prop_str = secprop.Add_string("ps2_mouse_model", only_at_start, "intellimouse"); + assert(prop_str); + prop_str->Set_values(list_models_ps2); + prop_str->Set_help( + "PS/2 AUX port mouse model:\n" + // TODO - Add option "none" + " standard: 3 buttons, standard PS/2 mouse.\n" + " intellimouse: 3 buttons + wheel, Microsoft IntelliMouse.\n" #ifdef ENABLE_EXPLORER_MOUSE - " explorer: 5 buttons + wheel, Microsoft IntelliMouse Explorer.\n" + " explorer: 5 buttons + wheel, Microsoft IntelliMouse Explorer.\n" #endif - "Default: intellimouse"); - - prop_str = secprop.Add_string("com_mouse_model", only_at_start, "wheel+msm"); - assert(prop_str); - prop_str->Set_values(list_models_com); - prop_str->Set_help("COM (serial) port default mouse model:\n" - " 2button: 2 buttons, Microsoft mouse.\n" - " 3button: 3 buttons, Logitech mouse, mostly compatible with Microsoft mouse.\n" - " wheel: 3 buttons + wheel, mostly compatible with Microsoft mouse.\n" - " msm: 3 buttons, Mouse Systems mouse, NOT COMPATIBLE with Microsoft mouse.\n" - " 2button+msm: Automatic choice between 2button and msm.\n" - " 3button+msm: Automatic choice between 3button and msm.\n" - " wheel+msm: Automatic choice between wheel and msm.\n" - "Default: wheel+msm\n" - "Notes:\n" - " Go to [serial] section to enable/disable COM port mice."); + "Default: intellimouse"); + + prop_str = secprop.Add_string("com_mouse_model", only_at_start, "wheel+msm"); + assert(prop_str); + prop_str->Set_values(list_models_com); + prop_str->Set_help( + "COM (serial) port default mouse model:\n" + " 2button: 2 buttons, Microsoft mouse.\n" + " 3button: 3 buttons, Logitech mouse, mostly compatible with Microsoft mouse.\n" + " wheel: 3 buttons + wheel, mostly compatible with Microsoft mouse.\n" + " msm: 3 buttons, Mouse Systems mouse, NOT COMPATIBLE with Microsoft mouse.\n" + " 2button+msm: Automatic choice between 2button and msm.\n" + " 3button+msm: Automatic choice between 3button and msm.\n" + " wheel+msm: Automatic choice between wheel and msm.\n" + "Default: wheel+msm\n" + "Notes:\n" + " Go to [serial] section to enable/disable COM port mice."); } void MOUSE_AddConfigSection(const config_ptr_t &conf) { - assert(conf); - Section_prop *sec = conf->AddSection_prop("mouse", &config_read, true); - assert(sec); - config_init(*sec); + assert(conf); + Section_prop *sec = conf->AddSection_prop("mouse", &config_read, true); + assert(sec); + config_init(*sec); } diff --git a/src/hardware/mouse/mouse_config.h b/src/hardware/mouse/mouse_config.h index 65570622c..35ca040b0 100644 --- a/src/hardware/mouse/mouse_config.h +++ b/src/hardware/mouse/mouse_config.h @@ -23,89 +23,87 @@ #include "mouse.h" - // *************************************************************************** // Predefined calibration // *************************************************************************** struct MousePredefined { - // Mouse equalization for consistent user experience - please adjust - // values so that on full screen, with RAW mouse input, the mouse feel - // is similar to Windows 3.11 for Workgroups with PS/2 mouse driver - // and default settings - const float sensitivity_dos = 0.6f; - const float sensitivity_ps2 = 0.6f; - const float sensitivity_vmm = 1.8f; - const float sensitivity_com = 0.6f; - // Constants to move 'intersection point' for the acceleration curve - // Requires raw mouse input, otherwise there is no effect - // Larger values = higher mouse acceleration - const float acceleration_dos = 1.0f; - const float acceleration_vmm = 1.0f; - - // Maximum allowe user sensitivity value - const int8_t sensitivity_user_max = 99; - // How many user steps causes sensitivity to double - // (sensitivity works exponentially) - const float sensitivity_double_steps = 10.0f; + // Mouse equalization for consistent user experience - please adjust + // values so that on full screen, with RAW mouse input, the mouse feel + // is similar to Windows 3.11 for Workgroups with PS/2 mouse driver + // and default settings + const float sensitivity_dos = 0.6f; + const float sensitivity_ps2 = 0.6f; + const float sensitivity_vmm = 1.8f; + const float sensitivity_com = 0.6f; + // Constants to move 'intersection point' for the acceleration curve + // Requires raw mouse input, otherwise there is no effect + // Larger values = higher mouse acceleration + const float acceleration_dos = 1.0f; + const float acceleration_vmm = 1.0f; + + // Maximum allowe user sensitivity value + const int8_t sensitivity_user_max = 99; + // How many user steps causes sensitivity to double + // (sensitivity works exponentially) + const float sensitivity_double_steps = 10.0f; }; extern MousePredefined mouse_predefined; - // *************************************************************************** // Configuration file content // *************************************************************************** enum class MouseModelPS2 : uint8_t { - // Values must match PS/2 protocol IDs - Standard = 0x00, - IntelliMouse = 0x03, - Explorer = 0x04, + // Values must match PS/2 protocol IDs + Standard = 0x00, + IntelliMouse = 0x03, + Explorer = 0x04, }; enum class MouseModelCOM : uint8_t { - NoMouse, // dummy value or no mouse - Microsoft, - Logitech, - Wheel, - MouseSystems + NoMouse, // dummy value or no mouse + Microsoft, + Logitech, + Wheel, + MouseSystems }; enum class MouseModelBus : uint8_t { - NoMouse, - Bus, - InPort, + NoMouse, + Bus, + InPort, }; struct MouseConfig { + // From [sdl] section - // From [sdl] section + bool no_mouse = false; // true = NoMouse selected in GUI + bool seamless = false; // true = seamless mouse integration - bool no_mouse = false; // true = NoMouse selected in GUI - bool seamless = false; // true = seamless mouse integration + // From [mouse] section - // From [mouse] section + int8_t sensitivity_x = 50; // default sensitivity values + int8_t sensitivity_y = 50; + bool raw_input = false; // true = relative input is raw data - int8_t sensitivity_x = 50; // default sensitivity values - int8_t sensitivity_y = 50; - bool raw_input = false; // true = relative input is raw data + bool dos_driver = false; // whether DOS virtual mouse driver should be + // enabled + bool dos_immediate = false; - bool dos_driver = false; // whether DOS virtual mouse driver should be enabled - bool dos_immediate = false; + MouseModelPS2 model_ps2 = MouseModelPS2::Standard; - MouseModelPS2 model_ps2 = MouseModelPS2::Standard; + MouseModelCOM model_com = MouseModelCOM::Wheel; + bool model_com_auto_msm = true; - MouseModelCOM model_com = MouseModelCOM::Wheel; - bool model_com_auto_msm = true; + // Helper functions for external modules - // Helper functions for external modules - - static const std::vector<uint16_t> &GetValidMinRateList(); - static bool ParseSerialModel(const std::string &model_str, MouseModelCOM &model, bool &auto_msm); + static const std::vector<uint16_t> &GetValidMinRateList(); + static bool ParseSerialModel(const std::string &model_str, + MouseModelCOM &model, bool &auto_msm); }; extern MouseConfig mouse_config; - #endif // DOSBOX_MOUSE_CONFIG_H diff --git a/src/hardware/mouse/mouse_interfaces.cpp b/src/hardware/mouse/mouse_interfaces.cpp index 1e4f4bbe3..379e2554b 100644 --- a/src/hardware/mouse/mouse_interfaces.cpp +++ b/src/hardware/mouse/mouse_interfaces.cpp @@ -16,10 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "mouse_interfaces.h" #include "mouse.h" #include "mouse_common.h" #include "mouse_config.h" -#include "mouse_interfaces.h" #include "mouse_manymouse.h" #include "mouse_queue.h" @@ -27,223 +27,193 @@ CHECK_NARROWING(); - std::vector<MouseInterface *> mouse_interfaces = {}; - // *************************************************************************** // Mouse interface information facade // *************************************************************************** -MouseInterfaceInfoEntry::MouseInterfaceInfoEntry(const MouseInterfaceId interface_id) : - idx(static_cast<uint8_t>(interface_id)) -{ -} +MouseInterfaceInfoEntry::MouseInterfaceInfoEntry(const MouseInterfaceId interface_id) + : idx(static_cast<uint8_t>(interface_id)) +{} const MouseInterface &MouseInterfaceInfoEntry::Interface() const { - return *mouse_interfaces[idx]; + return *mouse_interfaces[idx]; } const MousePhysical &MouseInterfaceInfoEntry::MappedPhysical() const { - const auto idx = Interface().GetMappedDeviceIdx(); - return ManyMouseGlue::GetInstance().physical_devices[idx]; + const auto idx = Interface().GetMappedDeviceIdx(); + return ManyMouseGlue::GetInstance().physical_devices[idx]; } bool MouseInterfaceInfoEntry::IsEmulated() const { - return Interface().IsEmulated(); + return Interface().IsEmulated(); } bool MouseInterfaceInfoEntry::IsMapped() const { - return Interface().IsMapped(); + return Interface().IsMapped(); } bool MouseInterfaceInfoEntry::IsMapped(const uint8_t device_idx) const { - return Interface().IsMapped(device_idx); + return Interface().IsMapped(device_idx); } bool MouseInterfaceInfoEntry::IsMappedDeviceDisconnected() const { - if (!IsMapped()) - return false; + if (!IsMapped()) + return false; - return MappedPhysical().IsDisconnected(); + return MappedPhysical().IsDisconnected(); } MouseInterfaceId MouseInterfaceInfoEntry::GetInterfaceId() const { - return Interface().GetInterfaceId(); + return Interface().GetInterfaceId(); } MouseMapStatus MouseInterfaceInfoEntry::GetMapStatus() const { - return Interface().GetMapStatus(); + return Interface().GetMapStatus(); } const std::string &MouseInterfaceInfoEntry::GetMappedDeviceName() const { - static const std::string empty = ""; - if (!IsMapped()) - return empty; + static const std::string empty = ""; + if (!IsMapped()) + return empty; - return MappedPhysical().GetName(); + return MappedPhysical().GetName(); } int8_t MouseInterfaceInfoEntry::GetSensitivityX() const { - return Interface().GetSensitivityX(); + return Interface().GetSensitivityX(); } int8_t MouseInterfaceInfoEntry::GetSensitivityY() const { - return Interface().GetSensitivityY(); + return Interface().GetSensitivityY(); } uint16_t MouseInterfaceInfoEntry::GetMinRate() const { - return Interface().GetMinRate(); + return Interface().GetMinRate(); } uint16_t MouseInterfaceInfoEntry::GetRate() const { - return Interface().GetRate(); + return Interface().GetRate(); } // *************************************************************************** // Physical mouse information facade // *************************************************************************** -MousePhysicalInfoEntry::MousePhysicalInfoEntry(const uint8_t idx) : - idx(idx) -{ -} +MousePhysicalInfoEntry::MousePhysicalInfoEntry(const uint8_t idx) : idx(idx) {} const MousePhysical &MousePhysicalInfoEntry::Physical() const { - return ManyMouseGlue::GetInstance().physical_devices[idx]; + return ManyMouseGlue::GetInstance().physical_devices[idx]; } bool MousePhysicalInfoEntry::IsMapped() const { - return Physical().IsMapped(); + return Physical().IsMapped(); } bool MousePhysicalInfoEntry::IsDeviceDisconnected() const { - return Physical().IsDisconnected(); + return Physical().IsDisconnected(); } const std::string &MousePhysicalInfoEntry::GetDeviceName() const { - return Physical().GetName(); + return Physical().GetName(); } // *************************************************************************** // Concrete interfaces - declarations // *************************************************************************** -class InterfaceDos final : public MouseInterface -{ +class InterfaceDos final : public MouseInterface { public: + void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) override; + void NotifyButton(MouseEvent &ev, const uint8_t idx, + const bool pressed) override; + void NotifyWheel(MouseEvent &ev, const int16_t w_rel) override; - void NotifyMoved(MouseEvent &ev, - const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) override; - void NotifyButton(MouseEvent &ev, - const uint8_t idx, - const bool pressed) override; - void NotifyWheel(MouseEvent &ev, - const int16_t w_rel) override; - - void NotifyBooting() override; + void NotifyBooting() override; private: + friend class MouseInterface; - friend class MouseInterface; - - InterfaceDos(); - ~InterfaceDos() = default; - InterfaceDos(const InterfaceDos &) = delete; - InterfaceDos &operator=(const InterfaceDos &) = delete; + InterfaceDos(); + ~InterfaceDos() = default; + InterfaceDos(const InterfaceDos &) = delete; + InterfaceDos &operator=(const InterfaceDos &) = delete; - void Init() override; + void Init() override; - void UpdateRawMapped() override; - void UpdateMinRate() override; - void UpdateRate() override; + void UpdateRawMapped() override; + void UpdateMinRate() override; + void UpdateRate() override; }; -class InterfacePS2 final : public MouseInterface -{ +class InterfacePS2 final : public MouseInterface { public: - - void NotifyMoved(MouseEvent &ev, - const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) override; - void NotifyButton(MouseEvent &ev, - const uint8_t idx, - const bool pressed) override; - void NotifyWheel(MouseEvent &ev, - const int16_t w_rel) override; + void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) override; + void NotifyButton(MouseEvent &ev, const uint8_t idx, + const bool pressed) override; + void NotifyWheel(MouseEvent &ev, const int16_t w_rel) override; private: + friend class MouseInterface; - friend class MouseInterface; - - InterfacePS2(); - ~InterfacePS2() = default; - InterfacePS2(const InterfacePS2 &other) = delete; - InterfacePS2 &operator=(const InterfacePS2 &other) = delete; + InterfacePS2(); + ~InterfacePS2() = default; + InterfacePS2(const InterfacePS2 &other) = delete; + InterfacePS2 &operator=(const InterfacePS2 &other) = delete; - void Init() override; + void Init() override; - void UpdateRawMapped() override; - void UpdateSensitivity() override; - void UpdateRate() override; + void UpdateRawMapped() override; + void UpdateSensitivity() override; + void UpdateRate() override; - float sensitivity_coeff_vmm_x = 1.0f; // cached sensitivity coefficient - float sensitivity_coeff_vmm_y = 1.0f; + float sensitivity_coeff_vmm_x = 1.0f; // cached sensitivity coefficient + float sensitivity_coeff_vmm_y = 1.0f; }; -class InterfaceCOM final : public MouseInterface -{ +class InterfaceCOM final : public MouseInterface { public: + void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) override; + void NotifyButton(MouseEvent &ev, const uint8_t idx, + const bool pressed) override; + void NotifyWheel(MouseEvent &ev, const int16_t w_rel) override; - void NotifyMoved(MouseEvent &ev, - const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) override; - void NotifyButton(MouseEvent &ev, - const uint8_t idx, - const bool pressed) override; - void NotifyWheel(MouseEvent &ev, - const int16_t w_rel) override; + void UpdateRate() override; - void UpdateRate() override; - - void RegisterListener(CSerialMouse &listener_object) override; - void UnRegisterListener() override; + void RegisterListener(CSerialMouse &listener_object) override; + void UnRegisterListener() override; private: + friend class MouseInterface; - friend class MouseInterface; - - InterfaceCOM(const uint8_t port_id); - InterfaceCOM() = delete; - ~InterfaceCOM() = default; - InterfaceCOM(const InterfaceCOM &) = delete; - InterfaceCOM &operator=(const InterfaceCOM &) = delete; + InterfaceCOM(const uint8_t port_id); + InterfaceCOM() = delete; + ~InterfaceCOM() = default; + InterfaceCOM(const InterfaceCOM &) = delete; + InterfaceCOM &operator=(const InterfaceCOM &) = delete; - CSerialMouse *listener = nullptr; + CSerialMouse *listener = nullptr; }; // *************************************************************************** @@ -252,636 +222,605 @@ private: void MouseInterface::InitAllInstances() { - if (!mouse_interfaces.empty()) - return; // already initialized - - const auto i_first = static_cast<uint8_t>(MouseInterfaceId::First); - const auto i_last = static_cast<uint8_t>(MouseInterfaceId::Last); - const auto i_com1 = static_cast<uint8_t>(MouseInterfaceId::COM1); - - for (uint8_t i = i_first; i <= i_last; i++) - switch (static_cast<MouseInterfaceId>(i)) { - case MouseInterfaceId::DOS: - mouse_interfaces.emplace_back(new InterfaceDos()); - break; - case MouseInterfaceId::PS2: - mouse_interfaces.emplace_back(new InterfacePS2()); - break; - case MouseInterfaceId::COM1: - case MouseInterfaceId::COM2: - case MouseInterfaceId::COM3: - case MouseInterfaceId::COM4: - mouse_interfaces.emplace_back(new InterfaceCOM(static_cast<uint8_t>(i - i_com1))); - break; - default: - assert(false); - break; - } - - for (auto interface : mouse_interfaces) - interface->Init(); + if (!mouse_interfaces.empty()) + return; // already initialized + + const auto i_first = static_cast<uint8_t>(MouseInterfaceId::First); + const auto i_last = static_cast<uint8_t>(MouseInterfaceId::Last); + const auto i_com1 = static_cast<uint8_t>(MouseInterfaceId::COM1); + + for (uint8_t i = i_first; i <= i_last; i++) + switch (static_cast<MouseInterfaceId>(i)) { + case MouseInterfaceId::DOS: + mouse_interfaces.emplace_back(new InterfaceDos()); + break; + case MouseInterfaceId::PS2: + mouse_interfaces.emplace_back(new InterfacePS2()); + break; + case MouseInterfaceId::COM1: + case MouseInterfaceId::COM2: + case MouseInterfaceId::COM3: + case MouseInterfaceId::COM4: + mouse_interfaces.emplace_back(new InterfaceCOM( + static_cast<uint8_t>(i - i_com1))); + break; + default: assert(false); break; + } + + for (auto interface : mouse_interfaces) + interface->Init(); } MouseInterface *MouseInterface::Get(const MouseInterfaceId interface_id) { - const auto idx = static_cast<size_t>(interface_id); - if (idx < mouse_interfaces.size()) - return mouse_interfaces[idx]; + const auto idx = static_cast<size_t>(interface_id); + if (idx < mouse_interfaces.size()) + return mouse_interfaces[idx]; - assert(interface_id == MouseInterfaceId::None); - return nullptr; + assert(interface_id == MouseInterfaceId::None); + return nullptr; } MouseInterface *MouseInterface::GetDOS() { - const auto idx = static_cast<uint8_t>(MouseInterfaceId::DOS); - return MouseInterface::Get(static_cast<MouseInterfaceId>(idx)); + const auto idx = static_cast<uint8_t>(MouseInterfaceId::DOS); + return MouseInterface::Get(static_cast<MouseInterfaceId>(idx)); } MouseInterface *MouseInterface::GetPS2() { - const auto idx = static_cast<uint8_t>(MouseInterfaceId::PS2); - return MouseInterface::Get(static_cast<MouseInterfaceId>(idx)); + const auto idx = static_cast<uint8_t>(MouseInterfaceId::PS2); + return MouseInterface::Get(static_cast<MouseInterfaceId>(idx)); } MouseInterface *MouseInterface::GetSerial(const uint8_t port_id) { - if (port_id < SERIAL_MAX_PORTS) { - const auto idx = static_cast<uint8_t>(MouseInterfaceId::COM1) + port_id; - return MouseInterface::Get(static_cast<MouseInterfaceId>(idx)); - } + if (port_id < SERIAL_MAX_PORTS) { + const auto idx = static_cast<uint8_t>(MouseInterfaceId::COM1) + port_id; + return MouseInterface::Get(static_cast<MouseInterfaceId>(idx)); + } - LOG_ERR("MOUSE: Ports above COM4 not supported"); - assert(false); - return nullptr; + LOG_ERR("MOUSE: Ports above COM4 not supported"); + assert(false); + return nullptr; } MouseInterface::MouseInterface(const MouseInterfaceId interface_id, - const float sensitivity_predefined) : - interface_id(interface_id), - sensitivity_predefined(sensitivity_predefined) + const float sensitivity_predefined) + : interface_id(interface_id), + sensitivity_predefined(sensitivity_predefined) { - ConfigResetSensitivity(); - mouse_info.interfaces.emplace_back(MouseInterfaceInfoEntry(interface_id)); + ConfigResetSensitivity(); + mouse_info.interfaces.emplace_back(MouseInterfaceInfoEntry(interface_id)); } -void MouseInterface::Init() -{ -} +void MouseInterface::Init() {} uint8_t MouseInterface::GetInterfaceIdx() const { - return static_cast<uint8_t>(interface_id); + return static_cast<uint8_t>(interface_id); } bool MouseInterface::IsMapped() const { - return mapped_idx < mouse_info.physical.size(); + return mapped_idx < mouse_info.physical.size(); } bool MouseInterface::IsMapped(const uint8_t device_idx) const { - return mapped_idx == device_idx; + return mapped_idx == device_idx; } bool MouseInterface::IsEmulated() const { - return emulated; + return emulated; } bool MouseInterface::IsUsingEvents() const { - return IsEmulated() && - (map_status == MouseMapStatus::HostPointer || - map_status == MouseMapStatus::Mapped); + return IsEmulated() && (map_status == MouseMapStatus::HostPointer || + map_status == MouseMapStatus::Mapped); } bool MouseInterface::IsUsingHostPointer() const { - return IsEmulated() && (map_status == MouseMapStatus::HostPointer); + return IsEmulated() && (map_status == MouseMapStatus::HostPointer); } uint16_t MouseInterface::GetMinRate() const { - return min_rate_hz; + return min_rate_hz; } MouseInterfaceId MouseInterface::GetInterfaceId() const { - return interface_id; + return interface_id; } MouseMapStatus MouseInterface::GetMapStatus() const { - return map_status; + return map_status; } uint8_t MouseInterface::GetMappedDeviceIdx() const { - return mapped_idx; + return mapped_idx; } int8_t MouseInterface::GetSensitivityX() const { - return sensitivity_user_x; + return sensitivity_user_x; } int8_t MouseInterface::GetSensitivityY() const { - return sensitivity_user_y; + return sensitivity_user_y; } uint16_t MouseInterface::GetRate() const { - return rate_hz; + return rate_hz; } void MouseInterface::NotifyInterfaceRate(const uint16_t value_hz) { - interface_rate_hz = value_hz; - UpdateRate(); + interface_rate_hz = value_hz; + UpdateRate(); } -void MouseInterface::NotifyBooting() -{ -} +void MouseInterface::NotifyBooting() {} void MouseInterface::NotifyDisconnect() { - SetMapStatus(MouseMapStatus::Disconnected, mapped_idx); + SetMapStatus(MouseMapStatus::Disconnected, mapped_idx); } -void MouseInterface::SetMapStatus(const MouseMapStatus status, - const uint8_t device_idx) +void MouseInterface::SetMapStatus(const MouseMapStatus status, const uint8_t device_idx) { - MouseMapStatus new_map_status = status; - uint8_t new_mapped_idx = device_idx; + MouseMapStatus new_map_status = status; + uint8_t new_mapped_idx = device_idx; - // Change 'mapped to host pointer' to just 'host pointer' - if (new_map_status == MouseMapStatus::Mapped && - new_mapped_idx >= mouse_info.physical.size()) - new_map_status = MouseMapStatus::HostPointer; + // Change 'mapped to host pointer' to just 'host pointer' + if (new_map_status == MouseMapStatus::Mapped && + new_mapped_idx >= mouse_info.physical.size()) + new_map_status = MouseMapStatus::HostPointer; - // if physical device is disconnected, change state from - // 'mapped' to 'disconnected' - if (new_map_status == MouseMapStatus::Mapped && - mouse_info.physical[new_mapped_idx].IsDeviceDisconnected()) - new_map_status = MouseMapStatus::Disconnected; + // if physical device is disconnected, change state from + // 'mapped' to 'disconnected' + if (new_map_status == MouseMapStatus::Mapped && + mouse_info.physical[new_mapped_idx].IsDeviceDisconnected()) + new_map_status = MouseMapStatus::Disconnected; - // Perform necessary updates for mapping change - if (map_status != new_map_status || mapped_idx != new_mapped_idx) - ResetButtons(); - if (map_status != new_map_status) - UpdateRawMapped(); - if (mapped_idx != new_mapped_idx) - ManyMouseGlue::GetInstance().Map(new_mapped_idx, interface_id); + // Perform necessary updates for mapping change + if (map_status != new_map_status || mapped_idx != new_mapped_idx) + ResetButtons(); + if (map_status != new_map_status) + UpdateRawMapped(); + if (mapped_idx != new_mapped_idx) + ManyMouseGlue::GetInstance().Map(new_mapped_idx, interface_id); - // Apply new mapping - mapped_idx = new_mapped_idx; - map_status = new_map_status; + // Apply new mapping + mapped_idx = new_mapped_idx; + map_status = new_map_status; } bool MouseInterface::ConfigMap(const uint8_t device_idx) { - if (!IsEmulated()) - return false; + if (!IsEmulated()) + return false; - SetMapStatus(MouseMapStatus::Mapped, device_idx); - return true; + SetMapStatus(MouseMapStatus::Mapped, device_idx); + return true; } void MouseInterface::ConfigUnMap() { - SetMapStatus(MouseMapStatus::Mapped, idx_host_pointer); + SetMapStatus(MouseMapStatus::Mapped, idx_host_pointer); } void MouseInterface::ConfigOnOff(const bool enable) { - if (!IsEmulated()) - return; + if (!IsEmulated()) + return; - if (!enable) - SetMapStatus(MouseMapStatus::Disabled); - else if (map_status == MouseMapStatus::Disabled) - SetMapStatus(MouseMapStatus::HostPointer); + if (!enable) + SetMapStatus(MouseMapStatus::Disabled); + else if (map_status == MouseMapStatus::Disabled) + SetMapStatus(MouseMapStatus::HostPointer); } void MouseInterface::ConfigReset() { - ConfigUnMap(); - ConfigOnOff(true); - ConfigResetSensitivity(); - ConfigResetMinRate(); + ConfigUnMap(); + ConfigOnOff(true); + ConfigResetSensitivity(); + ConfigResetMinRate(); } -void MouseInterface::ConfigSetSensitivity(const int8_t value_x, - const int8_t value_y) +void MouseInterface::ConfigSetSensitivity(const int8_t value_x, const int8_t value_y) { - if (!IsEmulated()) - return; + if (!IsEmulated()) + return; - sensitivity_user_x = value_x; - sensitivity_user_y = value_y; - UpdateSensitivity(); + sensitivity_user_x = value_x; + sensitivity_user_y = value_y; + UpdateSensitivity(); } void MouseInterface::ConfigSetSensitivityX(const int8_t value) { - if (!IsEmulated()) - return; + if (!IsEmulated()) + return; - sensitivity_user_x = value; - UpdateSensitivity(); + sensitivity_user_x = value; + UpdateSensitivity(); } void MouseInterface::ConfigSetSensitivityY(const int8_t value) { - if (!IsEmulated()) - return; + if (!IsEmulated()) + return; - sensitivity_user_y = value; - UpdateSensitivity(); + sensitivity_user_y = value; + UpdateSensitivity(); } void MouseInterface::ConfigResetSensitivity() { - ConfigSetSensitivity(mouse_config.sensitivity_x, - mouse_config.sensitivity_y); + ConfigSetSensitivity(mouse_config.sensitivity_x, mouse_config.sensitivity_y); } void MouseInterface::ConfigResetSensitivityX() { - ConfigSetSensitivityX(mouse_config.sensitivity_x); + ConfigSetSensitivityX(mouse_config.sensitivity_x); } void MouseInterface::ConfigResetSensitivityY() { - ConfigSetSensitivityY(mouse_config.sensitivity_y); + ConfigSetSensitivityY(mouse_config.sensitivity_y); } void MouseInterface::ConfigSetMinRate(const uint16_t value_hz) { - min_rate_hz = value_hz; - UpdateMinRate(); + min_rate_hz = value_hz; + UpdateMinRate(); } void MouseInterface::ConfigResetMinRate() { - ConfigSetMinRate(0); + ConfigSetMinRate(0); } void MouseInterface::RegisterListener(CSerialMouse &) { - assert(false); // should never be called for unsupported interface + assert(false); // should never be called for unsupported interface } void MouseInterface::UnRegisterListener() { - assert(false); // should never be called for unsupported interface + assert(false); // should never be called for unsupported interface } void MouseInterface::UpdateConfig() { - UpdateRawMapped(); - UpdateSensitivity(); + UpdateRawMapped(); + UpdateSensitivity(); } -void MouseInterface::UpdateRawMapped() -{ -} +void MouseInterface::UpdateRawMapped() {} void MouseInterface::UpdateSensitivity() { - auto calculate = [this](const int8_t user_val) - { - // Mouse sensitivity formula is exponential - as it is probably - // reasonable to expect user wanting to increase sensitivity - // 1.5 times, but not 1.9 times - while the difference between - // 5.0 and 5.4 times sensitivity increase is rather hard to - // notice in a real life + auto calculate = [this](const int8_t user_val) { + // Mouse sensitivity formula is exponential - as it is probably + // reasonable to expect user wanting to increase sensitivity + // 1.5 times, but not 1.9 times - while the difference between + // 5.0 and 5.4 times sensitivity increase is rather hard to + // notice in a real life - float power = 0.0f; - float scaling = 0.0f; + float power = 0.0f; + float scaling = 0.0f; - if (user_val > 0) { - power = static_cast<float>(user_val - 50); - scaling = sensitivity_predefined; - } else if (user_val < 0) { - power = static_cast<float>(-user_val - 50); - scaling = -sensitivity_predefined; - } else // user_cal == 0 - return 0.0f; + if (user_val > 0) { + power = static_cast<float>(user_val - 50); + scaling = sensitivity_predefined; + } else if (user_val < 0) { + power = static_cast<float>(-user_val - 50); + scaling = -sensitivity_predefined; + } else // user_cal == 0 + return 0.0f; - power /= mouse_predefined.sensitivity_double_steps; - return scaling * std::pow(2.0f, power); - }; + power /= mouse_predefined.sensitivity_double_steps; + return scaling * std::pow(2.0f, power); + }; - sensitivity_coeff_x = calculate(sensitivity_user_x); - sensitivity_coeff_y = calculate(sensitivity_user_y); + sensitivity_coeff_x = calculate(sensitivity_user_x); + sensitivity_coeff_y = calculate(sensitivity_user_y); } void MouseInterface::UpdateMinRate() { - UpdateRate(); + UpdateRate(); } void MouseInterface::UpdateRate() { - rate_hz = MOUSE_ClampRateHz(std::max(interface_rate_hz, min_rate_hz)); + rate_hz = MOUSE_ClampRateHz(std::max(interface_rate_hz, min_rate_hz)); } -void MouseInterface::UpdateButtons(const uint8_t idx, - const bool pressed) +void MouseInterface::UpdateButtons(const uint8_t idx, const bool pressed) { - old_buttons_12 = buttons_12; - old_buttons_345 = buttons_345; + old_buttons_12 = buttons_12; + old_buttons_345 = buttons_345; - switch (idx) { - case 0: // left button - buttons_12.left = pressed ? 1 : 0; - break; - case 1: // right button - buttons_12.right = pressed ? 1 : 0; - break; - case 2: // middle button - buttons_345.middle = pressed ? 1 : 0; - break; - case 3: // extra button #1 - buttons_345.extra_1 = pressed ? 1 : 0; - break; - case 4: // extra button #2 - buttons_345.extra_2 = pressed ? 1 : 0; - break; - default: // button not supported - return; - } + switch (idx) { + case 0: // left button + buttons_12.left = pressed ? 1 : 0; + break; + case 1: // right button + buttons_12.right = pressed ? 1 : 0; + break; + case 2: // middle button + buttons_345.middle = pressed ? 1 : 0; + break; + case 3: // extra button #1 + buttons_345.extra_1 = pressed ? 1 : 0; + break; + case 4: // extra button #2 + buttons_345.extra_2 = pressed ? 1 : 0; + break; + default: // button not supported + return; + } } void MouseInterface::ResetButtons() { - buttons_12 = 0; - buttons_345 = 0; + buttons_12 = 0; + buttons_345 = 0; } bool MouseInterface::ChangedButtonsJoined() const { - return (old_buttons_12.data != buttons_12.data) || - (old_buttons_345.data != buttons_345.data); + return (old_buttons_12.data != buttons_12.data) || + (old_buttons_345.data != buttons_345.data); } bool MouseInterface::ChangedButtonsSquished() const { - if (GCC_LIKELY(old_buttons_12.data != buttons_12.data)) - return true; + if (GCC_LIKELY(old_buttons_12.data != buttons_12.data)) + return true; - return (old_buttons_345.data == 0 && buttons_345.data != 0) || - (old_buttons_345.data != 0 && buttons_345.data == 0); + return (old_buttons_345.data == 0 && buttons_345.data != 0) || + (old_buttons_345.data != 0 && buttons_345.data == 0); } MouseButtonsAll MouseInterface::GetButtonsJoined() const { - MouseButtonsAll buttons_all; - buttons_all.data = buttons_12.data | buttons_345.data; + MouseButtonsAll buttons_all; + buttons_all.data = buttons_12.data | buttons_345.data; - return buttons_all; + return buttons_all; } MouseButtons12S MouseInterface::GetButtonsSquished() const { - MouseButtons12S buttons_12S; + MouseButtons12S buttons_12S; - // Squish buttons 3/4/5 into single virtual middle button - buttons_12S.data = buttons_12.data; - if (buttons_345.data) - buttons_12S.middle = 1; + // Squish buttons 3/4/5 into single virtual middle button + buttons_12S.data = buttons_12.data; + if (buttons_345.data) + buttons_12S.middle = 1; - return buttons_12S; + return buttons_12S; } // *************************************************************************** // Concrete interfaces - implementation // *************************************************************************** -InterfaceDos::InterfaceDos() : - MouseInterface(MouseInterfaceId::DOS, mouse_predefined.sensitivity_dos) +InterfaceDos::InterfaceDos() + : MouseInterface(MouseInterfaceId::DOS, mouse_predefined.sensitivity_dos) { - UpdateSensitivity(); + UpdateSensitivity(); } void InterfaceDos::Init() { - if (mouse_config.dos_driver) - MOUSEDOS_Init(); - else - emulated = false; - MOUSEDOS_NotifyMinRate(min_rate_hz); + if (mouse_config.dos_driver) + MOUSEDOS_Init(); + else + emulated = false; + MOUSEDOS_NotifyMinRate(min_rate_hz); } -void InterfaceDos::NotifyMoved(MouseEvent &ev, - const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) +void InterfaceDos::NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) { - ev.dos_moved = MOUSEDOS_NotifyMoved( - x_rel * sensitivity_coeff_x, - y_rel * sensitivity_coeff_y, - x_abs, - y_abs); - ev.request_dos = ev.dos_moved; + ev.dos_moved = MOUSEDOS_NotifyMoved(x_rel * sensitivity_coeff_x, + y_rel * sensitivity_coeff_y, + x_abs, + y_abs); + ev.request_dos = ev.dos_moved; } -void InterfaceDos::NotifyButton(MouseEvent &ev, - const uint8_t idx, - const bool pressed) +void InterfaceDos::NotifyButton(MouseEvent &ev, const uint8_t idx, const bool pressed) { - UpdateButtons(idx, pressed); - if (GCC_UNLIKELY(!ChangedButtonsSquished())) - return; + UpdateButtons(idx, pressed); + if (GCC_UNLIKELY(!ChangedButtonsSquished())) + return; - ev.dos_button = true; - ev.dos_buttons = GetButtonsSquished(); - ev.request_dos = true; + ev.dos_button = true; + ev.dos_buttons = GetButtonsSquished(); + ev.request_dos = true; } -void InterfaceDos::NotifyWheel(MouseEvent &ev, - const int16_t w_rel) +void InterfaceDos::NotifyWheel(MouseEvent &ev, const int16_t w_rel) { - ev.dos_wheel = MOUSEDOS_NotifyWheel(w_rel); - ev.request_dos = ev.dos_wheel; + ev.dos_wheel = MOUSEDOS_NotifyWheel(w_rel); + ev.request_dos = ev.dos_wheel; } void InterfaceDos::NotifyBooting() { - // DOS virtual mouse driver gets unavailable - // if guest OS is booted so do not waste time - // emulating this interface + // DOS virtual mouse driver gets unavailable + // if guest OS is booted so do not waste time + // emulating this interface - ConfigReset(); - emulated = false; - ManyMouseGlue::GetInstance().ShutdownIfSafe(); + ConfigReset(); + emulated = false; + ManyMouseGlue::GetInstance().ShutdownIfSafe(); } void InterfaceDos::UpdateRawMapped() { - MOUSEDOS_NotifyMapped(IsMapped()); - MOUSEDOS_NotifyRawInput(mouse_config.raw_input || IsMapped()); + MOUSEDOS_NotifyMapped(IsMapped()); + MOUSEDOS_NotifyRawInput(mouse_config.raw_input || IsMapped()); } void InterfaceDos::UpdateMinRate() { - MOUSEDOS_NotifyMinRate(min_rate_hz); + MOUSEDOS_NotifyMinRate(min_rate_hz); } void InterfaceDos::UpdateRate() { - MouseInterface::UpdateRate(); - MouseQueue::GetInstance().SetRateDOS(rate_hz); + MouseInterface::UpdateRate(); + MouseQueue::GetInstance().SetRateDOS(rate_hz); } -InterfacePS2::InterfacePS2() : - MouseInterface(MouseInterfaceId::PS2, mouse_predefined.sensitivity_ps2) +InterfacePS2::InterfacePS2() + : MouseInterface(MouseInterfaceId::PS2, mouse_predefined.sensitivity_ps2) { - UpdateSensitivity(); + UpdateSensitivity(); } void InterfacePS2::Init() { - MOUSEPS2_Init(); - MOUSEVMM_Init(); + MOUSEPS2_Init(); + MOUSEVMM_Init(); } -void InterfacePS2::NotifyMoved(MouseEvent &ev, - const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) +void InterfacePS2::NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) { - const bool request_ps2 = MOUSEPS2_NotifyMoved(x_rel * sensitivity_coeff_x, - y_rel * sensitivity_coeff_y); - const bool request_vmm = MOUSEVMM_NotifyMoved(x_rel * sensitivity_coeff_vmm_x, - y_rel * sensitivity_coeff_vmm_y, - x_abs, - y_abs); + const bool request_ps2 = MOUSEPS2_NotifyMoved(x_rel * sensitivity_coeff_x, + y_rel * sensitivity_coeff_y); + const bool request_vmm = MOUSEVMM_NotifyMoved(x_rel * sensitivity_coeff_vmm_x, + y_rel * sensitivity_coeff_vmm_y, + x_abs, + y_abs); - ev.request_ps2 = request_ps2 || request_vmm; + ev.request_ps2 = request_ps2 || request_vmm; } -void InterfacePS2::NotifyButton(MouseEvent &ev, - const uint8_t idx, - const bool pressed) +void InterfacePS2::NotifyButton(MouseEvent &ev, const uint8_t idx, const bool pressed) { - UpdateButtons(idx, pressed); - if (GCC_UNLIKELY(!ChangedButtonsJoined())) - return; + UpdateButtons(idx, pressed); + if (GCC_UNLIKELY(!ChangedButtonsJoined())) + return; - const bool request_ps2 = MOUSEPS2_NotifyButton(GetButtonsSquished(), - GetButtonsJoined()); - const bool request_vmm = MOUSEVMM_NotifyButton(GetButtonsSquished()); + const bool request_ps2 = MOUSEPS2_NotifyButton(GetButtonsSquished(), + GetButtonsJoined()); + const bool request_vmm = MOUSEVMM_NotifyButton(GetButtonsSquished()); - ev.request_ps2 = request_ps2 || request_vmm; + ev.request_ps2 = request_ps2 || request_vmm; } -void InterfacePS2::NotifyWheel(MouseEvent &ev, - const int16_t w_rel) +void InterfacePS2::NotifyWheel(MouseEvent &ev, const int16_t w_rel) { - const bool request_ps2 = MOUSEPS2_NotifyWheel(w_rel); - const bool request_vmm = MOUSEVMM_NotifyWheel(w_rel); + const bool request_ps2 = MOUSEPS2_NotifyWheel(w_rel); + const bool request_vmm = MOUSEVMM_NotifyWheel(w_rel); - ev.request_ps2 = request_ps2 || request_vmm; + ev.request_ps2 = request_ps2 || request_vmm; } void InterfacePS2::UpdateRawMapped() { - MOUSEVMM_NotifyMapped(IsMapped()); - MOUSEVMM_NotifyRawInput(mouse_config.raw_input || IsMapped()); + MOUSEVMM_NotifyMapped(IsMapped()); + MOUSEVMM_NotifyRawInput(mouse_config.raw_input || IsMapped()); } void InterfacePS2::UpdateSensitivity() { - MouseInterface::UpdateSensitivity(); + MouseInterface::UpdateSensitivity(); - const float tmp = mouse_predefined.sensitivity_vmm / - mouse_predefined.sensitivity_ps2; - sensitivity_coeff_vmm_x = sensitivity_coeff_x * tmp; - sensitivity_coeff_vmm_y = sensitivity_coeff_y * tmp; + const float tmp = mouse_predefined.sensitivity_vmm / + mouse_predefined.sensitivity_ps2; + sensitivity_coeff_vmm_x = sensitivity_coeff_x * tmp; + sensitivity_coeff_vmm_y = sensitivity_coeff_y * tmp; } void InterfacePS2::UpdateRate() { - MouseInterface::UpdateRate(); - MouseQueue::GetInstance().SetRatePS2(rate_hz); + MouseInterface::UpdateRate(); + MouseQueue::GetInstance().SetRatePS2(rate_hz); } -InterfaceCOM::InterfaceCOM(const uint8_t port_id) : - MouseInterface(static_cast<MouseInterfaceId>(static_cast<uint8_t>(MouseInterfaceId::COM1) + port_id), - mouse_predefined.sensitivity_com) +InterfaceCOM::InterfaceCOM(const uint8_t port_id) + : MouseInterface(static_cast<MouseInterfaceId>( + static_cast<uint8_t>(MouseInterfaceId::COM1) + port_id), + mouse_predefined.sensitivity_com) { - UpdateSensitivity(); - // Wait for CSerialMouse to register itself - emulated = false; + UpdateSensitivity(); + // Wait for CSerialMouse to register itself + emulated = false; } -void InterfaceCOM::NotifyMoved(MouseEvent &, - const float x_rel, - const float y_rel, - const uint16_t, - const uint16_t) +void InterfaceCOM::NotifyMoved(MouseEvent &, const float x_rel, + const float y_rel, const uint16_t, const uint16_t) { - assert(listener); + assert(listener); - listener->NotifyMoved(x_rel * sensitivity_coeff_x, - y_rel * sensitivity_coeff_y); + listener->NotifyMoved(x_rel * sensitivity_coeff_x, + y_rel * sensitivity_coeff_y); } -void InterfaceCOM::NotifyButton(MouseEvent &, - const uint8_t idx, - const bool pressed) +void InterfaceCOM::NotifyButton(MouseEvent &, const uint8_t idx, const bool pressed) { - assert(listener); + assert(listener); - UpdateButtons(idx, pressed); - if (GCC_UNLIKELY(!ChangedButtonsSquished())) - return; + UpdateButtons(idx, pressed); + if (GCC_UNLIKELY(!ChangedButtonsSquished())) + return; - listener->NotifyButton(GetButtonsSquished().data, idx); + listener->NotifyButton(GetButtonsSquished().data, idx); } -void InterfaceCOM::NotifyWheel(MouseEvent &, - const int16_t w_rel) +void InterfaceCOM::NotifyWheel(MouseEvent &, const int16_t w_rel) { - assert(listener); + assert(listener); - listener->NotifyWheel(w_rel); + listener->NotifyWheel(w_rel); } void InterfaceCOM::UpdateRate() { - MouseInterface::UpdateRate(); + MouseInterface::UpdateRate(); - if (!listener) - return; + if (!listener) + return; - if (interface_rate_hz >= rate_hz || !interface_rate_hz) - listener->BoostRate(0); - else - // Ask serial mouse emulation code to cheat on transmission - // speed to simulate higher sampling rate - listener->BoostRate(rate_hz); + if (interface_rate_hz >= rate_hz || !interface_rate_hz) + listener->BoostRate(0); + else + // Ask serial mouse emulation code to cheat on transmission + // speed to simulate higher sampling rate + listener->BoostRate(rate_hz); } void InterfaceCOM::RegisterListener(CSerialMouse &listener_object) { - listener = &listener_object; - emulated = true; + listener = &listener_object; + emulated = true; } void InterfaceCOM::UnRegisterListener() { - // Serial mouse gets unavailable when listener object disconnects + // Serial mouse gets unavailable when listener object disconnects - ConfigReset(); - listener = nullptr; - emulated = false; - ManyMouseGlue::GetInstance().ShutdownIfSafe(); + ConfigReset(); + listener = nullptr; + emulated = false; + ManyMouseGlue::GetInstance().ShutdownIfSafe(); } diff --git a/src/hardware/mouse/mouse_interfaces.h b/src/hardware/mouse/mouse_interfaces.h index 4eddf1bc4..a0c754960 100644 --- a/src/hardware/mouse/mouse_interfaces.h +++ b/src/hardware/mouse/mouse_interfaces.h @@ -45,16 +45,13 @@ void MOUSEDOS_NotifyMinRate(const uint16_t value_hz); void MOUSEDOS_DrawCursor(); bool MOUSEDOS_HasCallback(const uint8_t mask); -Bitu MOUSEDOS_DoCallback(const uint8_t mask, - const MouseButtons12S buttons_12S); +Bitu MOUSEDOS_DoCallback(const uint8_t mask, const MouseButtons12S buttons_12S); // - needs relative movements // - understands up to 3 buttons -bool MOUSEDOS_NotifyMoved(const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs); +bool MOUSEDOS_NotifyMoved(const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs); bool MOUSEDOS_NotifyWheel(const int16_t w_rel); uint8_t MOUSEDOS_UpdateMoved(); @@ -99,10 +96,8 @@ void MOUSEVMM_Deactivate(); // - needs absolute mouse position // - understands up to 3 buttons -bool MOUSEVMM_NotifyMoved(const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs); +bool MOUSEVMM_NotifyMoved(const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs); bool MOUSEVMM_NotifyButton(const MouseButtons12S buttons_12S); bool MOUSEVMM_NotifyWheel(const int16_t w_rel); @@ -120,120 +115,112 @@ bool MOUSEVMM_NotifyWheel(const int16_t w_rel); // Base mouse interface // *************************************************************************** -class MouseInterface -{ +class MouseInterface { public: - - static void InitAllInstances(); - static MouseInterface *Get(const MouseInterfaceId interface_id); - static MouseInterface *GetDOS(); - static MouseInterface *GetPS2(); - static MouseInterface *GetSerial(const uint8_t port_id); - - virtual void NotifyMoved(MouseEvent &ev, - const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) = 0; - virtual void NotifyButton(MouseEvent &ev, - const uint8_t idx, - const bool pressed) = 0; - virtual void NotifyWheel(MouseEvent &ev, - const int16_t w_rel) = 0; - - void NotifyInterfaceRate(const uint16_t rate_hz); - virtual void NotifyBooting(); - void NotifyDisconnect(); - - bool IsMapped() const; - bool IsMapped(const uint8_t device_idx) const; - bool IsEmulated() const; - bool IsUsingEvents() const; - bool IsUsingHostPointer() const; - - MouseInterfaceId GetInterfaceId() const; - MouseMapStatus GetMapStatus() const; - uint8_t GetMappedDeviceIdx() const; - int8_t GetSensitivityX() const; - int8_t GetSensitivityY() const; - uint16_t GetMinRate() const; - uint16_t GetRate() const; - - bool ConfigMap(const uint8_t device_idx); - void ConfigUnMap(); - - void ConfigOnOff(const bool enable); - void ConfigReset(); - void ConfigSetSensitivity(const int8_t value_x, const int8_t value_y); - void ConfigSetSensitivityX(const int8_t value); - void ConfigSetSensitivityY(const int8_t value); - void ConfigResetSensitivity(); - void ConfigResetSensitivityX(); - void ConfigResetSensitivityY(); - void ConfigSetMinRate(const uint16_t value_hz); - void ConfigResetMinRate(); - - virtual void UpdateConfig(); - virtual void RegisterListener(CSerialMouse &listener_object); - virtual void UnRegisterListener(); + static void InitAllInstances(); + static MouseInterface *Get(const MouseInterfaceId interface_id); + static MouseInterface *GetDOS(); + static MouseInterface *GetPS2(); + static MouseInterface *GetSerial(const uint8_t port_id); + + virtual void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) = 0; + virtual void NotifyButton(MouseEvent &ev, const uint8_t idx, + const bool pressed) = 0; + virtual void NotifyWheel(MouseEvent &ev, const int16_t w_rel) = 0; + + void NotifyInterfaceRate(const uint16_t rate_hz); + virtual void NotifyBooting(); + void NotifyDisconnect(); + + bool IsMapped() const; + bool IsMapped(const uint8_t device_idx) const; + bool IsEmulated() const; + bool IsUsingEvents() const; + bool IsUsingHostPointer() const; + + MouseInterfaceId GetInterfaceId() const; + MouseMapStatus GetMapStatus() const; + uint8_t GetMappedDeviceIdx() const; + int8_t GetSensitivityX() const; + int8_t GetSensitivityY() const; + uint16_t GetMinRate() const; + uint16_t GetRate() const; + + bool ConfigMap(const uint8_t device_idx); + void ConfigUnMap(); + + void ConfigOnOff(const bool enable); + void ConfigReset(); + void ConfigSetSensitivity(const int8_t value_x, const int8_t value_y); + void ConfigSetSensitivityX(const int8_t value); + void ConfigSetSensitivityY(const int8_t value); + void ConfigResetSensitivity(); + void ConfigResetSensitivityX(); + void ConfigResetSensitivityY(); + void ConfigSetMinRate(const uint16_t value_hz); + void ConfigResetMinRate(); + + virtual void UpdateConfig(); + virtual void RegisterListener(CSerialMouse &listener_object); + virtual void UnRegisterListener(); protected: + static constexpr uint8_t idx_host_pointer = UINT8_MAX; - static constexpr uint8_t idx_host_pointer = UINT8_MAX; - - MouseInterface(const MouseInterfaceId interface_id, - const float sensitivity_predefined); - virtual ~MouseInterface() = default; - virtual void Init(); + MouseInterface(const MouseInterfaceId interface_id, + const float sensitivity_predefined); + virtual ~MouseInterface() = default; + virtual void Init(); - uint8_t GetInterfaceIdx() const; + uint8_t GetInterfaceIdx() const; - void SetMapStatus(const MouseMapStatus status, - const uint8_t device_idx = idx_host_pointer); + void SetMapStatus(const MouseMapStatus status, + const uint8_t device_idx = idx_host_pointer); - virtual void UpdateRawMapped(); - virtual void UpdateSensitivity(); - virtual void UpdateMinRate(); - virtual void UpdateRate(); - void UpdateButtons(const uint8_t idx, const bool pressed); - void ResetButtons(); + virtual void UpdateRawMapped(); + virtual void UpdateSensitivity(); + virtual void UpdateMinRate(); + virtual void UpdateRate(); + void UpdateButtons(const uint8_t idx, const bool pressed); + void ResetButtons(); - bool ChangedButtonsJoined() const; - bool ChangedButtonsSquished() const; + bool ChangedButtonsJoined() const; + bool ChangedButtonsSquished() const; - MouseButtonsAll GetButtonsJoined() const; - MouseButtons12S GetButtonsSquished() const; + MouseButtonsAll GetButtonsJoined() const; + MouseButtons12S GetButtonsSquished() const; - bool emulated = true; + bool emulated = true; - float sensitivity_coeff_x = 1.0f; // cached combined sensitivity coefficients - float sensitivity_coeff_y = 1.0f; // to reduce amount of multiplications + float sensitivity_coeff_x = 1.0f; // cached combined sensitivity + // coefficients + float sensitivity_coeff_y = 1.0f; // to reduce amount of multiplications - int8_t sensitivity_user_x = 0; - int8_t sensitivity_user_y = 0; + int8_t sensitivity_user_x = 0; + int8_t sensitivity_user_y = 0; - uint16_t rate_hz = 0; - uint16_t min_rate_hz = 0; - uint16_t interface_rate_hz = 0; + uint16_t rate_hz = 0; + uint16_t min_rate_hz = 0; + uint16_t interface_rate_hz = 0; private: + MouseInterface() = delete; + MouseInterface(const MouseInterface &) = delete; + MouseInterface &operator=(const MouseInterface &) = delete; - MouseInterface() = delete; - MouseInterface(const MouseInterface &) = delete; - MouseInterface &operator=(const MouseInterface &) = delete; - - const MouseInterfaceId interface_id = MouseInterfaceId::None; + const MouseInterfaceId interface_id = MouseInterfaceId::None; - MouseMapStatus map_status = MouseMapStatus::HostPointer; - uint8_t mapped_idx = idx_host_pointer; // index of mapped physical mouse + MouseMapStatus map_status = MouseMapStatus::HostPointer; + uint8_t mapped_idx = idx_host_pointer; // index of mapped physical mouse - MouseButtons12 buttons_12 = 0; // host side buttons 1 (left), 2 (right) - MouseButtons345 buttons_345 = 0; // host side buttons 3 (middle), 4, and 5 + MouseButtons12 buttons_12 = 0; // host side buttons 1 (left), 2 (right) + MouseButtons345 buttons_345 = 0; // host side buttons 3 (middle), 4, and 5 - MouseButtons12 old_buttons_12 = 0; // pre-update values - MouseButtons345 old_buttons_345 = 0; + MouseButtons12 old_buttons_12 = 0; // pre-update values + MouseButtons345 old_buttons_345 = 0; - float sensitivity_predefined = 1.0f; // hardcoded for the given interface + float sensitivity_predefined = 1.0f; // hardcoded for the given interface }; extern std::vector<MouseInterface *> mouse_interfaces; diff --git a/src/hardware/mouse/mouse_manymouse.cpp b/src/hardware/mouse/mouse_manymouse.cpp index e732a700b..1cacc84db 100644 --- a/src/hardware/mouse/mouse_manymouse.cpp +++ b/src/hardware/mouse/mouse_manymouse.cpp @@ -28,412 +28,412 @@ CHECK_NARROWING(); - void manymouse_tick(uint32_t) { #if C_MANYMOUSE - ManyMouseGlue::GetInstance().Tick(); + ManyMouseGlue::GetInstance().Tick(); #endif // C_MANYMOUSE } -MousePhysical::MousePhysical(const std::string &name) : - name(name) -{ -} +MousePhysical::MousePhysical(const std::string &name) : name(name) {} bool MousePhysical::IsMapped() const { - return mapped_id != MouseInterfaceId::None; + return mapped_id != MouseInterfaceId::None; } bool MousePhysical::IsDisconnected() const { - return disconnected; + return disconnected; } MouseInterfaceId MousePhysical::GetMappedInterfaceId() const { - return mapped_id; + return mapped_id; } const std::string &MousePhysical::GetName() const { - return name; + return name; } ManyMouseGlue &ManyMouseGlue::GetInstance() { - static ManyMouseGlue *instance = nullptr; - if (!instance) - instance = new ManyMouseGlue(); - return *instance; + static ManyMouseGlue *instance = nullptr; + if (!instance) + instance = new ManyMouseGlue(); + return *instance; } #if C_MANYMOUSE void ManyMouseGlue::InitIfNeeded() { - if (initialized || malfunction) - return; - - num_mice = ManyMouse_Init(); - if (num_mice < 0) { - LOG_ERR("MOUSE: ManyMouse initialization failed"); - ManyMouse_Quit(); - malfunction = true; - return; - } - initialized = true; - - if (num_mice >= max_mice) { - num_mice = max_mice - 1; - static bool logged = false; - if (!logged) { - logged = true; - LOG_ERR("MOUSE: Up to %d simultaneously connected mice supported", - max_mice); - } - } - - const auto new_driver_name = std::string(ManyMouse_DriverName()); - if (new_driver_name != driver_name) { - driver_name = new_driver_name; - LOG_INFO("MOUSE: ManyMouse driver '%s'", driver_name.c_str()); - } - - Rescan(); + if (initialized || malfunction) + return; + + num_mice = ManyMouse_Init(); + if (num_mice < 0) { + LOG_ERR("MOUSE: ManyMouse initialization failed"); + ManyMouse_Quit(); + malfunction = true; + return; + } + initialized = true; + + if (num_mice >= max_mice) { + num_mice = max_mice - 1; + static bool logged = false; + if (!logged) { + logged = true; + LOG_ERR("MOUSE: Up to %d simultaneously connected mice supported", + max_mice); + } + } + + const auto new_driver_name = std::string(ManyMouse_DriverName()); + if (new_driver_name != driver_name) { + driver_name = new_driver_name; + LOG_INFO("MOUSE: ManyMouse driver '%s'", driver_name.c_str()); + } + + Rescan(); } void ManyMouseGlue::ShutdownIfSafe() { - if (is_mapping_in_effect || config_api_counter) - return; + if (is_mapping_in_effect || config_api_counter) + return; - ShutdownForced(); + ShutdownForced(); } void ManyMouseGlue::ShutdownForced() { - if (!initialized) - return; - - PIC_RemoveEvents(manymouse_tick); - ManyMouse_Quit(); - ClearPhysicalMice(); - num_mice = 0; - initialized = false; + if (!initialized) + return; + + PIC_RemoveEvents(manymouse_tick); + ManyMouse_Quit(); + ClearPhysicalMice(); + num_mice = 0; + initialized = false; } void ManyMouseGlue::StartConfigAPI() { - // Config API object is being created - ++config_api_counter; - assert(config_api_counter > 0); + // Config API object is being created + ++config_api_counter; + assert(config_api_counter > 0); } void ManyMouseGlue::StopConfigAPI() { - // Config API object is being destroyed - assert(config_api_counter > 0); - config_api_counter--; - ShutdownIfSafe(); - if (!config_api_counter) - rescan_blocked_config = false; + // Config API object is being destroyed + assert(config_api_counter > 0); + config_api_counter--; + ShutdownIfSafe(); + if (!config_api_counter) + rescan_blocked_config = false; } void ManyMouseGlue::ClearPhysicalMice() { - mouse_info.physical.clear(); - physical_devices.clear(); - rel_x.clear(); - rel_y.clear(); + mouse_info.physical.clear(); + physical_devices.clear(); + rel_x.clear(); + rel_y.clear(); } void ManyMouseGlue::Rescan() { - if (config_api_counter) - // Do not allow another rescan until MouseConfigAPI - // stops being used, it would be unsafe due to possible - // changes of physical device list size/indices - rescan_blocked_config = true; - - ClearPhysicalMice(); - - for (uint8_t idx = 0; idx < num_mice; idx++) { - const auto name_utf8 = ManyMouse_DeviceName(idx); - std::string name; - UTF8_RenderForDos(name_utf8, name); - - const char character_nbsp = 0x7f; // non-breaking space - const char character_space = 0x20; - - for (auto pos = name.size(); pos > 0; pos--) { - // Replace non-breaking space with a regular space - if (name[pos - 1] == character_nbsp) - name[pos - 1] = character_space; - // Remove non-ASCII and control characters - if (name[pos - 1] < character_space || - name[pos - 1] >= character_nbsp) - name.erase(pos - 1, 1); - } - - // Try to rework into something useful names in the forms - // 'FooBar Corp FooBar Corp Incredible Mouse' - size_t pos = name.size() / 2 + 1; - while (--pos > 2) { - if (name[pos - 1] != ' ') - continue; - const std::string tmp = name.substr(0, pos); - if (name.rfind(tmp + tmp, 0) == std::string::npos) - continue; - name = name.substr(pos); - break; - } - - // ManyMouse should limit device names to 64 characters, - // but make sure name is indeed limited in length, and - // strip trailing spaces - name.resize(std::min(static_cast<size_t>(64), name.size())); - while (!name.empty() && name.back() == ' ') - name.pop_back(); - - physical_devices.emplace_back(MousePhysical(name)); - mouse_info.physical.emplace_back( - MousePhysicalInfoEntry(static_cast<uint8_t>(physical_devices.size() - 1))); - } + if (config_api_counter) + // Do not allow another rescan until MouseConfigAPI + // stops being used, it would be unsafe due to possible + // changes of physical device list size/indices + rescan_blocked_config = true; + + ClearPhysicalMice(); + + for (uint8_t idx = 0; idx < num_mice; idx++) { + const auto name_utf8 = ManyMouse_DeviceName(idx); + std::string name; + UTF8_RenderForDos(name_utf8, name); + + const char character_nbsp = 0x7f; // non-breaking space + const char character_space = 0x20; + + for (auto pos = name.size(); pos > 0; pos--) { + // Replace non-breaking space with a regular space + if (name[pos - 1] == character_nbsp) + name[pos - 1] = character_space; + // Remove non-ASCII and control characters + if (name[pos - 1] < character_space || + name[pos - 1] >= character_nbsp) + name.erase(pos - 1, 1); + } + + // Try to rework into something useful names in the forms + // 'FooBar Corp FooBar Corp Incredible Mouse' + size_t pos = name.size() / 2 + 1; + while (--pos > 2) { + if (name[pos - 1] != ' ') + continue; + const std::string tmp = name.substr(0, pos); + if (name.rfind(tmp + tmp, 0) == std::string::npos) + continue; + name = name.substr(pos); + break; + } + + // ManyMouse should limit device names to 64 characters, + // but make sure name is indeed limited in length, and + // strip trailing spaces + name.resize(std::min(static_cast<size_t>(64), name.size())); + while (!name.empty() && name.back() == ' ') + name.pop_back(); + + physical_devices.emplace_back(MousePhysical(name)); + mouse_info.physical.emplace_back(MousePhysicalInfoEntry( + static_cast<uint8_t>(physical_devices.size() - 1))); + } } void ManyMouseGlue::RescanIfSafe() { - if (rescan_blocked_config) - return; + if (rescan_blocked_config) + return; - ShutdownIfSafe(); - InitIfNeeded(); + ShutdownIfSafe(); + InitIfNeeded(); } bool ManyMouseGlue::ProbeForMapping(uint8_t &device_id) { - // Wait a little to speedup screen update - const auto pic_ticks_start = PIC_Ticks; - while (PIC_Ticks >= pic_ticks_start && - PIC_Ticks - pic_ticks_start < 50) - CALLBACK_Idle(); - - // Make sure the module is initialized, - // but suppress default event handling - InitIfNeeded(); - if (!initialized) - return false; - PIC_RemoveEvents(manymouse_tick); - - // Flush events, handle critical ones - ManyMouseEvent event; - while (ManyMouse_PollEvent(&event)) - HandleEvent(event, true); // handle critical events - - bool success = false; - while (true) { - // Poll mouse events, handle critical ones - if (!ManyMouse_PollEvent(&event)) { - CALLBACK_Idle(); - continue; - } - if (event.device >= max_mice) - continue; - HandleEvent(event, true); - - // Wait for mouse button press - if (event.type != MANYMOUSE_EVENT_BUTTON || !event.value) - continue; - device_id = static_cast<uint8_t>(event.device); - - if (event.item >= 1) - break; // user cancelled the interactive mouse mapping - - // Do not accept already mapped devices - bool already_mapped = false; - for (const auto &interface : mouse_interfaces) - if (interface->IsMapped(device_id)) - already_mapped = true; - if (already_mapped) - continue; - - // Mouse probed successfully - device_id = static_cast<uint8_t>(event.device); - success = true; - break; - } - - if (is_mapping_in_effect && !mouse_config.no_mouse) - PIC_AddEvent(manymouse_tick, tick_interval); - return success; + // Wait a little to speedup screen update + const auto pic_ticks_start = PIC_Ticks; + while (PIC_Ticks >= pic_ticks_start && PIC_Ticks - pic_ticks_start < 50) + CALLBACK_Idle(); + + // Make sure the module is initialized, + // but suppress default event handling + InitIfNeeded(); + if (!initialized) + return false; + PIC_RemoveEvents(manymouse_tick); + + // Flush events, handle critical ones + ManyMouseEvent event; + while (ManyMouse_PollEvent(&event)) + HandleEvent(event, true); // handle critical events + + bool success = false; + while (true) { + // Poll mouse events, handle critical ones + if (!ManyMouse_PollEvent(&event)) { + CALLBACK_Idle(); + continue; + } + if (event.device >= max_mice) + continue; + HandleEvent(event, true); + + // Wait for mouse button press + if (event.type != MANYMOUSE_EVENT_BUTTON || !event.value) + continue; + device_id = static_cast<uint8_t>(event.device); + + if (event.item >= 1) + break; // user cancelled the interactive mouse mapping + + // Do not accept already mapped devices + bool already_mapped = false; + for (const auto &interface : mouse_interfaces) + if (interface->IsMapped(device_id)) + already_mapped = true; + if (already_mapped) + continue; + + // Mouse probed successfully + device_id = static_cast<uint8_t>(event.device); + success = true; + break; + } + + if (is_mapping_in_effect && !mouse_config.no_mouse) + PIC_AddEvent(manymouse_tick, tick_interval); + return success; } uint8_t ManyMouseGlue::GetIdx(const std::regex ®ex) { - // Try to match the mouse name which is not mapped yet + // Try to match the mouse name which is not mapped yet - for (size_t i = 0; i < physical_devices.size(); i++) { - const auto &physical_device = physical_devices[i]; + for (size_t i = 0; i < physical_devices.size(); i++) { + const auto &physical_device = physical_devices[i]; - if (physical_device.IsDisconnected() || - !std::regex_match(physical_device.GetName(), regex)) - // mouse disconnected or name does not match - continue; + if (physical_device.IsDisconnected() || + !std::regex_match(physical_device.GetName(), regex)) + // mouse disconnected or name does not match + continue; - if (physical_device.GetMappedInterfaceId() == MouseInterfaceId::None) - // name matches, mouse not mapped yet - use it! - return static_cast<uint8_t>(i); - } + if (physical_device.GetMappedInterfaceId() == MouseInterfaceId::None) + // name matches, mouse not mapped yet - use it! + return static_cast<uint8_t>(i); + } - return max_mice; // return value which will be considered out of range + return max_mice; // return value which will be considered out of range } -void ManyMouseGlue::Map(const uint8_t physical_idx, - const MouseInterfaceId interface_id) +void ManyMouseGlue::Map(const uint8_t physical_idx, const MouseInterfaceId interface_id) { - assert(interface_id != MouseInterfaceId::None); + assert(interface_id != MouseInterfaceId::None); - if (physical_idx >= physical_devices.size()) { - UnMap(interface_id); - return; - } + if (physical_idx >= physical_devices.size()) { + UnMap(interface_id); + return; + } - auto &physical_device = physical_devices[physical_idx]; - if (interface_id == physical_device.GetMappedInterfaceId()) - return; // nothing to update - physical_device.mapped_id = interface_id; + auto &physical_device = physical_devices[physical_idx]; + if (interface_id == physical_device.GetMappedInterfaceId()) + return; // nothing to update + physical_device.mapped_id = interface_id; - MapFinalize(); + MapFinalize(); } void ManyMouseGlue::UnMap(const MouseInterfaceId interface_id) { - for (auto &physical_device : physical_devices) { - if (interface_id != physical_device.GetMappedInterfaceId()) - continue; // not a device to unmap - physical_device.mapped_id = MouseInterfaceId::None; - break; - } - - MapFinalize(); + for (auto &physical_device : physical_devices) { + if (interface_id != physical_device.GetMappedInterfaceId()) + continue; // not a device to unmap + physical_device.mapped_id = MouseInterfaceId::None; + break; + } + + MapFinalize(); } void ManyMouseGlue::MapFinalize() { - PIC_RemoveEvents(manymouse_tick); - is_mapping_in_effect = false; - for (const auto &entry : mouse_info.physical) { - if (entry.IsMapped()) - continue; - - is_mapping_in_effect = true; - if (!mouse_config.no_mouse) - PIC_AddEvent(manymouse_tick, tick_interval); - break; - } + PIC_RemoveEvents(manymouse_tick); + is_mapping_in_effect = false; + for (const auto &entry : mouse_info.physical) { + if (entry.IsMapped()) + continue; + + is_mapping_in_effect = true; + if (!mouse_config.no_mouse) + PIC_AddEvent(manymouse_tick, tick_interval); + break; + } } bool ManyMouseGlue::IsMappingInEffect() const { - return is_mapping_in_effect; + return is_mapping_in_effect; } -void ManyMouseGlue::HandleEvent(const ManyMouseEvent &event, - const bool critical_only) +void ManyMouseGlue::HandleEvent(const ManyMouseEvent &event, const bool critical_only) { - if (GCC_UNLIKELY(event.device >= mouse_info.physical.size())) - return; // device ID out of supported range - if (GCC_UNLIKELY(mouse_config.no_mouse && - event.type != MANYMOUSE_EVENT_DISCONNECT)) - return; // mouse control disabled in GUI - - const auto device_idx = static_cast<uint8_t>(event.device); - const auto interface_id = physical_devices[device_idx].GetMappedInterfaceId(); - const bool no_interface = (interface_id == MouseInterfaceId::None); - - switch (event.type) { - - case MANYMOUSE_EVENT_ABSMOTION: - // LOG_INFO("MANYMOUSE #%u ABSMOTION axis %d, %d", event.device, event.item, event.value); - break; - - case MANYMOUSE_EVENT_RELMOTION: - // LOG_INFO("MANYMOUSE #%u RELMOTION axis %d, %d", event.device, event.item, event.value); - if (no_interface || critical_only) - break; // movements not relevant at this moment - if (event.item != 0 && event.item != 1) - break; // only movements related to x and y axis are relevant - - if (rel_x.size() <= device_idx) { - rel_x.resize(static_cast<size_t>(device_idx + 1), 0); - rel_y.resize(static_cast<size_t>(device_idx + 1), 0); - } - - if (event.item) - rel_y[event.device] += event.value; // event.item 1 - else - rel_x[event.device] += event.value; // event.item 0 - break; - - case MANYMOUSE_EVENT_BUTTON: - // LOG_INFO("MANYMOUSE #%u BUTTON %u %s", event.device, event.item, event.value ? "press" : "release"); - if (no_interface || (critical_only && !event.value) || (event.item >= max_buttons)) - // TODO: Consider supporting extra mouse buttons - // in the future. On Linux event items 3-7 are for - // scroll wheel(s), 8 is for SDL button X1, 9 is - // for X2, etc. - but I don't know yet if this - // is consistent across various platforms - break; - MOUSE_EventButton(static_cast<uint8_t>(event.item), event.value, interface_id); - break; - - case MANYMOUSE_EVENT_SCROLL: - // LOG_INFO("MANYMOUSE #%u WHEEL #%u %d", event.device, event.item, event.value); - if (no_interface || critical_only || (event.item != 0)) - break; // only the 1st wheel is supported - MOUSE_EventWheel(clamp_to_int16(-event.value), interface_id); - break; - - case MANYMOUSE_EVENT_DISCONNECT: - // LOG_INFO("MANYMOUSE #%u DISCONNECT", event.device); - physical_devices[event.device].disconnected = true; - for (uint8_t button = 0; button < max_buttons; button++) - MOUSE_EventButton(button, false, interface_id); - MOUSE_NotifyDisconnect(interface_id); - break; - - default: - // LOG_INFO("MANYMOUSE #%u (other event)", event.device); - break; - } + if (GCC_UNLIKELY(event.device >= mouse_info.physical.size())) + return; // device ID out of supported range + if (GCC_UNLIKELY(mouse_config.no_mouse && + event.type != MANYMOUSE_EVENT_DISCONNECT)) + return; // mouse control disabled in GUI + + const auto device_idx = static_cast<uint8_t>(event.device); + const auto interface_id = physical_devices[device_idx].GetMappedInterfaceId(); + const bool no_interface = (interface_id == MouseInterfaceId::None); + + switch (event.type) { + case MANYMOUSE_EVENT_ABSMOTION: + // LOG_INFO("MANYMOUSE #%u ABSMOTION axis %d, %d", event.device, + // event.item, event.value); + break; + + case MANYMOUSE_EVENT_RELMOTION: + // LOG_INFO("MANYMOUSE #%u RELMOTION axis %d, %d", event.device, + // event.item, event.value); + if (no_interface || critical_only) + break; // movements not relevant at this moment + if (event.item != 0 && event.item != 1) + break; // only movements related to x and y axis are + // relevant + + if (rel_x.size() <= device_idx) { + rel_x.resize(static_cast<size_t>(device_idx + 1), 0); + rel_y.resize(static_cast<size_t>(device_idx + 1), 0); + } + + if (event.item) + rel_y[event.device] += event.value; // event.item 1 + else + rel_x[event.device] += event.value; // event.item 0 + break; + + case MANYMOUSE_EVENT_BUTTON: + // LOG_INFO("MANYMOUSE #%u BUTTON %u %s", event.device, + // event.item, event.value ? "press" : "release"); + if (no_interface || (critical_only && !event.value) || + (event.item >= max_buttons)) + // TODO: Consider supporting extra mouse buttons + // in the future. On Linux event items 3-7 are for + // scroll wheel(s), 8 is for SDL button X1, 9 is + // for X2, etc. - but I don't know yet if this + // is consistent across various platforms + break; + MOUSE_EventButton(static_cast<uint8_t>(event.item), + event.value, + interface_id); + break; + + case MANYMOUSE_EVENT_SCROLL: + // LOG_INFO("MANYMOUSE #%u WHEEL #%u %d", event.device, + // event.item, event.value); + if (no_interface || critical_only || (event.item != 0)) + break; // only the 1st wheel is supported + MOUSE_EventWheel(clamp_to_int16(-event.value), interface_id); + break; + + case MANYMOUSE_EVENT_DISCONNECT: + // LOG_INFO("MANYMOUSE #%u DISCONNECT", event.device); + physical_devices[event.device].disconnected = true; + for (uint8_t button = 0; button < max_buttons; button++) + MOUSE_EventButton(button, false, interface_id); + MOUSE_NotifyDisconnect(interface_id); + break; + + default: + // LOG_INFO("MANYMOUSE #%u (other event)", event.device); + break; + } } void ManyMouseGlue::Tick() { - assert(!mouse_config.no_mouse); - - // Handle all the events from the queue - ManyMouseEvent event; - while (ManyMouse_PollEvent(&event)) - HandleEvent(event); - - // Report accumulated mouse movements - for (uint8_t idx = 0; idx < rel_x.size(); idx++) { - if (rel_x[idx] == 0 && rel_y[idx] == 0) - continue; - - const auto interface_id = physical_devices[idx].GetMappedInterfaceId(); - MOUSE_EventMoved(static_cast<float>(rel_x[idx]), - static_cast<float>(rel_y[idx]), - interface_id); - rel_x[idx] = 0; - rel_y[idx] = 0; - } - - if (is_mapping_in_effect) - PIC_AddEvent(manymouse_tick, tick_interval); + assert(!mouse_config.no_mouse); + + // Handle all the events from the queue + ManyMouseEvent event; + while (ManyMouse_PollEvent(&event)) + HandleEvent(event); + + // Report accumulated mouse movements + for (uint8_t idx = 0; idx < rel_x.size(); idx++) { + if (rel_x[idx] == 0 && rel_y[idx] == 0) + continue; + + const auto interface_id = physical_devices[idx].GetMappedInterfaceId(); + MOUSE_EventMoved(static_cast<float>(rel_x[idx]), + static_cast<float>(rel_y[idx]), + interface_id); + rel_x[idx] = 0; + rel_y[idx] = 0; + } + + if (is_mapping_in_effect) + PIC_AddEvent(manymouse_tick, tick_interval); } #else @@ -442,42 +442,34 @@ void ManyMouseGlue::Tick() void ManyMouseGlue::RescanIfSafe() { - static bool already_warned = false; - if (!already_warned) { - LOG_ERR("MOUSE: This build has no ManyMouse support"); - already_warned = true; - } + static bool already_warned = false; + if (!already_warned) { + LOG_ERR("MOUSE: This build has no ManyMouse support"); + already_warned = true; + } } -void ManyMouseGlue::ShutdownIfSafe() -{ -} +void ManyMouseGlue::ShutdownIfSafe() {} -void ManyMouseGlue::StartConfigAPI() -{ -} +void ManyMouseGlue::StartConfigAPI() {} -void ManyMouseGlue::StopConfigAPI() -{ -} +void ManyMouseGlue::StopConfigAPI() {} bool ManyMouseGlue::ProbeForMapping(uint8_t &) { - return false; + return false; } uint8_t ManyMouseGlue::GetIdx(const std::regex &) { - return UINT8_MAX; + return UINT8_MAX; } -void ManyMouseGlue::Map(const uint8_t, const MouseInterfaceId) -{ -} +void ManyMouseGlue::Map(const uint8_t, const MouseInterfaceId) {} bool ManyMouseGlue::IsMappingInEffect() const { - return false; + return false; } #endif // C_MANYMOUSE diff --git a/src/hardware/mouse/mouse_manymouse.h b/src/hardware/mouse/mouse_manymouse.h index 4853982c3..034aee515 100644 --- a/src/hardware/mouse/mouse_manymouse.h +++ b/src/hardware/mouse/mouse_manymouse.h @@ -26,93 +26,89 @@ #include <vector> #if C_MANYMOUSE -#include "../../libs/manymouse/manymouse.h" +# include "../../libs/manymouse/manymouse.h" #endif // C_MANYMOUSE -class MousePhysical -{ +class MousePhysical { public: - MousePhysical(const std::string &name); + MousePhysical(const std::string &name); - bool IsMapped() const; - bool IsDisconnected() const; + bool IsMapped() const; + bool IsDisconnected() const; - MouseInterfaceId GetMappedInterfaceId() const; - const std::string &GetName() const; + MouseInterfaceId GetMappedInterfaceId() const; + const std::string &GetName() const; private: + friend class ManyMouseGlue; - friend class ManyMouseGlue; - - const std::string name = ""; - MouseInterfaceId mapped_id = MouseInterfaceId::None; - bool disconnected = false; + const std::string name = ""; + MouseInterfaceId mapped_id = MouseInterfaceId::None; + bool disconnected = false; }; class ManyMouseGlue final { public: + static ManyMouseGlue &GetInstance(); - static ManyMouseGlue &GetInstance(); - - void RescanIfSafe(); - void ShutdownIfSafe(); - void StartConfigAPI(); - void StopConfigAPI(); + void RescanIfSafe(); + void ShutdownIfSafe(); + void StartConfigAPI(); + void StopConfigAPI(); - bool ProbeForMapping(uint8_t &device_id); - uint8_t GetIdx(const std::regex ®ex); + bool ProbeForMapping(uint8_t &device_id); + uint8_t GetIdx(const std::regex ®ex); - void Map(const uint8_t physical_idx, - const MouseInterfaceId interface_id); + void Map(const uint8_t physical_idx, const MouseInterfaceId interface_id); - bool IsMappingInEffect() const; + bool IsMappingInEffect() const; private: + friend class MouseInterfaceInfoEntry; + friend class MousePhysicalInfoEntry; - friend class MouseInterfaceInfoEntry; - friend class MousePhysicalInfoEntry; - - ManyMouseGlue() = default; - ~ManyMouseGlue() = delete; - ManyMouseGlue(const ManyMouseGlue &) = delete; - ManyMouseGlue &operator=(const ManyMouseGlue &) = delete; + ManyMouseGlue() = default; + ~ManyMouseGlue() = delete; + ManyMouseGlue(const ManyMouseGlue &) = delete; + ManyMouseGlue &operator=(const ManyMouseGlue &) = delete; - void Tick(); - friend void manymouse_tick(uint32_t); + void Tick(); + friend void manymouse_tick(uint32_t); #if C_MANYMOUSE - void InitIfNeeded(); - void ShutdownForced(); - void ClearPhysicalMice(); - void Rescan(); + void InitIfNeeded(); + void ShutdownForced(); + void ClearPhysicalMice(); + void Rescan(); - void UnMap(const MouseInterfaceId interface_id); - void MapFinalize(); + void UnMap(const MouseInterfaceId interface_id); + void MapFinalize(); - void HandleEvent(const ManyMouseEvent &event, - const bool critical_only = false); + void HandleEvent(const ManyMouseEvent &event, + const bool critical_only = false); - bool initialized = false; - bool malfunction = false; // once set to false, will stay false forever - bool is_mapping_in_effect = false; - bool rescan_blocked_config = false; // true = rescan blocked due to config API usage - uint32_t config_api_counter = 0; + bool initialized = false; + bool malfunction = false; // once set to false, will stay false forever + bool is_mapping_in_effect = false; + bool rescan_blocked_config = false; // true = rescan blocked due to + // config API usage + uint32_t config_api_counter = 0; - int num_mice = 0; + int num_mice = 0; - std::string driver_name = ""; + std::string driver_name = ""; - std::vector<int> rel_x = {}; // not yet reported accumulated movements - std::vector<int> rel_y = {}; + std::vector<int> rel_x = {}; // not yet reported accumulated movements + std::vector<int> rel_y = {}; - static constexpr uint8_t max_buttons = 3; - static constexpr uint8_t max_mice = UINT8_MAX; - static constexpr double tick_interval = 5.0; + static constexpr uint8_t max_buttons = 3; + static constexpr uint8_t max_mice = UINT8_MAX; + static constexpr double tick_interval = 5.0; #endif // C_MANYMOUSE - std::vector<MousePhysical> physical_devices = {}; + std::vector<MousePhysical> physical_devices = {}; }; #endif // DOSBOX_MOUSE_MANYMOUSE_H diff --git a/src/hardware/mouse/mouse_queue.cpp b/src/hardware/mouse/mouse_queue.cpp index 095013fe4..a9d173a37 100644 --- a/src/hardware/mouse/mouse_queue.cpp +++ b/src/hardware/mouse/mouse_queue.cpp @@ -27,7 +27,6 @@ CHECK_NARROWING(); - // *************************************************************************** // Debug code, normally not enabled // *************************************************************************** @@ -35,23 +34,23 @@ CHECK_NARROWING(); // #define DEBUG_QUEUE_ENABLE #ifndef DEBUG_QUEUE_ENABLE -# define DEBUG_QUEUE(...) ; +# define DEBUG_QUEUE(...) ; #else // TODO: after migrating to C++20, allow to skip the 2nd argument by using // '__VA_OPT__(,) __VA_ARGS__' instead of ', __VA_ARGS__' -# define DEBUG_QUEUE(fmt, ...) \ - LOG_INFO("(queue) %04d: " fmt, DEBUG_GetDiffTicks(), __VA_ARGS__); +# define DEBUG_QUEUE(fmt, ...) \ + LOG_INFO("(queue) %04d: " fmt, DEBUG_GetDiffTicks(), __VA_ARGS__); static uint32_t DEBUG_GetDiffTicks() { - static uint32_t previous_ticks = 0; - uint32_t diff_ticks = 0; + static uint32_t previous_ticks = 0; + uint32_t diff_ticks = 0; - if (previous_ticks) - diff_ticks = PIC_Ticks - previous_ticks; + if (previous_ticks) + diff_ticks = PIC_Ticks - previous_ticks; - previous_ticks = PIC_Ticks; - return diff_ticks; + previous_ticks = PIC_Ticks; + return diff_ticks; } #endif @@ -62,251 +61,254 @@ static uint32_t DEBUG_GetDiffTicks() void mouse_queue_tick(uint32_t) { - MouseQueue::GetInstance().Tick(); + MouseQueue::GetInstance().Tick(); } MouseQueue &MouseQueue::GetInstance() { - static MouseQueue *instance = nullptr; - if (!instance) - instance = new MouseQueue(); - return *instance; + static MouseQueue *instance = nullptr; + if (!instance) + instance = new MouseQueue(); + return *instance; } void MouseQueue::SetRateDOS(const uint16_t rate_hz) { - // Convert rate in Hz to delay in milliseconds - start_delay.dos_ms = MOUSE_GetDelayFromRateHz(rate_hz); + // Convert rate in Hz to delay in milliseconds + start_delay.dos_ms = MOUSE_GetDelayFromRateHz(rate_hz); } void MouseQueue::SetRatePS2(const uint16_t rate_hz) { - // Convert rate in Hz to delay in milliseconds - start_delay.ps2_ms = MOUSE_GetDelayFromRateHz(rate_hz); + // Convert rate in Hz to delay in milliseconds + start_delay.ps2_ms = MOUSE_GetDelayFromRateHz(rate_hz); } void MouseQueue::AddEvent(MouseEvent &ev) { - DEBUG_QUEUE("AddEvent: %s %s", - ev.request_dos ? "DOS" : "---" - ev.request_ps2 ? "PS2" : "---"); - - // Prevent unnecessary processing - AggregateDosEvents(ev); - if (!ev.request_dos && !ev.request_ps2) - return; // event not relevant any more - - bool restart_timer = false; - if (ev.request_dos) { - if (!HasEventDos() && timer_in_progress && !delay.dos_ms) { - DEBUG_QUEUE("AddEvent: restart timer for %s", "DOS"); - // We do not want the timer to start only then PS/2 - // event gets processed - for minimum latency it is - // better to restart the timer - restart_timer = true; - } - - if (ev.dos_moved) { - // Mouse has moved - pending_dos_moved = true; - } else if (ev.dos_wheel) { - // Wheel has moved - pending_dos_wheel = true; - } else { - // Button press/release - pending_dos_button = true; - pending_dos_buttons_state = ev.dos_buttons; - } - } - - if (ev.request_ps2) { - if (!HasEventPS2() && timer_in_progress && !delay.ps2_ms) { - DEBUG_QUEUE("AddEvent: restart timer for %s", "PS2"); - // We do not want the timer to start only when other event - // gets processed - for minimum latency it is better to - // restart the timer - restart_timer = true; - } - - // Events for PS/2 interface (or virtual machine compatible - // drivers) do not carry any information - they are only - // notifications that new data is available - pending_ps2 |= ev.request_ps2; - } - - if (restart_timer) { - timer_in_progress = false; - PIC_RemoveEvents(mouse_queue_tick); - UpdateDelayCounters(); - StartTimerIfNeeded(); - } else if (!timer_in_progress) { - DEBUG_QUEUE("ActivateIRQ, in %s", __FUNCTION__); - // If no timer in progress, handle the event now - PIC_ActivateIRQ(12); - } + DEBUG_QUEUE("AddEvent: %s %s", + ev.request_dos ? "DOS" + : "---" ev.request_ps2 ? "PS2" + : "---"); + + // Prevent unnecessary processing + AggregateDosEvents(ev); + if (!ev.request_dos && !ev.request_ps2) + return; // event not relevant any more + + bool restart_timer = false; + if (ev.request_dos) { + if (!HasEventDos() && timer_in_progress && !delay.dos_ms) { + DEBUG_QUEUE("AddEvent: restart timer for %s", "DOS"); + // We do not want the timer to start only then PS/2 + // event gets processed - for minimum latency it is + // better to restart the timer + restart_timer = true; + } + + if (ev.dos_moved) { + // Mouse has moved + pending_dos_moved = true; + } else if (ev.dos_wheel) { + // Wheel has moved + pending_dos_wheel = true; + } else { + // Button press/release + pending_dos_button = true; + pending_dos_buttons_state = ev.dos_buttons; + } + } + + if (ev.request_ps2) { + if (!HasEventPS2() && timer_in_progress && !delay.ps2_ms) { + DEBUG_QUEUE("AddEvent: restart timer for %s", "PS2"); + // We do not want the timer to start only when other + // event gets processed - for minimum latency it is + // better to restart the timer + restart_timer = true; + } + + // Events for PS/2 interface (or virtual machine compatible + // drivers) do not carry any information - they are only + // notifications that new data is available + pending_ps2 |= ev.request_ps2; + } + + if (restart_timer) { + timer_in_progress = false; + PIC_RemoveEvents(mouse_queue_tick); + UpdateDelayCounters(); + StartTimerIfNeeded(); + } else if (!timer_in_progress) { + DEBUG_QUEUE("ActivateIRQ, in %s", __FUNCTION__); + // If no timer in progress, handle the event now + PIC_ActivateIRQ(12); + } } void MouseQueue::AggregateDosEvents(MouseEvent &ev) { - // We do not need duplicate move / wheel events - if (pending_dos_moved) - ev.dos_moved = false; - if (pending_dos_wheel) - ev.dos_wheel = false; - - // Same for mouse buttons - but in such case always update button data - if (pending_dos_button && ev.dos_button) { - ev.dos_button = false; - pending_dos_buttons_state = ev.dos_buttons; - } - - // Check if we still need this event - if (!ev.dos_moved && !ev.dos_wheel && !ev.dos_button) - ev.request_dos = false; + // We do not need duplicate move / wheel events + if (pending_dos_moved) + ev.dos_moved = false; + if (pending_dos_wheel) + ev.dos_wheel = false; + + // Same for mouse buttons - but in such case always update button data + if (pending_dos_button && ev.dos_button) { + ev.dos_button = false; + pending_dos_buttons_state = ev.dos_buttons; + } + + // Check if we still need this event + if (!ev.dos_moved && !ev.dos_wheel && !ev.dos_button) + ev.request_dos = false; } void MouseQueue::FetchEvent(MouseEvent &ev) { - // First try (prioritized) DOS events - if (HasReadyEventDos()) { - DEBUG_QUEUE("FetchEvent %s", "DOS"); - // Mark event as DOS one - ev.request_dos = true; - ev.dos_moved = pending_dos_moved; - ev.dos_button = pending_dos_button; - ev.dos_wheel = pending_dos_wheel; - ev.dos_buttons = pending_dos_buttons_state; - // Set delay before next DOS events - delay.dos_ms = start_delay.dos_ms; - // Clear event information - pending_dos_moved = false; - pending_dos_button = false; - pending_dos_wheel = false; - return; - } - - // Now try PS/2 event - if (HasReadyEventPS2()) { - DEBUG_QUEUE("FetchEvent %s", "PS2"); - // Set delay before next PS/2 events - delay.ps2_ms = start_delay.ps2_ms; - // PS/2 events are really dummy - merely a notification - // that something has happened and driver has to react - ev.request_ps2 = true; - pending_ps2 = false; - return; - } - - // Nothing to provide to interrupt handler, - // event will stay empty + // First try (prioritized) DOS events + if (HasReadyEventDos()) { + DEBUG_QUEUE("FetchEvent %s", "DOS"); + // Mark event as DOS one + ev.request_dos = true; + ev.dos_moved = pending_dos_moved; + ev.dos_button = pending_dos_button; + ev.dos_wheel = pending_dos_wheel; + ev.dos_buttons = pending_dos_buttons_state; + // Set delay before next DOS events + delay.dos_ms = start_delay.dos_ms; + // Clear event information + pending_dos_moved = false; + pending_dos_button = false; + pending_dos_wheel = false; + return; + } + + // Now try PS/2 event + if (HasReadyEventPS2()) { + DEBUG_QUEUE("FetchEvent %s", "PS2"); + // Set delay before next PS/2 events + delay.ps2_ms = start_delay.ps2_ms; + // PS/2 events are really dummy - merely a notification + // that something has happened and driver has to react + ev.request_ps2 = true; + pending_ps2 = false; + return; + } + + // Nothing to provide to interrupt handler, + // event will stay empty } void MouseQueue::ClearEventsDOS() { - // Clear DOS relevant part of the queue - pending_dos_moved = false; - pending_dos_button = false; - pending_dos_wheel = false; - delay.dos_ms = 0; - - // If timer is not needed, stop it - if (!HasEventAny()) { - timer_in_progress = false; - PIC_RemoveEvents(mouse_queue_tick); - } + // Clear DOS relevant part of the queue + pending_dos_moved = false; + pending_dos_button = false; + pending_dos_wheel = false; + delay.dos_ms = 0; + + // If timer is not needed, stop it + if (!HasEventAny()) { + timer_in_progress = false; + PIC_RemoveEvents(mouse_queue_tick); + } } void MouseQueue::StartTimerIfNeeded() { - // Do nothing if timer is already in progress - if (timer_in_progress) - return; - - bool timer_needed = false; - uint8_t delay_ms = UINT8_MAX; // dummy delay, will never be used - - if (HasEventPS2() || delay.ps2_ms) { - timer_needed = true; - delay_ms = std::min(delay_ms, delay.ps2_ms); - } - if (HasEventDos() || delay.dos_ms) { - timer_needed = true; - delay_ms = std::min(delay_ms, delay.dos_ms); - } - - // If queue is empty and all expired, we need no timer - if (!timer_needed) - return; - - // Enforce some non-zero delay between events; needed - // for example if DOS interrupt handler is busy - delay_ms = std::max(delay_ms, static_cast<uint8_t>(1)); - - // Start the timer - DEBUG_QUEUE("StartTimer, %d", delay_ms); - pic_ticks_start = PIC_Ticks; - timer_in_progress = true; - PIC_AddEvent(mouse_queue_tick, static_cast<double>(delay_ms)); + // Do nothing if timer is already in progress + if (timer_in_progress) + return; + + bool timer_needed = false; + uint8_t delay_ms = UINT8_MAX; // dummy delay, will never be used + + if (HasEventPS2() || delay.ps2_ms) { + timer_needed = true; + delay_ms = std::min(delay_ms, delay.ps2_ms); + } + if (HasEventDos() || delay.dos_ms) { + timer_needed = true; + delay_ms = std::min(delay_ms, delay.dos_ms); + } + + // If queue is empty and all expired, we need no timer + if (!timer_needed) + return; + + // Enforce some non-zero delay between events; needed + // for example if DOS interrupt handler is busy + delay_ms = std::max(delay_ms, static_cast<uint8_t>(1)); + + // Start the timer + DEBUG_QUEUE("StartTimer, %d", delay_ms); + pic_ticks_start = PIC_Ticks; + timer_in_progress = true; + PIC_AddEvent(mouse_queue_tick, static_cast<double>(delay_ms)); } void MouseQueue::UpdateDelayCounters() { - const uint32_t tmp = (PIC_Ticks > pic_ticks_start) ? (PIC_Ticks - pic_ticks_start) : 1; - uint8_t elapsed = static_cast<uint8_t>( + const uint32_t tmp = (PIC_Ticks > pic_ticks_start) + ? (PIC_Ticks - pic_ticks_start) + : 1; + uint8_t elapsed = static_cast<uint8_t>( std::min(tmp, static_cast<uint32_t>(UINT8_MAX))); - if (!pic_ticks_start) - elapsed = 1; + if (!pic_ticks_start) + elapsed = 1; - auto calc_new_delay = [](const uint8_t delay, const uint8_t elapsed) { - return static_cast<uint8_t>((delay > elapsed) ? (delay - elapsed) - : 0); - }; + auto calc_new_delay = [](const uint8_t delay, const uint8_t elapsed) { + return static_cast<uint8_t>((delay > elapsed) ? (delay - elapsed) + : 0); + }; - delay.dos_ms = calc_new_delay(delay.dos_ms, elapsed); - delay.ps2_ms = calc_new_delay(delay.ps2_ms, elapsed); + delay.dos_ms = calc_new_delay(delay.dos_ms, elapsed); + delay.ps2_ms = calc_new_delay(delay.ps2_ms, elapsed); - pic_ticks_start = 0; + pic_ticks_start = 0; } void MouseQueue::Tick() { - DEBUG_QUEUE("%s", "Tick"); - - timer_in_progress = false; - UpdateDelayCounters(); - - // If we have anything to pass to guest side via INT74, activate - // interrupt; otherwise start the timer again - if (HasReadyEventDos() || HasReadyEventPS2()) { - DEBUG_QUEUE("ActivateIRQ, in %s", __FUNCTION__); - PIC_ActivateIRQ(12); - } else - StartTimerIfNeeded(); + DEBUG_QUEUE("%s", "Tick"); + + timer_in_progress = false; + UpdateDelayCounters(); + + // If we have anything to pass to guest side via INT74, activate + // interrupt; otherwise start the timer again + if (HasReadyEventDos() || HasReadyEventPS2()) { + DEBUG_QUEUE("ActivateIRQ, in %s", __FUNCTION__); + PIC_ActivateIRQ(12); + } else + StartTimerIfNeeded(); } bool MouseQueue::HasEventDos() const { - return pending_dos_moved || pending_dos_button || pending_dos_wheel; + return pending_dos_moved || pending_dos_button || pending_dos_wheel; } bool MouseQueue::HasEventPS2() const { - return pending_ps2; + return pending_ps2; } bool MouseQueue::HasEventAny() const { - return HasEventDos() || HasEventPS2(); + return HasEventDos() || HasEventPS2(); } bool MouseQueue::HasReadyEventDos() const { - return HasEventDos() && !delay.dos_ms && - // do not launch DOS callback if it's busy - !mouse_shared.dos_cb_running; + return HasEventDos() && !delay.dos_ms && + // do not launch DOS callback if it's busy + !mouse_shared.dos_cb_running; } bool MouseQueue::HasReadyEventPS2() const { - return HasEventPS2() && !delay.ps2_ms; + return HasEventPS2() && !delay.ps2_ms; } diff --git a/src/hardware/mouse/mouse_queue.h b/src/hardware/mouse/mouse_queue.h index 7268cc4d1..e5cfe3c2c 100644 --- a/src/hardware/mouse/mouse_queue.h +++ b/src/hardware/mouse/mouse_queue.h @@ -21,64 +21,61 @@ #include "mouse_common.h" - class MouseQueue final { public: + static MouseQueue &GetInstance(); - static MouseQueue &GetInstance(); - - void SetRateDOS(const uint16_t rate_hz); // for DOS mouse driver - void SetRatePS2(const uint16_t rate_hz); // for PS/2 AUX port mice + void SetRateDOS(const uint16_t rate_hz); // for DOS mouse driver + void SetRatePS2(const uint16_t rate_hz); // for PS/2 AUX port mice - void AddEvent(MouseEvent &ev); - void FetchEvent(MouseEvent &ev); - void ClearEventsDOS(); - void StartTimerIfNeeded(); + void AddEvent(MouseEvent &ev); + void FetchEvent(MouseEvent &ev); + void ClearEventsDOS(); + void StartTimerIfNeeded(); private: - - MouseQueue() = default; - ~MouseQueue() = delete; - MouseQueue(const MouseQueue &) = delete; - MouseQueue &operator=(const MouseQueue &) = delete; - - void Tick(); - friend void mouse_queue_tick(uint32_t); - - void AggregateDosEvents(MouseEvent &ev); - void UpdateDelayCounters(); - uint8_t ClampStartDelay(float value_ms) const; - - struct { // initial value of delay counters, in milliseconds - uint8_t dos_ms = 5; - uint8_t ps2_ms = 5; - } start_delay = {}; - - // Time in milliseconds which has to elapse before event can take place - struct { - uint8_t dos_ms = 0; - uint8_t ps2_ms = 0; - } delay = {}; - - // Pending events, waiting to be passed to guest system - bool pending_dos_moved = false; - bool pending_dos_button = false; - bool pending_dos_wheel = false; - bool pending_ps2 = false; - - MouseButtons12S pending_dos_buttons_state = 0; - - bool timer_in_progress = false; - uint32_t pic_ticks_start = 0; // PIC_Ticks value when timer starts - - // Helpers to check if there are events in the queue - bool HasEventDos() const; - bool HasEventPS2() const; - bool HasEventAny() const; - - // Helpers to check if there are events ready to be handled - bool HasReadyEventDos() const; - bool HasReadyEventPS2() const; + MouseQueue() = default; + ~MouseQueue() = delete; + MouseQueue(const MouseQueue &) = delete; + MouseQueue &operator=(const MouseQueue &) = delete; + + void Tick(); + friend void mouse_queue_tick(uint32_t); + + void AggregateDosEvents(MouseEvent &ev); + void UpdateDelayCounters(); + uint8_t ClampStartDelay(float value_ms) const; + + struct { // initial value of delay counters, in milliseconds + uint8_t dos_ms = 5; + uint8_t ps2_ms = 5; + } start_delay = {}; + + // Time in milliseconds which has to elapse before event can take place + struct { + uint8_t dos_ms = 0; + uint8_t ps2_ms = 0; + } delay = {}; + + // Pending events, waiting to be passed to guest system + bool pending_dos_moved = false; + bool pending_dos_button = false; + bool pending_dos_wheel = false; + bool pending_ps2 = false; + + MouseButtons12S pending_dos_buttons_state = 0; + + bool timer_in_progress = false; + uint32_t pic_ticks_start = 0; // PIC_Ticks value when timer starts + + // Helpers to check if there are events in the queue + bool HasEventDos() const; + bool HasEventPS2() const; + bool HasEventAny() const; + + // Helpers to check if there are events ready to be handled + bool HasReadyEventDos() const; + bool HasReadyEventPS2() const; }; #endif // DOSBOX_MOUSE_QUEUE_H diff --git a/src/hardware/mouse/mouseif_dos_driver.cpp b/src/hardware/mouse/mouseif_dos_driver.cpp index fea52df11..08e5111d6 100644 --- a/src/hardware/mouse/mouseif_dos_driver.cpp +++ b/src/hardware/mouse/mouseif_dos_driver.cpp @@ -49,8 +49,8 @@ CHECK_NARROWING(); // - https://www.stanislavs.org/helppc/int_33.html // - http://www2.ift.ulaval.ca/~marchand/ift17583/dosints.pdf -static constexpr uint8_t cursor_size_x = 16; -static constexpr uint8_t cursor_size_y = 16; +static constexpr uint8_t cursor_size_x = 16; +static constexpr uint8_t cursor_size_y = 16; static constexpr uint16_t cursor_size_xy = cursor_size_x * cursor_size_y; static constexpr uint8_t num_buttons = 3; @@ -59,27 +59,27 @@ enum class MouseCursor : uint8_t { Software = 0, Hardware = 1, Text = 2 }; // This enum has to be compatible with mask in DOS driver function 0x0c enum class MouseEventId : uint8_t { - NotDosEvent = 0, - MouseHasMoved = 1 << 0, - PressedLeft = 1 << 1, - ReleasedLeft = 1 << 2, - PressedRight = 1 << 3, - ReleasedRight = 1 << 4, - PressedMiddle = 1 << 5, - ReleasedMiddle = 1 << 6, - WheelHasMoved = 1 << 0, + NotDosEvent = 0, + MouseHasMoved = 1 << 0, + PressedLeft = 1 << 1, + ReleasedLeft = 1 << 2, + PressedRight = 1 << 3, + ReleasedRight = 1 << 4, + PressedMiddle = 1 << 5, + ReleasedMiddle = 1 << 6, + WheelHasMoved = 1 << 0, }; // These values represent 'hardware' state, not driver state static MouseButtons12S buttons = 0; -static float pos_x = 0.0f; -static float pos_y = 0.0f; -static int8_t counter_w = 0; // wheel counter -static bool is_mapped = false; // true = physical mouse is mapped to this interface -static bool raw_input = true; // true = no host mouse acceleration pre-applied +static float pos_x = 0.0f; +static float pos_y = 0.0f; +static int8_t counter_w = 0; // wheel counter +static bool is_mapped = false; // true = physical mouse is mapped to this interface +static bool raw_input = true; // true = no host mouse acceleration pre-applied -static bool rate_is_set = false; // true = rate was set by DOS application +static bool rate_is_set = false; // true = rate was set by DOS application static uint16_t rate_hz = 0; static uint16_t min_rate_hz = 0; @@ -87,121 +87,122 @@ static uint16_t min_rate_hz = 0; // but not necessary visible to the application static struct { - // Mouse movement - float x_rel = 0.0f; - float y_rel = 0.0f; - uint16_t x_abs = 0; - uint16_t y_abs = 0; - - // Wheel movement - int16_t w_rel = 0; - - void Reset() { - x_rel = 0.0f; - y_rel = 0.0f; - w_rel = 0; - } + // Mouse movement + float x_rel = 0.0f; + float y_rel = 0.0f; + uint16_t x_abs = 0; + uint16_t y_abs = 0; + + // Wheel movement + int16_t w_rel = 0; + + void Reset() + { + x_rel = 0.0f; + y_rel = 0.0f; + w_rel = 0; + } } pending; // Multiply by 6.0f to compensate for 'MOUSE_GetBallisticsCoeff', which uses // 6 as intersection point (just like 2:1 scaling model from PS/2 specification) constexpr float acceleration_multiplier = 6.0f; -static MouseSpeedCalculator speed_mickeys(acceleration_multiplier * - mouse_predefined.acceleration_dos); +static MouseSpeedCalculator speed_mickeys( + acceleration_multiplier *mouse_predefined.acceleration_dos); static struct { // DOS driver state - // Structure containing (only!) data which should be - // saved/restored during task switching - - // DANGER, WILL ROBINSON! - // - // This whole structure can be read or written from the guest side - // via virtual DOS driver, functions 0x15 / 0x16 / 0x17. - // Do not put here any array indices, pointers, or anything that - // can crash the emulator if filled-in incorrectly, or that can - // be used by malicious code to escape from emulation! - - bool enabled = false; // TODO: make use of this - bool wheel_api = false; // CuteMouse compatible wheel extension - - uint16_t times_pressed[num_buttons] = {0}; - uint16_t times_released[num_buttons] = {0}; - uint16_t last_released_x[num_buttons] = {0}; - uint16_t last_released_y[num_buttons] = {0}; - uint16_t last_pressed_x[num_buttons] = {0}; - uint16_t last_pressed_y[num_buttons] = {0}; - uint16_t last_wheel_moved_x = 0; - uint16_t last_wheel_moved_y = 0; - - int16_t mickey_counter_x = 0; - int16_t mickey_counter_y = 0; - - float mickey_delta_x = 0.0f; - float mickey_delta_y = 0.0f; - - float mickeys_per_pixel_x = 0.0f; - float mickeys_per_pixel_y = 0.0f; - - uint16_t double_speed_threshold = 0; // in mickeys/s - - uint16_t granularity_x = 0; // mask - uint16_t granularity_y = 0; - - int16_t update_region_x[2] = {0}; - int16_t update_region_y[2] = {0}; - - uint16_t language = 0; // language for driver messages, unused - uint8_t mode = 0; - - // sensitivity - uint8_t sensitivity_x = 0; - uint8_t sensitivity_y = 0; - // TODO: find out what it is for (acceleration?), for now - // just set it to default value on startup - uint8_t unknown_01 = 50; - - float sensitivity_coeff_x = 0; - float sensitivity_coeff_y = 0; - - // mouse position allowed range - int16_t minpos_x = 0; - int16_t maxpos_x = 0; - int16_t minpos_y = 0; - int16_t maxpos_y = 0; - - // mouse cursor - uint8_t page = 0; // cursor display page number - bool inhibit_draw = false; - uint16_t hidden = 0; - uint16_t oldhidden = 0; - int16_t clipx = 0; - int16_t clipy = 0; - int16_t hot_x = 0; // cursor hot spot, horizontal - int16_t hot_y = 0; // cursor hot spot, vertical - - struct { - bool enabled = false; - uint16_t pos_x = 0; - uint16_t pos_y = 0; - uint8_t data[cursor_size_xy] = {0}; - - } background = {}; - - MouseCursor cursor_type = MouseCursor::Software; - - // cursor shape definition - uint16_t text_and_mask = 0; - uint16_t text_xor_mask = 0; - bool user_screen_mask = false; - bool user_cursor_mask = false; - uint16_t user_def_screen_mask[cursor_size_x] = {0}; - uint16_t user_def_cursor_mask[cursor_size_y] = {0}; - - // user callback - uint16_t user_callback_mask = 0; - uint16_t user_callback_segment = 0; - uint16_t user_callback_offset = 0; + // Structure containing (only!) data which should be + // saved/restored during task switching + + // DANGER, WILL ROBINSON! + // + // This whole structure can be read or written from the guest side + // via virtual DOS driver, functions 0x15 / 0x16 / 0x17. + // Do not put here any array indices, pointers, or anything that + // can crash the emulator if filled-in incorrectly, or that can + // be used by malicious code to escape from emulation! + + bool enabled = false; // TODO: make use of this + bool wheel_api = false; // CuteMouse compatible wheel extension + + uint16_t times_pressed[num_buttons] = {0}; + uint16_t times_released[num_buttons] = {0}; + uint16_t last_released_x[num_buttons] = {0}; + uint16_t last_released_y[num_buttons] = {0}; + uint16_t last_pressed_x[num_buttons] = {0}; + uint16_t last_pressed_y[num_buttons] = {0}; + uint16_t last_wheel_moved_x = 0; + uint16_t last_wheel_moved_y = 0; + + int16_t mickey_counter_x = 0; + int16_t mickey_counter_y = 0; + + float mickey_delta_x = 0.0f; + float mickey_delta_y = 0.0f; + + float mickeys_per_pixel_x = 0.0f; + float mickeys_per_pixel_y = 0.0f; + + uint16_t double_speed_threshold = 0; // in mickeys/s + + uint16_t granularity_x = 0; // mask + uint16_t granularity_y = 0; + + int16_t update_region_x[2] = {0}; + int16_t update_region_y[2] = {0}; + + uint16_t language = 0; // language for driver messages, unused + uint8_t mode = 0; + + // sensitivity + uint8_t sensitivity_x = 0; + uint8_t sensitivity_y = 0; + // TODO: find out what it is for (acceleration?), for now + // just set it to default value on startup + uint8_t unknown_01 = 50; + + float sensitivity_coeff_x = 0; + float sensitivity_coeff_y = 0; + + // mouse position allowed range + int16_t minpos_x = 0; + int16_t maxpos_x = 0; + int16_t minpos_y = 0; + int16_t maxpos_y = 0; + + // mouse cursor + uint8_t page = 0; // cursor display page number + bool inhibit_draw = false; + uint16_t hidden = 0; + uint16_t oldhidden = 0; + int16_t clipx = 0; + int16_t clipy = 0; + int16_t hot_x = 0; // cursor hot spot, horizontal + int16_t hot_y = 0; // cursor hot spot, vertical + + struct { + bool enabled = false; + uint16_t pos_x = 0; + uint16_t pos_y = 0; + uint8_t data[cursor_size_xy] = {0}; + + } background = {}; + + MouseCursor cursor_type = MouseCursor::Software; + + // cursor shape definition + uint16_t text_and_mask = 0; + uint16_t text_xor_mask = 0; + bool user_screen_mask = false; + bool user_cursor_mask = false; + uint16_t user_def_screen_mask[cursor_size_x] = {0}; + uint16_t user_def_cursor_mask[cursor_size_y] = {0}; + + // user callback + uint16_t user_callback_mask = 0; + uint16_t user_callback_segment = 0; + uint16_t user_callback_offset = 0; } state; @@ -213,39 +214,39 @@ static RealPt user_callback; static uint8_t signed_to_reg8(const int8_t x) { - if (x >= 0) - return static_cast<uint8_t>(x); - else - // -1 for 0xff, -2 for 0xfe, etc. - return static_cast<uint8_t>(0x100 + x); + if (x >= 0) + return static_cast<uint8_t>(x); + else + // -1 for 0xff, -2 for 0xfe, etc. + return static_cast<uint8_t>(0x100 + x); } static uint16_t signed_to_reg16(const int16_t x) { - if (x >= 0) - return static_cast<uint16_t>(x); - else - // -1 for 0xffff, -2 for 0xfffe, etc. - return static_cast<uint16_t>(0x10000 + x); + if (x >= 0) + return static_cast<uint16_t>(x); + else + // -1 for 0xffff, -2 for 0xfffe, etc. + return static_cast<uint16_t>(0x10000 + x); } static int16_t reg_to_signed16(const uint16_t x) { - if (bit::is(x, b15)) - // 0xffff for -1, 0xfffe for -2, etc. - return static_cast<int16_t>(x - 0x10000); - else - return static_cast<int16_t>(x); + if (bit::is(x, b15)) + // 0xffff for -1, 0xfffe for -2, etc. + return static_cast<int16_t>(x - 0x10000); + else + return static_cast<int16_t>(x); } static uint16_t get_pos_x() { - return static_cast<uint16_t>(std::lround(pos_x)) & state.granularity_x; + return static_cast<uint16_t>(std::lround(pos_x)) & state.granularity_x; } static uint16_t get_pos_y() { - return static_cast<uint16_t>(std::lround(pos_y)) & state.granularity_y; + return static_cast<uint16_t>(std::lround(pos_y)) & state.granularity_y; } // *************************************************************************** @@ -256,13 +257,13 @@ static constexpr uint16_t default_text_and_mask = 0x77FF; static constexpr uint16_t default_text_xor_mask = 0x7700; static uint16_t default_screen_mask[cursor_size_y] = { - 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF, 0x03FF, 0x01FF, 0x00FF, 0x007F, - 0x003F, 0x001F, 0x01FF, 0x00FF, 0x30FF, 0xF87F, 0xF87F, 0xFCFF + 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF, 0x03FF, 0x01FF, 0x00FF, 0x007F, + 0x003F, 0x001F, 0x01FF, 0x00FF, 0x30FF, 0xF87F, 0xF87F, 0xFCFF }; static uint16_t default_cursor_mask[cursor_size_y] = { - 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, - 0x7F80, 0x7C00, 0x6C00, 0x4600, 0x0600, 0x0300, 0x0300, 0x0000 + 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7C00, 0x7E00, 0x7F00, + 0x7F80, 0x7C00, 0x6C00, 0x4600, 0x0600, 0x0300, 0x0300, 0x0000 }; // *************************************************************************** @@ -276,41 +277,41 @@ extern void ReadCharAttr(uint16_t col, uint16_t row, uint8_t page, uint16_t *res static void restore_cursor_background_text() { - if (state.hidden || state.inhibit_draw) - return; - - if (state.background.enabled) { - WriteChar(state.background.pos_x, - state.background.pos_y, - real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_PAGE), - state.background.data[0], - state.background.data[1], - true); - state.background.enabled = false; - } + if (state.hidden || state.inhibit_draw) + return; + + if (state.background.enabled) { + WriteChar(state.background.pos_x, + state.background.pos_y, + real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_PAGE), + state.background.data[0], + state.background.data[1], + true); + state.background.enabled = false; + } } static void draw_cursor_text() { - // Restore Background - restore_cursor_background_text(); - - // Check if cursor in update region - auto x = get_pos_x(); - auto y = get_pos_y(); - if ((y <= state.update_region_y[1]) && (y >= state.update_region_y[0]) && - (x <= state.update_region_x[1]) && (x >= state.update_region_x[0])) { - return; - } - - // Save Background - state.background.pos_x = static_cast<uint16_t>(x / 8); - state.background.pos_y = static_cast<uint16_t>(y / 8); - if (state.mode < 2) - state.background.pos_x = state.background.pos_x / 2; - - // use current page (CV program) - uint8_t page = real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_PAGE); + // Restore Background + restore_cursor_background_text(); + + // Check if cursor in update region + auto x = get_pos_x(); + auto y = get_pos_y(); + if ((y <= state.update_region_y[1]) && (y >= state.update_region_y[0]) && + (x <= state.update_region_x[1]) && (x >= state.update_region_x[0])) { + return; + } + + // Save Background + state.background.pos_x = static_cast<uint16_t>(x / 8); + state.background.pos_y = static_cast<uint16_t>(y / 8); + if (state.mode < 2) + state.background.pos_x = state.background.pos_x / 2; + + // use current page (CV program) + uint8_t page = real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_PAGE); if (state.cursor_type == MouseCursor::Software) { uint16_t result = 0; @@ -333,20 +334,22 @@ static void draw_cursor_text() read_high_byte(result), true); } else { - uint16_t address = static_cast<uint16_t>(page * - real_readw(BIOSMEM_SEG, BIOSMEM_PAGE_SIZE)); - address = static_cast<uint16_t>(address + - (state.background.pos_y * real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS) + - state.background.pos_x) * 2); - address /= 2; - uint16_t cr = real_readw(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS); - IO_Write(cr, 0xe); - IO_Write(static_cast<io_port_t>(cr + 1), - static_cast<uint8_t>((address >> 8) & 0xff)); - IO_Write(cr, 0xf); - IO_Write(static_cast<io_port_t>(cr + 1), - static_cast<uint8_t>(address & 0xff)); - } + uint16_t address = static_cast<uint16_t>( + page * real_readw(BIOSMEM_SEG, BIOSMEM_PAGE_SIZE)); + address = static_cast<uint16_t>( + address + (state.background.pos_y * + real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS) + + state.background.pos_x) * + 2); + address /= 2; + uint16_t cr = real_readw(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS); + IO_Write(cr, 0xe); + IO_Write(static_cast<io_port_t>(cr + 1), + static_cast<uint8_t>((address >> 8) & 0xff)); + IO_Write(cr, 0xf); + IO_Write(static_cast<io_port_t>(cr + 1), + static_cast<uint8_t>(address & 0xff)); + } } // *************************************************************************** @@ -354,218 +357,221 @@ static void draw_cursor_text() // *************************************************************************** static struct { - - uint8_t sequ_address = 0; - uint8_t sequ_data = 0; - uint8_t grdc_address[9] = {0}; + uint8_t sequ_address = 0; + uint8_t sequ_data = 0; + uint8_t grdc_address[9] = {0}; } vga_regs; static void save_vga_registers() { - if (IS_VGA_ARCH) { - for (uint8_t i = 0; i < 9; i++) { - IO_Write(VGAREG_GRDC_ADDRESS, i); - vga_regs.grdc_address[i] = IO_Read(VGAREG_GRDC_DATA); - } - // Setup some default values in GFX regs that should work - IO_Write(VGAREG_GRDC_ADDRESS, 3); - IO_Write(VGAREG_GRDC_DATA, 0); // disable rotate and operation - IO_Write(VGAREG_GRDC_ADDRESS, 5); - IO_Write(VGAREG_GRDC_DATA, vga_regs.grdc_address[5] & 0xf0); // Force read/write mode 0 - - // Set Map to all planes. Celtic Tales - vga_regs.sequ_address = IO_Read(VGAREG_SEQU_ADDRESS); - IO_Write(VGAREG_SEQU_ADDRESS, 2); - vga_regs.sequ_data = IO_Read(VGAREG_SEQU_DATA); - IO_Write(VGAREG_SEQU_DATA, 0xF); - } else if (machine == MCH_EGA) { - // Set Map to all planes. - IO_Write(VGAREG_SEQU_ADDRESS, 2); - IO_Write(VGAREG_SEQU_DATA, 0xF); - } + if (IS_VGA_ARCH) { + for (uint8_t i = 0; i < 9; i++) { + IO_Write(VGAREG_GRDC_ADDRESS, i); + vga_regs.grdc_address[i] = IO_Read(VGAREG_GRDC_DATA); + } + // Setup some default values in GFX regs that should work + IO_Write(VGAREG_GRDC_ADDRESS, 3); + IO_Write(VGAREG_GRDC_DATA, 0); // disable rotate and operation + IO_Write(VGAREG_GRDC_ADDRESS, 5); + IO_Write(VGAREG_GRDC_DATA, + vga_regs.grdc_address[5] & 0xf0); // Force read/write + // mode 0 + + // Set Map to all planes. Celtic Tales + vga_regs.sequ_address = IO_Read(VGAREG_SEQU_ADDRESS); + IO_Write(VGAREG_SEQU_ADDRESS, 2); + vga_regs.sequ_data = IO_Read(VGAREG_SEQU_DATA); + IO_Write(VGAREG_SEQU_DATA, 0xF); + } else if (machine == MCH_EGA) { + // Set Map to all planes. + IO_Write(VGAREG_SEQU_ADDRESS, 2); + IO_Write(VGAREG_SEQU_DATA, 0xF); + } } static void restore_vga_registers() { - if (IS_VGA_ARCH) { - for (uint8_t i = 0; i < 9; i++) { - IO_Write(VGAREG_GRDC_ADDRESS, i); - IO_Write(VGAREG_GRDC_DATA, vga_regs.grdc_address[i]); - } - - IO_Write(VGAREG_SEQU_ADDRESS, 2); - IO_Write(VGAREG_SEQU_DATA, vga_regs.sequ_data); - IO_Write(VGAREG_SEQU_ADDRESS, vga_regs.sequ_address); - } + if (IS_VGA_ARCH) { + for (uint8_t i = 0; i < 9; i++) { + IO_Write(VGAREG_GRDC_ADDRESS, i); + IO_Write(VGAREG_GRDC_DATA, vga_regs.grdc_address[i]); + } + + IO_Write(VGAREG_SEQU_ADDRESS, 2); + IO_Write(VGAREG_SEQU_DATA, vga_regs.sequ_data); + IO_Write(VGAREG_SEQU_ADDRESS, vga_regs.sequ_address); + } } static void clip_cursor_area(int16_t &x1, int16_t &x2, int16_t &y1, int16_t &y2, uint16_t &addx1, uint16_t &addx2, uint16_t &addy) { - addx1 = 0; - addx2 = 0; - addy = 0; - // Clip up - if (y1 < 0) { - addy = static_cast<uint16_t>(addy - y1); - y1 = 0; - } - // Clip down - if (y2 > state.clipy) { - y2 = state.clipy; - }; - // Clip left - if (x1 < 0) { - addx1 = static_cast<uint16_t>(addx1 - x1); - x1 = 0; - }; - // Clip right - if (x2 > state.clipx) { - addx2 = static_cast<uint16_t>(x2 - state.clipx); - x2 = state.clipx; - }; + addx1 = 0; + addx2 = 0; + addy = 0; + // Clip up + if (y1 < 0) { + addy = static_cast<uint16_t>(addy - y1); + y1 = 0; + } + // Clip down + if (y2 > state.clipy) { + y2 = state.clipy; + }; + // Clip left + if (x1 < 0) { + addx1 = static_cast<uint16_t>(addx1 - x1); + x1 = 0; + }; + // Clip right + if (x2 > state.clipx) { + addx2 = static_cast<uint16_t>(x2 - state.clipx); + x2 = state.clipx; + }; } static void restore_cursor_background() { - if (state.hidden || state.inhibit_draw || !state.background.enabled) - return; - - save_vga_registers(); - - // Restore background - uint16_t addx1, addx2, addy; - uint16_t data_pos = 0; - int16_t x1 = static_cast<int16_t>(state.background.pos_x); - int16_t y1 = static_cast<int16_t>(state.background.pos_y); - int16_t x2 = static_cast<int16_t>(x1 + cursor_size_x - 1); - int16_t y2 = static_cast<int16_t>(y1 + cursor_size_y - 1); - - clip_cursor_area(x1, x2, y1, y2, addx1, addx2, addy); - - data_pos = static_cast<uint16_t>(addy * cursor_size_x); - for (int16_t y = y1; y <= y2; y++) { - data_pos = static_cast<uint16_t>(data_pos + addx1); - for (int16_t x = x1; x <= x2; x++) { - INT10_PutPixel(static_cast<uint16_t>(x), - static_cast<uint16_t>(y), - state.page, - state.background.data[data_pos++]); - }; - data_pos = static_cast<uint16_t>(data_pos + addx2); - }; - state.background.enabled = false; - - restore_vga_registers(); + if (state.hidden || state.inhibit_draw || !state.background.enabled) + return; + + save_vga_registers(); + + // Restore background + uint16_t addx1, addx2, addy; + uint16_t data_pos = 0; + int16_t x1 = static_cast<int16_t>(state.background.pos_x); + int16_t y1 = static_cast<int16_t>(state.background.pos_y); + int16_t x2 = static_cast<int16_t>(x1 + cursor_size_x - 1); + int16_t y2 = static_cast<int16_t>(y1 + cursor_size_y - 1); + + clip_cursor_area(x1, x2, y1, y2, addx1, addx2, addy); + + data_pos = static_cast<uint16_t>(addy * cursor_size_x); + for (int16_t y = y1; y <= y2; y++) { + data_pos = static_cast<uint16_t>(data_pos + addx1); + for (int16_t x = x1; x <= x2; x++) { + INT10_PutPixel(static_cast<uint16_t>(x), + static_cast<uint16_t>(y), + state.page, + state.background.data[data_pos++]); + }; + data_pos = static_cast<uint16_t>(data_pos + addx2); + }; + state.background.enabled = false; + + restore_vga_registers(); } void MOUSEDOS_DrawCursor() { - if (state.hidden || state.inhibit_draw) - return; - INT10_SetCurMode(); - // In Textmode ? - if (CurMode->type == M_TEXT) { - draw_cursor_text(); - return; - } - - // Check video page. Seems to be ignored for text mode. - // hence the text mode handled above this - // >>> removed because BIOS page is not actual page in some cases, e.g. - // QQP games - // if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE) != state.page) - // return; - - // Check if cursor in update region - /* if ((get_pos_x() >= state.update_region_x[0]) && (get_pos_y() <= - state.update_region_x[1]) && (get_pos_y() >= state.update_region_y[0]) - && (GETPOS_Y <= state.update_region_y[1])) { if - (CurMode->type==M_TEXT16) restore_cursor_background_text(); else - restore_cursor_background(); - --mouse.shown; - return; - } - */ /*Not sure yet what to do update region should be set to ??? */ - - // Get Clipping ranges - - state.clipx = static_cast<int16_t>((Bits)CurMode->swidth - 1); // Get from BIOS? - state.clipy = static_cast<int16_t>((Bits)CurMode->sheight - 1); - - // might be vidmode == 0x13?2:1 - int16_t xratio = 640; - if (CurMode->swidth > 0) - xratio = static_cast<int16_t>(xratio / CurMode->swidth); - if (xratio == 0) - xratio = 1; - - restore_cursor_background(); - - save_vga_registers(); - - // Save Background - uint16_t addx1, addx2, addy; - uint16_t data_pos = 0; - int16_t x1 = static_cast<int16_t>(get_pos_x() / xratio - state.hot_x); - int16_t y1 = static_cast<int16_t>(get_pos_y() - state.hot_y); - int16_t x2 = static_cast<int16_t>(x1 + cursor_size_x - 1); - int16_t y2 = static_cast<int16_t>(y1 + cursor_size_y - 1); - - clip_cursor_area(x1, x2, y1, y2, addx1, addx2, addy); - - data_pos = static_cast<uint16_t>(addy * cursor_size_x); - for (int16_t y = y1; y <= y2; y++) { - data_pos = static_cast<uint16_t>(data_pos + addx1); - for (int16_t x = x1; x <= x2; x++) { - INT10_GetPixel(static_cast<uint16_t>(x), - static_cast<uint16_t>(y), - state.page, - &state.background.data[data_pos++]); - }; - data_pos = static_cast<uint16_t>(data_pos + addx2); - }; - state.background.enabled = true; - state.background.pos_x = static_cast<uint16_t>(get_pos_x() / xratio - state.hot_x); - state.background.pos_y = static_cast<uint16_t>(get_pos_y() - state.hot_y); - - // Draw Mousecursor - data_pos = static_cast<uint16_t>(addy * cursor_size_x); - const auto screen_mask = state.user_screen_mask ? state.user_def_screen_mask - : default_screen_mask; - const auto cursor_mask = state.user_cursor_mask ? state.user_def_cursor_mask - : default_cursor_mask; - for (int16_t y = y1; y <= y2; y++) { - uint16_t sc_mask = screen_mask[addy + y - y1]; - uint16_t cu_mask = cursor_mask[addy + y - y1]; - if (addx1 > 0) { - sc_mask = static_cast<uint16_t>(sc_mask << addx1); - cu_mask = static_cast<uint16_t>(cu_mask << addx1); - data_pos = static_cast<uint16_t>(data_pos + addx1); - }; - for (int16_t x = x1; x <= x2; x++) { - constexpr auto highest_bit = (1 << (cursor_size_x - 1)); - uint8_t pixel = 0; - // ScreenMask - if (sc_mask & highest_bit) - pixel = state.background.data[data_pos]; - // CursorMask - if (cu_mask & highest_bit) - pixel = pixel ^ 0x0f; - sc_mask = static_cast<uint16_t>(sc_mask << 1); - cu_mask = static_cast<uint16_t>(cu_mask << 1); - // Set Pixel - INT10_PutPixel(static_cast<uint16_t>(x), - static_cast<uint16_t>(y), - state.page, - pixel); - ++data_pos; - }; - data_pos = static_cast<uint16_t>(addx2 + data_pos); - }; - - restore_vga_registers(); + if (state.hidden || state.inhibit_draw) + return; + INT10_SetCurMode(); + // In Textmode ? + if (CurMode->type == M_TEXT) { + draw_cursor_text(); + return; + } + + // Check video page. Seems to be ignored for text mode. + // hence the text mode handled above this + // >>> removed because BIOS page is not actual page in some cases, e.g. + // QQP games + // if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE) != state.page) + // return; + + // Check if cursor in update region + /* if ((get_pos_x() >= state.update_region_x[0]) && (get_pos_y() <= + state.update_region_x[1]) && (get_pos_y() >= state.update_region_y[0]) + && (GETPOS_Y <= state.update_region_y[1])) { if + (CurMode->type==M_TEXT16) restore_cursor_background_text(); else + restore_cursor_background(); + --mouse.shown; + return; + } + */ /*Not sure yet what to do update region should be set to ??? */ + + // Get Clipping ranges + + state.clipx = static_cast<int16_t>((Bits)CurMode->swidth - 1); // Get from + // BIOS? + state.clipy = static_cast<int16_t>((Bits)CurMode->sheight - 1); + + // might be vidmode == 0x13?2:1 + int16_t xratio = 640; + if (CurMode->swidth > 0) + xratio = static_cast<int16_t>(xratio / CurMode->swidth); + if (xratio == 0) + xratio = 1; + + restore_cursor_background(); + + save_vga_registers(); + + // Save Background + uint16_t addx1, addx2, addy; + uint16_t data_pos = 0; + int16_t x1 = static_cast<int16_t>(get_pos_x() / xratio - state.hot_x); + int16_t y1 = static_cast<int16_t>(get_pos_y() - state.hot_y); + int16_t x2 = static_cast<int16_t>(x1 + cursor_size_x - 1); + int16_t y2 = static_cast<int16_t>(y1 + cursor_size_y - 1); + + clip_cursor_area(x1, x2, y1, y2, addx1, addx2, addy); + + data_pos = static_cast<uint16_t>(addy * cursor_size_x); + for (int16_t y = y1; y <= y2; y++) { + data_pos = static_cast<uint16_t>(data_pos + addx1); + for (int16_t x = x1; x <= x2; x++) { + INT10_GetPixel(static_cast<uint16_t>(x), + static_cast<uint16_t>(y), + state.page, + &state.background.data[data_pos++]); + }; + data_pos = static_cast<uint16_t>(data_pos + addx2); + }; + state.background.enabled = true; + state.background.pos_x = static_cast<uint16_t>(get_pos_x() / xratio - + state.hot_x); + state.background.pos_y = static_cast<uint16_t>(get_pos_y() - state.hot_y); + + // Draw Mousecursor + data_pos = static_cast<uint16_t>(addy * cursor_size_x); + const auto screen_mask = state.user_screen_mask ? state.user_def_screen_mask + : default_screen_mask; + const auto cursor_mask = state.user_cursor_mask ? state.user_def_cursor_mask + : default_cursor_mask; + for (int16_t y = y1; y <= y2; y++) { + uint16_t sc_mask = screen_mask[addy + y - y1]; + uint16_t cu_mask = cursor_mask[addy + y - y1]; + if (addx1 > 0) { + sc_mask = static_cast<uint16_t>(sc_mask << addx1); + cu_mask = static_cast<uint16_t>(cu_mask << addx1); + data_pos = static_cast<uint16_t>(data_pos + addx1); + }; + for (int16_t x = x1; x <= x2; x++) { + constexpr auto highest_bit = (1 << (cursor_size_x - 1)); + uint8_t pixel = 0; + // ScreenMask + if (sc_mask & highest_bit) + pixel = state.background.data[data_pos]; + // CursorMask + if (cu_mask & highest_bit) + pixel = pixel ^ 0x0f; + sc_mask = static_cast<uint16_t>(sc_mask << 1); + cu_mask = static_cast<uint16_t>(cu_mask << 1); + // Set Pixel + INT10_PutPixel(static_cast<uint16_t>(x), + static_cast<uint16_t>(y), + state.page, + pixel); + ++data_pos; + }; + data_pos = static_cast<uint16_t>(addx2 + data_pos); + }; + + restore_vga_registers(); } // *************************************************************************** @@ -574,1109 +580,1148 @@ void MOUSEDOS_DrawCursor() static void update_driver_active() { - mouse_shared.active_dos = (state.user_callback_mask != 0); - MOUSE_NotifyStateChanged(); + mouse_shared.active_dos = (state.user_callback_mask != 0); + MOUSE_NotifyStateChanged(); } static uint8_t get_reset_wheel_8bit() { - if (!state.wheel_api) - return 0; + if (!state.wheel_api) + return 0; - const auto tmp = counter_w; - counter_w = 0; // reading always clears the counter + const auto tmp = counter_w; + counter_w = 0; // reading always clears the counter - // 0xff for -1, 0xfe for -2, etc. - return signed_to_reg8(tmp); + // 0xff for -1, 0xfe for -2, etc. + return signed_to_reg8(tmp); } static uint16_t get_reset_wheel_16bit() { - if (!state.wheel_api) - return 0; + if (!state.wheel_api) + return 0; - const int16_t tmp = counter_w; - counter_w = 0; // reading always clears the counter + const int16_t tmp = counter_w; + counter_w = 0; // reading always clears the counter - return signed_to_reg16(tmp); + return signed_to_reg16(tmp); } static void set_mickey_pixel_rate(const int16_t ratio_x, const int16_t ratio_y) { - // According to https://www.stanislavs.org/helppc/int_33-f.html - // the values should be non-negative (highest bit not set) - - if ((ratio_x > 0) && (ratio_y > 0)) { - // ratio = number of mickeys per 8 pixels - constexpr auto pixels = 8.0f; - state.mickeys_per_pixel_x = static_cast<float>(ratio_x) / pixels; - state.mickeys_per_pixel_y = static_cast<float>(ratio_y) / pixels; - } + // According to https://www.stanislavs.org/helppc/int_33-f.html + // the values should be non-negative (highest bit not set) + + if ((ratio_x > 0) && (ratio_y > 0)) { + // ratio = number of mickeys per 8 pixels + constexpr auto pixels = 8.0f; + state.mickeys_per_pixel_x = static_cast<float>(ratio_x) / pixels; + state.mickeys_per_pixel_y = static_cast<float>(ratio_y) / pixels; + } } static void set_double_speed_threshold(const uint16_t threshold) { - if (threshold) - state.double_speed_threshold = threshold; - else - state.double_speed_threshold = 64; // default value + if (threshold) + state.double_speed_threshold = threshold; + else + state.double_speed_threshold = 64; // default value } static void set_sensitivity(const uint16_t sensitivity_x, - const uint16_t sensitivity_y, - const uint16_t unknown) + const uint16_t sensitivity_y, const uint16_t unknown) { - const auto tmp_x = std::min(static_cast<uint16_t>(100), sensitivity_x); - const auto tmp_y = std::min(static_cast<uint16_t>(100), sensitivity_y); - const auto tmp_u = std::min(static_cast<uint16_t>(100), unknown); + const auto tmp_x = std::min(static_cast<uint16_t>(100), sensitivity_x); + const auto tmp_y = std::min(static_cast<uint16_t>(100), sensitivity_y); + const auto tmp_u = std::min(static_cast<uint16_t>(100), unknown); - state.sensitivity_x = static_cast<uint8_t>(tmp_x); - state.sensitivity_y = static_cast<uint8_t>(tmp_y); - state.unknown_01 = static_cast<uint8_t>(tmp_u); + state.sensitivity_x = static_cast<uint8_t>(tmp_x); + state.sensitivity_y = static_cast<uint8_t>(tmp_y); + state.unknown_01 = static_cast<uint8_t>(tmp_u); - // It is unclear how the original mouse driver handles sensitivity, - // but one can observe that setting value 0 stops the mouse movement - // completely, 50 is the default, and 100 seems to more or less - // dobule it. Linear sensitivity should be good enough. + // It is unclear how the original mouse driver handles sensitivity, + // but one can observe that setting value 0 stops the mouse movement + // completely, 50 is the default, and 100 seems to more or less + // dobule it. Linear sensitivity should be good enough. - state.sensitivity_coeff_x = state.sensitivity_x / 50.0f; - state.sensitivity_coeff_y = state.sensitivity_y / 50.0f; + state.sensitivity_coeff_x = state.sensitivity_x / 50.0f; + state.sensitivity_coeff_y = state.sensitivity_y / 50.0f; } static void notify_interface_rate() { - // Real mouse drivers set the PS/2 mouse sampling rate - // to the following rates: - // - A4 Pointing Device 8.04A 100 Hz - // - CuteMouse 2.1b4 100 Hz - // - Genius Dynamic Mouse 9.20 60 Hz - // - Microsoft Mouse 8.20 60 Hz - // - Mouse Systems 8.00 100 Hz - // and the most common serial mice were 1200 bauds, which gives - // approx. 40 Hz sampling rate limit due to COM port bandwidth. - - // Original DOSBox uses 200 Hz for callbacks, but the internal - // states (buttons, mickey counters) are updated in realtime. - // This is too much (at least Ultima Underworld I and II do not - // like this). - - // Set default value to 200 Hz (which is the maximum setting for - // PS/2 mice - and hopefully this is safe (if it's not, user can - // always adjust it in configuration file or with MOUSECTL.COM). - - constexpr uint16_t rate_default_hz = 200; - - if (rate_is_set) - // Rate was set by guest application - use this value. The minimum - // will be enforced by MouseInterface nevertheless - MouseInterface::GetDOS()->NotifyInterfaceRate(rate_hz); - else if (min_rate_hz) - // If user set the minimum mouse rate - follow it - MouseInterface::GetDOS()->NotifyInterfaceRate(min_rate_hz); - else - // No user setting in effect - use default value - MouseInterface::GetDOS()->NotifyInterfaceRate(rate_default_hz); + // Real mouse drivers set the PS/2 mouse sampling rate + // to the following rates: + // - A4 Pointing Device 8.04A 100 Hz + // - CuteMouse 2.1b4 100 Hz + // - Genius Dynamic Mouse 9.20 60 Hz + // - Microsoft Mouse 8.20 60 Hz + // - Mouse Systems 8.00 100 Hz + // and the most common serial mice were 1200 bauds, which gives + // approx. 40 Hz sampling rate limit due to COM port bandwidth. + + // Original DOSBox uses 200 Hz for callbacks, but the internal + // states (buttons, mickey counters) are updated in realtime. + // This is too much (at least Ultima Underworld I and II do not + // like this). + + // Set default value to 200 Hz (which is the maximum setting for + // PS/2 mice - and hopefully this is safe (if it's not, user can + // always adjust it in configuration file or with MOUSECTL.COM). + + constexpr uint16_t rate_default_hz = 200; + + if (rate_is_set) + // Rate was set by guest application - use this value. The + // minimum will be enforced by MouseInterface nevertheless + MouseInterface::GetDOS()->NotifyInterfaceRate(rate_hz); + else if (min_rate_hz) + // If user set the minimum mouse rate - follow it + MouseInterface::GetDOS()->NotifyInterfaceRate(min_rate_hz); + else + // No user setting in effect - use default value + MouseInterface::GetDOS()->NotifyInterfaceRate(rate_default_hz); } static void set_interrupt_rate(const uint16_t rate_id) { - uint16_t val_hz; - - switch (rate_id) { - case 0: val_hz = 0; break; // no events, TODO: this should be simulated - case 1: val_hz = 30; break; - case 2: val_hz = 50; break; - case 3: val_hz = 100; break; - default: val_hz = 200; break; // above 4 is not suported, set max - } - - if (val_hz) { - rate_is_set = true; - rate_hz = val_hz; - notify_interface_rate(); - } + uint16_t val_hz; + + switch (rate_id) { + case 0: val_hz = 0; break; // no events, TODO: this should be simulated + case 1: val_hz = 30; break; + case 2: val_hz = 50; break; + case 3: val_hz = 100; break; + default: val_hz = 200; break; // above 4 is not suported, set max + } + + if (val_hz) { + rate_is_set = true; + rate_hz = val_hz; + notify_interface_rate(); + } } static void reset_hardware() { - // Resetting the wheel API status in reset() might seem to be a more - // logical approach, but this is clearly not what CuteMouse does; - // if this is done in reset(), the DN2 is unable to use mouse wheel - state.wheel_api = false; - counter_w = 0; + // Resetting the wheel API status in reset() might seem to be a more + // logical approach, but this is clearly not what CuteMouse does; + // if this is done in reset(), the DN2 is unable to use mouse wheel + state.wheel_api = false; + counter_w = 0; - PIC_SetIRQMask(12, false); // lower IRQ line + PIC_SetIRQMask(12, false); // lower IRQ line - // Reset mouse refresh rate - rate_is_set = false; - notify_interface_rate(); + // Reset mouse refresh rate + rate_is_set = false; + notify_interface_rate(); } void MOUSEDOS_NotifyMinRate(const uint16_t value_hz) { - min_rate_hz = value_hz; + min_rate_hz = value_hz; - // If rate was set by a DOS application, don't change it - if (rate_is_set) - return; + // If rate was set by a DOS application, don't change it + if (rate_is_set) + return; - notify_interface_rate(); + notify_interface_rate(); } void MOUSEDOS_BeforeNewVideoMode() { - if (CurMode->type != M_TEXT) - restore_cursor_background(); - else - restore_cursor_background_text(); - - state.hidden = 1; - state.oldhidden = 1; - state.background.enabled = false; + if (CurMode->type != M_TEXT) + restore_cursor_background(); + else + restore_cursor_background_text(); + + state.hidden = 1; + state.oldhidden = 1; + state.background.enabled = false; } // TODO: Does way to much. Many things should be moved to mouse reset one day void MOUSEDOS_AfterNewVideoMode(const bool setmode) { - state.inhibit_draw = false; - // Get the correct resolution from the current video mode - uint8_t mode = mem_readb(BIOS_VIDEO_MODE); - if (setmode && mode == state.mode) - LOG(LOG_MOUSE, LOG_NORMAL)("New video mode is the same as the old"); - state.granularity_x = 0xffff; - state.granularity_y = 0xffff; - switch (mode) { - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x07: { - state.granularity_x = (mode < 2) ? 0xfff0 : 0xfff8; - state.granularity_y = 0xfff8; - Bitu rows = IS_EGAVGA_ARCH ? real_readb(BIOSMEM_SEG, BIOSMEM_NB_ROWS) : 24; - if ((rows == 0) || (rows > 250)) - rows = 24; - state.maxpos_y = static_cast<int16_t>(8 * (rows + 1) - 1); - break; - } - case 0x04: - case 0x05: - case 0x06: - case 0x08: - case 0x09: - case 0x0a: - case 0x0d: - case 0x0e: - case 0x13: // 320x200 VGA - if (mode == 0x0d || mode == 0x13) - state.granularity_x = 0xfffe; - state.maxpos_y = 199; - break; - case 0x0f: - case 0x10: state.maxpos_y = 349; break; - case 0x11: - case 0x12: state.maxpos_y = 479; break; - default: - LOG(LOG_MOUSE, LOG_ERROR)("Unhandled videomode %X on reset", mode); - state.inhibit_draw = true; - return; - } - - state.mode = mode; - state.maxpos_x = 639; - state.minpos_x = 0; - state.minpos_y = 0; - state.hot_x = 0; - state.hot_y = 0; - state.user_screen_mask = false; - state.user_cursor_mask = false; - state.text_and_mask = default_text_and_mask; - state.text_xor_mask = default_text_xor_mask; - state.page = 0; - state.update_region_y[1] = -1; // offscreen - state.cursor_type = MouseCursor::Software; - state.enabled = true; - - MOUSE_NotifyResetDOS(); + state.inhibit_draw = false; + // Get the correct resolution from the current video mode + uint8_t mode = mem_readb(BIOS_VIDEO_MODE); + if (setmode && mode == state.mode) + LOG(LOG_MOUSE, LOG_NORMAL) + ("New video mode is the same as the old"); + state.granularity_x = 0xffff; + state.granularity_y = 0xffff; + switch (mode) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x07: { + state.granularity_x = (mode < 2) ? 0xfff0 : 0xfff8; + state.granularity_y = 0xfff8; + Bitu rows = IS_EGAVGA_ARCH + ? real_readb(BIOSMEM_SEG, BIOSMEM_NB_ROWS) + : 24; + if ((rows == 0) || (rows > 250)) + rows = 24; + state.maxpos_y = static_cast<int16_t>(8 * (rows + 1) - 1); + break; + } + case 0x04: + case 0x05: + case 0x06: + case 0x08: + case 0x09: + case 0x0a: + case 0x0d: + case 0x0e: + case 0x13: // 320x200 VGA + if (mode == 0x0d || mode == 0x13) + state.granularity_x = 0xfffe; + state.maxpos_y = 199; + break; + case 0x0f: + case 0x10: state.maxpos_y = 349; break; + case 0x11: + case 0x12: state.maxpos_y = 479; break; + default: + LOG(LOG_MOUSE, LOG_ERROR) + ("Unhandled videomode %X on reset", mode); + state.inhibit_draw = true; + return; + } + + state.mode = mode; + state.maxpos_x = 639; + state.minpos_x = 0; + state.minpos_y = 0; + state.hot_x = 0; + state.hot_y = 0; + state.user_screen_mask = false; + state.user_cursor_mask = false; + state.text_and_mask = default_text_and_mask; + state.text_xor_mask = default_text_xor_mask; + state.page = 0; + state.update_region_y[1] = -1; // offscreen + state.cursor_type = MouseCursor::Software; + state.enabled = true; + + MOUSE_NotifyResetDOS(); } static void reset() { - // Although these do not belong to the driver state, - // reset them too to avoid any possible problems - counter_w = 0; - pending.Reset(); + // Although these do not belong to the driver state, + // reset them too to avoid any possible problems + counter_w = 0; + pending.Reset(); - MOUSEDOS_BeforeNewVideoMode(); - MOUSEDOS_AfterNewVideoMode(false); + MOUSEDOS_BeforeNewVideoMode(); + MOUSEDOS_AfterNewVideoMode(false); - set_mickey_pixel_rate(8, 16); - set_double_speed_threshold(0); // set default value + set_mickey_pixel_rate(8, 16); + set_double_speed_threshold(0); // set default value - state.enabled = true; + state.enabled = true; - pos_x = static_cast<float>((state.maxpos_x + 1) / 2); - pos_y = static_cast<float>((state.maxpos_y + 1) / 2); + pos_x = static_cast<float>((state.maxpos_x + 1) / 2); + pos_y = static_cast<float>((state.maxpos_y + 1) / 2); - state.mickey_counter_x = 0; - state.mickey_counter_y = 0; - state.mickey_delta_x = 0.0f; - state.mickey_delta_y = 0.0f; + state.mickey_counter_x = 0; + state.mickey_counter_y = 0; + state.mickey_delta_x = 0.0f; + state.mickey_delta_y = 0.0f; - state.last_wheel_moved_x = 0; - state.last_wheel_moved_y = 0; + state.last_wheel_moved_x = 0; + state.last_wheel_moved_y = 0; - for (uint16_t idx = 0; idx < num_buttons; idx++) { - state.times_pressed[idx] = 0; - state.times_released[idx] = 0; - state.last_pressed_x[idx] = 0; - state.last_pressed_y[idx] = 0; - state.last_released_x[idx] = 0; - state.last_released_y[idx] = 0; - } + for (uint16_t idx = 0; idx < num_buttons; idx++) { + state.times_pressed[idx] = 0; + state.times_released[idx] = 0; + state.last_pressed_x[idx] = 0; + state.last_pressed_y[idx] = 0; + state.last_released_x[idx] = 0; + state.last_released_y[idx] = 0; + } - state.user_callback_mask = 0; - mouse_shared.dos_cb_running = false; + state.user_callback_mask = 0; + mouse_shared.dos_cb_running = false; - update_driver_active(); - MOUSE_NotifyResetDOS(); + update_driver_active(); + MOUSE_NotifyResetDOS(); } static void limit_coordinates() { - auto limit = [](float &pos, const int16_t minpos, const int16_t maxpos) { - const float min = static_cast<float>(minpos); - const float max = static_cast<float>(maxpos); + auto limit = [](float &pos, const int16_t minpos, const int16_t maxpos) { + const float min = static_cast<float>(minpos); + const float max = static_cast<float>(maxpos); - pos = std::clamp(pos, min, max); - }; + pos = std::clamp(pos, min, max); + }; - limit(pos_x, state.minpos_x, state.maxpos_x); - limit(pos_y, state.minpos_y, state.maxpos_y); + limit(pos_x, state.minpos_x, state.maxpos_x); + limit(pos_y, state.minpos_y, state.maxpos_y); } static void update_mickeys_on_move(const float x_rel, const float y_rel) { - auto update = [](int16_t &counter, float &delta, const float rel) { - delta += rel; - - // Check if movement is significant enough - const auto d = static_cast<int16_t>(std::lround(delta)); - if (d == 0) - return; - - // Consume part of delta to increase/decrease the counter - delta -= d; - int32_t counter_big = counter + d; - - // Handle counter wrap around int16_t limits - if (counter_big > INT16_MAX) - counter_big -= UINT16_MAX + 1; - else if (counter_big < INT16_MIN) - counter_big += UINT16_MAX + 1; - - counter = static_cast<int16_t>(counter_big); - }; - - const float x_mov = x_rel * state.mickeys_per_pixel_x; - const float y_mov = y_rel * state.mickeys_per_pixel_y; - - // Update mickey counters and mickey speed measurement - update(state.mickey_counter_x, state.mickey_delta_x, x_mov); - update(state.mickey_counter_y, state.mickey_delta_y, y_mov); - speed_mickeys.Update(std::sqrt(x_mov * x_mov + y_mov * y_mov)); + auto update = [](int16_t &counter, float &delta, const float rel) { + delta += rel; + + // Check if movement is significant enough + const auto d = static_cast<int16_t>(std::lround(delta)); + if (d == 0) + return; + + // Consume part of delta to increase/decrease the counter + delta -= d; + int32_t counter_big = counter + d; + + // Handle counter wrap around int16_t limits + if (counter_big > INT16_MAX) + counter_big -= UINT16_MAX + 1; + else if (counter_big < INT16_MIN) + counter_big += UINT16_MAX + 1; + + counter = static_cast<int16_t>(counter_big); + }; + + const float x_mov = x_rel * state.mickeys_per_pixel_x; + const float y_mov = y_rel * state.mickeys_per_pixel_y; + + // Update mickey counters and mickey speed measurement + update(state.mickey_counter_x, state.mickey_delta_x, x_mov); + update(state.mickey_counter_y, state.mickey_delta_y, y_mov); + speed_mickeys.Update(std::sqrt(x_mov * x_mov + y_mov * y_mov)); } static void move_cursor_captured(const float x_rel, const float y_rel) { - // Update mickey counters - update_mickeys_on_move(x_rel, y_rel); + // Update mickey counters + update_mickeys_on_move(x_rel, y_rel); - // Apply mouse movement according to our acceleration model - pos_x += x_rel; - pos_y += y_rel; + // Apply mouse movement according to our acceleration model + pos_x += x_rel; + pos_y += y_rel; } static void move_cursor_seamless(const float x_rel, const float y_rel, const uint16_t x_abs, const uint16_t y_abs) { - // Update mickey counters - update_mickeys_on_move(x_rel, y_rel); - - auto calculate = [](const uint16_t absolute, - const uint16_t res, - const uint16_t clip) { - assert(res > 1u); - return (static_cast<float>(absolute) - clip) / - static_cast<float>(res - 1); - }; - - // Apply mouse movement to mimic host OS - float x = calculate(x_abs, mouse_video.res_x, mouse_video.clip_x); - float y = calculate(y_abs, mouse_video.res_y, mouse_video.clip_y); - - // TODO: this is probably overcomplicated, especially - // the usage of relative movement - to be investigated - if (CurMode->type == M_TEXT) { - pos_x = x * 8; - pos_x *= real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS); - pos_y = y * 8; - pos_y *= IS_EGAVGA_ARCH - ? static_cast<float>(real_readb(BIOSMEM_SEG, BIOSMEM_NB_ROWS) + 1) - : 25.0f; - } else if ((state.maxpos_x < 2048) || (state.maxpos_y < 2048) || - (state.maxpos_x != state.maxpos_y)) { - if ((state.maxpos_x > 0) && (state.maxpos_y > 0)) { - pos_x = x * state.maxpos_x; - pos_y = y * state.maxpos_y; - } else { - pos_x += x_rel; - pos_y += y_rel; - } - } else { - // Fake relative movement through absolute coordinates - pos_x += x_rel; - pos_y += y_rel; - } + // Update mickey counters + update_mickeys_on_move(x_rel, y_rel); + + auto calculate = [](const uint16_t absolute, + const uint16_t res, + const uint16_t clip) { + assert(res > 1u); + return (static_cast<float>(absolute) - clip) / + static_cast<float>(res - 1); + }; + + // Apply mouse movement to mimic host OS + float x = calculate(x_abs, mouse_video.res_x, mouse_video.clip_x); + float y = calculate(y_abs, mouse_video.res_y, mouse_video.clip_y); + + // TODO: this is probably overcomplicated, especially + // the usage of relative movement - to be investigated + if (CurMode->type == M_TEXT) { + pos_x = x * 8; + pos_x *= real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS); + pos_y = y * 8; + pos_y *= IS_EGAVGA_ARCH + ? static_cast<float>( + real_readb(BIOSMEM_SEG, BIOSMEM_NB_ROWS) + 1) + : 25.0f; + } else if ((state.maxpos_x < 2048) || (state.maxpos_y < 2048) || + (state.maxpos_x != state.maxpos_y)) { + if ((state.maxpos_x > 0) && (state.maxpos_y > 0)) { + pos_x = x * state.maxpos_x; + pos_y = y * state.maxpos_y; + } else { + pos_x += x_rel; + pos_y += y_rel; + } + } else { + // Fake relative movement through absolute coordinates + pos_x += x_rel; + pos_y += y_rel; + } } static bool is_captured() { - // If DOS driver uses a mapped physical mouse, always consider it captured, - // as we have no absolute mouse position from the host OS + // If DOS driver uses a mapped physical mouse, always consider it + // captured, as we have no absolute mouse position from the host OS - return mouse_is_captured || is_mapped; + return mouse_is_captured || is_mapped; } static uint8_t move_cursor() { - const auto old_pos_x = get_pos_x(); - const auto old_pos_y = get_pos_y(); - - const auto old_mickey_x = static_cast<int16_t>(state.mickey_counter_x); - const auto old_mickey_y = static_cast<int16_t>(state.mickey_counter_y); - - if (is_captured()) { - - // For raw mouse input use our built-in pointer acceleration model - const float acceleration_coeff = raw_input ? - MOUSE_GetBallisticsCoeff(speed_mickeys.Get() / state.double_speed_threshold) * 2.0f: - 2.0f; - - const float tmp_x = pending.x_rel * acceleration_coeff * state.sensitivity_coeff_x; - const float tmp_y = pending.y_rel * acceleration_coeff * state.sensitivity_coeff_y; - - move_cursor_captured(MOUSE_ClampRelativeMovement(tmp_x), - MOUSE_ClampRelativeMovement(tmp_y)); - - } else - move_cursor_seamless(pending.x_rel, pending.y_rel, - pending.x_abs, pending.y_abs); - - // Pending relative movement is now consummed - pending.x_rel = 0.0f; - pending.y_rel = 0.0f; - - // Make sure cursor stays in the range defined by application - limit_coordinates(); - - // Filter out unneeded events (like sub-pixel mouse movements, - // which won't change guest side mouse state) - const bool abs_changed = (old_pos_x != get_pos_x()) || - (old_pos_y != get_pos_y()); - const bool rel_changed = (old_mickey_x != state.mickey_counter_x) || - (old_mickey_y != state.mickey_counter_y); - - if (abs_changed || rel_changed) - return static_cast<uint8_t>(MouseEventId::MouseHasMoved); - else - return 0; + const auto old_pos_x = get_pos_x(); + const auto old_pos_y = get_pos_y(); + + const auto old_mickey_x = static_cast<int16_t>(state.mickey_counter_x); + const auto old_mickey_y = static_cast<int16_t>(state.mickey_counter_y); + + if (is_captured()) { + // For raw mouse input use our built-in pointer acceleration model + const float acceleration_coeff = + raw_input ? MOUSE_GetBallisticsCoeff( + speed_mickeys.Get() / + state.double_speed_threshold) * + 2.0f + : 2.0f; + + const float tmp_x = pending.x_rel * acceleration_coeff * + state.sensitivity_coeff_x; + const float tmp_y = pending.y_rel * acceleration_coeff * + state.sensitivity_coeff_y; + + move_cursor_captured(MOUSE_ClampRelativeMovement(tmp_x), + MOUSE_ClampRelativeMovement(tmp_y)); + + } else + move_cursor_seamless(pending.x_rel, + pending.y_rel, + pending.x_abs, + pending.y_abs); + + // Pending relative movement is now consummed + pending.x_rel = 0.0f; + pending.y_rel = 0.0f; + + // Make sure cursor stays in the range defined by application + limit_coordinates(); + + // Filter out unneeded events (like sub-pixel mouse movements, + // which won't change guest side mouse state) + const bool abs_changed = (old_pos_x != get_pos_x()) || + (old_pos_y != get_pos_y()); + const bool rel_changed = (old_mickey_x != state.mickey_counter_x) || + (old_mickey_y != state.mickey_counter_y); + + if (abs_changed || rel_changed) + return static_cast<uint8_t>(MouseEventId::MouseHasMoved); + else + return 0; } uint8_t MOUSEDOS_UpdateMoved() { - if (mouse_config.dos_immediate) - return static_cast<uint8_t>(MouseEventId::MouseHasMoved); - else - return move_cursor(); + if (mouse_config.dos_immediate) + return static_cast<uint8_t>(MouseEventId::MouseHasMoved); + else + return move_cursor(); } uint8_t MOUSEDOS_UpdateButtons(const MouseButtons12S new_buttons_12S) { - if (buttons.data == new_buttons_12S.data) - return 0; - - auto mark_pressed = [](const uint8_t idx) { - state.last_pressed_x[idx] = get_pos_x(); - state.last_pressed_y[idx] = get_pos_y(); - ++state.times_pressed[idx]; - }; - - auto mark_released = [](const uint8_t idx) { - state.last_released_x[idx] = get_pos_x(); - state.last_released_y[idx] = get_pos_y(); - ++state.times_released[idx]; - }; - - uint8_t mask = 0; - if (new_buttons_12S.left && !buttons.left) { - mark_pressed(0); - mask |= static_cast<uint8_t>(MouseEventId::PressedLeft); - } else if (!new_buttons_12S.left && buttons.left) { - mark_released(0); - mask |= static_cast<uint8_t>(MouseEventId::ReleasedLeft); - } - - if (new_buttons_12S.right && !buttons.right) { - mark_pressed(1); - mask |= static_cast<uint8_t>(MouseEventId::PressedRight); - } else if (!new_buttons_12S.right && buttons.right) { - mark_released(1); - mask |= static_cast<uint8_t>(MouseEventId::ReleasedRight); - } - - if (new_buttons_12S.middle && !buttons.middle) { - mark_pressed(2); - mask |= static_cast<uint8_t>(MouseEventId::PressedMiddle); - } else if (!new_buttons_12S.middle && buttons.middle) { - mark_released(2); - mask |= static_cast<uint8_t>(MouseEventId::ReleasedMiddle); - } - - buttons = new_buttons_12S; - return mask; + if (buttons.data == new_buttons_12S.data) + return 0; + + auto mark_pressed = [](const uint8_t idx) { + state.last_pressed_x[idx] = get_pos_x(); + state.last_pressed_y[idx] = get_pos_y(); + ++state.times_pressed[idx]; + }; + + auto mark_released = [](const uint8_t idx) { + state.last_released_x[idx] = get_pos_x(); + state.last_released_y[idx] = get_pos_y(); + ++state.times_released[idx]; + }; + + uint8_t mask = 0; + if (new_buttons_12S.left && !buttons.left) { + mark_pressed(0); + mask |= static_cast<uint8_t>(MouseEventId::PressedLeft); + } else if (!new_buttons_12S.left && buttons.left) { + mark_released(0); + mask |= static_cast<uint8_t>(MouseEventId::ReleasedLeft); + } + + if (new_buttons_12S.right && !buttons.right) { + mark_pressed(1); + mask |= static_cast<uint8_t>(MouseEventId::PressedRight); + } else if (!new_buttons_12S.right && buttons.right) { + mark_released(1); + mask |= static_cast<uint8_t>(MouseEventId::ReleasedRight); + } + + if (new_buttons_12S.middle && !buttons.middle) { + mark_pressed(2); + mask |= static_cast<uint8_t>(MouseEventId::PressedMiddle); + } else if (!new_buttons_12S.middle && buttons.middle) { + mark_released(2); + mask |= static_cast<uint8_t>(MouseEventId::ReleasedMiddle); + } + + buttons = new_buttons_12S; + return mask; } static uint8_t move_wheel() { - counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + pending.w_rel)); + counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + pending.w_rel)); - // Pending wheel scroll is now consummed - pending.w_rel = 0; + // Pending wheel scroll is now consummed + pending.w_rel = 0; - state.last_wheel_moved_x = get_pos_x(); - state.last_wheel_moved_y = get_pos_y(); + state.last_wheel_moved_x = get_pos_x(); + state.last_wheel_moved_y = get_pos_y(); - if (counter_w != 0) - return static_cast<uint8_t>(MouseEventId::WheelHasMoved); - else - return 0; + if (counter_w != 0) + return static_cast<uint8_t>(MouseEventId::WheelHasMoved); + else + return 0; } uint8_t MOUSEDOS_UpdateWheel() { - if (mouse_config.dos_immediate) - return static_cast<uint8_t>(MouseEventId::WheelHasMoved); - else - return move_wheel(); + if (mouse_config.dos_immediate) + return static_cast<uint8_t>(MouseEventId::WheelHasMoved); + else + return move_wheel(); } -bool MOUSEDOS_NotifyMoved(const float x_rel, - const float y_rel, - const uint16_t x_abs, - const uint16_t y_abs) +bool MOUSEDOS_NotifyMoved(const float x_rel, const float y_rel, + const uint16_t x_abs, const uint16_t y_abs) { - // Check if an event is needed - bool event_needed = false; - if (is_captured()) { - // Uses relative mouse movements - processing is too complicated - // to easily predict whether the event can be safely omitted - event_needed = true; - // TODO: it actually can be done - but it will require some refactoring - } else { - // Uses absolute mouse position (seamless mode), relative movements - // can wait to be reported - they are completely unreliable anyway - if (pending.x_abs != x_abs || pending.y_abs != y_abs) - event_needed = true; - } - - // Update values to be consummed when the event arrives - pending.x_rel = MOUSE_ClampRelativeMovement(pending.x_rel + x_rel); - pending.y_rel = MOUSE_ClampRelativeMovement(pending.y_rel + y_rel); - pending.x_abs = x_abs; - pending.y_abs = y_abs; - - // NOTES: - // - // It might be tempting to optimize the flow here, by skipping - // the whole event-queue-callback flow if there is no callback - // registered, no graphic cursor to draw, etc. Don't do this - there - // is at least one game (Master of Orion II), which performs INT 0x33 - // calls with 0x0f parameter (changing the callback settings) - // constantly (don't ask me, why) - doing too much optimization - // can cause the game to skip mouse events. - - if (!event_needed) - return 0; - - if (mouse_config.dos_immediate) - return (move_cursor() != 0); - else - return true; + // Check if an event is needed + bool event_needed = false; + if (is_captured()) { + // Uses relative mouse movements - processing is too complicated + // to easily predict whether the event can be safely omitted + event_needed = true; + // TODO: it actually can be done - but it will require some + // refactoring + } else { + // Uses absolute mouse position (seamless mode), relative + // movements can wait to be reported - they are completely + // unreliable anyway + if (pending.x_abs != x_abs || pending.y_abs != y_abs) + event_needed = true; + } + + // Update values to be consummed when the event arrives + pending.x_rel = MOUSE_ClampRelativeMovement(pending.x_rel + x_rel); + pending.y_rel = MOUSE_ClampRelativeMovement(pending.y_rel + y_rel); + pending.x_abs = x_abs; + pending.y_abs = y_abs; + + // NOTES: + // + // It might be tempting to optimize the flow here, by skipping + // the whole event-queue-callback flow if there is no callback + // registered, no graphic cursor to draw, etc. Don't do this - there + // is at least one game (Master of Orion II), which performs INT 0x33 + // calls with 0x0f parameter (changing the callback settings) + // constantly (don't ask me, why) - doing too much optimization + // can cause the game to skip mouse events. + + if (!event_needed) + return 0; + + if (mouse_config.dos_immediate) + return (move_cursor() != 0); + else + return true; } bool MOUSEDOS_NotifyWheel(const int16_t w_rel) { - if (!state.wheel_api) - return 0; - - // Although in some places it is possible for the guest code to get - // wheel counter in 16-bit format, scrolling hundreds of lines in one - // go would be insane - thus, limit the wheel counter to 8 bits and - // reuse the code written for other mouse modules - pending.w_rel = clamp_to_int8(pending.w_rel + w_rel); - - if (pending.w_rel == 0) - return 0; - - if (mouse_config.dos_immediate) - return (move_wheel() != 0); - else - return true; + if (!state.wheel_api) + return 0; + + // Although in some places it is possible for the guest code to get + // wheel counter in 16-bit format, scrolling hundreds of lines in one + // go would be insane - thus, limit the wheel counter to 8 bits and + // reuse the code written for other mouse modules + pending.w_rel = clamp_to_int8(pending.w_rel + w_rel); + + if (pending.w_rel == 0) + return 0; + + if (mouse_config.dos_immediate) + return (move_wheel() != 0); + else + return true; } static Bitu int33_handler() { - switch (reg_ax) { - case 0x00: // MS MOUSE - reset driver and read status - reset_hardware(); - [[fallthrough]]; - case 0x21: // MS MOUSE v6.0+ - software reset - reg_ax = 0xffff; // mouse driver installed - reg_bx = 3; // for 2 buttons return 0xffff - reset(); - break; - case 0x01: // MS MOUSE v1.0+ - show mouse cursor - if (state.hidden) - --state.hidden; - state.update_region_y[1] = -1; // offscreen - MOUSEDOS_DrawCursor(); - break; - case 0x02: // MS MOUSE v1.0+ - hide mouse cursor - if (CurMode->type != M_TEXT) - restore_cursor_background(); - else - restore_cursor_background_text(); - ++state.hidden; - break; - case 0x03: // MS MOUSE v1.0+ / WheelAPI v1.0+ - get position and button status - reg_bl = buttons.data; - reg_bh = get_reset_wheel_8bit(); // CuteMouse clears wheel counter too - reg_cx = get_pos_x(); - reg_dx = get_pos_y(); - break; - case 0x04: // MS MOUSE v1.0+ - position mouse cursor - { - // If position isn't different from current position, don't - // change it. (position is rounded so numbers get lost when the - // rounded number is set) (arena/simulation Wolf) - if (reg_to_signed16(reg_cx) != get_pos_x()) - pos_x = static_cast<float>(reg_cx); - if (reg_to_signed16(reg_dx) != get_pos_y()) - pos_y = static_cast<float>(reg_dx); - limit_coordinates(); - MOUSEDOS_DrawCursor(); - break; - } - case 0x05: // MS MOUSE v1.0+ / WheelAPI v1.0+ - get button press / wheel data - { - const uint16_t idx = reg_bx; // button index - if (idx == 0xffff && state.wheel_api) { - // 'magic' index for checking wheel instead of button - reg_bx = get_reset_wheel_16bit(); - reg_cx = state.last_wheel_moved_x; - reg_dx = state.last_wheel_moved_y; - } else if (idx < num_buttons) { - reg_ax = buttons.data; - reg_bx = state.times_pressed[idx]; - reg_cx = state.last_pressed_x[idx]; - reg_dx = state.last_pressed_y[idx]; - state.times_pressed[idx] = 0; - } else { - // unsupported - try to do something same - reg_ax = buttons.data; - reg_bx = 0; - reg_cx = 0; - reg_dx = 0; - } - break; - } - case 0x06: // MS MOUSE v1.0+ / WheelAPI v1.0+ - get button release data / - // mouse wheel data - { - const uint16_t idx = reg_bx; // button index - if (idx == 0xffff && state.wheel_api) { - // 'magic' index for checking wheel instead of button - reg_bx = get_reset_wheel_16bit(); - reg_cx = state.last_wheel_moved_x; - reg_dx = state.last_wheel_moved_y; - } else if (idx < num_buttons) { - reg_ax = buttons.data; - reg_bx = state.times_released[idx]; - reg_cx = state.last_released_x[idx]; - reg_dx = state.last_released_y[idx]; - - state.times_released[idx] = 0; - } else { - // unsupported - try to do something same - reg_ax = buttons.data; - reg_bx = 0; - reg_cx = 0; - reg_dx = 0; - } - break; - } - case 0x07: // MS MOUSE v1.0+ - define horizontal cursor range - // Lemmings set 1-640 and wants that. Iron Seed set 0-640. but - // doesn't like 640. Iron Seed works if newvideo mode with mode - // 13 sets 0-639. Larry 6 actually wants newvideo mode with mode - // 13 to set it to 0-319. - state.minpos_x = std::min(reg_to_signed16(reg_cx), - reg_to_signed16(reg_dx)); - state.maxpos_x = std::max(reg_to_signed16(reg_cx), - reg_to_signed16(reg_dx)); - // Battle Chess wants this - pos_x = std::clamp(pos_x, - static_cast<float>(state.minpos_x), - static_cast<float>(state.maxpos_x)); - // Or alternatively this: - // pos_x = (state.maxpos_x - state.minpos_x + 1) / 2; - LOG(LOG_MOUSE, LOG_NORMAL)("Define Hortizontal range min:%d max:%d", - state.minpos_x, state.maxpos_x); - break; - case 0x08: // MS MOUSE v1.0+ - define vertical cursor range - // not sure what to take instead of the CurMode (see case 0x07 - // as well) especially the cases where sheight= 400 and we set - // it with the mouse_reset to 200 disabled it at the moment. - // Seems to break syndicate who want 400 in mode 13 - state.minpos_y = std::min(reg_to_signed16(reg_cx), - reg_to_signed16(reg_dx)); - state.maxpos_y = std::max(reg_to_signed16(reg_cx), - reg_to_signed16(reg_dx)); - // Battle Chess wants this - pos_y = std::clamp(pos_y, - static_cast<float>(state.minpos_y), - static_cast<float>(state.maxpos_y)); - // Or alternatively this: - // pos_y = (state.maxpos_y - state.minpos_y + 1) / 2; - LOG(LOG_MOUSE, LOG_NORMAL)("Define Vertical range min:%d max:%d", - state.minpos_y, state.maxpos_y); - break; - case 0x09: // MS MOUSE v3.0+ - define GFX cursor - { - auto clamp_hot = [](const uint16_t reg, const int cursor_size) { - return std::clamp(reg_to_signed16(reg), - static_cast<int16_t>(-cursor_size), - static_cast<int16_t>(cursor_size)); - }; - - PhysPt src = SegPhys(es) + reg_dx; - MEM_BlockRead(src, state.user_def_screen_mask, cursor_size_y * 2); - src += cursor_size_y * 2; - MEM_BlockRead(src, state.user_def_cursor_mask, cursor_size_y * 2); - state.user_screen_mask = true; - state.user_cursor_mask = true; - state.hot_x = clamp_hot(reg_bx, cursor_size_x); - state.hot_y = clamp_hot(reg_cx, cursor_size_y); - state.cursor_type = MouseCursor::Text; - MOUSEDOS_DrawCursor(); - break; - } - case 0x0a: // MS MOUSE v3.0+ - define text cursor - // TODO: shouldn't we use MouseCursor::Text, not - // MouseCursor::Software? - state.cursor_type = (reg_bx ? MouseCursor::Hardware - : MouseCursor::Software); - state.text_and_mask = reg_cx; - state.text_xor_mask = reg_dx; - if (reg_bx) { - INT10_SetCursorShape(reg_cl, reg_dl); - LOG(LOG_MOUSE, LOG_NORMAL)("Hardware Text cursor selected"); - } - MOUSEDOS_DrawCursor(); - break; - case 0x27: // MS MOUSE v7.01+ - get screen/cursor masks and mickey counts - reg_ax = state.text_and_mask; - reg_bx = state.text_xor_mask; - [[fallthrough]]; - case 0x0b: // MS MOUSE v1.0+ - read motion data - reg_cx = signed_to_reg16(state.mickey_counter_x); - reg_dx = signed_to_reg16(state.mickey_counter_y); - state.mickey_counter_x = 0; - state.mickey_counter_y = 0; - break; - case 0x0c: // MS MOUSE v1.0+ - define user callback parameters - state.user_callback_mask = reg_cx; - state.user_callback_segment = SegValue(es); - state.user_callback_offset = reg_dx; - update_driver_active(); - break; - case 0x0d: // MS MOUSE v1.0+ - light pen emulation on - case 0x0e: // MS MOUSE v1.0+ - light pen emulation off - // Both buttons down = pen pressed, otherwise pen considered - // off-screen - // TODO: maybe implement light pen using SDL touch events? - LOG(LOG_MOUSE, LOG_ERROR)("Mouse light pen emulation not implemented"); - break; - case 0x0f: // MS MOUSE v1.0+ - define mickey/pixel rate - set_mickey_pixel_rate(reg_to_signed16(reg_cx), reg_to_signed16(reg_dx)); - break; - case 0x10: // MS MOUSE v1.0+ - define screen region for updating - state.update_region_x[0] = reg_to_signed16(reg_cx); - state.update_region_y[0] = reg_to_signed16(reg_dx); - state.update_region_x[1] = reg_to_signed16(reg_si); - state.update_region_y[1] = reg_to_signed16(reg_di); - MOUSEDOS_DrawCursor(); - break; - case 0x11: // WheelAPI v1.0+ - get mouse capabilities - reg_ax = 0x574d; // Identifier for detection purposes - reg_bx = 0; // Reserved capabilities flags - reg_cx = 1; // Wheel is supported - state.wheel_api = true; // This call enables WheelAPI extensions - counter_w = 0; - // Previous implementation provided Genius Mouse 9.06 function - // to get number of buttons - // (https://sourceforge.net/p/dosbox/patches/32/), it was - // returning 0xffff in reg_ax and number of buttons in reg_bx; I - // suppose the WheelAPI extensions are more useful - break; - case 0x12: // MS MOUSE - set large graphics cursor block - LOG(LOG_MOUSE, LOG_ERROR)("Large graphics cursor block not implemented"); - break; - case 0x13: // MS MOUSE v5.0+ - set double-speed threshold - set_double_speed_threshold(reg_bx); - break; - case 0x14: // MS MOUSE v3.0+ - exchange event-handler - { - const auto old_segment = state.user_callback_segment; - const auto old_offset = state.user_callback_offset; - const auto old_mask = state.user_callback_mask; - // Set new values - state.user_callback_mask = reg_cx; - state.user_callback_segment = SegValue(es); - state.user_callback_offset = reg_dx; - update_driver_active(); - // Return old values - reg_cx = old_mask; - reg_dx = old_offset; - SegSet16(es, old_segment); - break; - } - case 0x15: // MS MOUSE v6.0+ - get driver storage space requirements - reg_bx = sizeof(state); - break; - case 0x16: // MS MOUSE v6.0+ - save driver state - LOG(LOG_MOUSE, LOG_WARN)("Saving driver state..."); - MEM_BlockWrite(SegPhys(es) + reg_dx, &state, sizeof(state)); - break; - case 0x17: // MS MOUSE v6.0+ - load driver state - LOG(LOG_MOUSE, LOG_WARN)("Loading driver state..."); - MEM_BlockRead(SegPhys(es) + reg_dx, &state, sizeof(state)); - pending.Reset(); - update_driver_active(); - set_sensitivity(state.sensitivity_x, - state.sensitivity_y, - state.unknown_01); - // TODO: we should probably also fake an event for mouse - // movement, redraw cursor, etc. - break; - case 0x18: // MS MOUSE v6.0+ - set alternate mouse user handler - case 0x19: // MS MOUSE v6.0+ - set alternate mouse user handler - LOG(LOG_MOUSE, LOG_ERROR)("Alternate mouse user handler not implemented"); - break; - case 0x1a: // MS MOUSE v6.0+ - set mouse sensitivity - // NOTE: Ralf Brown Interrupt List (and some other sources) claim, - // that this should duplicate functions 0x0f and 0x13 - this is - // not true at least for Mouse Systems driver v8.00 and - // IBM/Microsoft driver v8.20 - set_sensitivity(reg_bx, reg_cx, reg_dx); - break; - case 0x1b: // MS MOUSE v6.0+ - get mouse sensitivity - reg_bx = state.sensitivity_x; - reg_cx = state.sensitivity_y; - reg_dx = state.unknown_01; - break; - case 0x1c: // MS MOUSE v6.0+ - set interrupt rate - set_interrupt_rate(reg_bx); - break; - case 0x1d: // MS MOUSE v6.0+ - set display page number - state.page = reg_bl; - break; - case 0x1e: // MS MOUSE v6.0+ - get display page number - reg_bx = state.page; - break; - case 0x1f: // MS MOUSE v6.0+ - disable mouse driver - // ES:BX old mouse driver Zero at the moment TODO - reg_bx = 0; - SegSet16(es, 0); - state.enabled = false; - state.oldhidden = state.hidden; - state.hidden = 1; - // According to Ralf Brown Interrupt List it returns 0x20 if - // success, but CuteMouse source code claims the code for - // success if 0x1f. Both agree that 0xffff means failure. - // Since reg_ax is 0x1f here, no need to change anything. - break; - case 0x20: // MS MOUSE v6.0+ - enable mouse driver - state.enabled = true; - state.hidden = state.oldhidden; - break; - case 0x22: // MS MOUSE v6.0+ - set language for messages - // 00h = English, 01h = French, 02h = Dutch, 03h = German, 04h = - // Swedish 05h = Finnish, 06h = Spanish, 07h = Portugese, 08h = - // Italian - state.language = reg_bx; - break; - case 0x23: // MS MOUSE v6.0+ - get language for messages - reg_bx = state.language; - break; - case 0x24: // MS MOUSE v6.26+ - get Software version, mouse type, and IRQ number - reg_bx = 0x805; // version 8.05 woohoo - reg_ch = 0x04; // PS/2 type - reg_cl = 0; // PS/2 mouse; for others it would be an IRQ number - break; - case 0x25: // MS MOUSE v6.26+ - get general driver information - // TODO: According to PC sourcebook reference - // Returns: - // AH = status - // bit 7 driver type: 1=sys 0=com - // bit 6: 0=non-integrated 1=integrated mouse driver - // bits 4-5: cursor type 00=software text cursor - // 01=hardware text cursor 1X=graphics cursor bits 0-3: - // Function 28 mouse interrupt rate - // AL = Number of MDDS (?) - // BX = fCursor lock - // CX = FinMouse code - // DX = fMouse busy - LOG(LOG_MOUSE, LOG_ERROR)("General driver information not implemented"); - break; - case 0x26: // MS MOUSE v6.26+ - get maximum virtual coordinates - reg_bx = (state.enabled ? 0x0000 : 0xffff); - reg_cx = signed_to_reg16(state.maxpos_x); - reg_dx = signed_to_reg16(state.maxpos_y); - break; - case 0x28: // MS MOUSE v7.0+ - set video mode - // TODO: According to PC sourcebook - // Entry: - // CX = Requested video mode - // DX = Font size, 0 for default - // Returns: - // DX = 0 on success, nonzero (requested video mode) if not - LOG(LOG_MOUSE, LOG_ERROR)("Set video mode not implemented"); - break; - case 0x29: // MS MOUSE v7.0+ - enumerate video modes - // TODO: According to PC sourcebook - // Entry: - // CX = 0 for first, != 0 for next - // Exit: - // BX:DX = named string far ptr - // CX = video mode number - LOG(LOG_MOUSE, LOG_ERROR)("Enumerate video modes not implemented"); - break; - case 0x2a: // MS MOUSE v7.01+ - get cursor hot spot - // Microsoft uses a negative byte counter - // for cursor visibility - reg_al = static_cast<uint8_t>(-state.hidden); - reg_bx = signed_to_reg16(state.hot_x); - reg_cx = signed_to_reg16(state.hot_y); - reg_dx = 0x04; // PS/2 mouse type - break; - case 0x2b: // MS MOUSE v7.0+ - load acceleration profiles - case 0x2c: // MS MOUSE v7.0+ - get acceleration profiles - case 0x2d: // MS MOUSE v7.0+ - select acceleration profile - case 0x2e: // MS MOUSE v8.10+ - set acceleration profile names - case 0x33: // MS MOUSE v7.05+ - get/switch accelleration profile - LOG(LOG_MOUSE, LOG_ERROR)("Custom acceleration profiles not implemented"); - break; - case 0x2f: // MS MOUSE v7.02+ - mouse hardware reset - LOG(LOG_MOUSE, LOG_ERROR)("INT 33 AX=2F mouse hardware reset not implemented"); - break; - case 0x30: // MS MOUSE v7.04+ - get/set BallPoint information - LOG(LOG_MOUSE, LOG_ERROR)("Get/set BallPoint information not implemented"); - break; - case 0x31: // MS MOUSE v7.05+ - get current min/max virtual coordinates - reg_ax = signed_to_reg16(state.minpos_x); - reg_bx = signed_to_reg16(state.minpos_y); - reg_cx = signed_to_reg16(state.maxpos_x); - reg_dx = signed_to_reg16(state.maxpos_y); - break; - case 0x32: // MS MOUSE v7.05+ - get active advanced functions - LOG(LOG_MOUSE, LOG_ERROR)("Get active advanced functions not implemented"); - break; - case 0x34: // MS MOUSE v8.0+ - get initialization file - LOG(LOG_MOUSE, LOG_ERROR)("Get initialization file not implemented"); - break; - case 0x35: // MS MOUSE v8.10+ - LCD screen large pointer support - LOG(LOG_MOUSE, LOG_ERROR)("LCD screen large pointer support not implemented"); - break; - case 0x4d: // MS MOUSE - return pointer to copyright string - LOG(LOG_MOUSE, LOG_ERROR)("Return pointer to copyright string not implemented"); - break; - case 0x6d: // MS MOUSE - get version string - LOG(LOG_MOUSE, LOG_ERROR)("Get version string not implemented"); - break; - case 0x70: // Mouse Systems - installation check - case 0x72: // Mouse Systems 7.01+, Genius Mouse 9.06+ - unknown - case 0x73: // Mouse Systems 7.01+ - get button assignments - LOG(LOG_MOUSE, LOG_ERROR)("Mouse Sytems mouse extensions not implemented"); - break; - case 0x53C1: // Logitech CyberMan - LOG(LOG_MOUSE, LOG_NORMAL)("Mouse function 53C1 for Logitech CyberMan called. Ignored by regular mouse driver."); - break; - default: - LOG(LOG_MOUSE, LOG_ERROR)("Mouse function %04X not implemented", reg_ax); - break; - } - return CBRET_NONE; + switch (reg_ax) { + case 0x00: // MS MOUSE - reset driver and read status + reset_hardware(); + [[fallthrough]]; + case 0x21: // MS MOUSE v6.0+ - software reset + reg_ax = 0xffff; // mouse driver installed + reg_bx = 3; // for 2 buttons return 0xffff + reset(); + break; + case 0x01: // MS MOUSE v1.0+ - show mouse cursor + if (state.hidden) + --state.hidden; + state.update_region_y[1] = -1; // offscreen + MOUSEDOS_DrawCursor(); + break; + case 0x02: // MS MOUSE v1.0+ - hide mouse cursor + if (CurMode->type != M_TEXT) + restore_cursor_background(); + else + restore_cursor_background_text(); + ++state.hidden; + break; + case 0x03: // MS MOUSE v1.0+ / WheelAPI v1.0+ - get position and button + // status + reg_bl = buttons.data; + reg_bh = get_reset_wheel_8bit(); // CuteMouse clears wheel + // counter too + reg_cx = get_pos_x(); + reg_dx = get_pos_y(); + break; + case 0x04: // MS MOUSE v1.0+ - position mouse cursor + { + // If position isn't different from current position, don't + // change it. (position is rounded so numbers get lost when the + // rounded number is set) (arena/simulation Wolf) + if (reg_to_signed16(reg_cx) != get_pos_x()) + pos_x = static_cast<float>(reg_cx); + if (reg_to_signed16(reg_dx) != get_pos_y()) + pos_y = static_cast<float>(reg_dx); + limit_coordinates(); + MOUSEDOS_DrawCursor(); + break; + } + case 0x05: // MS MOUSE v1.0+ / WheelAPI v1.0+ - get button press / wheel + // data + { + const uint16_t idx = reg_bx; // button index + if (idx == 0xffff && state.wheel_api) { + // 'magic' index for checking wheel instead of button + reg_bx = get_reset_wheel_16bit(); + reg_cx = state.last_wheel_moved_x; + reg_dx = state.last_wheel_moved_y; + } else if (idx < num_buttons) { + reg_ax = buttons.data; + reg_bx = state.times_pressed[idx]; + reg_cx = state.last_pressed_x[idx]; + reg_dx = state.last_pressed_y[idx]; + state.times_pressed[idx] = 0; + } else { + // unsupported - try to do something same + reg_ax = buttons.data; + reg_bx = 0; + reg_cx = 0; + reg_dx = 0; + } + break; + } + case 0x06: // MS MOUSE v1.0+ / WheelAPI v1.0+ - get button release data + // / mouse wheel data + { + const uint16_t idx = reg_bx; // button index + if (idx == 0xffff && state.wheel_api) { + // 'magic' index for checking wheel instead of button + reg_bx = get_reset_wheel_16bit(); + reg_cx = state.last_wheel_moved_x; + reg_dx = state.last_wheel_moved_y; + } else if (idx < num_buttons) { + reg_ax = buttons.data; + reg_bx = state.times_released[idx]; + reg_cx = state.last_released_x[idx]; + reg_dx = state.last_released_y[idx]; + + state.times_released[idx] = 0; + } else { + // unsupported - try to do something same + reg_ax = buttons.data; + reg_bx = 0; + reg_cx = 0; + reg_dx = 0; + } + break; + } + case 0x07: // MS MOUSE v1.0+ - define horizontal cursor range + // Lemmings set 1-640 and wants that. Iron Seed set 0-640. but + // doesn't like 640. Iron Seed works if newvideo mode with mode + // 13 sets 0-639. Larry 6 actually wants newvideo mode with mode + // 13 to set it to 0-319. + state.minpos_x = std::min(reg_to_signed16(reg_cx), reg_to_signed16(reg_dx)); + state.maxpos_x = std::max(reg_to_signed16(reg_cx), reg_to_signed16(reg_dx)); + // Battle Chess wants this + pos_x = std::clamp(pos_x, + static_cast<float>(state.minpos_x), + static_cast<float>(state.maxpos_x)); + // Or alternatively this: + // pos_x = (state.maxpos_x - state.minpos_x + 1) / 2; + LOG(LOG_MOUSE, LOG_NORMAL) + ("Define Hortizontal range min:%d max:%d", state.minpos_x, state.maxpos_x); + break; + case 0x08: // MS MOUSE v1.0+ - define vertical cursor range + // not sure what to take instead of the CurMode (see case 0x07 + // as well) especially the cases where sheight= 400 and we set + // it with the mouse_reset to 200 disabled it at the moment. + // Seems to break syndicate who want 400 in mode 13 + state.minpos_y = std::min(reg_to_signed16(reg_cx), + reg_to_signed16(reg_dx)); + state.maxpos_y = std::max(reg_to_signed16(reg_cx), + reg_to_signed16(reg_dx)); + // Battle Chess wants this + pos_y = std::clamp(pos_y, + static_cast<float>(state.minpos_y), + static_cast<float>(state.maxpos_y)); + // Or alternatively this: + // pos_y = (state.maxpos_y - state.minpos_y + 1) / 2; + LOG(LOG_MOUSE, LOG_NORMAL) + ("Define Vertical range min:%d max:%d", state.minpos_y, state.maxpos_y); + break; + case 0x09: // MS MOUSE v3.0+ - define GFX cursor + { + auto clamp_hot = [](const uint16_t reg, const int cursor_size) { + return std::clamp(reg_to_signed16(reg), + static_cast<int16_t>(-cursor_size), + static_cast<int16_t>(cursor_size)); + }; + + PhysPt src = SegPhys(es) + reg_dx; + MEM_BlockRead(src, state.user_def_screen_mask, cursor_size_y * 2); + src += cursor_size_y * 2; + MEM_BlockRead(src, state.user_def_cursor_mask, cursor_size_y * 2); + state.user_screen_mask = true; + state.user_cursor_mask = true; + state.hot_x = clamp_hot(reg_bx, cursor_size_x); + state.hot_y = clamp_hot(reg_cx, cursor_size_y); + state.cursor_type = MouseCursor::Text; + MOUSEDOS_DrawCursor(); + break; + } + case 0x0a: // MS MOUSE v3.0+ - define text cursor + // TODO: shouldn't we use MouseCursor::Text, not + // MouseCursor::Software? + state.cursor_type = (reg_bx ? MouseCursor::Hardware + : MouseCursor::Software); + state.text_and_mask = reg_cx; + state.text_xor_mask = reg_dx; + if (reg_bx) { + INT10_SetCursorShape(reg_cl, reg_dl); + LOG(LOG_MOUSE, LOG_NORMAL) + ("Hardware Text cursor selected"); + } + MOUSEDOS_DrawCursor(); + break; + case 0x27: // MS MOUSE v7.01+ - get screen/cursor masks and mickey counts + reg_ax = state.text_and_mask; + reg_bx = state.text_xor_mask; + [[fallthrough]]; + case 0x0b: // MS MOUSE v1.0+ - read motion data + reg_cx = signed_to_reg16(state.mickey_counter_x); + reg_dx = signed_to_reg16(state.mickey_counter_y); + state.mickey_counter_x = 0; + state.mickey_counter_y = 0; + break; + case 0x0c: // MS MOUSE v1.0+ - define user callback parameters + state.user_callback_mask = reg_cx; + state.user_callback_segment = SegValue(es); + state.user_callback_offset = reg_dx; + update_driver_active(); + break; + case 0x0d: // MS MOUSE v1.0+ - light pen emulation on + case 0x0e: // MS MOUSE v1.0+ - light pen emulation off + // Both buttons down = pen pressed, otherwise pen considered + // off-screen + // TODO: maybe implement light pen using SDL touch events? + LOG(LOG_MOUSE, LOG_ERROR) + ("Mouse light pen emulation not implemented"); + break; + case 0x0f: // MS MOUSE v1.0+ - define mickey/pixel rate + set_mickey_pixel_rate(reg_to_signed16(reg_cx), + reg_to_signed16(reg_dx)); + break; + case 0x10: // MS MOUSE v1.0+ - define screen region for updating + state.update_region_x[0] = reg_to_signed16(reg_cx); + state.update_region_y[0] = reg_to_signed16(reg_dx); + state.update_region_x[1] = reg_to_signed16(reg_si); + state.update_region_y[1] = reg_to_signed16(reg_di); + MOUSEDOS_DrawCursor(); + break; + case 0x11: // WheelAPI v1.0+ - get mouse capabilities + reg_ax = 0x574d; // Identifier for detection purposes + reg_bx = 0; // Reserved capabilities flags + reg_cx = 1; // Wheel is supported + state.wheel_api = true; // This call enables WheelAPI extensions + counter_w = 0; + // Previous implementation provided Genius Mouse 9.06 function + // to get number of buttons + // (https://sourceforge.net/p/dosbox/patches/32/), it was + // returning 0xffff in reg_ax and number of buttons in reg_bx; I + // suppose the WheelAPI extensions are more useful + break; + case 0x12: // MS MOUSE - set large graphics cursor block + LOG(LOG_MOUSE, LOG_ERROR) + ("Large graphics cursor block not implemented"); + break; + case 0x13: // MS MOUSE v5.0+ - set double-speed threshold + set_double_speed_threshold(reg_bx); + break; + case 0x14: // MS MOUSE v3.0+ - exchange event-handler + { + const auto old_segment = state.user_callback_segment; + const auto old_offset = state.user_callback_offset; + const auto old_mask = state.user_callback_mask; + // Set new values + state.user_callback_mask = reg_cx; + state.user_callback_segment = SegValue(es); + state.user_callback_offset = reg_dx; + update_driver_active(); + // Return old values + reg_cx = old_mask; + reg_dx = old_offset; + SegSet16(es, old_segment); + break; + } + case 0x15: // MS MOUSE v6.0+ - get driver storage space requirements + reg_bx = sizeof(state); + break; + case 0x16: // MS MOUSE v6.0+ - save driver state + LOG(LOG_MOUSE, LOG_WARN)("Saving driver state..."); + MEM_BlockWrite(SegPhys(es) + reg_dx, &state, sizeof(state)); + break; + case 0x17: // MS MOUSE v6.0+ - load driver state + LOG(LOG_MOUSE, LOG_WARN)("Loading driver state..."); + MEM_BlockRead(SegPhys(es) + reg_dx, &state, sizeof(state)); + pending.Reset(); + update_driver_active(); + set_sensitivity(state.sensitivity_x, + state.sensitivity_y, + state.unknown_01); + // TODO: we should probably also fake an event for mouse + // movement, redraw cursor, etc. + break; + case 0x18: // MS MOUSE v6.0+ - set alternate mouse user handler + case 0x19: // MS MOUSE v6.0+ - set alternate mouse user handler + LOG(LOG_MOUSE, LOG_ERROR) + ("Alternate mouse user handler not implemented"); + break; + case 0x1a: // MS MOUSE v6.0+ - set mouse sensitivity + // NOTE: Ralf Brown Interrupt List (and some other sources) + // claim, that this should duplicate functions 0x0f and 0x13 - + // this is not true at least for Mouse Systems driver v8.00 and + // IBM/Microsoft driver v8.20 + set_sensitivity(reg_bx, reg_cx, reg_dx); + break; + case 0x1b: // MS MOUSE v6.0+ - get mouse sensitivity + reg_bx = state.sensitivity_x; + reg_cx = state.sensitivity_y; + reg_dx = state.unknown_01; + break; + case 0x1c: // MS MOUSE v6.0+ - set interrupt rate + set_interrupt_rate(reg_bx); + break; + case 0x1d: // MS MOUSE v6.0+ - set display page number + state.page = reg_bl; + break; + case 0x1e: // MS MOUSE v6.0+ - get display page number + reg_bx = state.page; + break; + case 0x1f: // MS MOUSE v6.0+ - disable mouse driver + // ES:BX old mouse driver Zero at the moment TODO + reg_bx = 0; + SegSet16(es, 0); + state.enabled = false; + state.oldhidden = state.hidden; + state.hidden = 1; + // According to Ralf Brown Interrupt List it returns 0x20 if + // success, but CuteMouse source code claims the code for + // success if 0x1f. Both agree that 0xffff means failure. + // Since reg_ax is 0x1f here, no need to change anything. + break; + case 0x20: // MS MOUSE v6.0+ - enable mouse driver + state.enabled = true; + state.hidden = state.oldhidden; + break; + case 0x22: // MS MOUSE v6.0+ - set language for messages + // 00h = English, 01h = French, 02h = Dutch, 03h = German, 04h = + // Swedish 05h = Finnish, 06h = Spanish, 07h = Portugese, 08h = + // Italian + state.language = reg_bx; + break; + case 0x23: // MS MOUSE v6.0+ - get language for messages + reg_bx = state.language; + break; + case 0x24: // MS MOUSE v6.26+ - get Software version, mouse type, and + // IRQ number + reg_bx = 0x805; // version 8.05 woohoo + reg_ch = 0x04; // PS/2 type + reg_cl = 0; // PS/2 mouse; for others it would be an IRQ number + break; + case 0x25: // MS MOUSE v6.26+ - get general driver information + // TODO: According to PC sourcebook reference + // Returns: + // AH = status + // bit 7 driver type: 1=sys 0=com + // bit 6: 0=non-integrated 1=integrated mouse driver + // bits 4-5: cursor type 00=software text cursor + // 01=hardware text cursor 1X=graphics cursor bits 0-3: + // Function 28 mouse interrupt rate + // AL = Number of MDDS (?) + // BX = fCursor lock + // CX = FinMouse code + // DX = fMouse busy + LOG(LOG_MOUSE, LOG_ERROR) + ("General driver information not implemented"); + break; + case 0x26: // MS MOUSE v6.26+ - get maximum virtual coordinates + reg_bx = (state.enabled ? 0x0000 : 0xffff); + reg_cx = signed_to_reg16(state.maxpos_x); + reg_dx = signed_to_reg16(state.maxpos_y); + break; + case 0x28: // MS MOUSE v7.0+ - set video mode + // TODO: According to PC sourcebook + // Entry: + // CX = Requested video mode + // DX = Font size, 0 for default + // Returns: + // DX = 0 on success, nonzero (requested video mode) if not + LOG(LOG_MOUSE, LOG_ERROR)("Set video mode not implemented"); + break; + case 0x29: // MS MOUSE v7.0+ - enumerate video modes + // TODO: According to PC sourcebook + // Entry: + // CX = 0 for first, != 0 for next + // Exit: + // BX:DX = named string far ptr + // CX = video mode number + LOG(LOG_MOUSE, LOG_ERROR) + ("Enumerate video modes not implemented"); + break; + case 0x2a: // MS MOUSE v7.01+ - get cursor hot spot + // Microsoft uses a negative byte counter + // for cursor visibility + reg_al = static_cast<uint8_t>(-state.hidden); + reg_bx = signed_to_reg16(state.hot_x); + reg_cx = signed_to_reg16(state.hot_y); + reg_dx = 0x04; // PS/2 mouse type + break; + case 0x2b: // MS MOUSE v7.0+ - load acceleration profiles + case 0x2c: // MS MOUSE v7.0+ - get acceleration profiles + case 0x2d: // MS MOUSE v7.0+ - select acceleration profile + case 0x2e: // MS MOUSE v8.10+ - set acceleration profile names + case 0x33: // MS MOUSE v7.05+ - get/switch accelleration profile + LOG(LOG_MOUSE, LOG_ERROR) + ("Custom acceleration profiles not implemented"); + break; + case 0x2f: // MS MOUSE v7.02+ - mouse hardware reset + LOG(LOG_MOUSE, LOG_ERROR) + ("INT 33 AX=2F mouse hardware reset not implemented"); + break; + case 0x30: // MS MOUSE v7.04+ - get/set BallPoint information + LOG(LOG_MOUSE, LOG_ERROR) + ("Get/set BallPoint information not implemented"); + break; + case 0x31: // MS MOUSE v7.05+ - get current min/max virtual coordinates + reg_ax = signed_to_reg16(state.minpos_x); + reg_bx = signed_to_reg16(state.minpos_y); + reg_cx = signed_to_reg16(state.maxpos_x); + reg_dx = signed_to_reg16(state.maxpos_y); + break; + case 0x32: // MS MOUSE v7.05+ - get active advanced functions + LOG(LOG_MOUSE, LOG_ERROR) + ("Get active advanced functions not implemented"); + break; + case 0x34: // MS MOUSE v8.0+ - get initialization file + LOG(LOG_MOUSE, LOG_ERROR) + ("Get initialization file not implemented"); + break; + case 0x35: // MS MOUSE v8.10+ - LCD screen large pointer support + LOG(LOG_MOUSE, LOG_ERROR) + ("LCD screen large pointer support not implemented"); + break; + case 0x4d: // MS MOUSE - return pointer to copyright string + LOG(LOG_MOUSE, LOG_ERROR) + ("Return pointer to copyright string not implemented"); + break; + case 0x6d: // MS MOUSE - get version string + LOG(LOG_MOUSE, LOG_ERROR)("Get version string not implemented"); + break; + case 0x70: // Mouse Systems - installation check + case 0x72: // Mouse Systems 7.01+, Genius Mouse 9.06+ - unknown + case 0x73: // Mouse Systems 7.01+ - get button assignments + LOG(LOG_MOUSE, LOG_ERROR) + ("Mouse Sytems mouse extensions not implemented"); + break; + case 0x53C1: // Logitech CyberMan + LOG(LOG_MOUSE, LOG_NORMAL) + ("Mouse function 53C1 for Logitech CyberMan called. Ignored by regular mouse driver."); + break; + default: + LOG(LOG_MOUSE, LOG_ERROR) + ("Mouse function %04X not implemented", reg_ax); + break; + } + return CBRET_NONE; } static Bitu mouse_bd_handler() { - // the stack contains offsets to register values - uint16_t raxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x0a)); - uint16_t rbxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x08)); - uint16_t rcxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x06)); - uint16_t rdxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x04)); - - // read out the actual values, registers ARE overwritten - const uint16_t rax = real_readw(SegValue(ds), raxpt); - reg_ax = rax; - reg_bx = real_readw(SegValue(ds), rbxpt); - reg_cx = real_readw(SegValue(ds), rcxpt); - reg_dx = real_readw(SegValue(ds), rdxpt); - - // some functions are treated in a special way (additional registers) - switch (rax) { - case 0x09: // Define GFX Cursor - case 0x16: // Save driver state - case 0x17: // load driver state - SegSet16(es, SegValue(ds)); - break; - case 0x0c: // Define interrupt subroutine parameters - case 0x14: // Exchange event-handler - if (reg_bx != 0) - SegSet16(es, reg_bx); - else - SegSet16(es, SegValue(ds)); - break; - case 0x10: // Define screen region for updating - reg_cx = real_readw(SegValue(ds), rdxpt); - reg_dx = real_readw(SegValue(ds), static_cast<uint16_t>(rdxpt + 2)); - reg_si = real_readw(SegValue(ds), static_cast<uint16_t>(rdxpt + 4)); - reg_di = real_readw(SegValue(ds), static_cast<uint16_t>(rdxpt + 6)); - break; - default: break; - } - - int33_handler(); - - // save back the registers, too - real_writew(SegValue(ds), raxpt, reg_ax); - real_writew(SegValue(ds), rbxpt, reg_bx); - real_writew(SegValue(ds), rcxpt, reg_cx); - real_writew(SegValue(ds), rdxpt, reg_dx); - switch (rax) { - case 0x1f: // Disable Mousedriver - real_writew(SegValue(ds), rbxpt, SegValue(es)); - break; - case 0x14: // Exchange event-handler - real_writew(SegValue(ds), rcxpt, SegValue(es)); - break; - default: break; - } - - return CBRET_NONE; + // the stack contains offsets to register values + uint16_t raxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x0a)); + uint16_t rbxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x08)); + uint16_t rcxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x06)); + uint16_t rdxpt = real_readw(SegValue(ss), static_cast<uint16_t>(reg_sp + 0x04)); + + // read out the actual values, registers ARE overwritten + const uint16_t rax = real_readw(SegValue(ds), raxpt); + reg_ax = rax; + reg_bx = real_readw(SegValue(ds), rbxpt); + reg_cx = real_readw(SegValue(ds), rcxpt); + reg_dx = real_readw(SegValue(ds), rdxpt); + + // some functions are treated in a special way (additional registers) + switch (rax) { + case 0x09: // Define GFX Cursor + case 0x16: // Save driver state + case 0x17: // load driver state + SegSet16(es, SegValue(ds)); + break; + case 0x0c: // Define interrupt subroutine parameters + case 0x14: // Exchange event-handler + if (reg_bx != 0) + SegSet16(es, reg_bx); + else + SegSet16(es, SegValue(ds)); + break; + case 0x10: // Define screen region for updating + reg_cx = real_readw(SegValue(ds), rdxpt); + reg_dx = real_readw(SegValue(ds), static_cast<uint16_t>(rdxpt + 2)); + reg_si = real_readw(SegValue(ds), static_cast<uint16_t>(rdxpt + 4)); + reg_di = real_readw(SegValue(ds), static_cast<uint16_t>(rdxpt + 6)); + break; + default: break; + } + + int33_handler(); + + // save back the registers, too + real_writew(SegValue(ds), raxpt, reg_ax); + real_writew(SegValue(ds), rbxpt, reg_bx); + real_writew(SegValue(ds), rcxpt, reg_cx); + real_writew(SegValue(ds), rdxpt, reg_dx); + switch (rax) { + case 0x1f: // Disable Mousedriver + real_writew(SegValue(ds), rbxpt, SegValue(es)); + break; + case 0x14: // Exchange event-handler + real_writew(SegValue(ds), rcxpt, SegValue(es)); + break; + default: break; + } + + return CBRET_NONE; } static Bitu user_callback_handler() { - mouse_shared.dos_cb_running = false; - return CBRET_NONE; + mouse_shared.dos_cb_running = false; + return CBRET_NONE; } bool MOUSEDOS_HasCallback(const uint8_t mask) { - return state.user_callback_mask & mask; + return state.user_callback_mask & mask; } -Bitu MOUSEDOS_DoCallback(const uint8_t mask, - const MouseButtons12S buttons_12S) +Bitu MOUSEDOS_DoCallback(const uint8_t mask, const MouseButtons12S buttons_12S) { - mouse_shared.dos_cb_running = true; - const bool mouse_moved = mask & static_cast<uint8_t>(MouseEventId::MouseHasMoved); - const bool wheel_moved = mask & static_cast<uint8_t>(MouseEventId::WheelHasMoved); - - // Extension for Windows mouse driver by javispedro: - // - https://git.javispedro.com/cgit/vbados.git/about/ - // which allows seamless mouse integration. It is also included in DOSBox-X and Dosemu2: - // - https://github.com/joncampbell123/dosbox-x/pull/3424 - // - https://github.com/dosemu2/dosemu2/issues/1552#issuecomment-1100777880 - // - https://github.com/dosemu2/dosemu2/commit/cd9d2dbc8e3d58dc7cbc92f172c0d447881526be - // - https://github.com/joncampbell123/dosbox-x/commit/aec29ce28eb4b520f21ead5b2debf370183b9f28 - reg_ah = (!is_captured() && mouse_moved) ? 1 : 0; - - reg_al = mask; - reg_bl = buttons_12S.data; - reg_bh = wheel_moved ? get_reset_wheel_8bit() : 0; - reg_cx = get_pos_x(); - reg_dx = get_pos_y(); - reg_si = signed_to_reg16(state.mickey_counter_x); - reg_di = signed_to_reg16(state.mickey_counter_y); - - CPU_Push16(RealSeg(user_callback)); - CPU_Push16(RealOff(user_callback)); - CPU_Push16(state.user_callback_segment); - CPU_Push16(state.user_callback_offset); - - return CBRET_NONE; + mouse_shared.dos_cb_running = true; + const bool mouse_moved = mask & + static_cast<uint8_t>(MouseEventId::MouseHasMoved); + const bool wheel_moved = mask & + static_cast<uint8_t>(MouseEventId::WheelHasMoved); + + // Extension for Windows mouse driver by javispedro: + // - https://git.javispedro.com/cgit/vbados.git/about/ + // which allows seamless mouse integration. It is also included in + // DOSBox-X and Dosemu2: + // - https://github.com/joncampbell123/dosbox-x/pull/3424 + // - + // https://github.com/dosemu2/dosemu2/issues/1552#issuecomment-1100777880 + // - + // https://github.com/dosemu2/dosemu2/commit/cd9d2dbc8e3d58dc7cbc92f172c0d447881526be + // - + // https://github.com/joncampbell123/dosbox-x/commit/aec29ce28eb4b520f21ead5b2debf370183b9f28 + reg_ah = (!is_captured() && mouse_moved) ? 1 : 0; + + reg_al = mask; + reg_bl = buttons_12S.data; + reg_bh = wheel_moved ? get_reset_wheel_8bit() : 0; + reg_cx = get_pos_x(); + reg_dx = get_pos_y(); + reg_si = signed_to_reg16(state.mickey_counter_x); + reg_di = signed_to_reg16(state.mickey_counter_y); + + CPU_Push16(RealSeg(user_callback)); + CPU_Push16(RealOff(user_callback)); + CPU_Push16(state.user_callback_segment); + CPU_Push16(state.user_callback_offset); + + return CBRET_NONE; } void MOUSEDOS_NotifyMapped(const bool enabled) { - is_mapped = enabled; + is_mapped = enabled; } void MOUSEDOS_NotifyRawInput(const bool enabled) { - raw_input = enabled; + raw_input = enabled; } void MOUSEDOS_Init() { - // Callback for mouse interrupt 0x33 - auto call_int33 = CALLBACK_Allocate(); - // RealPt int33_location = RealMake(CB_SEG + 1,(call_int33 * CB_SIZE) - 0x10); - RealPt int33_location = RealMake(static_cast<uint16_t>(DOS_GetMemory(0x1) - 1), 0x10); - CALLBACK_Setup(call_int33, &int33_handler, CB_MOUSE, - Real2Phys(int33_location), "Mouse"); - // Wasteland needs low(seg(int33))!=0 and low(ofs(int33))!=0 - real_writed(0, 0x33 << 2, int33_location); - - auto call_mouse_bd = CALLBACK_Allocate(); - CALLBACK_Setup(call_mouse_bd, - &mouse_bd_handler, - CB_RETF8, - PhysMake(RealSeg(int33_location), - static_cast<uint16_t>(RealOff(int33_location) + 2)), - "MouseBD"); - // pseudocode for CB_MOUSE (including the special backdoor entry point): - // jump near i33hd - // callback mouse_bd_handler - // retf 8 - // label i33hd: - // callback int33_handler - // iret - - // Callback for mouse user routine return - auto call_user = CALLBACK_Allocate(); - CALLBACK_Setup(call_user, &user_callback_handler, CB_RETF_CLI, "mouse user ret"); - user_callback = CALLBACK_RealPointer(call_user); - - state.user_callback_segment = 0x6362; // magic value - state.hidden = 1; // hide cursor on startup - state.mode = UINT8_MAX; // non-existing mode - - set_sensitivity(50, 50, 50); - reset_hardware(); - reset(); + // Callback for mouse interrupt 0x33 + auto call_int33 = CALLBACK_Allocate(); + // RealPt int33_location = RealMake(CB_SEG + 1,(call_int33 * CB_SIZE) - + // 0x10); + RealPt int33_location = RealMake(static_cast<uint16_t>(DOS_GetMemory(0x1) - 1), + 0x10); + CALLBACK_Setup(call_int33, + &int33_handler, + CB_MOUSE, + Real2Phys(int33_location), + "Mouse"); + // Wasteland needs low(seg(int33))!=0 and low(ofs(int33))!=0 + real_writed(0, 0x33 << 2, int33_location); + + auto call_mouse_bd = CALLBACK_Allocate(); + CALLBACK_Setup(call_mouse_bd, + &mouse_bd_handler, + CB_RETF8, + PhysMake(RealSeg(int33_location), + static_cast<uint16_t>(RealOff(int33_location) + 2)), + "MouseBD"); + // pseudocode for CB_MOUSE (including the special backdoor entry point): + // jump near i33hd + // callback mouse_bd_handler + // retf 8 + // label i33hd: + // callback int33_handler + // iret + + // Callback for mouse user routine return + auto call_user = CALLBACK_Allocate(); + CALLBACK_Setup(call_user, &user_callback_handler, CB_RETF_CLI, "mouse user ret"); + user_callback = CALLBACK_RealPointer(call_user); + + state.user_callback_segment = 0x6362; // magic value + state.hidden = 1; // hide cursor on startup + state.mode = UINT8_MAX; // non-existing mode + + set_sensitivity(50, 50, 50); + reset_hardware(); + reset(); } diff --git a/src/hardware/mouse/mouseif_ps2_bios.cpp b/src/hardware/mouse/mouseif_ps2_bios.cpp index 210ecfaf1..3ea8c9869 100644 --- a/src/hardware/mouse/mouseif_ps2_bios.cpp +++ b/src/hardware/mouse/mouseif_ps2_bios.cpp @@ -55,7 +55,7 @@ static float delta_x = 0.0f; // accumulated mouse movement since last reported static float delta_y = 0.0f; static int8_t counter_w = 0; // mouse wheel counter -static MouseModelPS2 protocol = MouseModelPS2::Standard; +static MouseModelPS2 protocol = MouseModelPS2::Standard; static uint8_t unlock_idx_im = 0; // sequence index for unlocking extended protocol static uint8_t unlock_idx_xp = 0; @@ -75,277 +75,277 @@ static float counts_rate = 0.0f; // 1.0 is 4 counts per mm void MOUSEPS2_UpdateButtonSquish() { - // - if VMware compatible driver is enabled, never try to report - // mouse buttons 4 and 5, that would be asking for trouble - // - for PS/2 modes other than IntelliMouse Explorer there is - // no standard way to report buttons 4 and 5 - - const bool squish = mouse_shared.active_vmm || (protocol != MouseModelPS2::Explorer); - buttons.data = squish ? buttons_12S.data : buttons_all.data; + // - if VMware compatible driver is enabled, never try to report + // mouse buttons 4 and 5, that would be asking for trouble + // - for PS/2 modes other than IntelliMouse Explorer there is + // no standard way to report buttons 4 and 5 + + const bool squish = mouse_shared.active_vmm || + (protocol != MouseModelPS2::Explorer); + buttons.data = squish ? buttons_12S.data : buttons_all.data; } static void terminate_unlick_sequence() { - unlock_idx_im = 0; - unlock_idx_xp = 0; + unlock_idx_im = 0; + unlock_idx_xp = 0; } static void set_protocol(const MouseModelPS2 new_protocol) { - terminate_unlick_sequence(); - - static bool first_time = true; - if (first_time || protocol != new_protocol) { - first_time = false; - protocol = new_protocol; - const char *protocol_name = nullptr; - switch (protocol) { - case MouseModelPS2::Standard: - protocol_name = "Standard, 3 buttons"; - break; - case MouseModelPS2::IntelliMouse: - protocol_name = "IntelliMouse, wheel, 3 buttons"; - break; - case MouseModelPS2::Explorer: - protocol_name = "IntelliMouse Explorer, wheel, 5 buttons"; - break; - default: break; - } - - LOG_MSG("MOUSE (PS/2): %s", protocol_name); - - packet[0] = 0; - packet[1] = 0; - packet[2] = 0; - packet[3] = 0; - - MOUSEPS2_UpdateButtonSquish(); - } + terminate_unlick_sequence(); + + static bool first_time = true; + if (first_time || protocol != new_protocol) { + first_time = false; + protocol = new_protocol; + const char *protocol_name = nullptr; + switch (protocol) { + case MouseModelPS2::Standard: + protocol_name = "Standard, 3 buttons"; + break; + case MouseModelPS2::IntelliMouse: + protocol_name = "IntelliMouse, wheel, 3 buttons"; + break; + case MouseModelPS2::Explorer: + protocol_name = "IntelliMouse Explorer, wheel, 5 buttons"; + break; + default: break; + } + + LOG_MSG("MOUSE (PS/2): %s", protocol_name); + + packet[0] = 0; + packet[1] = 0; + packet[2] = 0; + packet[3] = 0; + + MOUSEPS2_UpdateButtonSquish(); + } } static uint8_t get_reset_wheel_4bit() { - const int8_t tmp = std::clamp(counter_w, - static_cast<int8_t>(-0x08), - static_cast<int8_t>(0x07)); - counter_w = 0; // reading always clears the counter + const int8_t tmp = std::clamp(counter_w, + static_cast<int8_t>(-0x08), + static_cast<int8_t>(0x07)); + counter_w = 0; // reading always clears the counter - // 0x0f for -1, 0x0e for -2, etc. - return static_cast<uint8_t>((tmp >= 0) ? tmp : 0x10 + tmp); + // 0x0f for -1, 0x0e for -2, etc. + return static_cast<uint8_t>((tmp >= 0) ? tmp : 0x10 + tmp); } static uint8_t get_reset_wheel_8bit() { - const auto tmp = counter_w; - counter_w = 0; // reading always clears thecounter + const auto tmp = counter_w; + counter_w = 0; // reading always clears thecounter - // 0xff for -1, 0xfe for -2, etc. - return static_cast<uint8_t>((tmp >= 0) ? tmp : 0x100 + tmp); + // 0xff for -1, 0xfe for -2, etc. + return static_cast<uint8_t>((tmp >= 0) ? tmp : 0x100 + tmp); } static int16_t get_scaled_movement(const int16_t d) { - if (!scaling_21) - return d; - - switch (d) { - case -5: return -9; - case -4: return -6; - case -3: return -3; - case -2: return -1; - case -1: return -1; - case 1: return 1; - case 2: return 1; - case 3: return 3; - case 4: return 6; - case 5: return 9; - default: return static_cast<int16_t>(2 * d); - } + if (!scaling_21) + return d; + + switch (d) { + case -5: return -9; + case -4: return -6; + case -3: return -3; + case -2: return -1; + case -1: return -1; + case 1: return 1; + case 2: return 1; + case 3: return 3; + case 4: return 6; + case 5: return 9; + default: return static_cast<int16_t>(2 * d); + } } static void reset_counters() { - delta_x = 0.0f; - delta_y = 0.0f; - counter_w = 0; + delta_x = 0.0f; + delta_y = 0.0f; + counter_w = 0; } void MOUSEPS2_UpdatePacket() { - union { - uint8_t data = 0x08; - - bit_view<0, 1> left; - bit_view<1, 1> right; - bit_view<2, 1> middle; - bit_view<4, 1> sign_x; - bit_view<5, 1> sign_y; - bit_view<6, 1> overflow_x; - bit_view<7, 1> overflow_y; - } mdat; - - mdat.left = buttons.left; - mdat.right = buttons.right; - mdat.middle = buttons.middle; - - auto dx = static_cast<int16_t>(std::round(delta_x)); - auto dy = static_cast<int16_t>(std::round(delta_y)); - - delta_x -= dx; - delta_y -= dy; - - dx = get_scaled_movement(dx); - dy = get_scaled_movement(static_cast<int16_t>(-dy)); - - if (protocol == MouseModelPS2::Explorer) { - // There is no overflow for 5-button mouse protocol, see - // HT82M30A datasheet - dx = std::clamp(dx, - static_cast<int16_t>(-UINT8_MAX), - static_cast<int16_t>(UINT8_MAX)); - dy = std::clamp(dy, - static_cast<int16_t>(-UINT8_MAX), - static_cast<int16_t>(UINT8_MAX)); - } else { - if ((dx > 0xff) || (dx < -0xff)) - mdat.overflow_x = 1; - if ((dy > 0xff) || (dy < -0xff)) - mdat.overflow_y = 1; - } - - dx %= 0x100; - if (dx < 0) { - dx = static_cast<int16_t>(dx + 0x100); - mdat.sign_x = 1; - } - - dy %= 0x100; - if (dy < 0) { - dy = static_cast<int16_t>(dy + 0x100); - mdat.sign_y = 1; - } - - packet[0] = mdat.data; - packet[1] = static_cast<uint8_t>(dx); - packet[2] = static_cast<uint8_t>(dy); - - if (protocol == MouseModelPS2::IntelliMouse) - packet[3] = get_reset_wheel_8bit(); - else if (protocol == MouseModelPS2::Explorer) { - packet[3] = get_reset_wheel_4bit(); - if (buttons.extra_1) - bit::set(packet[3], b4); - if (buttons.extra_2) - bit::set(packet[3], b5); - } - else - packet[3] = 0; + union { + uint8_t data = 0x08; + + bit_view<0, 1> left; + bit_view<1, 1> right; + bit_view<2, 1> middle; + bit_view<4, 1> sign_x; + bit_view<5, 1> sign_y; + bit_view<6, 1> overflow_x; + bit_view<7, 1> overflow_y; + } mdat; + + mdat.left = buttons.left; + mdat.right = buttons.right; + mdat.middle = buttons.middle; + + auto dx = static_cast<int16_t>(std::round(delta_x)); + auto dy = static_cast<int16_t>(std::round(delta_y)); + + delta_x -= dx; + delta_y -= dy; + + dx = get_scaled_movement(dx); + dy = get_scaled_movement(static_cast<int16_t>(-dy)); + + if (protocol == MouseModelPS2::Explorer) { + // There is no overflow for 5-button mouse protocol, see + // HT82M30A datasheet + dx = std::clamp(dx, + static_cast<int16_t>(-UINT8_MAX), + static_cast<int16_t>(UINT8_MAX)); + dy = std::clamp(dy, + static_cast<int16_t>(-UINT8_MAX), + static_cast<int16_t>(UINT8_MAX)); + } else { + if ((dx > 0xff) || (dx < -0xff)) + mdat.overflow_x = 1; + if ((dy > 0xff) || (dy < -0xff)) + mdat.overflow_y = 1; + } + + dx %= 0x100; + if (dx < 0) { + dx = static_cast<int16_t>(dx + 0x100); + mdat.sign_x = 1; + } + + dy %= 0x100; + if (dy < 0) { + dy = static_cast<int16_t>(dy + 0x100); + mdat.sign_y = 1; + } + + packet[0] = mdat.data; + packet[1] = static_cast<uint8_t>(dx); + packet[2] = static_cast<uint8_t>(dy); + + if (protocol == MouseModelPS2::IntelliMouse) + packet[3] = get_reset_wheel_8bit(); + else if (protocol == MouseModelPS2::Explorer) { + packet[3] = get_reset_wheel_4bit(); + if (buttons.extra_1) + bit::set(packet[3], b4); + if (buttons.extra_2) + bit::set(packet[3], b5); + } else + packet[3] = 0; } static void cmd_set_resolution(const uint8_t new_counts_mm) { - terminate_unlick_sequence(); + terminate_unlick_sequence(); - if (new_counts_mm != 1 && new_counts_mm != 2 && new_counts_mm != 4 && - new_counts_mm != 8) - // Invalid parameter, set default - counts_mm = 4; - else - counts_mm = new_counts_mm; + if (new_counts_mm != 1 && new_counts_mm != 2 && new_counts_mm != 4 && + new_counts_mm != 8) + // Invalid parameter, set default + counts_mm = 4; + else + counts_mm = new_counts_mm; - counts_rate = counts_mm / 4.0f; + counts_rate = counts_mm / 4.0f; } static void cmd_set_sample_rate(const uint8_t new_rate_hz) { - reset_counters(); - - if (new_rate_hz != 10 && new_rate_hz != 20 && new_rate_hz != 40 && - new_rate_hz != 60 && new_rate_hz != 80 && new_rate_hz != 100 && - new_rate_hz != 200) { - // Invalid parameter, set default - terminate_unlick_sequence(); - rate_hz = 100; - } else - rate_hz = new_rate_hz; - - // Update event queue settings and interface information - MouseInterface::GetPS2()->NotifyInterfaceRate(rate_hz); - - // Handle extended mouse protocol unlock sequences - auto unlock = [](const std::vector<uint8_t> &sequence, - uint8_t &idx, - const MouseModelPS2 potential_protocol) { - if (sequence[idx] != rate_hz) - idx = 0; - else if (sequence.size() == ++idx) { - set_protocol(potential_protocol); - } - }; - - static const std::vector<uint8_t> seq_im = {200, 100, 80}; - static const std::vector<uint8_t> seq_xp = {200, 200, 80}; - - if (mouse_config.model_ps2 == MouseModelPS2::IntelliMouse) - unlock(seq_im, unlock_idx_im, MouseModelPS2::IntelliMouse); - else if (mouse_config.model_ps2 == MouseModelPS2::Explorer) { - unlock(seq_im, unlock_idx_im, MouseModelPS2::IntelliMouse); - unlock(seq_xp, unlock_idx_xp, MouseModelPS2::Explorer); - } + reset_counters(); + + if (new_rate_hz != 10 && new_rate_hz != 20 && new_rate_hz != 40 && + new_rate_hz != 60 && new_rate_hz != 80 && new_rate_hz != 100 && + new_rate_hz != 200) { + // Invalid parameter, set default + terminate_unlick_sequence(); + rate_hz = 100; + } else + rate_hz = new_rate_hz; + + // Update event queue settings and interface information + MouseInterface::GetPS2()->NotifyInterfaceRate(rate_hz); + + // Handle extended mouse protocol unlock sequences + auto unlock = [](const std::vector<uint8_t> &sequence, + uint8_t &idx, + const MouseModelPS2 potential_protocol) { + if (sequence[idx] != rate_hz) + idx = 0; + else if (sequence.size() == ++idx) { + set_protocol(potential_protocol); + } + }; + + static const std::vector<uint8_t> seq_im = {200, 100, 80}; + static const std::vector<uint8_t> seq_xp = {200, 200, 80}; + + if (mouse_config.model_ps2 == MouseModelPS2::IntelliMouse) + unlock(seq_im, unlock_idx_im, MouseModelPS2::IntelliMouse); + else if (mouse_config.model_ps2 == MouseModelPS2::Explorer) { + unlock(seq_im, unlock_idx_im, MouseModelPS2::IntelliMouse); + unlock(seq_xp, unlock_idx_xp, MouseModelPS2::Explorer); + } } static void cmd_set_defaults() { - cmd_set_resolution(4); - cmd_set_sample_rate(100); + cmd_set_resolution(4); + cmd_set_sample_rate(100); - MOUSEPS2_UpdateButtonSquish(); + MOUSEPS2_UpdateButtonSquish(); } static void cmd_reset() { - cmd_set_defaults(); - set_protocol(MouseModelPS2::Standard); - reset_counters(); + cmd_set_defaults(); + set_protocol(MouseModelPS2::Standard); + reset_counters(); } static void cmd_set_scaling_21(const bool enable) { - terminate_unlick_sequence(); + terminate_unlick_sequence(); - scaling_21 = enable; + scaling_21 = enable; } bool MOUSEPS2_NotifyMoved(const float x_rel, const float y_rel) { - delta_x = MOUSE_ClampRelativeMovement(delta_x + x_rel); - delta_y = MOUSE_ClampRelativeMovement(delta_y + y_rel); + delta_x = MOUSE_ClampRelativeMovement(delta_x + x_rel); + delta_y = MOUSE_ClampRelativeMovement(delta_y + y_rel); - return (std::fabs(delta_x) >= 0.5f) || (std::fabs(delta_y) >= 0.5f); + return (std::fabs(delta_x) >= 0.5f) || (std::fabs(delta_y) >= 0.5f); } bool MOUSEPS2_NotifyButton(const MouseButtons12S new_buttons_12S, const MouseButtonsAll new_buttons_all) { - const auto buttons_old = buttons; + const auto buttons_old = buttons; - buttons_12S = new_buttons_12S; - buttons_all = new_buttons_all; - MOUSEPS2_UpdateButtonSquish(); + buttons_12S = new_buttons_12S; + buttons_all = new_buttons_all; + MOUSEPS2_UpdateButtonSquish(); - return (buttons_old.data != buttons.data); + return (buttons_old.data != buttons.data); } bool MOUSEPS2_NotifyWheel(const int16_t w_rel) { - if (protocol != MouseModelPS2::IntelliMouse && - protocol != MouseModelPS2::Explorer) - return false; + if (protocol != MouseModelPS2::IntelliMouse && + protocol != MouseModelPS2::Explorer) + return false; - auto old_counter_w = counter_w; - counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + w_rel)); + auto old_counter_w = counter_w; + counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + w_rel)); - return (old_counter_w != counter_w); + return (old_counter_w != counter_w); } // *************************************************************************** @@ -369,157 +369,157 @@ static RealPt ps2_callback = 0; void MOUSEBIOS_Reset() { - cmd_reset(); - PIC_SetIRQMask(12, false); // lower IRQ line - MOUSEVMM_Deactivate(); // VBADOS seems to expect this + cmd_reset(); + PIC_SetIRQMask(12, false); // lower IRQ line + MOUSEVMM_Deactivate(); // VBADOS seems to expect this } void MOUSEBIOS_SetCallback(const uint16_t pseg, const uint16_t pofs) { - if ((pseg == 0) && (pofs == 0)) { - callback_init = false; - } else { - callback_init = true; - callback_seg = pseg; - callback_ofs = pofs; - } + if ((pseg == 0) && (pofs == 0)) { + callback_init = false; + } else { + callback_init = true; + callback_seg = pseg; + callback_ofs = pofs; + } } bool MOUSEBIOS_SetPacketSize(const uint8_t packet_size) { - if (packet_size == 3) - packet_4bytes = false; - else if (packet_size == 4) - packet_4bytes = true; - else - return false; // unsupported packet size - - return true; + if (packet_size == 3) + packet_4bytes = false; + else if (packet_size == 4) + packet_4bytes = true; + else + return false; // unsupported packet size + + return true; } bool MOUSEBIOS_SetSampleRate(const uint8_t rate_id) { - switch (rate_id) { - case 0: cmd_set_sample_rate(10); break; - case 1: cmd_set_sample_rate(20); break; - case 2: cmd_set_sample_rate(40); break; - case 3: cmd_set_sample_rate(60); break; - case 4: cmd_set_sample_rate(80); break; - case 5: cmd_set_sample_rate(100); break; - case 6: cmd_set_sample_rate(200); break; - default: return false; - } - - return true; + switch (rate_id) { + case 0: cmd_set_sample_rate(10); break; + case 1: cmd_set_sample_rate(20); break; + case 2: cmd_set_sample_rate(40); break; + case 3: cmd_set_sample_rate(60); break; + case 4: cmd_set_sample_rate(80); break; + case 5: cmd_set_sample_rate(100); break; + case 6: cmd_set_sample_rate(200); break; + default: return false; + } + + return true; } bool MOUSEBIOS_SetResolution(const uint8_t res_id) { - switch (res_id) { - case 0: cmd_set_resolution(1); break; - case 1: cmd_set_resolution(2); break; - case 2: cmd_set_resolution(4); break; - case 3: cmd_set_resolution(8); break; - default: return false; - } - - return true; + switch (res_id) { + case 0: cmd_set_resolution(1); break; + case 1: cmd_set_resolution(2); break; + case 2: cmd_set_resolution(4); break; + case 3: cmd_set_resolution(8); break; + default: return false; + } + + return true; } void MOUSEBIOS_SetScaling21(const bool enable) { - cmd_set_scaling_21(enable); + cmd_set_scaling_21(enable); } bool MOUSEBIOS_SetState(const bool use) { - if (use && !callback_init) { - mouse_shared.active_bios = false; - MOUSE_NotifyStateChanged(); - return false; - } else { - mouse_shared.active_bios = use; - MOUSE_NotifyStateChanged(); - return true; - } + if (use && !callback_init) { + mouse_shared.active_bios = false; + MOUSE_NotifyStateChanged(); + return false; + } else { + mouse_shared.active_bios = use; + MOUSE_NotifyStateChanged(); + return true; + } } uint8_t MOUSEBIOS_GetResolution() { - return counts_mm; + return counts_mm; } uint8_t MOUSEBIOS_GetSampleRate() { - return rate_hz; + return rate_hz; } uint8_t MOUSEBIOS_GetStatus() { - union { - uint8_t data = 0; - - bit_view<0, 1> left; - bit_view<1, 1> right; - bit_view<2, 1> middle; - // bit 3 - reserved - bit_view<4, 1> scaling_21; - bit_view<5, 1> reporting; - bit_view<6, 1> mode_remote; - // bit 7 - reserved - } ret; - - ret.left = buttons.left; - ret.right = buttons.right; - ret.middle = buttons.middle; - - ret.scaling_21 = scaling_21; - ret.reporting = 1; - - return ret.data; + union { + uint8_t data = 0; + + bit_view<0, 1> left; + bit_view<1, 1> right; + bit_view<2, 1> middle; + // bit 3 - reserved + bit_view<4, 1> scaling_21; + bit_view<5, 1> reporting; + bit_view<6, 1> mode_remote; + // bit 7 - reserved + } ret; + + ret.left = buttons.left; + ret.right = buttons.right; + ret.middle = buttons.middle; + + ret.scaling_21 = scaling_21; + ret.reporting = 1; + + return ret.data; } uint8_t MOUSEBIOS_GetProtocol() { - return static_cast<uint8_t>(protocol); + return static_cast<uint8_t>(protocol); } static Bitu callback_ret() { - CPU_Pop16(); - CPU_Pop16(); - CPU_Pop16(); - CPU_Pop16(); // remove 4 words - return CBRET_NONE; + CPU_Pop16(); + CPU_Pop16(); + CPU_Pop16(); + CPU_Pop16(); // remove 4 words + return CBRET_NONE; } Bitu MOUSEBIOS_DoCallback() { - if (!packet_4bytes) { - CPU_Push16(packet[0]); - CPU_Push16(packet[1]); - CPU_Push16(packet[2]); - } else { - CPU_Push16(static_cast<uint16_t>((packet[0] + packet[1] * 0x100))); - CPU_Push16(packet[2]); - CPU_Push16(packet[3]); - } - CPU_Push16((uint16_t)0); - - CPU_Push16(RealSeg(ps2_callback)); - CPU_Push16(RealOff(ps2_callback)); - SegSet16(cs, callback_seg); - reg_ip = callback_ofs; - - return CBRET_NONE; + if (!packet_4bytes) { + CPU_Push16(packet[0]); + CPU_Push16(packet[1]); + CPU_Push16(packet[2]); + } else { + CPU_Push16(static_cast<uint16_t>((packet[0] + packet[1] * 0x100))); + CPU_Push16(packet[2]); + CPU_Push16(packet[3]); + } + CPU_Push16((uint16_t)0); + + CPU_Push16(RealSeg(ps2_callback)); + CPU_Push16(RealOff(ps2_callback)); + SegSet16(cs, callback_seg); + reg_ip = callback_ofs; + + return CBRET_NONE; } void MOUSEPS2_Init() { - // Callback for ps2 user callback handling - auto call_ps2 = CALLBACK_Allocate(); - CALLBACK_Setup(call_ps2, &callback_ret, CB_RETF, "ps2 bios callback"); - ps2_callback = CALLBACK_RealPointer(call_ps2); + // Callback for ps2 user callback handling + auto call_ps2 = CALLBACK_Allocate(); + CALLBACK_Setup(call_ps2, &callback_ret, CB_RETF, "ps2 bios callback"); + ps2_callback = CALLBACK_RealPointer(call_ps2); - MOUSEBIOS_Reset(); + MOUSEBIOS_Reset(); } diff --git a/src/hardware/mouse/mouseif_virtual_machines.cpp b/src/hardware/mouse/mouseif_virtual_machines.cpp index 32f4a5fca..b2133bab6 100644 --- a/src/hardware/mouse/mouseif_virtual_machines.cpp +++ b/src/hardware/mouse/mouseif_virtual_machines.cpp @@ -43,41 +43,41 @@ CHECK_NARROWING(); // - https://git.javispedro.com/cgit/vbmouse.git (planned support) enum class VMwareCmd : uint16_t { - GetVersion = 10, - AbsPointerData = 39, - AbsPointerStatus = 40, - AbsPointerCommand = 41, + GetVersion = 10, + AbsPointerData = 39, + AbsPointerStatus = 40, + AbsPointerCommand = 41, }; enum class VMwareAbsPointer : uint32_t { - Enable = 0x45414552, - Relative = 0xF5, - Absolute = 0x53424152, + Enable = 0x45414552, + Relative = 0xF5, + Absolute = 0x53424152, }; union VMwareButtons { - uint8_t data = 0; - bit_view<5, 1> left; - bit_view<4, 1> right; - bit_view<3, 1> middle; + uint8_t data = 0; + bit_view<5, 1> left; + bit_view<4, 1> right; + bit_view<3, 1> middle; }; static constexpr io_port_t VMWARE_PORT = 0x5658u; // communication port // static constexpr io_port_t VMWARE_PORTHB = 0x5659u; // communication port, - // high bandwidth +// high bandwidth static constexpr uint32_t VMWARE_MAGIC = 0x564D5868u; // magic number for all // VMware calls static constexpr uint32_t ABS_UPDATED = 4; // tells that new pointer // position is available static constexpr uint32_t ABS_NOT_UPDATED = 0; -static bool raw_input = true; // true = no host mouse acceleration pre-applied -static bool is_mapped = false; // true = physical mouse is mapped to this interface -static bool updated = false; // true = mouse state update waits to be picked up -static VMwareButtons buttons; // state of mouse buttons, in VMware format -static uint16_t scaled_x = 0x7fff; // absolute position scaled from 0 to 0xffff -static uint16_t scaled_y = 0x7fff; // 0x7fff is a center position -static int8_t counter_w = 0; // wheel movement counter +static bool raw_input = true; // true = no host mouse acceleration pre-applied +static bool is_mapped = false; // true = physical mouse is mapped to this interface +static bool updated = false; // true = mouse state update waits to be picked up +static VMwareButtons buttons; // state of mouse buttons, in VMware format +static uint16_t scaled_x = 0x7fff; // absolute position scaled from 0 to 0xffff +static uint16_t scaled_y = 0x7fff; // 0x7fff is a center position +static int8_t counter_w = 0; // wheel movement counter static float pos_x = 0.0f; static float pos_y = 0.0f; @@ -85,8 +85,7 @@ static float pos_y = 0.0f; // Multiply scale by 0.02f to put acceleration_vmm in a reasonable // range, similar to sensitivity_dos or sensitivity_vmm) constexpr float acceleration_multiplier = 0.02f; -static MouseSpeedCalculator speed_xy(acceleration_multiplier * - mouse_predefined.acceleration_vmm); +static MouseSpeedCalculator speed_xy(acceleration_multiplier *mouse_predefined.acceleration_vmm); // *************************************************************************** // VMware interface implementation @@ -94,201 +93,203 @@ static MouseSpeedCalculator speed_xy(acceleration_multiplier * static void MOUSEVMM_Activate() { - if (!mouse_shared.active_vmm) { - mouse_shared.active_vmm = true; - LOG_MSG("MOUSE (PS/2): VMware protocol enabled"); - if (mouse_is_captured) { - // If mouse is captured, prepare sane start settings - // (center of the screen, will trigger mouse move event) - pos_x = mouse_video.res_x / 2.0f; - pos_y = mouse_video.res_y / 2.0f; - scaled_x = 0; - scaled_y = 0; - } - MOUSEPS2_UpdateButtonSquish(); - MOUSE_NotifyStateChanged(); - } - buttons.data = 0; - counter_w = 0; + if (!mouse_shared.active_vmm) { + mouse_shared.active_vmm = true; + LOG_MSG("MOUSE (PS/2): VMware protocol enabled"); + if (mouse_is_captured) { + // If mouse is captured, prepare sane start settings + // (center of the screen, will trigger mouse move event) + pos_x = mouse_video.res_x / 2.0f; + pos_y = mouse_video.res_y / 2.0f; + scaled_x = 0; + scaled_y = 0; + } + MOUSEPS2_UpdateButtonSquish(); + MOUSE_NotifyStateChanged(); + } + buttons.data = 0; + counter_w = 0; } void MOUSEVMM_Deactivate() { - if (mouse_shared.active_vmm) { - mouse_shared.active_vmm = false; - LOG_MSG("MOUSE (PS/2): VMware protocol disabled"); - MOUSEPS2_UpdateButtonSquish(); - MOUSE_NotifyStateChanged(); - } - buttons.data = 0; - counter_w = 0; + if (mouse_shared.active_vmm) { + mouse_shared.active_vmm = false; + LOG_MSG("MOUSE (PS/2): VMware protocol disabled"); + MOUSEPS2_UpdateButtonSquish(); + MOUSE_NotifyStateChanged(); + } + buttons.data = 0; + counter_w = 0; } void MOUSEVMM_NotifyMapped(const bool enabled) { - is_mapped = enabled; + is_mapped = enabled; } void MOUSEVMM_NotifyRawInput(const bool enabled) { - raw_input = enabled; + raw_input = enabled; } static void cmd_get_version() { - reg_eax = 0; // protocol version - reg_ebx = VMWARE_MAGIC; + reg_eax = 0; // protocol version + reg_ebx = VMWARE_MAGIC; } static void cmd_abs_pointer_data() { - reg_eax = buttons.data; - reg_ebx = scaled_x; - reg_ecx = scaled_y; - reg_edx = static_cast<uint32_t>((counter_w >= 0) ? counter_w : 0x100 + counter_w); + reg_eax = buttons.data; + reg_ebx = scaled_x; + reg_ecx = scaled_y; + reg_edx = static_cast<uint32_t>((counter_w >= 0) ? counter_w + : 0x100 + counter_w); - counter_w = 0; + counter_w = 0; } static void cmd_abs_pointer_status() { - reg_eax = updated ? ABS_UPDATED : ABS_NOT_UPDATED; - updated = false; + reg_eax = updated ? ABS_UPDATED : ABS_NOT_UPDATED; + updated = false; } static void cmd_abs_pointer_command() { - switch (static_cast<VMwareAbsPointer>(reg_ebx)) { - case VMwareAbsPointer::Enable: break; // can be safely ignored - case VMwareAbsPointer::Relative: MOUSEVMM_Deactivate(); break; - case VMwareAbsPointer::Absolute: MOUSEVMM_Activate(); break; - default: - LOG_WARNING("MOUSE (PS/2): unimplemented VMware subcommand 0x%08x", - reg_ebx); - break; - } + switch (static_cast<VMwareAbsPointer>(reg_ebx)) { + case VMwareAbsPointer::Enable: break; // can be safely ignored + case VMwareAbsPointer::Relative: MOUSEVMM_Deactivate(); break; + case VMwareAbsPointer::Absolute: MOUSEVMM_Activate(); break; + default: + LOG_WARNING("MOUSE (PS/2): unimplemented VMware subcommand 0x%08x", + reg_ebx); + break; + } } static uint32_t port_read_vmware(const io_port_t, const io_width_t) { - if (reg_eax != VMWARE_MAGIC) - return 0; - - switch (static_cast<VMwareCmd>(reg_cx)) { - case VMwareCmd::GetVersion: cmd_get_version(); break; - case VMwareCmd::AbsPointerData: cmd_abs_pointer_data(); break; - case VMwareCmd::AbsPointerStatus: cmd_abs_pointer_status(); break; - case VMwareCmd::AbsPointerCommand: cmd_abs_pointer_command(); break; - default: - LOG_WARNING("MOUSE (PS/2): unimplemented VMware command 0x%08x", - reg_ecx); - break; - } - - return reg_eax; + if (reg_eax != VMWARE_MAGIC) + return 0; + + switch (static_cast<VMwareCmd>(reg_cx)) { + case VMwareCmd::GetVersion: cmd_get_version(); break; + case VMwareCmd::AbsPointerData: cmd_abs_pointer_data(); break; + case VMwareCmd::AbsPointerStatus: cmd_abs_pointer_status(); break; + case VMwareCmd::AbsPointerCommand: cmd_abs_pointer_command(); break; + default: + LOG_WARNING("MOUSE (PS/2): unimplemented VMware command 0x%08x", + reg_ecx); + break; + } + + return reg_eax; } bool MOUSEVMM_NotifyMoved(const float x_rel, const float y_rel, const uint16_t x_abs, const uint16_t y_abs) { - if (!mouse_shared.active_vmm) - return false; - - speed_xy.Update(std::sqrt(x_rel * x_rel + y_rel * y_rel)); - - const auto old_scaled_x = scaled_x; - const auto old_scaled_y = scaled_y; - - auto calculate = [](float &position, - const float relative, - const uint16_t absolute, - const uint16_t resolution, - const uint16_t clip) { - assert(resolution > 1u); - - if (mouse_is_captured || is_mapped) { - // Mouse is captured, there is no need for pointer - // integration with host OS - we can use relative - // movement with configured sensitivity and (for - // raw mouse input) our built-in pointer acceleration - // model - - if (raw_input) { - const auto coeff = MOUSE_GetBallisticsCoeff(speed_xy.Get()); - position += MOUSE_ClampRelativeMovement(relative * coeff); - } - else - position += MOUSE_ClampRelativeMovement(relative); - } else - // Cursor position controlled by the host OS - position = static_cast<float>(std::max(absolute - clip, 0)); - - position = std::clamp(position, 0.0f, static_cast<float>(resolution)); - - const auto scale = static_cast<float>(UINT16_MAX) / - static_cast<float>(resolution - 1); - const auto tmp = std::min(static_cast<uint32_t>(UINT16_MAX), - static_cast<uint32_t>( - std::lround(position * scale))); - - return static_cast<uint16_t>(tmp); - }; - - scaled_x = calculate(pos_x, x_rel, x_abs, mouse_video.res_x, mouse_video.clip_x); - scaled_y = calculate(pos_y, y_rel, y_abs, mouse_video.res_y, mouse_video.clip_y); - - // Filter out unneeded events (like sub-pixel mouse movements, - // which won't change guest side mouse state) - if (GCC_UNLIKELY(old_scaled_x == scaled_x && old_scaled_y == scaled_y)) - return false; - - updated = true; - return true; + if (!mouse_shared.active_vmm) + return false; + + speed_xy.Update(std::sqrt(x_rel * x_rel + y_rel * y_rel)); + + const auto old_scaled_x = scaled_x; + const auto old_scaled_y = scaled_y; + + auto calculate = [](float &position, + const float relative, + const uint16_t absolute, + const uint16_t resolution, + const uint16_t clip) { + assert(resolution > 1u); + + if (mouse_is_captured || is_mapped) { + // Mouse is captured, there is no need for pointer + // integration with host OS - we can use relative + // movement with configured sensitivity and (for + // raw mouse input) our built-in pointer acceleration + // model + + if (raw_input) { + const auto coeff = MOUSE_GetBallisticsCoeff( + speed_xy.Get()); + position += MOUSE_ClampRelativeMovement( + relative * coeff); + } else + position += MOUSE_ClampRelativeMovement(relative); + } else + // Cursor position controlled by the host OS + position = static_cast<float>(std::max(absolute - clip, 0)); + + position = std::clamp(position, 0.0f, static_cast<float>(resolution)); + + const auto scale = static_cast<float>(UINT16_MAX) / + static_cast<float>(resolution - 1); + const auto tmp = std::min(static_cast<uint32_t>(UINT16_MAX), + static_cast<uint32_t>( + std::lround(position * scale))); + + return static_cast<uint16_t>(tmp); + }; + + scaled_x = calculate(pos_x, x_rel, x_abs, mouse_video.res_x, mouse_video.clip_x); + scaled_y = calculate(pos_y, y_rel, y_abs, mouse_video.res_y, mouse_video.clip_y); + + // Filter out unneeded events (like sub-pixel mouse movements, + // which won't change guest side mouse state) + if (GCC_UNLIKELY(old_scaled_x == scaled_x && old_scaled_y == scaled_y)) + return false; + + updated = true; + return true; } bool MOUSEVMM_NotifyButton(const MouseButtons12S buttons_12S) { - if (!mouse_shared.active_vmm) - return false; + if (!mouse_shared.active_vmm) + return false; - const auto old_buttons = buttons; - buttons.data = 0; + const auto old_buttons = buttons; + buttons.data = 0; - // Direct assignment of .data is not possible, as bit layout is different - buttons.left = static_cast<bool>(buttons_12S.left); - buttons.right = static_cast<bool>(buttons_12S.right); - buttons.middle = static_cast<bool>(buttons_12S.middle); + // Direct assignment of .data is not possible, as bit layout is different + buttons.left = static_cast<bool>(buttons_12S.left); + buttons.right = static_cast<bool>(buttons_12S.right); + buttons.middle = static_cast<bool>(buttons_12S.middle); - if (GCC_UNLIKELY(old_buttons.data == buttons.data)) - return false; + if (GCC_UNLIKELY(old_buttons.data == buttons.data)) + return false; - updated = true; - return true; + updated = true; + return true; } bool MOUSEVMM_NotifyWheel(const int16_t w_rel) { - if (!mouse_shared.active_vmm) - return false; + if (!mouse_shared.active_vmm) + return false; - const auto old_counter_w = counter_w; - counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + w_rel)); + const auto old_counter_w = counter_w; + counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + w_rel)); - if (GCC_UNLIKELY(old_counter_w == counter_w)) - return false; + if (GCC_UNLIKELY(old_counter_w == counter_w)) + return false; - updated = true; - return true; + updated = true; + return true; } void MOUSEVMM_NewScreenParams(const uint16_t x_abs, const uint16_t y_abs) { - // Report a fake mouse movement - if (MOUSEVMM_NotifyMoved(0.0f, 0.0f, x_abs, y_abs) && mouse_shared.active_vmm) - MOUSE_NotifyFakePS2(); + // Report a fake mouse movement + if (MOUSEVMM_NotifyMoved(0.0f, 0.0f, x_abs, y_abs) && mouse_shared.active_vmm) + MOUSE_NotifyFakePS2(); } void MOUSEVMM_Init() { - IO_RegisterReadHandler(VMWARE_PORT, port_read_vmware, io_width_t::dword); + IO_RegisterReadHandler(VMWARE_PORT, port_read_vmware, io_width_t::dword); } diff --git a/src/hardware/serialport/serialmouse.cpp b/src/hardware/serialport/serialmouse.cpp index 7063da017..745f740a1 100644 --- a/src/hardware/serialport/serialmouse.cpp +++ b/src/hardware/serialport/serialmouse.cpp @@ -34,545 +34,545 @@ CHECK_NARROWING(); - // Port clock divider for 1200 baud transmission static constexpr uint16_t divider_1200_baud = 96; // 1200 baud serial mice is limited to about 40 Hz sampling rate // due to serial port transmission constraints static constexpr uint16_t rate_1200_baud = 40; - CSerialMouse::CSerialMouse(const uint8_t id, CommandLine *cmd) : CSerial(id, cmd), port_id(id), port_num(static_cast<uint16_t>(id + 1)) { - auto interface = MouseInterface::GetSerial(port_id); - if (!interface) - return; + auto interface = MouseInterface::GetSerial(port_id); + if (!interface) + return; - // Get the parameters from the configuration file + // Get the parameters from the configuration file - param_model = mouse_config.model_com; - param_auto_msm = mouse_config.model_com_auto_msm; + param_model = mouse_config.model_com; + param_auto_msm = mouse_config.model_com_auto_msm; - // Handle deprecated parameters + // Handle deprecated parameters - HandleDeprecatedOptions(cmd); + HandleDeprecatedOptions(cmd); - // Override with parameters from command line or [serial] section + // Override with parameters from command line or [serial] section - std::string model_string; - if (cmd->FindStringBegin("model:", model_string, false) && - !MouseConfig::ParseSerialModel(model_string, param_model, param_auto_msm)) { - LOG_ERR("MOUSE (COM%d): Invalid model '%s'", - port_num, - model_string.c_str()); - } + std::string model_string; + if (cmd->FindStringBegin("model:", model_string, false) && + !MouseConfig::ParseSerialModel(model_string, param_model, param_auto_msm)) { + LOG_ERR("MOUSE (COM%d): Invalid model '%s'", + port_num, + model_string.c_str()); + } - CSerial::Init_Registers(); - setRI(false); - setDSR(false); - setCD(false); - setCTS(false); + CSerial::Init_Registers(); + setRI(false); + setDSR(false); + setCD(false); + setCTS(false); - interface->RegisterListener(*this); - interface->NotifyInterfaceRate(rate_1200_baud); - InstallationSuccessful = true; + interface->RegisterListener(*this); + interface->NotifyInterfaceRate(rate_1200_baud); + InstallationSuccessful = true; } CSerialMouse::~CSerialMouse() { - auto interface = MouseInterface::GetSerial(port_id); - if (interface) - interface->UnRegisterListener(); + auto interface = MouseInterface::GetSerial(port_id); + if (interface) + interface->UnRegisterListener(); - removeEvent(SERIAL_TX_EVENT); // clear events - SetModel(MouseModelCOM::NoMouse); + removeEvent(SERIAL_TX_EVENT); // clear events + SetModel(MouseModelCOM::NoMouse); } void CSerialMouse::HandleDeprecatedOptions(CommandLine *cmd) { - std::string option; - if (cmd->FindStringBegin("rate:", option, false)) - LOG_WARNING("MOUSE (COM%d): Deprecated option 'rate:' - ignored", port_num); - - const bool found_deprecated = cmd->FindStringBegin("type:", option, false); - - if (found_deprecated) { - LOG_WARNING("MOUSE (COM%d): Deprecated option 'type:'", port_num); - - if (option == "2btn") { - param_model = MouseModelCOM::Microsoft; - param_auto_msm = false; - } else if (option == "2btn+msm") { - param_model = MouseModelCOM::Microsoft; - param_auto_msm = true; - } else if (option == "3btn") { - param_model = MouseModelCOM::Logitech; - param_auto_msm = false; - } else if (option == "3btn+msm") { - param_model = MouseModelCOM::Logitech; - param_auto_msm = true; - } else if (option == "wheel") { - param_model = MouseModelCOM::Wheel; - param_auto_msm = false; - } else if (option == "wheel+msm") { - param_model = MouseModelCOM::Wheel; - param_auto_msm = true; - } else if (option == "msm") { - param_model = MouseModelCOM::MouseSystems; - param_auto_msm = false; - } else { - LOG_ERR("MOUSE (COM%d): Invalid type '%s'", - port_num, - option.c_str()); - return; - } - } + std::string option; + if (cmd->FindStringBegin("rate:", option, false)) + LOG_WARNING("MOUSE (COM%d): Deprecated option 'rate:' - ignored", + port_num); + + const bool found_deprecated = cmd->FindStringBegin("type:", option, false); + + if (found_deprecated) { + LOG_WARNING("MOUSE (COM%d): Deprecated option 'type:'", port_num); + + if (option == "2btn") { + param_model = MouseModelCOM::Microsoft; + param_auto_msm = false; + } else if (option == "2btn+msm") { + param_model = MouseModelCOM::Microsoft; + param_auto_msm = true; + } else if (option == "3btn") { + param_model = MouseModelCOM::Logitech; + param_auto_msm = false; + } else if (option == "3btn+msm") { + param_model = MouseModelCOM::Logitech; + param_auto_msm = true; + } else if (option == "wheel") { + param_model = MouseModelCOM::Wheel; + param_auto_msm = false; + } else if (option == "wheel+msm") { + param_model = MouseModelCOM::Wheel; + param_auto_msm = true; + } else if (option == "msm") { + param_model = MouseModelCOM::MouseSystems; + param_auto_msm = false; + } else { + LOG_ERR("MOUSE (COM%d): Invalid type '%s'", + port_num, + option.c_str()); + return; + } + } } void CSerialMouse::BoostRate(const uint16_t rate_hz) { - if (!rate_hz || model == MouseModelCOM::NoMouse) { - rate_coeff = 1.0f; - return; - } - - // Estimate current sampling rate, as precisely as possible - auto estimate = [](const uint16_t bauds, - const uint8_t byte_len, - const MouseModelCOM model) { - // In addition to byte_len, the mouse has to send - // 3 more bits per each byte: start, parity, stop - - if (model == MouseModelCOM::Microsoft || - model == MouseModelCOM::Logitech || - model == MouseModelCOM::Wheel) - // Microsoft-style protocol - // single movement needs exactly 3 bytes to be reported - return bauds / (static_cast<float>(byte_len + 3) * 3.0f); - else if (model == MouseModelCOM::MouseSystems) - // Mouse Systems protocol - // single movement needs per average 2.5 bytes to be reported - return bauds / (static_cast<float>(byte_len + 3) * 2.5f); - - assert(false); // unimplemented - return static_cast<float>(rate_1200_baud); - }; - - // Calculate coefficient to match requested rate - rate_coeff = estimate(1200, port_byte_len, model) / rate_hz; + if (!rate_hz || model == MouseModelCOM::NoMouse) { + rate_coeff = 1.0f; + return; + } + + // Estimate current sampling rate, as precisely as possible + auto estimate = [](const uint16_t bauds, + const uint8_t byte_len, + const MouseModelCOM model) { + // In addition to byte_len, the mouse has to send + // 3 more bits per each byte: start, parity, stop + + if (model == MouseModelCOM::Microsoft || + model == MouseModelCOM::Logitech || model == MouseModelCOM::Wheel) + // Microsoft-style protocol + // single movement needs exactly 3 bytes to be reported + return bauds / (static_cast<float>(byte_len + 3) * 3.0f); + else if (model == MouseModelCOM::MouseSystems) + // Mouse Systems protocol + // single movement needs per average 2.5 bytes to be + // reported + return bauds / (static_cast<float>(byte_len + 3) * 2.5f); + + assert(false); // unimplemented + return static_cast<float>(rate_1200_baud); + }; + + // Calculate coefficient to match requested rate + rate_coeff = estimate(1200, port_byte_len, model) / rate_hz; } void CSerialMouse::SetModel(const MouseModelCOM new_model) { - if (model != new_model) { - model = new_model; - const char *name = nullptr; - switch (model) { - case MouseModelCOM::NoMouse: // just to print out log in the destructor - name = "(none)"; - break; - case MouseModelCOM::Microsoft: - name = "Microsoft, 2 buttons"; - has_3rd_button = false; - has_wheel = false; - break; - case MouseModelCOM::Logitech: - name = "Logitech, 3 buttons"; - has_3rd_button = true; - has_wheel = false; - break; - case MouseModelCOM::Wheel: - name = "wheel, 3 buttons"; - has_3rd_button = true; - has_wheel = true; - break; - case MouseModelCOM::MouseSystems: - name = "Mouse Systems, 3 buttons"; - has_3rd_button = true; - has_wheel = false; - break; - default: - assert(false); // unimplemented - break; - } - - if (name) - LOG_MSG("MOUSE (COM%d): %s", port_num, name); - } - - // So far all emulated mice are 1200 bauds, but report anyway - // to trigger rate_coeff recalculation - MouseInterface::GetSerial(port_id)->NotifyInterfaceRate(rate_1200_baud); + if (model != new_model) { + model = new_model; + const char *name = nullptr; + switch (model) { + case MouseModelCOM::NoMouse: // just to print out log in the + // destructor + name = "(none)"; + break; + case MouseModelCOM::Microsoft: + name = "Microsoft, 2 buttons"; + has_3rd_button = false; + has_wheel = false; + break; + case MouseModelCOM::Logitech: + name = "Logitech, 3 buttons"; + has_3rd_button = true; + has_wheel = false; + break; + case MouseModelCOM::Wheel: + name = "wheel, 3 buttons"; + has_3rd_button = true; + has_wheel = true; + break; + case MouseModelCOM::MouseSystems: + name = "Mouse Systems, 3 buttons"; + has_3rd_button = true; + has_wheel = false; + break; + default: + assert(false); // unimplemented + break; + } + + if (name) + LOG_MSG("MOUSE (COM%d): %s", port_num, name); + } + + // So far all emulated mice are 1200 bauds, but report anyway + // to trigger rate_coeff recalculation + MouseInterface::GetSerial(port_id)->NotifyInterfaceRate(rate_1200_baud); } void CSerialMouse::AbortPacket() { - packet_len = 0; - xmit_idx = 0xff; - need_xmit_part2 = false; - got_another_move = false; - got_another_button = false; + packet_len = 0; + xmit_idx = 0xff; + need_xmit_part2 = false; + got_another_move = false; + got_another_button = false; } void CSerialMouse::ClearCounters() { - counter_x = 0; - counter_y = 0; - counter_w = 0; + counter_x = 0; + counter_y = 0; + counter_w = 0; } void CSerialMouse::MouseReset() { - AbortPacket(); - ClearCounters(); - buttons = 0; - send_ack = true; + AbortPacket(); + ClearCounters(); + buttons = 0; + send_ack = true; - SetEventRX(); + SetEventRX(); } void CSerialMouse::NotifyMoved(const float x_rel, const float y_rel) { - delta_x = MOUSE_ClampRelativeMovement(delta_x + x_rel); - delta_y = MOUSE_ClampRelativeMovement(delta_y + y_rel); + delta_x = MOUSE_ClampRelativeMovement(delta_x + x_rel); + delta_y = MOUSE_ClampRelativeMovement(delta_y + y_rel); - const auto dx = static_cast<int16_t>(std::lround(delta_x)); - const auto dy = static_cast<int16_t>(std::lround(delta_y)); + const auto dx = static_cast<int16_t>(std::lround(delta_x)); + const auto dy = static_cast<int16_t>(std::lround(delta_y)); - if (dx == 0 && dy == 0) - return; // movement not significant enough + if (dx == 0 && dy == 0) + return; // movement not significant enough - counter_x = clamp_to_int8(counter_x + dx); - counter_y = clamp_to_int8(counter_y + dy); + counter_x = clamp_to_int8(counter_x + dx); + counter_y = clamp_to_int8(counter_y + dy); - delta_x -= dx; - delta_y -= dy; + delta_x -= dx; + delta_y -= dy; - // Initiate data transfer and form the packet to transmit. If another - // packet is already transmitting now then wait for it to finish before - // transmitting ours, and let the mouse motion accumulate in the meantime + // Initiate data transfer and form the packet to transmit. If another + // packet is already transmitting now then wait for it to finish before + // transmitting ours, and let the mouse motion accumulate in the meantime - if (xmit_idx >= packet_len) - StartPacketData(); - else - got_another_move = true; + if (xmit_idx >= packet_len) + StartPacketData(); + else + got_another_move = true; } void CSerialMouse::NotifyButton(const uint8_t new_buttons, const uint8_t idx) { - if (!has_3rd_button && idx > 1) - return; + if (!has_3rd_button && idx > 1) + return; - buttons = new_buttons; + buttons = new_buttons; - if (xmit_idx >= packet_len) - StartPacketData(idx > 1); - else - got_another_button = true; + if (xmit_idx >= packet_len) + StartPacketData(idx > 1); + else + got_another_button = true; } void CSerialMouse::NotifyWheel(const int16_t w_rel) { - if (!has_wheel) - return; + if (!has_wheel) + return; - counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + w_rel)); + counter_w = clamp_to_int8(static_cast<int32_t>(counter_w + w_rel)); - if (xmit_idx >= packet_len) - StartPacketData(true); - else - got_another_button = true; + if (xmit_idx >= packet_len) + StartPacketData(true); + else + got_another_button = true; } void CSerialMouse::StartPacketId() // send the mouse identifier { - if (model == MouseModelCOM::NoMouse) - return; - AbortPacket(); - ClearCounters(); - - packet_len = 0; - switch (model) { - case MouseModelCOM::Microsoft: packet[packet_len++] = 'M'; break; - case MouseModelCOM::Logitech: - packet[packet_len++] = 'M'; - packet[packet_len++] = '3'; - break; - case MouseModelCOM::Wheel: - packet[packet_len++] = 'M'; - packet[packet_len++] = 'Z'; - packet[packet_len++] = '@'; // for some reason 86Box sends more - // than just 'MZ' - packet[packet_len++] = 0; - packet[packet_len++] = 0; - packet[packet_len++] = 0; - break; - case MouseModelCOM::MouseSystems: - packet[packet_len++] = 'H'; - break; - default: - assert(false); // unimplemented - break; - } - - // send packet - xmit_idx = 0; - SetEventRX(); + if (model == MouseModelCOM::NoMouse) + return; + AbortPacket(); + ClearCounters(); + + packet_len = 0; + switch (model) { + case MouseModelCOM::Microsoft: packet[packet_len++] = 'M'; break; + case MouseModelCOM::Logitech: + packet[packet_len++] = 'M'; + packet[packet_len++] = '3'; + break; + case MouseModelCOM::Wheel: + packet[packet_len++] = 'M'; + packet[packet_len++] = 'Z'; + packet[packet_len++] = '@'; // for some reason 86Box sends more + // than just 'MZ' + packet[packet_len++] = 0; + packet[packet_len++] = 0; + packet[packet_len++] = 0; + break; + case MouseModelCOM::MouseSystems: packet[packet_len++] = 'H'; break; + default: + assert(false); // unimplemented + break; + } + + // send packet + xmit_idx = 0; + SetEventRX(); } void CSerialMouse::StartPacketData(const bool extended) { - if (model == MouseModelCOM::NoMouse) - return; - - if (model == MouseModelCOM::Microsoft || model == MouseModelCOM::Logitech || - model == MouseModelCOM::Wheel) { - // -- -- -- -- -- -- -- -- - // Byte 0: X 1 LB RB Y7 Y6 X7 X6 - // Byte 1: X 0 X5 X4 X3 X2 X1 X0 - // Byte 2: X 0 Y5 Y4 Y3 Y2 Y1 Y0 - // Byte 3: X 0 MB 00 W3 W2 W1 W0 - only sent if needed - - // Do NOT set bit 7. It confuses CTMOUSE.EXE (CuteMouse) serial - // support. Leaving it clear is the only way to make mouse - // movement possible. Microsoft Windows on the other hand - // doesn't care if bit 7 is set. - - const auto dx = ClampCounter(counter_x); - const auto dy = ClampCounter(counter_y); - const auto bt = has_3rd_button ? (buttons & 7) : (buttons & 3); - - packet[0] = static_cast<uint8_t>( - 0x40 | ((bt & 1) << 5) | ((bt & 2) << 3) | - (((dy >> 6) & 3) << 2) | ((dx >> 6) & 3)); - packet[1] = static_cast<uint8_t>(0x00 | (dx & 0x3f)); - packet[2] = static_cast<uint8_t>(0x00 | (dy & 0x3f)); - if (extended) { - uint8_t dw = std::clamp(counter_w, - static_cast<int8_t>(-0x10), - static_cast<int8_t>(0x0f)) & 0x0f; - packet[3] = static_cast<uint8_t>(((bt & 4) ? 0x20 : 0) | dw); - packet_len = 4; - } else { - packet_len = 3; - } - need_xmit_part2 = false; - - } else if (model == MouseModelCOM::MouseSystems) { - // -- -- -- -- -- -- -- -- - // Byte 0: 1 0 0 0 0 LB MB RB - // Byte 1: X7 X6 X5 X4 X3 X2 X1 X0 - // Byte 2: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 - - const auto bt = has_3rd_button ? ((~buttons) & 7) - : ((~buttons) & 3); - - packet[0] = static_cast<uint8_t>(0x80 | ((bt & 1) << 2) | + if (model == MouseModelCOM::NoMouse) + return; + + if (model == MouseModelCOM::Microsoft || + model == MouseModelCOM::Logitech || model == MouseModelCOM::Wheel) { + // -- -- -- -- -- -- -- -- + // Byte 0: X 1 LB RB Y7 Y6 X7 X6 + // Byte 1: X 0 X5 X4 X3 X2 X1 X0 + // Byte 2: X 0 Y5 Y4 Y3 Y2 Y1 Y0 + // Byte 3: X 0 MB 00 W3 W2 W1 W0 - only sent if needed + + // Do NOT set bit 7. It confuses CTMOUSE.EXE (CuteMouse) serial + // support. Leaving it clear is the only way to make mouse + // movement possible. Microsoft Windows on the other hand + // doesn't care if bit 7 is set. + + const auto dx = ClampCounter(counter_x); + const auto dy = ClampCounter(counter_y); + const auto bt = has_3rd_button ? (buttons & 7) : (buttons & 3); + + packet[0] = static_cast<uint8_t>( + 0x40 | ((bt & 1) << 5) | ((bt & 2) << 3) | + (((dy >> 6) & 3) << 2) | ((dx >> 6) & 3)); + packet[1] = static_cast<uint8_t>(0x00 | (dx & 0x3f)); + packet[2] = static_cast<uint8_t>(0x00 | (dy & 0x3f)); + if (extended) { + uint8_t dw = std::clamp(counter_w, + static_cast<int8_t>(-0x10), + static_cast<int8_t>(0x0f)) & + 0x0f; + packet[3] = static_cast<uint8_t>(((bt & 4) ? 0x20 : 0) | dw); + packet_len = 4; + } else { + packet_len = 3; + } + need_xmit_part2 = false; + + } else if (model == MouseModelCOM::MouseSystems) { + // -- -- -- -- -- -- -- -- + // Byte 0: 1 0 0 0 0 LB MB RB + // Byte 1: X7 X6 X5 X4 X3 X2 X1 X0 + // Byte 2: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + + const auto bt = has_3rd_button ? ((~buttons) & 7) + : ((~buttons) & 3); + + packet[0] = static_cast<uint8_t>(0x80 | ((bt & 1) << 2) | ((bt & 2) >> 1) | ((bt & 4) >> 1)); - packet[1] = ClampCounter(counter_x); - packet[2] = ClampCounter(-counter_y); - packet_len = 3; - need_xmit_part2 = true; // next part contains mouse movement since - // the start of the 1st part - - } else - assert(false); // unimplemented - - ClearCounters(); - - // send packet - xmit_idx = 0; - got_another_button = false; - got_another_move = false; - SetEventRX(); + packet[1] = ClampCounter(counter_x); + packet[2] = ClampCounter(-counter_y); + packet_len = 3; + need_xmit_part2 = true; // next part contains mouse movement + // since the start of the 1st part + + } else + assert(false); // unimplemented + + ClearCounters(); + + // send packet + xmit_idx = 0; + got_another_button = false; + got_another_move = false; + SetEventRX(); } void CSerialMouse::StartPacketPart2() { - // port settings are valid at this point + // port settings are valid at this point - if (model == MouseModelCOM::MouseSystems) { - // -- -- -- -- -- -- -- -- - // Byte 3: X7 X6 X5 X4 X3 X2 X1 X0 - // Byte 4: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + if (model == MouseModelCOM::MouseSystems) { + // -- -- -- -- -- -- -- -- + // Byte 3: X7 X6 X5 X4 X3 X2 X1 X0 + // Byte 4: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 - packet[0] = ClampCounter(counter_x); - packet[1] = ClampCounter(-counter_y); + packet[0] = ClampCounter(counter_x); + packet[1] = ClampCounter(-counter_y); - packet_len = 2; - need_xmit_part2 = false; - } else - assert(false); // unimplemented + packet_len = 2; + need_xmit_part2 = false; + } else + assert(false); // unimplemented - ClearCounters(); + ClearCounters(); - // send packet - xmit_idx = 0; - got_another_move = false; - SetEventRX(); + // send packet + xmit_idx = 0; + got_another_move = false; + SetEventRX(); } void CSerialMouse::SetEventTX() { - setEvent(SERIAL_TX_EVENT, bytetime * rate_coeff); + setEvent(SERIAL_TX_EVENT, bytetime * rate_coeff); } void CSerialMouse::SetEventRX() { - setEvent(SERIAL_RX_EVENT, bytetime * rate_coeff); + setEvent(SERIAL_RX_EVENT, bytetime * rate_coeff); } void CSerialMouse::SetEventTHR() { - setEvent(SERIAL_THR_EVENT, bytetime / 10); + setEvent(SERIAL_THR_EVENT, bytetime / 10); } uint8_t CSerialMouse::ClampCounter(const int32_t counter) const { - const auto tmp = std::clamp(counter, - static_cast<int32_t>(INT8_MIN), - static_cast<int32_t>(INT8_MAX)); - return static_cast<uint8_t>(tmp); + const auto tmp = std::clamp(counter, + static_cast<int32_t>(INT8_MIN), + static_cast<int32_t>(INT8_MAX)); + return static_cast<uint8_t>(tmp); } void CSerialMouse::handleUpperEvent(const uint16_t event_type) { - if (event_type == SERIAL_TX_EVENT) { - ByteTransmitted(); // tx timeout - } else if (event_type == SERIAL_THR_EVENT) { - ByteTransmitting(); - SetEventTX(); - } else if (event_type == SERIAL_RX_EVENT) { - // check for bytes to be sent to port - if (CSerial::CanReceiveByte()) { - if (send_ack) { - send_ack = false; - StartPacketId(); - } else if (xmit_idx < packet_len) { - CSerial::receiveByte(packet[xmit_idx++]); - if (xmit_idx >= packet_len && need_xmit_part2) - StartPacketPart2(); - else if (xmit_idx >= packet_len && - (got_another_move || got_another_button)) - StartPacketData(); - else - SetEventRX(); - } - } else - SetEventRX(); - } + if (event_type == SERIAL_TX_EVENT) { + ByteTransmitted(); // tx timeout + } else if (event_type == SERIAL_THR_EVENT) { + ByteTransmitting(); + SetEventTX(); + } else if (event_type == SERIAL_RX_EVENT) { + // check for bytes to be sent to port + if (CSerial::CanReceiveByte()) { + if (send_ack) { + send_ack = false; + StartPacketId(); + } else if (xmit_idx < packet_len) { + CSerial::receiveByte(packet[xmit_idx++]); + if (xmit_idx >= packet_len && need_xmit_part2) + StartPacketPart2(); + else if (xmit_idx >= packet_len && + (got_another_move || got_another_button)) + StartPacketData(); + else + SetEventRX(); + } + } else + SetEventRX(); + } } void CSerialMouse::updatePortConfig(const uint16_t divider, const uint8_t lcr) { - AbortPacket(); - - // We have to select between Microsoft-style protocol (this includes Logitech - // and wheel mice) and Mouse Systems Mouse protocol, or decide the port - // settings are not valid for any mouse - - port_byte_len = static_cast<uint8_t>((lcr & 0x3) + 5); - const auto one_stop_bit = !(lcr & 0x4); - const auto parity_id = static_cast<uint8_t>((lcr & 0x38) >> 3); - - // LOG_MSG("MOUSE (COM%d): lcr 0x%04x, divider %d, byte_len %d, stop %d, parity %d", - // port_num, lcr, divider, port_byte_len, one_stop_bit, parity_id); - - if (divider != divider_1200_baud) { - // We need 1200 bauds for a mouse; TODO:support faster serial mice, - // see https://man7.org/linux/man-pages/man4/mouse.4.html - SetModel(MouseModelCOM::NoMouse); - return; - } - - // Require 1 sop bit - if (!one_stop_bit) { - SetModel(MouseModelCOM::NoMouse); - return; - } - - // Require parity 'N' - if (parity_id == 1 || parity_id == 3 || parity_id == 5 || parity_id == 7) { - SetModel(MouseModelCOM::NoMouse); - return; - } - - // Check protocol compatibility with byte length - bool ok_microsoft = (param_model != MouseModelCOM::MouseSystems); - bool ok_mouse_systems = param_auto_msm || (param_model == MouseModelCOM::MouseSystems); - - // NOTE: It seems some software (at least The Settlers) tries to use - // Microsoft-style protocol by setting port to 8 bits per byte; - // we allow this if autodetection is not enabled, otherwise it is - // impossible to guess which protocol the guest software expects - - if (port_byte_len != 7 && !(!param_auto_msm && port_byte_len == 8)) - ok_microsoft = false; - if (port_byte_len != 8) - ok_mouse_systems = false; - - // Set the mouse protocol - if (ok_microsoft) - SetModel(param_model); - else if (ok_mouse_systems) - SetModel(MouseModelCOM::MouseSystems); - else - SetModel(MouseModelCOM::NoMouse); + AbortPacket(); + + // We have to select between Microsoft-style protocol (this includes + // Logitech and wheel mice) and Mouse Systems Mouse protocol, or decide + // the port settings are not valid for any mouse + + port_byte_len = static_cast<uint8_t>((lcr & 0x3) + 5); + const auto one_stop_bit = !(lcr & 0x4); + const auto parity_id = static_cast<uint8_t>((lcr & 0x38) >> 3); + + // LOG_MSG("MOUSE (COM%d): lcr 0x%04x, divider %d, byte_len %d, stop %d, + // parity %d", + // port_num, lcr, divider, port_byte_len, one_stop_bit, parity_id); + + if (divider != divider_1200_baud) { + // We need 1200 bauds for a mouse; TODO:support faster serial + // mice, see https://man7.org/linux/man-pages/man4/mouse.4.html + SetModel(MouseModelCOM::NoMouse); + return; + } + + // Require 1 sop bit + if (!one_stop_bit) { + SetModel(MouseModelCOM::NoMouse); + return; + } + + // Require parity 'N' + if (parity_id == 1 || parity_id == 3 || parity_id == 5 || parity_id == 7) { + SetModel(MouseModelCOM::NoMouse); + return; + } + + // Check protocol compatibility with byte length + bool ok_microsoft = (param_model != MouseModelCOM::MouseSystems); + bool ok_mouse_systems = param_auto_msm || + (param_model == MouseModelCOM::MouseSystems); + + // NOTE: It seems some software (at least The Settlers) tries to use + // Microsoft-style protocol by setting port to 8 bits per byte; + // we allow this if autodetection is not enabled, otherwise it is + // impossible to guess which protocol the guest software expects + + if (port_byte_len != 7 && !(!param_auto_msm && port_byte_len == 8)) + ok_microsoft = false; + if (port_byte_len != 8) + ok_mouse_systems = false; + + // Set the mouse protocol + if (ok_microsoft) + SetModel(param_model); + else if (ok_mouse_systems) + SetModel(MouseModelCOM::MouseSystems); + else + SetModel(MouseModelCOM::NoMouse); } void CSerialMouse::updateMSR() {} void CSerialMouse::transmitByte(const uint8_t, const bool first) { - if (first) - SetEventTHR(); - else - SetEventTX(); + if (first) + SetEventTHR(); + else + SetEventTX(); } void CSerialMouse::setBreak(const bool) {} void CSerialMouse::setRTSDTR(const bool rts, const bool dtr) { - if (rts && dtr && !getRTS() && !getDTR()) { - - // The serial mouse driver turns on the mouse by bringing up - // RTS and DTR. Not just for show, but to give the serial mouse - // a power source to work from. Likewise, drivers "reset" the - // mouse by bringing down the lines, then bringing them back - // up. And most drivers turn off the mouse when not in use by - // bringing them back down and leaving them that way. - // - // We're expected to transmit ASCII character 'M' when first - // initialized, so that the driver knows we're a Microsoft - // compatible serial mouse attached to a COM port. - - MouseReset(); - } - - setRTS(rts); - setDTR(dtr); + if (rts && dtr && !getRTS() && !getDTR()) { + // The serial mouse driver turns on the mouse by bringing up + // RTS and DTR. Not just for show, but to give the serial mouse + // a power source to work from. Likewise, drivers "reset" the + // mouse by bringing down the lines, then bringing them back + // up. And most drivers turn off the mouse when not in use by + // bringing them back down and leaving them that way. + // + // We're expected to transmit ASCII character 'M' when first + // initialized, so that the driver knows we're a Microsoft + // compatible serial mouse attached to a COM port. + + MouseReset(); + } + + setRTS(rts); + setDTR(dtr); } void CSerialMouse::setRTS(const bool val) { - if (val && !getRTS() && getDTR()) { - MouseReset(); - } + if (val && !getRTS() && getDTR()) { + MouseReset(); + } - setCTS(val); + setCTS(val); } void CSerialMouse::setDTR(const bool val) { - if (val && !getDTR() && getRTS()) { - MouseReset(); - } + if (val && !getDTR() && getRTS()) { + MouseReset(); + } - setDSR(val); - setRI(val); - setCD(val); + setDSR(val); + setRI(val); + setCD(val); } diff --git a/src/hardware/serialport/serialmouse.h b/src/hardware/serialport/serialmouse.h index 8bb29beb5..8c9337446 100644 --- a/src/hardware/serialport/serialmouse.h +++ b/src/hardware/serialport/serialmouse.h @@ -24,76 +24,74 @@ #include "../mouse/mouse_config.h" - class CSerialMouse : public CSerial { public: - CSerialMouse(const uint8_t id, CommandLine *cmd); - virtual ~CSerialMouse(); + CSerialMouse(const uint8_t id, CommandLine *cmd); + virtual ~CSerialMouse(); - void NotifyMoved(const float x_rel, const float y_rel); - void NotifyButton(const uint8_t new_buttons, - const uint8_t idx); // changed button, staring from 0 - void NotifyWheel(const int16_t w_rel); + void NotifyMoved(const float x_rel, const float y_rel); + void NotifyButton(const uint8_t new_buttons, + const uint8_t idx); // changed button, staring from 0 + void NotifyWheel(const int16_t w_rel); - void BoostRate(const uint16_t rate_hz); // 0 = standard rate + void BoostRate(const uint16_t rate_hz); // 0 = standard rate - void setRTSDTR(const bool rts, const bool dtr) override; - void setRTS(const bool val) override; - void setDTR(const bool val) override; + void setRTSDTR(const bool rts, const bool dtr) override; + void setRTS(const bool val) override; + void setDTR(const bool val) override; - void updatePortConfig(const uint16_t divider, const uint8_t lcr) override; - void updateMSR() override; - void transmitByte(const uint8_t val, const bool first) override; - void setBreak(const bool value) override; - void handleUpperEvent(const uint16_t event_type) override; + void updatePortConfig(const uint16_t divider, const uint8_t lcr) override; + void updateMSR() override; + void transmitByte(const uint8_t val, const bool first) override; + void setBreak(const bool value) override; + void handleUpperEvent(const uint16_t event_type) override; private: - - void HandleDeprecatedOptions(CommandLine *cmd); - void SetModel(const MouseModelCOM new_type); - void AbortPacket(); - void ClearCounters(); - void MouseReset(); - void StartPacketId(); - void StartPacketData(const bool extended = false); - void StartPacketPart2(); - void SetEventTX(); - void SetEventRX(); - void SetEventTHR(); - uint8_t ClampCounter(const int32_t counter) const; - - const uint8_t port_id = 0; - const uint16_t port_num = 0; // for logging purposes - - // Mouse model as specified in the parameter - MouseModelCOM param_model = MouseModelCOM::NoMouse; - // If true = autoswitch between param_model and Mouse Systems mouse - bool param_auto_msm = false; - - MouseModelCOM model = MouseModelCOM::NoMouse; // currently emulated model - - uint8_t port_byte_len = 0; // how many bits the port transmits in a byte - - bool has_3rd_button = false; - bool has_wheel = false; - float rate_coeff = 1.0f; // coefficient for boosted sampling rate - bool send_ack = true; - uint8_t packet[6] = {}; - uint8_t packet_len = 0; - uint8_t xmit_idx = UINT8_MAX; // index of byte to send, if >= packet_len - // it means transmission ended - bool need_xmit_part2 = false; // true = packet has a second part, which could - // not be evaluated yet - bool got_another_move = false; // true = while transmitting a packet we - // received mouse move event - bool got_another_button = false; // true = while transmitting a packet we - // received mouse button event - uint8_t buttons = 0; // bit 0 = left, bit 1 = right, bit 2 = middle - float delta_x = 0.0f; // accumulated movements not yet reported - float delta_y = 0.0f; - int8_t counter_x = 0; // position counters, as visible on guest size - int8_t counter_y = 0; - int8_t counter_w = 0; + void HandleDeprecatedOptions(CommandLine *cmd); + void SetModel(const MouseModelCOM new_type); + void AbortPacket(); + void ClearCounters(); + void MouseReset(); + void StartPacketId(); + void StartPacketData(const bool extended = false); + void StartPacketPart2(); + void SetEventTX(); + void SetEventRX(); + void SetEventTHR(); + uint8_t ClampCounter(const int32_t counter) const; + + const uint8_t port_id = 0; + const uint16_t port_num = 0; // for logging purposes + + // Mouse model as specified in the parameter + MouseModelCOM param_model = MouseModelCOM::NoMouse; + // If true = autoswitch between param_model and Mouse Systems mouse + bool param_auto_msm = false; + + MouseModelCOM model = MouseModelCOM::NoMouse; // currently emulated model + + uint8_t port_byte_len = 0; // how many bits the port transmits in a byte + + bool has_3rd_button = false; + bool has_wheel = false; + float rate_coeff = 1.0f; // coefficient for boosted sampling rate + bool send_ack = true; + uint8_t packet[6] = {}; + uint8_t packet_len = 0; + uint8_t xmit_idx = UINT8_MAX; // index of byte to send, if >= packet_len + // it means transmission ended + bool need_xmit_part2 = false; // true = packet has a second part, which + // could not be evaluated yet + bool got_another_move = false; // true = while transmitting a packet we + // received mouse move event + bool got_another_button = false; // true = while transmitting a packet + // we received mouse button event + uint8_t buttons = 0; // bit 0 = left, bit 1 = right, bit 2 = middle + float delta_x = 0.0f; // accumulated movements not yet reported + float delta_y = 0.0f; + int8_t counter_x = 0; // position counters, as visible on guest size + int8_t counter_y = 0; + int8_t counter_w = 0; }; #endif // DOSBOX_SERIALMOUSE_H |