1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Merge pull request #2301 from Laserlicht/haptic_feedback

Haptic feedback
This commit is contained in:
Ivan Savenko
2023-07-13 12:56:54 +03:00
committed by GitHub
11 changed files with 77 additions and 3 deletions

View File

@@ -74,6 +74,8 @@
"vcmi.systemOptions.longTouchMenu.entry" : "%d milliseconds", "vcmi.systemOptions.longTouchMenu.entry" : "%d milliseconds",
"vcmi.systemOptions.framerateButton.hover" : "Show FPS", "vcmi.systemOptions.framerateButton.hover" : "Show FPS",
"vcmi.systemOptions.framerateButton.help" : "{Show FPS}\n\nToggle the visibility of the Frames Per Second counter in the corner of the game window", "vcmi.systemOptions.framerateButton.help" : "{Show FPS}\n\nToggle the visibility of the Frames Per Second counter in the corner of the game window",
"vcmi.systemOptions.hapticFeedbackButton.hover" : "Haptic feedback",
"vcmi.systemOptions.hapticFeedbackButton.help" : "{Haptic feedback}\n\nToggle the haptic feedback on touch inputs",
"vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel", "vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel",
"vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in the info panel, instead of popping up in a separate window.", "vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in the info panel, instead of popping up in a separate window.",

View File

@@ -74,6 +74,8 @@
"vcmi.systemOptions.longTouchMenu.entry" : "%d Millisekunden", "vcmi.systemOptions.longTouchMenu.entry" : "%d Millisekunden",
"vcmi.systemOptions.framerateButton.hover" : "FPS anzeigen", "vcmi.systemOptions.framerateButton.hover" : "FPS anzeigen",
"vcmi.systemOptions.framerateButton.help" : "{FPS anzeigen}\n\n Schaltet die Sichtbarkeit des Zählers für die Bilder pro Sekunde in der Ecke des Spielfensters um.", "vcmi.systemOptions.framerateButton.help" : "{FPS anzeigen}\n\n Schaltet die Sichtbarkeit des Zählers für die Bilder pro Sekunde in der Ecke des Spielfensters um.",
"vcmi.systemOptions.hapticFeedbackButton.hover" : "Haptisches Feedback",
"vcmi.systemOptions.hapticFeedbackButton.help" : "{Haptisches Feedback}\n\nHaptisches Feedback bei Touch-Eingaben.",
"vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen", "vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen",
"vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen", "vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen",

View File

@@ -3,6 +3,7 @@
package="eu.vcmi.vcmi"> package="eu.vcmi.vcmi">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<application <application
android:extractNativeLibs="true" android:extractNativeLibs="true"
@@ -50,4 +51,4 @@
android:exported="false"/> android:exported="false"/>
</application> </application>
</manifest> </manifest>

View File

@@ -3,11 +3,14 @@ package eu.vcmi.vcmi;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.VibrationEffect;
import android.os.Vibrator;
import org.libsdl.app.SDL; import org.libsdl.app.SDL;
import org.libsdl.app.SDLActivity; import org.libsdl.app.SDLActivity;
@@ -138,6 +141,17 @@ public class NativeMethods
{ {
internalProgressDisplay(false); internalProgressDisplay(false);
} }
@SuppressWarnings(Const.JNI_METHOD_SUPPRESS)
public static void hapticFeedback()
{
final Context ctx = SDL.getContext();
if (Build.VERSION.SDK_INT >= 26) {
((Vibrator) ctx.getSystemService(ctx.VIBRATOR_SERVICE)).vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
} else {
((Vibrator) ctx.getSystemService(ctx.VIBRATOR_SERVICE)).vibrate(30);
}
}
private static void internalProgressDisplay(final boolean show) private static void internalProgressDisplay(final boolean show)
{ {

View File

@@ -22,6 +22,12 @@
#include "../gui/MouseButton.h" #include "../gui/MouseButton.h"
#include "../gui/WindowHandler.h" #include "../gui/WindowHandler.h"
#if defined(VCMI_ANDROID)
#include "../../lib/CAndroidVMHelper.h"
#elif defined(VCMI_IOS)
#include "../ios/utils.h"
#endif
#include <SDL_events.h> #include <SDL_events.h>
#include <SDL_hints.h> #include <SDL_hints.h>
#include <SDL_timer.h> #include <SDL_timer.h>
@@ -32,6 +38,7 @@ InputSourceTouch::InputSourceTouch()
params.useRelativeMode = settings["general"]["userRelativePointer"].Bool(); params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float(); params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float(); params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
if (params.useRelativeMode) if (params.useRelativeMode)
state = TouchState::RELATIVE_MODE; state = TouchState::RELATIVE_MODE;
@@ -100,6 +107,7 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
{ {
// FIXME: better place to update potentially changed settings? // FIXME: better place to update potentially changed settings?
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float(); params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
lastTapTimeTicks = tfinger.timestamp; lastTapTimeTicks = tfinger.timestamp;
@@ -215,6 +223,7 @@ void InputSourceTouch::handleUpdate()
if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds) if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
{ {
GH.events().dispatchShowPopup(GH.getCursorPosition()); GH.events().dispatchShowPopup(GH.getCursorPosition());
hapticFeedback();
if (GH.windows().isTopWindowPopup()) if (GH.windows().isTopWindowPopup())
state = TouchState::TAP_DOWN_LONG; state = TouchState::TAP_DOWN_LONG;
@@ -287,3 +296,14 @@ void InputSourceTouch::emitPinchEvent(const SDL_TouchFingerEvent & tfinger)
if (distanceOld > params.pinchSensitivityThreshold) if (distanceOld > params.pinchSensitivityThreshold)
GH.events().dispatchGesturePinch(lastTapPosition, distanceNew / distanceOld); 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
}
}

View File

@@ -79,6 +79,8 @@ struct TouchInputParameters
uint32_t pinchSensitivityThreshold = 10; uint32_t pinchSensitivityThreshold = 10;
bool useRelativeMode = false; bool useRelativeMode = false;
bool hapticFeedbackEnabled = false;
}; };
/// Class that handles touchscreen input from SDL events /// Class that handles touchscreen input from SDL events
@@ -94,6 +96,8 @@ class InputSourceTouch
void emitPanningEvent(const SDL_TouchFingerEvent & tfinger); void emitPanningEvent(const SDL_TouchFingerEvent & tfinger);
void emitPinchEvent(const SDL_TouchFingerEvent & tfinger); void emitPinchEvent(const SDL_TouchFingerEvent & tfinger);
void hapticFeedback();
public: public:
InputSourceTouch(); InputSourceTouch();

View File

@@ -15,4 +15,6 @@ double screenScale();
void showLoadingIndicator(); void showLoadingIndicator();
void hideLoadingIndicator(); void hideLoadingIndicator();
void hapticFeedback();
} }

