summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2026-04-18 04:15:04 +1000
committer Vas Crabb <vas@vastheman.com>2026-04-18 04:15:04 +1000
commit46a890d2854838ce7e6c2bf177c54904b62720a8 (patch)
tree7e107b81246d67558dedf07ef07945c96a4362ed /src/osd
parent8077642755f1d0666cdcffaecbde195693a75cf6 (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.h12
-rw-r--r--src/osd/modules/input/input_rawinput.cpp2
-rw-r--r--src/osd/modules/input/input_sdl.cpp4
-rw-r--r--src/osd/modules/input/input_sdl3.cpp4
-rw-r--r--src/osd/modules/input/input_win32.cpp171
-rw-r--r--src/osd/modules/input/input_x11.cpp2
-rw-r--r--src/osd/windows/window.cpp96
-rw-r--r--src/osd/windows/winmain.h17
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
{