summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/windows
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/windows')
-rw-r--r--src/osd/windows/window.cpp667
-rw-r--r--src/osd/windows/window.h48
-rw-r--r--src/osd/windows/winmain.cpp18
-rw-r--r--src/osd/windows/winmain.h17
4 files changed, 625 insertions, 125 deletions
diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp
index 387a7f6daba..2759b32227f 100644
--- a/src/osd/windows/window.cpp
+++ b/src/osd/windows/window.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
//============================================================
//
// window.cpp - Win32 window handling
@@ -31,10 +31,10 @@
#include "modules/monitor/monitor_common.h"
-#define NOT_ALREADY_DOWN(x) (x & 0x40000000) == 0
+#define NOT_ALREADY_DOWN(x) ((x & 0x40000000) == 0)
#define SCAN_CODE(x) ((x >> 16) & 0xff)
-#define IS_EXTENDED(x) (0x01000000 & x)
-#define MAKE_DI_SCAN(scan, isextended) (scan & 0x7f) | (isextended ? 0x80 : 0x00)
+#define IS_EXTENDED(x) ((x >> 24) & 0x01)
+#define MAKE_DI_SCAN(scan, isextended) ((scan & 0x7f) | (isextended ? 0x80 : 0x00))
#define WINOSD(machine) downcast<windows_osd_interface*>(&machine.osd())
//============================================================
@@ -64,6 +64,34 @@
#define WM_USER_SET_MINSIZE (WM_USER + 5)
+namespace {
+
+// If legacy mouse to pointer event translation is enabled, translated
+// WM_POINTER* events have pointer ID zero. Assume this will never be
+// seen for "real" pointer events.
+constexpr WORD MOUSE_POINTER_ID = 0;
+
+constexpr unsigned get_pointer_buttons(WPARAM wparam)
+{
+ return
+ (IS_POINTER_FIRSTBUTTON_WPARAM(wparam) ? 0x01 : 0x00) |
+ (IS_POINTER_SECONDBUTTON_WPARAM(wparam) ? 0x02 : 0x00) |
+ (IS_POINTER_THIRDBUTTON_WPARAM(wparam) ? 0x04 : 0x00) |
+ (IS_POINTER_FOURTHBUTTON_WPARAM(wparam) ? 0x08 : 0x00) |
+ (IS_POINTER_FIFTHBUTTON_WPARAM(wparam) ? 0x10 : 0x00);
+}
+
+constexpr osd::ui_event_handler::pointer convert_pointer_type(POINTER_INPUT_TYPE type)
+{
+ // PT_POINTER isn't a real type, and we'll leave PT_TOUCHPAD as unknown for now
+ return
+ (PT_TOUCH == type) ? osd::ui_event_handler::pointer::TOUCH :
+ (PT_PEN == type) ? osd::ui_event_handler::pointer::PEN :
+ (PT_MOUSE == type) ? osd::ui_event_handler::pointer::MOUSE :
+ osd::ui_event_handler::pointer::UNKNOWN;
+}
+
+} // anonymous namespace
//============================================================
// GLOBAL VARIABLES
@@ -123,39 +151,12 @@ bool windows_osd_interface::window_init()
return true;
}
-void windows_osd_interface::update_slider_list()
-{
- for (const auto &window : osd_common_t::window_list())
- {
- // check if any window has dirty sliders
- if (window->has_renderer() && window->renderer().sliders_dirty())
- {
- build_slider_list();
- return;
- }
- }
-}
int windows_osd_interface::window_count()
{
return osd_common_t::window_list().size();
}
-void windows_osd_interface::build_slider_list()
-{
- m_sliders.clear();
-
- for (const auto &window : osd_common_t::window_list())
- {
- if (window->has_renderer())
- {
- // take the sliders of the first window
- std::vector<ui::menu_item> window_sliders = window->renderer().get_slider_list();
- m_sliders.insert(m_sliders.end(), window_sliders.begin(), window_sliders.end());
- }
- }
-}
-
void windows_osd_interface::add_audio_to_recording(const int16_t *buffer, int samples_this_frame)
{
auto const &window = osd_common_t::window_list().front(); // We only record on the first window
@@ -163,6 +164,7 @@ void windows_osd_interface::add_audio_to_recording(const int16_t *buffer, int sa
window->renderer().add_audio_to_recording(buffer, samples_this_frame);
}
+
//============================================================
// winwindow_exit
// (main thread)
@@ -190,6 +192,14 @@ void windows_osd_interface::window_exit()
}
+inline win_window_info::win_pointer_info::win_pointer_info(WORD p, POINTER_INPUT_TYPE t, unsigned i, unsigned d)
+ : pointer_info(i, d)
+ , ptrid(p)
+ , type(t)
+{
+}
+
+
win_window_info::win_window_info(
running_machine &machine,
render_module &renderprovider,
@@ -216,6 +226,10 @@ win_window_info::win_window_info(
, m_resize_state(RESIZE_STATE_NORMAL)
, m_main(nullptr)
, m_attached_mode(false)
+ , m_pointer_mask(0)
+ , m_next_pointer(0)
+ , m_next_ptrdev(0)
+ , m_tracking_mouse(false)
{
m_non_fullscreen_bounds.left = 0;
m_non_fullscreen_bounds.top = 0;
@@ -224,6 +238,10 @@ win_window_info::win_window_info(
m_fullscreen = !video_config.windowed;
m_prescale = video_config.prescale;
+
+ m_ptrdev_map.reserve(8);
+ m_ptrdev_info.reserve(1);
+ m_active_pointers.reserve(16);
}
POINT win_window_info::s_saved_cursor_pos = { -1, -1 };
@@ -258,7 +276,7 @@ void win_window_info::show_pointer()
s_saved_cursor_pos.x = s_saved_cursor_pos.y = -1;
}
- while (ShowCursor(TRUE) < 1) {};
+ while (ShowCursor(TRUE) < 1) { }
ShowCursor(FALSE);
}
@@ -303,7 +321,7 @@ static LRESULT CALLBACK winwindow_video_window_proc_ui(HWND wnd, UINT message, W
// is_mame_window
//============================================================
-static bool is_mame_window(HWND hwnd)
+inline bool is_mame_window(HWND hwnd)
{
for (const auto &window : osd_common_t::window_list())
if (dynamic_cast<win_window_info &>(*window).platform_window() == hwnd)
@@ -312,30 +330,50 @@ static bool is_mame_window(HWND hwnd)
return false;
}
-inline static BOOL handle_mouse_button(windows_osd_interface *osd, int button, int down, int x, int y)
+inline BOOL handle_mouse_button(windows_osd_interface &osd, int button, int down, LPARAM lparam)
{
- MouseButtonEventArgs args;
- args.button = button;
- args.keydown = down;
- args.xpos = x;
- args.ypos = y;
+ MouseUpdateEventArgs args;
+ args.pressed = (down ? 1 : 0) << button;
+ args.released = (down ? 0 : 1) << button;
+ args.vdelta = 0;
+ args.hdelta = 0;
+ args.xpos = GET_X_LPARAM(lparam);
+ args.ypos = GET_Y_LPARAM(lparam);
- bool handled = osd->handle_input_event(INPUT_EVENT_MOUSE_BUTTON, &args);
+ bool const handled = osd.handle_input_event(INPUT_EVENT_MOUSE_BUTTON, &args);
// When in lightgun mode or mouse mode, the mouse click 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();
+ return handled && !osd.options().lightgun() && !osd.options().mouse();
}
-inline static BOOL handle_keypress(windows_osd_interface *osd, int vkey, int down, int scancode, BOOL extended_key)
+inline BOOL handle_mouse_wheel(windows_osd_interface &osd, int v, int h, LPARAM lparam)
+{
+ MouseUpdateEventArgs args;
+ args.pressed = 0;
+ args.released = 0;
+ args.vdelta = v;
+ args.hdelta = h;
+ args.xpos = GET_X_LPARAM(lparam);
+ args.ypos = GET_Y_LPARAM(lparam);
+
+ bool const handled = osd.handle_input_event(INPUT_EVENT_MOUSE_WHEEL, &args);
+
+ // When in lightgun mode or mouse mode, the mouse wheel 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;
args.event_id = down ? INPUT_EVENT_KEYDOWN : INPUT_EVENT_KEYUP;
- args.scancode = MAKE_DI_SCAN(scancode, extended_key);
+ args.scancode = MAKE_DI_SCAN(SCAN_CODE(lparam), IS_EXTENDED(lparam));
args.vkey = vkey;
- return osd->handle_input_event(args.event_id, &args);
+ return osd.handle_input_event(args.event_id, &args);
}
//============================================================
@@ -376,52 +414,62 @@ void windows_osd_interface::process_events(bool ingame, bool nodispatch)
// forward mouse button downs to the input system
case WM_LBUTTONDOWN:
- dispatch = !handle_mouse_button(this, 0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, 0, TRUE, message.lParam);
break;
case WM_RBUTTONDOWN:
- dispatch = !handle_mouse_button(this, 1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, 1, TRUE, message.lParam);
break;
case WM_MBUTTONDOWN:
- dispatch = !handle_mouse_button(this, 2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, 2, TRUE, message.lParam);
break;
case WM_XBUTTONDOWN:
- dispatch = !handle_mouse_button(this, 3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, (GET_XBUTTON_WPARAM(message.wParam) == XBUTTON1) ? 3 : 4, TRUE, message.lParam);
break;
// forward mouse button ups to the input system
case WM_LBUTTONUP:
- dispatch = !handle_mouse_button(this, 0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, 0, FALSE, message.lParam);
break;
case WM_RBUTTONUP:
- dispatch = !handle_mouse_button(this, 1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, 1, FALSE, message.lParam);
break;
case WM_MBUTTONUP:
- dispatch = !handle_mouse_button(this, 2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, 2, FALSE, message.lParam);
break;
case WM_XBUTTONUP:
- dispatch = !handle_mouse_button(this, 3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = !handle_mouse_button(*this, (GET_XBUTTON_WPARAM(message.wParam) == XBUTTON1) ? 3 : 4, FALSE, message.lParam);
+ break;
+
+ // forward mouse wheel movement to the input system
+ case WM_MOUSEWHEEL:
+ dispatch = !handle_mouse_wheel(*this, GET_WHEEL_DELTA_WPARAM(message.wParam), 0, message.lParam);
break;
+ case WM_MOUSEHWHEEL:
+ dispatch = !handle_mouse_wheel(*this, 0, GET_WHEEL_DELTA_WPARAM(message.wParam), message.lParam);
+ break;
+
+ // forward keystrokes to the input system
case WM_KEYDOWN:
if (NOT_ALREADY_DOWN(message.lParam))
- dispatch = !handle_keypress(this, message.wParam, TRUE, SCAN_CODE(message.lParam), IS_EXTENDED(message.lParam));
+ dispatch = !handle_keypress(*this, message.wParam, TRUE, message.lParam);
break;
case WM_KEYUP:
- dispatch = !handle_keypress(this, message.wParam, FALSE, SCAN_CODE(message.lParam), IS_EXTENDED(message.lParam));
+ dispatch = !handle_keypress(*this, message.wParam, FALSE, message.lParam);
break;
}
}
// dispatch if necessary
if (dispatch)
- winwindow_dispatch_message(machine(), &message);
+ winwindow_dispatch_message(machine(), message);
}
}
while (ui_temp_pause > 0);
@@ -437,12 +485,12 @@ void windows_osd_interface::process_events(bool ingame, bool nodispatch)
// (main thread)
//============================================================
-void winwindow_dispatch_message(running_machine &machine, MSG *message)
+void winwindow_dispatch_message(running_machine &machine, MSG const &message)
{
assert(GetCurrentThreadId() == main_threadid);
// dispatch our special communication messages
- switch (message->message)
+ switch (message.message)
{
// special case for quit
case WM_QUIT:
@@ -451,8 +499,8 @@ void winwindow_dispatch_message(running_machine &machine, MSG *message)
// everything else dispatches normally
default:
- TranslateMessage(message);
- DispatchMessage(message);
+ TranslateMessage(&message);
+ DispatchMessage(&message);
break;
}
}
@@ -1066,49 +1114,7 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
case WM_SYSKEYDOWN:
break;
- // input events
- case WM_MOUSEMOVE:
- window->machine().ui_input().push_mouse_move_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
- case WM_MOUSELEAVE:
- window->machine().ui_input().push_mouse_leave_event(window->target());
- break;
-
- case WM_LBUTTONDOWN:
- {
- auto const ticks = std::chrono::steady_clock::now();
- window->machine().ui_input().push_mouse_down_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
-
- // check for a double-click - avoid overflow by adding times rather than subtracting
- if (ticks < (window->m_lastclicktime + std::chrono::milliseconds(GetDoubleClickTime())) &&
- GET_X_LPARAM(lparam) >= window->m_lastclickx - 4 && GET_X_LPARAM(lparam) <= window->m_lastclickx + 4 &&
- GET_Y_LPARAM(lparam) >= window->m_lastclicky - 4 && GET_Y_LPARAM(lparam) <= window->m_lastclicky + 4)
- {
- window->m_lastclicktime = std::chrono::steady_clock::time_point::min();
- window->machine().ui_input().push_mouse_double_click_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- }
- else
- {
- window->m_lastclicktime = ticks;
- window->m_lastclickx = GET_X_LPARAM(lparam);
- window->m_lastclicky = GET_Y_LPARAM(lparam);
- }
- }
- break;
-
- case WM_LBUTTONUP:
- window->machine().ui_input().push_mouse_up_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
- case WM_RBUTTONDOWN:
- window->machine().ui_input().push_mouse_rdown_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
- case WM_RBUTTONUP:
- window->machine().ui_input().push_mouse_rup_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
+ // text input events
case WM_CHAR:
{
char16_t const ch = char16_t(wparam);
@@ -1140,14 +1146,71 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
window->machine().ui_input().push_char_event(window->target(), char32_t(wparam));
break;
+ // legacy mouse events
+ case WM_MOUSEMOVE:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_MOUSELEAVE:
+ window->mouse_left(wparam, lparam);
+ break;
+
+ case WM_LBUTTONDOWN:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_LBUTTONUP:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP:
+ window->mouse_updated(wparam, lparam);
+ return TRUE;
+
case WM_MOUSEWHEEL:
{
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(wnd, &where);
UINT ucNumLines = 3; // default
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucNumLines, 0);
- window->machine().ui_input().push_mouse_wheel_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), GET_WHEEL_DELTA_WPARAM(wparam), ucNumLines);
+ window->machine().ui_input().push_mouse_wheel_event(window->target(), where.x, where.y, GET_WHEEL_DELTA_WPARAM(wparam), ucNumLines);
}
break;
+ // new-style pointer handling (mouse/pen/touch)
+ case WM_POINTERENTER:
+ window->pointer_entered(wparam, lparam);
+ break;
+ case WM_POINTERLEAVE:
+ window->pointer_left(wparam, lparam);
+ break;
+ case WM_POINTERDOWN:
+ case WM_POINTERUP:
+ case WM_POINTERUPDATE:
+ window->pointer_updated(wparam, lparam);
+ break;
+ case WM_POINTERCAPTURECHANGED:
+ window->pointer_capture_changed(wparam, lparam);
+ break;
+ // TODO: other pointer events?
+ //case WM_POINTERACTIVATE:
+ //case WM_POINTERDEVICECHANGE:
+ //case WM_POINTERDEVICEINRANGE:
+ //case WM_POINTERDEVICEOUTOFRANGE:
+ //case WM_POINTERROUTEDAWAY:
+ //case WM_POINTERROUTEDRELEASED:
+ //case WM_POINTERROUTEDTO:
+ //case WM_POINTERWHEEL:
+ //case WM_POINTERHWHEEL:
+
// pause the system when we start a menu or resize
case WM_ENTERSIZEMOVE:
window->m_resize_state = RESIZE_STATE_RESIZING;
@@ -1834,24 +1897,412 @@ win_window_focus win_window_info::focus() const
}
+void win_window_info::pointer_entered(WPARAM wparam, LPARAM lparam)
+{
+ assert(!IS_POINTER_CANCELED_WPARAM(wparam));
+ auto const info(map_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(platform_window(), &where);
+ info->x = where.x;
+ info->y = where.y;
+ machine().ui_input().push_pointer_update(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ where.x, where.y,
+ 0U, 0U, 0U, 0U);
+ }
+}
+
+void win_window_info::pointer_left(WPARAM wparam, LPARAM lparam)
+{
+ auto const info(find_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ bool const canceled(IS_POINTER_CANCELED_WPARAM(wparam));
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(platform_window(), &where);
+ expire_pointer(info, where, canceled);
+ }
+}
+
+void win_window_info::pointer_updated(WPARAM wparam, LPARAM lparam)
+{
+ bool const canceled(IS_POINTER_CANCELED_WPARAM(wparam));
+ auto const info(canceled ? find_pointer(wparam) : map_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(platform_window(), &where);
+ unsigned const buttons(canceled ? 0 : get_pointer_buttons(wparam));
+ update_pointer(*info, where, buttons, canceled);
+ }
+}
+
+void win_window_info::pointer_capture_changed(WPARAM wparam, LPARAM lparam)
+{
+ auto const info(find_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ // treat this as the pointer being stolen - fail any gestures
+ if (BIT(info->buttons, 0) && (0 < info->clickcnt))
+ info->clickcnt = -info->clickcnt;
+
+ // push to UI manager and dump pointer data
+ machine().ui_input().push_pointer_abort(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ info->x, info->y,
+ info->buttons, info->clickcnt);
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+ }
+}
+
+void win_window_info::mouse_left(WPARAM wparam, LPARAM lparam)
+{
+ m_tracking_mouse = false;
+ auto const info(find_mouse_pointer());
+ if (m_active_pointers.end() != info)
+ {
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ expire_pointer(info, where, false);
+ }
+}
+
+void win_window_info::mouse_updated(WPARAM wparam, LPARAM lparam)
+{
+ auto const info(map_mouse_pointer());
+ if (m_active_pointers.end() == info)
+ return;
+
+ POINT const where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ unsigned const buttons(
+ ((MK_LBUTTON & wparam) ? 0x01 : 0x00) |
+ ((MK_RBUTTON & wparam) ? 0x02 : 0x00) |
+ ((MK_MBUTTON & wparam) ? 0x04 : 0x00) |
+ ((MK_XBUTTON1 & wparam) ? 0x08 : 0x00) |
+ ((MK_XBUTTON2 & wparam) ? 0x10 : 0x00));
+
+ // continue to track the mouse outside the window if buttons are down
+ if (buttons && !info->buttons)
+ {
+ SetCapture(platform_window());
+
+ TRACKMOUSEEVENT tm;
+ std::memset(&tm, 0, sizeof(tm));
+ tm.cbSize = sizeof(tm);
+ tm.dwFlags = TME_CANCEL | TME_LEAVE;
+ tm.hwndTrack = platform_window();
+ TrackMouseEvent(&tm);
+ m_tracking_mouse = false;
+ }
+ else
+ {
+ if (!buttons && info->buttons)
+ {
+ if (GetCapture() == platform_window())
+ ReleaseCapture();
+ }
+
+ if (!m_tracking_mouse)
+ {
+ TRACKMOUSEEVENT tm;
+ std::memset(&tm, 0, sizeof(tm));
+ tm.cbSize = sizeof(tm);
+ tm.dwFlags = TME_LEAVE;
+ tm.hwndTrack = platform_window();
+ TrackMouseEvent(&tm);
+ m_tracking_mouse = true;
+ }
+ }
+
+ update_pointer(*info, where, buttons, false);
+}
+
+void win_window_info::expire_pointer(std::vector<win_pointer_info>::iterator info, POINT const &where, bool canceled)
+{
+ // leaving implicitly releases buttons, so check hold/drag if necessary
+ if (BIT(info->buttons, 0) && (0 < info->clickcnt))
+ {
+ if (!canceled)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const exp(std::chrono::milliseconds(GetDoubleClickTime()) + info->pressed);
+ int const dx(where.x - info->pressedx);
+ int const dy(where.y - info->pressedy);
+ int const distance((dx * dx) + (dy * dy));
+ int const tolerance((PT_TOUCH == info->type) ? TAP_DISTANCE : CLICK_DISTANCE);
+ if ((exp < now) || (tolerance < distance))
+ info->clickcnt = -info->clickcnt;
+ }
+ else
+ {
+ info->clickcnt = -info->clickcnt;
+ }
+ }
+
+ // need to remember touches to recognise multi-tap gestures
+ if (!canceled && (PT_TOUCH == info->type) && (0 < info->clickcnt))
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const time = std::chrono::milliseconds(GetDoubleClickTime());
+ if ((time + info->pressed) >= now)
+ {
+ try
+ {
+ unsigned i(0);
+ if (m_ptrdev_info.size() > info->device)
+ i = m_ptrdev_info[info->device].clear_expired_touches(now, time);
+ else
+ m_ptrdev_info.resize(info->device + 1);
+
+ if (std::size(m_ptrdev_info[info->device].touches) > i)
+ {
+ m_ptrdev_info[info->device].touches[i].when = info->pressed;
+ m_ptrdev_info[info->device].touches[i].x = info->pressedx;
+ m_ptrdev_info[info->device].touches[i].y = info->pressedy;
+ m_ptrdev_info[info->device].touches[i].cnt = info->clickcnt;
+ }
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ }
+ }
+ }
+
+ // push to UI manager and dump pointer data
+ if (!canceled)
+ {
+ machine().ui_input().push_pointer_leave(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ where.x, where.y,
+ info->buttons, info->clickcnt);
+ }
+ else
+ {
+ machine().ui_input().push_pointer_abort(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ where.x, where.y,
+ info->buttons, info->clickcnt);
+ }
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+}
+
+void win_window_info::update_pointer(win_pointer_info &info, POINT const &where, unsigned buttons, bool canceled)
+{
+ if (!canceled && (where.x == info.x) && (where.y == info.y) && (buttons == info.buttons))
+ return;
+
+ // detect multi-click actions
+ unsigned const pressed(canceled ? 0 : (buttons & ~info.buttons));
+ unsigned const released(canceled ? ~info.buttons : (~buttons & info.buttons));
+ if (BIT(pressed, 0))
+ {
+ info.primary_down(
+ where.x, where.y,
+ std::chrono::milliseconds(GetDoubleClickTime()),
+ (PT_TOUCH == info.type) ? TAP_DISTANCE : CLICK_DISTANCE,
+ PT_TOUCH == info.type,
+ m_ptrdev_info);
+ }
+ else if (BIT(info.buttons, 0))
+ {
+ info.check_primary_hold_drag(
+ where.x, where.y,
+ std::chrono::milliseconds(GetDoubleClickTime()),
+ (PT_TOUCH == info.type) ? TAP_DISTANCE : CLICK_DISTANCE);
+ }
+
+ // update info and push to UI manager
+ info.x = where.x;
+ info.y = where.y;
+ info.buttons = buttons;
+ if (!canceled)
+ {
+ machine().ui_input().push_pointer_update(
+ target(),
+ convert_pointer_type(info.type),
+ info.index,
+ info.device,
+ where.x, where.y,
+ buttons, pressed, released, info.clickcnt);
+ }
+ else
+ {
+ machine().ui_input().push_pointer_abort(
+ target(),
+ convert_pointer_type(info.type),
+ info.index,
+ info.device,
+ where.x, where.y,
+ released, info.clickcnt);
+ }
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::map_pointer(WPARAM wparam)
+{
+ WORD const ptrid(GET_POINTERID_WPARAM(wparam));
+ auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == ptrid))
+ return found;
+
+ if ((sizeof(m_next_pointer) * 8) <= m_next_pointer)
+ {
+ assert(~decltype(m_pointer_mask)(0) == m_pointer_mask);
+ osd_printf_warning("win_window_info: exceeded maximum number of active pointers\n");
+ return m_active_pointers.end();
+ }
+ assert(!BIT(m_pointer_mask, m_next_pointer));
+
+ POINTER_INFO info = { 0 };
+ if (!OSD_DYNAMIC_CALL(GetPointerInfo, ptrid, &info))
+ {
+ osd_printf_error("win_window_info: failed to get info for pointer ID %u\n", ptrid);
+ return m_active_pointers.end();
+ }
+
+ auto devpos(std::lower_bound(
+ m_ptrdev_map.begin(),
+ m_ptrdev_map.end(),
+ info.sourceDevice,
+ [] (std::pair<HANDLE, unsigned> const &mapping, HANDLE device)
+ {
+ return mapping.first < device;
+ }));
+
+ try
+ {
+ if ((m_ptrdev_map.end() == devpos) || (devpos->first != info.sourceDevice))
+ {
+ devpos = m_ptrdev_map.emplace(devpos, info.sourceDevice, m_next_ptrdev);
+ ++m_next_ptrdev;
+ }
+
+ found = m_active_pointers.emplace(
+ found,
+ win_pointer_info(ptrid, info.pointerType, m_next_pointer, devpos->second));
+ m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer;
+ do
+ {
+ ++m_next_pointer;
+ }
+ while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer));
+
+ return found;
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ return m_active_pointers.end();
+ }
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::find_pointer(WPARAM wparam)
+{
+ WORD const ptrid(GET_POINTERID_WPARAM(wparam));
+ auto const found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == ptrid))
+ return found;
+ else
+ return m_active_pointers.end();
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::map_mouse_pointer()
+{
+ auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), MOUSE_POINTER_ID, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == MOUSE_POINTER_ID))
+ return found;
+
+ if ((sizeof(m_next_pointer) * 8) <= m_next_pointer)
+ {
+ assert(~decltype(m_pointer_mask)(0) == m_pointer_mask);
+ osd_printf_warning("win_window_info: exceeded maximum number of active pointers\n");
+ return m_active_pointers.end();
+ }
+ assert(!BIT(m_pointer_mask, m_next_pointer));
+
+ auto devpos(std::lower_bound(
+ m_ptrdev_map.begin(),
+ m_ptrdev_map.end(),
+ INVALID_HANDLE_VALUE,
+ [] (std::pair<HANDLE, unsigned> const &mapping, HANDLE device)
+ {
+ return mapping.first < device;
+ }));
+
+ try
+ {
+ if ((m_ptrdev_map.end() == devpos) || (devpos->first != INVALID_HANDLE_VALUE))
+ {
+ devpos = m_ptrdev_map.emplace(devpos, INVALID_HANDLE_VALUE, m_next_ptrdev);
+ ++m_next_ptrdev;
+ }
+
+ found = m_active_pointers.emplace(
+ found,
+ win_pointer_info(MOUSE_POINTER_ID, PT_MOUSE, m_next_pointer, devpos->second));
+ m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer;
+ do
+ {
+ ++m_next_pointer;
+ }
+ while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer));
+
+ return found;
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ return m_active_pointers.end();
+ }
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::find_mouse_pointer()
+{
+ auto const found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), MOUSE_POINTER_ID, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == MOUSE_POINTER_ID))
+ return found;
+ else
+ return m_active_pointers.end();
+}
+
+
+#if (USE_QTDEBUG)
//============================================================
// winwindow_qt_filter
//============================================================
-#if (USE_QTDEBUG)
bool winwindow_qt_filter(void *message)
{
- MSG *msg = (MSG *)message;
+ MSG *const msg = reinterpret_cast<MSG *>(message);
- if(is_mame_window(msg->hwnd) || (!msg->hwnd && (msg->message >= WM_USER)))
+ if (is_mame_window(msg->hwnd) || (!msg->hwnd && (msg->message >= WM_USER)))
{
LONG_PTR ptr;
- if(msg->hwnd) // get the machine associated with this window
+ if (msg->hwnd) // get the machine associated with this window
ptr = GetWindowLongPtr(msg->hwnd, GWLP_USERDATA);
else // any one will have to do
ptr = (LONG_PTR)osd_common_t::window_list().front().get();
- winwindow_dispatch_message(((win_window_info *)ptr)->machine(), msg);
+ winwindow_dispatch_message(reinterpret_cast<win_window_info *>(ptr)->machine(), *msg);
return true;
}
return false;
diff --git a/src/osd/windows/window.h b/src/osd/windows/window.h
index 53021e43a78..ada71f1cba7 100644
--- a/src/osd/windows/window.h
+++ b/src/osd/windows/window.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
//============================================================
//
// window.h - Win32 window handling
@@ -128,6 +128,21 @@ public:
int m_resize_state;
private:
+ struct win_pointer_info : public pointer_info
+ {
+ static constexpr bool compare(win_pointer_info const &info, WORD ptrid) { return info.ptrid < ptrid; }
+
+ win_pointer_info(win_pointer_info const &) = default;
+ win_pointer_info(win_pointer_info &&) = default;
+ win_pointer_info &operator=(win_pointer_info const &) = default;
+ win_pointer_info &operator=(win_pointer_info &&) = default;
+
+ win_pointer_info(WORD p, POINTER_INPUT_TYPE t, unsigned i, unsigned d);
+
+ WORD ptrid;
+ POINTER_INPUT_TYPE type;
+ };
+
void draw_video_contents(HDC dc, bool update);
int complete_create();
int wnd_extra_width();
@@ -141,9 +156,38 @@ private:
void adjust_window_position_after_major_change();
void set_fullscreen(int fullscreen);
+ // pointer handling helpers
+ void pointer_entered(WPARAM wparam, LPARAM lparam);
+ void pointer_left(WPARAM wparam, LPARAM lparam);
+ void pointer_updated(WPARAM wparam, LPARAM lparam);
+ void pointer_capture_changed(WPARAM wparam, LPARAM lparam);
+ void mouse_left(WPARAM wparam, LPARAM lparam);
+ void mouse_updated(WPARAM wparam, LPARAM lparam);
+ void expire_pointer(std::vector<win_pointer_info>::iterator info, POINT const &where, bool canceled);
+ void update_pointer(win_pointer_info &info, POINT const &where, unsigned buttons, bool canceled);
+ std::vector<win_pointer_info>::iterator map_pointer(WPARAM wparam);
+ std::vector<win_pointer_info>::iterator find_pointer(WPARAM wparam);
+ std::vector<win_pointer_info>::iterator map_mouse_pointer();
+ std::vector<win_pointer_info>::iterator find_mouse_pointer();
+
win_window_info * m_main;
bool m_attached_mode;
+ // these functions first appear in Windows 8/Server 2012
+ OSD_DYNAMIC_API(user32, "User32.dll", "User32.dll");
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerType, UINT32, POINTER_INPUT_TYPE *);
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerInfo, UINT32, POINTER_INFO *);
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerPenInfo, UINT32, POINTER_PEN_INFO *);
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerTouchInfo, UINT32, POINTER_TOUCH_INFO *);
+
+ // info on currently active pointers - 64 pointers ought to be enough for anyone
+ uint64_t m_pointer_mask;
+ unsigned m_next_pointer, m_next_ptrdev;
+ bool m_tracking_mouse;
+ std::vector<std::pair<HANDLE, unsigned> > m_ptrdev_map;
+ std::vector<pointer_dev_info> m_ptrdev_info;
+ std::vector<win_pointer_info> m_active_pointers;
+
static POINT s_saved_cursor_pos;
};
@@ -163,7 +207,7 @@ void winwindow_toggle_fsfx(void);
void winwindow_ui_pause(running_machine &machine, int pause);
int winwindow_ui_is_paused(running_machine &machine);
-void winwindow_dispatch_message(running_machine &machine, MSG *message);
+void winwindow_dispatch_message(running_machine &machine, MSG const &message);
diff --git a/src/osd/windows/winmain.cpp b/src/osd/windows/winmain.cpp
index f1df872456f..2f7df659f44 100644
--- a/src/osd/windows/winmain.cpp
+++ b/src/osd/windows/winmain.cpp
@@ -21,6 +21,7 @@
#include "winutil.h"
#include "winfile.h"
#include "modules/diagnostics/diagnostics_module.h"
+#include "modules/lib/osdlib.h"
#include "modules/monitor/monitor_common.h"
// standard C headers
@@ -180,6 +181,17 @@ int main(int argc, char *argv[])
if (!isatty(fileno(stderr)))
setvbuf(stderr, (char *) nullptr, _IOFBF, 64);
+ {
+ // Disable legacy mouse to pointer event translation - it's broken:
+ // * No WM_POINTERLEAVE event when mouse pointer moves directly to an
+ // overlapping window from the same process.
+ // * Still receive occasional WM_MOUSEMOVE events.
+ OSD_DYNAMIC_API(user32, "User32.dll", "User32.dll");
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, EnableMouseInPointer, BOOL);
+ if (OSD_DYNAMIC_API_TEST(EnableMouseInPointer))
+ OSD_DYNAMIC_CALL(EnableMouseInPointer, FALSE);
+ }
+
// initialize common controls
InitCommonControls();
@@ -340,12 +352,6 @@ void windows_osd_interface::init(running_machine &machine)
// initialize the subsystems
osd_common_t::init_subsystems();
- // notify listeners of screen configuration
- for (const auto &info : osd_common_t::window_list())
- {
- machine.output().set_value(string_format("Orientation(%s)", info->monitor()->devicename()), dynamic_cast<win_window_info &>(*info).m_targetorient);
- }
-
// hook up the debugger log
if (options.oslog())
machine.add_logerror_callback(&windows_osd_interface::output_oslog);
diff --git a/src/osd/windows/winmain.h b/src/osd/windows/winmain.h
index bb96ec2a3e4..5b8e992be6b 100644
--- a/src/osd/windows/winmain.h
+++ b/src/osd/windows/winmain.h
@@ -30,7 +30,8 @@ enum input_event
INPUT_EVENT_RAWINPUT,
INPUT_EVENT_ARRIVAL,
INPUT_EVENT_REMOVAL,
- INPUT_EVENT_MOUSE_BUTTON
+ INPUT_EVENT_MOUSE_BUTTON,
+ INPUT_EVENT_MOUSE_WHEEL
};
struct KeyPressEventArgs
@@ -40,10 +41,12 @@ struct KeyPressEventArgs
uint8_t scancode;
};
-struct MouseButtonEventArgs
+struct MouseUpdateEventArgs
{
- int button;
- int keydown;
+ unsigned pressed;
+ unsigned released;
+ int vdelta;
+ int hdelta;
int xpos;
int ypos;
};
@@ -77,7 +80,7 @@ public:
void extract_video_config();
// windows OSD specific
- bool handle_input_event(input_event eventid, void *eventdata) const;
+ bool handle_input_event(input_event eventid, const void *eventdata) const;
bool should_hide_mouse() const;
virtual bool has_focus() const override;
@@ -89,10 +92,6 @@ public:
using osd_common_t::poll_input_modules; // Win32 debugger calls this directly, which it shouldn't
-protected:
- virtual void build_slider_list() override;
- virtual void update_slider_list() override;
-
private:
void process_events(bool ingame, bool nodispatch);
virtual void osd_exit() override;