fix segfault on disconnect (#5447)

* add new param to closeRequest

* don't emit signals in dtors

* send closeRequest

* fix build failure

* fix build failure

* see if we can get away with the overloaded triggered

* fix build failure
This commit is contained in:
RickyRister 2025-01-11 19:19:45 -08:00 committed by GitHub
parent 3b544a36a8
commit 7347ba88ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 59 additions and 67 deletions

View file

@ -50,7 +50,13 @@ public:
} }
virtual QString getTabText() const = 0; virtual QString getTabText() const = 0;
virtual void retranslateUi() = 0; virtual void retranslateUi() = 0;
virtual void closeRequest() /**
* Sends a request to close the tab.
* Signals for cleanup should be emitted from this method instead of the destructor.
*
* @param forced whether this close request was initiated by the user or forced by the server.
*/
virtual void closeRequest(bool /*forced*/ = false)
{ {
} }
virtual void tabActivated() virtual void tabActivated()

View file

@ -358,7 +358,7 @@ void TabDeckEditor::createMenus()
analyzeDeckMenu->addAction(aAnalyzeDeckTappedout); analyzeDeckMenu->addAction(aAnalyzeDeckTappedout);
aClose = new QAction(QString(), this); aClose = new QAction(QString(), this);
connect(aClose, SIGNAL(triggered()), this, SLOT(closeRequest())); connect(aClose, &QAction::triggered, this, [this] { closeRequest(); });
aClearFilterAll = new QAction(QString(), this); aClearFilterAll = new QAction(QString(), this);
aClearFilterAll->setIcon(QPixmap("theme:icons/clearsearch")); aClearFilterAll->setIcon(QPixmap("theme:icons/clearsearch"));
@ -721,11 +721,6 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
loadLayout(); loadLayout();
} }
TabDeckEditor::~TabDeckEditor()
{
emit deckEditorClosing(this);
}
void TabDeckEditor::retranslateUi() void TabDeckEditor::retranslateUi()
{ {
cardInfo->retranslateUi(); cardInfo->retranslateUi();
@ -980,10 +975,14 @@ bool TabDeckEditor::confirmClose()
return true; return true;
} }
void TabDeckEditor::closeRequest() void TabDeckEditor::closeRequest(bool forced)
{ {
if (confirmClose()) if (!forced && !confirmClose()) {
deleteLater(); return;
}
emit deckEditorClosing(this);
deleteLater();
} }
void TabDeckEditor::actNewDeck() void TabDeckEditor::actNewDeck()

View file

@ -164,7 +164,6 @@ private:
public: public:
explicit TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent = nullptr); explicit TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent = nullptr);
~TabDeckEditor() override;
void retranslateUi() override; void retranslateUi() override;
QString getTabText() const override; QString getTabText() const override;
void setDeck(DeckLoader *_deckLoader); void setDeck(DeckLoader *_deckLoader);
@ -179,7 +178,7 @@ public:
void updateCardInfo(CardInfoPtr _card); void updateCardInfo(CardInfoPtr _card);
public slots: public slots:
void closeRequest() override; void closeRequest(bool forced = false) override;
void showPrintingSelector(); void showPrintingSelector();
signals: signals:
void openDeckEditor(const DeckLoader *deckLoader); void openDeckEditor(const DeckLoader *deckLoader);

View file

