Skip to content

Commit 1e48a70

Browse files
committed
[wip] everything is terrible
1 parent 551f507 commit 1e48a70

File tree

3 files changed

+303
-72
lines changed

3 files changed

+303
-72
lines changed

src/Magnum/Platform/Sdl2Application.cpp

Lines changed: 157 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <emscripten/emscripten.h>
4545
#include <emscripten/html5.h>
4646
#endif
47+
#include <Corrade/Containers/GrowableArray.h>
4748
#include <Corrade/Utility/Algorithms.h>
4849
#include <Corrade/Utility/Arguments.h>
4950

@@ -82,14 +83,78 @@ enum class Sdl2ApplicationWindow::WindowFlag: UnsignedByte {
8283

8384
Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application, NoCreateT): _application{application}, _windowFlags{WindowFlag::Redraw} {}
8485

86+
#ifndef CORRADE_TARGET_EMSCRIPTEN
87+
Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application, const Configuration& configuration): Sdl2ApplicationWindow{application, NoCreate} {
88+
if(!tryCreateWindow(configuration)) std::exit(1);
89+
}
90+
91+
Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application): Sdl2ApplicationWindow{application, Configuration{}} {}
92+
#endif
93+
8594
Sdl2ApplicationWindow::~Sdl2ApplicationWindow() {
86-
/* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
87-
(it doesn't seem to crash on Linux) */
8895
#ifndef CORRADE_TARGET_EMSCRIPTEN
89-
if(_window) SDL_DestroyWindow(_window);
96+
/* If the window isn't created yet because tryCreateWindow(), nothing to
97+
do. This also covers the case for the main application window, which
98+
gets destroyed (and reset to null) from within ~Sdl2Application()
99+
because it has to be done before SDL_Quit(), and this destructor is
100+
called after that. There it also means that it's not needed to remove
101+
itself from the window list, as the list is already gone at this
102+
point. */
103+
if(_window)
104+
destroyWindow();
90105
#endif
91106
}
92107

108+
bool Sdl2ApplicationWindow::tryCreateWindow(const Configuration& configuration) {
109+
CORRADE_ASSERT(!_window,
110+
"Platform::Sdl2ApplicationWindow::tryCreateWindow(): window already created", false);
111+
112+
/* Scale window based on DPI */
113+
_dpiScaling = dpiScaling(configuration);
114+
const Vector2i scaledWindowSize = configuration.size()*_dpiScaling;
115+
116+
/* Create a window */
117+
if(!(_window = SDL_CreateWindow(
118+
#ifndef CORRADE_TARGET_IOS
119+
configuration.title().data(),
120+
#else
121+
nullptr,
122+
#endif
123+
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
124+
scaledWindowSize.x(), scaledWindowSize.y(),
125+
SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags())|_application._configurationFlags)))
126+
{
127+
Error() << "Platform::Sdl2ApplicationWindow::tryCreateWindow(): cannot create a window:" << SDL_GetError();
128+
return false;
129+
}
130+
131+
/* Add itself to the window list */
132+
const std::size_t windowId = SDL_GetWindowID(_window);
133+
if(windowId >= _application._windows.size()) {
134+
for(Sdl2ApplicationWindow*& i: arrayAppend(_application._windows, NoInit, windowId - _application._windows.size() + 1))
135+
i = nullptr;
136+
}
137+
CORRADE_INTERNAL_ASSERT(!_application._windows[windowId]);
138+
_application._windows[windowId] = this;
139+
140+
return true;
141+
}
142+
143+
#ifndef CORRADE_TARGET_EMSCRIPTEN
144+
void Sdl2ApplicationWindow::destroyWindow() {
145+
/* To prevent accidental double destructions and such, this function should
146+
only be called if a window is actually created */
147+
CORRADE_INTERNAL_ASSERT(_window);
148+
149+
/* Remove itself from the window list */
150+
const std::size_t id = SDL_GetWindowID(_window);
151+
CORRADE_INTERNAL_ASSERT(id < _application._windows.size());
152+
_application._windows[id] = nullptr;
153+
154+
SDL_DestroyWindow(_window);
155+
}
156+
#endif
157+
93158
Vector2 Sdl2ApplicationWindow::dpiScaling(const Configuration& configuration) {
94159
std::ostream* verbose = _application._verboseLog ? Debug::output() : nullptr;
95160

@@ -555,25 +620,18 @@ bool Sdl2Application::tryCreate(const Configuration& configuration) {
555620
return tryCreate(configuration, GLConfiguration{});
556621
#endif
557622

558-
#ifndef CORRADE_TARGET_EMSCRIPTEN
559-
/* Scale window based on DPI */
560-
_dpiScaling = dpiScaling(configuration);
561-
const Vector2i scaledWindowSize = configuration.size()*_dpiScaling;
623+
/* Save the application-global configuration flags to be used to create all
624+
windows */
625+
_configurationFlags = Uint32(configuration.flags());
562626

563-
/* Create window */
564-
if(!(_window = SDL_CreateWindow(
565-
#ifndef CORRADE_TARGET_IOS
566-
configuration.title().data(),
567-
#else
568-
nullptr,
569-
#endif
570-
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
571-
scaledWindowSize.x(), scaledWindowSize.y(),
572-
SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_OPENGL|Uint32(configuration.windowFlags()))))
573-
{
574-
Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError();
627+
#ifndef CORRADE_TARGET_EMSCRIPTEN
628+
/* Create the main window */
629+
if(!tryCreateWindow(configuration))
575630
return false;
576-
}
631+
632+
/* Register the main window in the window list */
633+
CORRADE_INTERNAL_ASSERT(_windows.isEmpty());
634+
arrayAppend(_windows, this);
577635

578636
/* Emscripten-specific initialization */
579637
#else
@@ -635,6 +693,11 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
635693
CORRADE_ASSERT(_context->version() == GL::Version::None,
636694
"Platform::Sdl2Application::tryCreate(): context already created", false);
637695

696+
/* Save the application-global configuration flags to be used to create all
697+
windows. Since we're creating a GL context, request the window to also
698+
be OpenGL-enabled. */
699+
_configurationFlags = Uint32(configuration.flags()|Configuration::Flag::OpenGL);
700+
638701
/* Enable double buffering, set up buffer sizes */
639702
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
640703
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, glConfiguration.colorBufferSize().r());
@@ -655,10 +718,6 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
655718

656719
/** @todo Remove when Emscripten has proper SDL2 support */
657720
#ifndef CORRADE_TARGET_EMSCRIPTEN
658-
/* Scale window based on DPI */
659-
_dpiScaling = dpiScaling(configuration);
660-
const Vector2i scaledWindowSize = configuration.size()*_dpiScaling;
661-
662721
/* Request debug context if GpuValidation is enabled either via the
663722
configuration or via command-line */
664723
GLConfiguration::Flags glFlags = glConfiguration.flags();
@@ -721,25 +780,18 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
721780
#endif
722781
}
723782

