Browse Source

Platform: replace mouse events with pointer events in AndroidApp.

And thus also add actual support for finger and pen input, instead of
reporting them as Button::None. Something that needed fixing ever since
the initial implementation in 2014.

I *really* wanted to make a setup where mouse input would be recognized
as such and reported in the app, but wasted the whole day on that and got
only as far as having it recognized as a stylus input with hover (!!!),
when using some Android Desktop image. Not sure if that's some stupid
mislabeling (because middle and right mouse buttons are reported as such)
or it's just the emulation layer being crap.

While at it, I at least added support for hover events. I still have to
document all the newly found warts and hard-to-remember workflows with
getting a simulator running.
pull/651/head
Vladimír Vondruš 2 years ago
parent
commit
b0abae88d1
  1. 3
      doc/changelog.dox
  2. 258
      src/Magnum/Platform/AndroidApplication.cpp
  3. 292
      src/Magnum/Platform/AndroidApplication.h
  4. 66
      src/Magnum/Platform/Test/AndroidApplicationTest.cpp

3
doc/changelog.dox

@ -1399,7 +1399,8 @@ See also:
@relativeref{Platform::Sdl2Application,PointerMoveEvent} APIs that provide
a better abstraction over general pointer input, not just a mouse alone.
The same change is done in @ref Platform::AbstractXApplication,
@ref Platform::EmscriptenApplication and @ref Platform::GlfwApplication.
@ref Platform::AndroidApplication, @ref Platform::EmscriptenApplication and
@ref Platform::GlfwApplication.
- @cpp Platform::AbstractXApplication::MouseEvent::Button::WheelUp @ce and
`WheelDown` members are deprecated in favor of a dedicated
@ref Platform::AbstractXApplication::mouseScrollEvent(). Similar change was

258
src/Magnum/Platform/AndroidApplication.cpp

