diff --git a/modules/FindGLFW.cmake b/modules/FindGLFW.cmake new file mode 100644 index 0000000..2e67547 --- /dev/null +++ b/modules/FindGLFW.cmake @@ -0,0 +1,94 @@ +#.rst: +# Find GLFW +# --------- +# +# Finds the GLFW library using its cmake config if that exists, otherwise +# falls back to finding it manually. This module defines: +# +# GLFW_FOUND - True if GLFW library is found +# GLFW::GLFW - GLFW imported target +# +# Additionally, in case the config was not found, these variables are defined +# for internal usage: +# +# GLFW_LIBRARY - GLFW library +# GLFW_INCLUDE_DIR - Root include dir +# + +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 +# Vladimír Vondruš +# Copyright © 2016 Jonathan Hale +# +# 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. +# + +# GLFW installs cmake package config files to shared/ folder which handles +# dependencies in case GLFW is built statically. Try to find first, quietly, so +# it doesn't print loud messages when it's not found, since that's okay. +find_package(glfw3 CONFIG QUIET) + +if(TARGET glfw) + if(NOT TARGET GLFW::GLFW) + # Aliases of (global) targets are only supported in CMake 3.11, so we + # work around it by this. This is easier than fetching all possible + # properties (which are impossible to track of) and then attempting to + # rebuild them into a new target. + add_library(GLFW::GLFW INTERFACE IMPORTED) + set_target_properties(GLFW::GLFW PROPERTIES INTERFACE_LINK_LIBRARIES glfw) + endif() + + # Just to make FPHSA print some meaningful location, nothing else + get_target_property(_GLFW_INTERFACE_INCLUDE_DIRECTORIES glfw INTERFACE_INCLUDE_DIRECTORIES) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args("GLFW" DEFAULT_MSG + _GLFW_INTERFACE_INCLUDE_DIRECTORIES) + return() +endif() + +# In case no config file was found, try manually finding the library. +find_library(GLFW_LIBRARY NAMES glfw glfw3) + +# Include dir +find_path(GLFW_INCLUDE_DIR + NAMES GLFW/glfw3.h) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args("GLFW" DEFAULT_MSG + GLFW_LIBRARY + GLFW_INCLUDE_DIR) + +if(NOT TARGET GLFW::GLFW) + add_library(GLFW::GLFW UNKNOWN IMPORTED) + + # Work around BUGGY framework support on macOS + # https://cmake.org/Bug/view.php?id=14105 + if(CORRADE_TARGET_APPLE AND ${GLFW_LIBRARY} MATCHES "\\.framework$") + set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY}/GLFW) + else() + set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY}) + endif() + + set_property(TARGET GLFW::GLFW PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${GLFW_INCLUDE_DIR}) +endif() + +mark_as_advanced(GLFW_LIBRARY GLFW_INCLUDE_DIR) diff --git a/modules/FindSDL2.cmake b/modules/FindSDL2.cmake new file mode 100644 index 0000000..850d0d6 --- /dev/null +++ b/modules/FindSDL2.cmake @@ -0,0 +1,173 @@ +#.rst: +# Find SDL2 +# --------- +# +# Finds the SDL2 library. This module defines: +# +# SDL2_FOUND - True if SDL2 library is found +# SDL2::SDL2 - SDL2 imported target +# +# Additionally these variables are defined for internal usage: +# +# SDL2_LIBRARY_DEBUG - SDL2 debug library, if found +# SDL2_LIBRARY_RELEASE - SDL2 release library, if found +# SDL2_INCLUDE_DIR - Root include dir +# + +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 +# Vladimír Vondruš +# Copyright © 2018 Jonathan Hale +# +# 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. +# + +# In Emscripten SDL is linked automatically, thus no need to find the library. +# Also the includes are in SDL subdirectory, not SDL2. +if(CORRADE_TARGET_EMSCRIPTEN) + set(_SDL2_PATH_SUFFIXES SDL) +else() + set(_SDL2_PATH_SUFFIXES SDL2) + if(WIN32) + # Precompiled libraries for MSVC are in x86/x64 subdirectories + if(MSVC) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_SDL2_LIBRARY_PATH_SUFFIX lib/x64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(_SDL2_LIBRARY_PATH_SUFFIX lib/x86) + endif() + + # Both includes and libraries for MinGW are in some directory deep + # inside. There's also a CMake config file but it has HARDCODED path + # to /opt/local/i686-w64-mingw32, which doesn't make ANY SENSE, + # especially on Windows. + elseif(MINGW) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_SDL2_LIBRARY_PATH_SUFFIX x86_64-w64-mingw32/lib) + list(APPEND _SDL2_PATH_SUFFIXES x86_64-w64-mingw32/include/SDL2) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(_SDL2_LIBRARY_PATH_SUFFIX i686-w64-mingw32/lib) + list(APPEND _SDL2_PATH_SUFFIXES i686-w64-mingw32/include/SDL2) + endif() + endif() + endif() + + find_library(SDL2_LIBRARY_RELEASE + # Compiling SDL2 from scratch on macOS creates dead libSDL2.so symlink + # which CMake somehow prefers before the SDL2-2.0.dylib file. Making + # the dylib first so it is preferred. Not sure how this maps to debug + # config though :/ + NAMES SDL2-2.0 SDL2 + PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX}) + find_library(SDL2_LIBRARY_DEBUG + NAMES SDL2d + PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX}) + # FPHSA needs one of the _DEBUG/_RELEASE variables to check that the + # library was found -- using SDL_LIBRARY, which will get populated by + # select_library_configurations() below. + set(SDL2_LIBRARY_NEEDED SDL2_LIBRARY) +endif() + +include(SelectLibraryConfigurations) +select_library_configurations(SDL2) + +# Include dir +find_path(SDL2_INCLUDE_DIR + # We must search file which is present only in SDL2 and not in SDL1. + # Apparently when both SDL.h and SDL_scancode.h are specified, CMake is + # happy enough that it found SDL.h and doesn't bother about the other. + # + # On macOS, where the includes are not in SDL2/SDL.h form (which would + # solve this issue), but rather SDL2.framework/Headers/SDL.h, CMake might + # find SDL.framework/Headers/SDL.h if SDL1 is installed, which is wrong. + NAMES SDL_scancode.h + PATH_SUFFIXES ${_SDL2_PATH_SUFFIXES}) + +# iOS dependencies +if(CORRADE_TARGET_IOS) + set(_SDL2_FRAMEWORKS + AudioToolbox + AVFoundation + CoreGraphics + CoreMotion + Foundation + GameController + QuartzCore + UIKit) + set(_SDL2_FRAMEWORK_LIBRARIES ) + foreach(framework ${_SDL2_FRAMEWORKS}) + find_library(_SDL2_${framework}_LIBRARY ${framework}) + mark_as_advanced(_SDL2_${framework}_LIBRARY) + list(APPEND _SDL2_FRAMEWORK_LIBRARIES ${_SDL2_${framework}_LIBRARY}) + list(APPEND _SDL2_FRAMEWORK_LIBRARY_NAMES _SDL2_${framework}_LIBRARY) + endforeach() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args("SDL2" DEFAULT_MSG + ${SDL2_LIBRARY_NEEDED} + ${_SDL2_FRAMEWORK_LIBRARY_NAMES} + SDL2_INCLUDE_DIR) + +if(NOT TARGET SDL2::SDL2) + if(SDL2_LIBRARY_NEEDED) + add_library(SDL2::SDL2 UNKNOWN IMPORTED) + + # Work around BUGGY framework support on macOS + # https://cmake.org/Bug/view.php?id=14105 + if(CORRADE_TARGET_APPLE AND SDL2_LIBRARY_RELEASE MATCHES "\\.framework$") + set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) + set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE}/SDL2) + else() + if(SDL2_LIBRARY_RELEASE) + set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) + set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE}) + endif() + + if(SDL2_LIBRARY_DEBUG) + set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) + set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_DEBUG ${SDL2_LIBRARY_DEBUG}) + endif() + endif() + + # Link additional `dl` and `pthread` libraries required by a static + # build of SDL on Unixy platforms (except Apple, where it is most + # probably some frameworks instead) + if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND SDL2_LIBRARY MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$") + find_package(Threads) + set_property(TARGET SDL2::SDL2 APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) + endif() + + # Link frameworks on iOS + if(CORRADE_TARGET_IOS) + set_property(TARGET SDL2::SDL2 APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${_SDL2_FRAMEWORK_LIBRARIES}) + endif() + else() + add_library(SDL2::SDL2 INTERFACE IMPORTED) + endif() + + set_property(TARGET SDL2::SDL2 PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}) +endif() + +mark_as_advanced(SDL2_INCLUDE_DIR) diff --git a/src/python/magnum/platform/CMakeLists.txt b/src/python/magnum/platform/CMakeLists.txt index 86b3d37..b345a2a 100644 --- a/src/python/magnum/platform/CMakeLists.txt +++ b/src/python/magnum/platform/CMakeLists.txt @@ -25,9 +25,31 @@ # *Not* REQUIRED find_package(Magnum COMPONENTS + GlfwApplication + Sdl2Application WindowlessEglApplication WindowlessGlxApplication) +if(Magnum_GlfwApplication_FOUND) + pybind11_add_module(magnum_platform_glfw glfw.cpp) + target_link_libraries(magnum_platform_glfw PRIVATE Magnum::GlfwApplication) + target_include_directories(magnum_platform_glfw PRIVATE ${CMAKE_SOURCE_DIR}/src/python) + set_target_properties(magnum_platform_glfw PROPERTIES + FOLDER "python/platform" + OUTPUT_NAME "glfw" + LIBRARY_OUTPUT_DIRECTORY ${output_dir}/magnum/platform) +endif() + +if(Magnum_Sdl2Application_FOUND) + pybind11_add_module(magnum_platform_sdl2 sdl2.cpp) + target_link_libraries(magnum_platform_sdl2 PRIVATE Magnum::Sdl2Application) + target_include_directories(magnum_platform_sdl2 PRIVATE ${CMAKE_SOURCE_DIR}/src/python) + set_target_properties(magnum_platform_sdl2 PROPERTIES + FOLDER "python/platform" + OUTPUT_NAME "sdl2" + LIBRARY_OUTPUT_DIRECTORY ${output_dir}/magnum/platform) +endif() + if(Magnum_WindowlessEglApplication_FOUND) pybind11_add_module(magnum_platform_egl egl.cpp) target_link_libraries(magnum_platform_egl PRIVATE Magnum::WindowlessEglApplication) diff --git a/src/python/magnum/platform/__init__.py b/src/python/magnum/platform/__init__.py index 3a75080..119801f 100644 --- a/src/python/magnum/platform/__init__.py +++ b/src/python/magnum/platform/__init__.py @@ -32,3 +32,11 @@ except ImportError: # pragma: no cover from .egl import WindowlessApplication except ImportError: pass + +try: + from .sdl2 import Application +except ImportError: # pragma: no cover + try: + from .glfw import Application + except ImportError: + pass diff --git a/src/python/magnum/platform/application.h b/src/python/magnum/platform/application.h new file mode 100644 index 0000000..42af527 --- /dev/null +++ b/src/python/magnum/platform/application.h @@ -0,0 +1,72 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + 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 + +#include "magnum/bootstrap.h" + +namespace magnum { namespace platform { + +template void application(py::class_& c) { + py::class_ configuration{c, "Configuration", "Configuration"}; + configuration + .def(py::init()) + .def_property("title", &T::Configuration::title, + [](typename T::Configuration& self, const std::string& title) { + self.setTitle(title); + }, "Window title") + .def_property("size", &T::Configuration::size, + [](typename T::Configuration& self, const Vector2i& size) { + self.setSize(size); + }, "Window size"); + /** @todo others */ + + py::class_ glConfiguration{c, "GLConfiguration", "OpenGL context configuration"}; + glConfiguration + .def(py::init()); + /** @todo others */ + + c + /* Constructor */ + .def(py::init(), py::arg("configuration") = typename T::Configuration{}, py::arg("gl_configuration") = typename T::GLConfiguration{}, + "Constructor") + /** @todo the nocreate ones */ + + /* Basic things */ + .def("exec", &T::exec, "Execute application main loop") + .def("exit", &T::exit, "Exit application main loop") + + /* Screen handling */ + .def("swap_buffers", &T::swapBuffers, "Swap buffers") + /** @todo setMinimalLoopPeriod, needs a getter */ + .def("redraw", &T::redraw, "Redraw immediately") + + /* Event handlers */ + .def("draw_event", &T::drawEvent, "Draw event") + /** @todo more */ + ; +} + +}} diff --git a/src/python/magnum/platform/glfw.cpp b/src/python/magnum/platform/glfw.cpp new file mode 100644 index 0000000..876de6c --- /dev/null +++ b/src/python/magnum/platform/glfw.cpp @@ -0,0 +1,68 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + 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 +#include + +#include "magnum/bootstrap.h" +#include "magnum/platform/application.h" + +namespace magnum { namespace platform { namespace { + +int argc = 0; + +void glfw(py::module& m) { + struct PublicizedApplication: Platform::Application { + explicit PublicizedApplication(const Configuration& configuration, const GLConfiguration& glConfiguration): Platform::Application{Arguments{argc, nullptr}, configuration, glConfiguration} {} + + void drawEvent() override = 0; + }; + + struct PyApplication: PublicizedApplication { + using PublicizedApplication::PublicizedApplication; + + void drawEvent() override { + PYBIND11_OVERLOAD_PURE_NAME( + void, + PublicizedApplication, + "draw_event", + drawEvent + ); + } + }; + + py::class_ glfwApplication{m, "Application", "GLFW application"}; + /** @todo def_property_writeonly for swap_interval */ + + application(glfwApplication); +} + +}}} + +PYBIND11_MODULE(glfw, m) { + m.doc() = "GLFW-based platform integration"; + + magnum::platform::glfw(m); +} diff --git a/src/python/magnum/platform/sdl2.cpp b/src/python/magnum/platform/sdl2.cpp new file mode 100644 index 0000000..219322c --- /dev/null +++ b/src/python/magnum/platform/sdl2.cpp @@ -0,0 +1,72 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + 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 +#include + +#include "magnum/bootstrap.h" +#include "magnum/platform/application.h" + +namespace magnum { namespace platform { namespace { + +int argc = 0; + +void sdl2(py::module& m) { + struct PublicizedApplication: Platform::Application { + explicit PublicizedApplication(const Configuration& configuration, const GLConfiguration& glConfiguration): Platform::Application{Arguments{argc, nullptr}, configuration, glConfiguration} {} + + void drawEvent() override = 0; + }; + + struct PyApplication: PublicizedApplication { + using PublicizedApplication::PublicizedApplication; + + void drawEvent() override { + PYBIND11_OVERLOAD_PURE_NAME( + void, + PublicizedApplication, + "draw_event", + drawEvent + ); + } + }; + + py::class_ sdl2application{m, "Application", "SDL2 application"}; + sdl2application + .def_property("swap_interval", &PyApplication::swapInterval, + [](PublicizedApplication& self, Int interval) { + self.setSwapInterval(interval); + }, "Swap interval"); + + application(sdl2application); +} + +}}} + +PYBIND11_MODULE(sdl2, m) { + m.doc() = "SDL2-based platform integration"; + + magnum::platform::sdl2(m); +} diff --git a/src/python/setup.py.cmake b/src/python/setup.py.cmake index ca90573..488409d 100644 --- a/src/python/setup.py.cmake +++ b/src/python/setup.py.cmake @@ -37,6 +37,8 @@ extension_paths = { 'magnum.shaders': '$<$:$>', 'magnum.platform.egl': '$<$:$>', 'magnum.platform.glx': '$<$:$>', + 'magnum.platform.glfw': '$<$:$>', + 'magnum.platform.sdl2': '$<$:$>', } class TheExtensionIsAlreadyBuiltWhyThisHasToBeSoDamnComplicated(build_ext):