724-
/* Create a window. Hide it by default so we don't have distracting window
783+
/* Hide the main window by default so we don't have distracting window
725784
blinking in case the context creation fails due to unsupported
726785
configuration or if it gets destroyed for fallback context creation
727786
below. */
728-
if(!(_window = SDL_CreateWindow(
729-
#ifndef CORRADE_TARGET_IOS
730-
configuration.title().data(),
731-
#else
732-
nullptr,
733-
#endif
734-
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
735-
scaledWindowSize.x(), scaledWindowSize.y(),
736-
SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN|SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags()))))
737-
{
738-
Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError();
787+
Sdl2ApplicationWindow::Configuration hiddenWindowConfiguration{configuration};
788+
hiddenWindowConfiguration.addWindowFlags(Sdl2ApplicationWindow::Configuration::WindowFlag::Hidden);
789+
790+
/* Create a window */
791+
if(!tryCreateWindow(hiddenWindowConfiguration))
739792
return false;
740-
}
741793

742-
/* Create context */
794+
/* Create a context */
743795
_glContext = SDL_GL_CreateContext(_window);
744796

745797
#ifndef MAGNUM_TARGET_GLES
@@ -780,7 +832,8 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
780832
on Linux at least, but better stay on the safe side as this way
781833
worked correctly for 10+ years on all platforms and reusing an
782834
existing window might not. */
783-
SDL_DestroyWindow(_window);
835+
destroyWindow();
836+
_window = nullptr;
784837

785838
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
786839
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
@@ -793,14 +846,8 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
793846
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, int(UnsignedLong(glFlags & ~GLConfiguration::Flag::ForwardCompatible) & 0xffffffffu));
794847

795848
/* Create a new window using the refreshed GL attributes */
796-
if(!(_window = SDL_CreateWindow(configuration.title().data(),
797-
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
798-
scaledWindowSize.x(), scaledWindowSize.y(),
799-
SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN|SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags()))))
800-
{
801-
Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError();
849+
if(!tryCreateWindow(hiddenWindowConfiguration))
802850
return false;
803-
}
804851

