diff --git a/TempGlfwApplication.cpp b/TempGlfwApplication.cpp new file mode 100644 index 0000000000..96210dcdaa --- /dev/null +++ b/TempGlfwApplication.cpp @@ -0,0 +1,926 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + Copyright © 2016, 2018 Jonathan Hale + Copyright © 2019 Konstantinos Chatzilygeroudis + Copyright © 2019, 2020 Marco Melorio + + 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 +#include +#include +#include + +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/Math/ConfigurationValue.h" +#include "Magnum/Platform/ScreenedApplication.hpp" +#include "Magnum/Platform/Implementation/DpiScaling.h" + +#ifdef MAGNUM_TARGET_GL +#include "Magnum/GL/Version.h" +#endif + +namespace Magnum { namespace Platform { + +using namespace Containers::Literals; + +#ifdef GLFW_TRUE +/* The docs say that it's the same, verify that just in case */ +static_assert(GLFW_TRUE == true && GLFW_FALSE == false, "GLFW does not have sane bool values"); +#endif + +enum class GlfwApplication::Flag: UnsignedByte { + Redraw = 1 << 0, + TextInputActive = 1 << 1, + Exit = 1 << 2, + #ifdef CORRADE_TARGET_APPLE + HiDpiWarningPrinted = 1 << 3 + #endif +}; + +GlfwApplication::GlfwApplication(const Arguments& arguments): GlfwApplication{arguments, Configuration{}} {} + +GlfwApplication::GlfwApplication(const Arguments& arguments, const Configuration& configuration): GlfwApplication{arguments, NoCreate} { + create(configuration); +} + +#ifdef MAGNUM_TARGET_GL +GlfwApplication::GlfwApplication(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration): GlfwApplication{arguments, NoCreate} { + create(configuration, glConfiguration); +} +#endif + +GlfwApplication::GlfwApplication(const Arguments& arguments, NoCreateT): + _flags{Flag::Redraw} +{ + Utility::Arguments args{Implementation::windowScalingArguments()}; + #ifdef MAGNUM_TARGET_GL + _context.emplace(NoCreate, args, arguments.argc, arguments.argv); + #else + /** @todo this is duplicated here, in Sdl2Application and in + EmscriptenApplication, figure out a nice non-duplicated way to handle + this */ + args.addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") + .setFromEnvironment("log") + .parse(arguments.argc, arguments.argv); + #endif + + /* Init GLFW */ + glfwSetErrorCallback([](int, const char* const description) { + Error{} << description; + }); + + if(!glfwInit()) { + Error() << "Could not initialize GLFW"; + std::exit(8); + } + + /* Save command-line arguments */ + if(args.value("log") == "verbose") _verboseLog = true; + const Containers::StringView dpiScaling = args.value("dpi-scaling"); + if(dpiScaling == "default"_s) + _commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Default; + #ifdef CORRADE_TARGET_APPLE + else if(dpiScaling == "framebuffer"_s) + _commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Framebuffer; + #else + else if(dpiScaling == "virtual"_s) + _commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Virtual; + else if(dpiScaling == "physical"_s) + _commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Physical; + #endif + else if(dpiScaling.containsAny(" \t\n"_s)) + _commandLineDpiScaling = args.value("dpi-scaling"); + else + _commandLineDpiScaling = Vector2{args.value("dpi-scaling")}; +} + +void GlfwApplication::create() { + create(Configuration{}); +} + +void GlfwApplication::create(const Configuration& configuration) { + if(!tryCreate(configuration)) std::exit(1); +} + +#ifdef MAGNUM_TARGET_GL +void GlfwApplication::create(const Configuration& configuration, const GLConfiguration& glConfiguration) { + if(!tryCreate(configuration, glConfiguration)) std::exit(1); +} +#endif + +Vector2 GlfwApplication::dpiScaling(const Configuration& configuration) { + std::ostream* verbose = _verboseLog ? Debug::output() : nullptr; + + /* Print a helpful warning in case some extra steps are needed for HiDPI + support */ + #ifdef CORRADE_TARGET_APPLE + if(!Implementation::isAppleBundleHiDpiEnabled() && !(_flags & Flag::HiDpiWarningPrinted)) { + Warning{} << "Platform::GlfwApplication: warning: the executable is not a HiDPI-enabled app bundle"; + _flags |= Flag::HiDpiWarningPrinted; + } + #elif defined(CORRADE_TARGET_WINDOWS) + /** @todo */ + #endif + + /* Use values from the configuration only if not overridden on command line + to something non-default. In any case explicit scaling has a precedence + before the policy. */ + Implementation::GlfwDpiScalingPolicy dpiScalingPolicy{}; + if(!_commandLineDpiScaling.isZero()) { + Debug{verbose} << "Platform::GlfwApplication: user-defined DPI scaling" << _commandLineDpiScaling; + return _commandLineDpiScaling; + } else if(_commandLineDpiScalingPolicy != Implementation::GlfwDpiScalingPolicy::Default) { + dpiScalingPolicy = _commandLineDpiScalingPolicy; + } else if(!configuration.dpiScaling().isZero()) { + Debug{verbose} << "Platform::GlfwApplication: app-defined DPI scaling" << configuration.dpiScaling(); + return configuration.dpiScaling(); + } else { + dpiScalingPolicy = configuration.dpiScalingPolicy(); + } + + /* There's no choice on Apple, it's all controlled by the plist file. So + unless someone specified custom scaling via config or command-line + above, return the default. */ + #ifdef CORRADE_TARGET_APPLE + return Vector2{1.0f}; + + /* Otherwise there's a choice between virtual and physical DPI scaling */ + #else + /* Try to get virtual DPI scaling first, if supported and requested */ + if(dpiScalingPolicy == Implementation::GlfwDpiScalingPolicy::Virtual) { + /* Use Xft.dpi on X11. This could probably be dropped for GLFW 3.3+ + as glfwGetMonitorContentScale() does the same, but I'd still need to + keep it for 2.2 and below, plus the same code needs to be used for + SDL anyway. So keeping it to reduce the chance for unexpected minor + differences across app implementations. */ + #ifdef _MAGNUM_PLATFORM_USE_X11 + const Vector2 dpiScaling{Implementation::x11DpiScaling()}; + if(!dpiScaling.isZero()) { + Debug{verbose} << "Platform::GlfwApplication: virtual DPI scaling" << dpiScaling.x(); + return dpiScaling; + } + + /* Check for DPI awareness on non-RT Windows and then ask for content + scale (available since GLFW 3.3). GLFW is advertising the + application to be DPI-aware on its own even without supplying an + explicit manifest -- https://github.com/glfw/glfw/blob/089ea9af227fdffdf872348923e1c12682e63029/src/win32_init.c#L564-L569 + If, for some reason, the app is still not DPI-aware, tell that to + the user explicitly and don't even attempt to query the value if the + app is not DPI aware. If it's desired to get the DPI value + unconditionally, the user should use physical DPI scaling instead. */ + #elif defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT) + if(!Implementation::isWindowsAppDpiAware()) { + Warning{verbose} << "Platform::GlfwApplication: your application is not set as DPI-aware, DPI scaling won't be used"; + return Vector2{1.0f}; + } + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 303 + GLFWmonitor* const monitor = glfwGetPrimaryMonitor(); + Vector2 dpiScaling; + glfwGetMonitorContentScale(monitor, &dpiScaling.x(), &dpiScaling.y()); + Debug{verbose} << "Platform::GlfwApplication: virtual DPI scaling" << dpiScaling; + return dpiScaling; + #else + Debug{verbose} << "Platform::GlfwApplication: sorry, virtual DPI scaling only available on GLFW 3.3+, falling back to physical DPI scaling"; + #endif + + /* Otherwise ¯\_(ツ)_/¯ */ + #else + Debug{verbose} << "Platform::GlfwApplication: sorry, virtual DPI scaling not implemented on this platform yet, falling back to physical DPI scaling"; + #endif + } + + /* At this point, either the virtual DPI query failed or a physical DPI + scaling is requested */ + CORRADE_INTERNAL_ASSERT(dpiScalingPolicy == Implementation::GlfwDpiScalingPolicy::Virtual || dpiScalingPolicy == Implementation::GlfwDpiScalingPolicy::Physical); + + /* Physical DPI scaling. Enable only on Linux (where it gets the usually + very-off value from X11) and on non-RT Windows (where it calculates it + from actual monitor dimensions). */ + #if defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) + GLFWmonitor* const monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* const mode = glfwGetVideoMode(monitor); + Vector2i monitorSize; + glfwGetMonitorPhysicalSize(monitor, &monitorSize.x(), &monitorSize.y()); + if(monitorSize.isZero()) { + Warning{verbose} << "Platform::GlfwApplication: the physical monitor size is zero? DPI scaling won't be used"; + return Vector2{1.0f}; + } + auto dpi = Vector2{Vector2i{mode->width, mode->height}*25.4f/Vector2{monitorSize}}; + const Vector2 dpiScaling{dpi/96.0f}; + Debug{verbose} << "Platform::GlfwApplication: physical DPI scaling" << dpiScaling; + return dpiScaling; + + /* Not implemented otherwise */ + #else + Debug{verbose} << "Platform::GlfwApplication: sorry, physical DPI scaling not implemented on this platform yet"; + return Vector2{1.0f}; + #endif + #endif +} + +void GlfwApplication::setWindowTitle(const Containers::StringView title) { + glfwSetWindowTitle(_window, Containers::String::nullTerminatedView(title).data()); +} + +#if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 302 +void GlfwApplication::setWindowIcon(const ImageView2D& image) { + setWindowIcon({&image, 1}); +} + +namespace { + +template inline void packPixels(const Containers::StridedArrayView2D& in, const Containers::StridedArrayView2D& out) { + for(std::size_t row = 0; row != in.size()[0]; ++row) + for(std::size_t col = 0; col != in.size()[1]; ++col) + out[row][col] = in[row][col]; +} + +} + +void GlfwApplication::setWindowIcon(const Containers::ArrayView images) { + /* Calculate the total size needed to allocate first so we don't allocate + a ton of tiny arrays */ + std::size_t size = 0; + for(const ImageView2D& image: images) + size += sizeof(GLFWimage) + 4*image.size().product(); + Containers::Array data{size}; + + /* Pack array of GLFWimages and pixel data together into the memory + allocated above */ + std::size_t offset = images.size()*sizeof(GLFWimage); + Containers::ArrayView glfwImages = Containers::arrayCast(data.prefix(offset)); + std::size_t i = 0; + for(const ImageView2D& image: images) { + /* Copy and tightly pack pixels. GLFW doesn't allow arbitrary formats + or strides (for subimages and/or Y flip), so we have to copy */ + Containers::ArrayView target = data.slice(offset, offset + 4*image.size().product()); + auto out = Containers::StridedArrayView2D{ + Containers::arrayCast(target), + {std::size_t(image.size().y()), + std::size_t(image.size().x())}}.flipped<0>(); + /** @todo handle sRGB differently? */ + if(image.format() == PixelFormat::RGB8Snorm || + image.format() == PixelFormat::RGB8Unorm) + packPixels(image.pixels(), out); + else if(image.format() == PixelFormat::RGBA8Snorm || + image.format() == PixelFormat::RGBA8Unorm) + packPixels(image.pixels(), out); + else CORRADE_ASSERT_UNREACHABLE("Platform::GlfwApplication::setWindowIcon(): unexpected format" << image.format(), ); + + /* Specify the image metadata */ + glfwImages[i].width = image.size().x(); + glfwImages[i].height = image.size().y(); + glfwImages[i].pixels = reinterpret_cast(target.data()); + + ++i; + offset += target.size(); + } + + glfwSetWindowIcon(_window, glfwImages.size(), glfwImages); +} + +void GlfwApplication::setWindowIcon(std::initializer_list images) { + setWindowIcon(Containers::arrayView(images)); +} +#endif + +bool GlfwApplication::tryCreate(const Configuration& configuration) { + #ifdef MAGNUM_TARGET_GL + #ifdef GLFW_NO_API + if(!(configuration.windowFlags() & Configuration::WindowFlag::Contextless)) + #endif + { + return tryCreate(configuration, GLConfiguration{}); + } + #endif + + CORRADE_ASSERT(!_window, "Platform::GlfwApplication::tryCreate(): window already created", false); + + /* Scale window based on DPI */ + _dpiScaling = dpiScaling(configuration); + const Vector2i scaledWindowSize = configuration.size()*_dpiScaling; + + /* 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_DECORATED, !(flags >= Configuration::WindowFlag::Borderless)); + glfwWindowHint(GLFW_RESIZABLE, flags >= Configuration::WindowFlag::Resizable); + glfwWindowHint(GLFW_VISIBLE, !(flags >= Configuration::WindowFlag::Hidden)); + #ifdef GLFW_MAXIMIZED + glfwWindowHint(GLFW_MAXIMIZED, flags >= Configuration::WindowFlag::Maximized); + #endif + glfwWindowHint(GLFW_FLOATING, flags >= Configuration::WindowFlag::AlwaysOnTop); + } + glfwWindowHint(GLFW_FOCUSED, configuration.windowFlags() >= Configuration::WindowFlag::Focused); + + #ifdef GLFW_NO_API + /* Disable implicit GL context creation */ + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + #endif + + /* Create the window */ + CORRADE_INTERNAL_ASSERT(configuration.title().flags() & Containers::StringViewFlag::NullTerminated); + _window = glfwCreateWindow(scaledWindowSize.x(), scaledWindowSize.y(), configuration.title().data(), monitor, nullptr); + if(!_window) { + Error() << "Platform::GlfwApplication::tryCreate(): cannot create window"; + glfwTerminate(); + return false; + } + + /* Proceed with configuring other stuff that couldn't be done with window + hints */ + if(configuration.windowFlags() >= Configuration::WindowFlag::Minimized) + glfwIconifyWindow(_window); + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + glfwSetInputMode(_window, GLFW_CURSOR, Int(configuration.cursorMode())); + CORRADE_IGNORE_DEPRECATED_POP + #endif + + return true; +} + +namespace { + +GlfwApplication::InputEvent::Modifiers currentGlfwModifiers(GLFWwindow* window) { + static_assert(GLFW_PRESS == true && GLFW_RELEASE == false, + "GLFW press and release constants do not correspond to bool values"); + + GlfwApplication::InputEvent::Modifiers mods; + if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) || + glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT)) + mods |= GlfwApplication::InputEvent::Modifier::Shift; + if(glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) || + glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL)) + mods |= GlfwApplication::InputEvent::Modifier::Ctrl; + if(glfwGetKey(window, GLFW_KEY_LEFT_ALT) || + glfwGetKey(window, GLFW_KEY_RIGHT_ALT)) + mods |= GlfwApplication::InputEvent::Modifier::Alt; + if(glfwGetKey(window, GLFW_KEY_LEFT_SUPER) || + glfwGetKey(window, GLFW_KEY_RIGHT_SUPER)) + mods |= GlfwApplication::InputEvent::Modifier::Super; + + return mods; +} + +} + +#ifdef MAGNUM_TARGET_GL +bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) { + CORRADE_ASSERT(!_window && _context->version() == GL::Version::None, "Platform::GlfwApplication::tryCreate(): window with OpenGL context already created", false); + + /* Scale window based on DPI */ + + // _dpiScaling = dpiScaling(configuration); + // const Vector2i scaledWindowSize = configuration.size()*_dpiScaling; + Vector2i scaledWindowSize = configuration.size(); // temp disable dpi stuff + + // /* 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_DECORATED, !(flags >= Configuration::WindowFlag::Borderless)); + // glfwWindowHint(GLFW_RESIZABLE, flags >= Configuration::WindowFlag::Resizable); + // glfwWindowHint(GLFW_VISIBLE, !(flags >= Configuration::WindowFlag::Hidden)); + // #ifdef GLFW_MAXIMIZED + // glfwWindowHint(GLFW_MAXIMIZED, flags >= Configuration::WindowFlag::Maximized); + // #endif + // glfwWindowHint(GLFW_FLOATING, flags >= Configuration::WindowFlag::AlwaysOnTop); + // } + // glfwWindowHint(GLFW_FOCUSED, configuration.windowFlags() >= Configuration::WindowFlag::Focused); + + /* Framebuffer setup */ + // glfwWindowHint(GLFW_RED_BITS, glConfiguration.colorBufferSize().r()); + // glfwWindowHint(GLFW_GREEN_BITS, glConfiguration.colorBufferSize().g()); + // glfwWindowHint(GLFW_BLUE_BITS, glConfiguration.colorBufferSize().b()); + // glfwWindowHint(GLFW_ALPHA_BITS, glConfiguration.colorBufferSize().a()); + // glfwWindowHint(GLFW_DEPTH_BITS, glConfiguration.depthBufferSize()); + // glfwWindowHint(GLFW_STENCIL_BITS, glConfiguration.stencilBufferSize()); + // glfwWindowHint(GLFW_SAMPLES, glConfiguration.sampleCount()); + // glfwWindowHint(GLFW_SRGB_CAPABLE, glConfiguration.isSrgbCapable()); + + /* Request debug context if GpuValidation is enabled either via the + configuration or via command-line */ + GLConfiguration::Flags glFlags = glConfiguration.flags(); + // if((glFlags & GLConfiguration::Flag::GpuValidation) || (_context->configurationFlags() & GL::Context::Configuration::Flag::GpuValidation)) + // glFlags |= GLConfiguration::Flag::Debug; + // #ifdef GLFW_CONTEXT_NO_ERROR + // else if((glFlags & GLConfiguration::Flag::GpuValidationNoError) || (_context->configurationFlags() & GL::Context::Configuration::Flag::GpuValidationNoError)) + // glFlags |= GLConfiguration::Flag::NoError; + // #endif + + // #ifdef GLFW_CONTEXT_NO_ERROR + // glfwWindowHint(GLFW_CONTEXT_NO_ERROR, glFlags >= GLConfiguration::Flag::NoError); + // #endif + // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, glFlags >= GLConfiguration::Flag::Debug); + // glfwWindowHint(GLFW_STEREO, glFlags >= GLConfiguration::Flag::Stereo); + + /* Set context version, if requested */ + // if(false) { // glConfiguration.version() != GL::Version::None) { + // Int major, minor; + // std::tie(major, minor) = version(glConfiguration.version()); + // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major); + // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor); + // #ifndef MAGNUM_TARGET_GLES + // if(glConfiguration.version() >= GL::Version::GL320) { + // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, glFlags >= GLConfiguration::Flag::ForwardCompatible); + // } + // #else + // glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + // #endif + // #ifdef MAGNUM_TARGET_EGL /* Force EGL if desired */ + // glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + // #endif + + // /* Request usable version otherwise */ + // } else { + // #ifndef MAGNUM_TARGET_GLES + // /* First try to create core context. This is needed mainly on macOS and + // Mesa, as support for recent OpenGL versions isn't implemented in + // compatibility contexts (which are the default). Unlike SDL2, GLFW + // requires at least version 3.2 to be able to request a core profile. */ + // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, glFlags >= GLConfiguration::Flag::ForwardCompatible); + // #else + // /* For ES the major context version is compile-time constant */ + // #ifdef MAGNUM_TARGET_GLES3 + // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + // #elif defined(MAGNUM_TARGET_GLES2) + // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + // #else + // #error unsupported OpenGL ES version + // #endif + // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + // glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + // #endif + // #ifdef MAGNUM_TARGET_EGL /* Force EGL if desired */ + // glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + // #endif + // } + + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + + /* Create window. Hide it by default so we don't have distracting window + blinking in case we have to destroy it again right away. If the creation + succeeds, make the context current so we can query GL_VENDOR below. + If we are on Wayland, this is causing a segfault; a blinking window is + acceptable in this case. */ + // if(std::getenv("XDG_SESSION_TYPE") != "wayland"_s) + // glfwWindowHint(GLFW_VISIBLE, false); + // else if(_verboseLog) + // Warning{} << "Platform::GlfwApplication: Wayland detected, GL context has to be created with the window visible and may cause flicker on startup"; + // CORRADE_INTERNAL_ASSERT(configuration.title().flags() & Containers::StringViewFlag::NullTerminated); + if((_window = glfwCreateWindow(scaledWindowSize.x(), scaledWindowSize.y(), configuration.title().data(), monitor, nullptr))) + glfwMakeContextCurrent(_window); + + #ifndef MAGNUM_TARGET_GLES + /* Fall back to (forward compatible) GL 2.1, if version is not + user-specified and either core context creation fails or we are on + binary NVidia/AMD drivers on Linux/Windows or Intel Windows drivers. + Instead of creating forward-compatible context with highest available + version, they force the version to the one specified, which is + completely useless behavior. */ + #ifndef CORRADE_TARGET_APPLE + Containers::StringView vendorString; + #endif + if(glConfiguration.version() == GL::Version::None && (!_window + #ifndef CORRADE_TARGET_APPLE + /* Sorry about the UGLY code, HOPEFULLY THERE WON'T BE MORE WORKAROUNDS */ + || (vendorString = reinterpret_cast(glGetString(GL_VENDOR)), + (vendorString == "NVIDIA Corporation"_s || + #ifdef CORRADE_TARGET_WINDOWS + vendorString == "Intel"_s || + #endif + vendorString == "ATI Technologies Inc."_s) + && !_context->isDriverWorkaroundDisabled("no-forward-compatible-core-context"_s)) + #endif + )) { + /* Don't print any warning when doing the workaround, because the bug + will be there probably forever */ + if(!_window) Warning{} + << "Platform::GlfwApplication::tryCreate(): cannot create a window with core OpenGL context, falling back to compatibility context"; + else glfwDestroyWindow(_window); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); + /* Discard the ForwardCompatible flag for the fallback. Having it set + makes the fallback context creation fail on Mesa's Zink (which is + just 2.1) and I assume on others as well. */ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, false); + + CORRADE_INTERNAL_ASSERT(configuration.title().flags() & Containers::StringViewFlag::NullTerminated); + _window = glfwCreateWindow(scaledWindowSize.x(), scaledWindowSize.y(), configuration.title().data(), monitor, nullptr); + } + #endif + + if(!_window) { + Error() << "Platform::GlfwApplication::tryCreate(): cannot create a window with OpenGL context"; + return false; + } + + /* Proceed with configuring other stuff that couldn't be done with window + hints */ + if(configuration.windowFlags() >= Configuration::WindowFlag::Minimized) + glfwIconifyWindow(_window); + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + glfwSetInputMode(_window, GLFW_CURSOR, Int(configuration.cursorMode())); + CORRADE_IGNORE_DEPRECATED_POP + #endif + + /* If exit() was called before the window got created, be sure to propagate + it */ + glfwSetWindowShouldClose(_window, !!(_flags & Flag::Exit)); + + /* Make the final context current */ + glfwMakeContextCurrent(_window); + + /* Destroy everything when the Magnum context creation fails */ + if(!_context->tryCreate(glConfiguration)) { + glfwDestroyWindow(_window); + _window = nullptr; + } + + /* Show the window once we are sure that everything is okay */ + if(!(configuration.windowFlags() & Configuration::WindowFlag::Hidden)) + glfwShowWindow(_window); + + /* Return true if the initialization succeeds */ + return true; +} +#endif + +void GlfwApplication::setupCallbacks() { + glfwSetWindowUserPointer(_window, this); + glfwSetWindowCloseCallback(_window, [](GLFWwindow* const window){ + ExitEvent e; + static_cast(glfwGetWindowUserPointer(window))->exitEvent(e); + if(!e.isAccepted()) glfwSetWindowShouldClose(window, false); + }); + glfwSetWindowRefreshCallback(_window, [](GLFWwindow* const window){ + /* Properly redraw after the window is restored from minimized state */ + static_cast(glfwGetWindowUserPointer(window))->drawEvent(); + }); + #ifdef MAGNUM_TARGET_GL + glfwSetFramebufferSizeCallback + #else + glfwSetWindowSizeCallback + #endif + (_window, [](GLFWwindow* const window, const int w, const int h) { + auto& app = *static_cast(glfwGetWindowUserPointer(window)); + #ifdef MAGNUM_TARGET_GL + ViewportEvent e{app.windowSize(), {w, h}, app.dpiScaling()}; + #else + ViewportEvent e{{w, h}, app.dpiScaling()}; + #endif + app.viewportEvent(e); + }); + glfwSetKeyCallback(_window, [](GLFWwindow* const window, const int key, int, const int action, const int mods) { + auto& app = *static_cast(glfwGetWindowUserPointer(window)); + + KeyEvent e(static_cast(key), {static_cast(mods)}, action == GLFW_REPEAT); + + if(action == GLFW_PRESS || action == GLFW_REPEAT) + app.keyPressEvent(e); + else if(action == GLFW_RELEASE) + app.keyReleaseEvent(e); + }); + glfwSetMouseButtonCallback(_window, [](GLFWwindow* const window, const int button, const int action, const int mods) { + auto& app = *static_cast(glfwGetWindowUserPointer(window)); + + double x, y; + glfwGetCursorPos(window, &x, &y); + MouseEvent e(static_cast(button), {Int(x), Int(y)}, {static_cast(mods)}); + + if(action == GLFW_PRESS) /* we don't handle GLFW_REPEAT */ + app.mousePressEvent(e); + else if(action == GLFW_RELEASE) + app.mouseReleaseEvent(e); + }); + glfwSetCursorPosCallback(_window, [](GLFWwindow* const window, const double x, const double y) { + auto& app = *static_cast(glfwGetWindowUserPointer(window)); + /* Avoid bogus offset at first -- report 0 when the event is called for + the first time */ + Vector2i position{Int(x), Int(y)}; + MouseMoveEvent e{window, position, + app._previousMouseMovePosition == Vector2i{-1} ? Vector2i{} : + position - app._previousMouseMovePosition}; + app._previousMouseMovePosition = position; + app.mouseMoveEvent(e); + }); + glfwSetScrollCallback(_window, [](GLFWwindow* window, double xoffset, double yoffset) { + MouseScrollEvent e(window, Vector2{Float(xoffset), Float(yoffset)}); + static_cast(glfwGetWindowUserPointer(window))->mouseScrollEvent(e); + }); + glfwSetCharCallback(_window, [](GLFWwindow* window, unsigned int codepoint) { + auto& app = *static_cast(glfwGetWindowUserPointer(window)); + + if(!(app._flags & Flag::TextInputActive)) return; + + /* One extra byte to ensure it gets always null-terminated */ + char utf8[4 + 1]{}; + const std::size_t size = Utility::Unicode::utf8(codepoint, Containers::staticArrayView(utf8).prefix<4>()); + TextInputEvent e{{utf8, size, Containers::StringViewFlag::NullTerminated}}; + app.textInputEvent(e); + }); +} + +GlfwApplication::~GlfwApplication() { + #ifdef MAGNUM_TARGET_GL + /* Destroy Magnum context first to avoid it potentially accessing the + now-destroyed GL context after */ + _context = Containers::NullOpt; + #endif + + glfwDestroyWindow(_window); + for(auto& cursor: _cursors) + glfwDestroyCursor(cursor); + glfwTerminate(); +} + +Vector2i GlfwApplication::windowSize() const { + CORRADE_ASSERT(_window, "Platform::GlfwApplication::windowSize(): no window opened", {}); + + Vector2i size; + glfwGetWindowSize(_window, &size.x(), &size.y()); + return size; +} + +void GlfwApplication::setWindowSize(const Vector2i& size) { + CORRADE_ASSERT(_window, "Platform::GlfwApplication::setWindowSize(): no window opened", ); + + const Vector2i newSize = _dpiScaling*size; + glfwSetWindowSize(_window, newSize.x(), newSize.y()); +} + +#if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 302 +void GlfwApplication::setMinWindowSize(const Vector2i& size) { + CORRADE_ASSERT(_window, "Platform::GlfwApplication::setMinWindowSize(): no window opened", ); + + const Vector2i newSize = _dpiScaling*size; + glfwSetWindowSizeLimits(_window, newSize.x(), newSize.y(), _maxWindowSize.x(), _maxWindowSize.y()); + _minWindowSize = newSize; +} + +void GlfwApplication::setMaxWindowSize(const Vector2i& size) { + CORRADE_ASSERT(_window, "Platform::GlfwApplication::setMaxWindowSize(): no window opened", ); + + const Vector2i newSize = _dpiScaling*size; + glfwSetWindowSizeLimits(_window, _minWindowSize.x(), _minWindowSize.y(), newSize.x(), newSize.y()); + _maxWindowSize = newSize; +} +#endif + +#ifdef MAGNUM_TARGET_GL +Vector2i GlfwApplication::framebufferSize() const { + CORRADE_ASSERT(_window, "Platform::GlfwApplication::framebufferSize(): no window opened", {}); + + Vector2i size; + glfwGetFramebufferSize(_window, &size.x(), &size.y()); + return size; +} +#endif + +void GlfwApplication::setSwapInterval(const Int interval) { + glfwSwapInterval(interval); +} + +void GlfwApplication::redraw() { _flags |= Flag::Redraw; } + +int GlfwApplication::exec() { + while(mainLoopIteration()) {} + + return _exitCode; +} + +bool GlfwApplication::mainLoopIteration() { + /* If exit was requested directly in the constructor, exit immediately + without calling anything else */ + if(_flags & Flag::Exit || glfwWindowShouldClose(_window)) return false; + + CORRADE_ASSERT(_window, "Platform::GlfwApplication::mainLoopIteration(): no window opened", {}); + + /* + If callbacks are not set up yet, do that. Can't be done inside + tryCreate() because: + + 1. On Windows, GLFW fires a viewport event already when creating the + window, which means viewportEvent() gets called even before the + constructor exits. That's not a problem if the window is created + implicitly (because derived class vtable is not setup yet and so + the call goes into the base class no-op viewportEvent()), but when + calling create() / tryCreate() from user constructor, this might + lead to crashes as things touched by viewportEvent() might not be + initialized yet. + 2. On macOS, GLFW might sometimes (hard to reproduce) trigger a draw + event when creating the window. That's even worse than on Windows + because this leads to pure virtual drawEvent() getting called and + the application aborting due to a pure virtual method call in case + GL context is created implicitly by the base class constructor (at + which point the vtable pointers for the derived class are not set + up yet). + */ + if(glfwGetWindowUserPointer(_window) != this) setupCallbacks(); + + /* If redrawing, poll for events immediately after drawEvent() (which could + be setting the Redraw flag again, thus doing constant redraw). If not, + avoid spinning the CPU by waiting for the next input event. */ + if(_flags & Flag::Redraw) { + _flags &= ~Flag::Redraw; + drawEvent(); + glfwPollEvents(); + } else glfwWaitEvents(); + + return !glfwWindowShouldClose(_window); +} + +void GlfwApplication::exit(int exitCode) { + _flags |= Flag::Exit; + _exitCode = exitCode; + + /* If the window is already created, tell GLFW that it should close. If + not, this is done in tryCreate() once the window is created */ + if(_window) glfwSetWindowShouldClose(_window, true); +} + +namespace { + +constexpr Int CursorMap[] { + GLFW_ARROW_CURSOR, + GLFW_IBEAM_CURSOR, + GLFW_CROSSHAIR_CURSOR, + #ifdef GLFW_RESIZE_NWSE_CURSOR + GLFW_RESIZE_NWSE_CURSOR, + GLFW_RESIZE_NESW_CURSOR, + #endif + GLFW_HRESIZE_CURSOR, + GLFW_VRESIZE_CURSOR, + #ifdef GLFW_RESIZE_NWSE_CURSOR + GLFW_RESIZE_ALL_CURSOR, + GLFW_NOT_ALLOWED_CURSOR, + #endif + GLFW_HAND_CURSOR +}; + +} + +void GlfwApplication::setCursor(Cursor cursor) { + CORRADE_ASSERT(_window, "Platform::GlfwApplication::setCursor(): no window opened", ); + + _cursor = cursor; + + if(cursor == Cursor::Hidden) { + glfwSetInputMode(_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + return; + } else if(cursor == Cursor::HiddenLocked) { + glfwSetInputMode(_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + return; + } else { + glfwSetInputMode(_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + + /* The second condition could be a static assert but it doesn't let me + because "this pointer only accessible in a constexpr function". Thanks + for nothing, C++. */ + CORRADE_INTERNAL_ASSERT(UnsignedInt(cursor) < Containers::arraySize(_cursors) && Containers::arraySize(_cursors) == Containers::arraySize(CursorMap)); + + if(!_cursors[UnsignedInt(cursor)]) + _cursors[UnsignedInt(cursor)] = glfwCreateStandardCursor(CursorMap[UnsignedInt(cursor)]); + + glfwSetCursor(_window, _cursors[UnsignedInt(cursor)]); +} + +GlfwApplication::Cursor GlfwApplication::cursor() { + return _cursor; +} + +auto GlfwApplication::MouseMoveEvent::buttons() -> Buttons { + if(!_buttons) { + _buttons = Buttons{}; + for(const Int button: {GLFW_MOUSE_BUTTON_LEFT, + GLFW_MOUSE_BUTTON_MIDDLE, + GLFW_MOUSE_BUTTON_RIGHT}) { + if(glfwGetMouseButton(_window, button) == GLFW_PRESS) + *_buttons |= Button(1 << button); + } + } + + return *_buttons; +} + +auto GlfwApplication::MouseMoveEvent::modifiers() -> Modifiers { + if(!_modifiers) _modifiers = currentGlfwModifiers(_window); + return *_modifiers; +} + +Vector2i GlfwApplication::MouseScrollEvent::position() { + if(!_position) { + Vector2d position; + glfwGetCursorPos(_window, &position.x(), &position.y()); + _position = Vector2i{position}; + } + + return *_position; +} + +auto GlfwApplication::MouseScrollEvent::modifiers() -> Modifiers { + if(!_modifiers) _modifiers = currentGlfwModifiers(_window); + return *_modifiers; +} + +void GlfwApplication::exitEvent(ExitEvent& event) { + event.setAccepted(); +} + +void GlfwApplication::viewportEvent(ViewportEvent&) {} +void GlfwApplication::keyPressEvent(KeyEvent&) {} +void GlfwApplication::keyReleaseEvent(KeyEvent&) {} +void GlfwApplication::mousePressEvent(MouseEvent&) {} +void GlfwApplication::mouseReleaseEvent(MouseEvent&) {} +void GlfwApplication::mouseMoveEvent(MouseMoveEvent&) {} +void GlfwApplication::mouseScrollEvent(MouseScrollEvent&) {} +void GlfwApplication::textInputEvent(TextInputEvent&) {} + +bool GlfwApplication::isTextInputActive() const { + return !!(_flags & Flag::TextInputActive); +} + +void GlfwApplication::startTextInput() { + _flags |= Flag::TextInputActive; +} + +void GlfwApplication::stopTextInput() { + _flags &= ~Flag::TextInputActive; +} + +#ifdef MAGNUM_TARGET_GL +GlfwApplication::GLConfiguration::GLConfiguration(): + _colorBufferSize{8, 8, 8, 8}, _depthBufferSize{24}, _stencilBufferSize{0}, + _sampleCount{0}, _version{GL::Version::None}, _srgbCapable{false} +{ + #ifndef MAGNUM_TARGET_GLES + addFlags(Flag::ForwardCompatible); + #endif +} + +GlfwApplication::GLConfiguration::~GLConfiguration() = default; +#endif + +GlfwApplication::Configuration::Configuration(): + _title{Containers::String::nullTerminatedGlobalView("Magnum GLFW Application"_s)}, + _size{800, 600}, + _windowFlags{WindowFlag::Focused}, + _dpiScalingPolicy{DpiScalingPolicy::Default} {} + +GlfwApplication::Configuration::~Configuration() = default; + +#if defined(DOXYGEN_GENERATING_OUTPUT) || GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 302 +Containers::StringView GlfwApplication::KeyEvent::keyName(const Key key) { + return glfwGetKeyName(int(key), 0); +} + +Containers::StringView GlfwApplication::KeyEvent::keyName() const { + return keyName(_key); +} +#endif + +template class BasicScreen; +template class BasicScreenedApplication; + +}} diff --git a/clipboard.txt b/clipboard.txt new file mode 100644 index 0000000000..ebd299b3de --- /dev/null +++ b/clipboard.txt @@ -0,0 +1,18 @@ + +BASE_URL=https://us.download.nvidia.com/tesla +DRIVER_VERSION=460.106.00 +curl -fSsl -O $BASE_URL/$DRIVER_VERSION/NVIDIA-Linux-x86_64-$DRIVER_VERSION.run +sudo sh NVIDIA-Linux-x86_64-$DRIVER_VERSION.run + + +python -m habitat_sim.utils.datasets_download --uids replica_cad_dataset + +/mnt/users/siro/habitat-sim/build/utils/viewer/viewer \ +--dataset /mnt/users/siro/habitat-lab/data/replica_cad/replicaCAD.scene_dataset_config.json \ +v3_sc0_staging_00 + +./build/utils/viewer/viewer \ +--dataset /Users/eundersander/projects/habitat-lab4/data/replica_cad/replicaCAD.scene_dataset_config.json \ +v3_sc0_staging_00 + +habitat-viewer --enable-physics --dataset /path/to/data/fpss/fphab/fphab.scene_dataset_config.json -- 108294897_176710602.scene_instance.json \ No newline at end of file diff --git a/minimal_magnum_app.py b/minimal_magnum_app.py new file mode 100644 index 0000000000..135b2411ad --- /dev/null +++ b/minimal_magnum_app.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# Copyright (c) Meta Platforms, Inc. and its affiliates. +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +import ctypes + +# must call this before importing habitat or magnum! avoids EGL_BAD_ACCESS error on some platforms +import sys + +flags = sys.getdlopenflags() +sys.setdlopenflags(flags | ctypes.RTLD_GLOBAL) + +import magnum as mn +from magnum.platform.glfw import Application + + +class MyApplication(Application): + def __init__(self, args): + super().__init__(args) + self.anim_fraction = 0 + + def draw_event(self): + self.anim_fraction = (self.anim_fraction + 0.05) % 1 + + mn.gl.Renderer.clear_color = mn.Color4(self.anim_fraction, 0, 0, 1) + mn.gl.default_framebuffer.clear(mn.gl.FramebufferClear.COLOR) + self.swap_buffers() + self.redraw() + + +if __name__ == "__main__": + glfw_config = Application.Configuration() + glfw_config.title = "Sandbox App" + glfw_config.size = (400, 300) + app = MyApplication(glfw_config) + app.exec() diff --git a/src/utils/viewer/CMakeLists.txt b/src/utils/viewer/CMakeLists.txt index fcffe3616e..e467386e12 100644 --- a/src/utils/viewer/CMakeLists.txt +++ b/src/utils/viewer/CMakeLists.txt @@ -6,6 +6,7 @@ find_package(Magnum REQUIRED DebugTools Text) find_package(MagnumPlugins REQUIRED StbTrueTypeFont) set(viewer_SOURCES viewer.cpp ObjectPickingHelper.cpp ObjectPickingHelper.h) +# set(viewer_SOURCES minimal_magnum_app.cpp) corrade_add_resource(viewer_RESOURCES resources.conf) diff --git a/src/utils/viewer/minimal_glfw.cpp b/src/utils/viewer/minimal_glfw.cpp new file mode 100644 index 0000000000..1b3c8ee07b --- /dev/null +++ b/src/utils/viewer/minimal_glfw.cpp @@ -0,0 +1,104 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 — Vladimír Vondruš + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + 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 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 +#include +#include +#include +#include +#include +#include + +using namespace Magnum; + +int main(int argc, char** argv) { + /* Initialize the library */ + if (!glfwInit()) + return -1; + + auto myErrorFun = [](int errorCode, const char* msg) { + printf("error: %s\n", msg); + }; + + glfwSetErrorCallback(myErrorFun); + + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + + /* Create a windowed mode window and its OpenGL context */ + GLFWwindow* const window = glfwCreateWindow( + 800, 600, "Magnum Plain GLFW Triangle Example", nullptr, nullptr); + if (!window) { + glfwTerminate(); + return -1; + } + + /* Make the window's context current */ + glfwMakeContextCurrent(window); + + { + /* Create Magnum context in an isolated scope */ + Platform::GLContext ctx{argc, argv}; + + /* Setup the colored triangle */ + using namespace Math::Literals; + + struct TriangleVertex { + Vector2 position; + Color3 color; + }; + const TriangleVertex vertices[]{ + {{-0.5f, -0.5f}, 0xff0000_rgbf}, /* Left vertex, red color */ + {{0.5f, -0.5f}, 0x00ff00_rgbf}, /* Right vertex, green color */ + {{0.0f, 0.5f}, 0x0000ff_rgbf} /* Top vertex, blue color */ + }; + + GL::Mesh mesh; + mesh.setCount(Containers::arraySize(vertices)) + .addVertexBuffer(GL::Buffer{vertices}, 0, + Shaders::VertexColorGL2D::Position{}, + Shaders::VertexColorGL2D::Color3{}); + + Shaders::VertexColorGL2D shader; + + /* Loop until the user closes the window */ + while (!glfwWindowShouldClose(window)) { + /* Render here */ + GL::defaultFramebuffer.clear(GL::FramebufferClear::Color); + shader.draw(mesh); + + /* Swap front and back buffers */ + glfwSwapBuffers(window); + + /* Poll for and process events */ + glfwPollEvents(); + } + } + + glfwTerminate(); +} diff --git a/src/utils/viewer/minimal_magnum_app.cpp b/src/utils/viewer/minimal_magnum_app.cpp new file mode 100644 index 0000000000..1ee718a5b2 --- /dev/null +++ b/src/utils/viewer/minimal_magnum_app.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Meta Platforms, Inc. and its affiliates. +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Setup the colored triangle */ +using namespace Magnum::Math::Literals; + +class MyApplication : public Magnum::Platform::Application { + public: + explicit MyApplication(const Arguments& arguments) + : Magnum::Platform::Application{ + arguments, + Configuration{}.setTitle("MyApplication").setSize({400, 300})} { + // _ctx = std::make_unique(); + + // struct TriangleVertex { + // Magnum::Vector2 position; + // Magnum::Color3 color; + // }; + // const TriangleVertex vertices[]{ + // {{-0.5f, -0.5f}, 0xff0000_rgbf}, /* Left vertex, red color */ + // {{0.5f, -0.5f}, 0x00ff00_rgbf}, /* Right vertex, green color */ + // {{0.0f, 0.5f}, 0x0000ff_rgbf} /* Top vertex, blue color */ + // }; + + // _mesh.setCount(Corrade::Containers::arraySize(vertices)) + // .addVertexBuffer(Magnum::GL::Buffer{vertices}, 0, + // Magnum::Shaders::VertexColorGL2D::Position{}, + // Magnum::Shaders::VertexColorGL2D::Color3{}); + } + + void drawEvent() override { + /* Render here */ + static float animFraction = 0.f; + animFraction = fmodf(animFraction + 0.05, 1.0); + + Magnum::GL::Renderer::setClearColor(Magnum::Color4(animFraction, 0, 0, 1)); + Magnum::GL::defaultFramebuffer.clear(Magnum::GL::FramebufferClear::Color); + // _shader.draw(_mesh); + + swapBuffers(); + redraw(); + } + + private: + std::unique_ptr _ctx; + Magnum::GL::Mesh _mesh; + Magnum::Shaders::VertexColorGL2D _shader; +}; + +MAGNUM_APPLICATION_MAIN(MyApplication)