diff options
Diffstat (limited to 'src/osd/modules/osdwindow.h')
-rw-r--r-- | src/osd/modules/osdwindow.h | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/osd/modules/osdwindow.h b/src/osd/modules/osdwindow.h index f3d39878b5e..9167a775682 100644 --- a/src/osd/modules/osdwindow.h +++ b/src/osd/modules/osdwindow.h @@ -15,6 +15,8 @@ #include "osdhelper.h" #include "../frontend/mame/ui/menuitem.h" +#include <cassert> +#include <chrono> #include <memory> #include <string> #include <vector> @@ -94,6 +96,65 @@ public: virtual void complete_destroy() = 0; protected: + static inline constexpr int CLICK_DISTANCE = 16; // in pointer units squared + static inline constexpr int TAP_DISTANCE = 49; // taps are less repeatable than clicks + + struct prev_touch + { + prev_touch() = default; + prev_touch(prev_touch const &) = default; + prev_touch(prev_touch &&) = default; + prev_touch &operator=(prev_touch const &) = default; + prev_touch &operator=(prev_touch &&) = default; + + std::chrono::steady_clock::time_point when = std::chrono::steady_clock::time_point::min(); + int x = 0, y = 0, cnt = 0; + }; + + struct pointer_dev_info + { + pointer_dev_info() = default; + pointer_dev_info(pointer_dev_info const &) = default; + pointer_dev_info(pointer_dev_info &&) = default; + pointer_dev_info &operator=(pointer_dev_info const &) = default; + pointer_dev_info &operator=(pointer_dev_info &&) = default; + + unsigned clear_expired_touches(std::chrono::steady_clock::time_point const &now, std::chrono::steady_clock::duration const &time); + unsigned consume_touch(unsigned i); + + prev_touch touches[8]; + }; + + struct pointer_info + { + pointer_info(pointer_info const &) = default; + pointer_info(pointer_info &&) = default; + pointer_info &operator=(pointer_info const &) = default; + pointer_info &operator=(pointer_info &&) = default; + + pointer_info(unsigned i, unsigned d); + + void primary_down( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance, + bool checkprev, + std::vector<pointer_dev_info> &devices); + void check_primary_hold_drag( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance); + + std::chrono::steady_clock::time_point pressed; + unsigned index, device; + int x, y; + unsigned buttons; + int pressedx, pressedy; + int clickcnt; + }; + osd_window( running_machine &machine, render_module &renderprovider, @@ -127,6 +188,140 @@ private: const std::string m_title; }; + +inline unsigned osd_window::pointer_dev_info::clear_expired_touches( + std::chrono::steady_clock::time_point const &now, + std::chrono::steady_clock::duration const &time) +{ + // find first non-expired touch (if any) + unsigned end(0); + while ((std::size(touches) > end) && touches[end].cnt && ((touches[end].when + time) < now)) + touches[end++].cnt = 0; + + // shift non-expired touches back if necessary + if (end) + { + unsigned pos(0); + while ((std::size(touches) > end) && touches[end].cnt) + { + touches[pos++] = std::move(touches[end]); + touches[end++].cnt = 0; + } + return pos; + } + else + { + while ((std::size(touches) > end) && touches[end].cnt) + ++end; + return end; + } +} + +inline unsigned osd_window::pointer_dev_info::consume_touch(unsigned i) +{ + assert(std::size(touches) > i); + touches[i].cnt = 0; + unsigned pos(i + 1); + while ((std::size(touches) > pos) && touches[pos].cnt) + { + touches[i++] = std::move(touches[pos]); + touches[pos++].cnt = 0; + } + return i; +} + +inline osd_window::pointer_info::pointer_info(unsigned i, unsigned d) : + pressed(std::chrono::steady_clock::time_point::min()), + index(i), + device(d), + x(-1), + y(-1), + buttons(0), + pressedx(0), + pressedy(0), + clickcnt(0) +{ +} + +inline void osd_window::pointer_info::primary_down( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance, + bool checkprev, + std::vector<pointer_dev_info> &devices) +{ + auto const now(std::chrono::steady_clock::now()); + auto const exp(time + pressed); + if (0 > clickcnt) + { + // previous click turned into a hold/drag + clickcnt = 1; + } + else if (clickcnt) + { + // potential multi-click action + int const dx(cx - pressedx); + int const dy(cy - pressedy); + int const distance((dx * dx) + (dy * dy)); + if ((exp < now) || (tolerance < distance)) + clickcnt = 1; + else + ++clickcnt; + } + else + { + // first click for this pointer, but may need to check previous touches + clickcnt = 1; + if (checkprev) + { + if (devices.size() > device) + { + auto &devinfo(devices[device]); + unsigned const end(devinfo.clear_expired_touches(now, time)); + for (int i = 0; end > i; ++i) + { + int const dx(cx - devinfo.touches[i].x); + int const dy(cy - devinfo.touches[i].y); + int const distance((dx * dx) + (dy * dy)); + if (tolerance >= distance) + { + // close match - consume it + clickcnt = devinfo.touches[i].cnt + 1; + devinfo.consume_touch(i); + break; + } + } + } + } + } + + // record the time/location where the pointer was pressed + pressed = now; + pressedx = cx; + pressedy = cy; +} + +inline void osd_window::pointer_info::check_primary_hold_drag( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance) +{ + // check for conversion to a (multi-)click-and-hold/drag + if (0 < clickcnt) + { + auto const now(std::chrono::steady_clock::now()); + auto const exp(time + pressed); + int const dx(cx - pressedx); + int const dy(cy - pressedy); + int const distance((dx * dx) + (dy * dy)); + if ((exp < now) || (tolerance < distance)) + clickcnt = -clickcnt; + } +} + + template <class TWindowHandle> class osd_window_t : public osd_window { |