Skip to content

Commit

Permalink
Close all docs correctly even in case of exception (fix aseprite#3162)
Browse files Browse the repository at this point in the history
  • Loading branch information
dacap committed Apr 6, 2022
1 parent 2a908f7 commit 3ed969f
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 56 deletions.
95 changes: 60 additions & 35 deletions src/app/app.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
Expand Down Expand Up @@ -161,7 +161,8 @@ class App::Modules {
}

~Modules() {
ASSERT(m_recovery == nullptr);
ASSERT(m_recovery == nullptr ||
ui::get_app_state() == ui::AppState::kClosingWithException);
}

app::crash::DataRecovery* recovery() {
Expand Down Expand Up @@ -368,14 +369,62 @@ int App::initialize(const AppOptions& options)
return code;
}

namespace {

#ifdef ENABLE_UI
struct CloseMainWindow {
std::unique_ptr<MainWindow>& m_win;
CloseMainWindow(std::unique_ptr<MainWindow>& win) : m_win(win) { }
~CloseMainWindow() { m_win.reset(nullptr); }
};
#endif

struct CloseAllDocs {
CloseAllDocs() { }
~CloseAllDocs() {
auto ctx = UIContext::instance();

std::vector<Doc*> docs;
#ifdef ENABLE_UI
for (Doc* doc : ctx->getAndRemoveAllClosedDocs())
docs.push_back(doc);
#endif
for (Doc* doc : ctx->documents())
docs.push_back(doc);
for (Doc* doc : docs) {
// First we close the document. In this way we receive recent
// notifications related to the document as a app::Doc. If
// we delete the document directly, we destroy the app::Doc
// too early, and then doc::~Document() call
// DocsObserver::onRemoveDocument(). In this way, observers
// could think that they have a fully created app::Doc when
// in reality it's a doc::Document (in the middle of a
// destruction process).
//
// TODO: This problem is because we're extending doc::Document,
// in the future, we should remove app::Doc.
doc->close();
delete doc;
}
}
};

} // anonymous namespace

void App::run()
{
#ifdef ENABLE_UI
CloseMainWindow closeMainWindow(m_mainWindow);
#endif
CloseAllDocs closeAllDocsAtExit;

#ifdef ENABLE_UI
// Run the GUI
if (isGui()) {
auto manager = ui::Manager::getDefault();
#if LAF_WINDOWS
// How to interpret one finger on Windows tablets.
ui::Manager::getDefault()->display()
manager->display()
->setInterpretOneFingerGestureAsMouseMovement(
preferences().experimental.oneFingerAsMouseMovement());
#endif
Expand Down Expand Up @@ -442,7 +491,14 @@ void App::run()
#endif

// Run the GUI main message loop
ui::Manager::getDefault()->run();
try {
manager->run();
set_app_state(AppState::kClosing);
}
catch (...) {
set_app_state(AppState::kClosingWithException);
throw;
}
}
#endif // ENABLE_UI

Expand Down Expand Up @@ -472,37 +528,6 @@ void App::run()
m_modules->deleteDataRecovery();
}
#endif

// Destroy all documents from the UIContext.
std::vector<Doc*> docs;
#ifdef ENABLE_UI
for (Doc* doc : static_cast<UIContext*>(context())->getAndRemoveAllClosedDocs())
docs.push_back(doc);
#endif
for (Doc* doc : context()->documents())
docs.push_back(doc);
for (Doc* doc : docs) {
// First we close the document. In this way we receive recent
// notifications related to the document as a app::Doc. If
// we delete the document directly, we destroy the app::Doc
// too early, and then doc::~Document() call
// DocsObserver::onRemoveDocument(). In this way, observers
// could think that they have a fully created app::Doc when
// in reality it's a doc::Document (in the middle of a
// destruction process).
//
// TODO: This problem is because we're extending doc::Document,
// in the future, we should remove app::Doc.
doc->close();
delete doc;
}

#ifdef ENABLE_UI
if (isGui()) {
// Destroy the window.
m_mainWindow.reset(nullptr);
}
#endif
}

// Finishes the Aseprite application.
Expand Down
5 changes: 3 additions & 2 deletions src/app/script/events_class.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2021-2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
Expand All @@ -20,6 +20,7 @@
#include "app/script/luacpp.h"
#include "doc/document.h"
#include "doc/sprite.h"
#include "ui/app_state.h"

#include <cstring>
#include <map>
Expand Down Expand Up @@ -201,7 +202,7 @@ class SpriteEvents : public Events

