diff --git a/dm/games/settings.tsv b/dm/games/settings.tsv index 70498b9..f688195 100644 --- a/dm/games/settings.tsv +++ b/dm/games/settings.tsv @@ -12,3 +12,4 @@ DM_OPMODE choice 1=Docked,0=Handheld DM_GAME_OPMODE always Console mode DM_LANG choice languages build:DM_LANG languages>1 Game language DM_SHOW_FPS toggle - build:DM_SHOW_FPS always Frame-rate counter DM_METAL_HUD toggle - 0 always Performance HUD +DM_TOUCH_HAPTICS toggle - 1 always Touch feedback Haptic clicks when pressing touch controls. diff --git a/dm/ios/Sources/DmGamepad.m b/dm/ios/Sources/DmGamepad.m index eb35e2f..f772fb3 100644 --- a/dm/ios/Sources/DmGamepad.m +++ b/dm/ios/Sources/DmGamepad.m @@ -105,6 +105,8 @@ static __weak DmGamepad *g_view; CAShapeLayer *_key[B_N]; CATextLayer *_label[B_N]; // one layer per key CAShapeLayer *_ring, *_knob; // the left stick (the right one is not drawn) CAShapeLayer *_swap; CATextLayer *_swapLabel; BOOL _swapHeld; + UIImpactFeedbackGenerator *_touchFeedback; // one short feedback per touch-control press edge + unsigned _hapticSticks; BOOL _stickShown; // the left stick was held at the last refresh BOOL _full, _tap; // the 3D layout / the Tap layout is current CALayer *_controls; // holds every control layer: hidden while a physical controller is connected (the view itself stays, for game touches) @@ -129,6 +131,11 @@ static BOOL fullOnly(int b) { return b == B_LS || b == B_RS || b >= B_LEFT; } - (instancetype)initWithFrame:(CGRect)f { if ((self = [super initWithFrame:f])) { self.multipleTouchEnabled = YES; self.opaque = NO; self.backgroundColor = UIColor.clearColor; + const char *feedback = getenv("DM_TOUCH_HAPTICS"); + if (!feedback || atoi(feedback)) { + _touchFeedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight]; + [_touchFeedback prepare]; + } _owner = [NSMapTable strongToStrongObjectsMapTable]; _faceOf = [NSMapTable strongToStrongObjectsMapTable]; static dispatch_once_t once; dispatch_once(&once, ^{ @@ -252,6 +259,12 @@ static CGFloat dist(CGPoint a, CGPoint b) { return hypot(a.x - b.x, a.y - b.y); ax[i * 2] = (int32_t)lrint(o.x * m * 32767); ax[i * 2 + 1] = (int32_t)lrint(-o.y * m * 32767); // host: up is positive; rounded so full tilt is exactly 32767 } BOOL changed = face != _faceBits || dpad != _dpadBits || btn != _btnBits || swap != _swapHeld; + uint64_t pressed = (face | dpad | btn) & ~(_faceBits | _dpadBits | _btnBits); + unsigned gripping = (_stick[0] ? 1u : 0) | (_stick[1] ? 2u : 0); + if (pressed || (swap && !_swapHeld) || (gripping & ~_hapticSticks)) { + [_touchFeedback impactOccurredWithIntensity:0.6]; [_touchFeedback prepare]; + } + _hapticSticks = gripping; _faceBits = face; _dpadBits = dpad; _btnBits = btn; _swapHeld = swap; pthread_mutex_lock(&g_mu); g_touch_btn = face | dpad | btn; g_touch_lx = ax[0]; g_touch_ly = ax[1]; g_touch_rx = ax[2]; g_touch_ry = ax[3];