Compare commits

...

4 Commits

Author SHA1 Message Date
Armin Burgmeier e39a3a40f7 editor: delete viewport widgets immediately
I don't know why the viewports are deleted with deleteLater(), but it leads
to an OpenGL context getting deselected behind our back, and so we don't know
when is a good time to re-select it. This leads to termination of the engine
when selecting File->Close (Ctrl+W) in the editor, because the graphics
re-initialization fails with no GL context active.

Instead, just delete the viewport widget immediately, which works fine at
least on Linux. This is also recommended by the Qt documentation at
http://doc.qt.io/qt-5/qopenglwidget.html.
2016-10-02 22:34:51 -07:00
Armin Burgmeier 4e7b396d31 editor: keep track of viewports properly
When removing a viewport, then also remove it from the internal list of
viewports. Otherwise, we might attempt to delete it again later. Fixes
crash on editor shutdown on Linux.
2016-10-01 14:03:15 -10:00
Armin Burgmeier 2d324c10e0 Make console destruction more deterministic
This fixes a crash on Linux when exiting the editor. It was caused by
C4Console::~C4Console being called by the C++ runtime after after main()
returned. The C4Console destructor then goes and deletes all the Qt
resources (QApplication and friends), and that caused a segfault, maybe
because some of the static Qt structures have already been deallocated.

Fix this by making the destruction of the Qt components deterministic. Add
a function "DeleteConsoleWindow" which deletes all the Qt components, and
call this function in C4Console::Clear.
2016-10-01 13:35:28 -10:00
Armin Burgmeier a56d2fecdb Change return type of CreateConsoleWindow to bool
All implementations were just returning `this` anyway, and this makes it
easier to add a corresponding cleanup function.
2016-09-30 23:01:04 -10:00
8 changed files with 106 additions and 19 deletions

View File

@ -62,7 +62,9 @@ C4Console::~C4Console()
C4Window * C4Console::Init(C4AbstractApp * pApp)
{
return C4ConsoleGUI::CreateConsoleWindow(pApp);
if (!C4ConsoleGUI::CreateConsoleWindow(pApp))
return NULL;
return this;
}
bool C4Console::In(const char *szText)
@ -366,15 +368,18 @@ void C4Console::Default()
void C4Console::Clear()
{
if (pSurface) delete pSurface;
pSurface = 0;
C4Window::Clear();
C4ConsoleGUI::DeleteConsoleWindow();
EditCursor.Clear();
ToolsDlg.Clear();
PropertyDlgClose();
ClearViewportMenu();
ClearPlayerMenu();
ClearNetMenu();
if (pSurface) delete pSurface;
pSurface = 0;
#ifndef _WIN32
Application.Quit();
#endif
@ -613,11 +618,14 @@ bool C4ConsoleGUI::ClearLog() {return 0;}
void C4ConsoleGUI::ClearNetMenu() {}
void C4ConsoleGUI::ClearPlayerMenu() {}
void C4ConsoleGUI::ClearViewportMenu() {}
C4Window * C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp * pApp)
bool C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp * pApp)
{
C4Rect r(0, 0, 400, 350);
return C4Window::Init(C4Window::W_Console, pApp, LoadResStr("IDS_CNS_CONSOLE"), &r);
if (!C4Window::Init(C4Window::W_Console, pApp, LoadResStr("IDS_CNS_CONSOLE"), &r))
return false;
return true;
}
void C4ConsoleGUI::DeleteConsoleWindow() {}
void C4ConsoleGUI::DisplayInfoText(C4ConsoleGUI::InfoTextType, StdStrBuf&) {}
void C4ConsoleGUI::DoEnableControls(bool) {}
bool C4ConsoleGUI::DoUpdateHaltCtrls(bool) {return 0;}

View File

@ -63,14 +63,18 @@ public:
void Clear() {}
};
C4Window* C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
bool C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
{
C4WindowController* controller = [C4EditorWindowController new];
setObjectiveCObject(controller);
[NSBundle loadNibNamed:@"Editor" owner:controller];
[controller setStdWindow:this];
this->Active = true;
return this;
return true;
}
void C4ConsoleGUI::DeleteConsoleWindow()
{
}
void C4ConsoleGUI::Out(const char* message)

View File

@ -122,7 +122,8 @@ public:
void ClearPlayerMenu();
void SetInputFunctions(std::list<const char*> &functions);
C4Window* CreateConsoleWindow(C4AbstractApp *application);
bool CreateConsoleWindow(C4AbstractApp *application);
void DeleteConsoleWindow();
void Out(const char* message);
bool ClearLog();
void DisplayInfoText(InfoTextType type, StdStrBuf& text);

