Browse Source

Platform: implement {Glfw,Sdl2}Application::exitEvent().

SDL has to be special and behave weirdly, of course, as always.
simd
Vladimír Vondruš 8 years ago
parent
commit
3ca13dbc1b
  1. 6
      doc/changelog.dox
  2. 9
      src/Magnum/Platform/GlfwApplication.cpp
  3. 51
      src/Magnum/Platform/GlfwApplication.h
  4. 29
      src/Magnum/Platform/Sdl2Application.cpp
  5. 79
      src/Magnum/Platform/Sdl2Application.h
  6. 6
      src/Magnum/Platform/Test/GlfwApplicationTest.cpp
  7. 5
      src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp

6
doc/changelog.dox

@ -80,13 +80,17 @@ See also:
@ref Platform::AndroidApplication::ViewportEvent::dpiScaling(), @ref Platform::AndroidApplication::ViewportEvent::dpiScaling(),
@ref Platform::GlfwApplication::ViewportEvent::framebufferSize() and @ref Platform::GlfwApplication::ViewportEvent::framebufferSize() and
@ref Platform::GlfwApplication::ViewportEvent::dpiScaling() to ensure @ref Platform::GlfwApplication::ViewportEvent::dpiScaling() to ensure
feature parity with @ref Sdl2Application feature parity with @ref Platform::Sdl2Application
- Clarified HiDPI behavior of Android apps and mentioning - Clarified HiDPI behavior of Android apps and mentioning
@ref platforms-android-apps-manifest-screen-compatibility-mode "the necessary steps" @ref platforms-android-apps-manifest-screen-compatibility-mode "the necessary steps"
in the docs in the docs
- Implemented handling of @ref Platform::AndroidApplication::viewportEvent() - Implemented handling of @ref Platform::AndroidApplication::viewportEvent()
and documenting how to get Android to call it instead of relaunching the and documenting how to get Android to call it instead of relaunching the
app from scratch app from scratch
- New @ref Platform::Sdl2Application::exitEvent() and
@ref Platform::GlfwApplication::exitEvent() events for responding to
application window close and possibility to cancel it (for example to
show an exit confirmation dialog)
@subsection changelog-latest-changes Changes and improvements @subsection changelog-latest-changes Changes and improvements

9
src/Magnum/Platform/GlfwApplication.cpp