~SpriteEvents() {
auto doc = this->doc();
ASSERT(doc);
ASSERT(doc || ui::get_app_state() == ui::AppState::kClosingWithException);
if (doc) {
disconnectFromUndoHistory(doc);
doc->remove_observer(this);
Expand Down
2 changes: 2 additions & 0 deletions src/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Aseprite UI Library
# Copyright (C) 2022 Igara Studio S.A.
# Copyright (C) 2001-2018 David Capello

if(WIN32)
Expand All @@ -8,6 +9,7 @@ endif()
add_library(ui-lib
accelerator.cpp
alert.cpp
app_state.cpp
box.cpp
button.cpp
combobox.cpp
Expand Down
40 changes: 40 additions & 0 deletions src/ui/app_state.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Aseprite UI Library
// Copyright (C) 2022 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "ui/app_state.h"

#include "ui/manager.h"

namespace ui {

AppState g_state = AppState::kNormal;

void set_app_state(AppState state)
{
g_state = state;

if (state == AppState::kClosingWithException) {
if (auto man = ui::Manager::getDefault())
man->_closingAppWithException();
}
}

AppState get_app_state()
{
return g_state;
}

bool is_app_state_closing()
{
return (g_state == AppState::kClosing ||
g_state == AppState::kClosingWithException);
}

} // namespace ui
25 changes: 25 additions & 0 deletions src/ui/app_state.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Aseprite UI Library
// Copyright (C) 2022 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

#ifndef UI_APP_STATE_H_INCLUDED
#define UI_APP_STATE_H_INCLUDED
#pragma once

namespace ui {

enum AppState {
kNormal,
kClosing,
kClosingWithException,
};

void set_app_state(AppState state);
AppState get_app_state();
bool is_app_state_closing();

} // namespace ui

#endif
33 changes: 20 additions & 13 deletions src/ui/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,20 +216,20 @@ Manager::~Manager()
// No more cursor
set_mouse_cursor(kNoCursor);

// Destroy timers
ASSERT(!Timer::haveTimers());

// Destroy filters
// Check timers & filters
#ifdef _DEBUG
for (Filters& msg_filter : msg_filters)
ASSERT(msg_filter.empty());
if (get_app_state() != AppState::kClosingWithException) {
ASSERT(!Timer::haveTimers());
for (Filters& msg_filter : msg_filters)
ASSERT(msg_filter.empty());
}
ASSERT(msg_queue.empty());
#endif

// No more default manager
m_defaultManager = nullptr;

// Shutdown system
ASSERT(msg_queue.empty());
mouse_widgets_list.clear();
}
}
Expand Down Expand Up @@ -1089,6 +1089,11 @@ void Manager::dirtyRect(const gfx::Rect& bounds)
m_dirtyRegion.createUnion(m_dirtyRegion, gfx::Region(bounds));
}

void Manager::_closingAppWithException()
{
redrawState = RedrawState::ClosingApp;
}

// Configures the window for begin the loop
void Manager::_openWindow(Window* window)
{
Expand Down Expand Up @@ -1188,12 +1193,14 @@ void Manager::_closeWindow(Window* window, bool redraw_background)
// recently closed window).
updateMouseWidgets(ui::get_mouse_position());

if (children().empty()) {
// All windows were closed...
redrawState = RedrawState::ClosingApp;
}
else {
redrawState = RedrawState::AWindowHasJustBeenClosed;
if (redrawState != RedrawState::ClosingApp) {
if (children().empty()) {
// All windows were closed...
redrawState = RedrawState::ClosingApp;
}
else {
redrawState = RedrawState::AWindowHasJustBeenClosed;
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/ui/manager.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
Expand Down Expand Up @@ -105,6 +105,7 @@ namespace ui {
void _openWindow(Window* window);
void _closeWindow(Window* window, bool redraw_background);
void _updateMouseWidgets();
void _closingAppWithException();

protected:
bool onProcessMessage(Message* msg) override;
Expand Down
3 changes: 2 additions & 1 deletion src/ui/ui.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
Expand All @@ -11,6 +11,7 @@

#include "ui/accelerator.h"
#include "ui/alert.h"
#include "ui/app_state.h"
#include "ui/base.h"
#include "ui/box.h"
#include "ui/button.h"
Expand Down
7 changes: 6 additions & 1 deletion src/ui/widget.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
Expand All @@ -20,6 +20,7 @@
#include "os/surface.h"
#include "os/system.h"
#include "os/window.h"
#include "ui/app_state.h"
#include "ui/init_theme_event.h"
#include "ui/intern.h"
#include "ui/layout_io.h"
Expand Down Expand Up @@ -687,6 +688,10 @@ Rect Widget::clientChildrenBounds() const

void Widget::setBounds(const Rect& rc)
{
// Don't generate onResize() events if the app is being closed
if (is_app_state_closing())
return;

ResizeEvent ev(this, rc);
onResize(ev);
}
Expand Down
8 changes: 5 additions & 3 deletions src/ui/window.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
Expand Down Expand Up @@ -130,7 +130,8 @@ Window::Window(Type type, const std::string& text)

Window::~Window()
{
manager()->_closeWindow(this, isVisible());
if (auto man = manager())
man->_closeWindow(this, isVisible());
}

void Window::setAutoRemap(bool state)
Expand Down Expand Up @@ -333,7 +334,8 @@ void Window::closeWindow(Widget* closer)

m_closer = closer;

manager()->_closeWindow(this, true);
if (auto man = manager())
man->_closeWindow(this, true);

onClose(ev);
}
Expand Down

0 comments on commit 3ed969f

Please sign in to comment.