1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-17 01:32:21 +02:00

Replaced SDL user events list with dispatching of arbitrary functors

This commit is contained in:
Ivan Savenko
2023-06-26 21:51:10 +03:00
parent 7fc66c2797
commit 0f8d53e978
15 changed files with 176 additions and 177 deletions

View File

@ -451,8 +451,16 @@ static void mainLoop()
{ {
SettingsListener resChanged = settings.listen["video"]["resolution"]; SettingsListener resChanged = settings.listen["video"]["resolution"];
SettingsListener fsChanged = settings.listen["video"]["fullscreen"]; SettingsListener fsChanged = settings.listen["video"]["fullscreen"];
resChanged([](const JsonNode &newState){ GH.pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
fsChanged([](const JsonNode &newState){ GH.pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); }); auto functor = [](const JsonNode &newState){
GH.dispatchMainThread([](){
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
GH.onScreenResize();
});
};
resChanged(functor);
fsChanged(functor);
inGuiThread.reset(new bool(true)); inGuiThread.reset(new bool(true));

View File

@ -29,7 +29,6 @@ set(client_SRCS
eventsSDL/NotificationHandler.cpp eventsSDL/NotificationHandler.cpp
eventsSDL/InputHandler.cpp eventsSDL/InputHandler.cpp
eventsSDL/UserEventHandler.cpp
eventsSDL/InputSourceKeyboard.cpp eventsSDL/InputSourceKeyboard.cpp
eventsSDL/InputSourceMouse.cpp eventsSDL/InputSourceMouse.cpp
eventsSDL/InputSourceText.cpp eventsSDL/InputSourceText.cpp
@ -173,7 +172,6 @@ set(client_HEADERS
eventsSDL/NotificationHandler.h eventsSDL/NotificationHandler.h
eventsSDL/InputHandler.h eventsSDL/InputHandler.h
eventsSDL/UserEventHandler.h
eventsSDL/InputSourceKeyboard.h eventsSDL/InputSourceKeyboard.h
eventsSDL/InputSourceMouse.h eventsSDL/InputSourceMouse.h
eventsSDL/InputSourceText.h eventsSDL/InputSourceText.h

View File

@ -23,6 +23,7 @@
#include "../CCallback.h" #include "../CCallback.h"
#include "windows/CCastleInterface.h" #include "windows/CCastleInterface.h"
#include "eventsSDL/InputHandler.h" #include "eventsSDL/InputHandler.h"
#include "mainmenu/CMainMenu.h"
#include "gui/CursorHandler.h" #include "gui/CursorHandler.h"
#include "windows/CKingdomInterface.h" #include "windows/CKingdomInterface.h"
#include "CGameInfo.h" #include "CGameInfo.h"
@ -1744,7 +1745,16 @@ void CPlayerInterface::requestReturningToMainMenu(bool won)
if(won && cb->getStartInfo()->campState) if(won && cb->getStartInfo()->campState)
CSH->startCampaignScenario(cb->getStartInfo()->campState); CSH->startCampaignScenario(cb->getStartInfo()->campState);
else else
GH.pushUserEvent(EUserEvent::RETURN_TO_MAIN_MENU); {
GH.dispatchMainThread(
[]()
{
CSH->endGameplay();
GH.defActionsDef = 63;
CMM->menu->switchToTab("main");
}
);
}
} }
void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
@ -1840,7 +1850,21 @@ void CPlayerInterface::waitForAllDialogs(bool unlockPim)
void CPlayerInterface::proposeLoadingGame() void CPlayerInterface::proposeLoadingGame()
{ {
showYesNoDialog(CGI->generaltexth->allTexts[68], [](){ GH.pushUserEvent(EUserEvent::RETURN_TO_MENU_LOAD); }, nullptr); showYesNoDialog(
CGI->generaltexth->allTexts[68],
[]()
{
GH.dispatchMainThread(
[]()
{
CSH->endGameplay();
GH.defActionsDef = 63;
CMM->menu->switchToTab("load");
}
);
},
nullptr
);
} }
bool CPlayerInterface::capturedAllEvents() bool CPlayerInterface::capturedAllEvents()

View File

@ -21,6 +21,7 @@
#include "windows/InfoWindows.h" #include "windows/InfoWindows.h"
#include "mainmenu/CMainMenu.h" #include "mainmenu/CMainMenu.h"
#include "mainmenu/CPrologEpilogVideo.h"
#ifdef VCMI_ANDROID #ifdef VCMI_ANDROID
#include "../lib/CAndroidVMHelper.h" #include "../lib/CAndroidVMHelper.h"
@ -662,10 +663,36 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs) void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs)
{ {
if(cs) std::shared_ptr<CampaignState> ourCampaign = cs;
GH.pushUserEvent(EUserEvent::CAMPAIGN_START_SCENARIO, CMemorySerializer::deepCopy(*cs.get()).release());
else if (!cs)
GH.pushUserEvent(EUserEvent::CAMPAIGN_START_SCENARIO, CMemorySerializer::deepCopy(*si->campState.get()).release()); ourCampaign = si->campState;
GH.dispatchMainThread([ourCampaign]()
{
CSH->campaignServerRestartLock.set(true);
CSH->endGameplay();
auto & epilogue = ourCampaign->scenario(*ourCampaign->lastScenario()).epilog;
auto finisher = [=]()
{
if(!ourCampaign->isCampaignFinished())
{
GH.windows().pushWindow(CMM);
GH.windows().pushWindow(CMM->menu);
CMM->openCampaignLobby(ourCampaign);
}
};
if(epilogue.hasPrologEpilog)
{
GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher);
}
else
{
CSH->campaignServerRestartLock.waitUntil(false);
finisher();
}
});
} }
void CServerHandler::showServerError(std::string txt) void CServerHandler::showServerError(std::string txt)
@ -842,7 +869,13 @@ void CServerHandler::threadHandleConnection()
if(client) if(client)
{ {
state = EClientState::DISCONNECTING; state = EClientState::DISCONNECTING;
GH.pushUserEvent(EUserEvent::RETURN_TO_MAIN_MENU);
GH.dispatchMainThread([]()
{
CSH->endGameplay();
GH.defActionsDef = 63;
CMM->menu->switchToTab("main");
});
} }
else else
{ {

View File

@ -13,6 +13,7 @@
#include "../CGameInfo.h" #include "../CGameInfo.h"
#include "../CPlayerInterface.h" #include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../PlayerLocalState.h" #include "../PlayerLocalState.h"
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../gui/Shortcut.h" #include "../gui/Shortcut.h"
@ -293,8 +294,19 @@ void AdventureMapShortcuts::viewPuzzleMap()
void AdventureMapShortcuts::restartGame() void AdventureMapShortcuts::restartGame()
{ {
LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"), LOCPLINT->showYesNoDialog(
[](){ GH.pushUserEvent(EUserEvent::RESTART_GAME); }, nullptr); CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"),
[]()
{
GH.dispatchMainThread(
[]()
{
CSH->sendRestartGame();
}
);
},
nullptr
);
} }
void AdventureMapShortcuts::visitObject() void AdventureMapShortcuts::visitObject()