View File

@@ -43,4 +43,10 @@ void hideLoadingIndicator()
[indicator removeFromSuperview]; [indicator removeFromSuperview];
indicator = nil; indicator = nil;
} }
void hapticFeedback()
{
auto hapticGen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
[hapticGen impactOccurred];
}
} }

View File

@@ -153,6 +153,10 @@ GeneralOptionsTab::GeneralOptionsTab()
{ {
setBoolSetting("video", "showfps", value); setBoolSetting("video", "showfps", value);
}); });
addCallback("hapticFeedbackChanged", [](bool value)
{
setBoolSetting("general", "hapticFeedback", value);
});
//moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content //moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content
addCallback("availableCreaturesAsDwellingChanged", [=](int value) addCallback("availableCreaturesAsDwellingChanged", [=](int value)
@@ -190,6 +194,10 @@ GeneralOptionsTab::GeneralOptionsTab()
std::shared_ptr<CToggleButton> framerateCheckbox = widget<CToggleButton>("framerateCheckbox"); std::shared_ptr<CToggleButton> framerateCheckbox = widget<CToggleButton>("framerateCheckbox");
framerateCheckbox->setSelected(settings["video"]["showfps"].Bool()); framerateCheckbox->setSelected(settings["video"]["showfps"].Bool());
std::shared_ptr<CToggleButton> hapticFeedbackCheckbox = widget<CToggleButton>("hapticFeedbackCheckbox");
if (hapticFeedbackCheckbox)
hapticFeedbackCheckbox->setSelected(settings["general"]["hapticFeedback"].Bool());
std::shared_ptr<CSlider> musicSlider = widget<CSlider>("musicSlider"); std::shared_ptr<CSlider> musicSlider = widget<CSlider>("musicSlider");
musicSlider->scrollTo(CCS->musich->getVolume()); musicSlider->scrollTo(CCS->musich->getVolume());

View File

@@ -33,7 +33,8 @@
"extraDump", "extraDump",
"userRelativePointer", "userRelativePointer",
"relativePointerSpeedMultiplier", "relativePointerSpeedMultiplier",
"longTouchTimeMilliseconds" "longTouchTimeMilliseconds",
"hapticFeedback"
], ],
"properties" : { "properties" : {
"playerName" : { "playerName" : {
@@ -101,6 +102,10 @@
"longTouchTimeMilliseconds" : { "longTouchTimeMilliseconds" : {
"type" : "number", "type" : "number",
"default" : 1000 "default" : 1000
},
"hapticFeedback" : {
"type" : "boolean",
"default" : false
} }
} }
}, },

View File

@@ -57,6 +57,10 @@
"name": "longTouchLabel", "name": "longTouchLabel",
"text": "vcmi.systemOptions.longTouchButton.hover", "text": "vcmi.systemOptions.longTouchButton.hover",
"created" : "touchscreen" "created" : "touchscreen"
},
{
"text": "vcmi.systemOptions.hapticFeedbackButton.hover",
"created" : "mobile"
} }
] ]
}, },
@@ -76,7 +80,7 @@
"name": "scalingButton", "name": "scalingButton",
"type": "buttonGear", "type": "buttonGear",
"help": "vcmi.systemOptions.scalingButton", "help": "vcmi.systemOptions.scalingButton",
"callback": "setGameScaling", "callback": "setGameScaling"
}, },
{ {
"name": "fullscreenBorderlessCheckbox", "name": "fullscreenBorderlessCheckbox",
@@ -106,6 +110,12 @@
"help": "vcmi.systemOptions.longTouchButton", "help": "vcmi.systemOptions.longTouchButton",
"callback": "setLongTouchDuration", "callback": "setLongTouchDuration",
"created" : "touchscreen" "created" : "touchscreen"
},
{
"name": "hapticFeedbackCheckbox",
"help": "vcmi.systemOptions.hapticFeedbackButton",
"callback": "hapticFeedbackChanged",
"created" : "mobile"
} }
] ]
}, },