@ -31,6 +31,7 @@
#include <android_native_app_glue.h>
#include "Magnum/GL/Version.h"
#include "Magnum/Math/Functions.h"
#include "Magnum/Platform/ScreenedApplication.hpp"
#include "Implementation/Egl.h"
@ -188,9 +189,6 @@ void AndroidApplication::redraw() {
}
void AndroidApplication::viewportEvent(ViewportEvent&) {}
void AndroidApplication::mousePressEvent(MouseEvent&) {}
void AndroidApplication::mouseReleaseEvent(MouseEvent&) {}
void AndroidApplication::mouseMoveEvent(MouseMoveEvent&) {}
namespace {
struct Data {
@ -241,6 +239,47 @@ void AndroidApplication::commandEvent(android_app* state, int32_t cmd) {
}
}
namespace {
AndroidApplication::Pointers motionEventButtons(AInputEvent* event) {
const std::int32_t buttons = AMotionEvent_getButtonState(event);
AndroidApplication::Pointers pointers;
if(buttons & AMOTION_EVENT_BUTTON_PRIMARY)
pointers |= AndroidApplication::Pointer::MouseLeft;
if(buttons & AMOTION_EVENT_BUTTON_TERTIARY)
pointers |= AndroidApplication::Pointer::MouseMiddle;
if(buttons & AMOTION_EVENT_BUTTON_SECONDARY)
pointers |= AndroidApplication::Pointer::MouseRight;
/** @todo AMOTION_EVENT_BUTTON_BACK, AMOTION_EVENT_BUTTON_FORWARD once it's
possible to verify they match MouseButton4 / MouseButton5 in
GlfwApplication and Sdl2Application */
return pointers;
}
AndroidApplication::Pointers motionEventPointers(AInputEvent* event, const AndroidApplication::Pointers pressedButtons) {
switch(AMotionEvent_getToolType(event, 0)) {
case AMOTION_EVENT_TOOL_TYPE_MOUSE:
/** @todo MouseButton4 / MouseButton5, once they're added &
tested */
return (AndroidApplication::Pointer::MouseLeft|
AndroidApplication::Pointer::MouseMiddle|
AndroidApplication::Pointer::MouseRight) & pressedButtons;
case AMOTION_EVENT_TOOL_TYPE_FINGER:
return AndroidApplication::Pointer::Finger;
case AMOTION_EVENT_TOOL_TYPE_STYLUS:
/** @todo use pressedButtonsPointers once there's additional pen
button enum values */
return AndroidApplication::Pointer::Pen;
case AMOTION_EVENT_TOOL_TYPE_ERASER:
return AndroidApplication::Pointer::Eraser;
case AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
default:
return AndroidApplication::Pointer::Unknown;
}
}
}
std::int32_t AndroidApplication::inputEvent(android_app* state, AInputEvent* event) {
CORRADE_INTERNAL_ASSERT(static_cast<Data*>(state->userData)->instance);
AndroidApplication& app = *static_cast<Data*>(state->userData)->instance;
@ -249,24 +288,132 @@ std::int32_t AndroidApplication::inputEvent(android_app* state, AInputEvent* eve
switch(action) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP: {
/* On a touch screen move events aren't reported when the
finger is moving above (of course), so remember the position
always */
app._previousMouseMovePosition = {Int(AMotionEvent_getX(event, 0)), Int(AMotionEvent_getY(event, 0))};
MouseEvent e(event);
action == AMOTION_EVENT_ACTION_DOWN ? app.mousePressEvent(e) : app.mouseReleaseEvent(e);
return e.isAccepted() ? 1 : 0;
const Vector2 position{AMotionEvent_getX(event, 0),
AMotionEvent_getY(event, 0)};
/* Query the currently pressed buttons. If this is not a mouse
event, it'll give back garbage, but that's fine as we won't
use it in that case. Then, based on whether it's a press or
a release, use the previously recorded pointers to figure
out what was actually pressed. */
const Pointers pressedButtons = motionEventButtons(event);
const Pointers pointers = motionEventPointers(event,
action == AMOTION_EVENT_ACTION_DOWN ?
pressedButtons & ~app._previousPressedButtons :
~pressedButtons & app._previousPressedButtons);
/* The expectation is that the difference betweeen the
previously recorded set of pointers and current one will be
exactly one bit for a pointer type that got either pressed
or released. If it's not, it means we lost some events, and
until API 33+ and AMotionEvent_getActionButton() on
AMOTION_EVENT_BUTTON_PRESS / AMOTION_EVENT_BUTTON_RELEASE,
there's no way to reliably know what concrete mouse / pen
button caused the event. */
Pointer pointer;
/* http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 */
if(pointers && !(UnsignedByte(pointers) & (UnsignedByte(pointers) - 1)))
pointer = Pointer(UnsignedByte(pointers));
else
pointer = Pointer::Unknown;
/** @todo Once there's an ability to actually *know* what
button was pressed or released (API 33+), implement
translation to move events like in GlfwApplication,
Sdl2Application and EmscriptenApplication. With my emulator
testing, where a mouse was interpreted as a stylus (?!),
multiple buttons being pressed didn't even trigger a press
or release event, so this scenario is seemingly impossible
to happen. */
PointerEvent e{event, pointer};
action == AMOTION_EVENT_ACTION_DOWN ?
app.pointerPressEvent(e) : app.pointerReleaseEvent(e);
/* Remember the currently pressed pointers for the next time */
app._previousPressedButtons = pressedButtons;
/* A touch screen doesn't have hover events, so remember the
position here as well. See below for why this has to be
remembered at all. */
app._previousPointerPosition = position;
return e.isAccepted();
}
case AMOTION_EVENT_ACTION_MOVE: {
Vector2i position{Int(AMotionEvent_getX(event, 0)), Int(AMotionEvent_getY(event, 0))};
MouseMoveEvent e{event,
app._previousMouseMovePosition == Vector2i{-1} ? Vector2i{} :
position - app._previousMouseMovePosition};
app._previousMouseMovePosition = position;
app.mouseMoveEvent(e);
return e.isAccepted() ? 1 : 0;
const Pointers pressedButtons = motionEventButtons(event);
const Pointers pointers = motionEventPointers(event, pressedButtons);
const Vector2 position{AMotionEvent_getX(event, 0),
AMotionEvent_getY(event, 0)};
const Vector2 relativePosition =
Math::isNan(app._previousPointerPosition).all() ?
Vector2{} : position - app._previousPointerPosition;
/* The thing fires move events right after press events, with
the exact same position, for (emulated?) events at least. I
suppose that's some sort of unasked-for misfeature for
"improving" UX or fixing broken apps. Not interested, filter
those out if the relative position is zero and the set of
pressed buttons is the same. Hopefully not accepting those
doesn't lead to some strange behavior. */
bool accepted = false;
if(relativePosition != Vector2{} || pressedButtons != app._previousPressedButtons) {
PointerMoveEvent e{event, {}, pointers, relativePosition};
app.pointerMoveEvent(e);
accepted = e.isAccepted();
}
/* Remember the currently pressed buttons for the next time.
Ideally should only be needed for AMOTION_EVENT_ACTION_DOWN
and AMOTION_EVENT_ACTION_UP, but if some events get lost, we
have a chance to resynchronize here. */
app._previousPressedButtons = pressedButtons;
/* Remember also the current position. There's
AMotionEvent_getHistoricalX()/Y(), but those are coalesced
events between the previous and currently fired move events,
i.e. not the full delta. Documented here:
https://developer.android.com/reference/android/view/MotionEvent#batching
There's also AMOTION_EVENT_AXIS_RELATIVE_X/_Y, but based on
https://developer.android.com/reference/android/view/MotionEvent#AXIS_X
the coordinate system is different for each event type, and
the last thing I want to do is adding special handling for
things the damn platform API should be doing for me. */
app._previousPointerPosition = position;
return accepted;
}
/* Like AMOTION_EVENT_ACTION_MOVE, but without anything pressed */
case AMOTION_EVENT_ACTION_HOVER_MOVE: {
const Vector2 position{AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0)};
const Vector2 relativePosition =
Math::isNan(app._previousPointerPosition).all() ?
Vector2{} : position - app._previousPointerPosition;
/* Similarly as with AMOTION_EVENT_ACTION_MOVE, the damn thing
fires hover events with zero position delta when scrolling
the mouse wheel. Useless, filter those away. */
bool accepted = false;
if(relativePosition != Vector2{}) {
PointerMoveEvent e{event, {}, {}, relativePosition};
app.pointerMoveEvent(e);
accepted = e.isAccepted();
}
/* Reset the currently pressed buttons, since there should be
none if we're just hovering */
app._previousPressedButtons = {};
/* Remember the current position. See above for why
AMotionEvent_getHistoricalX()/Y() is useless. */
app._previousPointerPosition = position;
return accepted;
}
/** @todo there's AMOTION_EVENT_ACTION_HOVER_ENTER and
AMOTION_EVENT_ACTION_HOVER_EXIT, implement once other apps get
something similar */
/** @todo AMOTION_EVENT_ACTION_SCROLL */
}
/** @todo Implement also other input events */
@ -324,6 +471,83 @@ void AndroidApplication::exec(android_app* state, Containers::Pointer<AndroidApp
state->userData = nullptr;
}
void AndroidApplication::pointerPressEvent(PointerEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
MouseEvent mouseEvent{event._event};
mousePressEvent(mouseEvent);
CORRADE_IGNORE_DEPRECATED_POP
#else
static_cast<void>(event);
#endif
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
void AndroidApplication::mousePressEvent(MouseEvent&) {}
CORRADE_IGNORE_DEPRECATED_POP
#endif
void AndroidApplication::pointerReleaseEvent(PointerEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
MouseEvent mouseEvent{event._event};
mouseReleaseEvent(mouseEvent);
CORRADE_IGNORE_DEPRECATED_POP
#else
static_cast<void>(event);
#endif
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
void AndroidApplication::mouseReleaseEvent(MouseEvent&) {}
CORRADE_IGNORE_DEPRECATED_POP
#endif
void AndroidApplication::pointerMoveEvent(PointerMoveEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED
const Vector2i roundedPosition{Math::round(event.position())};
/* If the event is due to some button being additionally pressed or one
button from a larger set being released, delegate to a press/release
event instead */
/** @todo This codepath is never used as move events with pointer() being
set aren't emitted at all above. Keeping it here as that may change
with API 33+. */
CORRADE_IGNORE_DEPRECATED_PUSH
if(event.pointer()) {
/* Android reports either a move or a press/release, so there shouldn't
be any move in this case */
CORRADE_INTERNAL_ASSERT(event.relativePosition() == Vector2{});
MouseEvent mouseEvent{event._event};
event.pointers() >= *event.pointer() ?
mousePressEvent(mouseEvent) : mouseReleaseEvent(mouseEvent);
} else {
/* Can't do just Math::round(event.relativePosition()) because if the
previous position was 4.6 and the new 5.3, they both round to 5 but
the relativePosition is 0.6 and rounds to 1. Conversely, if it'd be
5.3 and 5.6, the positions round to 5 and 6 but relative position
stays 0. */
const Vector2i previousRoundedPosition{Math::round(event.position() - event.relativePosition())};
/* Call the event only if the integer values actually changed */
if(roundedPosition != previousRoundedPosition) {
MouseMoveEvent mouseEvent{event._event, roundedPosition - previousRoundedPosition};
mouseMoveEvent(mouseEvent);
}
}
CORRADE_IGNORE_DEPRECATED_POP
#else
static_cast<void>(event);
#endif
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
void AndroidApplication::mouseMoveEvent(MouseMoveEvent&) {}
CORRADE_IGNORE_DEPRECATED_POP
#endif
template class BasicScreen<AndroidApplication>;
template class BasicScreenedApplication<AndroidApplication>;

292
src/Magnum/Platform/AndroidApplication.h

@ -170,8 +170,25 @@ class AndroidApplication {
class GLConfiguration;
class ViewportEvent;
class InputEvent;
class PointerEvent;
class PointerMoveEvent;
#ifdef MAGNUM_BUILD_DEPRECATED
class MouseEvent;
class MouseMoveEvent;
#endif
/* The damn thing cannot handle forward enum declarations */
#ifndef DOXYGEN_GENERATING_OUTPUT
enum class Pointer: UnsignedByte;
#endif
/**
* @brief Set of pointer types
* @m_since_latest
*
* @see @ref PointerMoveEvent::pointers()
*/
typedef Containers::EnumSet<Pointer> Pointers;
/**
* @brief Execute the application
@ -383,31 +400,93 @@ class AndroidApplication {
* @}
*/
/** @{ @name Mouse handling */
/** @{ @name Pointer handling */
private:
/**
* @brief Pointer press event
* @m_since_latest
*
* Called when a mouse is pressed. Note that if at least one mouse
* button is already pressed and another button gets pressed in
* addition, @ref pointerMoveEvent() with the new combination is
* called, not this function.
*
* On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default
* implementation delegates to @ref mousePressEvent(). On builds with
* deprecated functionality disabled, default implementation does
* nothing.
*/
virtual void pointerPressEvent(PointerEvent& event);
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Mouse press event
* @m_deprecated_since_latest Use @ref pointerPressEvent() instead,
* which is a better abstraction for covering both mouse and touch
* / pen input.
*
* Default implementation does nothing.
*/
virtual CORRADE_DEPRECATED("use pointerPressEvent() instead") void mousePressEvent(MouseEvent& event);
#endif
/**
* @brief Pointer release event
* @m_since_latest
*
* Called when a mouse is released. Note that if multiple mouse buttons
* are pressed and one of these is released, @ref pointerMoveEvent()
* with the new combination is called, not this function.
*
* Called when mouse button is pressed. Default implementation does
* On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default
* implementation delegates to @ref mouseReleaseEvent(). On builds with
* deprecated functionality disabled, default implementation does
* nothing.
*/
virtual void mousePressEvent(MouseEvent& event);
virtual void pointerReleaseEvent(PointerEvent& event);
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Mouse release event
* @m_deprecated_since_latest Use @ref pointerReleaseEvent() instead,
* which is a better abstraction for covering both mouse and touch
* / pen input.
*
* Default implementation does nothing.
*/
virtual CORRADE_DEPRECATED("use pointerReleaseEvent() instead") void mouseReleaseEvent(MouseEvent& event);
#endif
/**
* @brief Pointer move event
* @m_since_latest
*
* Called when any of the currently pressed pointers is moved or
* changes its properties. Gets called also if the set of pressed mouse
* buttons changes.
*
* Called when mouse button is released. Default implementation does
* On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default
* implementation delegates to @ref mouseMoveEvent(), or if
* @ref PointerMoveEvent::pointer() is not
* @relativeref{Corrade,Containers::NullOpt}, to either
* @ref mousePressEvent() or @ref mouseReleaseEvent(). On builds with
* deprecated functionality disabled, default implementation does
* nothing.
*/
virtual void mouseReleaseEvent(MouseEvent& event);
virtual void pointerMoveEvent(PointerMoveEvent& event);
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Mouse move event
* @m_deprecated_since_latest Use @ref pointerMoveEvent() instead,
* which is a better abstraction for covering both mouse and touch
* / pen input.
*
* Called when mouse is moved. Default implementation does nothing.
* Default implementation does nothing.
*/
virtual void mouseMoveEvent(MouseMoveEvent& event);
virtual CORRADE_DEPRECATED("use pointerMoveEvent() instead") void mouseMoveEvent(MouseMoveEvent& event);
#endif
/* Since 1.8.17, the original short-hand group closing doesn't work
anymore. FFS. */
@ -430,7 +509,10 @@ class AndroidApplication {
EGLDisplay _display;
EGLSurface _surface;
EGLContext _glContext;
Vector2i _previousMouseMovePosition{-1};
Vector2 _previousPointerPosition{Constants::nan()};
/* Contains just the Mouse* values */
Pointers _previousPressedButtons;
/* Has to be in an Optional because it gets explicitly destroyed before
the GL context */
@ -440,6 +522,63 @@ class AndroidApplication {
CORRADE_ENUMSET_FRIEND_OPERATORS(Flags)
};
/**
@brief Pointer type
@m_since_latest
@see @ref Pointers, @ref PointerEvent::pointer(),
@ref PointerMoveEvent::pointer(), @ref PointerMoveEvent::pointers()
*/
enum class AndroidApplication::Pointer: UnsignedByte {
/**
* Unknown. Corresponds to `AMOTION_EVENT_TOOL_TYPE_UNKNOWN` and other
* types not listed below.
*/
Unknown = 1 << 0,
/**
* Left mouse button. Corresponds to `AMOTION_EVENT_TOOL_TYPE_MOUSE` and
* `AMOTION_EVENT_BUTTON_PRIMARY`.
*/
MouseLeft = 1 << 1,
/**
* Middle mouse button. Corresponds to `AMOTION_EVENT_TOOL_TYPE_MOUSE` and
* `AMOTION_EVENT_BUTTON_SECONDARY`.
*/
MouseMiddle = 1 << 2,
/**
* Right mouse button. Corresponds to `AMOTION_EVENT_TOOL_TYPE_MOUSE` and
* `AMOTION_EVENT_BUTTON_TERTIARY`.
*/
MouseRight = 1 << 3,
/** @todo AMOTION_EVENT_BUTTON_BACK, AMOTION_EVENT_BUTTON_FORWARD once it's
possible to verify they match MouseButton4 / MouseButton5 in
GlfwApplication and Sdl2Application */
/** Finger. Corresponds to `AMOTION_EVENT_TOOL_TYPE_FINGER`. */
Finger = 1 << 4,
/** @todo There's AMOTION_EVENT_TOOL_TYPE_PALM, but no corresponding
constant on the Java MotionEvent class, and all links to it broken.
Accidental omission? Some scrapped feature with leftover traces? */
/** Pen. Corresponds to `AMOTION_EVENT_TOOL_TYPE_STYLUS`. */
Pen = 1 << 5,
/** @todo There's AMOTION_EVENT_BUTTON_STYLUS_PRIMARY and
AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, expose once similar constants
exist for EmscriptenApplication / Sdl3Application; implement chorded
behavior for those like w/ mouse buttons */
/** Eraser. Corresponds to `AMOTION_EVENT_TOOL_TYPE_ERASER`. */
Eraser = 1 << 6
};
CORRADE_ENUMSET_OPERATORS(AndroidApplication::Pointers)
/**
@brief OpenGL context configuration
@ -691,8 +830,8 @@ class AndroidApplication::ViewportEvent {
/**
@brief Base for input events
@see @ref MouseEvent, @ref MouseMoveEvent, @ref mousePressEvent(),
@ref mouseReleaseEvent(), @ref mouseMoveEvent()
@see @ref PointerEvent, @ref PointerMoveEvent, @ref pointerPressEvent(),
@ref pointerReleaseEvent(), @ref pointerMoveEvent()
*/
class AndroidApplication::InputEvent {
public:
@ -734,12 +873,60 @@ class AndroidApplication::InputEvent {
bool _accepted;
};
/**
@brief Pointer event
@m_since_latest
@see @ref PointerMoveEvent, @ref pointerPressEvent(),
@ref pointerReleaseEvent()
*/
class AndroidApplication::PointerEvent: public InputEvent {
public:
/** @brief Copying is not allowed */
PointerEvent(const PointerEvent&) = delete;
/** @brief Moving is not allowed */
PointerEvent(PointerEvent&&) = delete;
/** @brief Copying is not allowed */
PointerEvent& operator=(const PointerEvent&) = delete;
/** @brief Moving is not allowed */
PointerEvent& operator=(PointerEvent&&) = delete;
/** @brief Pointer type that was pressed or released */
Pointer pointer() const { return _pointer; }
/**
* @brief Position
*
* May return fractional values if the touch hardware has sub-pixel
* precision. Use @ref Math::round() to snap them to the nearest window
* pixel.
*/
Vector2 position() const {
return {AMotionEvent_getX(_event, 0),
AMotionEvent_getY(_event, 0)};
}
private:
friend AndroidApplication;
explicit PointerEvent(AInputEvent* event, Pointer pointer): InputEvent(event), _pointer{pointer} {}
const Pointer _pointer;
};
#ifdef MAGNUM_BUILD_DEPRECATED
/**
@brief Mouse event
@m_deprecated_since_latest Use @ref PointerEvent, @ref pointerPressEvent() and
@ref pointerReleaseEvent() instead, which is a better abstraction for
covering both mouse and touch / pen input.
@see @ref MouseMoveEvent, @ref mousePressEvent(), @ref mouseReleaseEvent()
*/
class AndroidApplication::MouseEvent: public InputEvent {
class CORRADE_DEPRECATED("use PointerEvent, pointerPressEvent() and pointerReleaseEvent() instead") AndroidApplication::MouseEvent: public InputEvent {
friend AndroidApplication;
public:
@ -779,13 +966,91 @@ class AndroidApplication::MouseEvent: public InputEvent {
private:
explicit MouseEvent(AInputEvent* event): InputEvent(event) {}
};
#endif
/**
@brief Pointer move event
@m_since_latest
@see @ref PointerEvent, @ref pointerMoveEvent()
*/
class AndroidApplication::PointerMoveEvent: public InputEvent {
public:
/** @brief Copying is not allowed */
PointerMoveEvent(const PointerMoveEvent&) = delete;
/** @brief Moving is not allowed */
PointerMoveEvent(PointerMoveEvent&&) = delete;
/** @brief Copying is not allowed */
PointerMoveEvent& operator=(const PointerMoveEvent&) = delete;
/** @brief Moving is not allowed */
PointerMoveEvent& operator=(PointerMoveEvent&&) = delete;
/**
* @brief Pointer type that was added or removed from the set of pressed pointers
*
* Is non-empty only in case a mouse button was pressed in addition to
* an already pressed button, or if one mouse button from multiple
* 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 value, the button was pressed.
*/
Containers::Optional<Pointer> pointer() const { return _pointer; }
/**
* @brief Pointer types pressed in this event
*
* Returns an empty set if no pointers are pressed, which happens for
* example when a mouse is just moved around.
* @see @ref pointer()
*/
Pointers pointers() const { return _pointers; }
/**
* @brief Position
*
* May return fractional values if the touch hardware has sub-pixel
* precision. Use @ref Math::round() to snap them to the nearest window
* pixel.
*/
Vector2 position() const {
return {AMotionEvent_getX(_event, 0),
AMotionEvent_getY(_event, 0)};
}
/**
* @brief Position relative to the previous touch event
*
* May return fractional values if the touch hardware has sub-pixel
* precision. Use @ref Math::round() to snap them to the nearest window
* pixel. Unlike @ref Sdl2Application, Android APIs don't provide
* relative position directly, so this is calculated explicitly as a
* delta from previous move event position.
*/
Vector2 relativePosition() const { return _relativePosition; }
private:
friend AndroidApplication;
explicit PointerMoveEvent(AInputEvent* event, Containers::Optional<Pointer> pointer, Pointers pointers, const Vector2& relativePosition): InputEvent{event}, _pointer{pointer}, _pointers{pointers}, _relativePosition{relativePosition} {}
const Containers::Optional<Pointer> _pointer;
const Pointers _pointers;
const Vector2 _relativePosition;
};
#ifdef MAGNUM_BUILD_DEPRECATED
/**
@brief Mouse move event
@m_deprecated_since_latest Use @ref PointerMoveEvent and
@ref pointerMoveEvent() instead, which is a better abstraction for covering
both mouse and touch / pen input.
@see @ref MouseEvent, @ref mouseMoveEvent()
*/
class AndroidApplication::MouseMoveEvent: public InputEvent {
class CORRADE_DEPRECATED("use PointerMoveEvent and pointerMoveEvent() instead") AndroidApplication::MouseMoveEvent: public InputEvent {
friend AndroidApplication;
public:
@ -843,7 +1108,10 @@ class AndroidApplication::MouseMoveEvent: public InputEvent {
const Vector2i _relativePosition;
};
CORRADE_IGNORE_DEPRECATED_PUSH
CORRADE_ENUMSET_OPERATORS(AndroidApplication::MouseMoveEvent::Buttons)
CORRADE_IGNORE_DEPRECATED_POP
#endif
/** @hideinitializer
@brief Entry point for Android applications

66
src/Magnum/Platform/Test/AndroidApplicationTest.cpp

@ -31,9 +31,30 @@
namespace Magnum { namespace Platform {
/* Cannot be in an anonymous namespace as enumSetDebugOutput() below wouldn't
be able to pick it up */
static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value) {
/* These cannot be in an anonymous namespace as enumSetDebugOutput() below
wouldn't be able to pick them up */
static Debug& operator<<(Debug& debug, Application::Pointer value) {
debug << "Pointer" << Debug::nospace;
switch(value) {
#define _c(value) case Application::Pointer::value: return debug << "::" #value;
_c(Unknown)
_c(MouseLeft)
_c(MouseMiddle)
_c(MouseRight)
_c(Finger)
_c(Pen)
_c(Eraser)
#undef _c
}
return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")";
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
CORRADE_UNUSED static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value) {
debug << "Button" << Debug::nospace;
switch(value) {
@ -46,10 +67,26 @@ static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value
return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")";
}
CORRADE_IGNORE_DEPRECATED_POP
#endif
namespace Test { namespace {
Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) {
Debug& operator<<(Debug& debug, Application::Pointers value) {
return Containers::enumSetDebugOutput(debug, value, "Pointers{}", {
Application::Pointer::Unknown,
Application::Pointer::MouseLeft,
Application::Pointer::MouseMiddle,
Application::Pointer::MouseRight,
Application::Pointer::Finger,
Application::Pointer::Pen,
Application::Pointer::Eraser
});
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) {
debug << "Button" << Debug::nospace;
switch(value) {
@ -64,13 +101,15 @@ Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) {
return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")";
}
Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) {
CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) {
return Containers::enumSetDebugOutput(debug, value, "Buttons{}", {
Application::MouseMoveEvent::Button::Left,
Application::MouseMoveEvent::Button::Middle,
Application::MouseMoveEvent::Button::Right,
});
}
CORRADE_IGNORE_DEPRECATED_POP
#endif
struct AndroidApplicationTest: Platform::Application {
explicit AndroidApplicationTest(const Arguments& arguments): Platform::Application{arguments} {
@ -88,17 +127,30 @@ struct AndroidApplicationTest: Platform::Application {
Debug{} << "viewport:" << event.windowSize() << event.framebufferSize() << event.dpiScaling();
}
/* Set to 0 to test the deprecated mouse events instead */
#if 1
void pointerPressEvent(PointerEvent& event) override {
Debug{} << "pointer press:" << event.pointer() << Debug::packed << event.position();
}
void pointerReleaseEvent(PointerEvent& event) override {
Debug{} << "pointer release:" << event.pointer() << Debug::packed << event.position();
}
void pointerMoveEvent(PointerMoveEvent& event) override {
Debug{} << "pointer move:" << event.pointer() << event.pointers() << Debug::packed << event.position() << Debug::packed << event.relativePosition();
}
#else
CORRADE_IGNORE_DEPRECATED_PUSH
void mousePressEvent(MouseEvent& event) override {
Debug{} << "mouse press:" << event.button() << Debug::packed << event.position();
}
void mouseReleaseEvent(MouseEvent& event) override {
Debug{} << "mouse release:" << event.button() << Debug::packed << event.position();
}
void mouseMoveEvent(MouseMoveEvent& event) override {
Debug{} << "mouse move:" << event.buttons() << Debug::packed << event.position() << Debug::packed << event.relativePosition();
}
CORRADE_IGNORE_DEPRECATED_POP
#endif
};
}}}}

Loading…
Cancel
Save