Browse Source

Platform: handle (multi-)touch events in Sdl2Application.

This makes 2.0.6 as the oldest supported because in older versions it's
not possible to disable touch to mouse event translation, and it'd be
too annoying to have it special-cased there. The version bump should be
fine as Ubuntu 18.04 has 2.0.8.
pull/651/head
Vladimír Vondruš 2 years ago
parent
commit
a76b7646f0
  1. 4
      doc/changelog.dox
  2. 9
      package/ci/appveyor-rt.bat
  3. 158
      src/Magnum/Platform/Sdl2Application.cpp
  4. 220
      src/Magnum/Platform/Sdl2Application.h
  5. 27
      src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp

4
doc/changelog.dox

@ -337,6 +337,10 @@ See also:
@relativeref{Platform::GlfwApplication,tickEvent()} to match the interface @relativeref{Platform::GlfwApplication,tickEvent()} to match the interface
of @ref Platform::Sdl2Application (see [mosra/magnum#577](https://github.com/mosra/magnum/issues/577) of @ref Platform::Sdl2Application (see [mosra/magnum#577](https://github.com/mosra/magnum/issues/577)
and [mosra/magnum#580](https://github.com/mosra/magnum/pull/580)) and [mosra/magnum#580](https://github.com/mosra/magnum/pull/580))
- Multi-touch support in @ref Platform::Sdl2Application through new
@relativeref{Platform::Sdl2Application,PointerEvent} and
@relativeref{Platform::Sdl2Application,PointerMoveEvent} that unify mouse
and touch input events
@subsubsection changelog-latest-new-scenegraph SceneGraph library @subsubsection changelog-latest-new-scenegraph SceneGraph library

9
package/ci/appveyor-rt.bat

@ -7,10 +7,11 @@ rem hell breaks loose. Thus also not passing CORRADE_RC_EXECUTABLE anywhere
rem below to ensure this doesn't regress. rem below to ensure this doesn't regress.
set PATH=%APPVEYOR_BUILD_FOLDER%\deps-native\bin;%PATH% set PATH=%APPVEYOR_BUILD_FOLDER%\deps-native\bin;%PATH%
rem Build SDL rem Build SDL, 2.0.6 is the oldest release that has SDL_HINT_TOUCH_MOUSE_EVENTS
appveyor DownloadFile https://www.libsdl.org/release/SDL2-2.0.4.zip || exit /b rem https://github.com/libsdl-org/SDL/commit/56cab6d45280fbb4b645083eceeaa8f474c0aac3
7z x SDL2-2.0.4.zip || exit /b appveyor DownloadFile https://www.libsdl.org/release/SDL2-2.0.6.zip || exit /b
ren SDL2-2.0.4 SDL || exit /b 7z x SDL2-2.0.6.zip || exit /b
ren SDL2-2.0.6 SDL || exit /b
cd SDL/VisualC-WinRT/UWP_VS2015 || exit/b cd SDL/VisualC-WinRT/UWP_VS2015 || exit/b
msbuild /p:Configuration=Release || exit /b msbuild /p:Configuration=Release || exit /b
cd ..\..\.. cd ..\..\..

158
src/Magnum/Platform/Sdl2Application.cpp

@ -144,6 +144,39 @@ Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT):
.parse(arguments.argc, arguments.argv); .parse(arguments.argc, arguments.argv);
#endif #endif
#ifndef CORRADE_TARGET_EMSCRIPTEN
/* Disable translation of touch events to mouse events and vice versa as
that's a very poor way of freeing users from having to implement
separate event handling for mouse and touch (and, in SDL3, pen). Instead
the Sdl2Application is providing a PointerEvent abstracting all of
those, so no event translation needs to take place anymore.
Though, just for historical records, what is quite funny / strange about
the SDL's translation, is that when the touch goes out of the window,
translated mouse events get clamped to the window size and thus also not
even being reported if the clamped value doesn't change. On the other
hand, with a regular mouse event, if a drag goes out of the window, it's
still reported correctly, with the coordinates being either larger than
the window size or negative. No idea why the SDL touch->mouse emulation
doesn't do this -- maybe because having a touchscreen device with a
window manager is still relatively rare so nobody reported that? Heh.
These enums are not exposed in the minimal Emscripten SDL implementation
which in turn means touch support there isn't implemented, because I
don't want to filter duplicate events by hand. Use EmscriptenApplication
instead, please. */
/* Added in 2.0.6, before it was apparently impossible to turn off the
event translation altogether. I could also make the touch available only
on 2.0.6+, but 2.0.6 is from 2017 and I don't think it makes sense to
bother with support for older versions. Ubuntu 18.04 has 2.0.8.
https://github.com/libsdl-org/SDL/commit/56cab6d45280fbb4b645083eceeaa8f474c0aac3 */
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
/* Added in 2.0.10, before mouse events don't generate touch events
https://github.com/libsdl-org/SDL/commit/e41576188d17fd09c95777d665f6c4532574f8ac */
#ifdef SDL_HINT_MOUSE_TOUCH_EVENTS
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
#endif
#endif
/* Available since 2.0.4, disables interception of SIGINT and SIGTERM so /* Available since 2.0.4, disables interception of SIGINT and SIGTERM so
it's possible to Ctrl-C the application even if exitEvent() doesn't set it's possible to Ctrl-C the application even if exitEvent() doesn't set
event.setAccepted(). */ event.setAccepted(). */
@ -1028,10 +1061,28 @@ bool Sdl2Application::mainLoopIteration() {
if((event.type == SDL_MOUSEBUTTONDOWN && (buttons & ~SDL_BUTTON(event.button.button))) || if((event.type == SDL_MOUSEBUTTONDOWN && (buttons & ~SDL_BUTTON(event.button.button))) ||
(event.type == SDL_MOUSEBUTTONUP && buttons)) { (event.type == SDL_MOUSEBUTTONUP && buttons)) {
Pointers pointers = buttonsToPointers(buttons); Pointers pointers = buttonsToPointers(buttons);
PointerMoveEvent e{event, pointer, pointers, position, {}}; PointerMoveEvent e{event, PointerEventSource::Mouse, pointer, pointers, true,
#ifdef CORRADE_TARGET_EMSCRIPTEN
0,
/* Since 2.0.22, added w/ SDL_HINT_MOUSE_TOUCH_EVENTS */
#elif defined(SDL_MOUSE_TOUCHID)
SDL_MOUSE_TOUCHID,
#else
-1,
#endif
position, {}};
pointerMoveEvent(e); pointerMoveEvent(e);
} else { } else {
PointerEvent e{event, pointer, position PointerEvent e{event, PointerEventSource::Mouse, pointer, true,
#ifdef CORRADE_TARGET_EMSCRIPTEN
0,
/* Since 2.0.22, added w/ SDL_HINT_MOUSE_TOUCH_EVENTS */
#elif defined(SDL_MOUSE_TOUCHID)
SDL_MOUSE_TOUCHID,
#else
-1,
#endif
position
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
, event.button.clicks , event.button.clicks
#endif #endif
@ -1047,12 +1098,95 @@ bool Sdl2Application::mainLoopIteration() {
} break; } break;
case SDL_MOUSEMOTION: { case SDL_MOUSEMOTION: {
PointerMoveEvent e{event, {}, buttonsToPointers(event.motion.state), PointerMoveEvent e{event, PointerEventSource::Mouse, {}, buttonsToPointers(event.motion.state), true,
#ifdef CORRADE_TARGET_EMSCRIPTEN
0,
/* Since 2.0.22, added w/ SDL_HINT_MOUSE_TOUCH_EVENTS */
#elif defined(SDL_MOUSE_TOUCHID)
SDL_MOUSE_TOUCHID,
#else
-1,
#endif
{Float(event.motion.x), Float(event.motion.y)}, {Float(event.motion.x), Float(event.motion.y)},
{Float(event.motion.xrel), Float(event.motion.yrel)}}; {Float(event.motion.xrel), Float(event.motion.yrel)}};
pointerMoveEvent(e); pointerMoveEvent(e);
} break; } break;
#ifndef CORRADE_TARGET_EMSCRIPTEN
case SDL_FINGERDOWN:
case SDL_FINGERUP: {
/* Scale the event from useless [0, 1] to the actual window
size, not sure why is it so weird. Also let's hope the
SDL_GetWindowSize() call isn't too demanding, I don't want
to be caching this value, it's bad enough to have to track
that on Emscripten. */
Vector2i windowSize{NoInit};
SDL_GetWindowSize(_window, &windowSize.x(), &windowSize.y());
/* Update primary finger info. If there's no primary finger yet
and this is the first finger pressed, it becomes the primary
finger. If the primary finger is lifted, no other finger
becomes primary until all others are lifted as well. This
was empirically verified by looking at behavior of a mouse
cursor on a multi-touch screen under X11, it's possible that
other systems do it differently. Also, right now there's an
assumption that there is just one touch device, fingers from
different touch devices would steal the primary bit from
each other on every press. */
bool primary;
if(_primaryFingerId == ~Long{} && event.type == SDL_FINGERDOWN && SDL_GetNumTouchFingers(event.tfinger.touchId) == 1) {
primary = true;
_primaryFingerId = event.tfinger.fingerId;
/* Otherwise, if this is the primary finger, mark it as such */
} else if(_primaryFingerId == event.tfinger.fingerId) {
primary = true;
/* ... but if it's a release, it's no longer primary */
if(event.type == SDL_FINGERUP)
_primaryFingerId = ~Long{};
/* Otherwise this is not the primary finger */
} else primary = false;
/* Make it so that value of 0 is reported as 0 and 1 is
reported as the rightmost / bottommost pixel, i.e. 799 / 599
for 800x600. This matches with what SDL itself does for the
touch event translation. */
const Vector2 scale = Vector2{windowSize - Vector2i{1}};
PointerEvent e{event, PointerEventSource::Touch,
Pointer::Finger, primary, event.tfinger.fingerId,
Vector2{event.tfinger.x, event.tfinger.y}*scale, 1};
event.type == SDL_FINGERDOWN ?
pointerPressEvent(e) : pointerReleaseEvent(e);
} break;
case SDL_FINGERMOTION: {
/* Scale the event from useless [0, 1] to the actual window
size, not sure why is it so weird. Also let's hope the
SDL_GetWindowSize() call isn't too demanding, I don't want
to be caching this value, it's bad enough to have to track
that on Emscripten. */
Vector2i windowSize{NoInit};
SDL_GetWindowSize(_window, &windowSize.x(), &windowSize.y());
/* In this case, it's a primary finger only if it was
registered as such during the last press. If the primary
finger was lifted, no other finger will step into its place
until all others are lifted as well. */
const bool primary = _primaryFingerId == event.tfinger.fingerId;
/* Make it so that value of 0 is reported as 0 and 1 is
reported as the rightmost / bottommost pixel, i.e. 799 / 599
for 800x600. This matches with what SDL itself does for the
touch event translation. */
const Vector2 scale = Vector2{windowSize - Vector2i{1}};
PointerMoveEvent e{event, PointerEventSource::Touch, {},
Pointer::Finger, primary, event.tfinger.fingerId,
Vector2{event.tfinger.x, event.tfinger.y}*scale,
Vector2{event.tfinger.dx, event.tfinger.dy}*scale};
pointerMoveEvent(e);
break;
}
#endif
case SDL_MULTIGESTURE: { case SDL_MULTIGESTURE: {
MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers}; MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers};
multiGestureEvent(e); multiGestureEvent(e);
@ -1284,6 +1418,9 @@ CORRADE_IGNORE_DEPRECATED_PUSH
Sdl2Application::MouseEvent::Button pointerToButton(const Sdl2Application::Pointer pointer) { Sdl2Application::MouseEvent::Button pointerToButton(const Sdl2Application::Pointer pointer) {
switch(pointer) { switch(pointer) {
case Sdl2Application::Pointer::MouseLeft: case Sdl2Application::Pointer::MouseLeft:
#ifndef CORRADE_TARGET_EMSCRIPTEN
case Sdl2Application::Pointer::Finger:
#endif
return Sdl2Application::MouseEvent::Button::Left; return Sdl2Application::MouseEvent::Button::Left;
case Sdl2Application::Pointer::MouseMiddle: case Sdl2Application::Pointer::MouseMiddle:
return Sdl2Application::MouseEvent::Button::Middle; return Sdl2Application::MouseEvent::Button::Middle;
@ -1304,6 +1441,9 @@ CORRADE_IGNORE_DEPRECATED_POP
void Sdl2Application::pointerPressEvent(PointerEvent& event) { void Sdl2Application::pointerPressEvent(PointerEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
if(!event.isPrimary())
return;
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH
MouseEvent mouseEvent{event.event(), pointerToButton(event.pointer()), Vector2i{Math::round(event.position())} MouseEvent mouseEvent{event.event(), pointerToButton(event.pointer()), Vector2i{Math::round(event.position())}
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
@ -1325,6 +1465,9 @@ CORRADE_IGNORE_DEPRECATED_POP
void Sdl2Application::pointerReleaseEvent(PointerEvent& event) { void Sdl2Application::pointerReleaseEvent(PointerEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
if(!event.isPrimary())
return;
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH
MouseEvent mouseEvent{event.event(), pointerToButton(event.pointer()), Vector2i{Math::round(event.position())} MouseEvent mouseEvent{event.event(), pointerToButton(event.pointer()), Vector2i{Math::round(event.position())}
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
@ -1346,6 +1489,9 @@ CORRADE_IGNORE_DEPRECATED_POP
void Sdl2Application::pointerMoveEvent(PointerMoveEvent& event) { void Sdl2Application::pointerMoveEvent(PointerMoveEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
if(!event.isPrimary())
return;
const Vector2i roundedPosition{Math::round(event.position())}; const Vector2i roundedPosition{Math::round(event.position())};
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH
@ -1366,7 +1512,11 @@ void Sdl2Application::pointerMoveEvent(PointerMoveEvent& event) {
mousePressEvent(mouseEvent) : mouseReleaseEvent(mouseEvent); mousePressEvent(mouseEvent) : mouseReleaseEvent(mouseEvent);
} else { } else {
MouseMoveEvent::Buttons buttons; MouseMoveEvent::Buttons buttons;
if(event.pointers() & Pointer::MouseLeft) if(event.pointers() & (Pointer::MouseLeft
#ifndef CORRADE_TARGET_EMSCRIPTEN
|Pointer::Finger
#endif
))
buttons |= MouseMoveEvent::Button::Left; buttons |= MouseMoveEvent::Button::Left;
if(event.pointers() & Pointer::MouseMiddle) if(event.pointers() & Pointer::MouseMiddle)
buttons |= MouseMoveEvent::Button::Middle; buttons |= MouseMoveEvent::Button::Middle;

220
src/Magnum/Platform/Sdl2Application.h

@ -285,6 +285,37 @@ If no other application header is included, this class is also aliased to
@cpp Platform::Application @ce and the macro is aliased to @cpp MAGNUM_APPLICATION_MAIN() @ce @cpp Platform::Application @ce and the macro is aliased to @cpp MAGNUM_APPLICATION_MAIN() @ce
to simplify porting. to simplify porting.
@section Platform-Sdl2Application-touch Touch input
The application recognizes touch input and reports it as @ref Pointer::Finger
and @ref PointerEventSource::Touch. Because both mouse and touch events are
exposed through a unified @ref PointerEvent / @ref PointerMoveEvent interface,
there's no need for compatibility mouse events to be synthesized from touch
events and vice versa, and thus given behavior is disabled in SDL. Pen input is
still reported as a mouse because SDL has dedicated support for pen stylus only
since SDL 3.
In case of a multi-touch scenario, @ref PointerEvent::isPrimary() /
@ref PointerMoveEvent::isPrimary() can be used to distinguish the primary touch
from secondary. For example, if an application doesn't need to recognize
gestures like pinch to zoom or rotate, it can ignore all non-primary pointer
events. @ref PointerEventSource::Mouse events are always marked as primary,
for touch input the first pressed finger is marked as primary and all following
pressed fingers are non-primary. Note that there can be up to one primary
pointer for each pointer event source, e.g. a finger and a mouse press may both
be marked as primary. On the other hand, in a multi-touch scenario, if the
first (and thus primary) finger is lifted, no other finger becomes primary
until all others are lifted as well.
If gesture recognition is desirable, @ref PointerEvent::id() /
@ref PointerMoveEvent::id() contains a pointer ID that's unique among all
pointer event sources, which can be used to track movements of secondary,
tertiary and further touch points. The ID allocation is platform-specific and
you can't rely on it to be contiguous or in any bounded range --- for example,
each new touch may generate a new ID that's only used until given finger is
lifted, and then never again. For @ref PointerEventSource::Mouse the ID is a
constant, as there's always just a single mouse cursor.
@section Platform-Sdl2Application-platform-specific Platform-specific behavior @section Platform-Sdl2Application-platform-specific Platform-specific behavior
@subsection Platform-Sdl2Application-platform-specific-power Power management @subsection Platform-Sdl2Application-platform-specific-power Power management
@ -514,6 +545,7 @@ class Sdl2Application {
/* The damn thing cannot handle forward enum declarations */ /* The damn thing cannot handle forward enum declarations */
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT
enum class PointerEventSource: UnsignedByte;
enum class Pointer: UnsignedByte; enum class Pointer: UnsignedByte;
#endif #endif
@ -1103,15 +1135,16 @@ class Sdl2Application {
* @brief Pointer press event * @brief Pointer press event
* @m_since_latest * @m_since_latest
* *
* Called when a mouse is pressed. Note that if at least one mouse * Called when either a mouse or a finger is pressed. Note that if at
* button is already pressed and another button gets pressed in * least one mouse button is already pressed and another button gets
* addition, @ref pointerMoveEvent() with the new combination is * pressed in addition, @ref pointerMoveEvent() with the new
* called, not this function. * combination is called, not this function.
* *
* On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default * On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, if the pointer
* implementation delegates to @ref mousePressEvent(). On builds with * is primary, default implementation delegates to
* deprecated functionality disabled, default implementation does * @ref mousePressEvent(), interpreting @ref Pointer::Finger as
* nothing. * @ref MouseEvent::Button::Left. On builds with deprecated
* functionality disabled, default implementation does nothing.
*/ */
virtual void pointerPressEvent(PointerEvent& event); virtual void pointerPressEvent(PointerEvent& event);
@ -1131,14 +1164,16 @@ class Sdl2Application {
* @brief Pointer release event * @brief Pointer release event
* @m_since_latest * @m_since_latest
* *
* Called when a mouse is released. Note that if multiple mouse buttons * Called when either a mouse or a finger is released. Note that if
* are pressed and one of these is released, @ref pointerMoveEvent() * multiple mouse buttons are pressed and one of these is released,
* with the new combination is called, not this function. * @ref pointerMoveEvent() with the new combination is called, not this
* function.
* *
* On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default * On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, if the pointer
* implementation delegates to @ref mouseReleaseEvent(). On builds with * is primary, default implementation delegates to
* deprecated functionality disabled, default implementation does * @ref mouseReleaseEvent(), interpreting @ref Pointer::Finger as
* nothing. * @ref MouseEvent::Button::Left. On builds with deprecated
* functionality disabled, default implementation does nothing.
*/ */
virtual void pointerReleaseEvent(PointerEvent& event); virtual void pointerReleaseEvent(PointerEvent& event);
@ -1162,13 +1197,14 @@ class Sdl2Application {
* changes its properties. Gets called also if the set of pressed mouse * changes its properties. Gets called also if the set of pressed mouse
* buttons changes. * buttons changes.
* *
* On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default * On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, if the pointer
* implementation delegates to @ref mouseMoveEvent(), or if * is primary, default implementation delegates to
* @ref PointerMoveEvent::pointer() is not * @ref mouseMoveEvent(), or if @ref PointerMoveEvent::pointer() is not
* @relativeref{Corrade,Containers::NullOpt}, to either * @relativeref{Corrade,Containers::NullOpt}, to either
* @ref mousePressEvent() or @ref mouseReleaseEvent(). On builds with * @ref mousePressEvent() or @ref mouseReleaseEvent().
* deprecated functionality disabled, default implementation does * @ref Pointer::Finger is interpreted as @ref MouseEvent::Button::Left.
* nothing. * On builds with deprecated functionality disabled, default
* implementation does nothing.
*/ */
virtual void pointerMoveEvent(PointerMoveEvent& event); virtual void pointerMoveEvent(PointerMoveEvent& event);
@ -1368,6 +1404,7 @@ class Sdl2Application {
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_Window* _window{}; SDL_Window* _window{};
Long _primaryFingerId = ~Long{};
/* Not using Nanoseconds as that would require including Time.h */ /* Not using Nanoseconds as that would require including Time.h */
UnsignedInt _minimalLoopPeriodMilliseconds{}; UnsignedInt _minimalLoopPeriodMilliseconds{};
#else #else
@ -1390,6 +1427,36 @@ class Sdl2Application {
int _exitCode = 0; int _exitCode = 0;
}; };
/**
@brief Pointer event source
@m_since_latest
@see @ref PointerEvent::source(), @ref PointerMoveEvent::source()
*/
enum class Sdl2Application::PointerEventSource: UnsignedByte {
/**
* The event is coming from a mouse. Corresponds to the
* `SDL_MOUSEBUTTONDOWN`, `SDL_MOUSEBUTTONUP` and `SDL_MOUSEMOTION` events.
* @see @ref Pointer::MouseLeft, @ref Pointer::MouseMiddle,
* @ref Pointer::MouseRight, @ref Pointer::MouseButton4,
* @ref Pointer::MouseButton5
*/
Mouse,
#ifndef CORRADE_TARGET_EMSCRIPTEN
/**
* The event is coming from a touch contact. Corresponds to the
* `SDL_FINGERDOWN`, `SDL_FINGERUP` and `SDL_FINGERMOTION` events.
* @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" as
* the minimal SDL2 implementation there doesn't expose a way to
* disable touch to mouse event translation, which would lead to
* duplicate events being emitted.
* @see @ref Pointer::Finger
*/
Touch
#endif
};
/** /**
@brief Pointer type @brief Pointer type
@m_since_latest @m_since_latest
@ -1401,32 +1468,51 @@ enum class Sdl2Application::Pointer: UnsignedByte {
/** /**
* Left mouse button. Corresponds to `SDL_BUTTON_LEFT` / * Left mouse button. Corresponds to `SDL_BUTTON_LEFT` /
* `SDL_BUTTON_LMASK`. * `SDL_BUTTON_LMASK`.
* @see @ref PointerEventSource::Mouse
*/ */
MouseLeft = 1 << 0, MouseLeft = 1 << 0,
/** /**
* Middle mouse button. Corresponds to `SDL_BUTTON_MIDDLE` / * Middle mouse button. Corresponds to `SDL_BUTTON_MIDDLE` /
* `SDL_BUTTON_MMASK`. * `SDL_BUTTON_MMASK`.
* @see @ref PointerEventSource::Mouse
*/ */
MouseMiddle = 1 << 1, MouseMiddle = 1 << 1,
/** /**
* Right mouse button. Corresponds to `SDL_BUTTON_RIGHT` / * Right mouse button. Corresponds to `SDL_BUTTON_RIGHT` /
* `SDL_BUTTON_RMASK`. * `SDL_BUTTON_RMASK`.
* @see @ref PointerEventSource::Mouse
*/ */
MouseRight = 1 << 2, MouseRight = 1 << 2,
/** /**
* Fourth mouse button, such as wheel left. Corresponds to `SDL_BUTTON_X1` * Fourth mouse button, such as wheel left. Corresponds to `SDL_BUTTON_X1`
* / `SDL_BUTTON_X1MASK`. * / `SDL_BUTTON_X1MASK`.
* @see @ref PointerEventSource::Mouse
*/ */
MouseButton4 = 1 << 3, MouseButton4 = 1 << 3,
/** /**
* Fifth mouse button, such as wheel right. Corresponds to `SDL_BUTTON_X2` * Fifth mouse button, such as wheel right. Corresponds to `SDL_BUTTON_X2`
* / `SDL_BUTTON_X2MASK`. * / `SDL_BUTTON_X2MASK`.
* @see @ref PointerEventSource::Mouse
*/ */
MouseButton5 = 1 << 4, MouseButton5 = 1 << 4,
#ifndef CORRADE_TARGET_EMSCRIPTEN
/**
* Finger
* @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" as
* the minimal SDL2 implementation there doesn't expose a way to
* disable touch to mouse event translation, which would lead to
* duplicate events being emitted.
* @see @ref PointerEventSource::Touch
*/
Finger = 1 << 5,
#endif
/* Pen support is since SDL3 only */
}; };
CORRADE_ENUMSET_OPERATORS(Sdl2Application::Pointers) CORRADE_ENUMSET_OPERATORS(Sdl2Application::Pointers)
@ -2382,9 +2468,10 @@ class Sdl2Application::InputEvent {
* @brief Underlying SDL event * @brief Underlying SDL event
* *
* Of type `SDL_KEYDOWN` / `SDL_KEYUP` for @ref KeyEvent, * Of type `SDL_KEYDOWN` / `SDL_KEYUP` for @ref KeyEvent,
* `SDL_MOUSEBUTTONDOWN` / `SDL_MOUSEBUTTONUP` for @ref PointerEvent, * `SDL_MOUSEBUTTONDOWN` / `SDL_MOUSEBUTTONUP` or `SDL_FINGERDOWN` /
* `SDL_MOUSEMOTION` for @ref PointerMoveEvent and `SDL_MOUSEWHEEL` for * `SDL_FINGERUP` for @ref PointerEvent, `SDL_MOUSEMOTION` or
* @ref ScrollEvent. * `SDL_FINGERMOTION` for @ref PointerMoveEvent and `SDL_MOUSEWHEEL`
* for @ref ScrollEvent.
* @see @ref Sdl2Application::anyEvent() * @see @ref Sdl2Application::anyEvent()
*/ */
const SDL_Event& event() const { return _event; } const SDL_Event& event() const { return _event; }
@ -2746,13 +2833,39 @@ class Sdl2Application::PointerEvent: public InputEvent {
/** @brief Moving is not allowed */ /** @brief Moving is not allowed */
PointerEvent& operator=(PointerEvent&&) = delete; PointerEvent& operator=(PointerEvent&&) = delete;
/** @brief Pointer event source */
PointerEventSource source() const { return _source; }
/** @brief Pointer type that was pressed or released */ /** @brief Pointer type that was pressed or released */
Pointer pointer() const { return _pointer; } Pointer pointer() const { return _pointer; }
/**
* @brief Whether the pointer is primary
*
* Useful to distinguish among multiple pointers in a multi-touch
* scenario. See @ref Platform-Sdl2Application-touch for more
* information.
*/
bool isPrimary() const { return _primary; }
/**
* @brief Pointer ID
*
* Useful to distinguish among multiple pointers in a multi-touch
* scenario. See @ref Platform-Sdl2Application-touch for more
* information.
*/
Long id() const { return _id; }
/** /**
* @brief Position * @brief Position
* *
* For mouse input the position is always reported in whole pixels. * For mouse input the position is always reported in whole pixels. For
* @ref Pointer::Finger the events may be reported with a subpixel
* precision, use @ref Math::round() to snap them to the nearest pixel.
* Note that, unlike the `SDL_TouchFingerEvent`, which is normalized in
* the @f$ [0, 1] @f$ range, the position for touch events is in the
* same coordinate system as mouse events.
*/ */
Vector2 position() const { return _position; } Vector2 position() const { return _position; }
@ -2760,6 +2873,7 @@ class Sdl2Application::PointerEvent: public InputEvent {
/** /**
* @brief Click count * @brief Click count
* *
* For @ref Pointer::Finger is always @cpp 1 @ce.
* @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten".
*/ */
Int clickCount() const { return _clickCount; } Int clickCount() const { return _clickCount; }
@ -2775,18 +2889,21 @@ class Sdl2Application::PointerEvent: public InputEvent {
private: private:
friend Sdl2Application; friend Sdl2Application;
explicit PointerEvent(const SDL_Event& event, Pointer pointer, const Vector2& position explicit PointerEvent(const SDL_Event& event, PointerEventSource source, Pointer pointer, bool primary, Long id, const Vector2& position
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
, Int clickCount , Int clickCount
#endif #endif
): InputEvent{event}, _pointer(pointer), _position{position} ): InputEvent{event}, _source{source}, _pointer{pointer}, _primary{primary}, _id{id}, _position{position}
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
, _clickCount{clickCount} , _clickCount{clickCount}
#endif #endif
{} {}
const PointerEventSource _source;
const Pointer _pointer; const Pointer _pointer;
Containers::Optional<Modifiers> _modifiers; Containers::Optional<Modifiers> _modifiers;
const bool _primary;
const Long _id;
const Vector2 _position; const Vector2 _position;
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
const Int _clickCount; const Int _clickCount;
@ -2886,6 +3003,15 @@ class Sdl2Application::PointerMoveEvent: public InputEvent {
/** @brief Moving is not allowed */ /** @brief Moving is not allowed */
PointerMoveEvent& operator=(PointerMoveEvent&&) = delete; PointerMoveEvent& operator=(PointerMoveEvent&&) = delete;
/**
* @brief Pointer event source
*
* Can be used to distinguish which source the event is coming from in
* case it's a movement with both @ref pointer() and @ref pointers()
* being empty.
*/
PointerEventSource source() const { return _source; }
/** /**
* @brief Pointer type that was added or removed from the set of pressed pointers * @brief Pointer type that was added or removed from the set of pressed pointers
* *
@ -2894,6 +3020,7 @@ class Sdl2Application::PointerMoveEvent: public InputEvent {
* pressed buttons was released. If non-empty and @ref pointers() don't * pressed buttons was released. If non-empty and @ref pointers() don't
* contain given @ref Pointer value, the button was released, if they * contain given @ref Pointer value, the button was released, if they
* contain given value, the button was pressed. * contain given value, the button was pressed.
* @see @ref source()
*/ */
Containers::Optional<Pointer> pointer() const { return _pointer; } Containers::Optional<Pointer> pointer() const { return _pointer; }
@ -2906,17 +3033,45 @@ class Sdl2Application::PointerMoveEvent: public InputEvent {
*/ */
Pointers pointers() const { return _pointers; } Pointers pointers() const { return _pointers; }
/**
* @brief Whether the pointer is primary
*
* Useful to distinguish among multiple pointers in a multi-touch
* scenario. See @ref Platform-Sdl2Application-touch for more
* information.
*/
bool isPrimary() const { return _primary; }
/**
* @brief Pointer ID
*
* Useful to distinguish among multiple pointers in a multi-touch
* scenario. See @ref Platform-Sdl2Application-touch for more
* information.
*/
Long id() const { return _id; }
/** /**
* @brief Position * @brief Position
* *
* For mouse input the position is always reported in whole pixels. * For mouse input the position is always reported in whole pixels. For
* @ref Pointer::Finger the events may be reported with a subpixel
* precision. Use @ref Math::round() to snap them to the nearest pixel.
* Note that, unlike the `SDL_TouchFingerEvent`, which is normalized in
* the @f$ [0, 1] @f$ range, the position for touch events is in the
* same coordinate system as mouse events.
*/ */
Vector2 position() const { return _position; } Vector2 position() const { return _position; }
/** /**
* @brief Position relative to the previous touch event * @brief Position relative to the previous touch event
* *
* For mouse input the position is always reported in whole pixels. * For mouse input the position is always reported in whole pixels. For
* @ref Pointer::Finger the events may be reported with a subpixel
* precision, meaning that the relative position might be less than a
* pixel. Note that, unlike the `SDL_TouchFingerEvent`, which is
* normalized in the @f$ [0, 1] @f$ range, the position for touch
* events is in the same coordinate system as mouse events.
*/ */
Vector2 relativePosition() const { return _relativePosition; } Vector2 relativePosition() const { return _relativePosition; }
@ -2930,12 +3085,15 @@ class Sdl2Application::PointerMoveEvent: public InputEvent {
private: private:
friend Sdl2Application; friend Sdl2Application;
explicit PointerMoveEvent(const SDL_Event& event, Containers::Optional<Pointer> pointer, Pointers pointers, const Vector2& position, const Vector2& relativePosition): InputEvent{event}, _pointer{pointer}, _pointers{pointers}, _position{position}, _relativePosition{relativePosition} {} explicit PointerMoveEvent(const SDL_Event& event, PointerEventSource source, Containers::Optional<Pointer> pointer, Pointers pointers, bool primary, Long id, const Vector2& position, const Vector2& relativePosition): InputEvent{event}, _source{source}, _pointer{pointer}, _pointers{pointers}, _primary{primary}, _id{id}, _position{position}, _relativePosition{relativePosition} {}
const PointerEventSource _source;
const Containers::Optional<Pointer> _pointer; const Containers::Optional<Pointer> _pointer;
const Pointers _pointers; const Pointers _pointers;
const Vector2 _position, _relativePosition; const bool _primary;
Containers::Optional<Modifiers> _modifiers; Containers::Optional<Modifiers> _modifiers;
const Long _id;
const Vector2 _position, _relativePosition;
}; };
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED

27
src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp

@ -87,6 +87,9 @@ static Debug& operator<<(Debug& debug, Application::Pointer value) {
_c(MouseRight) _c(MouseRight)
_c(MouseButton4) _c(MouseButton4)
_c(MouseButton5) _c(MouseButton5)
#ifndef CORRADE_TARGET_EMSCRIPTEN
_c(Finger)
#endif
#undef _c #undef _c
} }
@ -115,6 +118,21 @@ CORRADE_IGNORE_DEPRECATED_POP
namespace Test { namespace { namespace Test { namespace {
static Debug& operator<<(Debug& debug, Application::PointerEventSource value) {
debug << "PointerEventSource" << Debug::nospace;
switch(value) {
#define _c(value) case Application::PointerEventSource::value: return debug << "::" #value;
_c(Mouse)
#ifndef CORRADE_TARGET_EMSCRIPTEN
_c(Touch)
#endif
#undef _c
}
return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")";
}
Debug& operator<<(Debug& debug, Application::InputEvent::Modifiers value) { Debug& operator<<(Debug& debug, Application::InputEvent::Modifiers value) {
return Containers::enumSetDebugOutput(debug, value, "Modifiers{}", { return Containers::enumSetDebugOutput(debug, value, "Modifiers{}", {
Application::InputEvent::Modifier::Shift, Application::InputEvent::Modifier::Shift,
@ -134,6 +152,9 @@ Debug& operator<<(Debug& debug, Application::Pointers value) {
Application::Pointer::MouseRight, Application::Pointer::MouseRight,
Application::Pointer::MouseButton4, Application::Pointer::MouseButton4,
Application::Pointer::MouseButton5, Application::Pointer::MouseButton5,
#ifndef CORRADE_TARGET_EMSCRIPTEN
Application::Pointer::Finger,
#endif
}); });
} }
@ -322,17 +343,17 @@ struct Sdl2ApplicationTest: Platform::Application {
/* Set to 0 to test the deprecated mouse events instead */ /* Set to 0 to test the deprecated mouse events instead */
#if 1 #if 1
void pointerPressEvent(PointerEvent& event) override { void pointerPressEvent(PointerEvent& event) override {
Debug{} << "pointer press:" << event.pointer() << event.modifiers() << Debug::packed << event.position(); Debug{} << "pointer press:" << event.source() << event.pointer() << (event.isPrimary() ? "primary" : "secondary") << event.id() << event.modifiers() << Debug::packed << event.position();
_gestureDistance = {}; _gestureDistance = {};
_gestureRotation = {}; _gestureRotation = {};
} }
void pointerReleaseEvent(PointerEvent& event) override { void pointerReleaseEvent(PointerEvent& event) override {
Debug{} << "pointer release:" << event.pointer() << event.modifiers() << Debug::packed << event.position(); Debug{} << "pointer release:" << event.source() << event.pointer() << (event.isPrimary() ? "primary" : "secondary") << event.id() << event.modifiers() << Debug::packed << event.position();
_gestureDistance = {}; _gestureDistance = {};
_gestureRotation = {}; _gestureRotation = {};
} }
void pointerMoveEvent(PointerMoveEvent& event) override { void pointerMoveEvent(PointerMoveEvent& event) override {
Debug{} << "pointer move:" << event.pointer() << event.pointers() << event.modifiers() << Debug::packed << event.position() << Debug::packed << event.relativePosition(); Debug{} << "pointer move:" << event.source() << event.pointer() << event.pointers() << (event.isPrimary() ? "primary" : "secondary") << event.id() << event.modifiers() << Debug::packed << event.position() << Debug::packed << event.relativePosition();
} }
void scrollEvent(ScrollEvent& event) override { void scrollEvent(ScrollEvent& event) override {
Debug{} << "scroll:" << event.modifiers() << Debug::packed << event.offset() << Debug::packed << event.position(); Debug{} << "scroll:" << event.modifiers() << Debug::packed << event.offset() << Debug::packed << event.position();

Loading…
Cancel
Save