@ -581,16 +581,14 @@ void TabGame::emitUserEvent()
TabGame::~TabGame() TabGame::~TabGame()
{ {
scene->clearViews();
delete replay; delete replay;
QMapIterator<int, Player *> i(players); QMapIterator<int, Player *> i(players);
while (i.hasNext()) { while (i.hasNext()) {
delete i.next().value(); delete i.next().value();
} }
players.clear();
emit gameClosing(this);
} }
void TabGame::updatePlayerListDockTitle() void TabGame::updatePlayerListDockTitle()
@ -701,9 +699,15 @@ void TabGame::retranslateUi()
scene->retranslateUi(); scene->retranslateUi();
} }
void TabGame::closeRequest() void TabGame::closeRequest(bool forced)
{ {
actLeaveGame(); if (!forced && !leaveGame()) {
return;
}
emit gameClosing(this);
deleteLater();
} }
void TabGame::replayNextEvent(Player::EventProcessingOptions options) void TabGame::replayNextEvent(Player::EventProcessingOptions options)
@ -794,19 +798,23 @@ void TabGame::actConcede()
} }
} }
void TabGame::actLeaveGame() /**
* Confirms the leave game and sends the leave game command, if applicable.
*
* @return True if the leave game is confirmed
*/
bool TabGame::leaveGame()
{ {
if (!gameClosed) { if (!gameClosed) {
if (!spectator) if (!spectator)
if (QMessageBox::question(this, tr("Leave game"), tr("Are you sure you want to leave this game?"), if (QMessageBox::question(this, tr("Leave game"), tr("Are you sure you want to leave this game?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
return; return false;
if (!replay) if (!replay)
sendGameCommand(Command_LeaveGame()); sendGameCommand(Command_LeaveGame());
} }
scene->clearViews(); return true;
deleteLater();
} }
void TabGame::actSay() void TabGame::actSay()
@ -1598,7 +1606,7 @@ void TabGame::createMenuItems()
aConcede = new QAction(this); aConcede = new QAction(this);
connect(aConcede, SIGNAL(triggered()), this, SLOT(actConcede())); connect(aConcede, SIGNAL(triggered()), this, SLOT(actConcede()));
aLeaveGame = new QAction(this); aLeaveGame = new QAction(this);
connect(aLeaveGame, SIGNAL(triggered()), this, SLOT(actLeaveGame())); connect(aLeaveGame, &QAction::triggered, this, [this] { closeRequest(); });
aFocusChat = new QAction(this); aFocusChat = new QAction(this);
connect(aFocusChat, SIGNAL(triggered()), sayEdit, SLOT(setFocus())); connect(aFocusChat, SIGNAL(triggered()), sayEdit, SLOT(setFocus()));
aCloseReplay = nullptr; aCloseReplay = nullptr;
@ -1648,7 +1656,7 @@ void TabGame::createReplayMenuItems()
aFocusChat = nullptr; aFocusChat = nullptr;
aLeaveGame = nullptr; aLeaveGame = nullptr;
aCloseReplay = new QAction(this); aCloseReplay = new QAction(this);
connect(aCloseReplay, SIGNAL(triggered()), this, SLOT(actLeaveGame())); connect(aCloseReplay, &QAction::triggered, this, [this] { closeRequest(); });
phasesMenu = nullptr; phasesMenu = nullptr;
gameMenu = new QMenu(this); gameMenu = new QMenu(this);

View file

@ -189,6 +189,7 @@ private:
void startGame(bool resuming); void startGame(bool resuming);
void stopGame(); void stopGame();
void closeGame(); void closeGame();
bool leaveGame();
void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context); void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context);
void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context); void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
@ -242,7 +243,6 @@ private slots:
void actGameInfo(); void actGameInfo();
void actConcede(); void actConcede();
void actLeaveGame();
void actRemoveLocalArrows(); void actRemoveLocalArrows();
void actRotateViewCW(); void actRotateViewCW();
void actRotateViewCCW(); void actRotateViewCCW();
@ -278,7 +278,7 @@ public:
~TabGame() override; ~TabGame() override;
void retranslateUi() override; void retranslateUi() override;
void updatePlayerListDockTitle(); void updatePlayerListDockTitle();
void closeRequest() override; void closeRequest(bool forced = false) override;
const QMap<int, Player *> &getPlayers() const const QMap<int, Player *> &getPlayers() const
{ {
return players; return players;

View file

@ -38,7 +38,7 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor,
vbox->addWidget(sayEdit); vbox->addWidget(sayEdit);
aLeave = new QAction(this); aLeave = new QAction(this);
connect(aLeave, SIGNAL(triggered()), this, SLOT(actLeave())); connect(aLeave, &QAction::triggered, this, [this] { closeRequest(); });
messageMenu = new QMenu(this); messageMenu = new QMenu(this);
messageMenu->addAction(aLeave); messageMenu->addAction(aLeave);
@ -53,7 +53,6 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor,
TabMessage::~TabMessage() TabMessage::~TabMessage()
{ {
emit talkClosing(this);
delete ownUserInfo; delete ownUserInfo;
delete otherUserInfo; delete otherUserInfo;
} }
@ -86,9 +85,10 @@ QString TabMessage::getTabText() const
return tr("%1 - Private chat").arg(QString::fromStdString(otherUserInfo->name())); return tr("%1 - Private chat").arg(QString::fromStdString(otherUserInfo->name()));
} }
void TabMessage::closeRequest() void TabMessage::closeRequest(bool /*forced*/)
{ {
actLeave(); emit talkClosing(this);
deleteLater();
} }
void TabMessage::sendMessage() void TabMessage::sendMessage()
@ -114,11 +114,6 @@ void TabMessage::messageSent(const Response &response)
"This user is ignoring you, they cannot see your messages in main chat and you cannot join their games.")); "This user is ignoring you, they cannot see your messages in main chat and you cannot join their games."));
} }
void TabMessage::actLeave()
{
deleteLater();
}
void TabMessage::processUserMessageEvent(const Event_UserMessage &event) void TabMessage::processUserMessageEvent(const Event_UserMessage &event)
{ {
auto userInfo = event.sender_name() == otherUserInfo->name() ? otherUserInfo : ownUserInfo; auto userInfo = event.sender_name() == otherUserInfo->name() ? otherUserInfo : ownUserInfo;

View file

@ -29,7 +29,6 @@ signals:
void maximizeClient(); void maximizeClient();
private slots: private slots:
void sendMessage(); void sendMessage();
void actLeave();
void messageSent(const Response &response); void messageSent(const Response &response);
void addMentionTag(QString mentionTag); void addMentionTag(QString mentionTag);
void messageClicked(); void messageClicked();
@ -41,7 +40,7 @@ public:
const ServerInfo_User &_otherUserInfo); const ServerInfo_User &_otherUserInfo);
~TabMessage() override; ~TabMessage() override;
void retranslateUi() override; void retranslateUi() override;
void closeRequest() override; void closeRequest(bool forced = false) override;
void tabActivated() override; void tabActivated() override;
QString getUserName() const; QString getUserName() const;
QString getTabText() const override; QString getTabText() const override;

