diff options
author | FeralChild64 <unknown> | 2022-11-04 13:11:11 +0300 |
---|---|---|
committer | kcgen <1557255+kcgen@users.noreply.github.com> | 2022-11-06 17:23:52 +0300 |
commit | 838774fa2610a76a020822929e2b2f4a9c3836ec (patch) | |
tree | 9dd01a1b1345b74e69c7caaa0b06e092ddebe144 | |
parent | 58351c80f18277ddfe1fca3928bcf10198dbb306 (diff) |
Improve interactive mouse mapping
-rw-r--r-- | include/mouse.h | 15 | ||||
-rw-r--r-- | src/dos/program_mousectl.cpp | 13 | ||||
-rw-r--r-- | src/hardware/mouse/mouse.cpp | 58 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_config.cpp | 9 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_interfaces.cpp | 47 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_interfaces.h | 13 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_manymouse.cpp | 18 | ||||
-rw-r--r-- | src/hardware/mouse/mouse_manymouse.h | 5 |
8 files changed, 116 insertions, 62 deletions
diff --git a/include/mouse.h b/include/mouse.h index 11df6328c..5b48abf07 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -142,7 +142,7 @@ class MouseInterfaceInfoEntry final { public: bool IsEmulated() const; bool IsMapped() const; - bool IsMapped(const uint8_t device_idx) const; + bool IsMapped(const uint8_t physical_device_idx) const; bool IsMappedDeviceDisconnected() const; MouseInterfaceId GetInterfaceId() const; @@ -196,11 +196,14 @@ public: 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! + // This one is ONLY for interactive mapping in MOUSECTL.COM! + bool MapInteractively(const MouseInterfaceId interface_id, + uint8_t &physical_device_idx); - bool Map(const MouseInterfaceId interface_id, const uint8_t device_idx); - bool Map(const MouseInterfaceId interface_id, const std::regex ®ex); + bool Map(const MouseInterfaceId interface_id, + const uint8_t physical_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); @@ -228,6 +231,8 @@ public: private: MouseControlAPI(const MouseControlAPI &) = delete; MouseControlAPI &operator=(const MouseControlAPI &) = delete; + + bool was_interactive_mapping_started = false; }; #endif // DOSBOX_MOUSE_H diff --git a/src/dos/program_mousectl.cpp b/src/dos/program_mousectl.cpp index efe47c87e..92759cc2c 100644 --- a/src/dos/program_mousectl.cpp +++ b/src/dos/program_mousectl.cpp @@ -377,8 +377,8 @@ bool MOUSECTL::CmdMap() WriteOut(convert_ansi_markup("[color=cyan]%-4s[reset] ?").c_str(), MouseControlAPI::GetInterfaceNameStr(interface_id).c_str()); - uint8_t device_id = 0; - if (!mouse_config_api.ProbeForMapping(device_id)) { + uint8_t device_idx = 0; + if (!mouse_config_api.MapInteractively(interface_id, device_idx)) { mouse_config_api.UnMap(empty); WriteOut("\b"); WriteOut(MSG_Get("PROGRAM_MOUSECTL_MAP_CANCEL")); @@ -387,9 +387,8 @@ bool MOUSECTL::CmdMap() } WriteOut("\b"); - WriteOut(info_physical[device_id].GetDeviceName().c_str()); + WriteOut(info_physical[device_idx].GetDeviceName().c_str()); WriteOut("\n"); - mouse_config_api.Map(interface_id, device_id); } FinalizeMapping(); @@ -534,7 +533,7 @@ void MOUSECTL::AddMessages() " -reset restores all mouse settings from the configuration file\n" "\n" "Notes:\n" - " - if sensitivity or rate is omitted, it is reset to default value\n" + " If sensitivity or rate is omitted, it is reset to default value.\n" "\n" "Examples:\n" " [color=green]mousectl[reset] [color=white]DOS[reset] [color=white]COM1[reset] -map ; asks user to select mice for a two player game"); @@ -586,6 +585,6 @@ void MOUSECTL::AddMessages() "mouse interfaces."); MSG_Add("PROGRAM_MOUSECTL_MAP_CANCEL", "(mapping cancelled)"); MSG_Add("PROGRAM_MOUSECTL_MAP_HINT", - "Mouse captured. Seamless mouse integration is always disabled while mapping is\n" - "in effect and mapped mice always receive raw input events."); + "Seamless mouse integration is always disabled while mapping is in effect\n" + "and mapped mice always receive raw input events."); } diff --git a/src/hardware/mouse/mouse.cpp b/src/hardware/mouse/mouse.cpp index ee249f2ba..0b270439d 100644 --- a/src/hardware/mouse/mouse.cpp +++ b/src/hardware/mouse/mouse.cpp @@ -51,13 +51,14 @@ static struct { uint32_t clip_x = 0; // clipping = size of black border (one side) uint32_t clip_y = 0; - uint32_t cursor_x_abs = 0; // absolute position from start of drawing area + uint32_t cursor_x_abs = 0; // absolute position from start of drawing area uint32_t cursor_y_abs = 0; - bool cursor_is_outside = true; // if mouse cursor is outside of drawing area + bool cursor_is_outside = false; // if mouse cursor is outside of drawing area - bool has_focus = false; // if our window has focus - bool gui_has_taken_over = false; // if a GUI requested to take over the mouse - bool capture_was_requested = false; // if user requested mouse to be captured + bool has_focus = false; // if our window has focus + bool gui_has_taken_over = false; // if a GUI requested to take over the mouse + bool is_mapping_in_progress = false; // if interactive mapping is running + bool capture_was_requested = false; // if user requested mouse to be captured bool is_captured = false; // if GFX was requested to capture mouse bool is_visible = false; // if GFX was requested to make cursor visible @@ -171,7 +172,8 @@ static void update_state() // updates whole 'state' structure, except cursor vis // Due to ManyMouse API limitation, we are unable to support seamless // integration if mapping is in effect - const bool is_mapping = manymouse.IsMappingInEffect(); + const bool is_mapping = state.is_mapping_in_progress || + manymouse.IsMappingInEffect(); if (state.is_seamless && is_mapping) { state.is_seamless = false; static bool already_warned = false; @@ -264,6 +266,12 @@ static void update_state() // updates whole 'state' structure, except cursor vis state.is_captured && mouse_config.middle_release; + // Note: it would make sense to block capture/release on any mouse click + // while 'state.is_mapping_in_progress' - unfortunately this would lead + // to a race condition between events from SDL and ManyMouse at the end + // of mapping process, leading to ununiform (random) user experience. + // TODO: if SDL gets expanded to include ManyMouse, change the behavior! + // Select hint to be displayed on a title bar if (state.is_fullscreen || state.gui_has_taken_over || !state.has_focus) state.hint_id = MouseHint::None; @@ -318,6 +326,14 @@ bool MOUSE_IsCaptured() return state.is_captured; } +bool MOUSE_IsProbeForMappingAllowed() +{ + // Conditions to be met to accept mouse clicks for interactive mapping: + // - we have a focus + // - no GUI has taken over the mouse + return state.has_focus && !state.gui_has_taken_over; +} + // *************************************************************************** // Interrupt 74 implementation // *************************************************************************** @@ -660,6 +676,8 @@ MouseControlAPI::MouseControlAPI() MouseControlAPI::~MouseControlAPI() { manymouse.StopConfigAPI(); + if (was_interactive_mapping_started) + state.is_mapping_in_progress = false; MOUSE_UpdateGFX(); } @@ -717,16 +735,36 @@ bool MouseControlAPI::PatternToRegex(const std::string &pattern, std::regex ® return true; } -bool MouseControlAPI::ProbeForMapping(uint8_t &device_id) +bool MouseControlAPI::MapInteractively(const MouseInterfaceId interface_id, + uint8_t &physical_device_idx) { if (IsNoMouseMode()) return false; + if (!was_interactive_mapping_started) { + // Interactive mapping was started + assert(!state.is_mapping_in_progress); + // Capture the mouse, otherwise it might be confusing + // for the user when it gets captured after he clicks + // simply to select the mouse + state.capture_was_requested = true; + // Tell the other code that mapping is in progress, + // so that it can disable seamless mouse integration, + // and possibly apply other changes to mouse behavior + state.is_mapping_in_progress = true; + was_interactive_mapping_started = true; + MOUSE_UpdateGFX(); + } + manymouse.RescanIfSafe(); - return manymouse.ProbeForMapping(device_id); + if (!manymouse.ProbeForMapping(physical_device_idx)) + return false; + + return Map(interface_id, physical_device_idx); } -bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const uint8_t device_idx) +bool MouseControlAPI::Map(const MouseInterfaceId interface_id,\ + const uint8_t physical_device_idx) { if (IsNoMouseMode()) return false; @@ -735,7 +773,7 @@ bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const uint8_t dev if (!mouse_interface) return false; - return mouse_interface->ConfigMap(device_idx); + return mouse_interface->ConfigMap(physical_device_idx); } bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const std::regex ®ex) diff --git a/src/hardware/mouse/mouse_config.cpp b/src/hardware/mouse/mouse_config.cpp index 6474e54f0..e77ca6b80 100644 --- a/src/hardware/mouse/mouse_config.cpp +++ b/src/hardware/mouse/mouse_config.cpp @@ -340,9 +340,12 @@ static void config_init(Section_prop &secprop) 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" + " 3button: 3 buttons, Logitech mouse,\n" + " mostly compatible with Microsoft mouse.\n" + " wheel: 3 buttons + wheel,\n" + " mostly compatible with Microsoft mouse.\n" + " msm: 3 buttons, Mouse Systems mouse,\n" + " 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" diff --git a/src/hardware/mouse/mouse_interfaces.cpp b/src/hardware/mouse/mouse_interfaces.cpp index 4015256c2..4c5a6b257 100644 --- a/src/hardware/mouse/mouse_interfaces.cpp +++ b/src/hardware/mouse/mouse_interfaces.cpp @@ -46,8 +46,8 @@ const MouseInterface &MouseInterfaceInfoEntry::Interface() const const MousePhysical &MouseInterfaceInfoEntry::MappedPhysical() const { - const auto mapped_idx = Interface().GetMappedDeviceIdx(); - return ManyMouseGlue::GetInstance().physical_devices[mapped_idx]; + const auto mapped_physical_idx = Interface().GetMappedDeviceIdx(); + return ManyMouseGlue::GetInstance().physical_devices[mapped_physical_idx]; } bool MouseInterfaceInfoEntry::IsEmulated() const @@ -60,9 +60,9 @@ bool MouseInterfaceInfoEntry::IsMapped() const return Interface().IsMapped(); } -bool MouseInterfaceInfoEntry::IsMapped(const uint8_t device_idx) const +bool MouseInterfaceInfoEntry::IsMapped(const uint8_t physical_device_idx) const { - return Interface().IsMapped(device_idx); + return Interface().IsMapped(physical_device_idx); } bool MouseInterfaceInfoEntry::IsMappedDeviceDisconnected() const @@ -326,12 +326,12 @@ uint8_t MouseInterface::GetInterfaceIdx() const bool MouseInterface::IsMapped() const { - return mapped_idx < mouse_info.physical.size(); + return mapped_physical_idx < mouse_info.physical.size(); } -bool MouseInterface::IsMapped(const uint8_t device_idx) const +bool MouseInterface::IsMapped(const uint8_t physical_device_idx) const { - return mapped_idx == device_idx; + return mapped_physical_idx == physical_device_idx; } bool MouseInterface::IsEmulated() const @@ -367,7 +367,7 @@ MouseMapStatus MouseInterface::GetMapStatus() const uint8_t MouseInterface::GetMappedDeviceIdx() const { - return mapped_idx; + return mapped_physical_idx; } int16_t MouseInterface::GetSensitivityX() const @@ -395,44 +395,47 @@ void MouseInterface::NotifyBooting() {} void MouseInterface::NotifyDisconnect() { - SetMapStatus(MouseMapStatus::Disconnected, mapped_idx); + SetMapStatus(MouseMapStatus::Disconnected, mapped_physical_idx); } -void MouseInterface::SetMapStatus(const MouseMapStatus status, const uint8_t device_idx) +void MouseInterface::SetMapStatus(const MouseMapStatus status, + const uint8_t physical_device_idx) { - MouseMapStatus new_map_status = status; - uint8_t new_mapped_idx = device_idx; + MouseMapStatus new_map_status = status; + uint8_t new_mapped_physical_idx = physical_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_mapped_physical_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()) + mouse_info.physical[new_mapped_physical_idx].IsDeviceDisconnected()) new_map_status = MouseMapStatus::Disconnected; - // Perform necessary updates for mapping change - if (map_status != new_map_status || mapped_idx != new_mapped_idx) + // Perform necessary updates after mapping change + if (map_status != new_map_status || + mapped_physical_idx != new_mapped_physical_idx) ResetButtons(); if (map_status != new_map_status) UpdateInputType(); - if (mapped_idx != new_mapped_idx) - ManyMouseGlue::GetInstance().Map(new_mapped_idx, interface_id); + if (mapped_physical_idx != new_mapped_physical_idx) + ManyMouseGlue::GetInstance().Map(new_mapped_physical_idx, + interface_id); // Apply new mapping - mapped_idx = new_mapped_idx; - map_status = new_map_status; + mapped_physical_idx = new_mapped_physical_idx; + map_status = new_map_status; } -bool MouseInterface::ConfigMap(const uint8_t device_idx) +bool MouseInterface::ConfigMap(const uint8_t physical_device_idx) { if (!IsEmulated()) return false; - SetMapStatus(MouseMapStatus::Mapped, device_idx); + SetMapStatus(MouseMapStatus::Mapped, physical_device_idx); return true; } diff --git a/src/hardware/mouse/mouse_interfaces.h b/src/hardware/mouse/mouse_interfaces.h index 063ce34f6..49222d218 100644 --- a/src/hardware/mouse/mouse_interfaces.h +++ b/src/hardware/mouse/mouse_interfaces.h @@ -33,8 +33,9 @@ void MOUSE_NotifyDisconnect(const MouseInterfaceId interface_id); void MOUSE_NotifyFakePS2(); // fake PS/2 event, for VMware protocol support void MOUSE_NotifyResetDOS(); -bool MOUSE_IsCaptured(); void MOUSE_UpdateGFX(); +bool MOUSE_IsCaptured(); +bool MOUSE_IsProbeForMappingAllowed(); // *************************************************************************** // DOS mouse driver @@ -140,7 +141,7 @@ public: void NotifyDisconnect(); bool IsMapped() const; - bool IsMapped(const uint8_t device_idx) const; + bool IsMapped(const uint8_t physical_device_idx) const; bool IsEmulated() const; bool IsUsingEvents() const; bool IsUsingHostPointer() const; @@ -153,7 +154,7 @@ public: uint16_t GetMinRate() const; uint16_t GetRate() const; - bool ConfigMap(const uint8_t device_idx); + bool ConfigMap(const uint8_t physical_device_idx); void ConfigUnMap(); void ConfigOnOff(const bool enable); @@ -183,7 +184,7 @@ protected: uint8_t GetInterfaceIdx() const; void SetMapStatus(const MouseMapStatus status, - const uint8_t device_idx = idx_host_pointer); + const uint8_t physical_device_idx = idx_host_pointer); virtual void UpdateSensitivity(); virtual void UpdateMinRate(); @@ -212,8 +213,8 @@ protected: private: 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_physical_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 diff --git a/src/hardware/mouse/mouse_manymouse.cpp b/src/hardware/mouse/mouse_manymouse.cpp index a2cd51023..34b9aa472 100644 --- a/src/hardware/mouse/mouse_manymouse.cpp +++ b/src/hardware/mouse/mouse_manymouse.cpp @@ -227,7 +227,7 @@ void ManyMouseGlue::RescanIfSafe() InitIfNeeded(); } -bool ManyMouseGlue::ProbeForMapping(uint8_t &device_id) +bool ManyMouseGlue::ProbeForMapping(uint8_t &physical_device_idx) { // Do not even try if NoMouse is configured if (mouse_config.capture == MouseCapture::NoMouse) @@ -266,7 +266,10 @@ bool ManyMouseGlue::ProbeForMapping(uint8_t &device_id) // Wait for mouse button press if (event.type != MANYMOUSE_EVENT_BUTTON || !event.value) continue; - device_id = static_cast<uint8_t>(event.device); + // Drop button events if we have no focus, etc. + if (!MOUSE_IsProbeForMappingAllowed()) + continue; + physical_device_idx = static_cast<uint8_t>(event.device); if (event.item >= 1) break; // user cancelled the interactive mouse mapping @@ -274,13 +277,13 @@ bool ManyMouseGlue::ProbeForMapping(uint8_t &device_id) // Do not accept already mapped devices bool already_mapped = false; for (const auto &interface : mouse_interfaces) - if (interface->IsMapped(device_id)) + if (interface->IsMapped(physical_device_idx)) already_mapped = true; if (already_mapped) continue; // Mouse probed successfully - device_id = static_cast<uint8_t>(event.device); + physical_device_idx = static_cast<uint8_t>(event.device); success = true; break; } @@ -312,16 +315,17 @@ uint8_t ManyMouseGlue::GetIdx(const std::regex ®ex) return max_mice + 1; // 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_device_idx, + const MouseInterfaceId interface_id) { assert(interface_id != MouseInterfaceId::None); - if (physical_idx >= physical_devices.size()) { + if (physical_device_idx >= physical_devices.size()) { UnMap(interface_id); return; } - auto &physical_device = physical_devices[physical_idx]; + auto &physical_device = physical_devices[physical_device_idx]; if (interface_id == physical_device.GetMappedInterfaceId()) return; // nothing to update physical_device.mapped_id = interface_id; diff --git a/src/hardware/mouse/mouse_manymouse.h b/src/hardware/mouse/mouse_manymouse.h index 66d6edb94..69753e145 100644 --- a/src/hardware/mouse/mouse_manymouse.h +++ b/src/hardware/mouse/mouse_manymouse.h @@ -56,10 +56,11 @@ public: void StartConfigAPI(); void StopConfigAPI(); - bool ProbeForMapping(uint8_t &device_id); + bool ProbeForMapping(uint8_t &physical_device_idx); uint8_t GetIdx(const std::regex ®ex); - void Map(const uint8_t physical_idx, const MouseInterfaceId interface_id); + void Map(const uint8_t physical_device_idx, + const MouseInterfaceId interface_id); bool IsMappingInEffect() const; |