Browse Source

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

SDL has to be special and behave weirdly, of course, as always.
simd
Vladimír Vondruš 7 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::GlfwApplication::ViewportEvent::framebufferSize() and
@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
@ref platforms-android-apps-manifest-screen-compatibility-mode "the necessary steps"
in the docs
- Implemented handling of @ref Platform::AndroidApplication::viewportEvent()
and documenting how to get Android to call it instead of relaunching the
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

9
src/Magnum/Platform/GlfwApplication.cpp

@ -454,6 +454,11 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf
void GlfwApplication::setupCallbacks() {
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){
/* Properly redraw after the window is restored from minimized state */
static_cast<GlfwApplication*>(glfwGetWindowUserPointer(window))->drawEvent();
@ -577,6 +582,10 @@ auto GlfwApplication::MouseScrollEvent::modifiers() -> Modifiers {
return *_modifiers;
}
void GlfwApplication::exitEvent(ExitEvent& event) {
event.setAccepted();
}
void GlfwApplication::viewportEvent(ViewportEvent& event) {
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH

51
src/Magnum/Platform/GlfwApplication.h

@ -149,6 +149,7 @@ class GlfwApplication {
#ifdef MAGNUM_TARGET_GL
class GLConfiguration;
#endif
class ExitEvent;
class ViewportEvent;
class InputEvent;
class KeyEvent;
@ -426,6 +427,17 @@ class GlfwApplication {
#else
private:
#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
*
@ -1130,6 +1142,45 @@ class GlfwApplication::Configuration {
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

29
src/Magnum/Platform/Sdl2Application.cpp

@ -91,6 +91,12 @@ Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT):
args.parse(arguments.argc, arguments.argv);
#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
flickering on KWin as the compositor gets shut down on every startup */
#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
@ -713,13 +719,18 @@ void Sdl2Application::mainLoopIteration() {
textEditingEvent(e);
} break;
case SDL_QUIT:
#ifndef CORRADE_TARGET_EMSCRIPTEN
_flags |= Flag::Exit;
#else
emscripten_cancel_main_loop();
#endif
return;
case SDL_QUIT: {
ExitEvent e;
exitEvent(e);
if(e.isAccepted()) {
#ifndef CORRADE_TARGET_EMSCRIPTEN
_flags |= Flag::Exit;
#else
emscripten_cancel_main_loop();
#endif
return;
}
} break;
}
}
@ -795,6 +806,10 @@ void Sdl2Application::setTextInputRect(const Range2Di& rect) {
SDL_SetTextInputRect(&r);
}
void Sdl2Application::exitEvent(ExitEvent& event) {
event.setAccepted();
}
void Sdl2Application::tickEvent() {
/* If this got called, the tick event is not implemented by user and thus
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
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
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).
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
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}
SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR=1 ./your-app
@ -381,6 +403,7 @@ class Sdl2Application {
#ifdef MAGNUM_TARGET_GL
class GLConfiguration;
#endif
class ExitEvent;
class ViewportEvent;
class InputEvent;
class KeyEvent;
@ -715,6 +738,21 @@ class Sdl2Application {
#else
private:
#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
*
@ -1606,6 +1644,45 @@ class Sdl2Application::Configuration {
#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

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

@ -29,6 +29,12 @@ namespace Magnum { namespace Platform { namespace Test {
struct GlfwApplicationTest: Platform::Application {
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 {}
};

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

@ -31,6 +31,11 @@ struct Sdl2ApplicationTest: Platform::Application {
/* For testing resize events */
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 {}
/* For testing HiDPI resize events */

Loading…
Cancel
Save