View file

@ -101,7 +101,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
hbox->addWidget(userList, 1); hbox->addWidget(userList, 1);
aLeaveRoom = new QAction(this); aLeaveRoom = new QAction(this);
connect(aLeaveRoom, SIGNAL(triggered()), this, SLOT(actLeaveRoom())); connect(aLeaveRoom, &QAction::triggered, this, [this] { closeRequest(); });
roomMenu = new QMenu(this); roomMenu = new QMenu(this);
roomMenu->addAction(aLeaveRoom); roomMenu->addAction(aLeaveRoom);
@ -135,11 +135,6 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
setCentralWidget(mainWidget); setCentralWidget(mainWidget);
} }
TabRoom::~TabRoom()
{
emit roomClosing(this);
}
void TabRoom::retranslateUi() void TabRoom::retranslateUi()
{ {
gameSelector->retranslateUi(); gameSelector->retranslateUi();
@ -175,9 +170,11 @@ void TabRoom::actShowPopup(const QString &message)
} }
} }
void TabRoom::closeRequest() void TabRoom::closeRequest(bool /*forced*/)
{ {
actLeaveRoom(); sendRoomCommand(prepareRoomCommand(Command_LeaveRoom()));
emit roomClosing(this);
deleteLater();
} }
void TabRoom::tabActivated() void TabRoom::tabActivated()
@ -216,12 +213,6 @@ void TabRoom::sayFinished(const Response &response)
chatView->appendMessage(tr("You are flooding the chat. Please wait a couple of seconds.")); chatView->appendMessage(tr("You are flooding the chat. Please wait a couple of seconds."));
} }
void TabRoom::actLeaveRoom()
{
sendRoomCommand(prepareRoomCommand(Command_LeaveRoom()));
deleteLater();
}
void TabRoom::actClearChat() void TabRoom::actClearChat()
{ {
chatView->clearChat(); chatView->clearChat();

View file

@ -70,7 +70,6 @@ signals:
private slots: private slots:
void sendMessage(); void sendMessage();
void sayFinished(const Response &response); void sayFinished(const Response &response);
void actLeaveRoom();
void actClearChat(); void actClearChat();
void actOpenChatSettings(); void actOpenChatSettings();
void addMentionTag(QString mentionTag); void addMentionTag(QString mentionTag);
@ -91,9 +90,8 @@ public:
AbstractClient *_client, AbstractClient *_client,
ServerInfo_User *_ownUser, ServerInfo_User *_ownUser,
const ServerInfo_Room &info); const ServerInfo_Room &info);
~TabRoom() override;
void retranslateUi() override; void retranslateUi() override;
void closeRequest() override; void closeRequest(bool forced = false) override;
void tabActivated() override; void tabActivated() override;
void processRoomEvent(const RoomEvent &event); void processRoomEvent(const RoomEvent &event);
int getRoomId() const int getRoomId() const

