| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 | 
							- /*
 
- * 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 "InputSourceGameController.h"
 
- #include "../gui/CGuiHandler.h"
 
- #include "../gui/CursorHandler.h"
 
- #include "../gui/EventDispatcher.h"
 
- #include "../gui/MouseButton.h"
 
- #include "../media/IMusicPlayer.h"
 
- #include "../media/ISoundPlayer.h"
 
- #include "../CMT.h"
 
- #include "../CPlayerInterface.h"
 
- #include "../CGameInfo.h"
 
- #include "../../lib/CConfigHandler.h"
 
- #include <SDL_events.h>
 
- #include <SDL_timer.h>
 
- #include <SDL_clipboard.h>
 
- InputHandler::InputHandler()
 
- 	: enableMouse(settings["input"]["enableMouse"].Bool())
 
- 	, enableTouch(settings["input"]["enableTouch"].Bool())
 
- 	, enableController(settings["input"]["enableController"].Bool())
 
- 	, currentInputMode(InputMode::KEYBOARD_AND_MOUSE)
 
- 	, mouseHandler(std::make_unique<InputSourceMouse>())
 
- 	, keyboardHandler(std::make_unique<InputSourceKeyboard>())
 
- 	, fingerHandler(std::make_unique<InputSourceTouch>())
 
- 	, textHandler(std::make_unique<InputSourceText>())
 
- 	, gameControllerHandler(std::make_unique<InputSourceGameController>())
 
- {
 
- }
 
- InputHandler::~InputHandler() = default;
 
- void InputHandler::handleCurrentEvent(const SDL_Event & current)
 
- {
 
- 	switch (current.type)
 
- 	{
 
- 		case SDL_KEYDOWN:
 
- 			setCurrentInputMode(InputMode::KEYBOARD_AND_MOUSE);
 
- 			keyboardHandler->handleEventKeyDown(current.key);
 
- 			return;
 
- 		case SDL_KEYUP:
 
- 			keyboardHandler->handleEventKeyUp(current.key);
 
- 			return;
 
- #ifndef VCMI_EMULATE_TOUCHSCREEN_WITH_MOUSE
 
- 		case SDL_MOUSEMOTION:
 
- 			if (enableMouse)
 
- 			{
 
- 				setCurrentInputMode(InputMode::KEYBOARD_AND_MOUSE);
 
- 				mouseHandler->handleEventMouseMotion(current.motion);
 
- 			}
 
- 			return;
 
- 		case SDL_MOUSEBUTTONDOWN:
 
- 			if (enableMouse)
 
- 			{
 
- 				setCurrentInputMode(InputMode::KEYBOARD_AND_MOUSE);
 
- 				mouseHandler->handleEventMouseButtonDown(current.button);
 
- 			}
 
- 			return;
 
- 		case SDL_MOUSEBUTTONUP:
 
- 			if (enableMouse)
 
- 				mouseHandler->handleEventMouseButtonUp(current.button);
 
- 			return;
 
- 		case SDL_MOUSEWHEEL:
 
- 			if (enableMouse)
 
- 				mouseHandler->handleEventMouseWheel(current.wheel);
 
- 			return;
 
- #endif
 
- 		case SDL_TEXTINPUT:
 
- 			textHandler->handleEventTextInput(current.text);
 
- 			return;
 
- 		case SDL_TEXTEDITING:
 
- 			textHandler->handleEventTextEditing(current.edit);
 
- 			return;
 
- 		case SDL_FINGERMOTION:
 
- 			if (enableTouch)
 
- 			{
 
- 				setCurrentInputMode(InputMode::TOUCH);
 
- 				fingerHandler->handleEventFingerMotion(current.tfinger);
 
- 			}
 
- 			return;
 
- 		case SDL_FINGERDOWN:
 
- 			if (enableTouch)
 
- 			{
 
- 				setCurrentInputMode(InputMode::TOUCH);
 
- 				fingerHandler->handleEventFingerDown(current.tfinger);
 
- 			}
 
- 			return;
 
- 		case SDL_FINGERUP:
 
- 			if (enableTouch)
 
- 				fingerHandler->handleEventFingerUp(current.tfinger);
 
- 			return;
 
- 		case SDL_CONTROLLERAXISMOTION:
 
- 			if (enableController)
 
- 			{
 
- 				setCurrentInputMode(InputMode::CONTROLLER);
 
- 				gameControllerHandler->handleEventAxisMotion(current.caxis);
 
- 			}
 
- 			return;
 
- 		case SDL_CONTROLLERBUTTONDOWN:
 
- 			if (enableController)
 
- 			{
 
- 				setCurrentInputMode(InputMode::CONTROLLER);
 
- 				gameControllerHandler->handleEventButtonDown(current.cbutton);
 
- 			}
 
- 			return;
 
- 		case SDL_CONTROLLERBUTTONUP:
 
- 			if (enableController)
 
- 				gameControllerHandler->handleEventButtonUp(current.cbutton);
 
- 			return;
 
- 	}
 
- }
 
- void InputHandler::setCurrentInputMode(InputMode modi)
 
- {
 
- 	if(currentInputMode != modi)
 
- 	{
 
- 		currentInputMode = modi;
 
- 		GH.events().dispatchInputModeChanged(modi);
 
- 	}
 
- }
 
- InputMode InputHandler::getCurrentInputMode()
 
- {
 
- 	return currentInputMode;
 
- }
 
- void InputHandler::copyToClipBoard(const std::string & text)
 
- {
 
- 	SDL_SetClipboardText(text.c_str());
 
- }
 
- std::vector<SDL_Event> InputHandler::acquireEvents()
 
- {
 
- 	boost::unique_lock<boost::mutex> lock(eventsMutex);
 
- 	std::vector<SDL_Event> result;
 
- 	std::swap(result, eventsQueue);
 
- 	return result;
 
- }
 
- void InputHandler::processEvents()
 
- {
 
- 	std::vector<SDL_Event> eventsToProcess = acquireEvents();
 
- 	for(const auto & currentEvent : eventsToProcess)
 
- 		handleCurrentEvent(currentEvent);
 
- 	gameControllerHandler->handleUpdate();
 
- 	fingerHandler->handleUpdate();
 
- }
 
- bool InputHandler::ignoreEventsUntilInput()
 
- {
 
- 	bool inputFound = false;
 
- 	boost::unique_lock<boost::mutex> lock(eventsMutex);
 
- 	for(const auto & event : eventsQueue)
 
- 	{
 
- 		switch(event.type)
 
- 		{
 
- 			case SDL_MOUSEBUTTONDOWN:
 
- 			case SDL_FINGERDOWN:
 
- 			case SDL_KEYDOWN:
 
- 			case SDL_CONTROLLERBUTTONDOWN:
 
- 				inputFound = true;
 
- 		}
 
- 	}
 
- 	eventsQueue.clear();
 
- 	return inputFound;
 
- }
 
- void InputHandler::preprocessEvent(const SDL_Event & ev)
 
- {
 
- 	if(ev.type == SDL_QUIT)
 
- 	{
 
- 		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- #ifdef VCMI_ANDROID
 
- 		handleQuit(false);
 
- #else
 
- 		handleQuit(true);
 
- #endif
 
- 		return;
 
- 	}
 
- 	else if(ev.type == SDL_KEYDOWN)
 
- 	{
 
- 		if(ev.key.keysym.sym == SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT))
 
- 		{
 
- 			// FIXME: dead code? Looks like intercepted by OS/SDL and delivered as SDL_Quit instead?
 
- 			boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 			handleQuit(true);
 
- 			return;
 
- 		}
 
- 		if(ev.key.keysym.scancode == SDL_SCANCODE_AC_BACK)
 
- 		{
 
- 			boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 			handleQuit(true);
 
- 			return;
 
- 		}
 
- 	}
 
- 	else if(ev.type == SDL_USEREVENT)
 
- 	{
 
- 		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 		handleUserEvent(ev.user);
 
- 		return;
 
- 	}
 
- 	else if(ev.type == SDL_WINDOWEVENT)
 
- 	{
 
- 		switch (ev.window.event) {
 
- 			case SDL_WINDOWEVENT_RESTORED:
 
- #ifndef VCMI_IOS
 
- 			{
 
- 				boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 				GH.onScreenResize(false);
 
- 			}
 
- #endif
 
- 				break;
 
- 			case SDL_WINDOWEVENT_FOCUS_GAINED:
 
- 			{
 
- 				boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 				if(settings["general"]["audioMuteFocus"].Bool()) {
 
- 					CCS->musich->setVolume(settings["general"]["music"].Integer());
 
- 					CCS->soundh->setVolume(settings["general"]["sound"].Integer());
 
- 				}
 
- 			}
 
- 				break;
 
- 			case SDL_WINDOWEVENT_FOCUS_LOST:
 
- 			{
 
- 				boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 				if(settings["general"]["audioMuteFocus"].Bool()) {
 
- 					CCS->musich->setVolume(0);
 
- 					CCS->soundh->setVolume(0);
 
- 				}
 
- 			}
 
- 				break;
 
- 		}
 
- 		return;
 
- 	}
 
- 	else if(ev.type == SDL_SYSWMEVENT)
 
- 	{
 
- 		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 		if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
 
- 		{
 
- 			NotificationHandler::handleSdlEvent(ev);
 
- 		}
 
- 	}
 
- 	else if(ev.type == SDL_CONTROLLERDEVICEADDED)
 
- 	{
 
- 		gameControllerHandler->handleEventDeviceAdded(ev.cdevice);
 
- 		return;
 
- 	}
 
- 	else if(ev.type == SDL_CONTROLLERDEVICEREMOVED)
 
- 	{
 
- 		gameControllerHandler->handleEventDeviceRemoved(ev.cdevice);
 
- 		return;
 
- 	}
 
- 	else if(ev.type == SDL_CONTROLLERDEVICEREMAPPED)
 
- 	{
 
- 		gameControllerHandler->handleEventDeviceRemapped(ev.cdevice);
 
- 		return;
 
- 	}
 
- 	//preprocessing
 
- 	if(ev.type == SDL_MOUSEMOTION)
 
- 	{
 
- 		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
 
- 		if (CCS && CCS->curh)
 
- 			CCS->curh->cursorMove(ev.motion.x, ev.motion.y);
 
- 	}
 
- 	{
 
- 		boost::unique_lock<boost::mutex> lock(eventsMutex);
 
- 		// In a sequence of 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 (!eventsQueue.empty())
 
- 		{
 
- 			const SDL_Event & prev = eventsQueue.back();
 
- 			if(ev.type == SDL_MOUSEMOTION && prev.type == SDL_MOUSEMOTION)
 
- 			{
 
- 				SDL_Event accumulated = ev;
 
- 				accumulated.motion.xrel += prev.motion.xrel;
 
- 				accumulated.motion.yrel += prev.motion.yrel;
 
- 				eventsQueue.back() = accumulated;
 
- 				return;
 
- 			}
 
- 			if(ev.type == SDL_FINGERMOTION && prev.type == SDL_FINGERMOTION && ev.tfinger.fingerId == prev.tfinger.fingerId)
 
- 			{
 
- 				SDL_Event accumulated = ev;
 
- 				accumulated.tfinger.dx += prev.tfinger.dx;
 
- 				accumulated.tfinger.dy += prev.tfinger.dy;
 
- 				eventsQueue.back() = accumulated;
 
- 				return;
 
- 			}
 
- 		}
 
- 		eventsQueue.push_back(ev);
 
- 	}
 
- }
 
- void InputHandler::fetchEvents()
 
- {
 
- 	SDL_Event ev;
 
- 	while(1 == SDL_PollEvent(&ev))
 
- 	{
 
- 		preprocessEvent(ev);
 
- 	}
 
- }
 
- bool InputHandler::isKeyboardCmdDown() const
 
- {
 
- 	return keyboardHandler->isKeyboardCmdDown();
 
- }
 
- bool InputHandler::isKeyboardCtrlDown() const
 
- {
 
- 	return keyboardHandler->isKeyboardCtrlDown();
 
- }
 
- bool InputHandler::isKeyboardAltDown() const
 
- {
 
- 	return keyboardHandler->isKeyboardAltDown();
 
- }
 
- bool InputHandler::isKeyboardShiftDown() const
 
- {
 
- 	return keyboardHandler->isKeyboardShiftDown();
 
- }
 
- void InputHandler::moveCursorPosition(const Point & distance)
 
- {
 
- 	setCursorPosition(getCursorPosition() + distance);
 
- }
 
- void InputHandler::setCursorPosition(const Point & position)
 
- {
 
- 	cursorPosition = position;
 
- 	GH.events().dispatchMouseMoved(Point(0, 0), position);
 
- }
 
- void InputHandler::startTextInput(const Rect & where)
 
- {
 
- 	textHandler->startTextInput(where);
 
- }
 
- void InputHandler::stopTextInput()
 
- {
 
- 	textHandler->stopTextInput();
 
- }
 
- void InputHandler::hapticFeedback()
 
- {
 
- 	if(currentInputMode == InputMode::TOUCH)
 
- 		fingerHandler->hapticFeedback();
 
- }
 
- uint32_t InputHandler::getTicks()
 
- {
 
- 	return SDL_GetTicks();
 
- }
 
- bool InputHandler::hasTouchInputDevice() const
 
- {
 
- 	return fingerHandler->hasTouchInputDevice();
 
- }
 
- void InputHandler::dispatchMainThread(const std::function<void()> & functor)
 
- {
 
- 	auto heapFunctor = new std::function<void()>(functor);
 
- 	SDL_Event event;
 
- 	event.user.type = SDL_USEREVENT;
 
- 	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)();
 
- 	delete heapFunctor;
 
- }
 
- const Point & InputHandler::getCursorPosition() const
 
- {
 
- 	return cursorPosition;
 
- }
 
 
  |