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
8384Sdl2ApplicationWindow::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+
8594Sdl2ApplicationWindow::~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+
93158Vector2 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
9541001Sdl2Application::~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+
10091080bool 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