44
44
#include < emscripten/emscripten.h>
45
45
#include < emscripten/html5.h>
46
46
#endif
47
+ #include < Corrade/Containers/GrowableArray.h>
47
48
#include < Corrade/Utility/Algorithms.h>
48
49
#include < Corrade/Utility/Arguments.h>
49
50
@@ -82,14 +83,78 @@ enum class Sdl2ApplicationWindow::WindowFlag: UnsignedByte {
82
83
83
84
Sdl2ApplicationWindow::Sdl2ApplicationWindow (Sdl2Application& application, NoCreateT): _application{application}, _windowFlags{WindowFlag::Redraw} {}
84
85
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
+
85
94
Sdl2ApplicationWindow::~Sdl2ApplicationWindow () {
86
- /* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
87
- (it doesn't seem to crash on Linux) */
88
95
#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 ();
90
105
#endif
91
106
}
92
107
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
+
93
158
Vector2 Sdl2ApplicationWindow::dpiScaling (const Configuration& configuration) {
94
159
std::ostream* verbose = _application._verboseLog ? Debug::output () : nullptr ;
95
160
@@ -555,25 +620,18 @@ bool Sdl2Application::tryCreate(const Configuration& configuration) {
555
620
return tryCreate (configuration, GLConfiguration{});
556
621
#endif
557
622
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 ());
562
626
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))
575
630
return false ;
576
- }
631
+
632
+ /* Register the main window in the window list */
633
+ CORRADE_INTERNAL_ASSERT (_windows.isEmpty ());
634
+ arrayAppend (_windows, this );
577
635
578
636
/* Emscripten-specific initialization */
579
637
#else
@@ -635,6 +693,11 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
635
693
CORRADE_ASSERT (_context->version () == GL::Version::None,
636
694
" Platform::Sdl2Application::tryCreate(): context already created" , false );
637
695
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
+
638
701
/* Enable double buffering, set up buffer sizes */
639
702
SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1 );
640
703
SDL_GL_SetAttribute (SDL_GL_RED_SIZE, glConfiguration.colorBufferSize ().r ());
@@ -655,10 +718,6 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
655
718
656
719
/* * @todo Remove when Emscripten has proper SDL2 support */
657
720
#ifndef CORRADE_TARGET_EMSCRIPTEN
658
- /* Scale window based on DPI */
659
- _dpiScaling = dpiScaling (configuration);
660
- const Vector2i scaledWindowSize = configuration.size ()*_dpiScaling;
661
-
662
721
/* Request debug context if GpuValidation is enabled either via the
663
722
configuration or via command-line */
664
723
GLConfiguration::Flags glFlags = glConfiguration.flags ();
@@ -721,25 +780,18 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
721
780
#endif
722
781
}
723
782
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
725
784
blinking in case the context creation fails due to unsupported
726
785
configuration or if it gets destroyed for fallback context creation
727
786
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))
739
792
return false ;
740
- }
741
793
742
- /* Create context */
794
+ /* Create a context */
743
795
_glContext = SDL_GL_CreateContext (_window);
744
796
745
797
#ifndef MAGNUM_TARGET_GLES
@@ -780,7 +832,8 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
780
832
on Linux at least, but better stay on the safe side as this way
781
833
worked correctly for 10+ years on all platforms and reusing an
782
834
existing window might not. */
783
- SDL_DestroyWindow (_window);
835
+ destroyWindow ();
836
+ _window = nullptr ;
784
837
785
838
SDL_GL_SetAttribute (SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
786
839
SDL_GL_SetAttribute (SDL_GL_CONTEXT_MINOR_VERSION, 1 );
@@ -793,14 +846,8 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
793
846
SDL_GL_SetAttribute (SDL_GL_CONTEXT_FLAGS, int (UnsignedLong (glFlags & ~GLConfiguration::Flag::ForwardCompatible) & 0xffffffffu ));
794
847
795
848
/* 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))
802
850
return false ;
803
- }
804
851
805
852
/* Create compatibility context */
806
853
_glContext = SDL_GL_CreateContext (_window);
@@ -810,7 +857,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
810
857
/* Cannot create context (or fallback compatibility context on desktop) */
811
858
if (!_glContext) {
812
859
Error () << " Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError ();
813
- SDL_DestroyWindow (_window );
860
+ destroyWindow ( );
814
861
_window = nullptr ;
815
862
return false ;
816
863
}
@@ -880,7 +927,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
880
927
if (!_context->tryCreate (glConfiguration)) {
881
928
#ifndef CORRADE_TARGET_EMSCRIPTEN
882
929
SDL_GL_DeleteContext (_glContext);
883
- SDL_DestroyWindow (_window );
930
+ destroyWindow ( );
884
931
_window = nullptr ;
885
932
#else
886
933
SDL_FreeSurface (_surface);
@@ -952,10 +999,20 @@ bool Sdl2Application::setSwapInterval(const Int interval) {
952
999
}
953
1000
954
1001
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
955
1010
/* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
956
1011
(it doesn't seem to crash on Linux). Because this seems to be yet
957
1012
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
959
1016
960
1017
#ifdef MAGNUM_TARGET_GL
961
1018
/* Destroy Magnum context first to avoid it potentially accessing the
@@ -974,14 +1031,6 @@ Sdl2Application::~Sdl2Application() {
974
1031
SDL_FreeCursor (cursor);
975
1032
#endif
976
1033
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
-
985
1034
SDL_Quit ();
986
1035
}
987
1036
@@ -1006,6 +1055,28 @@ void Sdl2Application::exit(const int exitCode) {
1006
1055
_exitCode = exitCode;
1007
1056
}
1008
1057
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
+
1009
1080
bool Sdl2Application::mainLoopIteration () {
1010
1081
/* If exit was requested directly in the constructor, exit immediately
1011
1082
without calling anything else */
@@ -1054,6 +1125,7 @@ bool Sdl2Application::mainLoopIteration() {
1054
1125
while (SDL_PollEvent (&event)) {
1055
1126
switch (event.type ) {
1056
1127
case SDL_WINDOWEVENT:
1128
+ CORRADE_INTERNAL_ASSERT (event.window .windowID < _windows.size () && _windows[event.window .windowID ]);
1057
1129
switch (event.window .event ) {
1058
1130
/* Not using SDL_WINDOWEVENT_RESIZED, because that doesn't
1059
1131
get fired when the window is resized programmatically
@@ -1075,15 +1147,18 @@ bool Sdl2Application::mainLoopIteration() {
1075
1147
#endif
1076
1148
_dpiScaling};
1077
1149
/* * @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;
1080
1154
#endif
1081
1155
} break ;
1156
+ #warning SDL_WINDOWEVENT_CLOSE
1082
1157
/* Direct everything that wasn't exposed via a callback to
1083
1158
anyEvent(), so users can implement event handling for
1084
1159
things not present in the Application APIs */
1085
1160
case SDL_WINDOWEVENT_EXPOSED:
1086
- _windowFlags |= WindowFlag::Redraw;
1161
+ _windows[event. window . windowID ]-> _windowFlags |= WindowFlag::Redraw;
1087
1162
if (!(_flags & Flag::NoAnyEvent)) anyEvent (event);
1088
1163
break ;
1089
1164
default :
@@ -1093,7 +1168,9 @@ bool Sdl2Application::mainLoopIteration() {
1093
1168
case SDL_KEYDOWN:
1094
1169
case SDL_KEYUP: {
1095
1170
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);
1097
1174
} break ;
1098
1175
1099
1176
case SDL_MOUSEBUTTONDOWN:
@@ -1103,34 +1180,37 @@ bool Sdl2Application::mainLoopIteration() {
1103
1180
, event.button .clicks
1104
1181
#endif
1105
1182
};
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);
1107
1186
} break ;
1108
1187
1109
1188
case SDL_MOUSEWHEEL: {
1110
1189
MouseScrollEvent e{event, {Float (event.wheel .x ), Float (event.wheel .y )}};
1111
- mouseScrollEvent ( e);
1190
+ callEventHandler (event. wheel . windowID , &Sdl2ApplicationWindow::mouseScrollEvent, e);
1112
1191
} break ;
1113
1192
1114
1193
case SDL_MOUSEMOTION: {
1115
1194
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);
1117
1196
break ;
1118
1197
}
1119
1198
1120
1199
case SDL_MULTIGESTURE: {
1121
1200
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?!
1122
1202
multiGestureEvent (e);
1123
1203
break ;
1124
1204
}
1125
1205
1126
1206
case SDL_TEXTINPUT: {
1127
1207
TextInputEvent e{event, event.text .text };
1128
- textInputEvent ( e);
1208
+ callEventHandler (event. text . windowID , &Sdl2ApplicationWindow::textInputEvent, e);
1129
1209
} break ;
1130
1210
1131
1211
case SDL_TEXTEDITING: {
1132
1212
TextEditingEvent e{event, event.edit .text , event.edit .start , event.edit .length };
1133
- textEditingEvent ( e);
1213
+ callEventHandler (event. edit . windowID , &Sdl2ApplicationWindow::textEditingEvent, e);
1134
1214
} break ;
1135
1215
1136
1216
case SDL_QUIT: {
@@ -1156,11 +1236,18 @@ bool Sdl2Application::mainLoopIteration() {
1156
1236
/* Tick event */
1157
1237
if (!(_flags & Flag::NoTickEvent)) tickEvent ();
1158
1238
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
+ }
1163
1249
1250
+ if (somethingDrawn) {
1164
1251
#ifndef CORRADE_TARGET_EMSCRIPTEN
1165
1252
/* If VSync is not enabled, delay to prevent CPU hogging (if set) */
1166
1253
if (!(_flags & Flag::VSyncEnabled) && _minimalLoopPeriod) {
0 commit comments