Browse Source

[wip] everything is terrible

TODO: some of this (the configuration DPI scaling/policy) should go to
  the previos commit i guess?
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
17c74b23d7
  1. 237
      src/Magnum/Platform/Sdl2Application.cpp
  2. 48
      src/Magnum/Platform/Sdl2Application.h
  3. 112
      src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp

237
src/Magnum/Platform/Sdl2Application.cpp

@ -42,6 +42,7 @@
#include <emscripten/emscripten.h> #include <emscripten/emscripten.h>
#include <emscripten/html5.h> #include <emscripten/html5.h>
#endif #endif
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Utility/Algorithms.h> #include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/Arguments.h> #include <Corrade/Utility/Arguments.h>
@ -88,14 +89,80 @@ enum class Sdl2ApplicationWindow::WindowFlag: UnsignedByte {
Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application, NoCreateT): _application{application}, _windowFlags{WindowFlag::Redraw} {} Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application, NoCreateT): _application{application}, _windowFlags{WindowFlag::Redraw} {}
#ifndef CORRADE_TARGET_EMSCRIPTEN
Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application, const Configuration& configuration): Sdl2ApplicationWindow{application, NoCreate} {
if(!tryCreateWindow(configuration)) std::exit(1);
}
Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application): Sdl2ApplicationWindow{application, Configuration{}} {}
#endif
Sdl2ApplicationWindow::~Sdl2ApplicationWindow() { Sdl2ApplicationWindow::~Sdl2ApplicationWindow() {
/* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
(it doesn't seem to crash on Linux) */
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
if(_window) SDL_DestroyWindow(_window); /* If the window isn't created yet because tryCreateWindow(), nothing to
do. This also covers the case for the main application window, which
gets destroyed (and reset to null) from within ~Sdl2Application()
because it has to be done before SDL_Quit(), and this destructor is
called after that. There it also means that it's not needed to remove
itself from the window list, as the list is already gone at this
point. */
if(_window)
destroyWindow();
#endif #endif
} }
bool Sdl2ApplicationWindow::tryCreateWindow(const Configuration& configuration) {
CORRADE_ASSERT(!_window,
"Platform::Sdl2ApplicationWindow::tryCreateWindow(): window already created", false);
/* Save DPI scaling values from configuration for future use, scale window
based on those */
_configurationDpiScalingPolicy = configuration.dpiScalingPolicy();
_configurationDpiScaling = configuration.dpiScaling();
const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration);
/* Create a window */
if(!(_window = SDL_CreateWindow(
#ifndef CORRADE_TARGET_IOS
configuration.title().data(),
#else
nullptr,
#endif
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
scaledWindowSize.x(), scaledWindowSize.y(),
SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags())|_application._configurationFlags)))
{
Error() << "Platform::Sdl2ApplicationWindow::tryCreateWindow(): cannot create a window:" << SDL_GetError();
return false;
}
/* Add itself to the window list */
const std::size_t windowId = SDL_GetWindowID(_window);
if(windowId >= _application._windows.size()) {
for(Sdl2ApplicationWindow*& i: arrayAppend(_application._windows, NoInit, windowId - _application._windows.size() + 1))
i = nullptr;
}
CORRADE_INTERNAL_ASSERT(!_application._windows[windowId]);
_application._windows[windowId] = this;
return true;
}
#ifndef CORRADE_TARGET_EMSCRIPTEN
void Sdl2ApplicationWindow::destroyWindow() {
/* To prevent accidental double destructions and such, this function should
only be called if a window is actually created */
CORRADE_INTERNAL_ASSERT(_window);
/* Remove itself from the window list */
const std::size_t id = SDL_GetWindowID(_window);
CORRADE_INTERNAL_ASSERT(id < _application._windows.size());
_application._windows[id] = nullptr;
SDL_DestroyWindow(_window);
}
#endif
Vector2 Sdl2ApplicationWindow::dpiScaling(const Configuration& configuration) { Vector2 Sdl2ApplicationWindow::dpiScaling(const Configuration& configuration) {
/* Print a helpful warning in case some extra steps are needed for HiDPI /* Print a helpful warning in case some extra steps are needed for HiDPI
support */ support */
@ -568,27 +635,18 @@ bool Sdl2Application::tryCreate(const Configuration& configuration) {
return tryCreate(configuration, GLConfiguration{}); return tryCreate(configuration, GLConfiguration{});
#endif #endif
#ifndef CORRADE_TARGET_EMSCRIPTEN /* Save the application-global configuration flags to be used to create all
/* Save DPI scaling values from configuration for future use, scale window windows */
based on those */ _configurationFlags = Uint32(configuration.flags());
_configurationDpiScalingPolicy = configuration.dpiScalingPolicy();
_configurationDpiScaling = configuration.dpiScaling();
const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration);
/* Create window */ #ifndef CORRADE_TARGET_EMSCRIPTEN
if(!(_window = SDL_CreateWindow( /* Create the main window */
#ifndef CORRADE_TARGET_IOS if(!tryCreateWindow(configuration))
configuration.title().data(),
#else
nullptr,
#endif
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
scaledWindowSize.x(), scaledWindowSize.y(),
SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_OPENGL|Uint32(configuration.windowFlags()))))
{
Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError();
return false; return false;
}
/* Register the main window in the window list */
CORRADE_INTERNAL_ASSERT(_windows.isEmpty());
arrayAppend(_windows, this);
/* Emscripten-specific initialization */ /* Emscripten-specific initialization */
#else #else
@ -620,7 +678,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration) {
based on those */ based on those */
_configurationDpiScalingPolicy = configuration.dpiScalingPolicy(); _configurationDpiScalingPolicy = configuration.dpiScalingPolicy();
_configurationDpiScaling = configuration.dpiScaling(); _configurationDpiScaling = configuration.dpiScaling();
const Vector2i scaledWindowSize = windowSize*dpiScaling(configuration); const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration);
Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF; Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF;
if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) {
@ -646,6 +704,11 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
CORRADE_ASSERT(_context->version() == GL::Version::None, CORRADE_ASSERT(_context->version() == GL::Version::None,
"Platform::Sdl2Application::tryCreate(): context already created", false); "Platform::Sdl2Application::tryCreate(): context already created", false);
/* Save the application-global configuration flags to be used to create all
windows. Since we're creating a GL context, request the window to also
be OpenGL-enabled. */
_configurationFlags = Uint32(configuration.flags()|Configuration::Flag::OpenGL);
/* Enable double buffering, set up buffer sizes */ /* Enable double buffering, set up buffer sizes */
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, glConfiguration.colorBufferSize().r()); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, glConfiguration.colorBufferSize().r());
@ -665,12 +728,6 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
#endif #endif
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
/* Save DPI scaling values from configuration for future use, scale window
based on those */
_configurationDpiScalingPolicy = configuration.dpiScalingPolicy();
_configurationDpiScaling = configuration.dpiScaling();
const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration);
/* Request debug context if GpuValidation is enabled either via the /* Request debug context if GpuValidation is enabled either via the
configuration or via command-line */ configuration or via command-line */
GLConfiguration::Flags glFlags = glConfiguration.flags(); GLConfiguration::Flags glFlags = glConfiguration.flags();
@ -732,25 +789,18 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
#endif #endif
} }
/* Create a window. Hide it by default so we don't have distracting window /* Hide the main window by default so we don't have distracting window
blinking in case the context creation fails due to unsupported blinking in case the context creation fails due to unsupported
configuration or if it gets destroyed for fallback context creation configuration or if it gets destroyed for fallback context creation
below. */ below. */
if(!(_window = SDL_CreateWindow( Sdl2ApplicationWindow::Configuration hiddenWindowConfiguration{configuration};
#ifndef CORRADE_TARGET_IOS hiddenWindowConfiguration.addWindowFlags(Sdl2ApplicationWindow::Configuration::WindowFlag::Hidden);
configuration.title().data(),
#else /* Create a window */
nullptr, if(!tryCreateWindow(hiddenWindowConfiguration))
#endif
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
scaledWindowSize.x(), scaledWindowSize.y(),
SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN|SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags()))))
{
Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError();
return false; return false;
}
/* Create context */ /* Create a context */
_glContext = SDL_GL_CreateContext(_window); _glContext = SDL_GL_CreateContext(_window);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -791,7 +841,8 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
on Linux at least, but better stay on the safe side as this way on Linux at least, but better stay on the safe side as this way
worked correctly for 10+ years on all platforms and reusing an worked correctly for 10+ years on all platforms and reusing an
existing window might not. */ existing window might not. */
SDL_DestroyWindow(_window); destroyWindow();
_window = nullptr;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
@ -804,14 +855,8 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, int(UnsignedLong(glFlags & ~GLConfiguration::Flag::ForwardCompatible) & 0xffffffffu)); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, int(UnsignedLong(glFlags & ~GLConfiguration::Flag::ForwardCompatible) & 0xffffffffu));
/* Create a new window using the refreshed GL attributes */ /* Create a new window using the refreshed GL attributes */
if(!(_window = SDL_CreateWindow(configuration.title().data(), if(!tryCreateWindow(hiddenWindowConfiguration))
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
scaledWindowSize.x(), scaledWindowSize.y(),
SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN|SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags()))))
{
Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError();
return false; return false;
}
/* Create compatibility context */ /* Create compatibility context */
_glContext = SDL_GL_CreateContext(_window); _glContext = SDL_GL_CreateContext(_window);
@ -821,7 +866,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
/* Cannot create context (or fallback compatibility context on desktop) */ /* Cannot create context (or fallback compatibility context on desktop) */
if(!_glContext) { if(!_glContext) {
Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError(); Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError();
SDL_DestroyWindow(_window); destroyWindow();
_window = nullptr; _window = nullptr;
return false; return false;
} }
@ -867,7 +912,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
based on those */ based on those */
_configurationDpiScalingPolicy = configuration.dpiScalingPolicy(); _configurationDpiScalingPolicy = configuration.dpiScalingPolicy();
_configurationDpiScaling = configuration.dpiScaling(); _configurationDpiScaling = configuration.dpiScaling();
const Vector2i scaledWindowSize = windowSize*dpiScaling(); const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration);
Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF; Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF;
if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) {
@ -887,7 +932,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
if(!_context->tryCreate(glConfiguration)) { if(!_context->tryCreate(glConfiguration)) {
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_GL_DeleteContext(_glContext); SDL_GL_DeleteContext(_glContext);
SDL_DestroyWindow(_window); destroyWindow();
_window = nullptr; _window = nullptr;
#else #else
SDL_FreeSurface(_surface); SDL_FreeSurface(_surface);
@ -946,10 +991,20 @@ bool Sdl2Application::setSwapInterval(const Int interval) {
} }
Sdl2Application::~Sdl2Application() { Sdl2Application::~Sdl2Application() {
#ifndef CORRADE_TARGET_EMSCRIPTEN
/* Destroy the main SDL window first. After that, there should be no
remaining registered windows. */
if(_window)
destroyWindow();
for(Sdl2ApplicationWindow* i: _windows)
CORRADE_ASSERT(!i, "Sdl2Application: destructed with windows still open", );
#else
/* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr /* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
(it doesn't seem to crash on Linux). Because this seems to be yet (it doesn't seem to crash on Linux). Because this seems to be yet
another pointless platform difference, to be safe do the same check with another pointless platform difference, to be safe do the same check with
all. */ all APIs. */
if(_window) SDL_DestroyWindow(_window);
#endif
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
/* Destroy Magnum context first to avoid it potentially accessing the /* Destroy Magnum context first to avoid it potentially accessing the
@ -968,14 +1023,6 @@ Sdl2Application::~Sdl2Application() {
SDL_FreeCursor(cursor); SDL_FreeCursor(cursor);
#endif #endif
/* The window would be destroyed in the base ~Sdl2ApplicationWindow(), but
that's too late. Do it before calling SDL_Quit() and then reset the
window pointer so it's not called again after. */
if(_window) {
SDL_DestroyWindow(_window);
_window = nullptr;
}
SDL_Quit(); SDL_Quit();
} }
@ -1000,6 +1047,28 @@ void Sdl2Application::exit(const int exitCode) {
_exitCode = exitCode; _exitCode = exitCode;
} }
#ifndef CORRADE_TARGET_EMSCRIPTEN
void Sdl2Application::makeContextCurrent(Sdl2ApplicationWindow& window) {
/* Only do it if it is not active already */
if(_activeGlContextWindow != window._window) {
SDL_GL_MakeCurrent(window._window, _glContext);
_activeGlContextWindow = window._window;
#warning TODO
// Context::current().resetState(Context::State::WindowSpecific);
}
}
#endif
template<class ...Args> inline void Sdl2Application::callEventHandler(std::size_t windowId, void(Sdl2ApplicationWindow::*eventHandler)(Args...), Args&&... args) {
// if(!(windowId < _windows.size() && _windows[windowId])) {
// Debug() << "HUH" << windowId << _windows.size();
// return;
// }
CORRADE_INTERNAL_ASSERT(windowId < _windows.size() && _windows[windowId]);
(_windows[windowId]->*eventHandler)(std::forward<Args>(args)...);
}
bool Sdl2Application::mainLoopIteration() { bool Sdl2Application::mainLoopIteration() {
/* If exit was requested directly in the constructor, exit immediately /* If exit was requested directly in the constructor, exit immediately
without calling anything else */ without calling anything else */
@ -1045,6 +1114,7 @@ bool Sdl2Application::mainLoopIteration() {
while(SDL_PollEvent(&event)) { while(SDL_PollEvent(&event)) {
switch(event.type) { switch(event.type) {
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
CORRADE_INTERNAL_ASSERT(event.window.windowID < _windows.size() && _windows[event.window.windowID]);
switch(event.window.event) { switch(event.window.event) {
/* Not using SDL_WINDOWEVENT_RESIZED, because that doesn't /* Not using SDL_WINDOWEVENT_RESIZED, because that doesn't
get fired when the window is resized programmatically get fired when the window is resized programmatically
@ -1066,15 +1136,18 @@ bool Sdl2Application::mainLoopIteration() {
#endif #endif
dpiScaling()}; dpiScaling()};
/** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */ /** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */
viewportEvent(e); #warning make context current here? probably??
_windowFlags |= WindowFlag::Redraw; makeContextCurrent(*_windows[event.window.windowID]);
callEventHandler(event.window.windowID, &Sdl2ApplicationWindow::viewportEvent, e);
_windows[event.window.windowID]->_windowFlags |= WindowFlag::Redraw;
#endif #endif
} break; } break;
#warning SDL_WINDOWEVENT_CLOSE
/* Direct everything that wasn't exposed via a callback to /* Direct everything that wasn't exposed via a callback to
anyEvent(), so users can implement event handling for anyEvent(), so users can implement event handling for
things not present in the Application APIs */ things not present in the Application APIs */
case SDL_WINDOWEVENT_EXPOSED: case SDL_WINDOWEVENT_EXPOSED:
_windowFlags |= WindowFlag::Redraw; _windows[event.window.windowID]->_windowFlags |= WindowFlag::Redraw;
if(!(_flags & Flag::NoAnyEvent)) anyEvent(event); if(!(_flags & Flag::NoAnyEvent)) anyEvent(event);
break; break;
default: default:
@ -1084,7 +1157,9 @@ bool Sdl2Application::mainLoopIteration() {
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: { case SDL_KEYUP: {
KeyEvent e{event, static_cast<KeyEvent::Key>(event.key.keysym.sym), fixedModifiers(event.key.keysym.mod), event.key.repeat != 0}; KeyEvent e{event, static_cast<KeyEvent::Key>(event.key.keysym.sym), fixedModifiers(event.key.keysym.mod), event.key.repeat != 0};
event.type == SDL_KEYDOWN ? keyPressEvent(e) : keyReleaseEvent(e); callEventHandler(event.key.windowID,
event.type == SDL_KEYDOWN ? &Sdl2ApplicationWindow::keyPressEvent : &Sdl2ApplicationWindow::keyReleaseEvent,
e);
} break; } break;
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
@ -1094,34 +1169,37 @@ bool Sdl2Application::mainLoopIteration() {
, event.button.clicks , event.button.clicks
#endif #endif
}; };
event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e); callEventHandler(event.key.windowID,
event.type == SDL_MOUSEBUTTONDOWN ? &Sdl2ApplicationWindow::mousePressEvent : &Sdl2ApplicationWindow::mouseReleaseEvent,
e);
} break; } break;
case SDL_MOUSEWHEEL: { case SDL_MOUSEWHEEL: {
MouseScrollEvent e{event, {Float(event.wheel.x), Float(event.wheel.y)}}; MouseScrollEvent e{event, {Float(event.wheel.x), Float(event.wheel.y)}};
mouseScrollEvent(e); callEventHandler(event.wheel.windowID, &Sdl2ApplicationWindow::mouseScrollEvent, e);
} break; } break;
case SDL_MOUSEMOTION: { case SDL_MOUSEMOTION: {
MouseMoveEvent e{event, {event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}, static_cast<MouseMoveEvent::Button>(event.motion.state)}; MouseMoveEvent e{event, {event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}, static_cast<MouseMoveEvent::Button>(event.motion.state)};
mouseMoveEvent(e); callEventHandler(event.motion.windowID, &Sdl2ApplicationWindow::mouseMoveEvent, e);
break; break;
} }
case SDL_MULTIGESTURE: { case SDL_MULTIGESTURE: {
MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers}; MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers};
#warning wtf, why no window ID?!
multiGestureEvent(e); multiGestureEvent(e);
break; break;
} }
case SDL_TEXTINPUT: { case SDL_TEXTINPUT: {
TextInputEvent e{event, event.text.text}; TextInputEvent e{event, event.text.text};
textInputEvent(e); callEventHandler(event.text.windowID, &Sdl2ApplicationWindow::textInputEvent, e);
} break; } break;
case SDL_TEXTEDITING: { case SDL_TEXTEDITING: {
TextEditingEvent e{event, event.edit.text, event.edit.start, event.edit.length}; TextEditingEvent e{event, event.edit.text, event.edit.start, event.edit.length};
textEditingEvent(e); callEventHandler(event.edit.windowID, &Sdl2ApplicationWindow::textEditingEvent, e);
} break; } break;
case SDL_QUIT: { case SDL_QUIT: {
@ -1147,11 +1225,18 @@ bool Sdl2Application::mainLoopIteration() {
/* Tick event */ /* Tick event */
if(!(_flags & Flag::NoTickEvent)) tickEvent(); if(!(_flags & Flag::NoTickEvent)) tickEvent();
/* Draw event */ /* Draw events */
if(_windowFlags & WindowFlag::Redraw) { bool somethingDrawn = false;
_windowFlags &= ~WindowFlag::Redraw; for(std::size_t i = 0; i != _windows.size(); ++i) {
drawEvent(); if(!_windows[i] || !(_windows[i]->_windowFlags & WindowFlag::Redraw)) continue;
_windows[i]->_windowFlags &= ~WindowFlag::Redraw;
callEventHandler(i,
&Sdl2ApplicationWindow::drawEvent);
somethingDrawn = true;
}
if(somethingDrawn) {
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
/* If VSync is not enabled, delay to prevent CPU hogging (if set) */ /* If VSync is not enabled, delay to prevent CPU hogging (if set) */
if(!(_flags & Flag::VSyncEnabled) && _minimalLoopPeriod) { if(!(_flags & Flag::VSyncEnabled) && _minimalLoopPeriod) {

48
src/Magnum/Platform/Sdl2Application.h

@ -118,6 +118,19 @@ class Sdl2ApplicationWindow {
class TextInputEvent; class TextInputEvent;
class TextEditingEvent; class TextEditingEvent;
/**
* @brief Constructor
*
* @ref TODOTODO document
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
explicit Sdl2ApplicationWindow(Sdl2Application& application, const Configuration& configuration = Configuration{});
#else
/* Configuration is only forward-declared at this point */
explicit Sdl2ApplicationWindow(Sdl2Application& application, const Configuration& configuration);
explicit Sdl2ApplicationWindow(Sdl2Application& application);
#endif
/** @brief Copying is not allowed */ /** @brief Copying is not allowed */
Sdl2ApplicationWindow(const Sdl2ApplicationWindow&) = delete; Sdl2ApplicationWindow(const Sdl2ApplicationWindow&) = delete;
@ -135,6 +148,10 @@ class Sdl2ApplicationWindow {
virtual */ virtual */
virtual ~Sdl2ApplicationWindow(); virtual ~Sdl2ApplicationWindow();
/** @brief Application instance */
Sdl2Application& application() { return _application; }
const Sdl2Application& application() const { return _application; } /**< @overload */
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
/** /**
* @brief Underlying window handle * @brief Underlying window handle
@ -506,11 +523,21 @@ class Sdl2ApplicationWindow {
explicit Sdl2ApplicationWindow(Sdl2Application& application, NoCreateT); explicit Sdl2ApplicationWindow(Sdl2Application& application, NoCreateT);
Vector2 dpiScalingInternal(Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const; Vector2 dpiScalingInternal(Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const;
#ifndef CORRADE_TARGET_EMSCRIPTEN
bool tryCreateWindow(const Configuration& configuration);
void destroyWindow();
#endif
Sdl2Application& _application; Sdl2Application& _application;
/* These are saved from configuration to be reused in dpiScaling() and
viewportEvent() later */
Implementation::Sdl2DpiScalingPolicy _configurationDpiScalingPolicy{};
Vector2 _configurationDpiScaling;
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_Window* _window{}; SDL_Window* _window{};
Vector2i _viewportSize;
#else #else
SDL_Surface* _surface{}; SDL_Surface* _surface{};
Vector2i _lastKnownCanvasSize; Vector2i _lastKnownCanvasSize;
@ -1327,18 +1354,27 @@ class Sdl2Application: public Sdl2ApplicationWindow {
typedef Containers::EnumSet<Flag> Flags; typedef Containers::EnumSet<Flag> Flags;
CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) CORRADE_ENUMSET_FRIEND_OPERATORS(Flags)
#ifndef CORRADE_TARGET_EMSCRIPTEN
void makeContextCurrent(Sdl2ApplicationWindow& window);
#endif
template<class ...Args> void callEventHandler(std::size_t windowId, void(Sdl2ApplicationWindow::*eventHandler)(Args...), Args&&...);
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_Cursor* _cursors[12]{}; SDL_Cursor* _cursors[12]{};
#else #else
Cursor _cursor; Cursor _cursor;
#endif #endif
/* These are saved from command-line arguments, and from configuration /* These are saved from command-line arguments to be reused in
to be reused in dpiScaling() and viewportEvent() later */ dpiScaling() and viewportEvent() later */
bool _verboseLog{}; bool _verboseLog{};
Implementation::Sdl2DpiScalingPolicy _commandLineDpiScalingPolicy{}, _configurationDpiScalingPolicy{}; Implementation::Sdl2DpiScalingPolicy _commandLineDpiScalingPolicy{};
Vector2 _commandLineDpiScaling, _configurationDpiScaling; Vector2 _commandLineDpiScaling;
/* Configuration::Flags, propagated to all created windows. Can't use a
concrete type as Configuration is only a forward declaration at this
point. */
Uint32 _configurationFlags;
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
UnsignedInt _minimalLoopPeriod; UnsignedInt _minimalLoopPeriod;
#endif #endif
@ -1346,12 +1382,16 @@ class Sdl2Application: public Sdl2ApplicationWindow {
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_GLContext _glContext{}; SDL_GLContext _glContext{};
SDL_Window* _activeGlContextWindow{};
#endif #endif
/* Has to be in an Optional because we delay-create it in a constructor /* Has to be in an Optional because we delay-create it in a constructor
with populated Arguments and it gets explicitly destroyed before the with populated Arguments and it gets explicitly destroyed before the
GL context */ GL context */
Containers::Optional<Platform::GLContext> _context; Containers::Optional<Platform::GLContext> _context;
#endif #endif
#ifndef CORRADE_TARGET_EMSCRIPTEN
Containers::Array<Sdl2ApplicationWindow*> _windows;
#endif
Flags _flags; Flags _flags;

112
src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp

@ -23,9 +23,11 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/PluginManager/Manager.h> #include <Corrade/PluginManager/Manager.h>
#include <Corrade/Utility/Arguments.h> #include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/Format.h>
#include <Corrade/Utility/Resource.h> #include <Corrade/Utility/Resource.h>
#include "Magnum/ImageView.h" #include "Magnum/ImageView.h"
@ -54,6 +56,106 @@ namespace Magnum { namespace Platform { namespace Test { namespace {
using namespace Containers::Literals; using namespace Containers::Literals;
#ifndef CORRADE_TARGET_EMSCRIPTEN
struct Sdl2ApplicationTestWindow: Platform::ApplicationWindow {
explicit Sdl2ApplicationTestWindow(Platform::Application& application, std::size_t id): Platform::ApplicationWindow{application, Configuration{}
.setTitle(Utility::format("Window {}", id))
.setSize({400, 300})
.addWindowFlags(Configuration::WindowFlag::Resizable)
}, _id{id} {}
void viewportEvent(ViewportEvent& event) override {
Debug{} << "window" << _id << "viewport event" << event.windowSize()
#ifdef MAGNUM_TARGET_GL
<< event.framebufferSize()
#endif
<< event.dpiScaling();
#warning query the GL fb size here and compare
// GL::defaultFramebuffer.setViewport({{}, event.framebufferSize()});
}
void drawEvent() override {
Debug{} << "window" << _id << "draw event";
#ifdef MAGNUM_TARGET_GL
GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);
#endif
swapBuffers();
}
/* For testing event coordinates */
void mousePressEvent(MouseEvent& event) override {
Debug{} << "window" << _id << "mouse press event:" << event.position() << Int(event.button());
}
void mouseReleaseEvent(MouseEvent& event) override {
Debug{} << "window" << _id << "mouse release event:" << event.position() << Int(event.button());
}
void mouseMoveEvent(MouseMoveEvent& event) override {
Debug{} << "window" << _id << "mouse move event:" << event.position() << event.relativePosition() << Uint32(event.buttons());
}
void mouseScrollEvent(MouseScrollEvent& event) override {
Debug{} << "window" << _id << "mouse scroll event:" << event.offset() << event.position();
}
void keyPressEvent(KeyEvent& event) override {
Debug{} << "window" << _id << "key press event:" << SDL_Keycode(event.key()) << event.keyName();
if(event.key() == KeyEvent::Key::F1) {
Debug{} << "starting text input";
application().startTextInput();
} else if(event.key() == KeyEvent::Key::Esc) {
Debug{} << "stopping text input";
application().stopTextInput();
} else if(event.key() == KeyEvent::Key::T) {
Debug{} << "setting window title";
setWindowTitle("This is a UTF-8 Window Title™ and it should have no exclamation mark!!"_s.exceptSuffix(2));
}
#ifndef CORRADE_TARGET_EMSCRIPTEN
else if(event.key() == KeyEvent::Key::S) {
Debug{} << "setting window size, which should trigger a viewport event";
setWindowSize(Vector2i{300, 200});
} else if(event.key() == KeyEvent::Key::W) {
Debug{} << "setting max window size, which should trigger a viewport event";
setMaxWindowSize(Vector2i{700, 500});
}
#endif
else if(event.key() == KeyEvent::Key::H) {
Debug{} << "toggling hand cursor";
setCursor(cursor() == Cursor::Arrow ? Cursor::Hand : Cursor::Arrow);
}
#ifndef CORRADE_TARGET_EMSCRIPTEN
else if(event.key() == KeyEvent::Key::L) {
Debug{} << "toggling locked mouse";
setCursor(cursor() == Cursor::Arrow ? Cursor::HiddenLocked : Cursor::Arrow);
}
#else
else if(event.key() == KeyEvent::Key::F) {
Debug{} << "toggling fullscreen";
setContainerCssClass((_fullscreen ^= true) ? "mn-fullsize" : "");
}
#endif
else if(event.key() == KeyEvent::Key::X) {
Debug{} << "requesting an exit with code 5";
exit(5);
}
}
void keyReleaseEvent(KeyEvent& event) override {
Debug{} << "window" << _id << "key release event:" << SDL_Keycode(event.key()) << event.keyName();
}
void textInputEvent(TextInputEvent& event) override {
Debug{} << "window" << _id << "text input event:" << event.text();
}
private:
std::size_t _id;
};
#endif
struct Sdl2ApplicationTest: Platform::Application { struct Sdl2ApplicationTest: Platform::Application {
explicit Sdl2ApplicationTest(const Arguments& arguments); explicit Sdl2ApplicationTest(const Arguments& arguments);
@ -118,6 +220,10 @@ struct Sdl2ApplicationTest: Platform::Application {
Debug{} << "setting max window size, which should trigger a viewport event"; Debug{} << "setting max window size, which should trigger a viewport event";
setMaxWindowSize(Vector2i{700, 500}); setMaxWindowSize(Vector2i{700, 500});
} }
else if(event.key() == KeyEvent::Key::O) {
Debug{} << "opening window" << _windows.size();
arrayAppend(_windows, Containers::pointer<Sdl2ApplicationTestWindow>(*this, _windows.size()));
}
#endif #endif
else if(event.key() == KeyEvent::Key::H) { else if(event.key() == KeyEvent::Key::H) {
Debug{} << "toggling hand cursor"; Debug{} << "toggling hand cursor";
@ -157,10 +263,12 @@ struct Sdl2ApplicationTest: Platform::Application {
if(event.type == SDL_WINDOWEVENT) d << event.window.event; if(event.type == SDL_WINDOWEVENT) d << event.window.event;
} }
#ifdef CORRADE_TARGET_EMSCRIPTEN
private: private:
#ifndef CORRADE_TARGET_EMSCRIPTEN
Containers::Array<Containers::Pointer<Sdl2ApplicationTestWindow>> _windows;
#else
bool _fullscreen = false; bool _fullscreen = false;
#endif #endif
}; };
Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, NoCreate} { Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, NoCreate} {

Loading…
Cancel
Save