summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/osdwindow.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/modules/osdwindow.h')
-rw-r--r--src/osd/modules/osdwindow.h195
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
{