805852
/* Create compatibility context */
806853
_glContext = SDL_GL_CreateContext(_window);
@@ -810,7 +857,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
810857
/* Cannot create context (or fallback compatibility context on desktop) */
811858
if(!_glContext) {
812859
Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError();
813-
SDL_DestroyWindow(_window);
860+
destroyWindow();
814861
_window = nullptr;
815862
return false;
816863
}
@@ -880,7 +927,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
880927
if(!_context->tryCreate(glConfiguration)) {
881928
#ifndef CORRADE_TARGET_EMSCRIPTEN
882929
SDL_GL_DeleteContext(_glContext);
883-
SDL_DestroyWindow(_window);
930+
destroyWindow();
884931
_window = nullptr;
885932
#else
886933
SDL_FreeSurface(_surface);
@@ -952,10 +999,20 @@ bool Sdl2Application::setSwapInterval(const Int interval) {
952999
}
9531000

9541001
Sdl2Application::~Sdl2Application() {
1002+
#ifndef CORRADE_TARGET_EMSCRIPTEN
1003+
/* Destroy the main SDL window first. After that, there should be no
1004+
remaining registered windows. */
1005+
if(_window)
1006+
destroyWindow();
1007+
for(Sdl2ApplicationWindow* i: _windows)
1008+
CORRADE_ASSERT(!i, "Sdl2Application: destructed with windows still open", );
1009+
#else
9551010
/* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
9561011
(it doesn't seem to crash on Linux). Because this seems to be yet
9571012
another pointless platform difference, to be safe do the same check with
958-
all. */
1013+
all APIs. */
1014+
if(_window) SDL_DestroyWindow(_window);
1015+
#endif
9591016

9601017
#ifdef MAGNUM_TARGET_GL
9611018
/* Destroy Magnum context first to avoid it potentially accessing the
@@ -974,14 +1031,6 @@ Sdl2Application::~Sdl2Application() {
9741031
SDL_FreeCursor(cursor);
9751032
#endif
9761033

977-
/* The window would be destroyed in the base ~Sdl2ApplicationWindow(), but
978-
that's too late. Do it before calling SDL_Quit() and then reset the
979-
window pointer so it's not called again after. */
980-
if(_window) {
981-
SDL_DestroyWindow(_window);
982-
_window = nullptr;
983-
}
984-
9851034
SDL_Quit();
9861035
}
9871036

@@ -1006,6 +1055,28 @@ void Sdl2Application::exit(const int exitCode) {
10061055
_exitCode = exitCode;
10071056
}
10081057

1058+
#ifndef CORRADE_TARGET_EMSCRIPTEN
1059+
void Sdl2Application::makeContextCurrent(Sdl2ApplicationWindow& window) {
1060+
/* Only do it if it is not active already */
1061+
if(_activeGlContextWindow != window._window) {
1062+
SDL_GL_MakeCurrent(window._window, _glContext);
1063+
_activeGlContextWindow = window._window;
1064+
#warning TODO
1065+
// Context::current().resetState(Context::State::WindowSpecific);
1066+
}
1067+
}
1068+
#endif
1069+
1070+
template<class ...Args> inline void Sdl2Application::callEventHandler(std::size_t windowId, void(Sdl2ApplicationWindow::*eventHandler)(Args...), Args&&... args) {
1071+
// if(!(windowId < _windows.size() && _windows[windowId])) {
1072+
// Debug() << "HUH" << windowId << _windows.size();
1073+
// return;
1074+
// }
1075+
1076+
CORRADE_INTERNAL_ASSERT(windowId < _windows.size() && _windows[windowId]);
1077+
(_windows[windowId]->*eventHandler)(std::forward<Args>(args)...);
1078+
}
1079+
10091080
bool Sdl2Application::mainLoopIteration() {
10101081
/* If exit was requested directly in the constructor, exit immediately
10111082
without calling anything else */
@@ -1054,6 +1125,7 @@ bool Sdl2Application::mainLoopIteration() {
10541125
while(SDL_PollEvent(&event)) {
10551126
switch(event.type) {
10561127
case SDL_WINDOWEVENT:
1128+
CORRADE_INTERNAL_ASSERT(event.window.windowID < _windows.size() && _windows[event.window.windowID]);
10571129
switch(event.window.event) {
10581130
/* Not using SDL_WINDOWEVENT_RESIZED, because that doesn't
10591131
get fired when the window is resized programmatically
@@ -1075,15 +1147,18 @@ bool Sdl2Application::mainLoopIteration() {
10751147
#endif
10761148
_dpiScaling};
10771149
/** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */
1078-
viewportEvent(e);
1079-
_windowFlags |= WindowFlag::Redraw;
1150+
#warning make context current here? probably??
1151+
makeContextCurrent(*_windows[event.window.windowID]);
1152+
callEventHandler(event.window.windowID, &Sdl2ApplicationWindow::viewportEvent, e);
1153+
_windows[event.window.windowID]->_windowFlags |= WindowFlag::Redraw;
10801154
#endif
10811155
} break;
1156+
#warning SDL_WINDOWEVENT_CLOSE
10821157
/* Direct everything that wasn't exposed via a callback to
10831158
anyEvent(), so users can implement event handling for
10841159
things not present in the Application APIs */
10851160
case SDL_WINDOWEVENT_EXPOSED:
1086-
_windowFlags |= WindowFlag::Redraw;
1161+
_windows[event.window.windowID]->_windowFlags |= WindowFlag::Redraw;
10871162
if(!(_flags & Flag::NoAnyEvent)) anyEvent(event);
10881163
break;
10891164
default:
@@ -1093,7 +1168,9 @@ bool Sdl2Application::mainLoopIteration() {
10931168
case SDL_KEYDOWN:
10941169
case SDL_KEYUP: {
10951170
KeyEvent e{event, static_cast<KeyEvent::Key>(event.key.keysym.sym), fixedModifiers(event.key.keysym.mod), event.key.repeat != 0};
1096-
event.type == SDL_KEYDOWN ? keyPressEvent(e) : keyReleaseEvent(e);
1171+
callEventHandler(event.key.windowID,
1172+
event.type == SDL_KEYDOWN ? &Sdl2ApplicationWindow::keyPressEvent : &Sdl2ApplicationWindow::keyReleaseEvent,
1173+
e);
10971174
} break;
10981175

10991176
case SDL_MOUSEBUTTONDOWN:
@@ -1103,34 +1180,37 @@ bool Sdl2Application::mainLoopIteration() {
11031180
, event.button.clicks
11041181
#endif
11051182
};
1106-
event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e);
1183+
callEventHandler(event.key.windowID,
1184+
event.type == SDL_MOUSEBUTTONDOWN ? &Sdl2ApplicationWindow::mousePressEvent : &Sdl2ApplicationWindow::mouseReleaseEvent,
1185+
e);
11071186
} break;
11081187

11091188
case SDL_MOUSEWHEEL: {
11101189
MouseScrollEvent e{event, {Float(event.wheel.x), Float(event.wheel.y)}};
1111-
mouseScrollEvent(e);
1190+
callEventHandler(event.wheel.windowID, &Sdl2ApplicationWindow::mouseScrollEvent, e);
11121191
} break;
11131192

11141193
case SDL_MOUSEMOTION: {
11151194
MouseMoveEvent e{event, {event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}, static_cast<MouseMoveEvent::Button>(event.motion.state)};
1116-
mouseMoveEvent(e);
1195+
callEventHandler(event.motion.windowID, &Sdl2ApplicationWindow::mouseMoveEvent, e);
11171196
break;
11181197
}
11191198

11201199
case SDL_MULTIGESTURE: {
11211200
MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers};
1201+
#warning wtf, why no window ID?!
11221202
multiGestureEvent(e);
11231203
break;
11241204
}
11251205

11261206
case SDL_TEXTINPUT: {
11271207
TextInputEvent e{event, event.text.text};
1128-
textInputEvent(e);
1208+
callEventHandler(event.text.windowID, &Sdl2ApplicationWindow::textInputEvent, e);
11291209
} break;
11301210

11311211
case SDL_TEXTEDITING: {
11321212
TextEditingEvent e{event, event.edit.text, event.edit.start, event.edit.length};
1133-
textEditingEvent(e);
1213+
callEventHandler(event.edit.windowID, &Sdl2ApplicationWindow::textEditingEvent, e);
11341214
} break;
11351215

11361216
case SDL_QUIT: {
@@ -1156,11 +1236,18 @@ bool Sdl2Application::mainLoopIteration() {
11561236
/* Tick event */
11571237
if(!(_flags & Flag::NoTickEvent)) tickEvent();
11581238

1159-
/* Draw event */
1160-
if(_windowFlags & WindowFlag::Redraw) {
1161-
_windowFlags &= ~WindowFlag::Redraw;
1162-
drawEvent();
1239+
/* Draw events */
1240+
bool somethingDrawn = false;
1241+
for(std::size_t i = 0; i != _windows.size(); ++i) {
1242+
if(!_windows[i] || !(_windows[i]->_windowFlags & WindowFlag::Redraw)) continue;
1243+
1244+
_windows[i]->_windowFlags &= ~WindowFlag::Redraw;
1245+
callEventHandler(i,
1246+
&Sdl2ApplicationWindow::drawEvent);
1247+
somethingDrawn = true;
1248+
}
11631249

1250+
if(somethingDrawn) {
11641251
#ifndef CORRADE_TARGET_EMSCRIPTEN
11651252
/* If VSync is not enabled, delay to prevent CPU hogging (if set) */
11661253
if(!(_flags & Flag::VSyncEnabled) && _minimalLoopPeriod) {

0 commit comments

Comments
 (0)