View File

@ -82,12 +82,21 @@ void C4ConsoleGUI::SetInputFunctions(std::list<const char*> &functions)
if (Active) state->SetInputFunctions(functions);
}
C4Window* C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
bool C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
{
if (!state->CreateConsoleWindow(application)) return NULL;
if (!state->CreateConsoleWindow(application)) return false;
Active = true;
EnableControls(fGameOpen);
return this;
return true;
}
void C4ConsoleGUI::DeleteConsoleWindow()
{
if (Active)
{
Active = false;
state->DeleteConsoleWindow();
}
}
void C4ConsoleGUI::Out(const char* message)

View File

@ -734,6 +734,46 @@ bool C4ConsoleGUIState::CreateConsoleWindow(C4AbstractApp *app)
return true;
}
void C4ConsoleGUIState::DeleteConsoleWindow()
{
// Reset to a state before CreateConsoleWindow was called
action_object = C4VNull;
is_object_selection_updating = false;
editcursor_mode = C4CNS_ModePlay;
drawing_tool = C4TLS_Brush;
landscape_mode = LandscapeMode::Dynamic;
net_enabled = false;
recording = false;
enabled = false;
window_menu_separator = nullptr;
status_cursor = status_framecounter = status_timefps = nullptr;
while (!viewports.empty())
{
auto vp = viewports.front();
viewports.erase(viewports.begin());
viewport_area->removeDockWidget(vp);
delete vp;
}
client_actions.clear();
player_actions.clear();
viewport_actions.clear();
viewport_area = nullptr;
disable_shortcut_filter.reset(nullptr);
definition_list_model.reset(nullptr);
object_list_model.reset(nullptr);
property_name_delegate.reset(nullptr);
property_delegate_factory.reset(nullptr);
property_model.reset(nullptr);
window.reset(nullptr);
application.reset(nullptr);
}
void C4ConsoleGUIState::Execute(bool redraw_only)
{
// Nothing to do - Qt's event loop is handling everything.
@ -875,12 +915,26 @@ void C4ConsoleGUIState::AddViewport(C4ViewportWindow *cvp)
void C4ConsoleGUIState::RemoveViewport(C4ViewportWindow *cvp)
{
if (!viewport_area) return;
for (auto vp : viewports)
for (auto iter = viewports.begin(); iter != viewports.end(); )
{
auto vp = *iter;
if (vp->GetViewportWindow() == cvp)
{
viewport_area->removeDockWidget(vp);
vp->deleteLater();
iter = viewports.erase(iter);
// cannot use deleteLater here because Qt will then
// still select/deselect the viewport's GL context
// behind the scenes, leaving us with an unselected
// GL context.
// Documented at http://doc.qt.io/qt-5/qopenglwidget.html
// Instead, delete the viewport widget directly.
delete vp;
}
else
{
++iter;
}
}
}

View File

@ -220,6 +220,7 @@ public:
void AddToolbarSpacer(int space);
bool CreateConsoleWindow(C4AbstractApp *app);
void DeleteConsoleWindow();
void Execute(bool redraw_only=false);
void Redraw() { Execute(true); }
void UpdateActionStates();

View File

@ -524,9 +524,15 @@ void C4ConsoleQtViewportDockWidget::closeEvent(QCloseEvent * event)
QDockWidget::closeEvent(event);
if (event->isAccepted())
{
if (cvp) cvp->Close();
cvp = NULL;
deleteLater();
if (cvp)
{
// This causes "this" to be deleted:
cvp->Close();
}
else
{
deleteLater();
}
}
}

View File

@ -783,7 +783,7 @@ bool C4ConsoleGUI::UpdateModeCtrls(int iMode)
return true;
}
C4Window* C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
bool C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
{
hWindow = CreateDialog(application->GetInstance(), MAKEINTRESOURCE(IDD_CONSOLE), NULL, ConsoleDlgProc);
if (!hWindow)
@ -802,7 +802,7 @@ C4Window* C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
Log(FormatString("Error creating dialog window: %s", StdStrBuf(lpMsgBuf).getData()).getData());
// Free the buffer.
LocalFree(lpMsgBuf);
return NULL;
return false;
}
// Remember metrics
state->console_handle = hWindow;
@ -827,7 +827,11 @@ C4Window* C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
ShowCursor(true);
renderwnd = hWindow;
// Success
return this;
return true;
}
void C4ConsoleGUI::DeleteConsoleWindow()
{
}
void C4ConsoleGUI::DoEnableControls(bool fEnable)