dualSense emulation beta ready (works with Sunshine-Foundation)
This commit is contained in:
@@ -264,7 +264,7 @@
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="LGh-Lc-Odp" userLabel="emulatedControllerTypeStack">
|
||||
<rect key="frame" x="440" y="973" width="380" height="56.5"/>
|
||||
<rect key="frame" x="440" y="924" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Emulated Controller Type" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JTl-un-QJJ" userLabel="emulatedControllerTypeLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
@@ -272,12 +272,13 @@
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="JaY-XA-1V6" userLabel="emulatedControllerTypeSelector">
|
||||
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="JaY-XA-1V6" userLabel="emulatedControllerTypeSelector">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
|
||||
<segments>
|
||||
<segment title="Xbox 360"/>
|
||||
<segment title="DS4"/>
|
||||
<segment title="Both"/>
|
||||
<segment title="PS"/>
|
||||
<segment title="PS (Enhanced Haptics)"/>
|
||||
<segment title="Xbox + PS"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="selectedSegmentTintColor" systemColor="linkColor"/>
|
||||
@@ -414,37 +415,37 @@
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-StK" userLabel="interpolationLevel Stack">
|
||||
<rect key="frame" x="440" y="1350" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Interpolation Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-Lbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Interpolation Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-Lbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="17.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-Sel" userLabel="Interpolation Level Slider">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-Sel" userLabel="Interpolation Level Slider">
|
||||
<rect key="frame" x="0.0" y="22.5" width="380" height="34"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="380" id="FIp-Lv-Wid"/>
|
||||
<constraint firstAttribute="height" constant="56.5" id="FIp-Lv-Hgt"/>
|
||||
<constraint firstAttribute="width" constant="380" id="FIp-Lv-Wid"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-StK" userLabel="streamDimensionScale Stack">
|
||||
<rect key="frame" x="440" y="1411" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scaled Stream Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-Lbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Scaled Stream Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-Lbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="17.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-Sld" userLabel="Stream Dimension Scale Slider">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-Sld" userLabel="Stream Dimension Scale Slider">
|
||||
<rect key="frame" x="0.0" y="22.5" width="380" height="34"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="380" id="FIp-Sc-Wid"/>
|
||||
<constraint firstAttribute="height" constant="56.5" id="FIp-Sc-Hgt"/>
|
||||
<constraint firstAttribute="width" constant="380" id="FIp-Sc-Wid"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="hcl-DT-peO" userLabel="redirectMicStack">
|
||||
@@ -1078,48 +1079,8 @@
|
||||
<constraint firstAttribute="width" constant="380" id="qOk-ee-a8A"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="A31-WC-As7">
|
||||
<rect key="frame" x="441" y="1363" width="280" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable Timebase" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cpg-mq-kYj" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="130" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="StG-nI-WpK">
|
||||
<rect key="frame" x="150" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="lFV-eP-bfD"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="seO-0F-yIw"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="e0Q-XV-ydj">
|
||||
<rect key="frame" x="441" y="1402" width="322" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Async Frame Dequeue" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X0u-7f-F86" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="172" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="auR-cw-77D">
|
||||
<rect key="frame" x="192" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="mZn-6A-4OO"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="UK8-ck-1zc"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="ydX-NT-gG0">
|
||||
<rect key="frame" x="441" y="1441" width="383" height="31"/>
|
||||
<rect key="frame" x="437" y="1480" width="383" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SDR Performance Workaround" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WWl-77-twB" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="233" height="31"/>
|
||||
@@ -2280,6 +2241,44 @@
|
||||
<constraint firstAttribute="height" constant="55.5" id="f4J-WQ-LBI"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="e0Q-XV-ydj">
|
||||
<rect key="frame" x="852" y="1300" width="322" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Async Frame Dequeue" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X0u-7f-F86" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="172" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="auR-cw-77D">
|
||||
<rect key="frame" x="192" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="mZn-6A-4OO"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="UK8-ck-1zc"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="xrc-Fe-ETh" userLabel="dualSenseTransientStack">
|
||||
<rect key="frame" x="440" y="998" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="DualSense Haptic Intensity" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UpM-yi-yc9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="17.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="2" translatesAutoresizingMaskIntoConstraints="NO" id="ZOE-dg-g4w" userLabel="dualSenseTransientSlider">
|
||||
<rect key="frame" x="0.0" y="22.5" width="380" height="34"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="56.5" id="aRM-XQ-Xxd"/>
|
||||
<constraint firstAttribute="width" constant="380" id="k8R-Co-bxO"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
@@ -2324,12 +2323,14 @@
|
||||
<outlet property="disablePencilSlideGestureSwitch" destination="lcb-Iq-B5C" id="NZY-fW-4Yd"/>
|
||||
<outlet property="doubleTapShortcutStack" destination="prr-Do-Nna" id="sK3-va-U5W"/>
|
||||
<outlet property="doubleTapShortcutSwitch" destination="cw7-Jc-7bZ" id="bRy-9x-0Tl"/>
|
||||
<outlet property="dualSenseTransientSlider" destination="ZOE-dg-g4w" id="J8t-pc-pBo"/>
|
||||
<outlet property="dualSenseTransientStack" destination="xrc-Fe-ETh" id="bAL-at-fHy"/>
|
||||
<outlet property="duckOtherAppStack" destination="gBG-8F-6F8" id="T9P-ys-pHB"/>
|
||||
<outlet property="duckOtherAppSwitch" destination="YEv-1B-97I" id="o3Q-yt-74a"/>
|
||||
<outlet property="edgeSlidingSensitivitySlider" destination="41s-BD-8rZ" id="ytk-Y5-Alq"/>
|
||||
<outlet property="edgeSlidingSensitivityStack" destination="CVS-fB-e0q" id="tGR-K2-Jwq"/>
|
||||
<outlet property="emulatedControllerTypeSelector" destination="JaY-XA-1V6" id="aU2-ps-esq"/>
|
||||
<outlet property="emulatedControllerTypeStack" destination="LGh-Lc-Odp" id="Fny-rC-9zP"/>
|
||||
<outlet property="emulatedControllerTypeStack" destination="LGh-Lc-Odp" id="MHo-66-ltO"/>
|
||||
<outlet property="enableGraphsSwitch" destination="mhH-ZA-Kqg" id="R0X-hj-NgM"/>
|
||||
<outlet property="enableOswForNativeTouchSwitch" destination="Zmq-Kv-Nmp" id="f6j-0V-ldT"/>
|
||||
<outlet property="enableOswSwitchStack" destination="SkS-ft-peB" id="n3M-EK-U74"/>
|
||||
@@ -2338,15 +2339,9 @@
|
||||
<outlet property="fpsStack" destination="TuR-H8-e2H" id="zGQ-gR-P1i"/>
|
||||
<outlet property="framePacingModeSelector" destination="2Dp-Qg-chi" id="h8A-YF-SZP"/>
|
||||
<outlet property="framePacingStack" destination="oZG-5Y-pw5" id="vGd-hD-zTr"/>
|
||||
<outlet property="interpolationLevelSlider" destination="FIp-Lv-Sel" id="FIp-Out-Sel"/>
|
||||
<outlet property="interpolationLevelStack" destination="FIp-Lv-StK" id="FIp-Out-StK"/>
|
||||
<outlet property="streamDimensionScaleSlider" destination="FIp-Sc-Sld" id="FIp-Out-ScS"/>
|
||||
<outlet property="streamDimensionScaleStack" destination="FIp-Sc-StK" id="FIp-Out-ScK"/>
|
||||
<outlet property="frameQueueSizeLabel" destination="Ce3-FM-vIh" id="90D-Tq-OGd"/>
|
||||
<outlet property="frameQueueSizeSlider" destination="oZa-8Y-icz" id="Jo1-Fx-UMw"/>
|
||||
<outlet property="frameQueueSizeStack" destination="MML-c1-D5j" id="82B-r7-Zbh"/>
|
||||
<outlet property="frameTimebaseStack" destination="A31-WC-As7" id="vcp-SE-PuI"/>
|
||||
<outlet property="frameTimebaseSwitch" destination="StG-nI-WpK" id="yYY-DO-U1D"/>
|
||||
<outlet property="framerateSelector" destination="8hF-kb-2uV" id="dfT-hb-csE"/>
|
||||
<outlet property="fullColorRangeStack" destination="iPd-Rg-St7" id="iPd-Out-Let"/>
|
||||
<outlet property="fullColorRangeSwitch" destination="iPd-Wt-Ch9" id="iPd-Out-Swt"/>
|
||||
@@ -2367,6 +2362,8 @@
|
||||
<outlet property="hapticEngineStack" destination="MUp-Hc-VgH" id="XuW-ww-cmv"/>
|
||||
<outlet property="hdrStack" destination="dH7-Sy-EdJ" id="cJm-bj-Aqo"/>
|
||||
<outlet property="hdrSwitch" destination="wIq-fF-X8l" id="Ozz-h0-nJL"/>
|
||||
<outlet property="interpolationLevelSlider" destination="FIp-Lv-Sel" id="FIp-Out-Sel"/>
|
||||
<outlet property="interpolationLevelStack" destination="FIp-Lv-StK" id="FIp-Out-StK"/>
|
||||
<outlet property="leftClickDelaySlider" destination="Mh4-1P-bSv" id="Tbj-Y0-t1x"/>
|
||||
<outlet property="leftClickDelayStack" destination="q0S-CP-V6v" id="0lz-MN-OEb"/>
|
||||
<outlet property="leftStickMinOffsetSlider" destination="KtJ-qH-KJX" id="vIV-4l-gbA"/>
|
||||
@@ -2468,6 +2465,8 @@
|
||||
<outlet property="squeezeShortcutSwitch" destination="VCg-Es-PQV" id="kgj-FT-fcZ"/>
|
||||
<outlet property="statsOverlaySelector" destination="NLH-qN-qCo" id="EF8-JF-O3S"/>
|
||||
<outlet property="statsOverlayStack" destination="UcP-0b-nJD" id="UdI-NS-O6k"/>
|
||||
<outlet property="streamDimensionScaleSlider" destination="FIp-Sc-Sld" id="FIp-Out-ScS"/>
|
||||
<outlet property="streamDimensionScaleStack" destination="FIp-Sc-StK" id="FIp-Out-ScK"/>
|
||||
<outlet property="streamingRadialMenuDelaySlider" destination="qZX-tG-Pwf" id="V5Z-kn-irq"/>
|
||||
<outlet property="streamingRadialMenuDelayStack" destination="xXr-dB-QWZ" id="I9M-3Y-HGg"/>
|
||||
<outlet property="swapAbxyStack" destination="2rP-Ui-wVU" id="gUR-Vz-ORP"/>
|
||||
@@ -3978,19 +3977,19 @@
|
||||
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
|
||||
<image name="video.and.waveform" catalog="system" width="128" height="94"/>
|
||||
<systemColor name="linkColor">
|
||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBrownColor">
|
||||
<color red="0.67450980392156867" green="0.49803921568627452" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.63529411759999999" green="0.51764705879999995" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGray3Color">
|
||||
<color red="0.7803921568627451" green="0.7803921568627451" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.78039215689999997" green="0.78039215689999997" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemTealColor">
|
||||
<color red="0.0" green="0.76470588235294112" blue="0.81568627450980391" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -2405,12 +2405,13 @@
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="q6v-Jb-ysy" userLabel="emulatedControllerTypeSelector">
|
||||
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="q6v-Jb-ysy" userLabel="emulatedControllerTypeSelector">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
|
||||
<segments>
|
||||
<segment title="Xbox 360"/>
|
||||
<segment title="DS4"/>
|
||||
<segment title="Both"/>
|
||||
<segment title="PS"/>
|
||||
<segment title="PS (Enhanced Haptics)"/>
|
||||
<segment title="Xbox + PS"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="selectedSegmentTintColor" systemColor="linkColor"/>
|
||||
@@ -2551,37 +2552,37 @@
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-StK" userLabel="interpolationLevel Stack">
|
||||
<rect key="frame" x="456" y="2232" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Interpolation Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-Lbl">
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Interpolation Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-Lbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-Sel" userLabel="Interpolation Level Slider">
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-Sel" userLabel="Interpolation Level Slider">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="380" id="FIi-Lv-Wid"/>
|
||||
<constraint firstAttribute="height" constant="56.5" id="FIi-Lv-Hgt"/>
|
||||
<constraint firstAttribute="width" constant="380" id="FIi-Lv-Wid"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-StK" userLabel="streamDimensionScale Stack">
|
||||
<rect key="frame" x="456" y="2293" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scaled Stream Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-Lbl">
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Scaled Stream Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-Lbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-Sld" userLabel="Stream Dimension Scale Slider">
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-Sld" userLabel="Stream Dimension Scale Slider">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="380" id="FIi-Sc-Wid"/>
|
||||
<constraint firstAttribute="height" constant="56.5" id="FIi-Sc-Hgt"/>
|
||||
<constraint firstAttribute="width" constant="380" id="FIi-Sc-Wid"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="CKa-yk-GSi" userLabel="redirectMicStack">
|
||||
@@ -3127,28 +3128,8 @@
|
||||
<constraint firstAttribute="height" constant="56.5" id="MNC-Cn-lnb"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="HCc-SK-NBk">
|
||||
<rect key="frame" x="459" y="2252" width="280" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable Timebase" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="P4N-i6-1zD" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="130" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bwf-wJ-EC2">
|
||||
<rect key="frame" x="150" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="KUb-Mq-m9s"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="qzI-PN-1ml"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="dZ3-os-MKX">
|
||||
<rect key="frame" x="459" y="2291" width="312" height="31"/>
|
||||
<rect key="frame" x="455" y="2380" width="312" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Async Frame Deueue" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e1n-pw-04X" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="162" height="31"/>
|
||||
@@ -3167,26 +3148,6 @@
|
||||
<constraint firstAttribute="height" constant="31" id="O8n-y9-TrQ"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="Rw1-5R-Jjf" userLabel="SDR Performance Workaround Stack">
|
||||
<rect key="frame" x="459" y="2330" width="383" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SDR Performance Workaround" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F8d-OQ-PsH" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="233" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Y5l-G9-PyG">
|
||||
<rect key="frame" x="253" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="Qwc-Ko-5ZE"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="abg-h5-3Ml"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="fqe-49-wG1">
|
||||
<rect key="frame" x="15" y="1643" width="309.5" height="31"/>
|
||||
<subviews>
|
||||
@@ -3551,6 +3512,46 @@
|
||||
<constraint firstAttribute="width" constant="380" id="tSR-LD-qKK"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="Rw1-5R-Jjf" userLabel="SDR Performance Workaround Stack">
|
||||
<rect key="frame" x="734" y="2380" width="383" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SDR Performance Workaround" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F8d-OQ-PsH" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="233" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Y5l-G9-PyG">
|
||||
<rect key="frame" x="253" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="Qwc-Ko-5ZE"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="abg-h5-3Ml"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="7Ye-hW-K31" userLabel="dualSenseTransientStack">
|
||||
<rect key="frame" x="437" y="1540" width="380" height="55.5"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="DualSense Haptic Intensity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vak-fg-iAQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="2" translatesAutoresizingMaskIntoConstraints="NO" id="8fE-0o-N0V" userLabel="dualSenseTransientSlider">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="30"/>
|
||||
<color key="tintColor" systemColor="linkColor"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="55.5" id="8g8-0d-mPf"/>
|
||||
<constraint firstAttribute="width" constant="380" id="WTF-gm-sAY"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="0sf-mG-PCk"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -3591,6 +3592,8 @@
|
||||
<outlet property="customResolutionSwitch" destination="kRO-HO-zKO" id="ZUP-Yd-QDY"/>
|
||||
<outlet property="delayLeftClickStack" destination="fjb-oT-r1E" id="0Tb-f9-Pym"/>
|
||||
<outlet property="delayLeftClickSwitch" destination="YB5-xe-Zdx" id="FEK-Lb-f0r"/>
|
||||
<outlet property="dualSenseTransientSlider" destination="8fE-0o-N0V" id="ECI-p9-Fn4"/>
|
||||
<outlet property="dualSenseTransientStack" destination="7Ye-hW-K31" id="IaM-oQ-DVA"/>
|
||||
<outlet property="duckOtherAppStack" destination="FkE-Sc-6kA" id="YUO-j1-Bdv"/>
|
||||
<outlet property="duckOtherAppSwitch" destination="71J-Gt-A3D" id="3iT-jh-hWx"/>
|
||||
<outlet property="edgeSlidingSensitivitySlider" destination="KRf-2B-Reb" id="UaG-GM-wkO"/>
|
||||
@@ -3605,15 +3608,9 @@
|
||||
<outlet property="fpsStack" destination="aMv-7Q-yYg" id="JxU-wq-2Za"/>
|
||||
<outlet property="framePacingModeSelector" destination="CC6-pu-Uoh" id="HTB-6I-QgB"/>
|
||||
<outlet property="framePacingStack" destination="U0H-kh-QLS" id="M8w-wy-HI8"/>
|
||||
<outlet property="interpolationLevelSlider" destination="FIi-Lv-Sel" id="FIi-Out-Sel"/>
|
||||
<outlet property="interpolationLevelStack" destination="FIi-Lv-StK" id="FIi-Out-StK"/>
|
||||
<outlet property="streamDimensionScaleSlider" destination="FIi-Sc-Sld" id="FIi-Out-ScS"/>
|
||||
<outlet property="streamDimensionScaleStack" destination="FIi-Sc-StK" id="FIi-Out-ScK"/>
|
||||
<outlet property="frameQueueSizeLabel" destination="r8X-jt-JfH" id="Tr9-eb-bIx"/>
|
||||
<outlet property="frameQueueSizeSlider" destination="thv-JE-onX" id="gKD-6c-5tw"/>
|
||||
<outlet property="frameQueueSizeStack" destination="IHx-FO-ctr" id="gob-Ea-Avh"/>
|
||||
<outlet property="frameTimebaseStack" destination="HCc-SK-NBk" id="xts-bc-f0C"/>
|
||||
<outlet property="frameTimebaseSwitch" destination="bwf-wJ-EC2" id="fwl-GA-bQ6"/>
|
||||
<outlet property="framerateSelector" destination="dAN-Gy-Eg7" id="tMv-Va-PhO"/>
|
||||
<outlet property="fullColorRangeStack" destination="fRg-Rn-St7" id="fRg-Out-Let"/>
|
||||
<outlet property="fullColorRangeSwitch" destination="fRs-Wt-Ch9" id="fRs-Out-Let"/>
|
||||
@@ -3634,6 +3631,8 @@
|
||||
<outlet property="hapticEngineStack" destination="kiR-q0-FQX" id="zj5-eD-N5a"/>
|
||||
<outlet property="hdrStack" destination="kFw-d7-BTn" id="3aT-Gh-bDO"/>
|
||||
<outlet property="hdrSwitch" destination="Kgb-YU-AFW" id="ksG-A4-HhS"/>
|
||||
<outlet property="interpolationLevelSlider" destination="FIi-Lv-Sel" id="FIi-Out-Sel"/>
|
||||
<outlet property="interpolationLevelStack" destination="FIi-Lv-StK" id="FIi-Out-StK"/>
|
||||
<outlet property="leftClickDelaySlider" destination="HNu-vj-Q0t" id="X9f-ya-wVv"/>
|
||||
<outlet property="leftClickDelayStack" destination="muF-2y-dHu" id="rmd-Pm-nfV"/>
|
||||
<outlet property="leftStickMinOffsetSlider" destination="vOQ-zv-JOQ" id="kJb-ey-xMh"/>
|
||||
@@ -3720,6 +3719,8 @@
|
||||
<outlet property="softKeyboardToolbarSwitch" destination="c4W-2p-R3A" id="gf6-pq-L8o"/>
|
||||
<outlet property="statsOverlaySelector" destination="Bda-3L-Wtg" id="9kj-wY-VjF"/>
|
||||
<outlet property="statsOverlayStack" destination="dby-k3-aiH" id="1Ha-Y3-su4"/>
|
||||
<outlet property="streamDimensionScaleSlider" destination="FIi-Sc-Sld" id="FIi-Out-ScS"/>
|
||||
<outlet property="streamDimensionScaleStack" destination="FIi-Sc-StK" id="FIi-Out-ScK"/>
|
||||
<outlet property="streamingRadialMenuDelaySlider" destination="Tgh-nL-w5K" id="91f-Q5-IIl"/>
|
||||
<outlet property="streamingRadialMenuDelayStack" destination="ySL-A5-fcn" id="HrE-C7-y0g"/>
|
||||
<outlet property="swapAbxyStack" destination="reE-vr-Mdk" id="y3v-ac-LHy"/>
|
||||
|
||||
@@ -63,6 +63,15 @@
|
||||
rightSharpness:(float)rightSharpness
|
||||
rightTransient:(float)rightTransient
|
||||
delaySeconds:(double)delaySeconds;
|
||||
-(BOOL) hasDualSenseController:(uint16_t)controllerNumber;
|
||||
-(void) renderDeviceDualSenseHaptics:(uint16_t)controllerNumber
|
||||
leftAmplitude:(float)leftAmplitude
|
||||
leftSharpness:(float)leftSharpness
|
||||
leftTransient:(float)leftTransient
|
||||
rightAmplitude:(float)rightAmplitude
|
||||
rightSharpness:(float)rightSharpness
|
||||
rightTransient:(float)rightTransient
|
||||
delaySeconds:(double)delaySeconds;
|
||||
-(void) cancelScheduledDualSenseHaptics;
|
||||
-(void) updateTimerStateForOsc;
|
||||
|
||||
|
||||
+381
-235
@@ -184,9 +184,11 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
case RumbleDevice:
|
||||
[_oscController.lowFreqMotor setMotorAmplitude:lowFreqMotor];
|
||||
[_oscController.highFreqMotor setMotorAmplitude:highFreqMotor];
|
||||
break;
|
||||
case LeftRightSwapped:
|
||||
[voidController.lowFreqMotor setMotorAmplitude:highFreqMotor];
|
||||
[voidController.highFreqMotor setMotorAmplitude:lowFreqMotor];
|
||||
break;
|
||||
case RumbleOff:
|
||||
break;
|
||||
default:
|
||||
@@ -212,250 +214,317 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
return CGRectGetWidth([[UIScreen mainScreen]bounds]) > CGRectGetHeight([[UIScreen mainScreen]bounds]);
|
||||
}
|
||||
|
||||
-(void)updateTimerStateForController:(VoidController* )voidController{
|
||||
// if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||
if (true) {
|
||||
if(_gyroMode == GyroModeOff){
|
||||
[self stopTimerForController:voidController];
|
||||
return;
|
||||
}
|
||||
|
||||
if(voidController.gamepad.motion.hasAttitudeAndRotationRate) [voidController.motionTypes addObject:@(LI_MOTION_TYPE_ACCEL)];
|
||||
if (@available(iOS 14.0, *)) if(voidController.gamepad.motion.hasRotationRate) [voidController.motionTypes addObject:@(LI_MOTION_TYPE_GYRO)];
|
||||
#if !TARGET_OS_TV
|
||||
- (UIInterfaceOrientation)currentInterfaceOrientation
|
||||
{
|
||||
if (@available(iOS 13.0, *)) {
|
||||
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
||||
if (![scene isKindOfClass:UIWindowScene.class]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for(NSNumber* motionTypeObj in voidController.motionTypes){
|
||||
uint8_t motionType = motionTypeObj.intValue;
|
||||
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
||||
if (windowScene.activationState != UISceneActivationStateForegroundActive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (windowScene.interfaceOrientation != UIInterfaceOrientationUnknown) {
|
||||
return windowScene.interfaceOrientation;
|
||||
}
|
||||
}
|
||||
|
||||
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
||||
if (![scene isKindOfClass:UIWindowScene.class]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
||||
if (windowScene.activationState == UISceneActivationStateUnattached) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (windowScene.interfaceOrientation != UIInterfaceOrientationUnknown) {
|
||||
return windowScene.interfaceOrientation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UIApplication.sharedApplication.statusBarOrientation;
|
||||
}
|
||||
#endif
|
||||
|
||||
-(void)updateTimerStateForController:(VoidController* )voidController{
|
||||
if (![NSThread isMainThread]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateTimerStateForController:voidController];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (voidController == nil || voidController.reportRateHz == 0) {
|
||||
[self stopTimerForController:voidController];
|
||||
return;
|
||||
}
|
||||
|
||||
// if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||
if(_gyroMode == GyroModeOff){
|
||||
[self stopTimerForController:voidController];
|
||||
return;
|
||||
}
|
||||
if([ControllerUtil hasControllerAccelerometer:voidController.gamepad]) {
|
||||
[voidController.motionTypes addObject:@(LI_MOTION_TYPE_ACCEL)];
|
||||
}
|
||||
if (@available(iOS 14.0, *)) if(voidController.gamepad.motion.hasRotationRate){
|
||||
[voidController.motionTypes addObject:@(LI_MOTION_TYPE_GYRO)];
|
||||
}
|
||||
|
||||
for(NSNumber* motionTypeObj in voidController.motionTypes){
|
||||
uint8_t motionType = motionTypeObj.intValue;
|
||||
|
||||
#if !TARGET_OS_TV //tvOS has no device motion
|
||||
if(voidController == _oscController){
|
||||
//Player has no controller *or* no motion for controller 1 *or* wants to override controller 1 motion with device motion
|
||||
if(!voidController.motionManager) {
|
||||
voidController.motionManager = [[CMMotionManager alloc] init];
|
||||
}
|
||||
|
||||
switch (motionType) {
|
||||
case LI_MOTION_TYPE_ACCEL:
|
||||
[voidController.accelTimer invalidate];
|
||||
voidController.accelTimer = nil;
|
||||
// Reset the last motion sample
|
||||
CMAcceleration emptyDeviceAccelSample = {};
|
||||
voidController.lastDeviceAccelSample = emptyDeviceAccelSample;
|
||||
|
||||
{dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"setup device built-in gyro accelTimer");
|
||||
if(voidController == _oscController){
|
||||
//Player has no controller *or* no motion for controller 1 *or* wants to override controller 1 motion with device motion
|
||||
if(!voidController.motionManager) {
|
||||
voidController.motionManager = [[CMMotionManager alloc] init];
|
||||
}
|
||||
|
||||
switch (motionType) {
|
||||
case LI_MOTION_TYPE_ACCEL: {
|
||||
[voidController.accelTimer invalidate];
|
||||
voidController.accelTimer = nil;
|
||||
// Reset the last motion sample
|
||||
CMAcceleration emptyDeviceAccelSample = {};
|
||||
voidController.lastDeviceAccelSample = emptyDeviceAccelSample;
|
||||
voidController.motionManager.deviceMotionUpdateInterval = 1.0 / voidController.reportRateHz;
|
||||
[voidController.motionManager startDeviceMotionUpdates];
|
||||
{NSLog(@"setup device built-in accelTimer");
|
||||
voidController.hasAccelerometer = YES;
|
||||
voidController.accelTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / voidController.reportRateHz repeats:YES block:^(NSTimer *timer) {
|
||||
// Don't send duplicate samples
|
||||
CMAcceleration lastDeviceAccelSample = voidController.lastDeviceAccelSample;
|
||||
CMAcceleration deviceAccelSample = voidController.motionManager.deviceMotion.userAcceleration;
|
||||
//userAcceleration does not contain gravity, add gravity to x, y and z values:
|
||||
deviceAccelSample.x += voidController.motionManager.deviceMotion.gravity.x * self->_gyroSensitivity;
|
||||
deviceAccelSample.y += voidController.motionManager.deviceMotion.gravity.y * self->_gyroSensitivity;
|
||||
deviceAccelSample.z += voidController.motionManager.deviceMotion.gravity.z * self->_gyroSensitivity;
|
||||
|
||||
if (memcmp(&deviceAccelSample, &lastDeviceAccelSample, sizeof(deviceAccelSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
voidController.lastDeviceAccelSample = deviceAccelSample;
|
||||
|
||||
// Convert g to m/s^2
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if(UIApplication.sharedApplication.windows.firstObject.windowScene.interfaceOrientation == 4){ //check for landscape left or landscape right
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_ACCEL,
|
||||
deviceAccelSample.y * -9.80665f * self->_gyroSensitivity,
|
||||
deviceAccelSample.z * -9.80665f * self->_gyroSensitivity,
|
||||
deviceAccelSample.x * -9.80665f * self->_gyroSensitivity);
|
||||
}
|
||||
else{
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_ACCEL,
|
||||
deviceAccelSample.y * +9.80665f * self->_gyroSensitivity,
|
||||
deviceAccelSample.z * -9.80665f * self->_gyroSensitivity,
|
||||
deviceAccelSample.x * +9.80665f * self->_gyroSensitivity);
|
||||
}
|
||||
}
|
||||
else{
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_ACCEL,
|
||||
deviceAccelSample.y * -9.80665f * self->_gyroSensitivity,
|
||||
deviceAccelSample.z * -9.80665f * self->_gyroSensitivity,
|
||||
deviceAccelSample.x * -9.80665f * self->_gyroSensitivity);
|
||||
}
|
||||
}];
|
||||
});}
|
||||
break;
|
||||
case LI_MOTION_TYPE_GYRO:
|
||||
[voidController.gyroTimer invalidate];
|
||||
voidController.gyroTimer = nil;
|
||||
|
||||
// Reset the last motion sample
|
||||
CMRotationRate emptyDeviceGyroSample = {};
|
||||
voidController.lastDeviceGyroSample = emptyDeviceGyroSample;
|
||||
[voidController.motionManager startDeviceMotionUpdates];
|
||||
|
||||
NSLog(@"setup device built-in gyro gyroTimer");
|
||||
voidController.hasGyroscope = YES;
|
||||
voidController.gyroTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / voidController.reportRateHz repeats:YES block:^(NSTimer *timer) {
|
||||
|
||||
// Don't send duplicate samples
|
||||
CMRotationRate lastDeviceGyroSample = voidController.lastDeviceGyroSample;
|
||||
CMRotationRate deviceGyroSample = voidController.motionManager.deviceMotion.rotationRate;
|
||||
if (memcmp(&deviceGyroSample, &lastDeviceGyroSample, sizeof(deviceGyroSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
voidController.lastDeviceGyroSample = deviceGyroSample;
|
||||
|
||||
// Convert rad/s to deg/s
|
||||
|
||||
UIInterfaceOrientation interfaceOrientation = UIInterfaceOrientationUnknown;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
interfaceOrientation = UIApplication.sharedApplication.windows.firstObject.windowScene.interfaceOrientation;
|
||||
} else {
|
||||
interfaceOrientation = UIApplication.sharedApplication.statusBarOrientation;
|
||||
}
|
||||
|
||||
deviceAccelSample.x += voidController.motionManager.deviceMotion.gravity.x;
|
||||
deviceAccelSample.y += voidController.motionManager.deviceMotion.gravity.y;
|
||||
deviceAccelSample.z += voidController.motionManager.deviceMotion.gravity.z;
|
||||
NSLog(@"sending device accel %f", CACurrentMediaTime());
|
||||
|
||||
UIInterfaceOrientation interfaceOrientation = [self currentInterfaceOrientation];
|
||||
CMAcceleration mappedDeviceAccelSample = {};
|
||||
switch (interfaceOrientation) {
|
||||
case UIInterfaceOrientationLandscapeLeft:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0);
|
||||
mappedDeviceAccelSample.x = deviceAccelSample.y * -9.80665f;
|
||||
mappedDeviceAccelSample.y = deviceAccelSample.z * -9.80665f;
|
||||
mappedDeviceAccelSample.z = deviceAccelSample.x * -9.80665f;
|
||||
break;
|
||||
case UIInterfaceOrientationLandscapeRight:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0);
|
||||
mappedDeviceAccelSample.x = deviceAccelSample.y * 9.80665f;
|
||||
mappedDeviceAccelSample.y = deviceAccelSample.z * -9.80665f;
|
||||
mappedDeviceAccelSample.z = deviceAccelSample.x * 9.80665f;
|
||||
break;
|
||||
case UIInterfaceOrientationPortrait:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0);
|
||||
mappedDeviceAccelSample.x = deviceAccelSample.x * -9.80665f;
|
||||
mappedDeviceAccelSample.y = deviceAccelSample.z * -9.80665f;
|
||||
mappedDeviceAccelSample.z = deviceAccelSample.y * 9.80665f;
|
||||
break;
|
||||
case UIInterfaceOrientationPortraitUpsideDown:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0);
|
||||
mappedDeviceAccelSample.x = deviceAccelSample.x * 9.80665f;
|
||||
mappedDeviceAccelSample.y = deviceAccelSample.z * -9.80665f;
|
||||
mappedDeviceAccelSample.z = deviceAccelSample.y * -9.80665f;
|
||||
break;
|
||||
default:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0);
|
||||
mappedDeviceAccelSample.x = deviceAccelSample.y * -9.80665f;
|
||||
mappedDeviceAccelSample.y = deviceAccelSample.z * -9.80665f;
|
||||
mappedDeviceAccelSample.z = deviceAccelSample.x * -9.80665f;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't send duplicate samples after orientation remapping.
|
||||
CMAcceleration lastDeviceAccelSample = voidController.lastDeviceAccelSample;
|
||||
if (memcmp(&mappedDeviceAccelSample, &lastDeviceAccelSample, sizeof(mappedDeviceAccelSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
voidController.lastDeviceAccelSample = mappedDeviceAccelSample;
|
||||
|
||||
/*
|
||||
if(UIApplication.sharedApplication.windows.firstObject.windowScene.interfaceOrientation == 4){//check for landscape left or landscape right
|
||||
// Convert g to m/s^2
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_ACCEL,
|
||||
mappedDeviceAccelSample.x,
|
||||
mappedDeviceAccelSample.y,
|
||||
mappedDeviceAccelSample.z);
|
||||
}];}
|
||||
break;
|
||||
}
|
||||
case LI_MOTION_TYPE_GYRO: {
|
||||
[voidController.gyroTimer invalidate];
|
||||
voidController.gyroTimer = nil;
|
||||
|
||||
// Reset the last motion sample
|
||||
CMRotationRate emptyDeviceGyroSample = {};
|
||||
voidController.lastDeviceGyroSample = emptyDeviceGyroSample;
|
||||
[voidController.motionManager startDeviceMotionUpdates];
|
||||
|
||||
NSLog(@"setup device built-in gyro gyroTimer");
|
||||
voidController.hasGyroscope = YES;
|
||||
voidController.gyroTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / voidController.reportRateHz repeats:YES block:^(NSTimer *timer) {
|
||||
|
||||
// Don't send duplicate samples
|
||||
CMRotationRate lastDeviceGyroSample = voidController.lastDeviceGyroSample;
|
||||
CMRotationRate deviceGyroSample = voidController.motionManager.deviceMotion.rotationRate;
|
||||
if (memcmp(&deviceGyroSample, &lastDeviceGyroSample, sizeof(deviceGyroSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
voidController.lastDeviceGyroSample = deviceGyroSample;
|
||||
|
||||
// Convert rad/s to deg/s
|
||||
|
||||
UIInterfaceOrientation interfaceOrientation = [self currentInterfaceOrientation];
|
||||
|
||||
switch (interfaceOrientation) {
|
||||
case UIInterfaceOrientationLandscapeLeft:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0);
|
||||
}
|
||||
else{
|
||||
break;
|
||||
case UIInterfaceOrientationLandscapeRight:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0);
|
||||
}
|
||||
*/
|
||||
}];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
else{
|
||||
NSLog(@"controller obj timer update: controller timer ");
|
||||
|
||||
if (@available(iOS 14.0, *)) {
|
||||
switch (motionType) {
|
||||
case LI_MOTION_TYPE_ACCEL:
|
||||
[voidController.accelTimer invalidate];
|
||||
voidController.accelTimer = nil;
|
||||
|
||||
if (voidController.reportRateHz && voidController.gamepad.motion.hasGravityAndUserAcceleration) {
|
||||
// Reset the last motion sample
|
||||
GCAcceleration emptyAccelSample = {};
|
||||
voidController.lastAccelSample = emptyAccelSample;
|
||||
NSLog(@"setup controller gyro accelTimer");
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
voidController.hasAccelerometer = YES;
|
||||
voidController.accelTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / voidController.reportRateHz repeats:YES block:^(NSTimer *timer) {
|
||||
// Don't send duplicate samples
|
||||
GCAcceleration lastAccelSample = voidController.lastAccelSample;
|
||||
GCAcceleration accelSample = voidController.gamepad.motion.acceleration;
|
||||
|
||||
if (memcmp(&accelSample, &lastAccelSample, sizeof(accelSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
voidController.lastAccelSample = accelSample;
|
||||
|
||||
// Convert g to m/s^2
|
||||
//NSLog(@"sending controller gyro data, accelSample data 00: %f, playerIndex: %ld, obj: %@",accelSample.x, (long)voidController.gamepad.playerIndex, voidController);
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_ACCEL,
|
||||
accelSample.x * -9.80665f * self->_gyroSensitivity,
|
||||
accelSample.y * -9.80665f * self->_gyroSensitivity,
|
||||
accelSample.z * -9.80665f * self->_gyroSensitivity);
|
||||
}];
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case LI_MOTION_TYPE_GYRO:
|
||||
[voidController.gyroTimer invalidate];
|
||||
voidController.gyroTimer = nil;
|
||||
|
||||
if (voidController.reportRateHz && voidController.gamepad.motion.hasRotationRate) {
|
||||
// Reset the last motion sample
|
||||
GCRotationRate emptyGyroSample = {};
|
||||
voidController.lastGyroSample = emptyGyroSample;
|
||||
//dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
{dispatch_async(dispatch_get_main_queue(), ^{
|
||||
voidController.hasGyroscope = YES;
|
||||
voidController.gyroTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / voidController.reportRateHz repeats:YES block:^(NSTimer *timer) {
|
||||
// Don't send duplicate samples
|
||||
GCRotationRate lastGyroSample = voidController.lastGyroSample;
|
||||
GCRotationRate gyroSample = voidController.gamepad.motion.rotationRate;
|
||||
if (memcmp(&gyroSample, &lastGyroSample, sizeof(gyroSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
voidController.lastGyroSample = gyroSample;
|
||||
|
||||
// Convert rad/s to deg/s
|
||||
// NSLog(@"sending controller gyro data, gyroSample data 00: %f, playerIndex: %ld, obj: %@",gyroSample.x, (long)voidController.gamepad.playerIndex, voidController);
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? gyroSample.x * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? gyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? gyroSample.y * -57.2957795f * self->_gyroSensitivity : 0);
|
||||
}];
|
||||
// });
|
||||
});}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case UIInterfaceOrientationPortrait:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0);
|
||||
break;
|
||||
case UIInterfaceOrientationPortraitUpsideDown:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0);
|
||||
break;
|
||||
default:
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
if(UIApplication.sharedApplication.windows.firstObject.windowScene.interfaceOrientation == 4){//check for landscape left or landscape right
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0);
|
||||
}
|
||||
else{
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0);
|
||||
}
|
||||
*/
|
||||
}];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"controller obj timer, motionTypes: %lu", (unsigned long)voidController.motionTypes.count);
|
||||
|
||||
// Set the motion sensor state if they require manual activation
|
||||
[self updateSensorSateForController:voidController];
|
||||
// NSLog(@"sensor active: %d", voidController.gamepad.motion.sensorsActive);
|
||||
#endif
|
||||
else{
|
||||
// NSLog(@"controller obj timer update: controller timer ");
|
||||
if (@available(iOS 14.0, *)) {
|
||||
switch (motionType) {
|
||||
case LI_MOTION_TYPE_ACCEL:
|
||||
[voidController.accelTimer invalidate];
|
||||
voidController.accelTimer = nil;
|
||||
|
||||
// if (voidController.reportRateHz && voidController.gamepad.motion.hasGravityAndUserAcceleration) {
|
||||
if (voidController.reportRateHz) {
|
||||
// Reset the last motion sample
|
||||
GCAcceleration emptyAccelSample = {};
|
||||
voidController.lastAccelSample = emptyAccelSample;
|
||||
void (^setupAccelTimer)(void) = ^{
|
||||
voidController.hasAccelerometer = YES;
|
||||
voidController.accelTimer =
|
||||
[NSTimer scheduledTimerWithTimeInterval:1.0 / voidController.reportRateHz
|
||||
repeats:YES
|
||||
block:^(NSTimer *timer) {
|
||||
GCAcceleration lastAccelSample = voidController.lastAccelSample;
|
||||
GCAcceleration accelSample = voidController.gamepad.motion.acceleration;
|
||||
|
||||
if (memcmp(&accelSample, &lastAccelSample, sizeof(accelSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
voidController.lastAccelSample = accelSample;
|
||||
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_ACCEL,
|
||||
accelSample.x * -9.80665f,
|
||||
accelSample.y * -9.80665f,
|
||||
accelSample.z * -9.80665f);
|
||||
}];
|
||||
};
|
||||
if ([NSThread isMainThread]) {
|
||||
setupAccelTimer();
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), setupAccelTimer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LI_MOTION_TYPE_GYRO:
|
||||
[voidController.gyroTimer invalidate];
|
||||
voidController.gyroTimer = nil;
|
||||
|
||||
if (voidController.reportRateHz && voidController.gamepad.motion.hasRotationRate) {
|
||||
// Reset the last motion sample
|
||||
GCRotationRate emptyGyroSample = {};
|
||||
voidController.lastGyroSample = emptyGyroSample;
|
||||
//dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
void (^setupGyroTimer)(void) = ^{
|
||||
voidController.hasGyroscope = YES;
|
||||
voidController.gyroTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / voidController.reportRateHz repeats:YES block:^(NSTimer *timer) {
|
||||
// Don't send duplicate samples
|
||||
GCRotationRate lastGyroSample = voidController.lastGyroSample;
|
||||
GCRotationRate gyroSample = voidController.gamepad.motion.rotationRate;
|
||||
if (memcmp(&gyroSample, &lastGyroSample, sizeof(gyroSample)) == 0) {
|
||||
return;
|
||||
}
|
||||
voidController.lastGyroSample = gyroSample;
|
||||
|
||||
// Convert rad/s to deg/s
|
||||
// NSLog(@"sending controller gyro data, gyroSample data 00: %f, playerIndex: %ld, obj: %@",gyroSample.x, (long)voidController.gamepad.playerIndex, voidController);
|
||||
LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber,
|
||||
LI_MOTION_TYPE_GYRO,
|
||||
self->_gyroEnabledFlag ? gyroSample.x * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? gyroSample.z * 57.2957795f * self->_gyroSensitivity : 0,
|
||||
self->_gyroEnabledFlag ? gyroSample.y * -57.2957795f * self->_gyroSensitivity : 0);
|
||||
}];
|
||||
};
|
||||
if ([NSThread isMainThread]) setupGyroTimer();
|
||||
else dispatch_async(dispatch_get_main_queue(), setupGyroTimer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"controller obj timer, motionTypes: %lu", (unsigned long)voidController.motionTypes.count);
|
||||
|
||||
// Set the motion sensor state if they require manual activation
|
||||
[self updateSensorSateForController:voidController];
|
||||
// NSLog(@"sensor active: %d", voidController.gamepad.motion.sensorsActive);
|
||||
}
|
||||
|
||||
- (void)updateSensorSateForController:(VoidController* )voidController{
|
||||
@@ -472,6 +541,13 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
}
|
||||
|
||||
- (void) setMotionEventState:(uint16_t)controllerNumber motionType:(uint8_t)motionType reportRateHz:(uint16_t)reportRateHz {
|
||||
if (![NSThread isMainThread]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self setMotionEventState:controllerNumber motionType:motionType reportRateHz:reportRateHz];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
//if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||
NSLog(@"gyroMode: %ld", (long)_gyroMode);
|
||||
|
||||
@@ -483,16 +559,26 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
if(!voidController.motionTypes){
|
||||
voidController.motionTypes = [[NSMutableSet alloc] init];
|
||||
}
|
||||
[voidController.motionTypes addObject:@(motionType)];
|
||||
if (reportRateHz == 0) {
|
||||
[voidController.motionTypes removeObject:@(motionType)];
|
||||
}
|
||||
else {
|
||||
[voidController.motionTypes addObject:@(motionType)];
|
||||
}
|
||||
|
||||
voidController.hasGyroscope = NO;
|
||||
voidController.hasAccelerometer = NO;
|
||||
voidController.reportRateHz = reportRateHz;
|
||||
}
|
||||
|
||||
voidController.reportRateHz = reportRateHz;
|
||||
voidController.controllerNumber = controllerNumber;
|
||||
|
||||
if(voidController == _oscController) [self updateTimerStateForController:voidController];
|
||||
if (reportRateHz == 0) {
|
||||
[self stopTimerForController:voidController];
|
||||
}
|
||||
else if(voidController == _oscController) {
|
||||
[self updateTimerStateForController:voidController];
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -605,6 +691,62 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL) hasDualSenseController:(uint16_t)controllerNumber {
|
||||
__block BOOL result = NO;
|
||||
dispatch_block_t checkBlock = ^{
|
||||
if (@available(iOS 14.5, tvOS 14.5, *)) {
|
||||
VoidController* controller = [self->_voidControllers objectForKey:@(controllerNumber)];
|
||||
result = controller != nil &&
|
||||
[controller.gamepad.extendedGamepad isKindOfClass:[GCDualSenseGamepad class]];
|
||||
}
|
||||
};
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
checkBlock();
|
||||
}
|
||||
else {
|
||||
dispatch_sync(dispatch_get_main_queue(), checkBlock);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
-(void) renderDeviceDualSenseHaptics:(uint16_t)controllerNumber
|
||||
leftAmplitude:(float)leftAmplitude
|
||||
leftSharpness:(float)leftSharpness
|
||||
leftTransient:(float)leftTransient
|
||||
rightAmplitude:(float)rightAmplitude
|
||||
rightSharpness:(float)rightSharpness
|
||||
rightTransient:(float)rightTransient
|
||||
delaySeconds:(double)delaySeconds {
|
||||
__block uint64_t generation;
|
||||
@synchronized(self) {
|
||||
generation = _authoredHapticsGeneration;
|
||||
}
|
||||
dispatch_block_t renderBlock = ^{
|
||||
@synchronized(self) {
|
||||
if (generation != self->_authoredHapticsGeneration) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||
[self->_oscController.lowFreqMotor setAuthoredAmplitude:leftAmplitude
|
||||
sharpness:leftSharpness
|
||||
transientStrength:leftTransient];
|
||||
[self->_oscController.highFreqMotor setAuthoredAmplitude:rightAmplitude
|
||||
sharpness:rightSharpness
|
||||
transientStrength:rightTransient];
|
||||
}
|
||||
};
|
||||
|
||||
if (delaySeconds > 0.0) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySeconds * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(), renderBlock);
|
||||
}
|
||||
else {
|
||||
dispatch_async(dispatch_get_main_queue(), renderBlock);
|
||||
}
|
||||
}
|
||||
|
||||
-(void) cancelScheduledDualSenseHaptics {
|
||||
@synchronized(self) {
|
||||
_authoredHapticsGeneration++;
|
||||
@@ -1020,7 +1162,8 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
|
||||
// LI_CTYPE_UNKNOWN for option "Both"
|
||||
if(voidController.playerIndex == 0){
|
||||
type = _streamConfig.emulatedControllerType == LI_CTYPE_UNKNOWN ? LI_CTYPE_PS : _streamConfig.emulatedControllerType;
|
||||
type = _streamConfig.emulatedControllerType == ControllerEmulationXboxAndPs ? LI_CTYPE_PS : _streamConfig.emulatedControllerType;
|
||||
if(type == ControllerEmulationPsEnhancedHaptic) type = ControllerEmulationPs;
|
||||
}
|
||||
|
||||
if(voidController.playerIndex == 1 && _streamConfig.emulatedControllerType == LI_CTYPE_UNKNOWN){
|
||||
@@ -1038,15 +1181,10 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
if (@available(iOS 14.5, tvOS 14.5, *)) {
|
||||
if ([controller.extendedGamepad isKindOfClass:[GCDualSenseGamepad class]]) {
|
||||
type = LI_CTYPE_PS;
|
||||
capabilities |= LI_CCAP_DS5_HAPTICS_PCM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (@available(iOS 14.5, tvOS 14.5, *)) {
|
||||
if ([controller.extendedGamepad isKindOfClass:[GCDualSenseGamepad class]]) {
|
||||
capabilities |= LI_CCAP_DS5_HAPTICS_PCM;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect supported haptics localities
|
||||
if (controller.haptics) {
|
||||
@@ -1081,7 +1219,8 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
}
|
||||
|
||||
bool controllerLacksGyro = (capabilities & LI_CCAP_GYRO) == 0;
|
||||
if(_streamConfig.emulatedControllerType == LI_CTYPE_PS && (_gyroMode == AlwaysDevice || (_gyroMode == GyroModeAuto && controllerLacksGyro)))
|
||||
if((_streamConfig.emulatedControllerType == ControllerEmulationPs || _streamConfig.emulatedControllerType == ControllerEmulationPsEnhancedHaptic)
|
||||
&& (_gyroMode == AlwaysDevice || (_gyroMode == GyroModeAuto && controllerLacksGyro)))
|
||||
{
|
||||
type = LI_CTYPE_PS;
|
||||
capabilities |= LI_CCAP_GYRO | LI_CCAP_ACCEL;
|
||||
@@ -1092,7 +1231,7 @@ static void ApplyAdaptiveTriggerEffect(GCDualSenseAdaptiveTrigger* trigger,
|
||||
// This is a virtual controller corresponding to our OSC
|
||||
// set osc to PS to utilize built-in gyro when "both" is selected
|
||||
type = _streamConfig.emulatedControllerType == LI_CTYPE_UNKNOWN ? LI_CTYPE_PS : _streamConfig.emulatedControllerType;
|
||||
|
||||
if(type == ControllerEmulationPsEnhancedHaptic) type = LI_CTYPE_PS;
|
||||
/*
|
||||
if (_streamConfig.gyroMode != GyroModeOff) {
|
||||
type = LI_CTYPE_PS;
|
||||
@@ -1180,8 +1319,7 @@ double rc_expo(double x, double expo) {
|
||||
|
||||
|
||||
- (bool)useMotionHandler{
|
||||
return self->tempSettings.emulatedControllerType.intValue!=LI_CTYPE_PS
|
||||
||self->tempSettings.gyroMode.intValue==GyroModeOff;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)switchMotionControlOnOffByControllerButton{
|
||||
@@ -1190,7 +1328,7 @@ double rc_expo(double x, double expo) {
|
||||
if(self->_gyroEnabledFlag) [self->motionHandler startMotionControlByControllerButton];
|
||||
else [self->motionHandler stopMotionUpdateWithInterruptNoneGyroInput:false];
|
||||
}
|
||||
else self->_gyroEnabledFlag = self->_gyroEnabledFlag || !self->_controllerGyroSwitchEnabled;
|
||||
self->_gyroEnabledFlag = self->_gyroEnabledFlag || !self->_controllerGyroSwitchEnabled;
|
||||
}
|
||||
|
||||
- (void)sendNavigationButtonPress {
|
||||
@@ -1330,6 +1468,7 @@ double rc_expo(double x, double expo) {
|
||||
*/
|
||||
|
||||
if([self useMotionHandler]
|
||||
&& self->motionHandler.motionControlStarted
|
||||
&& self->oscProfile.mapGyroTo==mapGyroToControllerStick
|
||||
&& self->oscProfile.yawPitchToRightStick
|
||||
&& self->_gyroEnabledFlag
|
||||
@@ -1339,6 +1478,7 @@ double rc_expo(double x, double expo) {
|
||||
else [self updateRightStick: voidController.playerIndex==0?self->_oscController:voidController x:rightStickX y:rightStickY];
|
||||
|
||||
if([self useMotionHandler]
|
||||
&& self->motionHandler.motionControlStarted
|
||||
&& self->oscProfile.mapGyroTo==mapGyroToControllerStick
|
||||
&& self->oscProfile.rollToLeftStick
|
||||
&& self->_gyroEnabledFlag
|
||||
@@ -1611,7 +1751,7 @@ double rc_expo(double x, double expo) {
|
||||
voidController.hasGyroscope = NO;
|
||||
|
||||
|
||||
if(voidController.gamepad.motion.hasAttitudeAndRotationRate){
|
||||
if([ControllerUtil hasControllerAccelerometer:voidController.gamepad]){
|
||||
[voidController.motionTypes addObject:@(LI_MOTION_TYPE_ACCEL)];
|
||||
voidController.hasAccelerometer = YES;
|
||||
voidController.reportRateHz = 120;
|
||||
@@ -1848,7 +1988,7 @@ double rc_expo(double x, double expo) {
|
||||
NSLog(@"controller obj in dict: %@", controller);
|
||||
}
|
||||
|
||||
if([self gamepadGyroEnabledInSetting]) [self updateFinished:_oscController];
|
||||
if([self psGyroEnabledInSetting]) [self updateFinished:_oscController];
|
||||
}
|
||||
|
||||
-(id)initWithConfig:(StreamConfiguration*)streamConfig delegate:(id<ControllerSupportDelegate>)delegate
|
||||
@@ -2038,8 +2178,12 @@ double rc_expo(double x, double expo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
-(bool)gamepadGyroEnabledInSetting {
|
||||
return (tempSettings.emulatedControllerType.intValue == LI_CTYPE_PS && tempSettings.gyroMode.intValue != GyroModeOff);
|
||||
-(bool)psGyroEnabledInSetting {
|
||||
uint8_t emulation = tempSettings.emulatedControllerType.intValue;
|
||||
return (emulation == ControllerEmulationPs
|
||||
|| emulation == ControllerEmulationPsEnhancedHaptic
|
||||
|| emulation == ControllerEmulationXboxAndPs)
|
||||
&& tempSettings.gyroMode.intValue != GyroModeOff;
|
||||
}
|
||||
|
||||
-(void)connectionEstablished {
|
||||
@@ -2049,7 +2193,7 @@ double rc_expo(double x, double expo) {
|
||||
|
||||
//if (_oscEnabled
|
||||
// || (tempSettings.emulatedControllerType.intValue == LI_CTYPE_PS && tempSettings.gyroMode != GyroModeOff)) {
|
||||
if ([self gamepadGyroEnabledInSetting]) {
|
||||
if ([self psGyroEnabledInSetting]) {
|
||||
[self setButtonFlag:self->_oscController flags:A_FLAG];
|
||||
[self updateFinished:self->_oscController];
|
||||
[self clearButtonFlag:self->_oscController flags:A_FLAG];
|
||||
@@ -2068,14 +2212,14 @@ double rc_expo(double x, double expo) {
|
||||
[self resetGyroInputForController:voidController];
|
||||
if (@available(iOS 14.0, *)) {
|
||||
//NSLog(@"stop controller obj: %@, hasAcc %d, hasGyro %d", voidController, voidController.hasAccelerometer, voidController.hasGyroscope);
|
||||
if(voidController.hasAccelerometer){
|
||||
// if(voidController.hasAccelerometer){
|
||||
[voidController.accelTimer invalidate];
|
||||
voidController.accelTimer = nil;
|
||||
}
|
||||
if(voidController.hasGyroscope){
|
||||
// }
|
||||
// if(voidController.hasGyroscope){
|
||||
[voidController.gyroTimer invalidate];
|
||||
voidController.gyroTimer = nil;
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2083,11 +2227,12 @@ double rc_expo(double x, double expo) {
|
||||
// Stop all timers to ensure a clean slate before applying the new setting.
|
||||
[self stopTimerForAllControllers];
|
||||
|
||||
_gyroEnabledFlag = _gyroMode != GyroModeOff && tempSettings.emulatedControllerType.intValue == LI_CTYPE_PS;
|
||||
_gyroEnabledFlag = [self psGyroEnabledInSetting];
|
||||
switch(_gyroMode) {
|
||||
case AlwaysController:
|
||||
// Activate timers only for physical controllers.
|
||||
for (VoidController* voidController in _voidControllers.allValues) {
|
||||
if(voidController == _oscController) break;
|
||||
[self updateTimerStateForController:voidController];
|
||||
}
|
||||
break;
|
||||
@@ -2170,7 +2315,8 @@ double rc_expo(double x, double expo) {
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if (VLSharedControllerSupport == self) {
|
||||
VLSharedControllerSupport = nil;
|
||||
|
||||
@@ -260,6 +260,13 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
@objc enum ControllerEmulation: UInt8 {
|
||||
case xbox = 0x01 // LI_CTYPE_XBOX
|
||||
case ps = 0x02 // LI_CTYPE_PS
|
||||
case psEnhancedHaptic = 0xFE
|
||||
case xboxAndPs = 0x00 // LI_CTYPE_UNKNOWN
|
||||
}
|
||||
|
||||
@objc protocol ControllerUtilDelegate: AnyObject {
|
||||
func isStreaming() -> Bool
|
||||
func isInAppView() -> Bool
|
||||
@@ -269,6 +276,38 @@ import UIKit
|
||||
@objc static weak var delegate: ControllerUtilDelegate?
|
||||
|
||||
#if !VOIDLINK_PREVIEW
|
||||
@objc static var hasDualSenseController: Bool {
|
||||
if #available(iOS 14.5, *) {
|
||||
for gcController in GCController.controllers() {
|
||||
if gcController.extendedGamepad is GCDualSenseGamepad {return true}
|
||||
}
|
||||
return false
|
||||
}
|
||||
else {return false}
|
||||
}
|
||||
|
||||
@objc static func hasControllerAccelerometer(_ controller: GCController) -> Bool {
|
||||
guard let motion = controller.motion else { return false }
|
||||
|
||||
if #available(iOS 14.0, tvOS 14.0, *) {
|
||||
if motion.hasGravityAndUserAcceleration {
|
||||
return true
|
||||
}
|
||||
|
||||
if controller.extendedGamepad is GCDualShockGamepad {
|
||||
return true
|
||||
}
|
||||
|
||||
if #available(iOS 14.5, tvOS 14.5, *) {
|
||||
if controller.extendedGamepad is GCDualSenseGamepad {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private struct DualSensePCMChunk {
|
||||
let controllerNumber: UInt16
|
||||
let flags: UInt8
|
||||
@@ -310,17 +349,57 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
private final class AuthoredIrRenderState {
|
||||
var renderHostAnchorUs: UInt64?
|
||||
var renderLocalAnchor = 0.0
|
||||
var diagnosticFrameCounter: UInt32 = 0
|
||||
|
||||
func renderDelay(for timestampUs: UInt64, resetTimeline: Bool) -> TimeInterval {
|
||||
let now = ProcessInfo.processInfo.systemUptime
|
||||
if resetTimeline || renderHostAnchorUs == nil {
|
||||
renderHostAnchorUs = timestampUs
|
||||
renderLocalAnchor = now + 0.002
|
||||
}
|
||||
let anchor = renderHostAnchorUs ?? timestampUs
|
||||
let hostDelta = timestampUs >= anchor ? Double(timestampUs - anchor) / 1_000_000.0 : 0
|
||||
return min(max(renderLocalAnchor + hostDelta - now, 0), 0.025)
|
||||
}
|
||||
}
|
||||
|
||||
private static let dualSenseHapticsLock = NSLock()
|
||||
private static let dualSenseHapticsWorker = DispatchQueue(
|
||||
label: "com.voidlink.dualsense-authored-haptics",
|
||||
qos: .userInteractive
|
||||
)
|
||||
private static let maxQueuedDualSensePCMChunks = 8
|
||||
private static let defaultDualSenseHapticsEQ: [Float] = [0.78, 0.98, 1.59, 0.175, 1.2]
|
||||
private static let defaultDualSenseControllerHapticsEQ: [Float] = [0.78, 0.98, 1.59, 0.175, 1.2]
|
||||
private static let defaultDeviceDualSenseHapticsEQ: [Float] = [18, 0.98, 1.59, 0.9, 1.2]
|
||||
private static let dualSenseControllerAmplitudeScale: Float = 1.15
|
||||
private static let dualSenseControllerTransientScale: Float = 1.0
|
||||
private static let dualSenseControllerSharpnessScale: Float = 1.15
|
||||
private static let dualSenseControllerMirrorMixedHandles = false
|
||||
private static var dualSensePCMQueue = Deque<DualSensePCMChunk>()
|
||||
private static var dualSenseWorkerScheduled = false
|
||||
private static var dualSenseForcedDiscontinuities = Set<UInt16>()
|
||||
private static var dualSenseAuthoredEngines = [UInt16: AuthoredEngineState]()
|
||||
private static var dualSenseIrRenderStates = [UInt16: AuthoredIrRenderState]()
|
||||
private static let authoredHapticsRouteLock = NSLock()
|
||||
private static var authoredHapticsPreference = AuthoredHapticsPreference.auto
|
||||
private static var authoredHapticsLastRoutedToDualSense = false
|
||||
private static var authoredHapticsLastRoute = AuthoredHapticsRoute.off
|
||||
|
||||
private enum AuthoredHapticsPreference: Int {
|
||||
case auto = 0
|
||||
case device = 1
|
||||
case swapped = 2
|
||||
case off = 3
|
||||
}
|
||||
|
||||
private enum AuthoredHapticsRoute {
|
||||
case dualSense
|
||||
case device
|
||||
case off
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private static var dualSenseHapticsEQOverlayWindow: UIWindow?
|
||||
@@ -329,11 +408,70 @@ import UIKit
|
||||
#endif
|
||||
|
||||
fileprivate static func dualSenseHapticsEQSnapshot() -> [Float] {
|
||||
defaultDualSenseHapticsEQ
|
||||
defaultDeviceDualSenseHapticsEQ
|
||||
}
|
||||
|
||||
fileprivate static func defaultDualSenseHapticsEQSnapshot() -> [Float] {
|
||||
defaultDualSenseHapticsEQ
|
||||
defaultDeviceDualSenseHapticsEQ
|
||||
}
|
||||
|
||||
private static func dualSenseHapticsEQSnapshot(for route: AuthoredHapticsRoute) -> [Float] {
|
||||
switch route {
|
||||
case .dualSense:
|
||||
return defaultDualSenseControllerHapticsEQ
|
||||
case .device, .off:
|
||||
return defaultDeviceDualSenseHapticsEQ
|
||||
}
|
||||
}
|
||||
|
||||
private static func compensatedDualSenseControllerValues(
|
||||
_ values: (amplitude: Float, sharpness: Float, transient: Float),
|
||||
highBandRatio: Float
|
||||
) -> (amplitude: Float, sharpness: Float, transient: Float) {
|
||||
let lowFrequencyTrim = 0.82 + 0.18 * min(max(highBandRatio, 0), 1)
|
||||
let amplitude = min(max(
|
||||
pow(values.amplitude, 0.50) * dualSenseControllerAmplitudeScale * lowFrequencyTrim,
|
||||
0
|
||||
), 1)
|
||||
let sharpness = min(max(
|
||||
pow(values.sharpness, 0.8) * dualSenseControllerSharpnessScale,
|
||||
0
|
||||
), 1)
|
||||
let transient = min(max(
|
||||
pow(values.transient, 0.9) * dualSenseControllerTransientScale,
|
||||
0
|
||||
), 1)
|
||||
return (amplitude, sharpness, transient)
|
||||
}
|
||||
|
||||
private static func authoredHighBandRatio(for lane: AhAuthoredLaneFrame?) -> Float {
|
||||
1 - min(max(lane?.low_band_ratio ?? 1, 0), 1)
|
||||
}
|
||||
|
||||
private static func mixedDualSenseControllerHandleValues(
|
||||
left: (amplitude: Float, sharpness: Float, transient: Float),
|
||||
right: (amplitude: Float, sharpness: Float, transient: Float)
|
||||
) -> (
|
||||
left: (amplitude: Float, sharpness: Float, transient: Float),
|
||||
right: (amplitude: Float, sharpness: Float, transient: Float)
|
||||
) {
|
||||
guard dualSenseControllerMirrorMixedHandles else {
|
||||
return (left, right)
|
||||
}
|
||||
|
||||
let amplitude = min(max(sqrt(
|
||||
(left.amplitude * left.amplitude + right.amplitude * right.amplitude) * 0.5
|
||||
), 0), 1)
|
||||
let amplitudeSum = left.amplitude + right.amplitude
|
||||
let sharpness = amplitudeSum > 0.000_001
|
||||
? min(max(
|
||||
(left.sharpness * left.amplitude + right.sharpness * right.amplitude) / amplitudeSum,
|
||||
0
|
||||
), 1)
|
||||
: min(max(max(left.sharpness, right.sharpness), 0), 1)
|
||||
let transient = min(max(max(left.transient, right.transient), 0), 1)
|
||||
let mixed = (amplitude: amplitude, sharpness: sharpness, transient: transient)
|
||||
return (mixed, mixed)
|
||||
}
|
||||
|
||||
fileprivate static func previewDualSenseHapticsEQ(_ values: [Float]) {
|
||||
@@ -342,6 +480,86 @@ import UIKit
|
||||
fileprivate static func saveDualSenseHapticsEQ(_ values: [Float]) {
|
||||
}
|
||||
|
||||
private static func refreshAuthoredHapticsPreference() -> AuthoredHapticsPreference {
|
||||
let rawValue = Int(DataManager().getSettings().hapticEngine.intValue)
|
||||
let preference = AuthoredHapticsPreference(rawValue: rawValue) ?? .auto
|
||||
authoredHapticsRouteLock.lock()
|
||||
authoredHapticsPreference = preference
|
||||
authoredHapticsRouteLock.unlock()
|
||||
return preference
|
||||
}
|
||||
|
||||
private static func selectAuthoredHapticsRoute(
|
||||
controllerNumber: UInt16,
|
||||
controllerSupport: ControllerSupport
|
||||
) -> AuthoredHapticsRoute {
|
||||
switch refreshAuthoredHapticsPreference() {
|
||||
case .off:
|
||||
return .off
|
||||
case .device:
|
||||
return .device
|
||||
case .auto, .swapped:
|
||||
let hasDualSense = controllerSupport.hasDualSenseController(controllerNumber)
|
||||
authoredHapticsRouteLock.lock()
|
||||
authoredHapticsLastRoutedToDualSense = hasDualSense
|
||||
authoredHapticsRouteLock.unlock()
|
||||
return hasDualSense ? .dualSense : .device
|
||||
}
|
||||
}
|
||||
|
||||
private static func stopAuthoredHapticsRoute(
|
||||
_ route: AuthoredHapticsRoute,
|
||||
controllerNumber: UInt16,
|
||||
controllerSupport: ControllerSupport
|
||||
) {
|
||||
switch route {
|
||||
case .dualSense:
|
||||
controllerSupport.renderDualSenseHaptics(
|
||||
controllerNumber,
|
||||
leftAmplitude: 0,
|
||||
leftSharpness: 0,
|
||||
leftTransient: 0,
|
||||
rightAmplitude: 0,
|
||||
rightSharpness: 0,
|
||||
rightTransient: 0,
|
||||
delaySeconds: 0
|
||||
)
|
||||
case .device:
|
||||
controllerSupport.renderDeviceDualSenseHaptics(
|
||||
controllerNumber,
|
||||
leftAmplitude: 0,
|
||||
leftSharpness: 0,
|
||||
leftTransient: 0,
|
||||
rightAmplitude: 0,
|
||||
rightSharpness: 0,
|
||||
rightTransient: 0,
|
||||
delaySeconds: 0
|
||||
)
|
||||
case .off:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private static func updateAuthoredHapticsRouteTransition(
|
||||
to route: AuthoredHapticsRoute,
|
||||
controllerNumber: UInt16,
|
||||
controllerSupport: ControllerSupport
|
||||
) {
|
||||
authoredHapticsRouteLock.lock()
|
||||
let previousRoute = authoredHapticsLastRoute
|
||||
authoredHapticsLastRoute = route
|
||||
authoredHapticsLastRoutedToDualSense = route == .dualSense
|
||||
authoredHapticsRouteLock.unlock()
|
||||
|
||||
if previousRoute != route {
|
||||
stopAuthoredHapticsRoute(
|
||||
previousRoute,
|
||||
controllerNumber: controllerNumber,
|
||||
controllerSupport: controllerSupport
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
@available(iOS 14.0, *)
|
||||
@objc static func presentDualSenseHapticsEQIfNeeded() {
|
||||
@@ -425,6 +643,51 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
@objc(enqueueDualSenseHapticsIRV2WithControllerNumber:flags:sourceSequenceNumber:timestampUs:sourceFrameCount:leftRms:leftPeak:leftTransient:leftLowRatio:leftZeroCrossHz:rightRms:rightPeak:rightTransient:rightLowRatio:rightZeroCrossHz:laneCorrelation:)
|
||||
static func enqueueDualSenseHapticsIRV2(
|
||||
controllerNumber: UInt16,
|
||||
flags: UInt8,
|
||||
sourceSequenceNumber: UInt32,
|
||||
timestampUs: UInt64,
|
||||
sourceFrameCount: UInt32,
|
||||
leftRms: Float,
|
||||
leftPeak: Float,
|
||||
leftTransient: Float,
|
||||
leftLowRatio: Float,
|
||||
leftZeroCrossHz: Float,
|
||||
rightRms: Float,
|
||||
rightPeak: Float,
|
||||
rightTransient: Float,
|
||||
rightLowRatio: Float,
|
||||
rightZeroCrossHz: Float,
|
||||
laneCorrelation: Float
|
||||
) {
|
||||
dualSenseHapticsWorker.async {
|
||||
processDualSenseIRV2Frame(
|
||||
controllerNumber: controllerNumber,
|
||||
flags: flags,
|
||||
sourceSequenceNumber: sourceSequenceNumber,
|
||||
timestampUs: timestampUs,
|
||||
sourceFrameCount: sourceFrameCount,
|
||||
left: makeAuthoredLaneFrame(
|
||||
rmsAmplitude: leftRms,
|
||||
peakAmplitude: leftPeak,
|
||||
transientStrength: leftTransient,
|
||||
lowBandRatio: leftLowRatio,
|
||||
zeroCrossingRateHz: leftZeroCrossHz
|
||||
),
|
||||
right: makeAuthoredLaneFrame(
|
||||
rmsAmplitude: rightRms,
|
||||
peakAmplitude: rightPeak,
|
||||
transientStrength: rightTransient,
|
||||
lowBandRatio: rightLowRatio,
|
||||
zeroCrossingRateHz: rightZeroCrossHz
|
||||
),
|
||||
laneCorrelation: laneCorrelation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@objc static func stopAllDualSenseHaptics() {
|
||||
ControllerSupport.sharedInstance()?.cancelScheduledDualSenseHaptics()
|
||||
|
||||
@@ -434,14 +697,79 @@ import UIKit
|
||||
dualSenseHapticsLock.unlock()
|
||||
|
||||
dualSenseHapticsWorker.async {
|
||||
let controllerNumbers = Array(dualSenseAuthoredEngines.keys)
|
||||
let controllerNumbers = Array(
|
||||
Set(dualSenseAuthoredEngines.keys).union(dualSenseIrRenderStates.keys)
|
||||
)
|
||||
dualSenseAuthoredEngines.removeAll(keepingCapacity: false)
|
||||
dualSenseIrRenderStates.removeAll(keepingCapacity: false)
|
||||
for controllerNumber in controllerNumbers {
|
||||
renderDualSenseHaptics(controllerNumber: controllerNumber, left: nil, right: nil, delay: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func makeAuthoredLaneFrame(
|
||||
rmsAmplitude: Float,
|
||||
peakAmplitude: Float,
|
||||
transientStrength: Float,
|
||||
lowBandRatio: Float,
|
||||
zeroCrossingRateHz: Float
|
||||
) -> AhAuthoredLaneFrame {
|
||||
var lane = AhAuthoredLaneFrame()
|
||||
lane.rms_amplitude = rmsAmplitude
|
||||
lane.peak_amplitude = peakAmplitude
|
||||
lane.transient_strength = transientStrength
|
||||
lane.low_band_ratio = lowBandRatio
|
||||
lane.zero_crossing_rate_hz = zeroCrossingRateHz
|
||||
return lane
|
||||
}
|
||||
|
||||
private static func processDualSenseIRV2Frame(
|
||||
controllerNumber: UInt16,
|
||||
flags: UInt8,
|
||||
sourceSequenceNumber: UInt32,
|
||||
timestampUs: UInt64,
|
||||
sourceFrameCount: UInt32,
|
||||
left: AhAuthoredLaneFrame,
|
||||
right: AhAuthoredLaneFrame,
|
||||
laneCorrelation: Float
|
||||
) {
|
||||
let isDiscontinuity = (flags & 0x01) != 0
|
||||
let isStreamEnd = (flags & 0x04) != 0
|
||||
let isSilent = (flags & 0x08) != 0
|
||||
let state = dualSenseIrRenderStates[controllerNumber] ?? AuthoredIrRenderState()
|
||||
dualSenseIrRenderStates[controllerNumber] = state
|
||||
let delay = state.renderDelay(
|
||||
for: timestampUs,
|
||||
resetTimeline: isDiscontinuity || state.diagnosticFrameCounter == 0
|
||||
)
|
||||
state.diagnosticFrameCounter &+= 1
|
||||
|
||||
renderDualSenseHaptics(
|
||||
controllerNumber: controllerNumber,
|
||||
left: isSilent ? nil : left,
|
||||
right: isSilent ? nil : right,
|
||||
laneCorrelation: laneCorrelation,
|
||||
logDiagnostics: state.diagnosticFrameCounter % 100 == 0,
|
||||
appliesEQ: false,
|
||||
delay: delay
|
||||
)
|
||||
|
||||
if isStreamEnd {
|
||||
renderDualSenseHaptics(
|
||||
controllerNumber: controllerNumber,
|
||||
left: nil,
|
||||
right: nil,
|
||||
appliesEQ: false,
|
||||
delay: delay + 0.005
|
||||
)
|
||||
dualSenseIrRenderStates.removeValue(forKey: controllerNumber)
|
||||
}
|
||||
|
||||
_ = sourceSequenceNumber
|
||||
_ = sourceFrameCount
|
||||
}
|
||||
|
||||
private static func drainDualSensePCMQueue() {
|
||||
while true {
|
||||
dualSenseHapticsLock.lock()
|
||||
@@ -544,6 +872,7 @@ import UIKit
|
||||
right: silent ? nil : frame.lanes.1,
|
||||
laneCorrelation: frame.lane_correlation,
|
||||
logDiagnostics: state.diagnosticFrameCounter % 100 == 0,
|
||||
appliesEQ: false,
|
||||
delay: delay
|
||||
)
|
||||
}
|
||||
@@ -560,26 +889,52 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
@objc static var dualSenseHapticTransient: Float = 1.0
|
||||
private static func renderDualSenseHaptics(
|
||||
controllerNumber: UInt16,
|
||||
left: AhAuthoredLaneFrame?,
|
||||
right: AhAuthoredLaneFrame?,
|
||||
laneCorrelation: Float = 0,
|
||||
logDiagnostics: Bool = false,
|
||||
appliesEQ: Bool = true,
|
||||
delay: TimeInterval
|
||||
) {
|
||||
guard let controllerSupport = ControllerSupport.sharedInstance() else {
|
||||
return
|
||||
}
|
||||
|
||||
let eq = dualSenseHapticsEQSnapshot()
|
||||
let route = selectAuthoredHapticsRoute(
|
||||
controllerNumber: controllerNumber,
|
||||
controllerSupport: controllerSupport
|
||||
)
|
||||
updateAuthoredHapticsRouteTransition(
|
||||
to: route,
|
||||
controllerNumber: controllerNumber,
|
||||
controllerSupport: controllerSupport
|
||||
)
|
||||
|
||||
guard route != .off else {
|
||||
return
|
||||
}
|
||||
|
||||
let eq = appliesEQ ? dualSenseHapticsEQSnapshot(for: route) : [1, 1, 1, 1, 1] as [Float]
|
||||
let masterGain = eq[0]
|
||||
let lowBandGain = eq[1]
|
||||
let highBandGain = eq[2]
|
||||
let transientGain = eq[3]
|
||||
let sharpnessGain = eq[4]
|
||||
|
||||
func renderValues(for lane: AhAuthoredLaneFrame?) -> (amplitude: Float, sharpness: Float, transient: Float) {
|
||||
func rawIrValues(for lane: AhAuthoredLaneFrame?) -> (amplitude: Float, sharpness: Float, transient: Float) {
|
||||
guard let lane else {
|
||||
return (0, 0, 0)
|
||||
}
|
||||
let amplitude = min(max(lane.rms_amplitude, 0), 1)
|
||||
let sharpness = min(max(1 - lane.low_band_ratio, 0), 1)
|
||||
let transient = min(max(lane.transient_strength, 0), 1)
|
||||
return (amplitude, sharpness, transient)
|
||||
}
|
||||
|
||||
func renderValues1(for lane: AhAuthoredLaneFrame?) -> (amplitude: Float, sharpness: Float, transient: Float) {
|
||||
let lowBandRatio = min(max(lane?.low_band_ratio ?? 1, 0), 1)
|
||||
let highBandRatio = 1 - lowBandRatio
|
||||
let effectiveLowBandGain = pow(lowBandGain, 1.6)
|
||||
@@ -604,10 +959,12 @@ import UIKit
|
||||
: immediateSharpness
|
||||
let sourceTransient = min(max(lane?.transient_strength ?? 0, 0), 1)
|
||||
let rmsAmplitude = min(max(lane?.rms_amplitude ?? 0, 0), 1)
|
||||
let perceptualBandTrim = 0.55 + 0.55 * equalizedHighBandRatio
|
||||
let transientBandTrim = 0.55 + 0.35 * lowBandRatio + 0.10 * equalizedHighBandRatio
|
||||
let lowContinuousTrim = 0.78 + 0.22 * lowBandRatio
|
||||
let highContinuousTrim = 0.62 - 0.30 * equalizedHighBandRatio
|
||||
let renderedTransient = min(max(
|
||||
pow(sourceTransient, 0.45) * sqrt(masterGain) * transientGain *
|
||||
bandGain * perceptualBandTrim * 1.35,
|
||||
bandGain * transientBandTrim * 1.35,
|
||||
0
|
||||
), 1)
|
||||
let continuousShare = 1 - 0.85 * min(
|
||||
@@ -617,11 +974,11 @@ import UIKit
|
||||
let lowBody = pow(rmsAmplitude, 1.85) *
|
||||
(1 - highBandRatio) * effectiveLowBandGain * 0.55
|
||||
let highTexture = pow(rmsAmplitude, 0.55) *
|
||||
pow(highBandRatio, 1.15) * effectiveHighBandGain * 0.22
|
||||
pow(highBandRatio, 1.35) * effectiveHighBandGain * 0.09
|
||||
let midBodyTrim = 1 - 0.55 * min(max((representativeFrequency - 90) / 140, 0), 1) *
|
||||
(1 - highBandRatio)
|
||||
let continuousAmplitude = (lowBody + highTexture) * masterGain *
|
||||
perceptualBandTrim * midBodyTrim * continuousShare
|
||||
let continuousAmplitude = (lowBody * lowContinuousTrim + highTexture * highContinuousTrim) *
|
||||
masterGain * midBodyTrim * continuousShare
|
||||
return (
|
||||
min(max(continuousAmplitude, 0), 1),
|
||||
min(max(synthesizedSharpness * sharpnessGain, 0), 1),
|
||||
@@ -629,36 +986,83 @@ import UIKit
|
||||
)
|
||||
}
|
||||
|
||||
var leftValues = renderValues(for: left)
|
||||
var rightValues = renderValues(for: right)
|
||||
func renderValues2(for lane: AhAuthoredLaneFrame?) -> (amplitude: Float, sharpness: Float, transient: Float) {
|
||||
guard let lane else {
|
||||
return (0, 0, 0)
|
||||
}
|
||||
|
||||
let positiveCorrelation = min(max(laneCorrelation, 0), 1)
|
||||
let centerBlend = min(max((positiveCorrelation - 0.1) / 0.9, 0), 1) * 0.85
|
||||
if centerBlend > 0 {
|
||||
let commonAmplitude = sqrt(
|
||||
(leftValues.amplitude * leftValues.amplitude +
|
||||
rightValues.amplitude * rightValues.amplitude) * 0.5
|
||||
let useBuiltinHaptic = route == .device
|
||||
|
||||
let rmsAmplitude = min(max(lane.rms_amplitude, 0), 1)
|
||||
|
||||
let lowBandRatio = min(max(lane.low_band_ratio, 0), 1)
|
||||
let highBandRatio = 1 - lowBandRatio
|
||||
let amplitudeLowBandGain: Float = 1.0
|
||||
let amplitudeHighBandGain: Float = 1.0
|
||||
let bandGain = lowBandRatio * amplitudeLowBandGain + highBandRatio * amplitudeHighBandGain
|
||||
let sourceSharpness = min(max(highBandRatio, 0), 1)
|
||||
let sharpnessLowGain: Float = 0.62
|
||||
let sharpnessMidGain: Float = 0.82
|
||||
let sharpnessHighGain: Float = 1.2
|
||||
let sharpnessLowWeight = min(max((0.50 - sourceSharpness) / 0.50, 0), 1)
|
||||
let sharpnessHighWeight = min(max((sourceSharpness - 0.50) / 0.50, 0), 1)
|
||||
let sharpnessMidWeight = 1 - max(sharpnessLowWeight, sharpnessHighWeight)
|
||||
let sharpnessGain = sharpnessLowWeight * sharpnessLowGain +
|
||||
sharpnessMidWeight * sharpnessMidGain +
|
||||
sharpnessHighWeight * sharpnessHighGain
|
||||
let sharpness = min(max(sourceSharpness * sharpnessGain, 0), 1)
|
||||
|
||||
let transient = min(max(lane.transient_strength, 0), 1)
|
||||
let amplitudeCeiling: Float = 1.0
|
||||
let transientGain: Float = useBuiltinHaptic ? 1.03 : 0.343
|
||||
let transientCeiling: Float = useBuiltinHaptic ? 1.03 : 0.6
|
||||
let transientBottom: Float = useBuiltinHaptic ? 0.14 : 0.0
|
||||
|
||||
return (
|
||||
min(rmsAmplitude * bandGain, amplitudeCeiling),
|
||||
sharpness,
|
||||
min(max(transientBottom, transient * transientGain * 1.6666666667 * dualSenseHapticTransient), transientCeiling)
|
||||
)
|
||||
let amplitudeSum = leftValues.amplitude + rightValues.amplitude
|
||||
let commonSharpness = amplitudeSum > 0.000_001
|
||||
? (leftValues.sharpness * leftValues.amplitude +
|
||||
rightValues.sharpness * rightValues.amplitude) / amplitudeSum
|
||||
: max(leftValues.sharpness, rightValues.sharpness)
|
||||
let commonTransient = max(leftValues.transient, rightValues.transient)
|
||||
|
||||
leftValues.amplitude += (commonAmplitude - leftValues.amplitude) * centerBlend
|
||||
rightValues.amplitude += (commonAmplitude - rightValues.amplitude) * centerBlend
|
||||
leftValues.sharpness += (commonSharpness - leftValues.sharpness) * centerBlend
|
||||
rightValues.sharpness += (commonSharpness - rightValues.sharpness) * centerBlend
|
||||
leftValues.transient += (commonTransient - leftValues.transient) * centerBlend
|
||||
rightValues.transient += (commonTransient - rightValues.transient) * centerBlend
|
||||
}
|
||||
|
||||
var leftValues = appliesEQ ? renderValues1(for: left) : renderValues2(for: left)
|
||||
var rightValues = appliesEQ ? renderValues1(for: right) : renderValues2(for: right)
|
||||
|
||||
if appliesEQ {
|
||||
let positiveCorrelation = min(max(laneCorrelation, 0), 1)
|
||||
let commonHighBandRatio = (
|
||||
authoredHighBandRatio(for: left) + authoredHighBandRatio(for: right)
|
||||
) * 0.5
|
||||
let dualSenseCenterBlendScale = 0.10 + 0.55 * commonHighBandRatio
|
||||
let centerBlendScale: Float = route == .dualSense ? dualSenseCenterBlendScale : 0.85
|
||||
let centerBlend = min(max((positiveCorrelation - 0.1) / 0.9, 0), 1) * centerBlendScale
|
||||
if centerBlend > 0 {
|
||||
let commonAmplitude = sqrt(
|
||||
(leftValues.amplitude * leftValues.amplitude +
|
||||
rightValues.amplitude * rightValues.amplitude) * 0.5
|
||||
)
|
||||
let amplitudeSum = leftValues.amplitude + rightValues.amplitude
|
||||
let commonSharpness = amplitudeSum > 0.000_001
|
||||
? (leftValues.sharpness * leftValues.amplitude +
|
||||
rightValues.sharpness * rightValues.amplitude) / amplitudeSum
|
||||
: max(leftValues.sharpness, rightValues.sharpness)
|
||||
let commonTransient = max(leftValues.transient, rightValues.transient)
|
||||
|
||||
leftValues.amplitude += (commonAmplitude - leftValues.amplitude) * centerBlend
|
||||
rightValues.amplitude += (commonAmplitude - rightValues.amplitude) * centerBlend
|
||||
leftValues.sharpness += (commonSharpness - leftValues.sharpness) * centerBlend
|
||||
rightValues.sharpness += (commonSharpness - rightValues.sharpness) * centerBlend
|
||||
leftValues.transient += (commonTransient - leftValues.transient) * centerBlend
|
||||
rightValues.transient += (commonTransient - rightValues.transient) * centerBlend
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if logDiagnostics {
|
||||
NSLog(
|
||||
"DS5 authored: c=%u srcL[rms=%.3f high=%.3f hz=%.0f] srcR[rms=%.3f high=%.3f hz=%.0f] corr=%.2f outL[a=%.3f s=%.3f t=%.3f] outR[a=%.3f s=%.3f t=%.3f]",
|
||||
"DS5 authored: c=%u route=%@ srcL[rms=%.3f high=%.3f hz=%.0f] srcR[rms=%.3f high=%.3f hz=%.0f] corr=%.2f outL[a=%.3f s=%.3f t=%.3f] outR[a=%.3f s=%.3f t=%.3f]",
|
||||
controllerNumber,
|
||||
route == .dualSense ? "dualsense" : "device",
|
||||
Double(left?.rms_amplitude ?? 0),
|
||||
Double(1 - min(max(left?.low_band_ratio ?? 1, 0), 1)),
|
||||
Double(min(max((left?.zero_crossing_rate_hz ?? 0) * 0.5, 0), 400)),
|
||||
@@ -674,18 +1078,47 @@ import UIKit
|
||||
Double(rightValues.transient)
|
||||
)
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
controllerSupport.renderDualSenseHaptics(
|
||||
controllerNumber,
|
||||
leftAmplitude: leftValues.amplitude,
|
||||
leftSharpness: leftValues.sharpness,
|
||||
leftTransient: leftValues.transient,
|
||||
rightAmplitude: rightValues.amplitude,
|
||||
rightSharpness: rightValues.sharpness,
|
||||
rightTransient: rightValues.transient,
|
||||
delaySeconds: delay
|
||||
)
|
||||
switch route {
|
||||
case .dualSense:
|
||||
if appliesEQ {
|
||||
leftValues = compensatedDualSenseControllerValues(
|
||||
leftValues,
|
||||
highBandRatio: authoredHighBandRatio(for: left)
|
||||
)
|
||||
rightValues = compensatedDualSenseControllerValues(
|
||||
rightValues,
|
||||
highBandRatio: authoredHighBandRatio(for: right)
|
||||
)
|
||||
let mixedValues = mixedDualSenseControllerHandleValues(left: leftValues, right: rightValues)
|
||||
leftValues = mixedValues.left
|
||||
rightValues = mixedValues.right
|
||||
}
|
||||
controllerSupport.renderDualSenseHaptics(
|
||||
controllerNumber,
|
||||
leftAmplitude: leftValues.amplitude,
|
||||
leftSharpness: leftValues.sharpness,
|
||||
leftTransient: leftValues.transient,
|
||||
rightAmplitude: rightValues.amplitude,
|
||||
rightSharpness: rightValues.sharpness,
|
||||
rightTransient: rightValues.transient,
|
||||
delaySeconds: delay
|
||||
)
|
||||
case .device:
|
||||
controllerSupport.renderDeviceDualSenseHaptics(
|
||||
controllerNumber,
|
||||
leftAmplitude: leftValues.amplitude,
|
||||
leftSharpness: leftValues.sharpness,
|
||||
leftTransient: leftValues.transient,
|
||||
rightAmplitude: rightValues.amplitude,
|
||||
rightSharpness: rightValues.sharpness,
|
||||
rightTransient: rightValues.transient,
|
||||
delaySeconds: delay
|
||||
)
|
||||
case .off:
|
||||
break
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -711,6 +1144,7 @@ import UIKit
|
||||
let elementDict = tempMap as NSDictionary
|
||||
|
||||
// 单一 gamepad.valueChangedHandler
|
||||
|
||||
gamepad.valueChangedHandler = { gamepad, element in
|
||||
handler(elementDict, gamepad, element)
|
||||
if #available(iOS 13.0, *) {
|
||||
|
||||
@@ -65,6 +65,7 @@ typedef NS_ENUM(NSInteger, PencilAndHoverMode) {
|
||||
@property (nonatomic, assign) CGFloat touchPointerVelocityFactor;
|
||||
@property (nonatomic, assign) CGPoint normalizedStreamViewOffset;
|
||||
@property (nonatomic, assign) CGFloat streamViewScale;
|
||||
@property (nonatomic, assign) CGFloat dualSenseTransient;
|
||||
|
||||
@property (nonatomic, assign) bool gamepadOverlayEnabled;
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
[encoder encodeFloat:self.touchPointerVelocityFactor forKey:@"touchPointerVelocityFactor"];
|
||||
[encoder encodeCGPoint:self.normalizedStreamViewOffset forKey:@"normalizedStreamViewOffset"];
|
||||
[encoder encodeFloat:self.streamViewScale forKey:@"streamViewScale"];
|
||||
[encoder encodeFloat:self.dualSenseTransient forKey:@"dualSenseTransient"];
|
||||
[encoder encodeBool:self.gamepadOverlayEnabled forKey:@"gamepadOverlayEnabled"];
|
||||
|
||||
[encoder encodeObject:self.pressureCurvePoints forKey:@"pressureCurvePoints"];
|
||||
@@ -132,7 +133,8 @@
|
||||
|
||||
self.normalizedStreamViewOffset = [decoder containsValueForKey:@"normalizedStreamViewOffset"] ? [decoder decodeCGPointForKey:@"normalizedStreamViewOffset"] : CGPointZero;
|
||||
self.streamViewScale = [decoder containsValueForKey:@"streamViewScale"] ? [decoder decodeFloatForKey:@"streamViewScale"] : 1.0;
|
||||
|
||||
self.dualSenseTransient = [decoder containsValueForKey:@"dualSenseTransient"] ? [decoder decodeFloatForKey:@"dualSenseTransient"] : 1.0;
|
||||
|
||||
self.gamepadOverlayEnabled = [decoder containsValueForKey:@"gamepadOverlayEnabled"] ? [decoder decodeBoolForKey:@"gamepadOverlayEnabled"] : false;
|
||||
|
||||
self.pressureCurvePoints =
|
||||
|
||||
@@ -18,14 +18,20 @@
|
||||
@implementation HapticContext {
|
||||
GCControllerPlayerIndex _playerIndex;
|
||||
CHHapticEngine* _hapticEngine API_AVAILABLE(ios(13.0), tvos(14.0));
|
||||
id<CHHapticPatternPlayer> _hapticPlayer API_AVAILABLE(ios(13.0), tvos(14.0));
|
||||
BOOL _playing;
|
||||
id<CHHapticPatternPlayer> _motorHapticPlayer API_AVAILABLE(ios(13.0), tvos(14.0));
|
||||
id<CHHapticPatternPlayer> _authoredHapticPlayer API_AVAILABLE(ios(13.0), tvos(14.0));
|
||||
BOOL _motorPlaying;
|
||||
BOOL _authoredPlaying;
|
||||
}
|
||||
|
||||
-(void)cleanup API_AVAILABLE(ios(14.0), tvos(14.0)) {
|
||||
if (_hapticPlayer != nil) {
|
||||
[_hapticPlayer cancelAndReturnError:nil];
|
||||
_hapticPlayer = nil;
|
||||
if (_motorHapticPlayer != nil) {
|
||||
[_motorHapticPlayer cancelAndReturnError:nil];
|
||||
_motorHapticPlayer = nil;
|
||||
}
|
||||
if (_authoredHapticPlayer != nil) {
|
||||
[_authoredHapticPlayer cancelAndReturnError:nil];
|
||||
_authoredHapticPlayer = nil;
|
||||
}
|
||||
if (_hapticEngine != nil) {
|
||||
[_hapticEngine stopWithCompletionHandler:nil];
|
||||
@@ -43,15 +49,15 @@
|
||||
|
||||
// Stop the effect entirely if the amplitude is 0
|
||||
if (amplitude == 0) {
|
||||
if (_playing) {
|
||||
[_hapticPlayer stopAtTime:0 error:&error];
|
||||
_playing = NO;
|
||||
if (_motorPlaying) {
|
||||
[_motorHapticPlayer stopAtTime:0 error:&error];
|
||||
_motorPlaying = NO;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hapticPlayer == nil) {
|
||||
if (_motorHapticPlayer == nil) {
|
||||
// We must initialize the intensity to 1.0f because the dynamic parameters are multiplied by this value before being applied
|
||||
CHHapticEventParameter* intensityParameter = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:1.0f];
|
||||
CHHapticEvent* hapticEvent = [[CHHapticEvent alloc] initWithEventType:CHHapticEventTypeHapticContinuous parameters:[NSArray arrayWithObject:intensityParameter] relativeTime:0 duration:GCHapticDurationInfinite];
|
||||
@@ -61,7 +67,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
_hapticPlayer = [_hapticEngine createPlayerWithPattern:hapticPattern error:&error];
|
||||
_motorHapticPlayer = [_hapticEngine createPlayerWithPattern:hapticPattern error:&error];
|
||||
if (error != nil) {
|
||||
Log(LOG_W, @"Controller %d: Haptic player creation failed: %@", _playerIndex, error);
|
||||
return;
|
||||
@@ -69,21 +75,21 @@
|
||||
}
|
||||
|
||||
CHHapticDynamicParameter* intensityParameter = [[CHHapticDynamicParameter alloc] initWithParameterID:CHHapticDynamicParameterIDHapticIntensityControl value:amplitude / 65535.0f relativeTime:0];
|
||||
[_hapticPlayer sendParameters:[NSArray arrayWithObject:intensityParameter] atTime:CHHapticTimeImmediate error:&error];
|
||||
[_motorHapticPlayer sendParameters:[NSArray arrayWithObject:intensityParameter] atTime:CHHapticTimeImmediate error:&error];
|
||||
if (error != nil) {
|
||||
Log(LOG_W, @"Controller %d: Haptic player parameter update failed: %@", _playerIndex, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_playing) {
|
||||
[_hapticPlayer startAtTime:0 error:&error];
|
||||
if (!_motorPlaying) {
|
||||
[_motorHapticPlayer startAtTime:0 error:&error];
|
||||
if (error != nil) {
|
||||
_hapticPlayer = nil;
|
||||
_motorHapticPlayer = nil;
|
||||
Log(LOG_W, @"Controller %d: Haptic playback start failed: %@", _playerIndex, error);
|
||||
return;
|
||||
}
|
||||
|
||||
_playing = YES;
|
||||
_motorPlaying = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,13 +106,13 @@
|
||||
|
||||
NSError* error = nil;
|
||||
if (amplitude == 0.0f) {
|
||||
if (_playing) {
|
||||
[_hapticPlayer stopAtTime:CHHapticTimeImmediate error:&error];
|
||||
_playing = NO;
|
||||
if (_authoredPlaying) {
|
||||
[_authoredHapticPlayer stopAtTime:CHHapticTimeImmediate error:&error];
|
||||
_authoredPlaying = NO;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_hapticPlayer == nil) {
|
||||
if (_authoredHapticPlayer == nil) {
|
||||
CHHapticEventParameter* intensity = [[CHHapticEventParameter alloc]
|
||||
initWithParameterID:CHHapticEventParameterIDHapticIntensity value:1.0f];
|
||||
CHHapticEventParameter* eventSharpness = [[CHHapticEventParameter alloc]
|
||||
@@ -122,8 +128,8 @@
|
||||
Log(LOG_W, @"Controller %d: Authored haptic pattern creation failed: %@", _playerIndex, error);
|
||||
return;
|
||||
}
|
||||
_hapticPlayer = [_hapticEngine createPlayerWithPattern:pattern error:&error];
|
||||
if (_hapticPlayer == nil || error != nil) {
|
||||
_authoredHapticPlayer = [_hapticEngine createPlayerWithPattern:pattern error:&error];
|
||||
if (_authoredHapticPlayer == nil || error != nil) {
|
||||
Log(LOG_W, @"Controller %d: Authored haptic player creation failed: %@", _playerIndex, error);
|
||||
return;
|
||||
}
|
||||
@@ -137,19 +143,19 @@
|
||||
initWithParameterID:CHHapticDynamicParameterIDHapticSharpnessControl
|
||||
value:sharpness relativeTime:0]
|
||||
];
|
||||
[_hapticPlayer sendParameters:parameters atTime:CHHapticTimeImmediate error:&error];
|
||||
[_authoredHapticPlayer sendParameters:parameters atTime:CHHapticTimeImmediate error:&error];
|
||||
if (error != nil) {
|
||||
Log(LOG_W, @"Controller %d: Authored haptic parameter update failed: %@", _playerIndex, error);
|
||||
return;
|
||||
}
|
||||
if (!_playing) {
|
||||
[_hapticPlayer startAtTime:CHHapticTimeImmediate error:&error];
|
||||
if (!_authoredPlaying) {
|
||||
[_authoredHapticPlayer startAtTime:CHHapticTimeImmediate error:&error];
|
||||
if (error != nil) {
|
||||
_hapticPlayer = nil;
|
||||
_authoredHapticPlayer = nil;
|
||||
Log(LOG_W, @"Controller %d: Authored haptic playback start failed: %@", _playerIndex, error);
|
||||
return;
|
||||
}
|
||||
_playing = YES;
|
||||
_authoredPlaying = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,9 +203,11 @@
|
||||
}
|
||||
|
||||
Log(LOG_W, @"Controller %d: Haptic engine stopped: %p", me->_playerIndex, stoppedReason);
|
||||
me->_hapticPlayer = nil;
|
||||
me->_motorHapticPlayer = nil;
|
||||
me->_authoredHapticPlayer = nil;
|
||||
me->_hapticEngine = nil;
|
||||
me->_playing = NO;
|
||||
me->_motorPlaying = NO;
|
||||
me->_authoredPlaying = NO;
|
||||
};
|
||||
_hapticEngine.resetHandler = ^{
|
||||
HapticContext* me = weakSelf;
|
||||
@@ -208,8 +216,10 @@
|
||||
}
|
||||
|
||||
Log(LOG_W, @"Controller %d: Haptic engine reset", me->_playerIndex);
|
||||
me->_hapticPlayer = nil;
|
||||
me->_playing = NO;
|
||||
me->_motorHapticPlayer = nil;
|
||||
me->_authoredHapticPlayer = nil;
|
||||
me->_motorPlaying = NO;
|
||||
me->_authoredPlaying = NO;
|
||||
[me->_hapticEngine startAndReturnError:nil];
|
||||
};
|
||||
|
||||
@@ -258,9 +268,11 @@
|
||||
}
|
||||
|
||||
Log(LOG_W, @"Controller %d: Haptic engine stopped: %p", me->_playerIndex, stoppedReason);
|
||||
me->_hapticPlayer = nil;
|
||||
me->_motorHapticPlayer = nil;
|
||||
me->_authoredHapticPlayer = nil;
|
||||
me->_hapticEngine = nil;
|
||||
me->_playing = NO;
|
||||
me->_motorPlaying = NO;
|
||||
me->_authoredPlaying = NO;
|
||||
};
|
||||
_hapticEngine.resetHandler = ^{
|
||||
HapticContext* me = weakSelf;
|
||||
@@ -269,8 +281,10 @@
|
||||
}
|
||||
|
||||
Log(LOG_W, @"Controller %d: Haptic engine reset", me->_playerIndex);
|
||||
me->_hapticPlayer = nil;
|
||||
me->_playing = NO;
|
||||
me->_motorHapticPlayer = nil;
|
||||
me->_authoredHapticPlayer = nil;
|
||||
me->_motorPlaying = NO;
|
||||
me->_authoredPlaying = NO;
|
||||
[me->_hapticEngine startAndReturnError:nil];
|
||||
};
|
||||
|
||||
|
||||
@@ -179,6 +179,9 @@ import CoreMotion
|
||||
|
||||
if useBuiltinGyro {
|
||||
if motionManager.isGyroAvailable {
|
||||
if rollToLeftStick {
|
||||
leftStickMotion = leftStickTouchInputX
|
||||
}
|
||||
motionManager.startDeviceMotionUpdates(to: .main) { [weak self] motionData, _ in
|
||||
guard let self = self, let data = motionData else { return }
|
||||
self.handleMotionData(deviceMotion: data)
|
||||
@@ -229,7 +232,7 @@ import CoreMotion
|
||||
}
|
||||
|
||||
/// 停止更新
|
||||
private var interruptNoneGyroInput:Bool = false
|
||||
// private var interruptNoneGyroInput:Bool = false
|
||||
|
||||
@objc public func stopMotionUpdate(interruptNoneGyroInput:Bool=false) {
|
||||
motionControlStarted = false
|
||||
@@ -362,7 +365,7 @@ import CoreMotion
|
||||
}
|
||||
|
||||
if !motionControlStarted {
|
||||
self.clearGyroInput(interruptNonGyroInput: interruptNoneGyroInput)
|
||||
self.clearGyroInput(interruptNonGyroInput: false)
|
||||
// print("Gyro: stopped")
|
||||
return
|
||||
}
|
||||
@@ -394,12 +397,12 @@ import CoreMotion
|
||||
if rollToLeftStick {
|
||||
roll = gyroInputToStickInput(input:rollSource*sensitvityRoll*widgetRollFactor*0.2)
|
||||
|
||||
leftStickMotion = useBuiltinGyro ? -stickMaxOffset*(gravityYOffset/Double.pi)*3*sensitvityRoll*widgetRollFactor : leftStickMotion + roll
|
||||
leftStickMotion = self.clampStickInput(input: useBuiltinGyro ? -stickMaxOffset*(gravityYOffset/Double.pi)*3*sensitvityRoll*widgetRollFactor : leftStickMotion + roll)
|
||||
|
||||
let mixedLeftStickOffsetX = self.clampStickInput(input: leftStickMotion+leftStickTouchInputX+leftStickPhysicalInputX)
|
||||
let mixedLeftStickOffsetY = self.clampStickInput(input: leftStickTouchInputY+leftStickPhysicalInputY)
|
||||
|
||||
let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: mixedLeftStickOffsetX, dy: mixedLeftStickOffsetY), minOffset: gyroToStickMinOffset)
|
||||
let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: mixedLeftStickOffsetX, dy: mixedLeftStickOffsetY), minOffset: gyroToStickMinOffset, circulate: true)
|
||||
|
||||
onScreenControls?.sendLeftStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
|
||||
}
|
||||
@@ -410,11 +413,19 @@ import CoreMotion
|
||||
guard ControllerUtil.gamepadArrivalReported else { return }
|
||||
|
||||
if yawPitchToRightStick{
|
||||
onScreenControls?.sendRightStickTouchPadEvent(rightStickPhysicalInputX+rightStickTouchInputX-yawBias, rightStickPhysicalInputY+rightStickTouchInputY-pitchBias)
|
||||
let clearX = rightStickPhysicalInputX+rightStickTouchInputX-yawBias
|
||||
let clearY = rightStickPhysicalInputY+rightStickTouchInputY-pitchBias
|
||||
let clearVector = ControllerUtil.circulated(offsetVector: CGVector(dx: clearX, dy: clearY))
|
||||
|
||||
onScreenControls?.sendRightStickTouchPadEvent(clearVector.dx, clearVector.dy)
|
||||
}
|
||||
if rollToLeftStick{
|
||||
leftStickMotion = 0
|
||||
onScreenControls?.sendLeftStickTouchPadEvent(leftStickPhysicalInputX+leftStickTouchInputX-rollBias,leftStickPhysicalInputY+leftStickTouchInputY)
|
||||
leftStickMotion = leftStickTouchInputX
|
||||
let clearX = leftStickPhysicalInputX+leftStickTouchInputX-rollBias
|
||||
let clearY = leftStickPhysicalInputY+leftStickTouchInputY
|
||||
let clearVector = ControllerUtil.circulated(offsetVector: CGVector(dx: clearX, dy: clearY))
|
||||
|
||||
onScreenControls?.sendLeftStickTouchPadEvent(clearVector.dx,clearVector.dy)
|
||||
}
|
||||
if(interruptNonGyroInput){
|
||||
onScreenControls?.clearLeftStickTouchPadFlag()
|
||||
|
||||
@@ -2000,12 +2000,13 @@ import ObjectiveC.runtime
|
||||
|
||||
let mixRightStickInputToGyro = (oscProfile.mapGyroTo == .mapGyroToControllerStick
|
||||
&& oscProfile.yawPitchToRightStick)
|
||||
|
||||
stickOffsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: targetX, dy: targetY), minOffset: minStickOffset, circulate: circulate)
|
||||
|
||||
if !mixRightStickInputToGyro || (self.motionHandler?.gyroMixInputStarted() != true) {
|
||||
|
||||
stickOffsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: targetX, dy: targetY), minOffset: minStickOffset, circulate: circulate)
|
||||
self.onScreenControls?.sendRightStickTouchPadEvent(stickOffsetVector.dx, stickOffsetVector.dy)
|
||||
}
|
||||
self.motionHandler?.mixOnScreenRightStickAndGyroInput(x: targetX, y: targetY)
|
||||
self.motionHandler?.mixOnScreenRightStickAndGyroInput(x: stickOffsetVector.dx, y: stickOffsetVector.dy)
|
||||
if !ControllerUtil.gamepadArrivalReported {ControllerUtil.gamepadArrivalReported = true}
|
||||
}
|
||||
|
||||
@@ -2015,12 +2016,13 @@ import ObjectiveC.runtime
|
||||
|
||||
let mixLeftStickInputToGyro = (oscProfile.mapGyroTo == .mapGyroToControllerStick
|
||||
&& oscProfile.rollToLeftStick)
|
||||
|
||||
stickOffsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: targetX, dy: targetY), minOffset: minStickOffset, circulate: circulate)
|
||||
|
||||
if !mixLeftStickInputToGyro || (self.motionHandler?.gyroMixInputStarted() != true) {
|
||||
|
||||
stickOffsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: targetX, dy: targetY), minOffset: minStickOffset, circulate: circulate)
|
||||
self.onScreenControls?.sendLeftStickTouchPadEvent(stickOffsetVector.dx, stickOffsetVector.dy)
|
||||
}
|
||||
self.motionHandler?.mixOnScreenLeftStickAndGyroInput(x: targetX, y: targetY)
|
||||
self.motionHandler?.mixOnScreenLeftStickAndGyroInput(x: stickOffsetVector.dx, y: stickOffsetVector.dy)
|
||||
if !ControllerUtil.gamepadArrivalReported {ControllerUtil.gamepadArrivalReported = true}
|
||||
}
|
||||
|
||||
|
||||
@@ -714,6 +714,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
OnScreenWidgetView.buttonVisualFeedbackEnabled = self->settings.buttonVisualFeedback;
|
||||
OnScreenWidgetView.gamepadOverlayFLag = profile.gamepadOverlayEnabled;
|
||||
ControllerUtil.dualSenseHapticTransient = profile.dualSenseTransient;
|
||||
|
||||
bool hasLegacyWidget = false;
|
||||
if(reloadWidgets && !OnScreenWidgetView.editMode){
|
||||
|
||||
@@ -8212,25 +8212,25 @@
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Sunshine emulates game controller via VigemBus, which support DS4 & Xbox360. \n\n- Xbox360 does not support gyro.\n\n- When “both” is selected, the app will map built-in gyro as player0 & external game controller as player1"
|
||||
"value" : "Sunshine emulates game controller via VigemBus, which support DS4 & Xbox360. \n- Xbox360 does not support gyro.\n- When “both” is selected, the app will map built-in gyro as player0 & external game controller as player1\n\nWhen DualSense sidecar is running on Sunshine-Foundation, VigemBus will be taken over, left DualSense to be the emulated Controller on your host. In this case:\n- The option “PS (Enhanced Haptic)” provides better haptic feedback for a real DualSense controller.\n- The option “PS” provides DualSense haptic feedback translation to traditional rumbling for all controllers.\n- Enhanced haptics are also available with iPhone motor by setting “Haptic Engine” to “Device”.\n"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Sunshine 通过 VigemBus 模拟游戏手柄,支持 DS4 和 Xbox 360。\n\n- Xbox 360 不支持陀螺仪。\n\n- 当选择“同时映射”时,app会将内置陀螺仪映射为玩家0,外接手柄映射为玩家1。"
|
||||
"value" : "Sunshine 通过 VigemBus 模拟游戏手柄,支持 DS4 和 Xbox 360。\n- Xbox 360 不支持陀螺仪。\n- 当选择“同时映射”时,app会将内置陀螺仪映射为玩家0,外接手柄映射为玩家1。\n\n当 DualSense 组件在【Sunshine 基地版】上运行时,它会接管 ViGEmBus,并使 DualSense 成为主机端的模拟手柄。在这种情况下:\n- 选择“PS(触觉增强)”将为真实的DualSense 手柄提供更好的触觉反馈。\n- 选择“PS”:将 DualSense 触觉反馈转换为传统震动,适用于所有手柄。\n- 将 “振动引擎” 设置为 “内置”,也可以通过 iPhone 马达获得增强触觉反馈。\n\nDualSense的自适应板机、触觉反馈仅与Sunshine基地版配合使用时有效。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Sunshine 透過 VigemBus 模擬遊戲控制器,支援 DS4 和 Xbox 360。\n\n- Xbox 360 不支援陀螺儀。\n\n- 當選擇「同時映射」時,app 會將內建陀螺儀對應為玩家0,外接控制器對應為玩家1。"
|
||||
"value" : "Sunshine 透過 VigemBus 模擬遊戲控制器,支援 DS4 和 Xbox 360。\n- Xbox 360 不支援陀螺儀。\n- 當選擇「同時映射」時,app 會將內建陀螺儀對應為玩家0,外接控制器對應為玩家1。\n\n當 DualSense 元件在 「Sunshine 基地版」上執行時,它會接管 ViGEmBus,並使 DualSense 成為主機端的模擬手把。在這種情況下:\n- 選擇「PS(觸覺增強)」將為真實的 DualSense 手把提供更好的觸覺回饋。\n- 選擇「PS」:將 DualSense 觸覺回饋轉換為傳統震動,適用於所有手把。\n- 將「振動引擎」設定為「內建」,也可以透過 iPhone 馬達獲得增強觸覺回饋。\n\nDualSense 的自適應扳機、觸覺回饋僅在與 Sunshine 基地版配合使用時有效。"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Sunshine 透過 VigemBus 模擬遊戲控制器,支援 DS4 和 Xbox 360。\n\n- Xbox 360 不支援陀螺儀。\n\n- 當選擇「同時映射」時,app 會將內建陀螺儀對應為玩家0,外接控制器對應為玩家1。"
|
||||
"value" : "Sunshine 透過 VigemBus 模擬遊戲控制器,支援 DS4 和 Xbox 360。\n- Xbox 360 不支援陀螺儀。\n- 當選擇「同時映射」時,app 會將內建陀螺儀對應為玩家0,外接控制器對應為玩家1。\n\n當 DualSense 元件在 「Sunshine 基地版」上執行時,它會接管 ViGEmBus,並使 DualSense 成為主機端的模擬手把。在這種情況下:\n- 選擇「PS(觸覺增強)」將為真實的 DualSense 手把提供更好的觸覺回饋。\n- 選擇「PS」:將 DualSense 觸覺回饋轉換為傳統震動,適用於所有手把。\n- 將「振動引擎」設定為「內建」,也可以透過 iPhone 馬達獲得增強觸覺回饋。\n\nDualSense 的自適應扳機、觸覺回饋僅在與 Sunshine 基地版配合使用時有效。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8256,7 +8256,8 @@
|
||||
"value" : "啟用 Metal 渲染?"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"shouldTranslate" : false
|
||||
},
|
||||
"enableOswForNativeTouchSwitchTip" : {
|
||||
"extractionState" : "manual",
|
||||
@@ -9890,25 +9891,25 @@
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "This setting is saved in [Game Profile]. To open game profile selector:\n- Tap the gamepad icon on navigation bar\n- While streaming, swipe inward from the %@ edge of the screen by %@ of screen width, open [Switch Game Profile] in the toolbox menu"
|
||||
"value" : "This setting is saved in [Game Profile]. To open game profile selector:\n- Tap the gamepad icon on navigation bar\n- While streaming, swipe inward from the %@ edge of the screen by %@ of screen width, open [Switch Game Profile] in the toolbox menu\n- Also available by controller radial menu -> [more options]"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "该设置保存在 [游戏配置] 中. 游戏配置切换入口:\n- 点击导航栏的手柄图标\n- 串流时,由屏幕 %@ 边缘向内滑动 %@ 屏宽,在工具箱菜单中打开 [切换游戏配置]"
|
||||
"state" : "needs_review",
|
||||
"value" : "该设置保存在 [游戏配置] 中. 游戏配置切换入口:\n- 点击导航栏的手柄图标\n- 串流时,由屏幕 %@ 边缘向内滑动 %@ 屏宽,在工具箱菜单中打开 [切换游戏配置]\n- 手柄用户请在环形菜单 -> [更多] 中打开"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此設定保存在[遊戲配置]中。遊戲配置切換入口:\n- 點擊導覽列的手把圖示\n- 串流時,由螢幕 %@ 邊緣向內滑動 %@ 螢幕寬度,在工具箱選單中開啟[切換遊戲配置]"
|
||||
"state" : "needs_review",
|
||||
"value" : "此設定保存在[遊戲配置]中。遊戲配置切換入口:\n- 點擊導覽列的手把圖示\n- 串流時,由螢幕 %@ 邊緣向內滑動 %@ 螢幕寬度,在工具箱選單中開啟[切換遊戲配置]\n- 手把使用者請在環形選單 -> [更多] 中開啟"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此設定保存在[遊戲配置]中。遊戲配置切換入口:\n- 點擊導覽列的手把圖示\n- 串流時,由螢幕 %@ 邊緣向內滑動 %@ 螢幕寬度,在工具箱選單中開啟[切換遊戲配置]"
|
||||
"state" : "needs_review",
|
||||
"value" : "此設定保存在[遊戲配置]中。遊戲配置切換入口:\n- 點擊導覽列的手把圖示\n- 串流時,由螢幕 %@ 邊緣向內滑動 %@ 螢幕寬度,在工具箱選單中開啟[切換遊戲配置]\n- 手把使用者請在環形選單 -> [更多] 中開啟"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
@interface Connection : NSOperation <NSStreamDelegate>
|
||||
@property (class, nonatomic, assign) bool muteInBackground;
|
||||
@property (class, nonatomic, assign) bool useSystemAudioEngine;
|
||||
@property (class, nonatomic, assign) bool useDualSenseHapticsIrV2;
|
||||
|
||||
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)callbacks;
|
||||
-(void) terminate;
|
||||
@@ -32,4 +33,8 @@
|
||||
+ (void)setVolume:(float)newVolume;
|
||||
+ (void)resetSysAudioPlayback;
|
||||
|
||||
+ (bool)useDualSenseAuthoredPCM;
|
||||
+ (void)setuseDualSenseAuthoredPCM:(bool)use;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -61,6 +61,7 @@ static AVAudioFormat *audioFormat;
|
||||
static bool muteInBackground;
|
||||
static bool fullColorRange;
|
||||
static bool request10BitCodec;
|
||||
static bool useDualSenseAuthoredPCM = false;
|
||||
|
||||
static VideoDecoderRenderer* renderer;
|
||||
|
||||
@@ -357,6 +358,14 @@ void ArCleanup(void)
|
||||
useSystemAudioEngine = useSysAudioEngine;
|
||||
}
|
||||
|
||||
+ (bool)useDualSenseAuthoredPCM {
|
||||
return useDualSenseAuthoredPCM;
|
||||
}
|
||||
|
||||
+ (void)setuseDualSenseAuthoredPCM:(bool)use {
|
||||
useDualSenseAuthoredPCM = use;
|
||||
}
|
||||
|
||||
void AudioEngineInit(int sampleRate, int channelCount) {
|
||||
|
||||
audioEngine = [[AVAudioEngine alloc] init];
|
||||
@@ -604,6 +613,30 @@ void ClDs5HapticsPcm(const LI_DS5_HAPTICS_PCM_FRAME* frame)
|
||||
pcmData:pcmData];
|
||||
}
|
||||
|
||||
void ClDs5HapticsIrV2(const LI_DS5_HAPTICS_IR_FRAME_V2* frame)
|
||||
{
|
||||
if (frame == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
[ControllerUtil enqueueDualSenseHapticsIRV2WithControllerNumber:frame->controllerNumber
|
||||
flags:frame->flags
|
||||
sourceSequenceNumber:frame->sourceSequenceNumber
|
||||
timestampUs:frame->timestampUs
|
||||
sourceFrameCount:frame->sourceFrameCount
|
||||
leftRms:frame->lanes[0].rmsAmplitude
|
||||
leftPeak:frame->lanes[0].peakAmplitude
|
||||
leftTransient:frame->lanes[0].transientStrength
|
||||
leftLowRatio:frame->lanes[0].lowBandRatio
|
||||
leftZeroCrossHz:frame->lanes[0].zeroCrossingRateHz
|
||||
rightRms:frame->lanes[1].rmsAmplitude
|
||||
rightPeak:frame->lanes[1].peakAmplitude
|
||||
rightTransient:frame->lanes[1].transientStrength
|
||||
rightLowRatio:frame->lanes[1].lowBandRatio
|
||||
rightZeroCrossHz:frame->lanes[1].zeroCrossingRateHz
|
||||
laneCorrelation:frame->laneCorrelation];
|
||||
}
|
||||
|
||||
-(void) terminate
|
||||
{
|
||||
// Interrupt any action blocking LiStartConnection(). This is
|
||||
@@ -706,6 +739,7 @@ void ClDs5HapticsPcm(const LI_DS5_HAPTICS_PCM_FRAME* frame)
|
||||
_streamConfig.audioConfiguration = config.audioConfiguration;
|
||||
_streamConfig.redirectMic = config.redirectMic && [MicHandler permissionGranted];
|
||||
[Connection setVolume:config.localVolume];
|
||||
|
||||
// Since we require iOS 12 or above, we're guaranteed to be running
|
||||
// on a 64-bit device with ARMv8 crypto instructions, so we don't
|
||||
// need to check for that here.
|
||||
@@ -767,8 +801,13 @@ void ClDs5HapticsPcm(const LI_DS5_HAPTICS_PCM_FRAME* frame)
|
||||
_clCallbacks.setMotionEventState = ClSetMotionEventState;
|
||||
_clCallbacks.setControllerLED = ClSetControllerLED;
|
||||
_clCallbacks.setAdaptiveTriggers = ClSetAdaptiveTriggers;
|
||||
_clCallbacks.ds5HapticsPcm = ClDs5HapticsPcm;
|
||||
|
||||
useDualSenseAuthoredPCM = (config.emulatedControllerType == ControllerEmulationPsEnhancedHaptic
|
||||
&& ControllerUtil.hasDualSenseController) || config.hapticEngine == RumbleDevice;
|
||||
if (useDualSenseAuthoredPCM) {
|
||||
_clCallbacks.ds5HapticsPcm = ClDs5HapticsPcm;
|
||||
// _clCallbacks.ds5HapticsIrV2 = ClDs5HapticsIrV2;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleAudioSessionInterruption:)
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
@property BOOL asyncNativeTouchPriority;
|
||||
@property int gyroMode;
|
||||
@property int emulatedControllerType;
|
||||
@property int hapticEngine;
|
||||
@property int audioConfiguration;
|
||||
@property int supportedVideoFormats;
|
||||
@property BOOL multiController;
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
#import "StreamConfiguration.h"
|
||||
|
||||
@implementation StreamConfiguration
|
||||
@synthesize host, httpsPort, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, buttonVisualFeedback, gyroMode, emulatedControllerType, audioConfiguration, supportedVideoFormats, multiController, serverCert, rtspSessionUrl, serverCodecModeSupport, enableYUV444, enablePIP, fullColorRange, enableHdr, sdrPerformanceWorkaround, localVolume;
|
||||
@synthesize host, httpsPort, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, buttonVisualFeedback, gyroMode, emulatedControllerType, hapticEngine, audioConfiguration, supportedVideoFormats, multiController, serverCert, rtspSessionUrl, serverCodecModeSupport, enableYUV444, enablePIP, fullColorRange, enableHdr, sdrPerformanceWorkaround, localVolume;
|
||||
@end
|
||||
|
||||
@@ -117,6 +117,24 @@ import UIKit
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public static func handleControllerEmulationTip(in vc: UIViewController?) {
|
||||
let key = "hasShownControllerEmulationTip2"
|
||||
guard !UserDefaults.standard.bool(forKey: key) else {
|
||||
return
|
||||
}
|
||||
UserDefaults.standard.set(true, forKey: key)
|
||||
|
||||
AlertControllerUtil.showAlert(
|
||||
in: vc,
|
||||
title: LocalizationHelper.localizedString(forKey: "Tips"),
|
||||
message: LocalizationHelper.localizedString(forKey: "emulatedControllerTypeStackTip"),
|
||||
withCancel: false,
|
||||
buttonTitle: LocalizationHelper.localizedString(forKey: "Got it!"),
|
||||
countdown: 7
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@objc public static func needUpdateDefaultSettings() -> Bool {
|
||||
// let key = "needUpdateDefaultSettings20260226-1"
|
||||
|
||||
@@ -882,6 +882,7 @@ static NSMutableSet* hostList;
|
||||
_streamConfig.asyncNativeTouchPriority = streamSettings.asyncNativeTouchPriority; // new streamConfig segment
|
||||
_streamConfig.gyroMode = [streamSettings.gyroMode intValue];
|
||||
_streamConfig.emulatedControllerType = streamSettings.emulatedControllerType.intValue;
|
||||
_streamConfig.hapticEngine = streamSettings.hapticEngine.intValue;
|
||||
//NSLog(@"gyroMode from settings: %ld", _streamConfig.gyroMode);
|
||||
|
||||
// multiController must be set before calling getConnectedGamepadMask
|
||||
|
||||
@@ -268,9 +268,6 @@
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *pressureCurveStack;
|
||||
@property (strong, nonatomic) IBOutlet UISwitch *pressureCurveSwitch;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *frameTimebaseStack;
|
||||
@property (weak, nonatomic) IBOutlet UISwitch *frameTimebaseSwitch;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *asyncFrameDequeueStack;
|
||||
@property (weak, nonatomic) IBOutlet UISwitch *asyncFrameDequeueSwitch;
|
||||
|
||||
@@ -283,6 +280,9 @@
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *globeAsEscapeStack;
|
||||
@property (weak, nonatomic) IBOutlet UISwitch *globeAsEscapeSwitch;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *dualSenseTransientStack;
|
||||
@property (weak, nonatomic) IBOutlet UISlider *dualSenseTransientSlider;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *testStack;
|
||||
|
||||
@property (nonatomic, strong) MicHandler *micHandler;
|
||||
|
||||
@@ -211,15 +211,21 @@ CMVideoDimensions resolutionTable[RESOLUTION_TABLE_SIZE];
|
||||
-(uint16_t)controllerTypeToSegmentIndex:(uint16_t)type{
|
||||
uint16_t index;
|
||||
switch (type) {
|
||||
case LI_CTYPE_XBOX:
|
||||
case ControllerEmulationXbox:
|
||||
index = 0;
|
||||
break;
|
||||
case LI_CTYPE_PS:
|
||||
case ControllerEmulationPs:
|
||||
index = 1;
|
||||
break;
|
||||
default:
|
||||
case ControllerEmulationPsEnhancedHaptic:
|
||||
index = 2;
|
||||
break;
|
||||
case ControllerEmulationXboxAndPs:
|
||||
index = 3;
|
||||
break;
|
||||
default:
|
||||
index = 3;
|
||||
break;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
@@ -228,10 +234,16 @@ CMVideoDimensions resolutionTable[RESOLUTION_TABLE_SIZE];
|
||||
uint16_t type;
|
||||
switch (index) {
|
||||
case 0:
|
||||
type = LI_CTYPE_XBOX;
|
||||
type = ControllerEmulationXbox;
|
||||
break;
|
||||
case 1:
|
||||
type = LI_CTYPE_PS;
|
||||
type = ControllerEmulationPs;
|
||||
break;
|
||||
case 2:
|
||||
type = ControllerEmulationPsEnhancedHaptic;
|
||||
break;
|
||||
case 3:
|
||||
type = ControllerEmulationXboxAndPs;
|
||||
break;
|
||||
default:
|
||||
type = LI_CTYPE_UNKNOWN;
|
||||
@@ -428,6 +440,9 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
|
||||
[self.synthPhysicalInputSwitch setOn:oscProfile.synthesizePhysicalStick];
|
||||
|
||||
[self.dualSenseTransientSlider setValue:oscProfile.dualSenseTransient];
|
||||
[self.dualSenseTransientSlider sendActionsForControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[self.pressureCurveSwitch setOn:oscProfile.pressureCurveEnabled];
|
||||
[self.doubleTapShortcutSwitch setOn:oscProfile.doubleTapShorcutEnabled];
|
||||
[self.squeezeShortcutSwitch setOn:oscProfile.squeezeShorcutEnabled];
|
||||
@@ -461,6 +476,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
&& (int16_t)(oscProfile.gyroSensitivityPitch*100) == (int16_t)pitchSensitivityPercent
|
||||
&& (int16_t)(oscProfile.gyroSensitivityRoll*100) == (int16_t)rollSensitivityPercent
|
||||
&& (int16_t)(oscProfile.gyroToStickMinOffset) == (int16_t)self.gyroToStickMinOffsetSlider.value
|
||||
&& (int16_t)(oscProfile.dualSenseTransient*100) == (int16_t)(self.dualSenseTransientSlider.value*100)
|
||||
&& oscProfile.synthesizePhysicalStick == self.synthPhysicalInputSwitch.isOn
|
||||
&& oscProfile.controllerGyroSwitchMode == self.controllerGyroSwitchButtonSetter.selectedSegmentIndex
|
||||
&& oscProfile.reverseGyroHoldButton == self.reverseHoldButtonSwitch.isOn
|
||||
@@ -488,6 +504,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
oscProfile.gyroSensitivityPitch = pitchSensitivityPercent/100;
|
||||
oscProfile.gyroSensitivityRoll = rollSensitivityPercent/100;
|
||||
oscProfile.gyroToStickMinOffset = (int16_t)self.gyroToStickMinOffsetSlider.value;
|
||||
oscProfile.dualSenseTransient = self.dualSenseTransientSlider.value;
|
||||
oscProfile.synthesizePhysicalStick = self.synthPhysicalInputSwitch.isOn;
|
||||
oscProfile.controllerGyroSwitchMode = (int)self.controllerGyroSwitchButtonSetter.selectedSegmentIndex;
|
||||
oscProfile.reverseGyroHoldButton = self.reverseHoldButtonSwitch.isOn;
|
||||
@@ -996,6 +1013,11 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
self.emulatedControllerTypeStack.hasInfoTag = YES;
|
||||
// self.emulatedControllerTypeStack.isGameProfileSetting = YES;
|
||||
[self addSetting:self.emulatedControllerTypeStack ofId:@"emulatedControllerTypeStack" to:controllerSection];
|
||||
|
||||
self.dualSenseTransientStack.hasInfoTag = YES;
|
||||
self.dualSenseTransientStack.isGameProfileSetting = YES;
|
||||
self.dualSenseTransientStack.hasDynamicLabel = YES;
|
||||
[self addSetting:self.dualSenseTransientStack ofId:@"dualSenseTransientStack" to:controllerSection];
|
||||
|
||||
self.gyroModeStack.hasInfoTag = YES;
|
||||
// self.gyroModeStack.isGameProfileSetting = YES;
|
||||
@@ -2321,6 +2343,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self.hapticEngineSelector setEnabled:!disableControllerRumble forSegmentAtIndex:LeftRightSwapped];
|
||||
[self.hapticEngineSelector setEnabled:!disableControllerRumble forSegmentAtIndex:HapticEngineAuto];
|
||||
[self.hapticEngineSelector setEnabled:[self isIPhone] forSegmentAtIndex:RumbleDevice];
|
||||
|
||||
[self.dualSenseTransientSlider addTarget:self action:@selector(dualSenseTransientSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
self.gyroModeSelector.selectedSegmentIndex = self->tempSettings.gyroMode.intValue;
|
||||
[self.gyroSensitivitySlider setValue: (uint16_t)(self->tempSettings.gyroSensitivity.floatValue * 100) animated:NO]; // Load old setting.
|
||||
@@ -3425,6 +3449,11 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dualSenseTransientSliderMoved:(UISlider* )sender {
|
||||
[self findDynamicLabelFromStack:(UIStackView*)sender.superview].text = [NSString stringWithFormat:@" %.2f ", sender.value]; // Update label display
|
||||
ControllerUtil.dualSenseHapticTransient = sender.value;
|
||||
}
|
||||
|
||||
- (void) gyroSensitivitySliderMoved:(UISlider* )sender {
|
||||
[self findDynamicLabelFromStack:(UIStackView*)sender.superview].text = [NSString stringWithFormat:@" %d%% ", (uint16_t)sender.value]; // Update label display
|
||||
}
|
||||
@@ -3619,6 +3648,12 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
- (void)emulatedControllerTypeChanged:(UISegmentedControl* )sender{
|
||||
[self setHidden:sender.selectedSegmentIndex == 0 forStack:_gyroModeStack];
|
||||
[self setHidden:sender.selectedSegmentIndex == 0 forStack:_gyroSensitivityStack];
|
||||
[self setHidden:sender.selectedSegmentIndex != 2 forStack:_dualSenseTransientStack];
|
||||
|
||||
if(settingsViewJustExpanded) return;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[GenericUtils handleControllerEmulationTipIn:self];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2425,18 +2425,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"cpg-mq-kYj.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Enable Timebase\"; ObjectID = \"cpg-mq-kYj\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Enable Timebase"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CpY-Sb-BDc.title" : {
|
||||
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"CpY-Sb-BDc\";",
|
||||
"extractionState" : "stale",
|
||||
@@ -3999,43 +3987,55 @@
|
||||
}
|
||||
},
|
||||
"JaY-XA-1V6.segmentTitles[1]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; JaY-XA-1V6.segmentTitles[1] = \"DS4\"; ObjectID = \"JaY-XA-1V6\";",
|
||||
"comment" : "Class = \"UISegmentedControl\"; JaY-XA-1V6.segmentTitles[1] = \"PS\"; ObjectID = \"JaY-XA-1V6\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "DS4"
|
||||
"value" : "PS"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"JaY-XA-1V6.segmentTitles[2]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; JaY-XA-1V6.segmentTitles[2] = \"Both\"; ObjectID = \"JaY-XA-1V6\";",
|
||||
"comment" : "Class = \"UISegmentedControl\"; JaY-XA-1V6.segmentTitles[2] = \"PS (Enhanced Haptics)\"; ObjectID = \"JaY-XA-1V6\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Both"
|
||||
"value" : "PS (Enhanced Haptics)"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "同时映射"
|
||||
"value" : "PS (触觉增强)"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "同時映射"
|
||||
"value" : "PS(觸覺增強)"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "同時映射"
|
||||
"value" : "PS(觸覺增強)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"JaY-XA-1V6.segmentTitles[3]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; JaY-XA-1V6.segmentTitles[3] = \"Xbox + PS\"; ObjectID = \"JaY-XA-1V6\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Xbox + PS"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6598,6 +6598,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"UpM-yi-yc9.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"DualSense Haptic Intensity\"; ObjectID = \"UpM-yi-yc9\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "DualSense Haptic Intensity"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "DualSense振动强度"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "DualSense振動強度"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "DualSense振動強度"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"V1y-Wk-uHM.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Height\"; ObjectID = \"V1y-Wk-uHM\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
|
||||
@@ -4789,18 +4789,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"P4N-i6-1zD.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Enable Timebase\"; ObjectID = \"P4N-i6-1zD\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Enable Timebase"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"P9b-Th-Ybc.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \" Batch move \"; ObjectID = \"P9b-Th-Ybc\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -5216,43 +5204,55 @@
|
||||
}
|
||||
},
|
||||
"q6v-Jb-ysy.segmentTitles[1]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; q6v-Jb-ysy.segmentTitles[1] = \"DS4\"; ObjectID = \"q6v-Jb-ysy\";",
|
||||
"comment" : "Class = \"UISegmentedControl\"; q6v-Jb-ysy.segmentTitles[1] = \"PS\"; ObjectID = \"q6v-Jb-ysy\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "DS4"
|
||||
"value" : "PS"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"q6v-Jb-ysy.segmentTitles[2]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; q6v-Jb-ysy.segmentTitles[2] = \"Both\"; ObjectID = \"q6v-Jb-ysy\";",
|
||||
"comment" : "Class = \"UISegmentedControl\"; q6v-Jb-ysy.segmentTitles[2] = \"PS (Enhanced Haptics)\"; ObjectID = \"q6v-Jb-ysy\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Both"
|
||||
"value" : "PS (Enhanced Haptics)"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "同时映射"
|
||||
"value" : "PS (触觉增强)"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "同時映射"
|
||||
"value" : "PS(觸覺增強)"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "同時映射"
|
||||
"value" : "PS(觸覺增強)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"q6v-Jb-ysy.segmentTitles[3]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; q6v-Jb-ysy.segmentTitles[3] = \"Xbox + PS\"; ObjectID = \"q6v-Jb-ysy\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Xbox + PS"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6325,6 +6325,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"vak-fg-iAQ.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"DualSense Haptic Intensity\"; ObjectID = \"vak-fg-iAQ\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "DualSense Haptic Intensity"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "DualSense振动强度"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "DualSense振動強度"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "DualSense振動強度"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"VEc-6n-QcS.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Lift Stream View for Local Keyboard \"; ObjectID = \"VEc-6n-QcS\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
|
||||
Reference in New Issue
Block a user