View File

@ -16,7 +16,6 @@
#include "InputSourceKeyboard.h" #include "InputSourceKeyboard.h"
#include "InputSourceTouch.h" #include "InputSourceTouch.h"
#include "InputSourceText.h" #include "InputSourceText.h"
#include "UserEventHandler.h"
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../gui/CursorHandler.h" #include "../gui/CursorHandler.h"
@ -35,7 +34,6 @@ InputHandler::InputHandler()
, keyboardHandler(std::make_unique<InputSourceKeyboard>()) , keyboardHandler(std::make_unique<InputSourceKeyboard>())
, fingerHandler(std::make_unique<InputSourceTouch>()) , fingerHandler(std::make_unique<InputSourceTouch>())
, textHandler(std::make_unique<InputSourceText>()) , textHandler(std::make_unique<InputSourceText>())
, userHandler(std::make_unique<UserEventHandler>())
{ {
} }
@ -127,7 +125,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
} }
else if(ev.type == SDL_USEREVENT) else if(ev.type == SDL_USEREVENT)
{ {
userHandler->handleUserEvent(ev.user); handleUserEvent(ev.user);
return; return;
} }
@ -244,15 +242,25 @@ bool InputHandler::hasTouchInputDevice() const
return fingerHandler->hasTouchInputDevice(); return fingerHandler->hasTouchInputDevice();
} }
void InputHandler::pushUserEvent(EUserEvent usercode, void * userdata) void InputHandler::dispatchMainThread(const std::function<void()> & functor)
{ {
auto heapFunctor = new std::function<void()>(functor);
SDL_Event event; SDL_Event event;
event.type = SDL_USEREVENT; event.type = SDL_USEREVENT;
event.user.code = static_cast<int32_t>(usercode); event.user.code = 0;
event.user.data1 = userdata; event.user.data1 = static_cast <void*>(heapFunctor);
event.user.data2 = nullptr;
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
void InputHandler::handleUserEvent(const SDL_UserEvent & current)
{
auto heapFunctor = static_cast<std::function<void()>*>(current.data1);
(*heapFunctor)();
}
const Point & InputHandler::getCursorPosition() const const Point & InputHandler::getCursorPosition() const
{ {
return cursorPosition; return cursorPosition;

View File

@ -15,12 +15,12 @@
enum class EUserEvent; enum class EUserEvent;
enum class MouseButton; enum class MouseButton;
union SDL_Event; union SDL_Event;
struct SDL_UserEvent;
class InputSourceMouse; class InputSourceMouse;
class InputSourceKeyboard; class InputSourceKeyboard;
class InputSourceTouch; class InputSourceTouch;
class InputSourceText; class InputSourceText;
class UserEventHandler;
class InputHandler class InputHandler
{ {
@ -31,12 +31,12 @@ class InputHandler
void preprocessEvent(const SDL_Event & event); void preprocessEvent(const SDL_Event & event);
void handleCurrentEvent(const SDL_Event & current); void handleCurrentEvent(const SDL_Event & current);
void handleUserEvent(const SDL_UserEvent & current);
std::unique_ptr<InputSourceMouse> mouseHandler; std::unique_ptr<InputSourceMouse> mouseHandler;
std::unique_ptr<InputSourceKeyboard> keyboardHandler; std::unique_ptr<InputSourceKeyboard> keyboardHandler;
std::unique_ptr<InputSourceTouch> fingerHandler; std::unique_ptr<InputSourceTouch> fingerHandler;
std::unique_ptr<InputSourceText> textHandler; std::unique_ptr<InputSourceText> textHandler;
std::unique_ptr<UserEventHandler> userHandler;
public: public:
InputHandler(); InputHandler();
@ -66,8 +66,8 @@ public:
/// returns true if system has active touchscreen /// returns true if system has active touchscreen
bool hasTouchInputDevice() const; bool hasTouchInputDevice() const;
/// Generates new user event that will be processed on next frame /// Calls provided functor in main thread on next execution frame
void pushUserEvent(EUserEvent usercode, void * userdata); void dispatchMainThread(const std::function<void()> & functor);
/// Returns current position of cursor, in VCMI logical screen coordinates /// Returns current position of cursor, in VCMI logical screen coordinates
const Point & getCursorPosition() const; const Point & getCursorPosition() const;

View File

@ -1,93 +0,0 @@
/*
* EventHandlerSDLUser.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "UserEventHandler.h"
#include "../CMT.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/WindowHandler.h"
#include "../mainmenu/CMainMenu.h"
#include "../mainmenu/CPrologEpilogVideo.h"
#include "../gui/EventDispatcher.h"
#include "../../lib/campaign/CampaignState.h"
#include <SDL_events.h>
void UserEventHandler::handleUserEvent(const SDL_UserEvent & user)
{
switch(static_cast<EUserEvent>(user.code))
{
case EUserEvent::FORCE_QUIT:
{
handleQuit(false);
return;
}
break;
case EUserEvent::RETURN_TO_MAIN_MENU:
{
CSH->endGameplay();
GH.defActionsDef = 63;
CMM->menu->switchToTab("main");
}
break;
case EUserEvent::RESTART_GAME:
{
CSH->sendRestartGame();
}
break;
case EUserEvent::CAMPAIGN_START_SCENARIO:
{
CSH->campaignServerRestartLock.set(true);
CSH->endGameplay();
auto ourCampaign = std::shared_ptr<CampaignState>(reinterpret_cast<CampaignState *>(user.data1));
auto & epilogue = ourCampaign->scenario(*ourCampaign->lastScenario()).epilog;
auto finisher = [=]()
{
if(!ourCampaign->isCampaignFinished())
{
GH.windows().pushWindow(CMM);
GH.windows().pushWindow(CMM->menu);
CMM->openCampaignLobby(ourCampaign);
}
};
if(epilogue.hasPrologEpilog)
{
GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher);
}
else
{
CSH->campaignServerRestartLock.waitUntil(false);
finisher();
}
}
break;
case EUserEvent::RETURN_TO_MENU_LOAD:
CSH->endGameplay();
GH.defActionsDef = 63;
CMM->menu->switchToTab("load");
break;
case EUserEvent::FULLSCREEN_TOGGLED:
{
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
GH.onScreenResize();
break;
}
case EUserEvent::FAKE_MOUSE_MOVE:
GH.events().dispatchMouseMoved(Point(0, 0), GH.getCursorPosition());
break;
default:
logGlobal->error("Unknown user event. Code %d", user.code);
break;
}
}

View File

@ -1,20 +0,0 @@
/*
* EventHandlerSDLUser.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
struct SDL_UserEvent;
/// Class for handling events of type SDL_UserEvent
class UserEventHandler
{
public:
void handleUserEvent(const SDL_UserEvent & current);
};

View File

@ -87,7 +87,9 @@ void CGuiHandler::handleEvents()
void CGuiHandler::fakeMouseMove() void CGuiHandler::fakeMouseMove()
{ {
pushUserEvent(EUserEvent::FAKE_MOUSE_MOVE); dispatchMainThread([](){
GH.events().dispatchMouseMoved(Point(0, 0), GH.getCursorPosition());
});
} }
void CGuiHandler::startTextInput(const Rect & whereInput) void CGuiHandler::startTextInput(const Rect & whereInput)
@ -203,14 +205,9 @@ bool CGuiHandler::amIGuiThread()
return inGuiThread.get() && *inGuiThread; return inGuiThread.get() && *inGuiThread;
} }
void CGuiHandler::pushUserEvent(EUserEvent usercode) void CGuiHandler::dispatchMainThread(const std::function<void()> & functor)
{ {
inputHandlerInstance->pushUserEvent(usercode, nullptr); inputHandlerInstance->dispatchMainThread(functor);
}
void CGuiHandler::pushUserEvent(EUserEvent usercode, void * userdata)
{
inputHandlerInstance->pushUserEvent(usercode, userdata);
} }
IScreenHandler & CGuiHandler::screenHandler() IScreenHandler & CGuiHandler::screenHandler()

View File

@ -27,18 +27,6 @@ class WindowHandler;
class EventDispatcher; class EventDispatcher;
class InputHandler; class InputHandler;
// TODO: event handling need refactoring. Perhaps convert into delayed function call?
enum class EUserEvent
{
RETURN_TO_MAIN_MENU,
RESTART_GAME,
RETURN_TO_MENU_LOAD,
FULLSCREEN_TOGGLED,
CAMPAIGN_START_SCENARIO,
FORCE_QUIT,
FAKE_MOUSE_MOVE,
};
// Handles GUI logic and drawing // Handles GUI logic and drawing
class CGuiHandler class CGuiHandler
{ {
@ -108,8 +96,9 @@ public:
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen void drawFPSCounter(); // draws the FPS to the upper left corner of the screen
bool amIGuiThread(); bool amIGuiThread();
void pushUserEvent(EUserEvent usercode);
void pushUserEvent(EUserEvent usercode, void * userdata); /// Calls provided functor in main thread on next execution frame
void dispatchMainThread(const std::function<void()> & functor);
CondSh<bool> * terminate_cond; // confirm termination CondSh<bool> * terminate_cond; // confirm termination
}; };

View File

@ -427,10 +427,19 @@ void CBonusSelection::startMap()
void CBonusSelection::restartMap() void CBonusSelection::restartMap()
{ {
close(); close();
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [=]() LOCPLINT->showYesNoDialog(
{ CGI->generaltexth->allTexts[67],
GH.pushUserEvent(EUserEvent::RESTART_GAME); [=]()
}, 0); {
GH.dispatchMainThread(
[]()
{
CSH->sendRestartGame();
}
);
},
0
);
} }
void CBonusSelection::increaseDifficulty() void CBonusSelection::increaseDifficulty()

View File

@ -68,7 +68,10 @@ ISelectionScreenInfo * SEL;
static void do_quit() static void do_quit()
{ {
GH.pushUserEvent(EUserEvent::FORCE_QUIT); GH.dispatchMainThread([]()
{
handleQuit(false);
});
} }
CMenuScreen::CMenuScreen(const JsonNode & configNode) CMenuScreen::CMenuScreen(const JsonNode & configNode)
@ -562,10 +565,13 @@ void CSimpleJoinScreen::connectThread(const std::string & addr, ui16 port)
else else
CSH->justConnectToServer(addr, port); CSH->justConnectToServer(addr, port);
if(GH.windows().isTopWindow(this)) // async call to prevent thread race
{ GH.dispatchMainThread([this](){
close(); if(GH.windows().isTopWindow(this))
} {
close();
}
});
} }
CLoadingScreen::CLoadingScreen(std::function<void()> loader) CLoadingScreen::CLoadingScreen(std::function<void()> loader)

View File

@ -16,6 +16,7 @@
#include "GeneralOptionsTab.h" #include "GeneralOptionsTab.h"
#include "OtherOptionsTab.h" #include "OtherOptionsTab.h"
#include "CMT.h"
#include "CGameInfo.h" #include "CGameInfo.h"
#include "CGeneralTextHandler.h" #include "CGeneralTextHandler.h"
#include "CPlayerInterface.h" #include "CPlayerInterface.h"
@ -112,7 +113,18 @@ void SettingsMainWindow::close()
void SettingsMainWindow::quitGameButtonCallback() void SettingsMainWindow::quitGameButtonCallback()
{ {
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(EUserEvent::FORCE_QUIT); }, 0); LOCPLINT->showYesNoDialog(
CGI->generaltexth->allTexts[578],
[this]()
{
close();
GH.dispatchMainThread( []()
{
handleQuit(false);
});
},
0
);
} }
void SettingsMainWindow::backButtonCallback() void SettingsMainWindow::backButtonCallback()
@ -122,7 +134,20 @@ void SettingsMainWindow::backButtonCallback()
void SettingsMainWindow::mainMenuButtonCallback() void SettingsMainWindow::mainMenuButtonCallback()
{ {
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(EUserEvent::RETURN_TO_MAIN_MENU); }, 0); LOCPLINT->showYesNoDialog(
CGI->generaltexth->allTexts[578],
[this]()
{
close();
GH.dispatchMainThread( []()
{
CSH->endGameplay();
GH.defActionsDef = 63;
CMM->menu->switchToTab("main");
});
},
0
);
} }
void SettingsMainWindow::loadGameButtonCallback() void SettingsMainWindow::loadGameButtonCallback()
@ -139,13 +164,17 @@ void SettingsMainWindow::saveGameButtonCallback()
void SettingsMainWindow::restartGameButtonCallback() void SettingsMainWindow::restartGameButtonCallback()
{ {
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [this](){ closeAndPushEvent(EUserEvent::RESTART_GAME); }, 0); LOCPLINT->showYesNoDialog(
} CGI->generaltexth->allTexts[67],
[this]()
void SettingsMainWindow::closeAndPushEvent(EUserEvent code) {
{ close();
close(); GH.dispatchMainThread([](){
GH.pushUserEvent(code); CSH->sendRestartGame();
});
},
0
);
} }
void SettingsMainWindow::showAll(Canvas & to) void SettingsMainWindow::showAll(Canvas & to)

View File

@ -30,7 +30,6 @@ private:
void openTab(size_t index); void openTab(size_t index);
void close(); //TODO: copypaste of WindowBase::close(), consider changing Windowbase to IWindowbase with default close() implementation and changing WindowBase inheritance to CIntObject + IWindowBase void close(); //TODO: copypaste of WindowBase::close(), consider changing Windowbase to IWindowbase with default close() implementation and changing WindowBase inheritance to CIntObject + IWindowBase
void closeAndPushEvent(EUserEvent code);
void loadGameButtonCallback(); void loadGameButtonCallback();
void saveGameButtonCallback(); void saveGameButtonCallback();