diff --git a/CMakeLists.txt b/CMakeLists.txt index 58abee0bf..76873776f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,7 @@ endif() # Platform-independent (almost) application libraries if(NOT CORRADE_TARGET_NACL AND NOT CORRADE_TARGET_ANDROID) + option(WITH_GLFWAPPLICATION "Build GlfwApplication library") cmake_dependent_option(WITH_GLUTAPPLICATION "Build GlutApplication library" OFF "NOT TARGET_GLES" OFF) option(WITH_SDL2APPLICATION "Build Sdl2Application library" OFF) endif() diff --git a/src/Magnum/Platform/CMakeLists.txt b/src/Magnum/Platform/CMakeLists.txt index e1abc2ecc..1a1dbca71 100644 --- a/src/Magnum/Platform/CMakeLists.txt +++ b/src/Magnum/Platform/CMakeLists.txt @@ -37,7 +37,7 @@ set(MagnumPlatform_FILES ) install(FILES ${MagnumPlatform_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) # Decide about platform-specific context for cross-platform toolkits -if(WITH_GLUTAPPLICATION OR WITH_SDL2APPLICATION) +if(WITH_GLFWAPPLICATION OR WITH_GLUTAPPLICATION OR WITH_SDL2APPLICATION) if(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES) set(NEED_CGLCONTEXT 1) set(MagnumSomeContext_OBJECTS $) @@ -91,6 +91,36 @@ if(WITH_ANDROIDAPPLICATION) add_library(Magnum::AndroidApplication ALIAS MagnumAndroidApplication) endif() +# GLFW application +if(WITH_GLFWAPPLICATION) + find_package(GLFW) + if(NOT GLFW_FOUND) + message(FATAL_ERROR "GLFW library, required by GlfwApplication, was not found. Set WITH_GLFWAPPLICATION to OFF to skip building it.") + endif() + + set(MagnumGlfwApplication_SRCS + GlfwApplication.cpp + ${MagnumSomeContext_OBJECTS}) + set(MagnumGlfwApplication_HEADERS GlfwApplication.h) + + add_library(MagnumGlfwApplication STATIC + ${MagnumGlfwApplication_SRCS} + ${MagnumGlfwApplication_HEADERS}) + set_target_properties(MagnumGlfwApplication PROPERTIES DEBUG_POSTFIX "-d") + # Assuming that PIC is not needed because the Application lib is always + # linked to the executable and not to any intermediate shared lib + target_link_libraries(MagnumGlfwApplication Magnum GLFW::GLFW) + + install(FILES ${MagnumGlfwApplication_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) + install(TARGETS MagnumGlfwApplication + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) + + # Magnum GlfwApplication target alias for superprojects + add_library(Magnum::GlfwApplication ALIAS MagnumGlfwApplication) +endif() + # GLUT application if(WITH_GLUTAPPLICATION) find_package(GLUT) diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp new file mode 100644 index 000000000..5d751fecb --- /dev/null +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -0,0 +1,208 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "GlfwApplication.h" + +#include + +#include "Magnum/Version.h" +#include "Magnum/Platform/Context.h" +#include "Magnum/Platform/ScreenedApplication.hpp" + +namespace Magnum { namespace Platform { + +GlfwApplication* GlfwApplication::_instance = nullptr; + +#ifndef DOXYGEN_GENERATING_OUTPUT +GlfwApplication::GlfwApplication(const Arguments& arguments): GlfwApplication{arguments, Configuration{}} {} +#endif + +GlfwApplication::GlfwApplication(const Arguments& arguments, const Configuration& configuration): GlfwApplication{arguments, nullptr} { + createContext(configuration); +} + +GlfwApplication::GlfwApplication(const Arguments& arguments, std::nullptr_t) + : _context{new Context{NoCreate, arguments.argc, arguments.argv}}, + _needsRedraw(true) +{ + /* Save global instance */ + _instance = this; + + /* Init GLFW */ + glfwSetErrorCallback(staticErrorCallback); + + if(!glfwInit()) { + Error() << "Could not initialize GLFW"; + std::exit(8); + } +} + +void GlfwApplication::createContext() { createContext({}); } + +void GlfwApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); +} + +bool GlfwApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::GlfwApplication::tryCreateContext(): context already created", false); + + static_assert(GLFW_TRUE == true && GLFW_FALSE == false, "GLFW does not have sane bool values."); + + /* Window flags */ + GLFWmonitor* monitor = nullptr; /* Needed for setting fullscreen */ + if (configuration.windowFlags() >= Configuration::WindowFlag::Fullscreen) { + monitor = glfwGetPrimaryMonitor(); + glfwWindowHint(GLFW_AUTO_ICONIFY, configuration.windowFlags() >= Configuration::WindowFlag::AutoIconify); + } else { + const Configuration::WindowFlags& flags = configuration.windowFlags(); + glfwWindowHint(GLFW_RESIZABLE, flags >= Configuration::WindowFlag::Resizeable); + glfwWindowHint(GLFW_VISIBLE, !(flags >= Configuration::WindowFlag::Hidden)); + glfwWindowHint(GLFW_MAXIMIZED, flags >= Configuration::WindowFlag::Maximized); + glfwWindowHint(GLFW_ICONIFIED, flags >= Configuration::WindowFlag::Minimized); + glfwWindowHint(GLFW_FLOATING, flags >= Configuration::WindowFlag::Floating); + } + glfwWindowHint(GLFW_FLOATING, configuration.windowFlags() >= Configuration::WindowFlag::Focused); + + /* Context window hints */ + glfwWindowHint(GLFW_SAMPLES, configuration.sampleCount()); + glfwWindowHint(GLFW_SRGB_CAPABLE, configuration.isSRGBCapable()); + + const Configuration::Flags& flags = configuration.flags(); + glfwWindowHint(GLFW_CONTEXT_NO_ERROR, flags >= Configuration::Flag::NoError); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, flags >= Configuration::Flag::Debug); + glfwWindowHint(GLFW_STEREO, flags >= Configuration::Flag::Stereo); + + /* Cursor flags */ + glfwWindowHint(GLFW_CURSOR, Int(configuration.cursorMode())); + + /* Set context version, if requested */ + if(configuration.version() != Version::None) { + Int major, minor; + std::tie(major, minor) = version(configuration.version()); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor); + #ifndef MAGNUM_TARGET_GLES + if(configuration.version() >= Version::GL310) { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + } + #else + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + #endif + } + + /* Set context flags */ + _window = glfwCreateWindow(configuration.size().x(), configuration.size().y(), configuration.title().c_str(), monitor, nullptr); + if(!_window) { + Error() << "Platform::GlfwApplication::tryCreateContext(): cannot create context"; + glfwTerminate(); + return false; + } + + /* Set callbacks */ + glfwSetFramebufferSizeCallback(_window, staticViewportEvent); + glfwSetKeyCallback(_window, staticKeyEvent); + glfwSetCursorPosCallback(_window, staticMouseMoveEvent); + glfwSetMouseButtonCallback(_window, staticMouseEvent); + glfwSetScrollCallback(_window, staticMouseScrollEvent); + + glfwMakeContextCurrent(_window); + glfwSwapInterval(1); + + /* Return true if the initialization succeeds */ + return _context->tryCreate(); +} + +GlfwApplication::~GlfwApplication() { + glfwDestroyWindow(_window); + glfwTerminate(); +} + +int GlfwApplication::exec() { + while(!glfwWindowShouldClose(_window)) { + if(_needsRedraw) { + drawEvent(); + } + glfwPollEvents(); + } + return 0; +} + +void GlfwApplication::staticKeyEvent(GLFWwindow*, int key, int, int action, int) { + KeyEvent e(static_cast(key)); + + if(action == GLFW_PRESS) { + _instance->keyPressEvent(e); + } else if(action == GLFW_RELEASE) { + _instance->keyReleaseEvent(e); + } /* we don't handle GLFW_REPEAT */ +} + +void GlfwApplication::staticMouseMoveEvent(GLFWwindow*, double x, double y) { + MouseMoveEvent e{Vector2i{Int(x), Int(y)}}; + _instance->mouseMoveEvent(e); +} + +void GlfwApplication::staticMouseEvent(GLFWwindow*, int button, int action, int) { + MouseEvent e(static_cast(button)); + + if(action == GLFW_PRESS) { + _instance->mousePressEvent(e); + } else if(action == GLFW_RELEASE) { + _instance->mouseReleaseEvent(e); + } /* we don't handle GLFW_REPEAT */ +} + +void GlfwApplication::staticMouseScrollEvent(GLFWwindow*, double xoffset, double yoffset) { + MouseScrollEvent e(Vector2d{xoffset, yoffset}); + _instance->mouseScrollEvent(e); +} + +void GlfwApplication::staticErrorCallback(int, const char* description) { + Error() << description; +} + +void GlfwApplication::viewportEvent(const Vector2i&) {} +void GlfwApplication::keyPressEvent(KeyEvent&) {} +void GlfwApplication::keyReleaseEvent(KeyEvent&) {} +void GlfwApplication::mousePressEvent(MouseEvent&) {} +void GlfwApplication::mouseReleaseEvent(MouseEvent&) {} +void GlfwApplication::mouseMoveEvent(MouseMoveEvent&) {} +void GlfwApplication::mouseScrollEvent(MouseScrollEvent&) {} + +GlfwApplication::Configuration::Configuration() + : _title("Magnum GLFW Application"), + _size(800, 600), _sampleCount(0), + _version(Version::None), + _windowFlags(WindowFlag::Focused), + _cursorMode(CursorMode::Normal) +{} + +GlfwApplication::Configuration::~Configuration() = default; + +template class BasicScreen; +template class BasicScreenedApplication; + +}} diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h new file mode 100644 index 000000000..9e821588d --- /dev/null +++ b/src/Magnum/Platform/GlfwApplication.h @@ -0,0 +1,753 @@ +#ifndef Magnum_Platform_GlfwApplication_h +#define Magnum_Platform_GlfwApplication_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Platform::GlfwApplication, macro @ref MAGNUM_GLFWAPPLICATION_MAIN() + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Math/Vector2.h" +#include "Magnum/Platform/Platform.h" + +/* We must include our own GL headers first to avoid conflicts */ +#include "Magnum/OpenGL.h" + +#include + +namespace Magnum { namespace Platform { + +/** @nosubgrouping +@brief GLFW application + +Application using GLFW toolkit. Supports keyboard and mouse handling with +support for changing cursor and mouse tracking and warping. + +This application library is available only on desktop OpenGL (Linux, Windows, +OS X). It depends on **GLFW** library and is built if `WITH_GLFWAPPLICATION` is +enabled in CMake. + +## Bootstrap application + +Fully contained base application using @ref GlfwApplication along with +CMake setup is available in `base-glfw` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-glfw.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-glfw.zip) file. +After extracting the downloaded archive you can build and run the application +with these four commands: + + mkdir build && cd build + cmake .. + cmake --build . + ./src/MyApplication # or ./src/Debug/MyApplication + +See @ref cmake for more information. + +## General usage + +In CMake you need to request `GlfwApplication` component of `Magnum` package +and link to `Magnum::GlfwApplication` target. If no other application is +requested, you can also use generic `Magnum::Application` alias to simplify +porting. Again, see @ref building and @ref cmake for more information. + +In C++ code you need to implement at least @ref drawEvent() to be able to draw +on the screen. The subclass can be then used directly in `main()` -- see +convenience macro @ref MAGNUM_GLFWAPPLICATION_MAIN(). See @ref platform for +more information. +@code +class MyApplication: public Platform::GlfwApplication { + // implement required methods... +}; +MAGNUM_GLFWAPPLICATION_MAIN(MyApplication) +@endcode + +If no other application header is included, this class is also aliased to +`Platform::Application` and the macro is aliased to `MAGNUM_APPLICATION_MAIN()` +to simplify porting. +*/ +class GlfwApplication { + public: + /** @brief Application arguments */ + struct Arguments { + /** @brief Constructor */ + /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {} + + int& argc; /**< @brief Argument count */ + char** argv; /**< @brief Argument values */ + }; + + class Configuration; + class InputEvent; + class KeyEvent; + class MouseEvent; + class MouseMoveEvent; + class MouseScrollEvent; + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, const Configuration&) */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit GlfwApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + explicit GlfwApplication(const Arguments& arguments, const Configuration& configuration); + explicit GlfwApplication(const Arguments& arguments); + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t) */ + explicit GlfwApplication(const Arguments& arguments, std::nullptr_t); + + /** @brief Copying is not allowed */ + GlfwApplication(const GlfwApplication&) = delete; + + /** @brief Moving is not allowed */ + GlfwApplication(GlfwApplication&&) = delete; + + /** @brief Copying is not allowed */ + GlfwApplication& operator=(const GlfwApplication&) = delete; + + /** @brief Moving is not allowed */ + GlfwApplication& operator=(GlfwApplication&&) = delete; + + /** + * @brief Execute main loop + * @return Value for returning from `main()` + * + * See @ref MAGNUM_GLFWAPPLICATION_MAIN() for usage information. + */ + int exec(); + + /** @brief Exit application main loop */ + void exit() { + glfwSetWindowShouldClose(_window, GLFW_TRUE); + } + + protected: + /* Nobody will need to have (and delete) GlfwApplication*, thus this is + faster than public pure virtual destructor */ + ~GlfwApplication(); + + /** @copydoc Sdl2Application::createContext() */ + #ifdef DOXYGEN_GENERATING_OUTPUT + void createContext(const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + void createContext(const Configuration& configuration); + void createContext(); + #endif + + /** @copydoc Sdl2Application::tryCreateContext() */ + bool tryCreateContext(const Configuration& configuration); + + /** @{ @name Screen handling */ + + /** + * @brief Swap buffers + * + * Paints currently rendered framebuffer on screen. + */ + void swapBuffers() { glfwSwapBuffers(_window); } + + /** @copydoc Sdl2Application::redraw() */ + void redraw() { _needsRedraw = true; } + + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif + /** @copydoc Sdl2Application::viewportEvent() */ + virtual void viewportEvent(const Vector2i& size); + + /** @copydoc Sdl2Application::drawEvent() */ + virtual void drawEvent() = 0; + + /*@}*/ + + /** @{ @name Keyboard handling */ + + /** @copydoc Sdl2Application::keyPressEvent() */ + virtual void keyPressEvent(KeyEvent& event); + + /** @copydoc Sdl2Application::keyReleaseEvent() */ + virtual void keyReleaseEvent(KeyEvent& event); + + /*@}*/ + + /** @{ @name Mouse handling */ + + public: + /** + * @brief Mouse cursor + * + * @see @ref setMouseCursor() + */ + enum class MouseCursor: int { + Default = GLFW_CURSOR_NORMAL, /**< Default cursor provided by parent window */ + Hidden = GLFW_CURSOR_HIDDEN, /**< Hidden cursor */ + None = GLFW_CURSOR_DISABLED /**< No cursor */ + }; + + /** @brief Warp mouse cursor to given coordinates */ + void warpCursor(const Vector2i& position) { + glfwSetCursorPos(_window, Double(position.x()), Double(position.y())); + } + + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif + /** @copydoc Sdl2Application::mousePressEvent() */ + virtual void mousePressEvent(MouseEvent& event); + + /** @copydoc Sdl2Application::mouseReleaseEvent() */ + virtual void mouseReleaseEvent(MouseEvent& event); + + /** + * @brief Mouse move event + * + * Called when any mouse button is pressed and mouse is moved. Default + * implementation does nothing. + * @see @ref setMouseTracking() + */ + virtual void mouseMoveEvent(MouseMoveEvent& event); + + /** + * @brief Mouse scroll event + * + * Called when a scrolling device is used (mouse wheel or scrolling area + * on touchpad). Default implementation does nothing. + */ + virtual void mouseScrollEvent(MouseScrollEvent& event); + + /*@}*/ + + private: + static void staticViewportEvent(GLFWwindow*, int w, int h) { + _instance->viewportEvent({w, h}); + } + + static void staticKeyEvent(GLFWwindow* window, int key, int scancode, int action, int mod); + + static void staticMouseEvent(GLFWwindow* window, int button, int action, int mods); + + static void staticMouseMoveEvent(GLFWwindow* window, double x, double y); + + static void staticMouseScrollEvent(GLFWwindow* window, double xoffset, double yoffset); + + static void staticErrorCallback(int error, const char* description); + + static GlfwApplication* _instance; + + GLFWwindow* _window; + + std::unique_ptr _context; + + bool _needsRedraw; +}; + +/** +@brief Configuration + +Double-buffered RGBA window with depth and stencil buffers. +@see @ref GlfwApplication(), @ref createContext(), @ref tryCreateContext() +*/ +class GlfwApplication::Configuration { + public: + /** + * @brief Context flag + * + * @see @ref Flags, @ref setFlags() + */ + enum class Flag: Int { + /** + * Specifies whether errors should be generated by the context. + * If enabled, situations that would have generated errors instead + * cause undefined behavior. + */ + NoError = GLFW_CONTEXT_NO_ERROR, + Debug = GLFW_OPENGL_DEBUG_CONTEXT, /**< Debug context */ + Stereo = GLFW_STEREO, /**< Stereo rendering */ + }; + + /** + * @brief Context flags + * + * @see @ref setFlags() + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Window flag + * + * @see @ref WindowFlags, @ref setWindowFlags() + */ + enum class WindowFlag: UnsignedShort { + Fullscreen = 1 << 0, /**< Fullscreen window */ + Resizeable = 1 << 1, /**< Resizeable window */ + Hidden = 1 << 2, /**< Hidden window */ + Maximized = 1 << 3, /**< Maximized window */ + Minimized = 1 << 4, /**< Minimized window */ + Floating = 1 << 5, /**< Window floating above others, top-most */ + AutoIconify = 1 << 6, /**< Automatically iconify (minimize) if fullscreen window loses input focus */ + Focused = 1 << 7, /**< Window has input focus */ + }; + + /** + * @brief Window flags + * + * @see @ref setWindowFlags() + */ + typedef Containers::EnumSet WindowFlags; + + enum class CursorMode: Int { + Normal = GLFW_CURSOR_NORMAL, /**< Visible unconstrained cursor */ + Hidden = GLFW_CURSOR_HIDDEN, /**< Hidden cursor */ + Diabled = GLFW_CURSOR_DISABLED, /**< Cursor hidden and locked window */ + }; + + /*implicit*/ Configuration(); + ~Configuration(); + + /** @brief Window title */ + std::string title() const { return _title; } + + /** + * @brief Set window title + * @return Reference to self (for method chaining) + * + * Default is `"Magnum GLFW Application"`. + */ + Configuration& setTitle(std::string title) { + _title = std::move(title); + return *this; + } + + /** @brief Window size */ + Vector2i size() const { return _size; } + + /** + * @brief Set window size + * @return Reference to self (for method chaining) + * + * Default is `{800, 600}`. + */ + Configuration& setSize(const Vector2i& size) { + _size = size; + return *this; + } + + /** @brief Context flags */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + /** @brief Window flags */ + WindowFlags windowFlags() const { + return _windowFlags; + } + + /** + * @brief Set window flags + * @return Reference to self (for method chaining) + * + * Default is @ref WindowFlag::Focused. + */ + Configuration& setWindowFlags(WindowFlags windowFlags) { + _windowFlags = windowFlags; + return *this; + } + + /** @brief Cursor mode */ + CursorMode cursorMode() const { + return _cursorMode; + } + + /** + * @brief Set cursor flags + * @return Reference to self (for method chaining) + * + * Default is @ref CursorMode::Normal. + */ + Configuration& setCursorMode(CursorMode cursorMode) { + _cursorMode = cursorMode; + return *this; + } + + /** @brief Context version */ + Version version() const { return _version; } + + /** + * @brief Set context version + * + * If requesting version greater or equal to OpenGL 3.1, core profile + * is used. The created context will then have any version which is + * backwards-compatible with requested one. Default is + * @ref Version::None, i.e. any provided version is used. + */ + Configuration& setVersion(Version version) { + _version = version; + return *this; + } + + /** @brief Sample count */ + Int sampleCount() const { return _sampleCount; } + + /** + * @brief Set sample count + * @return Reference to self (for method chaining) + * + * Default is `0`, thus no multisampling. The actual sample count is + * ignored, GLFW either enables it or disables. See also + * @ref Renderer::Feature::Multisampling. + */ + Configuration& setSampleCount(Int count) { + _sampleCount = count; + return *this; + } + + /** @brief sRGB-capable default framebuffer */ + bool isSRGBCapable() const { + return _srgbCapable; + } + + /** + * @brief Set sRGB-capable default framebuffer + * @return Reference to self (for method chaining) + */ + Configuration& setSRGBCapable(bool enabled) { + _srgbCapable = enabled; + return *this; + } + + private: + std::string _title; + Vector2i _size; + Int _sampleCount; + Version _version; + Flags _flags; + WindowFlags _windowFlags; + CursorMode _cursorMode; + bool _srgbCapable; +}; + +CORRADE_ENUMSET_OPERATORS(GlfwApplication::Configuration::Flags) +CORRADE_ENUMSET_OPERATORS(GlfwApplication::Configuration::WindowFlags) + +/** +@brief Base for input events + +@see @ref KeyEvent, @ref MouseEvent, @ref MouseMoveEvent, @ref keyPressEvent(), + @ref mousePressEvent(), @ref mouseReleaseEvent(), @ref mouseMoveEvent() +*/ +class GlfwApplication::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; + + /** @copydoc Sdl2Application::InputEvent::setAccepted() */ + void setAccepted(bool accepted = true) { _accepted = accepted; } + + /** @copydoc Sdl2Application::InputEvent::isAccepted() */ + constexpr bool isAccepted() const { return _accepted; } + + protected: + constexpr InputEvent(): _accepted(false) {} + + ~InputEvent() = default; + + private: + bool _accepted; +}; + +/** +@brief Key event + +@see @ref keyPressEvent() +*/ +class GlfwApplication::KeyEvent: public GlfwApplication::InputEvent { + friend GlfwApplication; + + public: + /** + * @brief Key + * + * @see @ref key() + */ + enum class Key: Int { + Unknown = GLFW_KEY_UNKNOWN, /**< Unknown key */ + + Up = GLFW_KEY_UP, /**< Up arrow */ + Down = GLFW_KEY_DOWN, /**< Down arrow */ + Left = GLFW_KEY_LEFT, /**< Left arrow */ + Right = GLFW_KEY_RIGHT, /**< Right arrow */ + F1 = GLFW_KEY_F1, /**< F1 */ + F2 = GLFW_KEY_F2, /**< F2 */ + F3 = GLFW_KEY_F3, /**< F3 */ + F4 = GLFW_KEY_F4, /**< F4 */ + F5 = GLFW_KEY_F5, /**< F5 */ + F6 = GLFW_KEY_F6, /**< F6 */ + F7 = GLFW_KEY_F7, /**< F7 */ + F8 = GLFW_KEY_F8, /**< F8 */ + F9 = GLFW_KEY_F9, /**< F9 */ + F10 = GLFW_KEY_F10, /**< F10 */ + F11 = GLFW_KEY_F11, /**< F11 */ + F12 = GLFW_KEY_F12, /**< F12 */ + Home = GLFW_KEY_HOME, /**< Home */ + End = GLFW_KEY_END, /**< End */ + PageUp = GLFW_KEY_PAGE_UP, /**< Page up */ + PageDown = GLFW_KEY_PAGE_DOWN, /**< Page down */ + + Space = ' ', /**< Space */ + Comma = ',', /**< Comma */ + Period = '.', /**< Period */ + Minus = '-', /**< Minus */ + Plus = '+', /**< Plus */ + Slash = '/', /**< Slash */ + Percent = '%', /**< Percent */ + Smicolon = ';', /**< Semicolon */ + Equal = '=', /**< Equal */ + + Zero = '0', /**< Zero */ + One = '1', /**< One */ + Two = '2', /**< Two */ + Three = '3', /**< Three */ + Four = '4', /**< Four */ + Five = '5', /**< Five */ + Six = '6', /**< Six */ + Seven = '7', /**< Seven */ + Eight = '8', /**< Eight */ + Nine = '9', /**< Nine */ + + A = 'a', /**< Letter A */ + B = 'b', /**< Letter B */ + C = 'c', /**< Letter C */ + D = 'd', /**< Letter D */ + E = 'e', /**< Letter E */ + F = 'f', /**< Letter F */ + G = 'g', /**< Letter G */ + H = 'h', /**< Letter H */ + I = 'i', /**< Letter I */ + J = 'j', /**< Letter J */ + K = 'k', /**< Letter K */ + L = 'l', /**< Letter L */ + M = 'm', /**< Letter M */ + N = 'n', /**< Letter N */ + O = 'o', /**< Letter O */ + P = 'p', /**< Letter P */ + Q = 'q', /**< Letter Q */ + R = 'r', /**< Letter R */ + S = 's', /**< Letter S */ + T = 't', /**< Letter T */ + U = 'u', /**< Letter U */ + V = 'v', /**< Letter V */ + W = 'w', /**< Letter W */ + X = 'x', /**< Letter X */ + Y = 'y', /**< Letter Y */ + Z = 'z', /**< Letter Z */ + + /* Function keys */ + Esc = GLFW_KEY_ESCAPE, /**< Escape */ + Enter = GLFW_KEY_ENTER, /**< Enter */ + Tab = GLFW_KEY_TAB, /**< Tab */ + Backspace = GLFW_KEY_BACKSPACE, /**< Backspace */ + Insert = GLFW_KEY_INSERT, /**< Insert */ + Delete = GLFW_KEY_DELETE, /**< Delete */ + CapsLock = GLFW_KEY_CAPS_LOCK, /**< Caps lock */ + ScrollLock = GLFW_KEY_SCROLL_LOCK, /**< Scroll lock */ + NumLock = GLFW_KEY_NUM_LOCK, /**< Num lock */ + PrintScreen = GLFW_KEY_PRINT_SCREEN,/**< Print screen */ + Pause = GLFW_KEY_PAUSE, /**< Pause */ + NumZero = GLFW_KEY_KP_0, /**< Numpad zero */ + NumOne = GLFW_KEY_KP_1, /**< Numpad one */ + NumTwo = GLFW_KEY_KP_2, /**< Numpad two */ + NumThree = GLFW_KEY_KP_3, /**< Numpad three */ + NumFour = GLFW_KEY_KP_4, /**< Numpad four */ + NumFive = GLFW_KEY_KP_5, /**< Numpad five */ + NumSix = GLFW_KEY_KP_6, /**< Numpad six */ + NumSeven = GLFW_KEY_KP_7, /**< Numpad seven */ + NumEight = GLFW_KEY_KP_8, /**< Numpad eight */ + NumNine = GLFW_KEY_KP_9, /**< Numpad nine */ + NumDecimal = GLFW_KEY_KP_DECIMAL, /**< Numpad decimal */ + NumDivide = GLFW_KEY_KP_DIVIDE, /**< Numpad divide */ + NumMultiply = GLFW_KEY_KP_MULTIPLY, /**< Numpad multiply */ + NumSubtract = GLFW_KEY_KP_SUBTRACT, /**< Numpad subtract */ + NumAdd = GLFW_KEY_KP_ADD, /**< Numpad add */ + NumEnter = GLFW_KEY_KP_ENTER, /**< Numpad enter */ + NumEqual = GLFW_KEY_KP_EQUAL, /**< Numpad equal */ + LeftShift = GLFW_KEY_LEFT_SHIFT, /**< Left shift */ + LeftCtrl = GLFW_KEY_LEFT_CONTROL, /**< Left control */ + LeftAlt = GLFW_KEY_LEFT_ALT, /**< Left alt */ + LeftSuper = GLFW_KEY_LEFT_SUPER, /**< Left super */ + RightShift = GLFW_KEY_RIGHT_SHIFT, /**< Right shift */ + RightCtrl = GLFW_KEY_RIGHT_CONTROL, /**< Right control */ + RightAlt = GLFW_KEY_RIGHT_ALT, /**< Right alt */ + RightSuper = GLFW_KEY_RIGHT_SUPER, /**< Right super */ + Menu = GLFW_KEY_MENU, /**< Menu */ + }; + + /** @brief Key */ + constexpr Key key() const { return _key; } + + private: + constexpr KeyEvent(Key key): _key(key) {} + + const Key _key; +}; + +/** +@brief Mouse event + +@see @ref MouseMoveEvent, @ref MouseScrollEvent, @ref mousePressEvent(), @ref mouseReleaseEvent() +*/ +class GlfwApplication::MouseEvent: public GlfwApplication::InputEvent { + friend GlfwApplication; + + public: + /** + * @brief Mouse button + * + * @see @ref button() + */ + enum class Button: int { + Left = GLFW_MOUSE_BUTTON_LEFT, /**< Left button */ + Middle = GLFW_MOUSE_BUTTON_MIDDLE, /**< Middle button */ + Right = GLFW_MOUSE_BUTTON_RIGHT, /**< Right button */ + Button1 = GLFW_MOUSE_BUTTON_1, /**< Mouse button 1 */ + Button2 = GLFW_MOUSE_BUTTON_2, /**< Mouse button 2 */ + Button3 = GLFW_MOUSE_BUTTON_3, /**< Mouse button 3 */ + Button4 = GLFW_MOUSE_BUTTON_4, /**< Mouse button 4 */ + Button5 = GLFW_MOUSE_BUTTON_5, /**< Mouse button 5 */ + Button6 = GLFW_MOUSE_BUTTON_6, /**< Mouse button 6 */ + Button7 = GLFW_MOUSE_BUTTON_7, /**< Mouse button 7 */ + Button8 = GLFW_MOUSE_BUTTON_8, /**< Mouse button 8 */ + }; + + /** @brief Button */ + constexpr Button button() const { return _button; } + + private: + constexpr MouseEvent(Button button): _button(button) {} + + const Button _button; +}; + +/** +@brief Mouse move event + +@see @ref MouseEvent, @ref MouseScrollEvent, @ref mouseMoveEvent() +*/ +class GlfwApplication::MouseMoveEvent: public GlfwApplication::InputEvent { + friend GlfwApplication; + + public: + + /** @brief Position */ + constexpr Vector2i position() const { return _position; } + + private: + constexpr MouseMoveEvent(const Vector2i& position): _position(position) {} + + const Vector2i _position; +}; + +/** +@brief Mouse scroll event + +@see @ref MouseEvent, @ref MouseMoveEvent, @ref mouseScrollEvent() +*/ +class GlfwApplication::MouseScrollEvent: public GlfwApplication::InputEvent { + friend GlfwApplication; + + public: + + /** @brief Scroll offset */ + constexpr Vector2d offset() const { return _offset; } + + private: + constexpr MouseScrollEvent(const Vector2d& offset): _offset(offset) {} + + const Vector2d _offset; +}; + +/** @hideinitializer +@brief Entry point for GLFW-based applications +@param className Class name + +See @ref Magnum::Platform::GlfwApplication "Platform::GlfwApplication" for +usage information. This macro abstracts out platform-specific entry point code +and is equivalent to the following, see @ref portability-applications for more +information. +@code +int main(int argc, char** argv) { + className app({argc, argv}); + return app.exec(); +} +@endcode +When no other application header is included this macro is also aliased to +`MAGNUM_APPLICATION_MAIN()`. +*/ +#define MAGNUM_GLFWAPPLICATION_MAIN(className) \ + int main(int argc, char** argv) { \ + className app({argc, argv}); \ + return app.exec(); \ + } + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_APPLICATION_MAIN +typedef GlfwApplication Application; +typedef BasicScreen Screen; +typedef BasicScreenedApplication ScreenedApplication; +#define MAGNUM_APPLICATION_MAIN(className) MAGNUM_GLFWAPPLICATION_MAIN(className) +#else +#undef MAGNUM_APPLICATION_MAIN +#endif +#endif + +}} + +#endif