diff options
| author | 2026-04-18 04:15:04 +1000 | |
|---|---|---|
| committer | 2026-04-18 04:15:04 +1000 | |
| commit | 46a890d2854838ce7e6c2bf177c54904b62720a8 (patch) | |
| tree | 7e107b81246d67558dedf07ef07945c96a4362ed /src/osd | |
| parent | 8077642755f1d0666cdcffaecbde195693a75cf6 (diff) | |
-input/input_win32.cpp: Made Win32 lightgun module usable with pen/touch.
* windows/window.cpp: Forward pointer events to input modules.
-igs/igs011.cpp: Bad graphics on dbc title screen is an original game
bug.
Diffstat (limited to 'src/osd')
| -rw-r--r-- | src/osd/modules/input/input_common.h | 12 | ||||
| -rw-r--r-- | src/osd/modules/input/input_rawinput.cpp | 2 | ||||
| -rw-r--r-- | src/osd/modules/input/input_sdl.cpp | 4 | ||||
| -rw-r--r-- | src/osd/modules/input/input_sdl3.cpp | 4 | ||||
| -rw-r--r-- | src/osd/modules/input/input_win32.cpp | 171 | ||||
| -rw-r--r-- | src/osd/modules/input/input_x11.cpp | 2 | ||||
| -rw-r--r-- | src/osd/windows/window.cpp | 96 | ||||
| -rw-r--r-- | src/osd/windows/winmain.h | 17 |
8 files changed, 268 insertions, 40 deletions
diff --git a/src/osd/modules/input/input_common.h b/src/osd/modules/input/input_common.h index 73ddd2bf936..406e0464c16 100644 --- a/src/osd/modules/input/input_common.h +++ b/src/osd/modules/input/input_common.h @@ -27,6 +27,7 @@ #include <memory> #include <mutex> #include <queue> +#include <span> //============================================================ @@ -106,11 +107,16 @@ public: { } - void queue_events(TEvent const *events, int count) + void queue_event(TEvent const &event) + { + queue_events(std::span<TEvent const>(&event, 1)); + } + + void queue_events(std::span<TEvent const> events) { std::lock_guard<std::mutex> scope_lock(m_device_lock); - for (int i = 0; i < count; i++) - m_event_queue.push(events[i]); + for (TEvent const &event : events) + m_event_queue.push(event); // If we've gone over the size, remove old events from the queue while (m_event_queue.size() > DEFAULT_EVENT_QUEUE_SIZE) diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp index 5b6813dcd59..88c712e6469 100644 --- a/src/osd/modules/input/input_rawinput.cpp +++ b/src/osd/modules/input/input_rawinput.cpp @@ -798,7 +798,7 @@ protected: if (devicelist().end() == target_device) return false; - (*target_device)->queue_events(input, 1); + (*target_device)->queue_event(*input); return true; } } diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp index 2c1772ae42b..3971bc30c5d 100644 --- a/src/osd/modules/input/input_sdl.cpp +++ b/src/osd/modules/input/input_sdl.cpp @@ -2158,7 +2158,7 @@ protected: { // dispatch event to every device by default this->devicelist().for_each_device( - [&event] (auto &device) { device.queue_events(&event, 1); }); + [&event] (auto &device) { device.queue_event(event); }); } }; @@ -2504,7 +2504,7 @@ protected: // if we find a matching joystick, dispatch the event to the joystick if (target_device) - target_device->queue_events(&event, 1); + target_device->queue_event(event); } device_info *find_reconnect_match(SDL_JoystickGUID const &guid, char const *serial) diff --git a/src/osd/modules/input/input_sdl3.cpp b/src/osd/modules/input/input_sdl3.cpp index e344f9304cb..3ed0934067a 100644 --- a/src/osd/modules/input/input_sdl3.cpp +++ b/src/osd/modules/input/input_sdl3.cpp @@ -2182,7 +2182,7 @@ protected: { // dispatch event to every device by default this->devicelist().for_each_device( - [&event] (auto &device) { device.queue_events(&event, 1); }); + [&event] (auto &device) { device.queue_event(event); }); } }; @@ -2569,7 +2569,7 @@ protected: // if we find a matching joystick, dispatch the event to the joystick if (target_device) - target_device->queue_events(&event, 1); + target_device->queue_event(event); } device_info *find_reconnect_match(SDL_GUID const &guid, char const *serial) diff --git a/src/osd/modules/input/input_win32.cpp b/src/osd/modules/input/input_win32.cpp index f346913c225..58ee805549f 100644 --- a/src/osd/modules/input/input_win32.cpp +++ b/src/osd/modules/input/input_win32.cpp @@ -22,6 +22,13 @@ #include "strconv.h" +#include <algorithm> +#include <optional> +#include <string> +#include <string_view> +#include <utility> +#include <variant> + // standard windows headers #include <tchar.h> @@ -115,7 +122,7 @@ public: devicelist().for_each_device( [args = reinterpret_cast<KeyPressEventArgs const *>(eventdata)] (auto &device) { - device.queue_events(args, 1); + device.queue_event(*args); }); return false; // we still want text input events to be generated @@ -207,7 +214,7 @@ public: &m_mouse.lH); // populate the buttons - for (int butnum = 0; butnum < 5; butnum++) + for (int butnum = 0; butnum < std::size(m_mouse.rgbButtons); butnum++) { device.add_item( default_button_name(butnum), @@ -231,7 +238,7 @@ protected: { // set the button state assert(!(args.pressed & args.released)); - for (unsigned i = 0; 5 > i; ++i) + for (unsigned i = 0; std::size(m_mouse.rgbButtons) > i; ++i) { if (BIT(args.pressed, i)) m_mouse.rgbButtons[i] = 0x80; @@ -283,7 +290,7 @@ public: { auto const *const args = reinterpret_cast<MouseUpdateEventArgs const *>(eventdata); devicelist().for_each_device( - [args] (auto &device) { device.queue_events(args, 1); }); + [args] (auto &device) { device.queue_event(*args); }); return true; } } @@ -297,7 +304,7 @@ public: // win32_lightgun_device_base //============================================================ -class win32_lightgun_device_base : public event_based_device<MouseUpdateEventArgs> +class win32_lightgun_device_base : public event_based_device<std::variant<MouseUpdateEventArgs, PointerUpdateEventArgs> > { public: virtual void reset() override @@ -359,7 +366,8 @@ public: input_module &module) : win32_lightgun_device_base(std::move(name), std::move(id), module), m_vscroll(0), - m_hscroll(0) + m_hscroll(0), + m_active_pointer(0) { } @@ -370,24 +378,27 @@ public: int32_t xpos = 0, ypos = 0; // get the cursor position and transform into final results - POINT mousepos; - GetCursorPos(&mousepos); - if (!osd_common_t::window_list().empty()) + if (m_active_pointer == 0) { - // get the position relative to the window - HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); - RECT client_rect; - GetClientRect(hwnd, &client_rect); - ScreenToClient(hwnd, &mousepos); + POINT mousepos; + GetCursorPos(&mousepos); + if (!osd_common_t::window_list().empty()) + { + // get the position relative to the window + HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); + RECT client_rect; + GetClientRect(hwnd, &client_rect); + ScreenToClient(hwnd, &mousepos); - // convert to absolute coordinates - xpos = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right); - ypos = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom); - } + // convert to absolute coordinates + xpos = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right); + ypos = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom); + } - // update the X/Y positions - m_mouse.lX = xpos; - m_mouse.lY = ypos; + // update the X/Y positions + m_mouse.lX = xpos; + m_mouse.lY = ypos; + } // update the scroll axes if appropriate if (relative_reset) @@ -399,7 +410,7 @@ public: virtual void configure(input_device &device) override { - do_configure(device, 5); + do_configure(device, std::size(m_mouse.rgbButtons)); // add scroll axes device.add_item( @@ -423,11 +434,32 @@ public: } protected: - virtual void process_event(MouseUpdateEventArgs const &args) override + virtual void process_event(std::variant<MouseUpdateEventArgs, PointerUpdateEventArgs> const &args) override { - // In non-shared axis mode, just update the button state + std::visit([this] (auto &&a) { process(a); }, args); + } + +private: + void process(MouseUpdateEventArgs const &args) + { + // work out if this should become the active pointer + if (!m_active_pointer) + { + m_active_pointer = 0; + } + else if (*m_active_pointer != 0) + { + if (!args.pressed) + return; + else if (!any_pressed()) + m_active_pointer = 0; + else + return; + } + + // in non-shared axis mode, just update the button state assert(!(args.pressed & args.released)); - for (unsigned i = 0; 5 > i; ++i) + for (unsigned i = 0; std::size(m_mouse.rgbButtons) > i; ++i) { if (BIT(args.pressed, i)) m_mouse.rgbButtons[i] = 0x80; @@ -440,8 +472,70 @@ protected: m_hscroll += args.hdelta; } -private: - long m_vscroll, m_hscroll; + void process(PointerUpdateEventArgs const &args) + { + if (args.lost) + { + // release all the buttons if the active pointer was lost + if (m_active_pointer == args.id) + { + m_active_pointer = std::nullopt; + m_mouse.lX = 0; + m_mouse.lY = 0; + std::fill(std::begin(m_mouse.rgbButtons), std::end(m_mouse.rgbButtons), 0); + } + } + else + { + // work out if this should become the active pointer + if (!m_active_pointer) + { + m_active_pointer = args.id; + } + else if (*m_active_pointer != args.id) + { + bool const any_down = args.buttons[0] || args.buttons[1] || args.buttons[2] || args.buttons[3] || args.buttons[4]; + + if (!any_down) + return; + else if (!any_pressed()) + m_active_pointer = args.id; + else + return; + } + + // get the position relative to the window + POINT pos; + pos.x = args.xpos; + pos.y = args.ypos; + ScreenToClient(reinterpret_cast<HWND>(args.window), &pos); + + // convert to absolute coordinates + RECT client_rect; + GetClientRect(reinterpret_cast<HWND>(args.window), &client_rect); + m_mouse.lX = normalize_absolute_axis(pos.x, client_rect.left, client_rect.right); + m_mouse.lY = normalize_absolute_axis(pos.y, client_rect.top, client_rect.bottom); + + // set button state + m_mouse.rgbButtons[0] = args.buttons[0] ? 0x80 : 0x00; + m_mouse.rgbButtons[1] = args.buttons[1] ? 0x80 : 0x00; + m_mouse.rgbButtons[2] = args.buttons[2] ? 0x80 : 0x00; + m_mouse.rgbButtons[3] = args.buttons[3] ? 0x80 : 0x00; + m_mouse.rgbButtons[4] = args.buttons[4] ? 0x80 : 0x00; + } + } + + bool any_pressed() const + { + auto const pressed = std::find_if( + std::begin(m_mouse.rgbButtons), + std::end(m_mouse.rgbButtons), + [] (BYTE b) { return b != 0; }); + return pressed != std::end(m_mouse.rgbButtons); + } + + long m_vscroll, m_hscroll; + std::optional<unsigned> m_active_pointer; }; @@ -468,7 +562,13 @@ public: } protected: - virtual void process_event(MouseUpdateEventArgs const &args) override + virtual void process_event(std::variant<MouseUpdateEventArgs, PointerUpdateEventArgs> const &args) override + { + std::visit([this] (auto &&a) { process(a); }, args); + } + +private: + void process(MouseUpdateEventArgs const &args) { // We only handle the first four buttons in shared axis mode assert(!(args.pressed & args.released)); @@ -501,7 +601,11 @@ protected: } } -private: + void process(PointerUpdateEventArgs const &args) + { + // Only consider primary system pointer in dual lightgun mode + } + int const m_gun_index; }; @@ -545,7 +649,14 @@ public: { auto const *const args = reinterpret_cast<MouseUpdateEventArgs const *>(eventdata); devicelist().for_each_device( - [args] (auto &device) { device.queue_events(args, 1); }); + [args] (auto &device) { device.queue_event(*args); }); + return true; + } + else if (eventid == INPUT_EVENT_POINTER_UPDATE) + { + auto const *const args = reinterpret_cast<PointerUpdateEventArgs const *>(eventdata); + devicelist().for_each_device( + [args] (auto &device) { device.queue_event(*args); }); return true; } } diff --git a/src/osd/modules/input/input_x11.cpp b/src/osd/modules/input/input_x11.cpp index 8fcaf0fa461..82318f22005 100644 --- a/src/osd/modules/input/input_x11.cpp +++ b/src/osd/modules/input/input_x11.cpp @@ -709,7 +709,7 @@ public: // If we find a matching lightgun, dispatch the event to the lightgun if (target_device != devicelist().end()) - (*target_device)->queue_events(&xevent, 1); + (*target_device)->queue_event(xevent); } }; diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp index 9482ed8c49d..98c9ac531d7 100644 --- a/src/osd/windows/window.cpp +++ b/src/osd/windows/window.cpp @@ -372,6 +372,86 @@ inline BOOL handle_mouse_wheel(windows_osd_interface &osd, int v, int h, LPARAM return handled && !osd.options().lightgun() && !osd.options().mouse(); } +inline BOOL handle_pointer_update(windows_osd_interface &osd, HWND window, WPARAM wparam, LPARAM lparam) +{ + PointerUpdateEventArgs args; + args.window = window; + args.id = GET_POINTERID_WPARAM(wparam); + args.xpos = GET_X_LPARAM(lparam); + args.ypos = GET_Y_LPARAM(lparam); + args.vdelta = args.hdelta = 0; + args.isnew = IS_POINTER_NEW_WPARAM(wparam); + args.lost = IS_POINTER_CANCELED_WPARAM(wparam); + args.inrange = IS_POINTER_INRANGE_WPARAM(wparam); + args.incontact = IS_POINTER_INCONTACT_WPARAM(wparam); + args.primary = IS_POINTER_PRIMARY_WPARAM(wparam); + args.buttons[0] = IS_POINTER_FIRSTBUTTON_WPARAM(wparam); + args.buttons[1] = IS_POINTER_SECONDBUTTON_WPARAM(wparam); + args.buttons[2] = IS_POINTER_THIRDBUTTON_WPARAM(wparam); + args.buttons[3] = IS_POINTER_FOURTHBUTTON_WPARAM(wparam); + args.buttons[4] = IS_POINTER_FIFTHBUTTON_WPARAM(wparam); + + bool const handled = osd.handle_input_event(INPUT_EVENT_POINTER_UPDATE, &args); + + // When in lightgun mode or mouse mode, the pointer may be routed to the input system + // because the mouse interactions in the UI are routed from the video_window_proc below + // we need to make sure they aren't suppressed in these cases. + return handled && !osd.options().lightgun() && !osd.options().mouse(); +} + +inline BOOL handle_pointer_leave(windows_osd_interface &osd, HWND window, WPARAM wparam, LPARAM lparam) +{ + PointerUpdateEventArgs args; + args.window = window; + args.id = GET_POINTERID_WPARAM(wparam); + args.xpos = GET_X_LPARAM(lparam); + args.ypos = GET_Y_LPARAM(lparam); + args.vdelta = args.hdelta = 0; + args.isnew = false; + args.lost = true; + args.inrange = IS_POINTER_INRANGE_WPARAM(wparam); + args.incontact = IS_POINTER_INCONTACT_WPARAM(wparam); + args.primary = false; + args.buttons[0] = false; + args.buttons[1] = false; + args.buttons[2] = false; + args.buttons[3] = false; + args.buttons[4] = false; + + bool const handled = osd.handle_input_event(INPUT_EVENT_POINTER_UPDATE, &args); + + // When in lightgun mode or mouse mode, the pointer may be routed to the input system + // because the mouse interactions in the UI are routed from the video_window_proc below + // we need to make sure they aren't suppressed in these cases. + return handled && !osd.options().lightgun() && !osd.options().mouse(); +} + +inline BOOL handle_pointer_capture_change(windows_osd_interface &osd, HWND window, WPARAM wparam, LPARAM lparam) +{ + PointerUpdateEventArgs args; + args.window = window; + args.id = GET_POINTERID_WPARAM(wparam); + args.xpos = args.ypos = 0; + args.vdelta = args.hdelta = 0; + args.isnew = false; + args.lost = true; + args.inrange = false; + args.incontact = false; + args.primary = false; + args.buttons[0] = false; + args.buttons[1] = false; + args.buttons[2] = false; + args.buttons[3] = false; + args.buttons[4] = false; + + bool const handled = osd.handle_input_event(INPUT_EVENT_POINTER_UPDATE, &args); + + // When in lightgun mode or mouse mode, the pointer may be routed to the input system + // because the mouse interactions in the UI are routed from the video_window_proc below + // we need to make sure they aren't suppressed in these cases. + return handled && !osd.options().lightgun() && !osd.options().mouse(); +} + inline BOOL handle_keypress(windows_osd_interface &osd, int vkey, int down, LPARAM lparam) { KeyPressEventArgs args; @@ -461,6 +541,22 @@ void windows_osd_interface::process_events(bool ingame, bool nodispatch) dispatch = !handle_mouse_wheel(*this, 0, GET_WHEEL_DELTA_WPARAM(message.wParam), message.lParam); break; + // forward pointer events to the input system + case WM_POINTERENTER: + case WM_POINTERDOWN: + case WM_POINTERUP: + case WM_POINTERUPDATE: + dispatch = !handle_pointer_update(*this, message.hwnd, message.wParam, message.lParam); + break; + + case WM_POINTERLEAVE: + dispatch = !handle_pointer_leave(*this, message.hwnd, message.wParam, message.lParam); + break; + + case WM_POINTERCAPTURECHANGED: + dispatch = !handle_pointer_capture_change(*this, message.hwnd, message.wParam, message.lParam); + break; + // forward keystrokes to the input system case WM_KEYDOWN: if (NOT_ALREADY_DOWN(message.lParam)) diff --git a/src/osd/windows/winmain.h b/src/osd/windows/winmain.h index 5b8e992be6b..0b4eb256144 100644 --- a/src/osd/windows/winmain.h +++ b/src/osd/windows/winmain.h @@ -31,7 +31,8 @@ enum input_event INPUT_EVENT_ARRIVAL, INPUT_EVENT_REMOVAL, INPUT_EVENT_MOUSE_BUTTON, - INPUT_EVENT_MOUSE_WHEEL + INPUT_EVENT_MOUSE_WHEEL, + INPUT_EVENT_POINTER_UPDATE }; struct KeyPressEventArgs @@ -51,6 +52,20 @@ struct MouseUpdateEventArgs int ypos; }; +struct PointerUpdateEventArgs +{ + void *window; + unsigned id; + int xpos, ypos; + int vdelta, hdelta; + bool isnew; + bool lost; + bool inrange; + bool incontact; + bool primary; + bool buttons[5]; +}; + class windows_osd_interface : public osd_common_t { |