@ -454,6 +454,11 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf
void GlfwApplication::setupCallbacks() { void GlfwApplication::setupCallbacks() {
glfwSetWindowUserPointer(_window, this); glfwSetWindowUserPointer(_window, this);
glfwSetWindowCloseCallback(_window, [](GLFWwindow* const window){
ExitEvent e;
static_cast<GlfwApplication*>(glfwGetWindowUserPointer(window))->exitEvent(e);
if(!e.isAccepted()) glfwSetWindowShouldClose(window, false);
});
glfwSetWindowRefreshCallback(_window, [](GLFWwindow* const window){ glfwSetWindowRefreshCallback(_window, [](GLFWwindow* const window){
/* Properly redraw after the window is restored from minimized state */ /* Properly redraw after the window is restored from minimized state */
static_cast<GlfwApplication*>(glfwGetWindowUserPointer(window))->drawEvent(); static_cast<GlfwApplication*>(glfwGetWindowUserPointer(window))->drawEvent();
@ -577,6 +582,10 @@ auto GlfwApplication::MouseScrollEvent::modifiers() -> Modifiers {
return *_modifiers; return *_modifiers;
} }
void GlfwApplication::exitEvent(ExitEvent& event) {
event.setAccepted();
}
void GlfwApplication::viewportEvent(ViewportEvent& event) { void GlfwApplication::viewportEvent(ViewportEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH

51
src/Magnum/Platform/GlfwApplication.h

@ -149,6 +149,7 @@ class GlfwApplication {
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
class GLConfiguration; class GLConfiguration;
#endif #endif
class ExitEvent;
class ViewportEvent; class ViewportEvent;
class InputEvent; class InputEvent;
class KeyEvent; class KeyEvent;
@ -426,6 +427,17 @@ class GlfwApplication {
#else #else
private: private:
#endif #endif
/**
* @brief Exit event
*
* If implemented, it allows the application to react to an application
* exit (for example to save its internal state) and suppress it as
* well (for example to show a exit confirmation dialog). The default
* implementation calls @ref ExitEvent::setAccepted() on @p event,
* which tells the application that it's safe to exit.
*/
virtual void exitEvent(ExitEvent& event);
/** /**
* @brief Viewport event * @brief Viewport event
* *
@ -1130,6 +1142,45 @@ class GlfwApplication::Configuration {
CORRADE_ENUMSET_OPERATORS(GlfwApplication::Configuration::WindowFlags) CORRADE_ENUMSET_OPERATORS(GlfwApplication::Configuration::WindowFlags)
/**
@brief Exit event
@see @ref exitEvent()
*/
class GlfwApplication::ExitEvent {
public:
/** @brief Copying is not allowed */
ExitEvent(const ExitEvent&) = delete;
/** @brief Moving is not allowed */
ExitEvent(ExitEvent&&) = delete;
/** @brief Copying is not allowed */
ExitEvent& operator=(const ExitEvent&) = delete;
/** @brief Moving is not allowed */
ExitEvent& operator=(ExitEvent&&) = delete;
/** @brief Whether the event is accepted */
bool isAccepted() const { return _accepted; }
/**
* @brief Set event as accepted
*
* If the event is ignored (i.e., not set as accepted) in
* @ref exitEvent(), the application won't exit. Default implementation
* of @ref exitEvent() accepts the event.
*/
void setAccepted(bool accepted = true) { _accepted = accepted; }
private:
friend GlfwApplication;
explicit ExitEvent(): _accepted(false) {}
bool _accepted;
};
/** /**
@brief Viewport event @brief Viewport event

29
src/Magnum/Platform/Sdl2Application.cpp

@ -91,6 +91,12 @@ Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT):
args.parse(arguments.argc, arguments.argv); args.parse(arguments.argc, arguments.argv);
#endif #endif
/* Available since 2.0.4, disables interception of SIGINT and SIGTERM so
it's possible to Ctrl-C the application even if exitEvent() doesn't set
event.setAccepted(). */
#ifdef SDL_HINT_NO_SIGNAL_HANDLERS
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
#endif
/* Available since 2.0.8, disables compositor bypass on X11, which causes /* Available since 2.0.8, disables compositor bypass on X11, which causes
flickering on KWin as the compositor gets shut down on every startup */ flickering on KWin as the compositor gets shut down on every startup */
#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
@ -713,13 +719,18 @@ void Sdl2Application::mainLoopIteration() {
textEditingEvent(e); textEditingEvent(e);
} break; } break;
case SDL_QUIT: case SDL_QUIT: {
#ifndef CORRADE_TARGET_EMSCRIPTEN ExitEvent e;
_flags |= Flag::Exit; exitEvent(e);
#else if(e.isAccepted()) {
emscripten_cancel_main_loop(); #ifndef CORRADE_TARGET_EMSCRIPTEN
#endif _flags |= Flag::Exit;
return; #else
emscripten_cancel_main_loop();
#endif
return;
}
} break;
} }
} }
@ -795,6 +806,10 @@ void Sdl2Application::setTextInputRect(const Range2Di& rect) {
SDL_SetTextInputRect(&r); SDL_SetTextInputRect(&r);
} }
void Sdl2Application::exitEvent(ExitEvent& event) {
event.setAccepted();
}
void Sdl2Application::tickEvent() { void Sdl2Application::tickEvent() {
/* If this got called, the tick event is not implemented by user and thus /* If this got called, the tick event is not implemented by user and thus
we don't need to call it ever again */ we don't need to call it ever again */

79
src/Magnum/Platform/Sdl2Application.h

@ -239,13 +239,35 @@ If no other application header is included, this class is also aliased to
@cpp Platform::Application @ce and the macro is aliased to @cpp MAGNUM_APPLICATION_MAIN() @ce @cpp Platform::Application @ce and the macro is aliased to @cpp MAGNUM_APPLICATION_MAIN() @ce
to simplify porting. to simplify porting.
@subsection Platform-Sdl2Application-usage-posix POSIX specifics
On POSIX systems, SDL by default intercepts the `SIGTERM` signal and generates
an exit event for it, instead of doing the usual application exit. This would
mean that if the application fails to set @ref ExitEvent::setAccepted() in an
@ref exitEvent() override for some reason, pressing
@m_class{m-label m-warning} **Ctrl** @m_class{m-label m-default} **C** would
not terminate it either and you'd have to forcibly kill it instead. When using
SDL >= 2.0.4, @ref Sdl2Application turns this behavior off, making
@ref exitEvent() behave consistently with other application implementations
such as @ref GlfwApplication. You can turn this behavior back on by enabling
the [corresponding SDL hint](https://wiki.libsdl.org/SDL_HINT_NO_SIGNAL_HANDLERS)
through an environment variable:
@code{.sh}
SDL_NO_SIGNAL_HANDLERS=1 ./your-app
@endcode
See also the [SDL Wiki](https://wiki.libsdl.org/SDL_EventType#SDL_QUIT) for
details.
@subsection Platform-Sdl2Application-usage-linux Linux specifics @subsection Platform-Sdl2Application-usage-linux Linux specifics
SDL by default attempts to disable compositing, which may cause ugly flickering SDL by default attempts to disable compositing, which may cause ugly flickering
for non-fullscreen apps (KWin, among others, is known to respect this setting). for non-fullscreen apps (KWin, among others, is known to respect this setting).
When using SDL >= 2.0.8, @ref Sdl2Application turns this behavior off, keeping When using SDL >= 2.0.8, @ref Sdl2Application turns this behavior off, keeping
the compositor running to avoid the flicker. You can turn this behavior back on the compositor running to avoid the flicker. You can turn this behavior back on
by enabling the [corresponding SDL hint](https://wiki.libsdl.org/CategoryHints) through an environment variable: by enabling the [corresponding SDL hint](https://wiki.libsdl.org/CategoryHints)
through an environment variable:
@code{.sh} @code{.sh}
SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR=1 ./your-app SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR=1 ./your-app
@ -381,6 +403,7 @@ class Sdl2Application {
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
class GLConfiguration; class GLConfiguration;
#endif #endif
class ExitEvent;
class ViewportEvent; class ViewportEvent;
class InputEvent; class InputEvent;
class KeyEvent; class KeyEvent;
@ -715,6 +738,21 @@ class Sdl2Application {
#else #else
private: private:
#endif #endif
/**
* @brief Exit event
*
* If implemented, it allows the application to react to an application
* exit (for example to save its internal state) and suppress it as
* well (for example to show a exit confirmation dialog). The default
* implementation calls @ref ExitEvent::setAccepted() on @p event,
* which tells the application that it's safe to exit.
*
* SDL has special behavior on POSIX systems regarding `SIGINT` and
* `SIGTERM` handling, see @ref Platform-Sdl2Application-usage-posix
* for more information.
*/
virtual void exitEvent(ExitEvent& event);
/** /**
* @brief Tick event * @brief Tick event
* *
@ -1606,6 +1644,45 @@ class Sdl2Application::Configuration {
#endif #endif
}; };
/**
@brief Exit event
@see @ref exitEvent()
*/
class Sdl2Application::ExitEvent {
public:
/** @brief Copying is not allowed */
ExitEvent(const ExitEvent&) = delete;
/** @brief Moving is not allowed */
ExitEvent(ExitEvent&&) = delete;
/** @brief Copying is not allowed */
ExitEvent& operator=(const ExitEvent&) = delete;
/** @brief Moving is not allowed */
ExitEvent& operator=(ExitEvent&&) = delete;
/** @brief Whether the event is accepted */
bool isAccepted() const { return _accepted; }
/**
* @brief Set event as accepted
*
* If the event is ignored (i.e., not set as accepted) in
* @ref exitEvent(), the application won't exit. Default implementation
* of @ref exitEvent() accepts the event.
*/
void setAccepted(bool accepted = true) { _accepted = accepted; }
private:
friend Sdl2Application;
explicit ExitEvent(): _accepted(false) {}
bool _accepted;
};
/** /**
@brief Viewport event @brief Viewport event

6
src/Magnum/Platform/Test/GlfwApplicationTest.cpp

@ -29,6 +29,12 @@ namespace Magnum { namespace Platform { namespace Test {
struct GlfwApplicationTest: Platform::Application { struct GlfwApplicationTest: Platform::Application {
explicit GlfwApplicationTest(const Arguments& arguments): Platform::Application{arguments} {} explicit GlfwApplicationTest(const Arguments& arguments): Platform::Application{arguments} {}
void exitEvent(ExitEvent& event) override {
Debug{} << "application exiting";
event.setAccepted(); /* Comment-out to test app exit suppression */
}
void drawEvent() override {} void drawEvent() override {}
}; };

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

@ -31,6 +31,11 @@ struct Sdl2ApplicationTest: Platform::Application {
/* For testing resize events */ /* For testing resize events */
explicit Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)} {} explicit Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setWindowFlags(Configuration::WindowFlag::Resizable)} {}
void exitEvent(ExitEvent& event) override {
Debug{} << "application exiting";
event.setAccepted(); /* Comment-out to test app exit suppression */
}
void drawEvent() override {} void drawEvent() override {}
/* For testing HiDPI resize events */ /* For testing HiDPI resize events */

Loading…
Cancel
Save