diff --git a/CMakeLists.txt b/CMakeLists.txt index c82f0a837..74dbfe3ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,10 @@ elseif(CORRADE_TARGET_UNIX AND NOT APPLE) option(WITH_GLXAPPLICATION "Build GlxApplication library" OFF) cmake_dependent_option(WITH_WINDOWLESSGLXAPPLICATION "Build WindowlessGlxApplication library" OFF "NOT WITH_MAGNUMINFO;NOT WITH_FONTCONVERTER;NOT WITH_DISTANCEFIELDCONVERTER" ON) cmake_dependent_option(WITH_XEGLAPPLICATION "Build XEglApplication library" OFF "TARGET_GLES" OFF) + +# Windows-specific application libraries +elseif(CORRADE_TARGET_WINDOWS) + cmake_dependent_option(WITH_WINDOWLESSWGLAPPLICATION "Build WindowlessWglApplication library" OFF "NOT WITH_MAGNUMINFO;NOT WITH_FONTCONVERTER;NOT WITH_DISTANCEFIELDCONVERTER" ON) endif() # Platform-independent (almost) application libraries diff --git a/doc/building.dox b/doc/building.dox index 53ac127ee..3f07a5501 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -201,6 +201,7 @@ platform best: - `WITH_XEGLAPPLICATION` - @ref Platform::XEglApplication "XEglApplication" - `WITH_WINDOWLESSGLXAPPLICATION` - @ref Platform::WindowlessGlxApplication "WindowlessGlxApplication" - `WITH_WINDOWLESSNACLAPPLICATION` - @ref Platform::WindowlessNaClApplication "WindowlessNaClApplication" +- `WITH_WINDOWLESSWGLAPPLICATION` - @ref Platform::WindowlessWglApplication "WindowlessWglApplication" There are also a few command-line utilities. They are currently available only on Linux and are disabled by default: diff --git a/doc/cmake.dox b/doc/cmake.dox index c8cb7b1cb..35c0e1029 100644 --- a/doc/cmake.dox +++ b/doc/cmake.dox @@ -90,6 +90,7 @@ Platform namespace is split into more components: - `%XEglApplication` -- @ref Platform::XEglApplication "XEglApplication" - `%WindowlessGlxApplication` -- @ref Platform::WindowlessGlxApplication "WindowlessGlxApplication" - `%WindowlessNaClApplication` -- @ref Platform::WindowlessNaClApplication "WindowlessNaClApplication" +- `%WindowlessWglApplication` -- @ref Platform::WindowlessWglApplication "WindowlessWglApplication" The library also contains a set of plugins for importing essential file formats. Additional plugins are provided in separate plugin repository, see diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index fc3082a0f..f927e1cfd 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -49,6 +49,7 @@ # XEglApplication - X/EGL application # WindowlessGlxApplication - Windowless GLX application # WindowlessNaClApplication - Windowless NaCl application +# WindowlessWglApplication - Windowless WGL application # Example usage with specifying additional components is: # find_package(Magnum [REQUIRED|COMPONENTS] # MeshTools Primitives GlutApplication) @@ -329,7 +330,7 @@ foreach(component ${Magnum_FIND_COMPONENTS}) if(${component} STREQUAL AndroidApplication) find_package(EGL) if(EGL_FOUND) - set(_MAGNUM_${_COMPONENT}_LIBRARIES android ${EGL_LIBRARY} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_LIBRARIES android ${EGL_LIBRARY}) set(_MAGNUM_${_COMPONENT}_INCLUDE_DIRS ${ANDROID_NATIVE_APP_GLUE_INCLUDE_DIR}) else() unset(MAGNUM_${_COMPONENT}_LIBRARY) @@ -340,7 +341,7 @@ foreach(component ${Magnum_FIND_COMPONENTS}) if(${component} STREQUAL GlutApplication) find_package(GLUT) if(GLUT_FOUND) - set(_MAGNUM_${_COMPONENT}_LIBRARIES ${GLUT_glut_LIBRARY} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_LIBRARIES ${GLUT_glut_LIBRARY}) else() unset(MAGNUM_${_COMPONENT}_LIBRARY) endif() @@ -350,7 +351,7 @@ foreach(component ${Magnum_FIND_COMPONENTS}) if(${component} STREQUAL Sdl2Application) find_package(SDL2) if(SDL2_FOUND) - set(_MAGNUM_${_COMPONENT}_LIBRARIES ${SDL2_LIBRARY} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_LIBRARIES ${SDL2_LIBRARY}) set(_MAGNUM_${_COMPONENT}_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) else() unset(MAGNUM_${_COMPONENT}_LIBRARY) @@ -359,14 +360,14 @@ foreach(component ${Magnum_FIND_COMPONENTS}) # (Windowless) NaCl application dependencies if(${component} STREQUAL NaClApplication OR ${component} STREQUAL WindowlessNaClApplication) - set(_MAGNUM_${_COMPONENT}_LIBRARIES ppapi_cpp ppapi ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_LIBRARIES ppapi_cpp ppapi) endif() # GLX application dependencies if(${component} STREQUAL GlxApplication OR ${component} STREQUAL WindowlessGlxApplication) find_package(X11) if(X11_FOUND) - set(_MAGNUM_${_COMPONENT}_LIBRARIES ${X11_LIBRARIES} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_LIBRARIES ${X11_LIBRARIES}) else() unset(MAGNUM_${_COMPONENT}_LIBRARY) endif() @@ -377,12 +378,15 @@ foreach(component ${Magnum_FIND_COMPONENTS}) find_package(EGL) find_package(X11) if(EGL_FOUND AND X11_FOUND) - set(_MAGNUM_${_COMPONENT}_LIBRARIES ${EGL_LIBRARY} ${X11_LIBRARIES} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_LIBRARIES ${EGL_LIBRARY} ${X11_LIBRARIES}) else() unset(MAGNUM_${_COMPONENT}_LIBRARY) endif() endif() + # Common application dependencies + set(_MAGNUM_${_COMPONENT}_LIBRARIES ${_MAGNUM_${_COMPONENT}_LIBRARIES} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + # Audio library elseif(${component} STREQUAL Audio) find_package(OpenAL) diff --git a/src/Magnum/Platform/CMakeLists.txt b/src/Magnum/Platform/CMakeLists.txt index e3a70508e..401244d6e 100644 --- a/src/Magnum/Platform/CMakeLists.txt +++ b/src/Magnum/Platform/CMakeLists.txt @@ -178,6 +178,19 @@ if(WITH_WINDOWLESSGLXAPPLICATION) ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) endif() +# Windowless WGL application +if(WITH_WINDOWLESSWGLAPPLICATION) + add_library(MagnumWindowlessWglApplication STATIC WindowlessWglApplication.cpp) + set_target_properties(MagnumWindowlessWglApplication PROPERTIES + COMPILE_FLAGS "-DUNICODE" + DEBUG_POSTFIX "-d") + install(FILES WindowlessWglApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) + install(TARGETS MagnumWindowlessWglApplication + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) +endif() + # Abstract X application if(NEED_ABSTRACTXAPPLICATION) add_library(MagnumAbstractXApplication OBJECT AbstractXApplication.cpp) diff --git a/src/Magnum/Platform/WindowlessWglApplication.cpp b/src/Magnum/Platform/WindowlessWglApplication.cpp new file mode 100644 index 000000000..4ef4ef02c --- /dev/null +++ b/src/Magnum/Platform/WindowlessWglApplication.cpp @@ -0,0 +1,132 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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 "WindowlessWglApplication.h" + +#include +#include +#include + +#include "Magnum/Context.h" + +namespace Magnum { namespace Platform { + +/** @todo Delegating constructor when support for GCC 4.6 is dropped */ + +#ifndef DOXYGEN_GENERATING_OUTPUT +int WindowlessWglApplication::create(LRESULT(CALLBACK windowProcedure)(HWND, UINT, WPARAM, LPARAM)) { + const WNDCLASS wc{ + 0, + windowProcedure, + 0, + 0, + GetModuleHandle(nullptr), + nullptr, + nullptr, + HBRUSH(COLOR_BACKGROUND), + nullptr, + L"Magnum Windowless Application" + }; + if(!RegisterClass(&wc)) return 1; + + CreateWindowW(wc.lpszClassName, L"Magnum Windowless Application", + WS_OVERLAPPEDWINDOW, 0, 0, 32, 32, 0, 0, wc.hInstance, 0); + + return 0; +} +#endif + +WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration): _window(arguments.window), _c(nullptr) { + createContext(configuration); +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments): _window(arguments.window), _c(nullptr) { + createContext(); +} +#endif + +WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, std::nullptr_t): _window(arguments.window), _c(nullptr) {} + +void WindowlessWglApplication::createContext() { createContext({}); } + +void WindowlessWglApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); +} + +bool WindowlessWglApplication::tryCreateContext(const Configuration&) { + CORRADE_ASSERT(!_c, "Platform::WindowlessWglApplication::tryCreateContext(): context already created", false); + + /* Get device context */ + _deviceContext = GetDC(_window); + + /* Use first provided pixel format */ + constexpr static const PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + /* Double-buffered with OpenGL support */ + PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, /* RGBA */ + 32, /* 32 bit */ + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 24, /* 24-bit depth buffer */ + 8, /* 8-bit stencil buffer */ + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + const int pixelFormat = ChoosePixelFormat(_deviceContext, &pfd); + SetPixelFormat(_deviceContext, pixelFormat, &pfd); + + /* Create context and make it current */ + _renderingContext = wglCreateContext(_deviceContext); + if(!_renderingContext) { + Error() << "Platform::WindowlessWglApplication::tryCreateContext(): cannot create context:" << GetLastError(); + return false; + } + + /* Set OpenGL context as current */ + if(!wglMakeCurrent(_deviceContext, _renderingContext)) { + Error() << "Platform::WindowlessWglApplication::tryCreateContext(): cannot make context current:" << GetLastError(); + return false; + } + + _c = new Context; + return true; +} + +WindowlessWglApplication::~WindowlessWglApplication() { + delete _c; + + wglMakeCurrent(_deviceContext, nullptr); + wglDeleteContext(_renderingContext); +} + +}} diff --git a/src/Magnum/Platform/WindowlessWglApplication.h b/src/Magnum/Platform/WindowlessWglApplication.h new file mode 100644 index 000000000..c3623b901 --- /dev/null +++ b/src/Magnum/Platform/WindowlessWglApplication.h @@ -0,0 +1,214 @@ +#ifndef Magnum_Platform_WindowlessWglApplication_h +#define Magnum_Platform_WindowlessWglApplication_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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::WindowlessWglApplication + */ + +#include "Magnum/OpenGL.h" + +#include "Magnum/Magnum.h" + +namespace Magnum { namespace Platform { + +/** +@brief Windowless WGL application + +Application for offscreen rendering using pure WINAPI. + +This application library is available on desktop OpenGL on Windows. It is built +if `WITH_WINDOWLESSWGLAPPLICATION` is enabled in CMake. + +## Bootstrap application + +Fully contained windowless application using @ref WindowlessWglApplication +along with CMake setup is available in `windowless` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/windowless.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/windowless.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 + +## General usage + +In CMake you need to request `%WindowlessWglApplication` component, add +`${MAGNUM_WINDOWLESSWGLAPPLICATION_INCLUDE_DIRS}` to include path and link to +`${MAGNUM_WINDOWLESSWGLAPPLICATION_LIBRARIES}`. If no other windowless +application is requested, you can also use generic +`${MAGNUM_WINDOWLESSAPPLICATION_INCLUDE_DIRS}` and +`${MAGNUM_WINDOWLESSAPPLICATION_LIBRARIES}` aliases to simplify porting. See +@ref building and @ref cmake for more information. + +Place your code into @ref exec(). The subclass can be then used in main +function using @ref MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN() macro. See +@ref platform for more information. +@code +class MyApplication: public Platform::WindowlessWglApplication { + // implement required methods... +}; +MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN(MyApplication) +@endcode + +If no other application header is included, this class is also aliased to +`Platform::WindowlessApplication` and the macro is aliased to +`MAGNUM_WINDOWLESSAPPLICATION_MAIN()` to simplify porting. +*/ +class WindowlessWglApplication { + public: + /** @brief Application arguments */ + struct Arguments { + int& argc; /**< @brief Argument count */ + char** argv; /**< @brief Argument values */ + #ifndef DOXYGEN_GENERATING_OUTPUT + HWND window; + #endif + }; + + class Configuration; + + #ifndef DOXYGEN_GENERATING_OUTPUT + static int create(LRESULT(CALLBACK windowProcedure)(HWND, UINT, WPARAM, LPARAM)); + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, const Configuration&) */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + explicit WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration); + explicit WindowlessWglApplication(const Arguments& arguments); + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t) */ + explicit WindowlessWglApplication(const Arguments& arguments, std::nullptr_t); + + /** @brief Copying is not allowed */ + WindowlessWglApplication(const WindowlessWglApplication&) = delete; + + /** @brief Moving is not allowed */ + WindowlessWglApplication(WindowlessWglApplication&&) = delete; + + /** @brief Copying is not allowed */ + WindowlessWglApplication& operator=(const WindowlessWglApplication&) = delete; + + /** @brief Moving is not allowed */ + WindowlessWglApplication& operator=(WindowlessWglApplication&&) = delete; + + /** + * @brief Execute application + * @return Value for returning from `main()` + * + * See @ref MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN() for usage + * information. + */ + virtual int exec() = 0; + + protected: + /* Nobody will need to have (and delete) WindowlessWglApplication*, + thus this is faster than public pure virtual destructor */ + ~WindowlessWglApplication(); + + /** @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); + + private: + HWND _window; + HDC _deviceContext; + HGLRC _renderingContext; + + Context* _c; +}; + +/** +@brief %Configuration + +@see @ref WindowlessWglApplication(), @ref createContext(), + @ref tryCreateContext() +*/ +class WindowlessWglApplication::Configuration { + public: + constexpr /*implicit*/ Configuration() {} +}; + +/** @hideinitializer +@brief Entry point for windowless WGL application +@param className Class name + +See @ref Magnum::Platform::WindowlessWglApplication "Platform::WindowlessWglApplication" +for usage information. This macro abstracts out platform-specific entry point +code, see @ref portability-applications for more information. When no other +windowless application header is included this macro is also aliased to +`MAGNUM_WINDOWLESSAPPLICATION_MAIN()`. +*/ +#define MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN(className) \ + int globalArgc; char** globalArgv; \ + LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); \ + LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { \ + switch(message) { \ + case WM_CREATE: \ + { \ + className app({globalArgc, globalArgv, hWnd}); \ + app.exec(); \ + } \ + PostQuitMessage(0); \ + break; \ + default: return DefWindowProc(hWnd, message, wParam, lParam); \ + } \ + return 0; \ + } \ + int main(int argc, char** argv) { \ + globalArgc = argc; \ + globalArgv = argv; \ + return Magnum::Platform::WindowlessWglApplication::create(windowProcedure); \ + } + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN +typedef WindowlessWglApplication WindowlessApplication; +#define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN(className) +#else +#undef MAGNUM_WINDOWLESSAPPLICATION_MAIN +#endif +#endif + +}} + +#endif