| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 | 
							- /*
 
- * 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 "../CGameInfo.h"
 
- #include "../gui/CursorHandler.h"
 
- #include "../gui/CGuiHandler.h"
 
- #include "../gui/EventDispatcher.h"
 
- #include "../gui/MouseButton.h"
 
- #include "../gui/WindowHandler.h"
 
- #include "../render/IScreenHandler.h"
 
- #include "../CServerHandler.h"
 
- #include "../globalLobby/GlobalLobbyClient.h"
 
- #if defined(VCMI_ANDROID)
 
- #include "../../lib/CAndroidVMHelper.h"
 
- #elif defined(VCMI_IOS)
 
- #include "../ios/utils.h"
 
- #endif
 
- #include <SDL_events.h>
 
- #include <SDL_hints.h>
 
- #include <SDL_timer.h>
 
- InputSourceTouch::InputSourceTouch()
 
- 	: lastTapTimeTicks(0), lastLeftClickTimeTicks(0)
 
- {
 
- 	params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
 
- 	params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
 
- 	params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
 
- 	params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
 
- 	params.touchToleranceDistance = settings["input"]["touchToleranceDistance"].Float();
 
- 	if (params.useRelativeMode)
 
- 		state = TouchState::RELATIVE_MODE;
 
- 	else
 
- 		state = TouchState::IDLE;
 
- #ifdef VCMI_EMULATE_TOUCHSCREEN_WITH_MOUSE
 
- 	SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1");
 
- #else
 
- 	SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
 
- #endif
 
- 	SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
 
- }
 
- void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfinger)
 
- {
 
- 	if (CCS && CCS->curh && settings["video"]["cursor"].String() == "software" && state != TouchState::RELATIVE_MODE)
 
- 		CCS->curh->cursorMove(GH.getCursorPosition().x, GH.getCursorPosition().y);
 
- 	switch(state)
 
- 	{
 
- 		case TouchState::RELATIVE_MODE:
 
- 		{
 
- 			Point screenSize = GH.screenDimensions();
 
- 			int scalingFactor = GH.screenHandler().getScalingFactor();
 
- 			Point moveDistance {
 
- 				static_cast<int>(screenSize.x * params.relativeModeSpeedFactor * tfinger.dx),
 
- 				static_cast<int>(screenSize.y * params.relativeModeSpeedFactor * tfinger.dy)
 
- 			};
 
- 			GH.input().moveCursorPosition(moveDistance);
 
- 			if (CCS && CCS->curh)
 
- 				CCS->curh->cursorMove(GH.getCursorPosition().x * scalingFactor, GH.getCursorPosition().y * scalingFactor);
 
- 			break;
 
- 		}
 
- 		case TouchState::IDLE:
 
- 		{
 
- 			// no-op, might happen in some edge cases, e.g. when fingerdown event was ignored
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_SHORT:
 
- 		case TouchState::TAP_DOWN_LONG_AWAIT:
 
- 		{
 
- 			Point distance = convertTouchToMouse(tfinger) - lastTapPosition;
 
- 			if ( std::abs(distance.x) > params.panningSensitivityThreshold || std::abs(distance.y) > params.panningSensitivityThreshold)
 
- 			{
 
- 				state = state == TouchState::TAP_DOWN_SHORT ? TouchState::TAP_DOWN_PANNING : TouchState::TAP_DOWN_PANNING_POPUP;
 
- 				GH.events().dispatchGesturePanningStarted(lastTapPosition);
 
- 			}
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_PANNING:
 
- 		case TouchState::TAP_DOWN_PANNING_POPUP:
 
- 		{
 
- 			emitPanningEvent(tfinger);
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_DOUBLE:
 
- 		{
 
- 			emitPinchEvent(tfinger);
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_LONG:
 
- 		{
 
- 			// no-op
 
- 			break;
 
- 		}
 
- 	}
 
- }
 
- void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinger)
 
- {
 
- 	// FIXME: better place to update potentially changed settings?
 
- 	params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
 
- 	params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
 
- 	lastTapTimeTicks = tfinger.timestamp;
 
- 	switch(state)
 
- 	{
 
- 		case TouchState::RELATIVE_MODE:
 
- 		{
 
- 			if(tfinger.x > 0.5)
 
- 			{
 
- 				if (tfinger.y < 0.5)
 
- 					GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
 
- 				else
 
- 					GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition(), params.touchToleranceDistance);
 
- 			}
 
- 			break;
 
- 		}
 
- 		case TouchState::IDLE:
 
- 		{
 
- 			lastTapPosition = convertTouchToMouse(tfinger);
 
- 			GH.input().setCursorPosition(lastTapPosition);
 
- 			state = TouchState::TAP_DOWN_SHORT;
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_SHORT:
 
- 		{
 
- 			GH.input().setCursorPosition(convertTouchToMouse(tfinger));
 
- 			GH.events().dispatchGesturePanningStarted(lastTapPosition);
 
- 			state = TouchState::TAP_DOWN_DOUBLE;
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_PANNING:
 
- 		{
 
- 			GH.input().setCursorPosition(convertTouchToMouse(tfinger));
 
- 			state = TouchState::TAP_DOWN_DOUBLE;
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_DOUBLE:
 
- 		{
 
- 			CSH->getGlobalLobby().activateInterface();
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_LONG_AWAIT:
 
- 			lastTapPosition = convertTouchToMouse(tfinger);
 
- 			break;
 
- 		case TouchState::TAP_DOWN_LONG:
 
- 		case TouchState::TAP_DOWN_PANNING_POPUP:
 
- 		{
 
- 			// no-op
 
- 			break;
 
- 		}
 
- 	}
 
- }
 
- void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
 
- {
 
- 	switch(state)
 
- 	{
 
- 		case TouchState::RELATIVE_MODE:
 
- 		{
 
- 			if(tfinger.x > 0.5)
 
- 			{
 
- 				if (tfinger.y < 0.5)
 
- 					GH.events().dispatchClosePopup(GH.getCursorPosition());
 
- 				else
 
- 					GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition(), params.touchToleranceDistance);
 
- 			}
 
- 			break;
 
- 		}
 
- 		case TouchState::IDLE:
 
- 		{
 
- 			// no-op, might happen in some edge cases, e.g. when fingerdown event was ignored
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_SHORT:
 
- 		{
 
- 			GH.input().setCursorPosition(convertTouchToMouse(tfinger));
 
- 			if(tfinger.timestamp - lastLeftClickTimeTicks < params.doubleTouchTimeMilliseconds && (convertTouchToMouse(tfinger) - lastLeftClickPosition).length() < params.doubleTouchToleranceDistance)
 
- 			{
 
- 				GH.events().dispatchMouseDoubleClick(convertTouchToMouse(tfinger), params.touchToleranceDistance);
 
- 				GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
 
- 			}
 
- 			else
 
- 			{
 
- 				GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger), params.touchToleranceDistance);
 
- 				GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
 
- 				lastLeftClickTimeTicks = tfinger.timestamp;
 
- 				lastLeftClickPosition = convertTouchToMouse(tfinger);
 
- 			}
 
- 			state = TouchState::IDLE;
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_PANNING:
 
- 		case TouchState::TAP_DOWN_PANNING_POPUP:
 
- 		{
 
- 			GH.events().dispatchGesturePanningEnded(lastTapPosition, convertTouchToMouse(tfinger));
 
- 			state = state == TouchState::TAP_DOWN_PANNING ? TouchState::IDLE : TouchState::TAP_DOWN_LONG_AWAIT;
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_DOUBLE:
 
- 		{
 
- 			if (SDL_GetNumTouchFingers(tfinger.touchId) == 1)
 
- 				state = TouchState::TAP_DOWN_PANNING;
 
- 			if (SDL_GetNumTouchFingers(tfinger.touchId) == 0)
 
- 			{
 
- 				GH.events().dispatchGesturePanningEnded(lastTapPosition, convertTouchToMouse(tfinger));
 
- 				state = TouchState::IDLE;
 
- 			}
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_LONG:
 
- 		{
 
- 			if (SDL_GetNumTouchFingers(tfinger.touchId) == 0)
 
- 			{
 
- 				state = TouchState::TAP_DOWN_LONG_AWAIT;
 
- 			}
 
- 			break;
 
- 		}
 
- 		case TouchState::TAP_DOWN_LONG_AWAIT:
 
- 		{
 
- 			if (SDL_GetNumTouchFingers(tfinger.touchId) == 0)
 
- 			{
 
- 				GH.input().setCursorPosition(convertTouchToMouse(tfinger));
 
- 				GH.events().dispatchClosePopup(convertTouchToMouse(tfinger));
 
- 				state = TouchState::IDLE;
 
- 			}
 
- 			break;
 
- 		}
 
- 	}
 
- }
 
- void InputSourceTouch::handleUpdate()
 
- {
 
- 	if ( state == TouchState::TAP_DOWN_SHORT)
 
- 	{
 
- 		uint32_t currentTime = SDL_GetTicks();
 
- 		if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
 
- 		{
 
- 			GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
 
- 			if (GH.windows().isTopWindowPopup())
 
- 			{
 
- 				hapticFeedback();
 
- 				state = TouchState::TAP_DOWN_LONG;
 
- 			}
 
- 		}
 
- 	}
 
- }
 
- Point InputSourceTouch::convertTouchToMouse(const SDL_TouchFingerEvent & tfinger)
 
- {
 
- 	return convertTouchToMouse(tfinger.x, tfinger.y);
 
- }
 
- Point InputSourceTouch::convertTouchToMouse(float x, float y)
 
- {
 
- 	return Point(x * GH.screenDimensions().x, y * GH.screenDimensions().y);
 
- }
 
- bool InputSourceTouch::hasTouchInputDevice() const
 
- {
 
- 	return SDL_GetNumTouchDevices() > 0;
 
- }
 
- void InputSourceTouch::emitPanningEvent(const SDL_TouchFingerEvent & tfinger)
 
- {
 
- 	Point distance = convertTouchToMouse(-tfinger.dx, -tfinger.dy);
 
- 	GH.events().dispatchGesturePanning(lastTapPosition, convertTouchToMouse(tfinger), distance);
 
- }
 
- void InputSourceTouch::emitPinchEvent(const SDL_TouchFingerEvent & tfinger)
 
- {
 
- 	int fingers = SDL_GetNumTouchFingers(tfinger.touchId);
 
- 	if (fingers < 2)
 
- 		return;
 
- 	bool otherFingerFound = false;
 
- 	double otherX;
 
- 	double otherY;
 
- 	for (int i = 0; i < fingers; ++i)
 
- 	{
 
- 		SDL_Finger * finger = SDL_GetTouchFinger(tfinger.touchId, i);
 
- 		if (finger && finger->id != tfinger.fingerId)
 
- 		{
 
- 			otherX = finger->x * GH.screenDimensions().x;
 
- 			otherY = finger->y * GH.screenDimensions().y;
 
- 			otherFingerFound = true;
 
- 			break;
 
- 		}
 
- 	}
 
- 	if (!otherFingerFound)
 
- 		return; // should be impossible, but better to avoid weird edge cases
 
- 	float thisX = tfinger.x * GH.screenDimensions().x;
 
- 	float thisY = tfinger.y * GH.screenDimensions().y;
 
- 	float deltaX = tfinger.dx * GH.screenDimensions().x;
 
- 	float deltaY = tfinger.dy * GH.screenDimensions().y;
 
- 	float oldX = thisX - deltaX - otherX;
 
- 	float oldY = thisY - deltaY - otherY;
 
- 	float newX = thisX - otherX;
 
- 	float newY = thisY - otherY;
 
- 	double distanceOld = std::sqrt(oldX * oldX + oldY + oldY);
 
- 	double distanceNew = std::sqrt(newX * newX + newY + newY);
 
- 	if (distanceOld > params.pinchSensitivityThreshold)
 
- 		GH.events().dispatchGesturePinch(lastTapPosition, distanceNew / distanceOld);
 
- }
 
- void InputSourceTouch::hapticFeedback() {
 
- 	if(params.hapticFeedbackEnabled) {
 
- #if defined(VCMI_ANDROID)
 
-         CAndroidVMHelper vmHelper;
 
-         vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "hapticFeedback");
 
- #elif defined(VCMI_IOS)
 
-     	iOS_utils::hapticFeedback();
 
- #endif
 
- 	}
 
- }
 
 
  |