Skip to content

Commit

Permalink
Platform: make *Application::exit() exit right after constructor ends.
Browse files Browse the repository at this point in the history
Instead of first entering the main loop, processing events etc. This
also makes it finally possible to exit the application cleanly, with all
non-global destructors executed as well.
  • Loading branch information
mosra committed Apr 9, 2020
1 parent 3de5075 commit 2149e78
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 36 deletions.
3 changes: 3 additions & 0 deletions doc/changelog.dox
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ See also:
@ref Platform::Sdl2Application (which was behaving like virtual before)
(see [mosra/magnum#243](https://github.com/mosra/magnum/issues/243))
- Undefining more noise from `Xlib.h` (see [mosra/magnum#430](https://github.com/mosra/magnum/pull/430))
- Calling @ref Platform::Sdl2Application::exit() "Platform::*Application::exit()"
directly in the application constructor will now make it exit right after
constructor finished, without any event processing (see [mosra/magnum#429](https://github.com/mosra/magnum/issues/429))

@subsubsection changelog-latest-changes-trade Trade library

Expand Down
24 changes: 24 additions & 0 deletions doc/snippets/MagnumPlatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,27 @@ for(Platform::Screen* s = app.screens().first(); s; s = s->nextFartherScreen())
}

}

namespace G {

struct MyApplication: Platform::Application {
MyApplication(const Arguments& arguments);
bool everythingGoingAsExpected = false;
};

/* [exit-from-constructor] */
MyApplication::MyApplication(const Arguments& arguments):
Platform::Application{arguments, NoCreate}
{
//

if(!everythingGoingAsExpected) {
exit(1);
return;
}

//
}
/* [exit-from-constructor] */

}
8 changes: 8 additions & 0 deletions src/Magnum/Platform/AbstractXApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ void AbstractXApplication::swapBuffers() {
}

int AbstractXApplication::exec() {
/* If exit was requested directly in the constructor, exit immediately
without calling anything else */
if(_flags & Flag::Exit) return _exitCode;

/* Show window */
XMapWindow(_display, _window);

Expand All @@ -132,6 +136,10 @@ int AbstractXApplication::exec() {
}

bool AbstractXApplication::mainLoopIteration() {
/* If exit was requested directly in the constructor, exit immediately
without calling anything else */
if(_flags & Flag::Exit) return false;

XEvent event;

/* Closed window */
Expand Down
15 changes: 14 additions & 1 deletion src/Magnum/Platform/AbstractXApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,21 @@ class AbstractXApplication {
bool mainLoopIteration();

/**
* @brief Exit application main loop
* @brief Exit application
* @param exitCode The exit code the application should return
*
* When called from application constructor, it will cause the
* application to exit immediately after constructor ends, without any
* events being processed. Calling this function is recommended over
* @ref std::exit() or @ref Corrade::Utility::Fatal "Fatal", which exit
* without calling destructors on local scope. Note that, however, you
* need to explicitly @cpp return @ce after calling it, as it can't
* exit the constructor on its own:
*
* @snippet MagnumPlatform.cpp exit-from-constructor
*
* When called from the main loop, the application exits cleanly
* before next main loop iteration is executed.
*/
void exit(int exitCode = 0) {
_flags |= Flag::Exit;
Expand Down
4 changes: 4 additions & 0 deletions src/Magnum/Platform/EmscriptenApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,10 @@ EmscriptenApplication::GLConfiguration::GLConfiguration():
#endif

int EmscriptenApplication::exec() {
/* If exit was requested directly in the constructor, exit immediately
without calling anything else */
if(_flags & Flag::ExitRequested) return 0;

redraw();
return 0;
}
Expand Down
17 changes: 14 additions & 3 deletions src/Magnum/Platform/EmscriptenApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,21 @@ class EmscriptenApplication {

/**
* @brief Exit application main loop
* @param exitCode Ignored, present only for API compatibility with
* other app implementations.
*
* Stops execution started by @ref exec(). The @p exitCode is ignored
* and present only for API compatibility with other app
* implementations.
* When called from application constructor, it will cause the
* application to exit immediately after constructor ends, without any
* events being processed. Calling this function is recommended over
* @ref std::exit() or @ref Corrade::Utility::Fatal "Fatal", which exit
* immediately and without calling destructors on local scope. Note
* that, however, you need to explicitly @cpp return @ce after calling
* it, as it can't exit the constructor on its own:
*
* @snippet MagnumPlatform.cpp exit-from-constructor
*
* When called from the main loop, the application exits cleanly
* before next main loop iteration is executed.
*/
void exit(int exitCode = 0);

Expand Down
20 changes: 19 additions & 1 deletion src/Magnum/Platform/GlfwApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ static_assert(GLFW_TRUE == true && GLFW_FALSE == false, "GLFW does not have sane
enum class GlfwApplication::Flag: UnsignedByte {
Redraw = 1 << 0,
TextInputActive = 1 << 1,
Exit = 1 << 2,
#ifdef CORRADE_TARGET_APPLE
HiDpiWarningPrinted = 1 << 2
HiDpiWarningPrinted = 1 << 3
#endif
};

Expand Down Expand Up @@ -546,6 +547,10 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf
CORRADE_IGNORE_DEPRECATED_POP
#endif

/* If exit() was called before the window got created, be sure to propagate
it */
glfwSetWindowShouldClose(_window, !!(_flags & Flag::Exit));

/* Make the final context current */
glfwMakeContextCurrent(_window);

Expand Down Expand Up @@ -701,6 +706,10 @@ int GlfwApplication::exec() {
}

bool GlfwApplication::mainLoopIteration() {
/* If exit was requested directly in the constructor, exit immediately
without calling anything else */
if(_flags & Flag::Exit || glfwWindowShouldClose(_window)) return false;

CORRADE_ASSERT(_window, "Platform::GlfwApplication::mainLoopIteration(): no window opened", {});

/*
Expand Down Expand Up @@ -737,6 +746,15 @@ bool GlfwApplication::mainLoopIteration() {
return !glfwWindowShouldClose(_window);
}

void GlfwApplication::exit(int exitCode) {
_flags |= Flag::Exit;
_exitCode = exitCode;

/* If the window is already created, tell GLFW that it should close. If
not, this is done in tryCreate() once the window is created */
if(_window) glfwSetWindowShouldClose(_window, true);
}

namespace {

constexpr Int CursorMap[] {
Expand Down
21 changes: 16 additions & 5 deletions src/Magnum/Platform/GlfwApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,24 @@ class GlfwApplication {
bool mainLoopIteration();

/**
* @brief Exit application main loop
* @brief Exit application
* @param exitCode The exit code the application should return
*
* When called from application constructor, it will cause the
* application to exit immediately after constructor ends, without any
* events being processed (thus not even @ref exitEvent()). Calling
* this function is recommended over @ref std::exit() or
* @ref Corrade::Utility::Fatal "Fatal", which exit without calling
* destructors on local scope. Note that, however, you need to
* explicitly @cpp return @ce after calling it, as it can't exit the
* constructor on its own:
*
* @snippet MagnumPlatform.cpp exit-from-constructor
*
* When called from the main loop, the application exits cleanly
* before next main loop iteration is executed.
*/
void exit(int exitCode = 0) {
glfwSetWindowShouldClose(_window, true);
_exitCode = exitCode;
}
void exit(int exitCode = 0);

/**
* @brief Underlying window handle
Expand Down
4 changes: 4 additions & 0 deletions src/Magnum/Platform/Sdl2Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,10 @@ void Sdl2Application::exit(const int exitCode) {
}

bool Sdl2Application::mainLoopIteration() {
/* If exit was requested directly in the constructor, exit immediately
without calling anything else */
if(_flags & Flag::Exit) return false;

#ifndef CORRADE_TARGET_EMSCRIPTEN
CORRADE_ASSERT(_window, "Platform::Sdl2Application::mainLoopIteration(): no window opened", {});
#else
Expand Down
28 changes: 20 additions & 8 deletions src/Magnum/Platform/Sdl2Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,14 +529,6 @@ class Sdl2Application {
*/
int exec();

/**
* @brief Exit application main loop
* @param exitCode The exit code the application should return
*
* Stops main loop started by @ref exec().
*/
void exit(int exitCode = 0);

/**
* @brief Run one iteration of application main loop
* @return @cpp false @ce if @ref exit() was called and the application
Expand All @@ -549,6 +541,26 @@ class Sdl2Application {
*/
bool mainLoopIteration();

/**
* @brief Exit application
* @param exitCode The exit code the application should return
*
* When called from application constructor, it will cause the
* application to exit immediately after constructor ends, without any
* events being processed (thus not even @ref exitEvent()). Calling
* this function is recommended over @ref std::exit() or
* @ref Corrade::Utility::Fatal "Fatal", which exit without calling
* destructors on local scope. Note that, however, you need to
* explicitly @cpp return @ce after calling it, as it can't exit the
* constructor on its own:
*
* @snippet MagnumPlatform.cpp exit-from-constructor
*
* When called from the main loop, the application exits cleanly
* before next main loop iteration is executed.
*/
void exit(int exitCode = 0);

#ifndef CORRADE_TARGET_EMSCRIPTEN
/**
* @brief Underlying window handle
Expand Down
44 changes: 28 additions & 16 deletions src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
DEALINGS IN THE SOFTWARE.
*/

#include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/DebugStl.h>

#include "Magnum/Platform/EmscriptenApplication.h"
Expand All @@ -35,22 +36,7 @@ namespace Magnum { namespace Platform { namespace Test {

struct EmscriptenApplicationTest: Platform::Application {
/* For testing resize events */
explicit EmscriptenApplicationTest(const Arguments& arguments):
Platform::Application{arguments,
Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)
//, GLConfiguration{}.setFlags({})
} {

Debug{} << "window size" << windowSize()
#ifdef MAGNUM_TARGET_GL
<< framebufferSize()
#endif
<< dpiScaling() << devicePixelRatio();

/* This uses a VAO on WebGL 1, so it will crash in case GL flags are
missing EnableExtensionsByDefault (uncomment above) */
GL::Mesh mesh;
}
explicit EmscriptenApplicationTest(const Arguments& arguments);

virtual void drawEvent() override {
Debug() << "draw event";
Expand Down Expand Up @@ -150,6 +136,32 @@ struct EmscriptenApplicationTest: Platform::Application {
bool _redraw = false;
};

EmscriptenApplicationTest::EmscriptenApplicationTest(const Arguments& arguments): Platform::Application{arguments, NoCreate} {
Utility::Arguments args;
args.addSkippedPrefix("magnum", "engine-specific options")
.addBooleanOption("exit-immediately").setHelp("exit-immediately", "exit the application immediately from the constructor, to test that the app doesn't run any event handlers after")
.parse(arguments.argc, arguments.argv);

if(args.isSet("exit-immediately")) {
exit();
return;
}

create(Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)
//, GLConfiguration{}.setFlags({})
);

Debug{} << "window size" << windowSize()
#ifdef MAGNUM_TARGET_GL
<< framebufferSize()
#endif
<< dpiScaling() << devicePixelRatio();

/* This uses a VAO on WebGL 1, so it will crash in case GL flags are
missing EnableExtensionsByDefault (uncomment above) */
GL::Mesh mesh;
}

}}}

MAGNUM_APPLICATION_MAIN(Magnum::Platform::Test::EmscriptenApplicationTest)
6 changes: 6 additions & 0 deletions src/Magnum/Platform/Test/GlfwApplicationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,14 @@ GlfwApplicationTest::GlfwApplicationTest(const Arguments& arguments): Platform::
Utility::Arguments args;
args.addOption("dpi-scaling").setHelp("dpi-scaling", "DPI scaled passed via Configuration instead of --magnum-dpi-scaling, to test app overrides")
.addSkippedPrefix("magnum", "engine-specific options")
.addBooleanOption("exit-immediately").setHelp("exit-immediately", "exit the application immediately from the constructor, to test that the app doesn't run any event handlers after")
.parse(arguments.argc, arguments.argv);

if(args.isSet("exit-immediately")) {
exit();
return;
}

Configuration conf;
conf.setWindowFlags(Configuration::WindowFlag::Resizable);
if(!args.value("dpi-scaling").empty())
Expand Down
18 changes: 17 additions & 1 deletion src/Magnum/Platform/Test/GlxApplicationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,35 @@
DEALINGS IN THE SOFTWARE.
*/

#include <Corrade/Utility/Arguments.h>

#include "Magnum/Platform/GlxApplication.h"

namespace Magnum { namespace Platform { namespace Test { namespace {

struct GlxApplicationTest: Platform::Application {
explicit GlxApplicationTest(const Arguments& arguments): Platform::Application{arguments} {}
explicit GlxApplicationTest(const Arguments& arguments);

void drawEvent() override {
Debug{} << "draw event";
swapBuffers();
}
};

GlxApplicationTest::GlxApplicationTest(const Arguments& arguments): Platform::Application{arguments, NoCreate} {
Utility::Arguments args;
args.addSkippedPrefix("magnum", "engine-specific options")
.addBooleanOption("exit-immediately").setHelp("exit-immediately", "exit the application immediately from the constructor, to test that the app doesn't run any event handlers after")
.parse(arguments.argc, arguments.argv);

if(args.isSet("exit-immediately")) {
exit();
return;
}

create(Configuration{});
}

}}}}

MAGNUM_APPLICATION_MAIN(Magnum::Platform::Test::GlxApplicationTest)
6 changes: 6 additions & 0 deletions src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,14 @@ Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform::
Utility::Arguments args;
args.addOption("dpi-scaling").setHelp("dpi-scaling", "DPI scaled passed via Configuration instead of --magnum-dpi-scaling, to test app overrides")
.addSkippedPrefix("magnum", "engine-specific options")
.addBooleanOption("exit-immediately").setHelp("exit-immediately", "exit the application immediately from the constructor, to test that the app doesn't run any event handlers after")
.parse(arguments.argc, arguments.argv);

if(args.isSet("exit-immediately")) {
exit();
return;
}

Configuration conf;
conf.setWindowFlags(Configuration::WindowFlag::Resizable);
if(!args.value("dpi-scaling").empty())
Expand Down
Loading

0 comments on commit 2149e78

Please sign in to comment.