1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Moved input handling from GuiHandler to set of classes in eventsSDL dir

This commit is contained in:
Ivan Savenko 2023-05-18 20:32:29 +03:00
parent 5bd044521a
commit 5e86b00dda
27 changed files with 968 additions and 600 deletions

View File

@ -14,36 +14,27 @@
#include "CGameInfo.h"
#include "mainmenu/CMainMenu.h"
#include "mainmenu/CPrologEpilogVideo.h"
#include "gui/CursorHandler.h"
#include "eventsSDL/InputHandler.h"
#include "CPlayerInterface.h"
#include "CVideoHandler.h"
#include "CMusicHandler.h"
#include "gui/CGuiHandler.h"
#include "gui/WindowHandler.h"
#include "CServerHandler.h"
#include "gui/NotificationHandler.h"
#include "ClientCommandManager.h"
#include "windows/CMessage.h"
#include "render/IScreenHandler.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/CConsoleHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/VCMIDirs.h"
#include "../lib/mapping/CCampaignHandler.h"
#include "../lib/CConfigHandler.h"
#include "../lib/logging/CBasicLogConfigurator.h"
#include <boost/program_options.hpp>
#include <vstd/StringUtils.h>
#include <SDL_events.h>
#include <SDL_hints.h>
#include <SDL_main.h>
#ifdef VCMI_WINDOWS
#include <SDL_syswm.h>
#endif
#ifdef VCMI_ANDROID
#include "../lib/CAndroidVMHelper.h"
@ -54,15 +45,14 @@
#undef main
#endif
extern boost::mutex eventsM;
namespace po = boost::program_options;
namespace po_style = boost::program_options::command_line_style;
namespace bfs = boost::filesystem;
extern boost::thread_specific_ptr<bool> inGuiThread;
std::queue<SDL_Event> SDLEventsQueue;
boost::mutex eventsM;
static po::variables_map vm;
#ifndef VCMI_IOS
@ -331,7 +321,7 @@ int main(int argc, char * argv[])
#endif
#ifdef SDL_HINT_MOUSE_TOUCH_EVENTS
if(GH.isPointerRelativeMode)
if(settings["general"]["userRelativePointer"].Bool())
{
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
@ -469,147 +459,18 @@ void playIntro()
}
}
static void handleEvent(SDL_Event & ev)
{
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
{
#ifdef VCMI_ANDROID
handleQuit(false);
#else
handleQuit();
#endif
return;
}
#ifdef VCMI_ANDROID
else if (ev.type == SDL_KEYDOWN && ev.key.keysym.scancode == SDL_SCANCODE_AC_BACK)
{
handleQuit(true);
}
#endif
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
{
Settings full = settings.write["video"]["fullscreen"];
full->Bool() = !full->Bool();
return;
}
else if(ev.type == SDL_USEREVENT)
{
switch(static_cast<EUserEvent>(ev.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<CCampaignState>(reinterpret_cast<CCampaignState *>(ev.user.data1));
auto & epilogue = ourCampaign->camp->scenarios[ourCampaign->mapsConquered.back()].epilog;
auto finisher = [=]()
{
if(ourCampaign->mapsRemaining.size())
{
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;
}
default:
logGlobal->error("Unknown user event. Code %d", ev.user.code);
break;
}
return;
}
else if(ev.type == SDL_WINDOWEVENT)
{
switch (ev.window.event) {
case SDL_WINDOWEVENT_RESTORED:
#ifndef VCMI_IOS
{
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
GH.onScreenResize();
}
#endif
break;
}
return;
}
else if(ev.type == SDL_SYSWMEVENT)
{
if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
{
NotificationHandler::handleSdlEvent(ev);
}
}
//preprocessing
if(ev.type == SDL_MOUSEMOTION)
{
CCS->curh->cursorMove(ev.motion.x, ev.motion.y);
}
{
boost::unique_lock<boost::mutex> lock(eventsM);
SDLEventsQueue.push(ev);
}
}
static void mainLoop()
{
SettingsListener resChanged = settings.listen["video"]["resolution"];
SettingsListener fsChanged = settings.listen["video"]["fullscreen"];
resChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
fsChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
resChanged([](const JsonNode &newState){ GH.pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
fsChanged([](const JsonNode &newState){ GH.pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
inGuiThread.reset(new bool(true));
while(1) //main SDL events loop
{
SDL_Event ev;
while(1 == SDL_PollEvent(&ev))
{
handleEvent(ev);
}
GH.input().fetchEvents();
CSH->applyPacksOnLobbyScreen();
GH.renderFrame();
}

View File

@ -27,6 +27,14 @@ set(client_SRCS
battle/BattleWindow.cpp
battle/CreatureAnimation.cpp
eventsSDL/NotificationHandler.cpp
eventsSDL/InputHandler.cpp
eventsSDL/UserEventHandler.cpp
eventsSDL/InputSourceKeyboard.cpp
eventsSDL/InputSourceMouse.cpp
eventsSDL/InputSourceText.cpp
eventsSDL/InputSourceTouch.cpp
gui/CGuiHandler.cpp
gui/CIntObject.cpp
gui/CursorHandler.cpp
@ -34,7 +42,6 @@ set(client_SRCS
gui/EventsReceiver.cpp
gui/InterfaceObjectConfigurable.cpp
gui/FramerateManager.cpp
gui/NotificationHandler.cpp
gui/ShortcutHandler.cpp
gui/WindowHandler.cpp
@ -162,6 +169,14 @@ set(client_HEADERS
battle/BattleWindow.h
battle/CreatureAnimation.h
eventsSDL/NotificationHandler.h
eventsSDL/InputHandler.h
eventsSDL/UserEventHandler.h
eventsSDL/InputSourceKeyboard.h
eventsSDL/InputSourceMouse.h
eventsSDL/InputSourceText.h
eventsSDL/InputSourceTouch.h
gui/CGuiHandler.h
gui/CIntObject.h
gui/CursorHandler.h
@ -170,7 +185,6 @@ set(client_HEADERS
gui/InterfaceObjectConfigurable.h
gui/FramerateManager.h
gui/MouseButton.h
gui/NotificationHandler.h
gui/Shortcut.h
gui/ShortcutHandler.h
gui/TextAlignment.h
@ -337,7 +351,7 @@ if(WIN32)
endif()
target_compile_definitions(vcmiclient PRIVATE WINDOWS_IGNORE_PACKING_MISMATCH)
# TODO: very hacky, find proper solution to copy AI dlls into bin dir
# TODO: very hacky, find proper solution to copy AI dlls into bin dir
if(MSVC)
add_custom_command(TARGET vcmiclient POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:vcmiclient>"

View File

@ -74,7 +74,7 @@
#include "CServerHandler.h"
// FIXME: only needed for CGameState::mutex
#include "../lib/CGameState.h"
#include "gui/NotificationHandler.h"
#include "eventsSDL/NotificationHandler.h"
#include "adventureMap/CInGameConsole.h"
#include <SDL_events.h>

View File

@ -843,7 +843,7 @@ void CServerHandler::threadHandleConnection()
if(client)
{
state = EClientState::DISCONNECTING;
CGuiHandler::pushUserEvent(EUserEvent::RETURN_TO_MAIN_MENU);
GH.pushUserEvent(EUserEvent::RETURN_TO_MAIN_MENU);
}
else
{

View File

@ -371,7 +371,7 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
auto packet_duration = frame->duration;
#endif
double frameEndTime = (frame->pts + packet_duration) * av_q2d(format->streams[stream]->time_base);
frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.0;
frameTime += GH.framerate().getElapsedMilliseconds() / 1000.0;
if (frameTime >= frameEndTime )
{

View File

@ -0,0 +1,250 @@
/*
* InputHandler.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 "InputHandler.h"
#include "NotificationHandler.h"
#include "InputSourceMouse.h"
#include "InputSourceKeyboard.h"
#include "InputSourceTouch.h"
#include "InputSourceText.h"
#include "UserEventHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CursorHandler.h"
#include "../gui/EventDispatcher.h"
#include "../CMT.h"
#include "../CPlayerInterface.h"
#include "../CGameInfo.h"
#include "../../lib/CConfigHandler.h"
#include <SDL_events.h>
std::queue<SDL_Event> SDLEventsQueue;
boost::mutex eventsM;
InputHandler::InputHandler()
: mouseHandler(std::make_unique<InputSourceMouse>())
, keyboardHandler(std::make_unique<InputSourceKeyboard>())
, fingerHandler(std::make_unique<InputSourceTouch>())
, textHandler(std::make_unique<InputSourceText>())
, userHandler(std::make_unique<UserEventHandler>())
, mouseButtonsMask(0)
, pointerSpeedMultiplier(settings["general"]["relativePointerSpeedMultiplier"].Float())
{
}
InputHandler::~InputHandler() = default;
void InputHandler::handleCurrentEvent(const SDL_Event & current)
{
switch (current.type)
{
case SDL_KEYDOWN:
return keyboardHandler->handleEventKeyDown(current.key);
case SDL_KEYUP:
return keyboardHandler->handleEventKeyUp(current.key);
case SDL_MOUSEMOTION:
return mouseHandler->handleEventMouseMotion(current.motion);
case SDL_MOUSEBUTTONDOWN:
return mouseHandler->handleEventMouseButtonDown(current.button);
case SDL_MOUSEWHEEL:
return mouseHandler->handleEventMouseWheel(current.wheel);
case SDL_TEXTINPUT:
return textHandler->handleEventTextInput(current.text);
case SDL_TEXTEDITING:
return textHandler->handleEventTextEditing(current.edit);
case SDL_MOUSEBUTTONUP:
return mouseHandler->handleEventMouseButtonUp(current.button);
case SDL_FINGERMOTION:
return fingerHandler->handleEventFingerMotion(current.tfinger);
case SDL_FINGERDOWN:
return fingerHandler->handleEventFingerDown(current.tfinger);
case SDL_FINGERUP:
return fingerHandler->handleEventFingerUp(current.tfinger);
}
}
void InputHandler::processEvents()
{
boost::unique_lock<boost::mutex> lock(eventsM);
while(!SDLEventsQueue.empty())
{
GH.events().allowEventHandling(true);
SDL_Event currentEvent = SDLEventsQueue.front();
if (currentEvent.type == SDL_MOUSEMOTION)
{
cursorPosition = Point(currentEvent.motion.x, currentEvent.motion.y);
mouseButtonsMask = currentEvent.motion.state;
}
SDLEventsQueue.pop();
// In a sequence of mouse motion events, skip all but the last one.
// This prevents freezes when every motion event takes longer to handle than interval at which
// the events arrive (like dragging on the minimap in world view, with redraw at every event)
// so that the events would start piling up faster than they can be processed.
if ((currentEvent.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION))
continue;
handleCurrentEvent(currentEvent);
}
}
void InputHandler::preprocessEvent(const SDL_Event & ev)
{
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
{
#ifdef VCMI_ANDROID
handleQuit(false);
#else
handleQuit();
#endif
return;
}
#ifdef VCMI_ANDROID
else if (ev.type == SDL_KEYDOWN && ev.key.keysym.scancode == SDL_SCANCODE_AC_BACK)
{
handleQuit(true);
}
#endif
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
{
Settings full = settings.write["video"]["fullscreen"];
full->Bool() = !full->Bool();
return;
}
else if(ev.type == SDL_USEREVENT)
{
userHandler->handleUserEvent(ev.user);
return;
}
else if(ev.type == SDL_WINDOWEVENT)
{
switch (ev.window.event) {
case SDL_WINDOWEVENT_RESTORED:
#ifndef VCMI_IOS
{
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
GH.onScreenResize();
}
#endif
break;
}
return;
}
else if(ev.type == SDL_SYSWMEVENT)
{
if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
{
NotificationHandler::handleSdlEvent(ev);
}
}
//preprocessing
if(ev.type == SDL_MOUSEMOTION)
{
CCS->curh->cursorMove(ev.motion.x, ev.motion.y);
}
{
boost::unique_lock<boost::mutex> lock(eventsM);
SDLEventsQueue.push(ev);
}
}
void InputHandler::fetchEvents()
{
SDL_Event ev;
while(1 == SDL_PollEvent(&ev))
{
preprocessEvent(ev);
}
}
bool InputHandler::isKeyboardCtrlDown() const
{
#ifdef VCMI_MAC
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LGUI] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RGUI];
#else
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RCTRL];
#endif
}
bool InputHandler::isKeyboardAltDown() const
{
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LALT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RALT];
}
bool InputHandler::isKeyboardShiftDown() const
{
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LSHIFT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RSHIFT];
}
void InputHandler::fakeMoveCursor(float dx, float dy)
{
int x, y, w, h;
SDL_Event event;
SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0};
sme.state = SDL_GetMouseState(&x, &y);
SDL_GetWindowSize(mainWindow, &w, &h);
sme.x = GH.getCursorPosition().x + (int)(pointerSpeedMultiplier * w * dx);
sme.y = GH.getCursorPosition().y + (int)(pointerSpeedMultiplier * h * dy);
vstd::abetween(sme.x, 0, w);
vstd::abetween(sme.y, 0, h);
event.motion = sme;
SDL_PushEvent(&event);
}
void InputHandler::startTextInput(const Rect & where)
{
textHandler->startTextInput(where);
}
void InputHandler::stopTextInput()
{
textHandler->stopTextInput();
}
bool InputHandler::isMouseButtonPressed(MouseButton button) const
{
static_assert(static_cast<uint32_t>(MouseButton::LEFT) == SDL_BUTTON_LEFT, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::MIDDLE) == SDL_BUTTON_MIDDLE, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::RIGHT) == SDL_BUTTON_RIGHT, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::EXTRA1) == SDL_BUTTON_X1, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::EXTRA2) == SDL_BUTTON_X2, "mismatch between VCMI and SDL enum!");
uint32_t index = static_cast<uint32_t>(button);
return mouseButtonsMask & SDL_BUTTON(index);
}
void InputHandler::pushUserEvent(EUserEvent usercode, void * userdata)
{
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = static_cast<int32_t>(usercode);
event.user.data1 = userdata;
SDL_PushEvent(&event);
}
const Point & InputHandler::getCursorPosition() const
{
return cursorPosition;
}

View File

@ -0,0 +1,59 @@
/*
* InputHandler.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
#include "../lib/Rect.h"
enum class EUserEvent;
enum class MouseButton;
union SDL_Event;
class InputSourceMouse;
class InputSourceKeyboard;
class InputSourceTouch;
class InputSourceText;
class UserEventHandler;
class InputHandler
{
Point cursorPosition;
float pointerSpeedMultiplier;
int mouseButtonsMask;
void preprocessEvent(const SDL_Event & event);
void handleCurrentEvent(const SDL_Event & 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();
~InputHandler();
void fetchEvents();
void processEvents();
void fakeMoveCursor(float dx, float dy);
void startTextInput(const Rect & where);
void stopTextInput();
bool isMouseButtonPressed(MouseButton button) const;
void pushUserEvent(EUserEvent usercode, void * userdata);
const Point & getCursorPosition() const;
/// returns true if chosen keyboard key is currently pressed down
bool isKeyboardAltDown() const;
bool isKeyboardCtrlDown() const;
bool isKeyboardShiftDown() const;
};

View File

@ -0,0 +1,76 @@
/*
* InputSourceKeyboard.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 "InputSourceKeyboard.h"
#include "../../lib/CConfigHandler.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../gui/EventDispatcher.h"
#include "../gui/ShortcutHandler.h"
#include <SDL_events.h>
void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
{
if(key.repeat != 0)
return; // ignore periodic event resends
assert(key.state == SDL_PRESSED);
if(key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
{
//TODO: we need some central place for all interface-independent hotkeys
Settings s = settings.write["session"];
switch(key.keysym.sym)
{
case SDLK_F5:
if(settings["session"]["spectate-locked-pim"].Bool())
CPlayerInterface::pim->unlock();
else
CPlayerInterface::pim->lock();
s["spectate-locked-pim"].Bool() = !settings["session"]["spectate-locked-pim"].Bool();
break;
case SDLK_F6:
s["spectate-ignore-hero"].Bool() = !settings["session"]["spectate-ignore-hero"].Bool();
break;
case SDLK_F7:
s["spectate-skip-battle"].Bool() = !settings["session"]["spectate-skip-battle"].Bool();
break;
case SDLK_F8:
s["spectate-skip-battle-result"].Bool() = !settings["session"]["spectate-skip-battle-result"].Bool();
break;
default:
break;
}
return;
}
auto shortcutsVector = GH.shortcuts().translateKeycode(key.keysym.sym);
GH.events().dispatchShortcutPressed(shortcutsVector);
}
void InputSourceKeyboard::handleEventKeyUp(const SDL_KeyboardEvent & key)
{
if(key.repeat != 0)
return; // ignore periodic event resends
assert(key.state == SDL_RELEASED);
auto shortcutsVector = GH.shortcuts().translateKeycode(key.keysym.sym);
GH.events().dispatchShortcutReleased(shortcutsVector);
}

View File

@ -0,0 +1,20 @@
/*
* InputSourceKeyboard.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_KeyboardEvent;
class InputSourceKeyboard
{
public:
void handleEventKeyDown(const SDL_KeyboardEvent & current);
void handleEventKeyUp(const SDL_KeyboardEvent & current);
};

View File

@ -0,0 +1,72 @@
/*
* InputSourceMouse.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 "InputSourceMouse.h"
#include "../../lib/Point.h"
#include "../gui/CGuiHandler.h"
#include "../gui/EventDispatcher.h"
#include "../gui/MouseButton.h"
#include <SDL_events.h>
void InputSourceMouse::handleEventMouseMotion(const SDL_MouseMotionEvent & motion)
{
GH.events().dispatchMouseMoved(Point(motion.x, motion.y));
}
void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & button)
{
Point position(button.x, button.y);
switch(button.button)
{
case SDL_BUTTON_LEFT:
if(button.clicks > 1)
GH.events().dispatchMouseDoubleClick(position);
else
GH.events().dispatchMouseButtonPressed(MouseButton::LEFT, position);
break;
case SDL_BUTTON_RIGHT:
GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, position);
break;
case SDL_BUTTON_MIDDLE:
GH.events().dispatchMouseButtonPressed(MouseButton::MIDDLE, position);
break;
}
}
void InputSourceMouse::handleEventMouseWheel(const SDL_MouseWheelEvent & wheel)
{
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
int x = 0, y = 0;
SDL_GetMouseState(&x, &y);
GH.events().dispatchMouseScrolled(Point(wheel.x, wheel.y), Point(x, y));
}
void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & button)
{
Point position(button.x, button.y);
switch(button.button)
{
case SDL_BUTTON_LEFT:
GH.events().dispatchMouseButtonReleased(MouseButton::LEFT, position);
break;
case SDL_BUTTON_RIGHT:
GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, position);
break;
case SDL_BUTTON_MIDDLE:
GH.events().dispatchMouseButtonReleased(MouseButton::MIDDLE, position);
break;
}
}

View File

@ -0,0 +1,24 @@
/*
* InputSourceMouse.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_MouseWheelEvent;
struct SDL_MouseMotionEvent;
struct SDL_MouseButtonEvent;
class InputSourceMouse
{
public:
void handleEventMouseMotion(const SDL_MouseMotionEvent & current);
void handleEventMouseButtonDown(const SDL_MouseButtonEvent & current);
void handleEventMouseWheel(const SDL_MouseWheelEvent & current);
void handleEventMouseButtonUp(const SDL_MouseButtonEvent & current);
};

View File

@ -0,0 +1,92 @@
/*
* InputSourceText.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 "InputSourceText.h"
#include "../CMT.h"
#include "../gui/CGuiHandler.h"
#include "../gui/EventDispatcher.h"
#include "../../lib/Rect.h"
#include <SDL_events.h>
#include <SDL_render.h>
#ifdef VCMI_APPLE
# include <dispatch/dispatch.h>
#endif
#ifdef VCMI_IOS
# include "ios/utils.h"
#endif
void InputSourceText::handleEventTextInput(const SDL_TextInputEvent & text)
{
GH.events().dispatchTextInput(text.text);
}
void InputSourceText::handleEventTextEditing(const SDL_TextEditingEvent & text)
{
GH.events().dispatchTextEditing(text.text);
}
void InputSourceText::startTextInput(const Rect & whereInput)
{
#ifdef VCMI_APPLE
dispatch_async(dispatch_get_main_queue(), ^{
#endif
// TODO ios: looks like SDL bug actually, try fixing there
auto renderer = SDL_GetRenderer(mainWindow);
float scaleX, scaleY;
SDL_Rect viewport;
SDL_RenderGetScale(renderer, &scaleX, &scaleY);
SDL_RenderGetViewport(renderer, &viewport);
#ifdef VCMI_IOS
const auto nativeScale = iOS_utils::screenScale();
scaleX /= nativeScale;
scaleY /= nativeScale;
#endif
SDL_Rect rectInScreenCoordinates;
rectInScreenCoordinates.x = (viewport.x + whereInput.x) * scaleX;
rectInScreenCoordinates.y = (viewport.y + whereInput.y) * scaleY;
rectInScreenCoordinates.w = whereInput.w * scaleX;
rectInScreenCoordinates.h = whereInput.h * scaleY;
SDL_SetTextInputRect(&rectInScreenCoordinates);
if (SDL_IsTextInputActive() == SDL_FALSE)
{
SDL_StartTextInput();
}
#ifdef VCMI_APPLE
});
#endif
}
void InputSourceText::stopTextInput()
{
#ifdef VCMI_APPLE
dispatch_async(dispatch_get_main_queue(), ^{
#endif
if (SDL_IsTextInputActive() == SDL_TRUE)
{
SDL_StopTextInput();
}
#ifdef VCMI_APPLE
});
#endif
}

View File

@ -0,0 +1,28 @@
/*
* InputSourceText.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
VCMI_LIB_NAMESPACE_BEGIN
class Rect;
VCMI_LIB_NAMESPACE_END
struct SDL_TextEditingEvent;
struct SDL_TextInputEvent;
class InputSourceText
{
public:
void handleEventTextInput(const SDL_TextInputEvent & current);
void handleEventTextEditing(const SDL_TextEditingEvent & current);
void startTextInput(const Rect & where);
void stopTextInput();
};

View File

@ -0,0 +1,128 @@
/*
* InputSourceTouch.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 "InputSourceTouch.h"
#include "InputHandler.h"
#include "../../lib/CConfigHandler.h"
#include "../CMT.h"
#include "../gui/CGuiHandler.h"
#include "../gui/EventDispatcher.h"
#include <SDL_events.h>
#include <SDL_render.h>
InputSourceTouch::InputSourceTouch()
: multifinger(false)
, isPointerRelativeMode(settings["general"]["userRelativePointer"].Bool())
{
}
void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfinger)
{
if(isPointerRelativeMode)
{
GH.input().fakeMoveCursor(tfinger.dx, tfinger.dy);
}
}
void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinger)
{
auto fingerCount = SDL_GetNumTouchFingers(tfinger.touchId);
multifinger = fingerCount > 1;
if(isPointerRelativeMode)
{
if(tfinger.x > 0.5)
{
bool isRightClick = tfinger.y < 0.5;
fakeMouseButtonEventRelativeMode(true, isRightClick);
}
}
#ifndef VCMI_IOS
else if(fingerCount == 2)
{
Point position = convertTouchToMouse(tfinger);
GH.events().dispatchMouseMoved(position);
GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, position);
}
#endif //VCMI_IOS
}
void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
{
#ifndef VCMI_IOS
auto fingerCount = SDL_GetNumTouchFingers(tfinger.touchId);
#endif //VCMI_IOS
if(isPointerRelativeMode)
{
if(tfinger.x > 0.5)
{
bool isRightClick = tfinger.y < 0.5;
fakeMouseButtonEventRelativeMode(false, isRightClick);
}
}
#ifndef VCMI_IOS
else if(multifinger)
{
Point position = convertTouchToMouse(tfinger);
GH.events().dispatchMouseMoved(position);
GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, position);
multifinger = fingerCount != 0;
}
#endif //VCMI_IOS
}
Point InputSourceTouch::convertTouchToMouse(const SDL_TouchFingerEvent & tfinger)
{
return Point(tfinger.x * GH.screenDimensions().x, tfinger.y * GH.screenDimensions().y);
}
void InputSourceTouch::fakeMouseButtonEventRelativeMode(bool down, bool right)
{
SDL_Event event;
SDL_MouseButtonEvent sme = {SDL_MOUSEBUTTONDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if(!down)
{
sme.type = SDL_MOUSEBUTTONUP;
}
sme.button = right ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT;
sme.x = GH.getCursorPosition().x;
sme.y = GH.getCursorPosition().y;
float xScale, yScale;
int w, h, rLogicalWidth, rLogicalHeight;
SDL_GetWindowSize(mainWindow, &w, &h);
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
SDL_RenderGetScale(mainRenderer, &xScale, &yScale);
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
moveCursorToPosition(Point((int)(sme.x * xScale) + (w - rLogicalWidth * xScale) / 2, (int)(sme.y * yScale + (h - rLogicalHeight * yScale) / 2)));
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
event.button = sme;
SDL_PushEvent(&event);
}
void InputSourceTouch::moveCursorToPosition(const Point & position)
{
SDL_WarpMouseInWindow(mainWindow, position.x, position.y);
}

View File

@ -0,0 +1,36 @@
/*
* InputSourceTouch.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
VCMI_LIB_NAMESPACE_BEGIN
class Point;
VCMI_LIB_NAMESPACE_END
struct SDL_TouchFingerEvent;
class InputSourceTouch
{
bool multifinger;
bool isPointerRelativeMode;
/// moves mouse pointer into specified position inside vcmi window
void moveCursorToPosition(const Point & position);
Point convertTouchToMouse(const SDL_TouchFingerEvent & current);
void fakeMouseButtonEventRelativeMode(bool down, bool right);
public:
InputSourceTouch();
void handleEventFingerMotion(const SDL_TouchFingerEvent & current);
void handleEventFingerDown(const SDL_TouchFingerEvent & current);
void handleEventFingerUp(const SDL_TouchFingerEvent & current);
};

View File

@ -10,11 +10,11 @@
#include "StdInc.h"
#include "NotificationHandler.h"
#include <SDL_video.h>
#include <SDL_events.h>
#if defined(VCMI_WINDOWS)
#include <SDL_syswm.h>
#include <SDL_video.h>
#include <SDL_events.h>
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:

View File

@ -0,0 +1,87 @@
/*
* 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 <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<CCampaignState>(reinterpret_cast<CCampaignState *>(user.data1));
auto & epilogue = ourCampaign->camp->scenarios[ourCampaign->mapsConquered.back()].epilog;
auto finisher = [=]()
{
if(!ourCampaign->mapsRemaining.empty())
{
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;
}
default:
logGlobal->error("Unknown user event. Code %d", user.code);
break;
}
}

View File

@ -0,0 +1,19 @@
/*
* 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 UserEventHandler
{
public:
void handleUserEvent(const SDL_UserEvent & current);
};

View File

@ -17,6 +17,7 @@
#include "FramerateManager.h"
#include "WindowHandler.h"
#include "EventDispatcher.h"
#include "../eventsSDL/InputHandler.h"
#include "../CGameInfo.h"
#include "../render/Colors.h"
@ -30,17 +31,6 @@
#include "../../lib/CConfigHandler.h"
#include <SDL_render.h>
#include <SDL_timer.h>
#include <SDL_events.h>
#include <SDL_keycode.h>
#ifdef VCMI_APPLE
#include <dispatch/dispatch.h>
#endif
#ifdef VCMI_IOS
#include "ios/utils.h"
#endif
CGuiHandler GH;
@ -80,382 +70,38 @@ SSetCaptureState::~SSetCaptureState()
void CGuiHandler::init()
{
inputHandlerInstance = std::make_unique<InputHandler>();
eventDispatcherInstance = std::make_unique<EventDispatcher>();
windowHandlerInstance = std::make_unique<WindowHandler>();
screenHandlerInstance = std::make_unique<ScreenHandler>();
shortcutsHandlerInstance = std::make_unique<ShortcutHandler>();
framerateManagerInstance = std::make_unique<FramerateManager>(settings["video"]["targetfps"].Integer());
isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool();
pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float();
}
void CGuiHandler::handleEvents()
{
eventDispatcher().dispatchTimer(framerateManager().getElapsedMilliseconds());
events().dispatchTimer(framerate().getElapsedMilliseconds());
//player interface may want special event handling
if(nullptr != LOCPLINT && LOCPLINT->capturedAllEvents())
return;
boost::unique_lock<boost::mutex> lock(eventsM);
while(!SDLEventsQueue.empty())
{
eventDispatcher().allowEventHandling(true);
SDL_Event currentEvent = SDLEventsQueue.front();
if (currentEvent.type == SDL_MOUSEMOTION)
{
cursorPosition = Point(currentEvent.motion.x, currentEvent.motion.y);
mouseButtonsMask = currentEvent.motion.state;
}
SDLEventsQueue.pop();
// In a sequence of mouse motion events, skip all but the last one.
// This prevents freezes when every motion event takes longer to handle than interval at which
// the events arrive (like dragging on the minimap in world view, with redraw at every event)
// so that the events would start piling up faster than they can be processed.
if ((currentEvent.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION))
continue;
handleCurrentEvent(currentEvent);
}
}
void CGuiHandler::convertTouchToMouse(SDL_Event * current)
{
int rLogicalWidth, rLogicalHeight;
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
int adjustedMouseY = (int)(current->tfinger.y * rLogicalHeight);
int adjustedMouseX = (int)(current->tfinger.x * rLogicalWidth);
current->button.x = adjustedMouseX;
current->motion.x = adjustedMouseX;
current->button.y = adjustedMouseY;
current->motion.y = adjustedMouseY;
}
void CGuiHandler::fakeMoveCursor(float dx, float dy)
{
int x, y, w, h;
SDL_Event event;
SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0};
sme.state = SDL_GetMouseState(&x, &y);
SDL_GetWindowSize(mainWindow, &w, &h);
sme.x = CCS->curh->position().x + (int)(GH.pointerSpeedMultiplier * w * dx);
sme.y = CCS->curh->position().y + (int)(GH.pointerSpeedMultiplier * h * dy);
vstd::abetween(sme.x, 0, w);
vstd::abetween(sme.y, 0, h);
event.motion = sme;
SDL_PushEvent(&event);
input().processEvents();
}
void CGuiHandler::fakeMouseMove()
{
fakeMoveCursor(0, 0);
input().fakeMoveCursor(0, 0);
}
void CGuiHandler::startTextInput(const Rect & whereInput)
{
#ifdef VCMI_APPLE
dispatch_async(dispatch_get_main_queue(), ^{
#endif
// TODO ios: looks like SDL bug actually, try fixing there
auto renderer = SDL_GetRenderer(mainWindow);
float scaleX, scaleY;
SDL_Rect viewport;
SDL_RenderGetScale(renderer, &scaleX, &scaleY);
SDL_RenderGetViewport(renderer, &viewport);
#ifdef VCMI_IOS
const auto nativeScale = iOS_utils::screenScale();
scaleX /= nativeScale;
scaleY /= nativeScale;
#endif
SDL_Rect rectInScreenCoordinates;
rectInScreenCoordinates.x = (viewport.x + whereInput.x) * scaleX;
rectInScreenCoordinates.y = (viewport.y + whereInput.y) * scaleY;
rectInScreenCoordinates.w = whereInput.w * scaleX;
rectInScreenCoordinates.h = whereInput.h * scaleY;
SDL_SetTextInputRect(&rectInScreenCoordinates);
if (SDL_IsTextInputActive() == SDL_FALSE)
{
SDL_StartTextInput();
}
#ifdef VCMI_APPLE
});
#endif
input().startTextInput(whereInput);
}
void CGuiHandler::stopTextInput()
{
#ifdef VCMI_APPLE
dispatch_async(dispatch_get_main_queue(), ^{
#endif
if (SDL_IsTextInputActive() == SDL_TRUE)
{
SDL_StopTextInput();
}
#ifdef VCMI_APPLE
});
#endif
}
void CGuiHandler::fakeMouseButtonEventRelativeMode(bool down, bool right)
{
SDL_Event event;
SDL_MouseButtonEvent sme = {SDL_MOUSEBUTTONDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if(!down)
{
sme.type = SDL_MOUSEBUTTONUP;
}
sme.button = right ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT;
sme.x = CCS->curh->position().x;
sme.y = CCS->curh->position().y;
float xScale, yScale;
int w, h, rLogicalWidth, rLogicalHeight;
SDL_GetWindowSize(mainWindow, &w, &h);
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
SDL_RenderGetScale(mainRenderer, &xScale, &yScale);
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
moveCursorToPosition( Point(
(int)(sme.x * xScale) + (w - rLogicalWidth * xScale) / 2,
(int)(sme.y * yScale + (h - rLogicalHeight * yScale) / 2)));
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
event.button = sme;
SDL_PushEvent(&event);
}
void CGuiHandler::handleCurrentEvent( SDL_Event & current )
{
switch (current.type)
{
case SDL_KEYDOWN:
return handleEventKeyDown(current);
case SDL_KEYUP:
return handleEventKeyUp(current);
case SDL_MOUSEMOTION:
return handleEventMouseMotion(current);
case SDL_MOUSEBUTTONDOWN:
return handleEventMouseButtonDown(current);
case SDL_MOUSEWHEEL:
return handleEventMouseWheel(current);
case SDL_TEXTINPUT:
return handleEventTextInput(current);
case SDL_TEXTEDITING:
return handleEventTextEditing(current);
case SDL_MOUSEBUTTONUP:
return handleEventMouseButtonUp(current);
case SDL_FINGERMOTION:
return handleEventFingerMotion(current);
case SDL_FINGERDOWN:
return handleEventFingerDown(current);
case SDL_FINGERUP:
return handleEventFingerUp(current);
}
}
void CGuiHandler::handleEventKeyDown(SDL_Event & current)
{
SDL_KeyboardEvent key = current.key;
if(key.repeat != 0)
return; // ignore periodic event resends
assert(key.state == SDL_PRESSED);
if(current.type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
{
//TODO: we need some central place for all interface-independent hotkeys
Settings s = settings.write["session"];
switch(key.keysym.sym)
{
case SDLK_F5:
if(settings["session"]["spectate-locked-pim"].Bool())
LOCPLINT->pim->unlock();
else
LOCPLINT->pim->lock();
s["spectate-locked-pim"].Bool() = !settings["session"]["spectate-locked-pim"].Bool();
break;
case SDLK_F6:
s["spectate-ignore-hero"].Bool() = !settings["session"]["spectate-ignore-hero"].Bool();
break;
case SDLK_F7:
s["spectate-skip-battle"].Bool() = !settings["session"]["spectate-skip-battle"].Bool();
break;
case SDLK_F8:
s["spectate-skip-battle-result"].Bool() = !settings["session"]["spectate-skip-battle-result"].Bool();
break;
default:
break;
}
return;
}
auto shortcutsVector = shortcutsHandler().translateKeycode(key.keysym.sym);
eventDispatcher().dispatchShortcutPressed(shortcutsVector);
}
void CGuiHandler::handleEventKeyUp(SDL_Event & current)
{
SDL_KeyboardEvent key = current.key;
if(key.repeat != 0)
return; // ignore periodic event resends
assert(key.state == SDL_RELEASED);
auto shortcutsVector = shortcutsHandler().translateKeycode(key.keysym.sym);
eventDispatcher().dispatchShortcutReleased(shortcutsVector);
}
void CGuiHandler::handleEventMouseMotion(SDL_Event & current)
{
eventDispatcher().dispatchMouseMoved(Point(current.motion.x, current.motion.y));
}
void CGuiHandler::handleEventMouseButtonDown(SDL_Event & current)
{
switch(current.button.button)
{
case SDL_BUTTON_LEFT:
if (current.button.clicks > 1)
eventDispatcher().dispatchMouseDoubleClick(Point(current.button.x, current.button.y));
else
eventDispatcher().dispatchMouseButtonPressed(MouseButton::LEFT, Point(current.button.x, current.button.y));
break;
case SDL_BUTTON_RIGHT:
eventDispatcher().dispatchMouseButtonPressed(MouseButton::RIGHT, Point(current.button.x, current.button.y));
break;
case SDL_BUTTON_MIDDLE:
eventDispatcher().dispatchMouseButtonPressed(MouseButton::MIDDLE, Point(current.button.x, current.button.y));
break;
}
}
void CGuiHandler::handleEventMouseWheel(SDL_Event & current)
{
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
int x = 0, y = 0;
SDL_GetMouseState(&x, &y);
eventDispatcher().dispatchMouseScrolled(Point(current.wheel.x, current.wheel.y), Point(x, y));
}
void CGuiHandler::handleEventTextInput(SDL_Event & current)
{
eventDispatcher().dispatchTextInput(current.text.text);
}
void CGuiHandler::handleEventTextEditing(SDL_Event & current)
{
eventDispatcher().dispatchTextEditing(current.text.text);
}
void CGuiHandler::handleEventMouseButtonUp(SDL_Event & current)
{
if(!multifinger)
{
switch(current.button.button)
{
case SDL_BUTTON_LEFT:
eventDispatcher().dispatchMouseButtonReleased(MouseButton::LEFT, Point(current.button.x, current.button.y));
break;
case SDL_BUTTON_RIGHT:
eventDispatcher().dispatchMouseButtonReleased(MouseButton::RIGHT, Point(current.button.x, current.button.y));
break;
case SDL_BUTTON_MIDDLE:
eventDispatcher().dispatchMouseButtonReleased(MouseButton::MIDDLE, Point(current.button.x, current.button.y));
break;
}
}
}
void CGuiHandler::handleEventFingerMotion(SDL_Event & current)
{
if(isPointerRelativeMode)
{
fakeMoveCursor(current.tfinger.dx, current.tfinger.dy);
}
}
void CGuiHandler::handleEventFingerDown(SDL_Event & current)
{
auto fingerCount = SDL_GetNumTouchFingers(current.tfinger.touchId);
multifinger = fingerCount > 1;
if(isPointerRelativeMode)
{
if(current.tfinger.x > 0.5)
{
bool isRightClick = current.tfinger.y < 0.5;
fakeMouseButtonEventRelativeMode(true, isRightClick);
}
}
#ifndef VCMI_IOS
else if(fingerCount == 2)
{
convertTouchToMouse(&current);
eventDispatcher().dispatchMouseMoved(Point(current.button.x, current.button.y));
eventDispatcher().dispatchMouseButtonPressed(MouseButton::RIGHT, Point(current.button.x, current.button.y));
}
#endif //VCMI_IOS
}
void CGuiHandler::handleEventFingerUp(SDL_Event & current)
{
#ifndef VCMI_IOS
auto fingerCount = SDL_GetNumTouchFingers(current.tfinger.touchId);
#endif //VCMI_IOS
if(isPointerRelativeMode)
{
if(current.tfinger.x > 0.5)
{
bool isRightClick = current.tfinger.y < 0.5;
fakeMouseButtonEventRelativeMode(false, isRightClick);
}
}
#ifndef VCMI_IOS
else if(multifinger)
{
convertTouchToMouse(&current);
eventDispatcher().dispatchMouseMoved(Point(current.button.x, current.button.y));
eventDispatcher().dispatchMouseButtonReleased(MouseButton::RIGHT, Point(current.button.x, current.button.y));
multifinger = fingerCount != 0;
}
#endif //VCMI_IOS
input().stopTextInput();
}
void CGuiHandler::renderFrame()
@ -493,14 +139,12 @@ void CGuiHandler::renderFrame()
windows().onFrameRendered();
}
framerateManager().framerateDelay(); // holds a constant FPS
framerate().framerateDelay(); // holds a constant FPS
}
CGuiHandler::CGuiHandler()
: defActionsDef(0)
, captureChildren(false)
, multifinger(false)
, mouseButtonsMask(0)
, curInt(nullptr)
, fakeStatusBar(std::make_shared<EmptyStatusBar>())
, terminate_cond (new CondSh<bool>(false))
@ -512,50 +156,41 @@ CGuiHandler::~CGuiHandler()
delete terminate_cond;
}
ShortcutHandler & CGuiHandler::shortcutsHandler()
ShortcutHandler & CGuiHandler::shortcuts()
{
assert(shortcutsHandlerInstance);
return *shortcutsHandlerInstance;
}
FramerateManager & CGuiHandler::framerateManager()
FramerateManager & CGuiHandler::framerate()
{
assert(framerateManagerInstance);
return *framerateManagerInstance;
}
void CGuiHandler::moveCursorToPosition(const Point & position)
{
SDL_WarpMouseInWindow(mainWindow, position.x, position.y);
}
bool CGuiHandler::isKeyboardCtrlDown() const
{
#ifdef VCMI_MAC
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LGUI] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RGUI];
#else
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RCTRL];
#endif
return inputHandlerInstance->isKeyboardCtrlDown();
}
bool CGuiHandler::isKeyboardAltDown() const
{
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LALT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RALT];
return inputHandlerInstance->isKeyboardAltDown();
}
bool CGuiHandler::isKeyboardShiftDown() const
{
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LSHIFT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RSHIFT];
return inputHandlerInstance->isKeyboardShiftDown();
}
void CGuiHandler::breakEventHandling()
{
eventDispatcher().allowEventHandling(false);
events().allowEventHandling(false);
}
const Point & CGuiHandler::getCursorPosition() const
{
return cursorPosition;
return inputHandlerInstance->getCursorPosition();
}
Point CGuiHandler::screenDimensions() const
@ -565,19 +200,12 @@ Point CGuiHandler::screenDimensions() const
bool CGuiHandler::isMouseButtonPressed() const
{
return mouseButtonsMask > 0;
return isMouseButtonPressed(MouseButton::LEFT) || isMouseButtonPressed(MouseButton::MIDDLE) || isMouseButtonPressed(MouseButton::RIGHT);
}
bool CGuiHandler::isMouseButtonPressed(MouseButton button) const
{
static_assert(static_cast<uint32_t>(MouseButton::LEFT) == SDL_BUTTON_LEFT, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::MIDDLE) == SDL_BUTTON_MIDDLE, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::RIGHT) == SDL_BUTTON_RIGHT, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::EXTRA1) == SDL_BUTTON_X1, "mismatch between VCMI and SDL enum!");
static_assert(static_cast<uint32_t>(MouseButton::EXTRA2) == SDL_BUTTON_X2, "mismatch between VCMI and SDL enum!");
uint32_t index = static_cast<uint32_t>(button);
return mouseButtonsMask & SDL_BUTTON(index);
return inputHandlerInstance->isMouseButtonPressed(button);
}
void CGuiHandler::drawFPSCounter()
@ -585,7 +213,7 @@ void CGuiHandler::drawFPSCounter()
static SDL_Rect overlay = { 0, 0, 64, 32};
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
SDL_FillRect(screen, &overlay, black);
std::string fps = std::to_string(framerateManager().getFramerate());
std::string fps = std::to_string(framerate().getFramerate());
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10));
}
@ -596,16 +224,12 @@ bool CGuiHandler::amIGuiThread()
void CGuiHandler::pushUserEvent(EUserEvent usercode)
{
pushUserEvent(usercode, nullptr);
inputHandlerInstance->pushUserEvent(usercode, nullptr);
}
void CGuiHandler::pushUserEvent(EUserEvent usercode, void * userdata)
{
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = static_cast<int32_t>(usercode);
event.user.data1 = userdata;
SDL_PushEvent(&event);
inputHandlerInstance->pushUserEvent(usercode, userdata);
}
IScreenHandler & CGuiHandler::screenHandler()
@ -613,11 +237,16 @@ IScreenHandler & CGuiHandler::screenHandler()
return *screenHandlerInstance;
}
EventDispatcher & CGuiHandler::eventDispatcher()
EventDispatcher & CGuiHandler::events()
{
return *eventDispatcherInstance;
}
InputHandler & CGuiHandler::input()
{
return *inputHandlerInstance;
}
WindowHandler & CGuiHandler::windows()
{
assert(windowHandlerInstance);

View File

@ -31,18 +31,17 @@ class IShowActivatable;
class IScreenHandler;
class WindowHandler;
class EventDispatcher;
class InputHandler;
// TODO: event handling need refactoring
// TODO: event handling need refactoring. Perhaps convert into delayed function call?
enum class EUserEvent
{
/*CHANGE_SCREEN_RESOLUTION = 1,*/
RETURN_TO_MAIN_MENU = 2,
//STOP_CLIENT = 3,
RESTART_GAME = 4,
RETURN_TO_MAIN_MENU,
RESTART_GAME,
RETURN_TO_MENU_LOAD,
FULLSCREEN_TOGGLED,
CAMPAIGN_START_SCENARIO,
FORCE_QUIT, //quit client without question
FORCE_QUIT,
};
// Handles GUI logic and drawing
@ -55,41 +54,22 @@ private:
/// Status bar of current window, if any. Uses weak_ptr to allow potential hanging reference after owned window has been deleted
std::weak_ptr<IStatusBar> currentStatusBar;
Point cursorPosition;
uint32_t mouseButtonsMask;
std::unique_ptr<ShortcutHandler> shortcutsHandlerInstance;
std::unique_ptr<WindowHandler> windowHandlerInstance;
std::unique_ptr<IScreenHandler> screenHandlerInstance;
std::unique_ptr<FramerateManager> framerateManagerInstance;
std::unique_ptr<EventDispatcher> eventDispatcherInstance;
void handleCurrentEvent(SDL_Event &current);
void convertTouchToMouse(SDL_Event * current);
void fakeMoveCursor(float dx, float dy);
void fakeMouseButtonEventRelativeMode(bool down, bool right);
void handleEventKeyDown(SDL_Event & current);
void handleEventKeyUp(SDL_Event & current);
void handleEventMouseMotion(SDL_Event & current);
void handleEventMouseButtonDown(SDL_Event & current);
void handleEventMouseWheel(SDL_Event & current);
void handleEventTextInput(SDL_Event & current);
void handleEventTextEditing(SDL_Event & current);
void handleEventMouseButtonUp(SDL_Event & current);
void handleEventFingerMotion(SDL_Event & current);
void handleEventFingerDown(SDL_Event & current);
void handleEventFingerUp(SDL_Event & current);
std::unique_ptr<InputHandler> inputHandlerInstance;
public:
/// returns current position of mouse cursor, relative to vcmi window
const Point & getCursorPosition() const;
ShortcutHandler & shortcutsHandler();
FramerateManager & framerateManager();
EventDispatcher & eventDispatcher();
ShortcutHandler & shortcuts();
FramerateManager & framerate();
EventDispatcher & events();
InputHandler & input();
/// Returns current logical screen dimensions
/// May not match size of window if user has UI scaling different from 100%
@ -109,9 +89,6 @@ public:
void startTextInput(const Rect & where);
void stopTextInput();
/// moves mouse pointer into specified position inside vcmi window
void moveCursorToPosition(const Point & position);
IScreenHandler & screenHandler();
WindowHandler & windows();
@ -124,10 +101,6 @@ public:
IUpdateable *curInt;
bool multifinger;
bool isPointerRelativeMode;
float pointerSpeedMultiplier;
ui8 defActionsDef; //default auto actions
bool captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
std::list<CIntObject *> createdObj; //stack of objs being created
@ -146,9 +119,9 @@ public:
void breakEventHandling(); //current event won't be propagated anymore
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen
static bool amIGuiThread();
static void pushUserEvent(EUserEvent usercode);
static void pushUserEvent(EUserEvent usercode, void * userdata);
bool amIGuiThread();
void pushUserEvent(EUserEvent usercode);
void pushUserEvent(EUserEvent usercode, void * userdata);
CondSh<bool> * terminate_cond; // confirm termination
};

View File

@ -251,7 +251,7 @@ void CursorHandler::updateSpellcastCursor()
{
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.f;
frameTime += GH.framerate().getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame;
while (frameTime >= frameDisplayDuration)

View File

@ -51,14 +51,14 @@ void AEventsReceiver::activateEvents(ui16 what)
assert((what & GENERAL) || (activeState & GENERAL));
activeState |= GENERAL;
GH.eventDispatcher().handleElementActivate(this, what);
GH.events().handleElementActivate(this, what);
}
void AEventsReceiver::deactivateEvents(ui16 what)
{
if (what & GENERAL)
activeState &= ~GENERAL;
GH.eventDispatcher().handleElementDeActivate(this, what & activeState);
GH.events().handleElementDeActivate(this, what & activeState);
}
void AEventsReceiver::click(MouseButton btn, tribool down, bool previousState)

View File

@ -224,7 +224,7 @@ EShortcut InterfaceObjectConfigurable::readHotkey(const JsonNode & config) const
return EShortcut::NONE;
}
EShortcut result = GH.shortcutsHandler().findShortcut(config.String());
EShortcut result = GH.shortcuts().findShortcut(config.String());
if (result == EShortcut::NONE)
logGlobal->error("Invalid hotkey '%s' in interface configuration!", config.String());
return result;;

View File

@ -233,7 +233,7 @@ std::shared_ptr<CButton> CMenuEntry::createButton(CMenuScreen * parent, const Js
if(posy < 0)
posy = pos.h + posy;
EShortcut shortcut = GH.shortcutsHandler().findShortcut(button["shortcut"].String());
EShortcut shortcut = GH.shortcuts().findShortcut(button["shortcut"].String());
auto result = std::make_shared<CButton>(Point(posx, posy), button["name"].String(), help, command, shortcut);

View File

@ -98,8 +98,8 @@ void MapViewActions::handleSwipeMove(const Point & cursorPosition)
if(!swipeEnabled() && !GH.isMouseButtonPressed(MouseButton::MIDDLE))
return;
// on mobile platforms with enabled swipe any button is enough
if(swipeEnabled() && (!GH.isMouseButtonPressed() || GH.multifinger))
// on mobile platforms with enabled swipe we use left button
if(swipeEnabled() && !GH.isMouseButtonPressed(MouseButton::LEFT))
return;
if(!isSwiping)

View File

@ -13,7 +13,7 @@
#include "../../lib/CConfigHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/NotificationHandler.h"
#include "../eventsSDL/NotificationHandler.h"
#include "../gui/WindowHandler.h"
#include "CMT.h"
#include "SDL_Extensions.h"