From 090c6bb4a7dec1db810f9c63ddfcd253f89337dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 16 Nov 2019 17:55:14 +0100 Subject: [PATCH] Platform: window icon management in Glfw and Sdl2Application. --- doc/changelog.dox | 3 + package/ci/appveyor-desktop-vulkan.bat | 5 +- package/ci/travis-desktop-vulkan.sh | 7 +- src/Magnum/Platform/GlfwApplication.cpp | 62 ++++++++++++++++++ src/Magnum/Platform/GlfwApplication.h | 18 +++++ src/Magnum/Platform/Sdl2Application.cpp | 28 ++++++++ src/Magnum/Platform/Sdl2Application.h | 21 ++++++ src/Magnum/Platform/Test/CMakeLists.txt | 15 +++++ .../Platform/Test/GlfwApplicationTest.cpp | 37 +++++++++-- .../Platform/Test/Sdl2ApplicationTest.cpp | 39 ++++++++--- src/Magnum/Platform/Test/icon-16.tga | Bin 0 -> 1042 bytes src/Magnum/Platform/Test/icon-32.tga | Bin 0 -> 4114 bytes src/Magnum/Platform/Test/icon-64.tga | Bin 0 -> 16402 bytes src/Magnum/Platform/Test/resources.conf | 12 ++++ 14 files changed, 227 insertions(+), 20 deletions(-) create mode 100644 src/Magnum/Platform/Test/icon-16.tga create mode 100644 src/Magnum/Platform/Test/icon-32.tga create mode 100644 src/Magnum/Platform/Test/icon-64.tga create mode 100644 src/Magnum/Platform/Test/resources.conf diff --git a/doc/changelog.dox b/doc/changelog.dox index 39617ac63..851e887eb 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -54,6 +54,9 @@ See also: @ref Platform::GlfwApplication::setCursor() and @ref Platform::EmscriptenApplication::setCursor() (see [mosra/magnum#383](https://github.com/mosra/magnum/pull/383)) +- Window icon management using @ref Platform::Sdl2Application::setWindowIcon() + and @ref Platform::GlfwApplication::setWindowIcon() (see + [mosra/magnum#393](https://github.com/mosra/magnum/issues/393)) - Added @ref Platform::GlfwApplication::warpCursor() to match the equivalent API in @ref Platform::Sdl2Application (see [mosra/magnum#383](https://github.com/mosra/magnum/pull/383)) diff --git a/package/ci/appveyor-desktop-vulkan.bat b/package/ci/appveyor-desktop-vulkan.bat index 8d40d85dd..1b6ed74a1 100644 --- a/package/ci/appveyor-desktop-vulkan.bat +++ b/package/ci/appveyor-desktop-vulkan.bat @@ -21,7 +21,8 @@ cl.exe /c package/ci/libvulkan.cpp || exit /b lib.exe /OUT:%APPVEYOR_BUILD_FOLDER%/deps/lib/libvulkan.lib libvulkan.obj || exit /b rem Enabling only stuff that's directly affected by Vulkan (which means also -rem parts of Platform), disabling everything else. +rem parts of Platform, which need Trade for icon import in tests), disabling +rem everything else. mkdir build && cd build || exit /b cmake .. ^ -DCMAKE_BUILD_TYPE=Debug ^ @@ -37,7 +38,7 @@ cmake .. ^ -DWITH_SHADERS=OFF ^ -DWITH_TEXT=OFF ^ -DWITH_TEXTURETOOLS=OFF ^ - -DWITH_TRADE=OFF ^ + -DWITH_TRADE=ON ^ -DWITH_VK=ON ^ -DWITH_ANYAUDIOIMPORTER=OFF ^ -DWITH_ANYIMAGECONVERTER=OFF ^ diff --git a/package/ci/travis-desktop-vulkan.sh b/package/ci/travis-desktop-vulkan.sh index c3f537332..efe4e8cf3 100755 --- a/package/ci/travis-desktop-vulkan.sh +++ b/package/ci/travis-desktop-vulkan.sh @@ -12,7 +12,7 @@ cmake .. \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_DEPRECATED=$BUILD_DEPRECATED \ -DWITH_INTERCONNECT=OFF \ - -DWITH_PLUGINMANAGER=OFF \ + -DWITH_PLUGINMANAGER=ON \ -G Ninja ninja install cd ../.. @@ -22,7 +22,8 @@ cd ../.. g++ package/ci/libvulkan.cpp -std=c++11 -shared -o $HOME/libvulkan.so # Enabling only stuff that's directly affected by Vulkan (which means also -# parts of Platform), disabling everything else. +# parts of Platform, which need Trade for icon import in tests), disabling +# everything else. mkdir build && cd build # Not using CXXFLAGS in order to avoid affecting dependencies cmake .. \ @@ -39,7 +40,7 @@ cmake .. \ -DWITH_SHADERS=OFF \ -DWITH_TEXT=OFF \ -DWITH_TEXTURETOOLS=OFF \ - -DWITH_TRADE=OFF \ + -DWITH_TRADE=ON \ -DWITH_VK=ON \ -DWITH_AL_INFO=OFF \ -DWITH_GL_INFO=OFF \ diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index f53a31a9e..f03770897 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -28,10 +28,14 @@ #include #include +#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" @@ -230,6 +234,64 @@ void GlfwApplication::setWindowTitle(const std::string& title) { glfwSetWindowTitle(_window, title.data()); } +#if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 302 +void GlfwApplication::setWindowIcon(const ImageView2D& image) { + setWindowIcon({image}); +} + +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(std::initializer_list 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(false, "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); +} +#endif + bool GlfwApplication::tryCreate(const Configuration& configuration) { #ifdef MAGNUM_TARGET_GL #ifdef GLFW_NO_API diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index a0dc61eba..a23cd86ec 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -427,6 +427,24 @@ class GlfwApplication { */ void setWindowTitle(const std::string& title); + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 302 || defined(DOXYGEN_GENERATING_OUTPUT) + /** + * @brief Set window icon + * + * The @p images are expected to be with origin at bottom left (which + * is the default for imported images) and in one of + * @ref PixelFormat::RGB8Unorm, @ref PixelFormat::RGB8Srgb, + * @ref PixelFormat::RGBA8Unorm or @ref PixelFormat::RGBA8Srgb formats. + * If you have just one image, you can use + * @ref setWindowIcon(const ImageView2D&) instead. + * @note Available since GLFW 3.2. The function has no effect on macOS + * / Wayland, see @m_class{m-doc-external} [glfwSetWindowIcon()](https://www.glfw.org/docs/latest/group__window.html#gadd7ccd39fe7a7d1f0904666ae5932dc5) + * for more information. + */ + void setWindowIcon(std::initializer_list images); + void setWindowIcon(const ImageView2D& image); /**< @overload */ + #endif + /** * @brief Swap buffers * diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 3617ec6cf..332936f0f 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -35,6 +35,8 @@ #endif #include +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" #include "Magnum/Math/ConfigurationValue.h" #include "Magnum/Math/Range.h" #include "Magnum/Platform/ScreenedApplication.hpp" @@ -275,6 +277,32 @@ void Sdl2Application::setWindowTitle(const std::string& title) { #endif } +#if !defined(CORRADE_TARGET_EMSCRIPTEN) && SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 +void Sdl2Application::setWindowIcon(const ImageView2D& image) { + Uint32 format; /** @todo handle sRGB differently? */ + switch(image.format()) { + case PixelFormat::RGB8Srgb: + case PixelFormat::RGB8Unorm: + format = SDL_PIXELFORMAT_RGB24; + break; + case PixelFormat::RGBA8Srgb: + case PixelFormat::RGBA8Unorm: + format = SDL_PIXELFORMAT_RGBA32; + break; + default: + CORRADE_ASSERT(false, "Platform::Sdl2Application::setWindowIcon(): unexpected format" << image.format(), ); + } + + /* Images are loaded with origin at bottom left, flip it to top left. + Fortunately SDL accepts negative stride, so we don't need to do an + expensive flip ourselves. */ + Containers::StridedArrayView3D pixels = image.pixels().flipped<0>(); + SDL_Surface* icon = SDL_CreateRGBSurfaceWithFormatFrom(const_cast(pixels.data()) , image.size().x(), image.size().y(), 32, pixels.stride()[0], format); + SDL_SetWindowIcon(_window, icon); + SDL_FreeSurface(icon); +} +#endif + bool Sdl2Application::tryCreate(const Configuration& configuration) { #ifdef MAGNUM_TARGET_GL if(!(configuration.windowFlags() & Configuration::WindowFlag::Contextless)) diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index d007adaf0..86d210003 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -702,6 +702,27 @@ class Sdl2Application { */ void setWindowTitle(const std::string& title); + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && (SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 || defined(DOXYGEN_GENERATING_OUTPUT)) + /** + * @brief Set window icon + * + * The @p image is expected to be with origin at bottom left (which is + * the default for imported images) and in one of + * @ref PixelFormat::RGB8Unorm, @ref PixelFormat::RGB8Srgb, + * @ref PixelFormat::RGBA8Unorm or @ref PixelFormat::RGBA8Srgb formats. + * Unlike @ref GlfwApplication::setWindowIcon(), SDL doesn't provide a + * way to supply multiple images in different sizes. + * @note Available since SDL 2.0.5. Not available on + * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", use + * @cb{.html} @ce in your HTML markup instead. + * Although it's not documented in SDL itself, the function might + * have no effect on macOS / Wayland, similarly to how + * @ref GlfwApplication::setWindowIcon() behaves on those + * platforms. + */ + void setWindowIcon(const ImageView2D& image); + #endif + #if defined(CORRADE_TARGET_EMSCRIPTEN) || defined(DOXYGEN_GENERATING_OUTPUT) /** * @brief Set container CSS class diff --git a/src/Magnum/Platform/Test/CMakeLists.txt b/src/Magnum/Platform/Test/CMakeLists.txt index c882339dc..273fd9be9 100644 --- a/src/Magnum/Platform/Test/CMakeLists.txt +++ b/src/Magnum/Platform/Test/CMakeLists.txt @@ -25,6 +25,11 @@ find_package(Corrade REQUIRED Main) +# Icons for SDL/GLFW +if(NOT CORRADE_TARGET_EMSCRIPTEN AND (WITH_SDL2APPLICATION OR WITH_GLFWAPPLICATION)) + corrade_add_resource(Platform_RESOURCES resources.conf) +endif() + if(WITH_ANDROIDAPPLICATION) add_library(PlatformAndroidApplicationTest SHARED AndroidApplicationTest.cpp) target_link_libraries(PlatformAndroidApplicationTest PRIVATE MagnumAndroidApplication) @@ -52,6 +57,11 @@ if(WITH_GLFWAPPLICATION) add_executable(PlatformGlfwApplicationTest WIN32 GlfwApplicationTest.cpp) # HiDPi.manifest not needed, as GLFW sets that on its own target_link_libraries(PlatformGlfwApplicationTest PRIVATE MagnumGlfwApplication Corrade::Main) + # Window icon loading + if(NOT CORRADE_TARGET_EMSCRIPTEN) + target_sources(PlatformGlfwApplicationTest PRIVATE ${Platform_RESOURCES}) + target_link_libraries(PlatformGlfwApplicationTest PRIVATE MagnumTrade) + endif() set_target_properties(PlatformGlfwApplicationTest PROPERTIES FOLDER "Magnum/Platform/Test") endif() @@ -71,6 +81,11 @@ if(WITH_SDL2APPLICATION) endif() endif() target_link_libraries(PlatformSdl2ApplicationTest PRIVATE MagnumSdl2Application Corrade::Main) + # Window icon loading + if(NOT CORRADE_TARGET_EMSCRIPTEN) + target_sources(PlatformSdl2ApplicationTest PRIVATE ${Platform_RESOURCES}) + target_link_libraries(PlatformSdl2ApplicationTest PRIVATE MagnumTrade) + endif() set_target_properties(PlatformSdl2ApplicationTest PROPERTIES FOLDER "Magnum/Platform/Test") if(CORRADE_TARGET_EMSCRIPTEN) add_custom_command(TARGET PlatformSdl2ApplicationTest POST_BUILD diff --git a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp index efb3db2f7..9ea1ef775 100644 --- a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp +++ b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp @@ -23,20 +23,20 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include +#include +#include "Magnum/ImageView.h" #include "Magnum/Platform/GlfwApplication.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/ImageData.h" namespace Magnum { namespace Platform { namespace Test { namespace { struct GlfwApplicationTest: Platform::Application { - explicit GlfwApplicationTest(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)} { - Debug{} << "window size" << windowSize() - #ifdef MAGNUM_TARGET_GL - << framebufferSize() - #endif - << dpiScaling(); - } + explicit GlfwApplicationTest(const Arguments& arguments); /* For testing HiDPI resize events */ void viewportEvent(ViewportEvent& event) override { @@ -93,6 +93,29 @@ struct GlfwApplicationTest: Platform::Application { void drawEvent() override {} }; +GlfwApplicationTest::GlfwApplicationTest(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)} { + /* For testing resize events */ + Debug{} << "window size" << windowSize() + #ifdef MAGNUM_TARGET_GL + << framebufferSize() + #endif + << dpiScaling(); + + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 302 + Utility::Resource rs{"icons"}; + PluginManager::Manager manager; + Containers::Pointer importer; + Containers::Optional image16, image32, image64; + if((importer = manager.loadAndInstantiate("AnyImageImporter")) && + importer->openData(rs.getRaw("icon-16.tga")) && (image16 = importer->image2D(0)) && + importer->openData(rs.getRaw("icon-32.tga")) && (image32 = importer->image2D(0)) && + importer->openData(rs.getRaw("icon-64.tga")) && (image64 = importer->image2D(0))) setWindowIcon({*image16, *image32, *image64}); + else Warning{} << "Can't load the plugin / images, not setting window icon"; + #else + Debug{} << "GLFW too old, can't set window icon"; + #endif +} + }}}} MAGNUM_APPLICATION_MAIN(Magnum::Platform::Test::GlfwApplicationTest) diff --git a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp index 8e74cfa4f..9e4cbd2c6 100644 --- a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp +++ b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp @@ -23,23 +23,22 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include +#include +#include "Magnum/ImageView.h" #include "Magnum/Platform/Sdl2Application.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/ImageData.h" #include namespace Magnum { namespace Platform { namespace Test { namespace { struct Sdl2ApplicationTest: Platform::Application { - /* For testing resize events */ - explicit Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)} { - Debug{} << "window size" << windowSize() - #ifdef MAGNUM_TARGET_GL - << framebufferSize() - #endif - << dpiScaling(); - } + explicit Sdl2ApplicationTest(const Arguments& arguments); void exitEvent(ExitEvent& event) override { Debug{} << "application exiting"; @@ -129,6 +128,30 @@ struct Sdl2ApplicationTest: Platform::Application { #endif }; +Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)} { + /* For testing resize events */ + Debug{} << "window size" << windowSize() + #ifdef MAGNUM_TARGET_GL + << framebufferSize() + #endif + << dpiScaling(); + + #ifndef CORRADE_TARGET_EMSCRIPTEN + #if SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 + Utility::Resource rs{"icons"}; + PluginManager::Manager manager; + Containers::Pointer importer; + Containers::Optional image; + if((importer = manager.loadAndInstantiate("AnyImageImporter")) && + importer->openData(rs.getRaw("icon-64.tga")) && + (image = importer->image2D(0))) setWindowIcon(*image); + else Warning{} << "Can't load the plugin / file, not setting window icon"; + #else + Debug{} << "SDL too old, can't set window icon"; + #endif + #endif +} + }}}} MAGNUM_APPLICATION_MAIN(Magnum::Platform::Test::Sdl2ApplicationTest) diff --git a/src/Magnum/Platform/Test/icon-16.tga b/src/Magnum/Platform/Test/icon-16.tga new file mode 100644 index 0000000000000000000000000000000000000000..907fb99dbbf5f78cc05f08c55a879461499fd0ab GIT binary patch literal 1042 zcmZQzU}As)0R{mE1qMBJrT?Q?>HqQx<^NxuT>3vQz!@GM4-U-xe}Dg6I6pqn1*~RS ze;Hg17Iwo@|H1jF{tqJT|Ei2|aM)NHYW;tCV#)s}M;88fu`&XS6(t3M!*5P&9?tN4 zbZGwn)srj#?_b;wR(o^z%>TD{&-%ZxtLXpL?bE?xr70m8`lmPL{6D>J!v7$5YcLIp z%Zr<*{P%UT_+OG7^#AVuIsacAU;KaF)aw7nI%*jDVetr~&Gj@;)EHG!2ZV0Zf};5?yXG0T0UIeJ{_*+^0sMM z)$~=R!PS7wObz$Mst!5rjoLpN{-fz{H2=ZU0Iu@q#;zIgvIST9GpRNMu7;ZBkG_WT z|7XV*!_CHJ|H7^!xEhfC@qsQRmw%vg*2~@uMPH<^1B$sXPb~Rgn;nUw2IR&w>nEV7 z^K-U9QG;CW!r18LFUZ{w4$S-Cm>2!u+rjLAtiRL$Q)~NC%!S3(iizd_!@TVO`#W3y zpV^#?qUOor1^TMFa;BiFg0IGI$F zjuNI}UUvVt&1v}m;`kD4>3tfThd a-PZWOg}w$(HRyUq`J?U6(KJB+Gynk0zhGkk literal 0 HcmV?d00001 diff --git a/src/Magnum/Platform/Test/icon-64.tga b/src/Magnum/Platform/Test/icon-64.tga new file mode 100644 index 0000000000000000000000000000000000000000..396f940ce058c5a68150ec5d51ddd3f1dec5d4b6 GIT binary patch literal 16402 zcmeHM?MqW(6d(O1QIXC}Hyx$oZcc9(pxzBBD?86y6T(&*?o%1{QcV6~fjb@!jdKooF z&3a8<)&`(OF(3_)21o;>0nz|zfHXiFAPtZPNCTt+(g0~7;5`YSBO}bE*KQI%LwFYQ zGI1UUJ;FGJxtp0d=&RpzTo_A9 z#GjwzbpJ{Jyj-LF7q9vNbmncV;0&fH}Bt%iS9v5tv@{QVwB#+`p*o%sGL z%=B9g|5i%{69@VCpKVJU|DhfyKYmlmdl|4MWM@Hw2*R`*Z(mSQm< z?gO3nPycYAuqG9Q>+T~d<<8UpyC=62wrus4@cwB)^}YKr8-QIq^a&iacevREALoWu zW8t$^JQq__+eG%V@_%luca^Quf?SyLbqV{u^k5Knm&^By%A$Oj9qkduLc81YvRC;d z!cAlk)?Z)uvA>qfUN39UT5F8=fUhs_$oju{e*n%qTA(0D`F|5?VS4C-Y