Skip to content

Instantly share code, notes, and snippets.

@hsdk123
Created September 15, 2021 18:12
Show Gist options
  • Save hsdk123/ea5b70493cc0923cb48de1bb86c6fbfe to your computer and use it in GitHub Desktop.
Save hsdk123/ea5b70493cc0923cb48de1bb86c6fbfe to your computer and use it in GitHub Desktop.
#pragma once
//---------------------------------------------------------
// This is my modified version of Magnum/Platform/AndroidApplication.h
// HS: including original header to fetch additional structs so I can keep this file short
#include <Magnum/Platform/AndroidApplication.h>
#include <Magnum/Magnum.h>
#include <Magnum/Math/Vector2.h>
//---------------------------------------------------------
#include <EGL/egl.h>
#include <Corrade/Containers/Pointer.h>
#include <Magnum/Magnum.h>
#include <Magnum/Tags.h>
#include <Magnum/GL/GL.h>
#include <Magnum/Math/Vector4.h>
#include <Magnum/Platform/Platform.h>
#if defined(CORRADE_TARGET_ANDROID) || defined(DOXYGEN_GENERATING_OUTPUT)
#include <android/input.h>
/* Undef Xlib nonsense which might get pulled in by EGL */
#undef None
struct android_app;
struct ANativeActivity;
namespace Magnum {
namespace Modified {
namespace Platform {
class AndroidApplication {
public:
/** @brief Application arguments */
typedef android_app* Arguments;
class Configuration;
class GLConfiguration;
class ViewportEvent;
class InputEvent;
class MouseEvent;
class MouseMoveEvent;
/**
* @brief Execute the application
*
* See @ref MAGNUM_ANDROIDAPPLICATION_MAIN() for usage information.
*/
static void exec(android_app* state, Corrade::Containers::Pointer<AndroidApplication>(*instancer)(const Arguments&));
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T> static Corrade::Containers::Pointer<AndroidApplication> instancer(const Arguments& arguments) {
return Containers::Pointer<AndroidApplication>{new T{ arguments }};
}
#endif
/**
* @brief Construct with given configuration for OpenGL context
* @param arguments Application arguments
* @param configuration Application configuration
* @param glConfiguration OpenGL context configuration
*
* Creates application with default or user-specified configuration.
* See @ref Configuration for more information. The program exits if
* the context cannot be created, see @ref tryCreate() for an
* alternative.
*/
explicit AndroidApplication(const Arguments& arguments,
const Configuration& configuration,
const GLConfiguration& glConfiguration);
/**
* @brief Construct with given configuration
*
* Equivalent to calling @ref AndroidApplication(const Arguments&, const Configuration&, const GLConfiguration&)
* with default-constructed @ref GLConfiguration.
*/
explicit AndroidApplication(const Arguments& arguments,
const Configuration& configuration);
/**
* @brief Construct with default configuration
*
* Equivalent to calling @ref AndroidApplication(const Arguments&, const Configuration&)
* with default-constructed @ref Configuration.
*/
explicit AndroidApplication(const Arguments& arguments);
/**
* @brief Construct without creating a window
* @param arguments Application arguments
*
* Unlike above, the window is not created and must be created later
* with @ref create() or @ref tryCreate().
*/
explicit AndroidApplication(const Arguments& arguments, Magnum::NoCreateT);
/** @brief Copying is not allowed */
AndroidApplication(const AndroidApplication&) = delete;
/** @brief Moving is not allowed */
AndroidApplication(AndroidApplication&&) = delete;
virtual ~AndroidApplication();
/** @brief Copying is not allowed */
AndroidApplication& operator=(const AndroidApplication&) = delete;
/** @brief Moving is not allowed */
AndroidApplication& operator=(AndroidApplication&&) = delete;
/**
* @brief Underlying native activity handle
*
* Use in case you need to call NDK functionality directly.
*/
ANativeActivity* nativeActivity();
protected:
/**
* @brief Create a window with given configuration for OpenGL context
* @param configuration Application configuration
* @param glConfiguration OpenGL context configuration
*
* Must be called only if the context wasn't created by the constructor
* itself, i.e. when passing @ref NoCreate to it. Error message is
* printed and the program exits if the context cannot be created, see
* @ref tryCreate() for an alternative.
*/
void create(const Configuration& configuration,
const GLConfiguration& glConfiguration);
/**
* @brief Create a window with given configuration and OpenGL context
*
* Equivalent to calling @ref create(const Configuration&, const GLConfiguration&)
* with default-constructed @ref GLConfiguration.
*/
void create(const Configuration& configuration);
/**
* @brief Create a window with default configuration and OpenGL context
*
* Equivalent to calling @ref create(const Configuration&) with
* default-constructed @ref Configuration.
*/
void create();
/**
* @brief Try to create context with given configuration for OpenGL context
*
* Unlike @ref create(const Configuration&, const GLConfiguration&)
* returns @cpp false @ce if the context cannot be created,
* @cpp true @ce otherwise.
*/
bool tryCreate(const Configuration& configuration,
const GLConfiguration& glConfiguration);
/**
* @brief Try to create context with given configuration and OpenGL context
*
* Unlike @ref create(const Configuration&) returns @cpp false @ce if
* the context cannot be created, @cpp true @ce otherwise.
*/
bool tryCreate(const Configuration& configuration);
/** @{ @name Screen handling */
//-----------------------------------------------------------------------
// HS STUFF
bool reactivate();
bool pause_context();
virtual void pause_app() = 0;
virtual void continue_app() = 0;
bool fine_to_render() { return /*_app_resumed &&*/ _app_focused && _app_surface; }
private:
bool _app_resumed = false;
bool _app_focused = false;
bool _app_surface = false;
//-----------------------------------------------------------------------
public:
/**
* @brief Window size
*
* Window size to which all input event coordinates can be related.
* Expects that a window is already created, equivalent to
* @ref framebufferSize().
*
* @attention The reported value will be incorrect in case you use
* the [Screen Compatibility Mode](http://www.androiddocs.com/guide/practices/screen-compat-mode.html).
* See @ref platforms-android-apps-manifest-screen-compatibility-mode
* for details.
*/
Vector2i windowSize() const { return framebufferSize(); }
/**
* @brief Framebuffer size
*
* Size of the default framebuffer, equivalent to @ref windowSize().
* Expects that a window is already created.
* @see @ref dpiScaling()
*/
Vector2i framebufferSize() const;
/**
* @brief DPI scaling
*
* Provided only for compatibility with other toolkits. Returns always
* @cpp {1.0f, 1.0f} @ce.
* @see @ref framebufferSize()
*/
Vector2 dpiScaling() const { return Vector2{ 1.0f }; }
/**
* @brief DPI scaling for given configuration
*
* Provided only for compatibility with other toolkits. Returns always
* @cpp {1.0f, 1.0f} @ce.
* @see @ref framebufferSize()
*/
Vector2 dpiScaling(const Magnum::Platform::AndroidApplication::Configuration& configuration) const {
static_cast<void>(configuration);
return Vector2{ 1.0f };
}
/**
* @brief Swap buffers
*
* Paints currently rendered framebuffer on screen.
*/
void swapBuffers();
/** @copydoc Sdl2Application::redraw() */
void redraw() { _flags |= Flag::Redraw; }
private:
/**
* @brief Viewport event
*
* Called when window size changes, for example after device
* orientation change. The default implementation does nothing. If you
* want to respond to size changes, you should pass the new size to
* @ref GL::DefaultFramebuffer::setViewport() (if using OpenGL) and
* possibly elsewhere (to @ref SceneGraph::Camera::setViewport(), other
* framebuffers...).
*
* @attention Android by default kills and fully recreates the
* application on device orientation change instead of calling the
* viewport event. To prevent that, you need to modify the
* `AndroidManifest.xml` file. See the
* @ref platforms-android-apps-manifest-screen-resize "manifest file docs"
* for more information.
*
* Note that this function might not get called at all if the window
* size doesn't change. You should configure the initial state of your
* cameras, framebuffers etc. in application constructor rather than
* relying on this function to be called. Size of the window can be
* retrieved using @ref windowSize(), size of the backing framebuffer
* via @ref framebufferSize() and DPI scaling using @ref dpiScaling().
* See @ref Platform-GlfwApplication-dpi for detailed info about these
* values.
*/
virtual void viewportEvent(ViewportEvent& event);
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copydoc GlfwApplication::viewportEvent(const Vector2i&) */
virtual CORRADE_DEPRECATED("use viewportEvent(ViewportEvent&) instead") void viewportEvent(const Vector2i& size);
#endif
/** @copydoc Sdl2Application::drawEvent() */
virtual void drawEvent() = 0;
/* Since 1.8.17, the original short-hand group closing doesn't work
anymore. FFS. */
/**
* @}
*/
/** @{ @name Mouse handling */
private:
/**
* @brief Mouse press event
*
* Called when mouse button is pressed. Default implementation does
* nothing.
*/
virtual void mousePressEvent(MouseEvent& event);
/**
* @brief Mouse release event
*
* Called when mouse button is released. Default implementation does
* nothing.
*/
virtual void mouseReleaseEvent(MouseEvent& event);
/**
* @brief Mouse move event
*
* Called when mouse is moved. Default implementation does nothing.
*/
virtual void mouseMoveEvent(MouseMoveEvent& event);
/* Since 1.8.17, the original short-hand group closing doesn't work
anymore. FFS. */
/**
* @}
*/
private:
struct LogOutput;
enum class Flag : Magnum::UnsignedByte {
Redraw = 1 << 0
};
typedef Corrade::Containers::EnumSet<Flag> Flags;
static void commandEvent(android_app* state, std::int32_t cmd);
static std::int32_t inputEvent(android_app* state, AInputEvent* event);
android_app* const _state;
Flags _flags;
EGLDisplay _display;
EGLSurface _surface;
EGLContext _glContext;
Vector2i _previousMouseMovePosition{ -1 };
Corrade::Containers::Pointer<Magnum::Platform::GLContext> _context;
Corrade::Containers::Pointer<LogOutput> _logOutput;
CORRADE_ENUMSET_FRIEND_OPERATORS(Flags)
};
/**
@brief OpenGL context configuration
Double-buffered RGBA canvas with depth and stencil buffers.
@see @ref AndroidApplication(), @ref Configuration, @ref create(),
@ref tryCreate()
*/
class AndroidApplication::GLConfiguration {
public:
/*implicit*/ GLConfiguration();
/**
* @brief Set context version
*
* @note This function does nothing and is included only for
* compatibility with other toolkits. @ref GL::Version::GLES200 or
* @ref GL::Version::GLES300 is used based on engine compile-time
* settings.
*/
GLConfiguration& setVersion(GL::Version) { return *this; }
/** @brief Color buffer size */
Vector4i colorBufferSize() const { return _colorBufferSize; }
/**
* @brief Set color buffer size
*
* Default is @cpp {8, 8, 8, 8} @ce (8-bit-per-channel RGBA).
* @see @ref setDepthBufferSize(), @ref setStencilBufferSize()
*/
GLConfiguration& setColorBufferSize(const Vector4i& size) {
_colorBufferSize = size;
return *this;
}
/** @brief Depth buffer size */
Int depthBufferSize() const { return _depthBufferSize; }
/**
* @brief Set depth buffer size
*
* Default is @cpp 24 @ce bits.
* @see @ref setColorBufferSize(), @ref setStencilBufferSize()
*/
GLConfiguration& setDepthBufferSize(Int size) {
_depthBufferSize = size;
return *this;
}
/** @brief Stencil buffer size */
Int stencilBufferSize() const { return _stencilBufferSize; }
/**
* @brief Set stencil buffer size
*
* Default is @cpp 0 @ce bits (i.e., no stencil buffer).
* @see @ref setColorBufferSize(), @ref setDepthBufferSize()
*/
GLConfiguration& setStencilBufferSize(Int size) {
_stencilBufferSize = size;
return *this;
}
private:
Vector4i _colorBufferSize;
Int _depthBufferSize, _stencilBufferSize;
};
/**
@brief Configuration
Double-buffered RGBA canvas with depth and stencil buffers.
@see @ref AndroidApplication(), @ref GLConfiguration, @ref create(),
@ref tryCreate()
*/
class AndroidApplication::Configuration {
public:
constexpr /*implicit*/ Configuration() {}
/**
* @brief Set window title
* @return Reference to self (for method chaining)
*
* @note This function does nothing and is included only for
* compatibility with other toolkits. You need to set the title
* separately in the `AndroidManifest.xml` file.
*/
template<class T> Configuration& setTitle(const T&) { return *this; }
/** @brief Window size */
Vector2i size() const { return _size; }
/**
* @brief Set window size
* @return Reference to self (for method chaining)
*
* Default is @cpp {0, 0} @ce, which means that the size of the
* physical window will be used. If set to different value than the
* physical size, the surface will be scaled.
*/
Configuration& setSize(const Vector2i& size) {
_size = size;
return *this;
}
private:
Vector2i _size;
};
/**
@brief Viewport event
@see @ref viewportEvent()
*/
class AndroidApplication::ViewportEvent {
public:
/** @brief Copying is not allowed */
ViewportEvent(const ViewportEvent&) = delete;
/** @brief Moving is not allowed */
ViewportEvent(ViewportEvent&&) = delete;
/** @brief Copying is not allowed */
ViewportEvent& operator=(const ViewportEvent&) = delete;
/** @brief Moving is not allowed */
ViewportEvent& operator=(ViewportEvent&&) = delete;
/**
* @brief Window size
*
* The same as @ref framebufferSize(). See
* @ref AndroidApplication::windowSize() for possible caveats.
*/
Vector2i windowSize() const { return _windowSize; }
/**
* @brief Framebuffer size
*
* The same as @ref windowSize(). See
* @ref AndroidApplication::framebufferSize() for possible caveats.
*/
Vector2i framebufferSize() const { return _windowSize; }
/**
* @brief DPI scaling
*
* Always @cpp {1.0f, 1.0f} @ce.
*/
Vector2 dpiScaling() const { return Vector2{ 1.0f }; }
private:
friend AndroidApplication;
explicit ViewportEvent(const Vector2i& windowSize) : _windowSize{ windowSize } {}
const Vector2i _windowSize;
};
/**
@brief Base for input events
@see @ref MouseEvent, @ref MouseMoveEvent, @ref mousePressEvent(),
@ref mouseReleaseEvent(), @ref mouseMoveEvent()
*/
class AndroidApplication::InputEvent {
public:
/** @brief Copying is not allowed */
InputEvent(const InputEvent&) = delete;
/** @brief Moving is not allowed */
InputEvent(InputEvent&&) = delete;
/** @brief Copying is not allowed */
InputEvent& operator=(const InputEvent&) = delete;
/** @brief Moving is not allowed */
InputEvent& operator=(InputEvent&&) = delete;
/**
* @brief Set event as accepted
*
* If the event is ignored (i.e., not set as accepted), it will be
* propagated elsewhere, for example to the Android system or to
* another screen when using @ref BasicScreenedApplication "ScreenedApplication".
* By default is each event ignored and thus propagated.
*/
void setAccepted(bool accepted = true) { _accepted = accepted; }
/** @brief Whether the event is accepted */
bool isAccepted() const { return _accepted; }
#ifndef DOXYGEN_GENERATING_OUTPUT
protected:
explicit InputEvent(AInputEvent* event) : _event(event), _accepted(false) {}
~InputEvent() = default;
AInputEvent* const _event;
#endif
private:
bool _accepted;
};
/**
@brief Mouse event
@see @ref MouseMoveEvent, @ref mousePressEvent(), @ref mouseReleaseEvent()
*/
class AndroidApplication::MouseEvent : public InputEvent {
friend AndroidApplication;
public:
/**
* @brief Mouse button
*
* @see @ref button()
*/
enum class Button : std::int32_t {
/** No button was pressed (touch or stylus event) */
None = 0,
/**
* Left mouse button. Note that this button is not set if only
* touch or stylus event occured.
* @attention Available since Android 4.0 (API level 14), not
* detectable in earlier versions.
*/
#if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14
Left = AMOTION_EVENT_BUTTON_PRIMARY,
#else
Left = 1 << 0,
#endif
/**
* Middle mouse button or second stylus button
* @attention Available since Android 4.0 (API level 14), not
* detectable in earlier versions.
*/
#if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14
Middle = AMOTION_EVENT_BUTTON_TERTIARY,
#else
Middle = 1 << 1,
#endif
/**
* Right mouse button or first stylus button
* @attention Available since Android 4.0 (API level 14), not
* detectable in earlier versions.
*/
#if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14
Right = AMOTION_EVENT_BUTTON_SECONDARY
#else
Right = 1 << 2
#endif
};
/** @brief Button */
Button button() {
#if __ANDROID_API__ >= 14
return Button(AMotionEvent_getButtonState(_event));
#else
return Button::None;
#endif
}
/** @brief Position */
Vector2i position() {
return { Int(AMotionEvent_getX(_event, 0)),
Int(AMotionEvent_getY(_event, 0)) };
}
private:
explicit MouseEvent(AInputEvent* event) : InputEvent(event) {}
};
/**
@brief Mouse move event
@see @ref MouseEvent, @ref mouseMoveEvent()
*/
class AndroidApplication::MouseMoveEvent : public InputEvent {
friend AndroidApplication;
public:
/**
* @brief Mouse button
*
* @see @ref buttons()
*/
enum class Button : std::int32_t {
/**
* Left mouse button. Note that this button is not set if only
* touch or stylus event occured.
* @attention Available since Android 4.0 (API level 14), not
* detectable in earlier versions.
*/
#if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14
Left = AMOTION_EVENT_BUTTON_PRIMARY,
#else
Left = 1 << 0,
#endif
/**
* Middle mouse button or second stylus button
* @attention Available since Android 4.0 (API level 14), not
* detectable in earlier versions.
*/
#if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14
Middle = AMOTION_EVENT_BUTTON_TERTIARY,
#else
Middle = 1 << 1,
#endif
/**
* Right mouse button or first stylus button
* @attention Available since Android 4.0 (API level 14), not
* detectable in earlier versions.
*/
#if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14
Right = AMOTION_EVENT_BUTTON_SECONDARY
#else
Right = 1 << 2
#endif
};
/**
* @brief Set of mouse buttons
*
* @see @ref buttons()
*/
typedef Containers::EnumSet<Button> Buttons;
/** @brief Position */
Vector2i position() const {
return { Int(AMotionEvent_getX(_event, 0)),
Int(AMotionEvent_getY(_event, 0)) };
}
/**
* @brief Relative position
* @m_since{2019,10}
*
* Position relative to previous move event. Unlike
* @ref Sdl2Application, Android APIs don't provide relative position
* directly, so this is calculated explicitly as a delta from previous
* move event position.
*/
Vector2i relativePosition() const { return _relativePosition; }
/** @brief Mouse buttons */
Buttons buttons() const {
#if __ANDROID_API__ >= 14
return Button(AMotionEvent_getButtonState(_event));
#else
return {};
#endif
}
private:
explicit MouseMoveEvent(AInputEvent* event, Vector2i relativePosition) : InputEvent{ event }, _relativePosition{ relativePosition } {}
const Vector2i _relativePosition;
};
CORRADE_ENUMSET_OPERATORS(AndroidApplication::MouseMoveEvent::Buttons)
#define MAGNUM_ANDROIDAPPLICATION_MAIN(className) \
extern "C" CORRADE_VISIBILITY_EXPORT void android_main(android_app* state); \
extern "C" void android_main(android_app* state) { \
Magnum::Platform::AndroidApplication::exec(state, \
Magnum::Platform::AndroidApplication::instancer<className>); \
}
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_APPLICATION_MAIN
typedef AndroidApplication Application;
typedef BasicScreen<AndroidApplication> Screen;
typedef BasicScreenedApplication<AndroidApplication> ScreenedApplication;
#define MAGNUM_APPLICATION_MAIN(className) MAGNUM_ANDROIDAPPLICATION_MAIN(className)
#else
#undef MAGNUM_APPLICATION_MAIN
#endif
#endif
}
}
}
#else
#error this file is available only on Android build
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment