|
|
|
|
/*
|
|
|
|
|
This file is part of Magnum.
|
|
|
|
|
|
|
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
|
|
|
|
2020, 2021, 2022, 2023, 2024
|
|
|
|
|
Vladimír Vondruš <mosra@centrum.cz>
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
namespace Magnum {
|
|
|
|
|
/** @page platform Platform support
|
|
|
|
|
@brief Integration into windowing toolkits and creation of windowless contexts.
|
|
|
|
|
|
|
|
|
|
@tableofcontents
|
|
|
|
|
@m_footernavigation
|
|
|
|
|
|
|
|
|
|
The @ref Platform namespace contains `*Application` classes integrating Magnum
|
|
|
|
|
into various toolkits, both windowed and windowless. Each class has slightly
|
|
|
|
|
different dependencies and platform requirements, see documentation of the
|
|
|
|
|
@ref Platform namespace and particular `*Application` classes for more
|
|
|
|
|
information about building and usage with CMake.
|
|
|
|
|
|
|
|
|
|
The classes aim to provide a common API to achieve static polymorphism, which
|
|
|
|
|
ultimately means you should be able to use different toolkits on different
|
|
|
|
|
platforms by only changing an @cpp #include @ce and linking to a different
|
|
|
|
|
library.
|
|
|
|
|
|
|
|
|
|
@section platform-windowed Windowed applications
|
|
|
|
|
|
|
|
|
|
Windowed applications provide a window, keyboard and pointer input handling.
|
|
|
|
|
The most widely used toolkit is SDL2, which is implemented in
|
|
|
|
|
@ref Platform::Sdl2Application. At the very least, provide an one-argument
|
|
|
|
|
constructor and the @relativeref{Platform::Sdl2Application,drawEvent()}
|
|
|
|
|
function. The class can be then used directly in @cpp main() @ce, but for
|
|
|
|
|
convenience and portability to other toolkits and platforms it's better to use
|
|
|
|
|
the @ref MAGNUM_SDL2APPLICATION_MAIN() macro. If just a single application
|
|
|
|
|
header is included, the class is aliased to a @cpp Platform::Application @ce
|
|
|
|
|
typedef and to a @cpp MAGNUM_APPLICATION_MAIN() @ce macro.
|
|
|
|
|
|
|
|
|
|
Barebone application implementation which will just clear the window to dark
|
|
|
|
|
blue color is shown in the following code listing.
|
|
|
|
|
|
|
|
|
|
@note A fully contained base application along with CMake setup is available in
|
|
|
|
|
the `base` branch of the [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap)
|
|
|
|
|
repository.
|
|
|
|
|
|
|
|
|
|
@snippet Platform.cpp windowed
|
|
|
|
|
|
|
|
|
|
@subsection platform-windowed-pointer-events Handling mouse, touch and pen events
|
|
|
|
|
|
|
|
|
|
All application implementations have a generalized pointer input that covers
|
|
|
|
|
mouse, (multi-)touch and pen input in a single interface, exposed through
|
|
|
|
|
@relativeref{Platform::Sdl2Application,pointerPressEvent()},
|
|
|
|
|
@relativeref{Platform::Sdl2Application,pointerReleaseEvent()} and
|
|
|
|
|
@relativeref{Platform::Sdl2Application,pointerMoveEvent()}. Override a subset
|
|
|
|
|
based on the events you want to handle. Mouse input is present in every
|
|
|
|
|
application, touch and pen support varies based on the underlying toolkit.
|
|
|
|
|
|
|
|
|
|
To make the application touch- and pen-aware, all you need to do is treat
|
|
|
|
|
@relativeref{Platform::Sdl2Application,Pointer::Finger} (and
|
|
|
|
|
@relativeref{Platform::AndroidApplication,Pointer::Pen}) the same way as
|
|
|
|
|
@relativeref{Platform::Sdl2Application,Pointer::MouseLeft}. The pointer types
|
|
|
|
|
can be ORed into an enum set for a simpler check, as shown below. Additionally,
|
|
|
|
|
pointer events report also secondary touches from multi-touch interfaces.
|
|
|
|
|
Unless you specifically handle multi-touch gestures in the application, it
|
|
|
|
|
makes sense to handle only primary touches, i.e. only the first pressed finger,
|
|
|
|
|
with @relativeref{Platform::Sdl2Application,PointerEvent::isPrimary()}. Mouse
|
|
|
|
|
and pen input is marked as primary always.
|
|
|
|
|
|
|
|
|
|
@snippet Platform.cpp windowed-pointer-events
|
|
|
|
|
|
|
|
|
|
In addition to the above generalized interface, there's
|
|
|
|
|
@relativeref{Platform::Sdl2Application,scrollEvent()} providing 2D scroll
|
|
|
|
|
events from a mouse wheel, trackball or touchpad.
|
|
|
|
|
|
|
|
|
|
@subsection platform-windowed-key-events Attaching to keyboard and text input
|
|
|
|
|
|
|
|
|
|
The @relativeref{Platform::Sdl2Application,keyPressEvent()} and
|
|
|
|
|
@relativeref{Platform::Sdl2Application,keyReleaseEvent()} provide key input
|
|
|
|
|
events, both a from a physical keyboard and a virtual one on touch-only
|
|
|
|
|
devices. Apart from the @relativeref{Platform::Sdl2Application,Key} being
|
|
|
|
|
pressed or released, they track information about which keyboard
|
|
|
|
|
@relativeref{Platform::Sdl2Application,Modifiers} are pressed.
|
|
|
|
|
|
|
|
|
|
For text input there's @relativeref{Platform::Sdl2Application,textInputEvent()},
|
|
|
|
|
which has to be explicitly enabled with
|
|
|
|
|
@relativeref{Platform::Sdl2Application,startTextInput()} and then disabled
|
|
|
|
|
again with @relativeref{Platform::Sdl2Application,stopTextInput()}. The text
|
|
|
|
|
input directly gives the application bits of UTF-8 text, matching current
|
|
|
|
|
keyboard layout, system configuration and other platform-specific state.
|
|
|
|
|
|
|
|
|
|
Key events are still fired even with text input enabled in order to be able to
|
|
|
|
|
react to arrow keys for cursor movement, editing shortcuts and such. Key events
|
|
|
|
|
* *should not* be used for getting actual typed characters, attempting to do so
|
|
|
|
|
will never lead to a solution that works reliably on all platforms.
|
|
|
|
|
|
|
|
|
|
@snippet Platform.cpp windowed-key-events
|
|
|
|
|
|
|
|
|
|
For [IME](https://en.wikipedia.org/wiki/Input_method) and other advanced text
|
|
|
|
|
editing, certain application implementations provide also a
|
|
|
|
|
@relativeref{Platform::Sdl2Application,textEditingEvent()}.
|
|
|
|
|
|
|
|
|
|
@section platform-windowed-configuration Specifying configuration
|
|
|
|
|
|
|
|
|
|
By default the application is created with reasonable defaults (such as a
|
|
|
|
|
window size of 800x600 pixels). Pass a
|
|
|
|
|
@relativeref{Platform::Sdl2Application,Configuration} instance to the
|
|
|
|
|
application constructor to modify those. Using method chaining it can be done
|
|
|
|
|
conveniently like this:
|
|
|
|
|
|
|
|
|
|
@snippet Platform.cpp windowed-configuration
|
|
|
|
|
|
|
|
|
|
@subsection platform-windowed-viewport-events Responding to window size changes
|
|
|
|
|
|
|
|
|
|
By default the application doesn't respond to window size changes in any way,
|
|
|
|
|
and the window has a fixed size. To make the window resizable and properly
|
|
|
|
|
respond to size changes, construct the application with
|
|
|
|
|
@relativeref{Platform::Sdl2Application,Configuration::WindowFlag::Resizable}
|
|
|
|
|
and override the @relativeref{Platform::Sdl2Application,viewportEvent()}
|
|
|
|
|
function, where you pass the new size to the default framebuffer and where else
|
|
|
|
|
is appropriate:
|
|
|
|
|
|
|
|
|
|
@snippet Platform.cpp windowed-viewport-events
|
|
|
|
|
|
|
|
|
|
@section platform-windowless Windowless applications
|
|
|
|
|
|
|
|
|
|
Windowless applications provide just a context for offscreen rendering or for
|
|
|
|
|
performing tasks on a GPU. For OpenGL there is no platform-independent toolkit
|
|
|
|
|
which could handle this in portable way, thus you have to use platform-specific
|
|
|
|
|
implementations. Magnum provides windowless applications for GLX and EGL on
|
|
|
|
|
Unix, CGL on macOS and WGL or EGL on Windows. To make things simple, as an
|
|
|
|
|
example we will use only @ref Platform::WindowlessEglApplication, below is a
|
|
|
|
|
link to a bootstrap application for a fully cross-platform example.
|
|
|
|
|
|
|
|
|
|
You need to implement just the
|
|
|
|
|
@relativeref{Platform::WindowlessEglApplication,exec()} function. The class can
|
|
|
|
|
be then used directly in @cpp main() @ce, but again, for convenience and
|
|
|
|
|
portability it's better to use the @ref MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN()
|
|
|
|
|
macro.
|
|
|
|
|
|
|
|
|
|
Similarly as with windowed applications, if just a single windowless
|
|
|
|
|
application header is included, the library provides a
|
|
|
|
|
@cpp Platform::WindowlessApplication @ce typedef and a
|
|
|
|
|
@cpp MAGNUM_WINDOWLESSAPPLICATION_MAIN() @ce macro. Changing the code to use
|
|
|
|
|
a different toolkit is then again a matter of using a different
|
|
|
|
|
@cpp #include @ce and linking to a different library. Aliases for windowless
|
|
|
|
|
applications are separated from aliases for windowed applications, because
|
|
|
|
|
projects commonly contain both graphics applications and helper command-line
|
|
|
|
|
tools for data processing and such.
|
|
|
|
|
|
|
|
|
|
A barebone application which will just print out the current OpenGL version and
|
|
|
|
|
renderer string is in the following code listing.
|
|
|
|
|
|
|
|
|
|
@note A fully contained windowless application using
|
|
|
|
|
@ref Platform::WindowlessCglApplication on macOS,
|
|
|
|
|
@ref Platform::WindowlessGlxApplication /
|
|
|
|
|
@ref Platform::WindowlessEglApplication on Unix and
|
|
|
|
|
@ref Platform::WindowlessWglApplication on Windows along with CMake setup
|
|
|
|
|
is available in the `windowless` branch of the
|
|
|
|
|
[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository.
|
|
|
|
|
|
|
|
|
|
@snippet Platform-windowless.cpp windowless
|
|
|
|
|
|
|
|
|
|
@section platform-compilation Compilation with CMake
|
|
|
|
|
|
|
|
|
|
Barebone compilation consists just of finding Magnum library with, for example,
|
|
|
|
|
`Sdl2Application` component, compilation of the executable and linking
|
|
|
|
|
`Magnum::Magnum` and `Magnum::Sdl2Application` to it.
|
|
|
|
|
|
|
|
|
|
Again, to simplify porting, you can also use generic `Magnum::Application`
|
|
|
|
|
aliases (or `Magnum::WindowlessApplication` for windowless applications), but
|
|
|
|
|
only if only one application (windowless application) component is requested to
|
|
|
|
|
avoid ambiguity. Changing the build script to use different toolkit is then
|
|
|
|
|
matter of replacing only the requested `*Application` component (and one
|
|
|
|
|
@cpp #include @ce line in the actual code, as said above).
|
|
|
|
|
|
|
|
|
|
@code{.cmake}
|
|
|
|
|
find_package(Magnum REQUIRED Sdl2Application)
|
|
|
|
|
|
|
|
|
|
add_executable(myapplication MyApplication.cpp)
|
|
|
|
|
target_link_libraries(myapplication
|
|
|
|
|
Magnum::Magnum
|
|
|
|
|
Magnum::Application)
|
|
|
|
|
@endcode
|
|
|
|
|
|
|
|
|
|
@section platform-configuration-delayed Delayed context creation
|
|
|
|
|
|
|
|
|
|
Sometimes you may want to set up the application based on a configuration file
|
|
|
|
|
or system introspection, which can't really be done inside the base class
|
|
|
|
|
initializer. You can specify @ref NoCreate in the constructor instead and pass
|
|
|
|
|
the @relativeref{Platform::Sdl2Application,Configuration} later to a
|
|
|
|
|
@relativeref{Platform::Sdl2Application,create()} function:
|
|
|
|
|
|
|
|
|
|
@snippet Platform.cpp createcontext
|
|
|
|
|
|
|
|
|
|
If context creation in the constructor or in
|
|
|
|
|
@relativeref{Platform::Sdl2Application,create()} fails, the application prints
|
|
|
|
|
an error message to standard output and exits. While that frees you from having
|
|
|
|
|
to do explicit error handling, sometimes a more graceful behavior may be
|
|
|
|
|
desirable --- with @relativeref{Platform::Sdl2Application,tryCreate()} the
|
|
|
|
|
function returns @cpp false @ce instead of exiting and it's up to you whether
|
|
|
|
|
you abort the launch or retry with different configuration. You can for example
|
|
|
|
|
try enabling MSAA first, and if the context creation fails, fall back to no-AA
|
|
|
|
|
rendering:
|
|
|
|
|
|
|
|
|
|
@snippet Platform.cpp trycreatecontext
|
|
|
|
|
|
|
|
|
|
<b></b>
|
|
|
|
|
|
|
|
|
|
@m_class{m-block m-warning}
|
|
|
|
|
|
|
|
|
|
@par Implications for GL objects as class members
|
|
|
|
|
Delaying GL context creation to
|
|
|
|
|
@relativeref{Platform::Sdl2Application,create()} /
|
|
|
|
|
@relativeref{Platform::Sdl2Application,tryCreate()} means that, at the
|
|
|
|
|
point when class members get constructed, the context isn't available yet.
|
|
|
|
|
If you have GL objects such as @ref GL::Mesh or @ref Shaders::PhongGL as
|
|
|
|
|
class members, the application may hit the following assert during startup:
|
|
|
|
|
@par
|
|
|
|
|
@code{.shell-session}
|
|
|
|
|
GL::Context::current(): no current context
|
|
|
|
|
@endcode
|
|
|
|
|
@par
|
|
|
|
|
The solution is to construct the GL objects with @ref NoCreate constructors
|
|
|
|
|
as well and populate them with live instances only after the context has
|
|
|
|
|
been created. See @ref opengl-wrapping-instances-nocreate for more
|
|
|
|
|
information.
|
|
|
|
|
|
|
|
|
|
@section platform-custom Using custom platform toolkits
|
|
|
|
|
|
|
|
|
|
In case you want to use some not-yet-supported toolkit or you don't want to use
|
|
|
|
|
the application wrappers in @ref Platform namespace, you can initialize Magnum
|
|
|
|
|
manually. First create OpenGL context and then create instance of
|
|
|
|
|
@ref Platform::GLContext class, which will take care of proper initialization
|
|
|
|
|
and feature detection. The instance must be alive for whole application
|
|
|
|
|
lifetime. Example @cpp main() @ce function with manual initialization is in the
|
|
|
|
|
following code listing.
|
|
|
|
|
|
|
|
|
|
@note Fully contained application using with manual Magnum initialization on
|
|
|
|
|
top of Qt toolkit is available in `base-qt` branch of
|
|
|
|
|
[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap/tree/base-qt)
|
|
|
|
|
repository. Similar project, but for wxWidgets and gtkmm are in the
|
|
|
|
|
[base-wxwidgets](https://github.com/mosra/magnum-bootstrap/tree/base-wxwidgets)
|
|
|
|
|
and [base-gtkmm](https://github.com/mosra/magnum-bootstrap/tree/base-gtkmm)
|
|
|
|
|
branches.
|
|
|
|
|
@note
|
|
|
|
|
There's also an example showing @ref examples-triangle-plain-glfw "usage of plain GLFW to render a basic triangle".
|
|
|
|
|
|
|
|
|
|
@snippet Platform-custom.cpp custom
|
|
|
|
|
|
|
|
|
|
@attention The @ref Platform::GLContext instance is bound to a single OpenGL
|
|
|
|
|
context, which must be always set as current when calling any Magnum APIs
|
|
|
|
|
touching OpenGL state.
|
|
|
|
|
|
|
|
|
|
On majority of platforms the @ref Platform::GLContext class does GL function
|
|
|
|
|
pointer loading using platform-specific APIs. In that case you also need to
|
|
|
|
|
find particular `*Context` library, add its include dir and then link to it.
|
|
|
|
|
These platform-specific libraries are available:
|
|
|
|
|
|
|
|
|
|
- `CglContext` --- CGL context (macOS)
|
|
|
|
|
- `EglContext` --- EGL context (everywhere except Emscripten)
|
|
|
|
|
- `GlxContext` --- GLX context (X11-based Unix)
|
|
|
|
|
- `WglContext` --- WGL context (Windows)
|
|
|
|
|
|
|
|
|
|
Systems not listed here (such as Emscripten) don't need any `Context` library,
|
|
|
|
|
because dynamic function pointer loading is not available on these.
|
|
|
|
|
|
|
|
|
|
For example, when you create the OpenGL context using GLX, you need to find
|
|
|
|
|
`GlxContext` component, and link to `Magnum::GlxContext` target. Similarly to
|
|
|
|
|
application libraries, you can also use the generic `Magnum::GLContext` target,
|
|
|
|
|
providing you requested only one `*Context` component in the
|
|
|
|
|
@cmake find_package() @ce call. Complete example:
|
|
|
|
|
|
|
|
|
|
@code{.cmake}
|
|
|
|
|
find_package(Magnum REQUIRED GlxContext)
|
|
|
|
|
|
|
|
|
|
add_executable(myapplication MyCustomApplication.cpp)
|
|
|
|
|
target_link_libraries(myapplication
|
|
|
|
|
Magnum::Magnum
|
|
|
|
|
Magnum::GLContext)
|
|
|
|
|
@endcode
|
|
|
|
|
|
|
|
|
|
@section platform-windowless-contexts Manually managing windowless contexts
|
|
|
|
|
|
|
|
|
|
In case you need to manage windowless OpenGL contexts manually (for example
|
|
|
|
|
to use Magnum for data processing in a thread or when having more than one
|
|
|
|
|
OpenGL context), there is a possibility to directly use the context wrappers
|
|
|
|
|
from windowless applications. Each @ref Platform::WindowlessEglApplication "Platform::Windowless*Application"
|
|
|
|
|
is accompanied by a @ref Platform::WindowlessEglContext "Platform::Windowless*Context"
|
|
|
|
|
class that manages just GL context creation, making it current and destruction.
|
|
|
|
|
Similarly to using custom platform toolkits above, the workflow is to first
|
|
|
|
|
create a GL context instance, then making it current and finally instantiating
|
|
|
|
|
the @ref Platform::GLContext instance to initialize Magnum.
|
|
|
|
|
|
|
|
|
|
Similarly as with the applications, to simplify the porting, the library
|
|
|
|
|
provides @cpp Platform::WindowlessGLContext @ce typedef, but only if just one
|
|
|
|
|
windowless application header is included.
|
|
|
|
|
|
|
|
|
|
@attention With this approach it is possible to switch between different GL
|
|
|
|
|
contexts, but make sure that Magnum is used only with its own OpenGL
|
|
|
|
|
context.
|
|
|
|
|
|
|
|
|
|
@snippet Platform-windowless-custom.cpp custom
|
|
|
|
|
|
|
|
|
|
The main purpose of windowless contexts is threaded OpenGL, used for example
|
|
|
|
|
for background data processing. The workflow is to create the windowless
|
|
|
|
|
context on the main thread, but make it current in the worker thread. This way
|
|
|
|
|
the main thread state isn't affected so it can have any other GL context
|
|
|
|
|
current (for example for the main application rendering). See
|
|
|
|
|
@ref GL-Context-multithreading and @ref CORRADE_BUILD_MULTITHREADED for more
|
|
|
|
|
information.
|
|
|
|
|
|
|
|
|
|
@note Context creation is not thread safe on all platforms, that's why it still
|
|
|
|
|
has to be done on the main thread.
|
|
|
|
|
|
|
|
|
|
@snippet Platform-windowless-thread.cpp thread
|
|
|
|
|
*/
|
|
|
|
|
}
|