diff --git a/client/CMT.cpp b/client/CMT.cpp index bdda4da9b..828ef9ae8 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -374,7 +374,8 @@ int main(int argc, char * argv[]) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); #endif // VCMI_ANDROID - GH.mainFPSmng->init(); //(!)init here AFTER SDL_Init() while using SDL for FPS management + //(!)init here AFTER SDL_Init() while using SDL for FPS management + GH.init(); SDL_LogSetOutputFunction(&SDLLogCallback, nullptr); @@ -430,11 +431,20 @@ int main(int argc, char * argv[]) CCS->musich->setVolume((ui32)settings["general"]["music"].Float()); logGlobal->info("Initializing screen and sound handling: %d ms", pomtime.getDiff()); } + #ifdef VCMI_MAC // Ctrl+click should be treated as a right click on Mac OS X SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); #endif +#ifdef SDL_HINT_MOUSE_TOUCH_EVENTS + if(GH.isPointerRelativeMode) + { + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + } +#endif + #ifndef VCMI_NO_THREADED_LOAD //we can properly play intro only in the main thread, so we have to move loading to the separate thread boost::thread loading(init); diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 926ee3e6e..68f9468db 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -78,6 +78,13 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::functioninit(); + isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool(); + pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float(); +} + void CGuiHandler::handleElementActivate(CIntObject * elem, ui16 activityFlag) { processLists(activityFlag,[&](std::list * lst){ @@ -206,7 +213,7 @@ void CGuiHandler::handleEvents() } } -void convertTouch(SDL_Event * current) +void CGuiHandler::convertTouchToMouse(SDL_Event * current) { int rLogicalWidth, rLogicalHeight; @@ -221,6 +228,63 @@ void convertTouch(SDL_Event * current) 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->xpos + (int)(GH.pointerSpeedMultiplier * w * dx); + sme.y = CCS->curh->ypos + (int)(GH.pointerSpeedMultiplier * h * dy); + + vstd::abetween(sme.x, 0, w); + vstd::abetween(sme.y, 0, h); + + event.motion = sme; + SDL_PushEvent(&event); +} + +void CGuiHandler::fakeMouseMove() +{ + fakeMoveCursor(0, 0); +} + +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->xpos; + sme.y = CCS->curh->ypos; + + 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); + SDL_WarpMouse( + (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() { if(current->type == SDL_KEYDOWN || current->type == SDL_KEYUP) @@ -371,33 +435,60 @@ void CGuiHandler::handleCurrentEvent() } } } -#ifndef VCMI_IOS + else if(current->type == SDL_FINGERMOTION) + { + if(isPointerRelativeMode) + { + fakeMoveCursor(current->tfinger.dx, current->tfinger.dy); + } + } else if(current->type == SDL_FINGERDOWN) { auto fingerCount = SDL_GetNumTouchFingers(current->tfinger.touchId); multifinger = fingerCount > 1; - if(fingerCount == 2) + if(isPointerRelativeMode) { - convertTouch(current); + 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); handleMouseMotion(); handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, true); } +#endif //VCMI_IOS } else if(current->type == SDL_FINGERUP) { auto fingerCount = SDL_GetNumTouchFingers(current->tfinger.touchId); - if(multifinger) + if(isPointerRelativeMode) { - convertTouch(current); + if(current->tfinger.x > 0.5) + { + bool isRightClick = current->tfinger.y < 0.5; + + fakeMouseButtonEventRelativeMode(false, isRightClick); + } + } +#ifndef VCMI_IOS + else if(multifinger) + { + convertTouchToMouse(current); handleMouseMotion(); handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, false); multifinger = fingerCount != 0; } - } #endif //VCMI_IOS + } current = nullptr; } //event end @@ -471,20 +562,6 @@ void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion) } } -void CGuiHandler::fakeMouseMove() -{ - SDL_Event event; - SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0}; - int x, y; - - sme.state = SDL_GetMouseState(&x, &y); - sme.x = x; - sme.y = y; - - event.motion = sme; - SDL_PushEvent(&event); -} - void CGuiHandler::renderFrame() { diff --git a/client/gui/CGuiHandler.h b/client/gui/CGuiHandler.h index d9aa2068d..4baac9932 100644 --- a/client/gui/CGuiHandler.h +++ b/client/gui/CGuiHandler.h @@ -88,6 +88,10 @@ private: void handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed); void processLists(const ui16 activityFlag, std::function *)> cb); + void convertTouchToMouse(SDL_Event * current); + void fakeMoveCursor(float dx, float dy); + void fakeMouseButtonEventRelativeMode(bool down, bool right); + public: void handleElementActivate(CIntObject * elem, ui16 activityFlag); void handleElementDeActivate(CIntObject * elem, ui16 activityFlag); @@ -102,6 +106,8 @@ public: Point lastClick; unsigned lastClickTime; 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 @@ -110,6 +116,7 @@ public: CGuiHandler(); ~CGuiHandler(); + void init(); void renderFrame(); void totalRedraw(); //forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 9baa638c6..0a774bd1a 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -17,7 +17,20 @@ "type" : "object", "default": {}, "additionalProperties" : false, - "required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe", "saveRandomMaps", "saveFrequency", "notifications", "extraDump" ], + "required" : [ + "playerName", + "showfps", + "music", + "sound", + "encoding", + "swipe", + "saveRandomMaps", + "saveFrequency", + "notifications", + "extraDump", + "userRelativePointer", + "relativePointerSpeedMultiplier" + ], "properties" : { "playerName" : { "type":"string", @@ -70,6 +83,14 @@ "extraDump" : { "type" : "boolean", "default" : false + }, + "userRelativePointer" : { + "type" : "boolean", + "default" : false + }, + "relativePointerSpeedMultiplier" : { + "type" : "number", + "default" : 1 } } },