View file

@ -304,30 +304,27 @@ void TabSupervisor::stop()
tabAdmin = 0; tabAdmin = 0;
tabLog = 0; tabLog = 0;
for (const auto tab : deckEditorTabs) { QList<Tab *> tabsToDelete;
disconnect(tab, nullptr, this, nullptr);
tab->deleteLater();
}
deckEditorTabs.clear();
for (auto i = roomTabs.cbegin(), end = roomTabs.cend(); i != end; ++i) { for (auto i = roomTabs.cbegin(), end = roomTabs.cend(); i != end; ++i) {
disconnect(i.value(), nullptr, this, nullptr); tabsToDelete << i.value();
i.value()->deleteLater();
} }
roomTabs.clear(); roomTabs.clear();
for (auto i = gameTabs.cbegin(), end = gameTabs.cend(); i != end; ++i) { for (auto i = gameTabs.cbegin(), end = gameTabs.cend(); i != end; ++i) {
disconnect(i.value(), nullptr, this, nullptr); tabsToDelete << i.value();
i.value()->deleteLater();
} }
gameTabs.clear(); gameTabs.clear();
for (const auto tab : replayTabs) { for (const auto tab : replayTabs) {
disconnect(tab, nullptr, this, nullptr); tabsToDelete << tab;
tab->deleteLater();
} }
replayTabs.clear(); replayTabs.clear();
for (const auto tab : tabsToDelete) {
tab->closeRequest(true);
}
delete userInfo; delete userInfo;
userInfo = 0; userInfo = 0;
} }

View file

@ -58,7 +58,7 @@ TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor, Abstra
retranslateUi(); retranslateUi();
} }
void TabDeckStorageVisual::closeRequest() void TabDeckStorageVisual::closeRequest(bool /*forced*/)
{ {
this->close(); this->close();
} }

View file

@ -33,7 +33,7 @@ public:
} }
public slots: public slots:
void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus); void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus);
void closeRequest() override; void closeRequest(bool forced = false) override;
void actOpenLocalDeck(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); void actOpenLocalDeck(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
void actDeleteLocalDeck(); void actDeleteLocalDeck();
signals: signals: