1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-24 03:47:18 +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 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));

View File

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

View File

@ -23,6 +23,7 @@
#include "../CCallback.h"
#include "windows/CCastleInterface.h"
#include "eventsSDL/InputHandler.h"
#include "mainmenu/CMainMenu.h"
#include "gui/CursorHandler.h"
#include "windows/CKingdomInterface.h"
#include "CGameInfo.h"
@ -1744,7 +1745,16 @@ void CPlayerInterface::requestReturningToMainMenu(bool won)
if(won && cb->getStartInfo()->campState)
CSH->startCampaignScenario(cb->getStartInfo()->campState);
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)
@ -1840,7 +1850,21 @@ void CPlayerInterface::waitForAllDialogs(bool unlockPim)
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()

View File

@ -21,6 +21,7 @@
#include "windows/InfoWindows.h"
#include "mainmenu/CMainMenu.h"
#include "mainmenu/CPrologEpilogVideo.h"
#ifdef VCMI_ANDROID
#include "../lib/CAndroidVMHelper.h"
@ -662,10 +663,36 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs)
{
if(cs)
GH.pushUserEvent(EUserEvent::CAMPAIGN_START_SCENARIO, CMemorySerializer::deepCopy(*cs.get()).release());
else
GH.pushUserEvent(EUserEvent::CAMPAIGN_START_SCENARIO, CMemorySerializer::deepCopy(*si->campState.get()).release());
std::shared_ptr<CampaignState> ourCampaign = cs;
if (!cs)
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)
@ -842,7 +869,13 @@ void CServerHandler::threadHandleConnection()
if(client)
{
state = EClientState::DISCONNECTING;
GH.pushUserEvent(EUserEvent::RETURN_TO_MAIN_MENU);
GH.dispatchMainThread([]()
{
CSH->endGameplay();
GH.defActionsDef = 63;
CMM->menu->switchToTab("main");
});
}
else
{

View File

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

View File

@ -16,7 +16,6 @@
#include "InputSourceKeyboard.h"
#include "InputSourceTouch.h"
#include "InputSourceText.h"
#include "UserEventHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CursorHandler.h"
@ -35,7 +34,6 @@ InputHandler::InputHandler()
, keyboardHandler(std::make_unique<InputSourceKeyboard>())
, fingerHandler(std::make_unique<InputSourceTouch>())
, 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)
{
userHandler->handleUserEvent(ev.user);
handleUserEvent(ev.user);
return;
}
@ -244,15 +242,25 @@ bool InputHandler::hasTouchInputDevice() const
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;
event.type = SDL_USEREVENT;
event.user.code = static_cast<int32_t>(usercode);
event.user.data1 = userdata;
event.user.code = 0;
event.user.data1 = static_cast <void*>(heapFunctor);
event.user.data2 = nullptr;
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
{
return cursorPosition;

View File

@ -15,12 +15,12 @@
enum class EUserEvent;
enum class MouseButton;
union SDL_Event;
struct SDL_UserEvent;
class InputSourceMouse;
class InputSourceKeyboard;
class InputSourceTouch;
class InputSourceText;
class UserEventHandler;
class InputHandler
{
@ -31,12 +31,12 @@ class InputHandler
void preprocessEvent(const SDL_Event & event);
void handleCurrentEvent(const SDL_Event & current);
void handleUserEvent(const SDL_UserEvent & current);
std::unique_ptr<InputSourceMouse> mouseHandler;
std::unique_ptr<InputSourceKeyboard> keyboardHandler;
std::unique_ptr<InputSourceTouch> fingerHandler;
std::unique_ptr<InputSourceText> textHandler;
std::unique_ptr<UserEventHandler> userHandler;
public:
InputHandler();
@ -66,8 +66,8 @@ public:
/// returns true if system has active touchscreen
bool hasTouchInputDevice() const;
/// Generates new user event that will be processed on next frame
void pushUserEvent(EUserEvent usercode, void * userdata);
/// Calls provided functor in main thread on next execution frame
void dispatchMainThread(const std::function<void()> & functor);
/// Returns current position of cursor, in VCMI logical screen coordinates
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()
{
pushUserEvent(EUserEvent::FAKE_MOUSE_MOVE);
dispatchMainThread([](){
GH.events().dispatchMouseMoved(Point(0, 0), GH.getCursorPosition());
});
}
void CGuiHandler::startTextInput(const Rect & whereInput)
@ -203,14 +205,9 @@ bool CGuiHandler::amIGuiThread()
return inGuiThread.get() && *inGuiThread;
}
void CGuiHandler::pushUserEvent(EUserEvent usercode)
void CGuiHandler::dispatchMainThread(const std::function<void()> & functor)
{
inputHandlerInstance->pushUserEvent(usercode, nullptr);
}
void CGuiHandler::pushUserEvent(EUserEvent usercode, void * userdata)
{
inputHandlerInstance->pushUserEvent(usercode, userdata);
inputHandlerInstance->dispatchMainThread(functor);
}
IScreenHandler & CGuiHandler::screenHandler()

View File

@ -27,18 +27,6 @@ class WindowHandler;
class EventDispatcher;
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
class CGuiHandler
{
@ -108,8 +96,9 @@ public:
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen
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
};

View File

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

View File

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

View File

@ -16,6 +16,7 @@
#include "GeneralOptionsTab.h"
#include "OtherOptionsTab.h"
#include "CMT.h"
#include "CGameInfo.h"
#include "CGeneralTextHandler.h"
#include "CPlayerInterface.h"
@ -112,7 +113,18 @@ void SettingsMainWindow::close()
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()
@ -122,7 +134,20 @@ void SettingsMainWindow::backButtonCallback()
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()
@ -139,13 +164,17 @@ void SettingsMainWindow::saveGameButtonCallback()
void SettingsMainWindow::restartGameButtonCallback()
{
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [this](){ closeAndPushEvent(EUserEvent::RESTART_GAME); }, 0);
}
void SettingsMainWindow::closeAndPushEvent(EUserEvent code)
{
close();
GH.pushUserEvent(code);
LOCPLINT->showYesNoDialog(
CGI->generaltexth->allTexts[67],
[this]()
{
close();
GH.dispatchMainThread([](){
CSH->sendRestartGame();
});
},
0
);
}
void SettingsMainWindow::showAll(Canvas & to)

View File

@ -30,7 +30,6 @@ private:
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 closeAndPushEvent(EUserEvent code);
void loadGameButtonCallback();
void saveGameButtonCallback();