- motion control demo1
- feat: movable on-screen functional buttons
This commit is contained in:
@@ -15,11 +15,13 @@
|
||||
1829CE3F2E2154DF0074A99A /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1829CE3E2E2154DF0074A99A /* SceneDelegate.m */; };
|
||||
18341A042DEB662C0094E5F2 /* FixedTintImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 18341A032DEB662C0094E5F2 /* FixedTintImageView.m */; };
|
||||
1850C82A2E667E4000845308 /* MicHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1850C8292E667E4000845308 /* MicHandler.swift */; };
|
||||
18729EDE2E8BD68800101194 /* UUIDHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18729EDD2E8BD68800101194 /* UUIDHelper.swift */; };
|
||||
187F4B282E04658A00EECC59 /* WidgetPanelStackView.m in Sources */ = {isa = PBXBuildFile; fileRef = 187F4B272E04658A00EECC59 /* WidgetPanelStackView.m */; };
|
||||
1888C1922E24C33100108730 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1888C1912E24C33100108730 /* AboutViewController.swift */; };
|
||||
1888C1932E24C41000108730 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1888C1902E24C2C800108730 /* AboutView.swift */; };
|
||||
188973D32E386A670025E48C /* widgetTemplatesIPhone.bin in Resources */ = {isa = PBXBuildFile; fileRef = 188973D22E386A670025E48C /* widgetTemplatesIPhone.bin */; };
|
||||
189AD29A2DE7446000E24B88 /* HostCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 189AD2992DE7446000E24B88 /* HostCollectionViewController.m */; };
|
||||
18E4BCEA2E891895000A8657 /* MotionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E4BCE92E891895000A8657 /* MotionHandler.swift */; };
|
||||
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; };
|
||||
841BAC922DE35D6B00DA639E /* ThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 841BAC912DE35D6B00DA639E /* ThemeManager.m */; };
|
||||
84203F232C5F24C00032EEED /* OnScreenWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84203F222C5F24C00032EEED /* OnScreenWidgetView.swift */; };
|
||||
@@ -164,6 +166,7 @@
|
||||
18341A022DEB65AB0094E5F2 /* FixedTintImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FixedTintImageView.h; sourceTree = "<group>"; };
|
||||
18341A032DEB662C0094E5F2 /* FixedTintImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FixedTintImageView.m; sourceTree = "<group>"; };
|
||||
1850C8292E667E4000845308 /* MicHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MicHandler.swift; sourceTree = "<group>"; };
|
||||
18729EDD2E8BD68800101194 /* UUIDHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UUIDHelper.swift; sourceTree = "<group>"; };
|
||||
187F4B232E04633100EECC59 /* WidgetPanelStackView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetPanelStackView.h; sourceTree = "<group>"; };
|
||||
187F4B272E04658A00EECC59 /* WidgetPanelStackView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetPanelStackView.m; sourceTree = "<group>"; };
|
||||
1888C1902E24C2C800108730 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
@@ -172,6 +175,7 @@
|
||||
189AD2962DE743B700E24B88 /* HostCollectionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HostCollectionViewController.h; sourceTree = "<group>"; };
|
||||
189AD2992DE7446000E24B88 /* HostCollectionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HostCollectionViewController.m; sourceTree = "<group>"; };
|
||||
18BF141D2E0D7A00009DEE4F /* VoidLink v1.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "VoidLink v1.0.xcdatamodel"; sourceTree = "<group>"; };
|
||||
18E4BCE92E891895000A8657 /* MotionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionHandler.swift; sourceTree = "<group>"; };
|
||||
693B3A9A218638CD00982F7B /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||
840751B12BF49B5A0096B657 /* CustomTapGestureRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomTapGestureRecognizer.h; sourceTree = "<group>"; };
|
||||
840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomTapGestureRecognizer.m; sourceTree = "<group>"; };
|
||||
@@ -701,6 +705,7 @@
|
||||
988FCD3F293B091B003050E2 /* KeyboardInputField.h */,
|
||||
988FCD40293B091B003050E2 /* KeyboardInputField.m */,
|
||||
1850C8292E667E4000845308 /* MicHandler.swift */,
|
||||
18E4BCE92E891895000A8657 /* MotionHandler.swift */,
|
||||
84E1CDEB2C4F67D300547904 /* CommandManager.swift */,
|
||||
84203F222C5F24C00032EEED /* OnScreenWidgetView.swift */,
|
||||
1817B61D2E6FD04D000D938D /* SafeTimer.swift */,
|
||||
@@ -743,6 +748,7 @@
|
||||
FB89461E19F646E200339C8A /* Utility */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
18729EDD2E8BD68800101194 /* UUIDHelper.swift */,
|
||||
97E20DD42E2164A9000D5DCA /* BandwidthTracker.h */,
|
||||
97E20DD52E2164A9000D5DCA /* BandwidthTracker.m */,
|
||||
97E20DD62E2164A9000D5DCA /* FloatBuffer.h */,
|
||||
@@ -1067,6 +1073,7 @@
|
||||
84351B2C2C29AD6100A77989 /* OSCProfilesTableViewController.m in Sources */,
|
||||
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */,
|
||||
1829CE3F2E2154DF0074A99A /* SceneDelegate.m in Sources */,
|
||||
18729EDE2E8BD68800101194 /* UUIDHelper.swift in Sources */,
|
||||
FB89462819F646E200339C8A /* CryptoManager.m in Sources */,
|
||||
DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */,
|
||||
FB89462E19F646E200339C8A /* PairManager.m in Sources */,
|
||||
@@ -1091,6 +1098,7 @@
|
||||
18341A042DEB662C0094E5F2 /* FixedTintImageView.m in Sources */,
|
||||
FB89463119F646E200339C8A /* StreamManager.m in Sources */,
|
||||
84351B2D2C29AD6100A77989 /* ToolBarContainerView.m in Sources */,
|
||||
18E4BCEA2E891895000A8657 /* MotionHandler.swift in Sources */,
|
||||
988FCD41293B091B003050E2 /* KeyboardInputField.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -746,7 +746,7 @@
|
||||
<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" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="150" minValue="0.0" maxValue="3600" translatesAutoresizingMaskIntoConstraints="NO" id="4gD-tc-95I" userLabel="touchMoveEventIntervalSlider">
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="2667" minValue="0.0" maxValue="20000" translatesAutoresizingMaskIntoConstraints="NO" id="4gD-tc-95I" userLabel="touchMoveEventIntervalSlider">
|
||||
<rect key="frame" x="-2" y="25.5" width="384" height="32"/>
|
||||
<color key="tintColor" systemColor="linkColor"/>
|
||||
</slider>
|
||||
@@ -1426,7 +1426,7 @@
|
||||
<viewControllerLayoutGuide type="bottom" id="eaS-aj-VtA"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Acw-S4-f8a">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1300" height="1194"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="834" height="1210"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fZU-G0-fgM" userLabel="Toolbar Root View" customClass="ToolBarContainerView">
|
||||
@@ -1838,7 +1838,7 @@
|
||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LGW-oJ-5Zf" userLabel="triggerModeStack">
|
||||
<rect key="frame" x="0.0" y="518" width="650" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Trigger mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qNQ-fB-jhY" userLabel="uiLabel">
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Button mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qNQ-fB-jhY" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="130" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="P7b-Jz-LEK"/>
|
||||
@@ -1848,21 +1848,22 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" alpha="0.76999998092651367" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="cKS-fy-Zpg">
|
||||
<rect key="frame" x="130" y="0.0" width="385" height="32"/>
|
||||
<rect key="frame" x="130" y="0.0" width="420" height="32"/>
|
||||
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<segments>
|
||||
<segment title="slide to toggle"/>
|
||||
<segment title="slide & hold"/>
|
||||
<segment title="regular"/>
|
||||
<segment title="tap to toggle"/>
|
||||
<segment title="movable"/>
|
||||
</segments>
|
||||
<color key="tintColor" systemColor="systemGray3Color"/>
|
||||
<color key="selectedSegmentTintColor" red="0.23087820410000001" green="0.4576539993" blue="0.51765710119999997" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
</segmentedControl>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dOF-FW-cBU" userLabel="uiLabel">
|
||||
<rect key="frame" x="515" y="0.0" width="135" height="31"/>
|
||||
<rect key="frame" x="550" y="0.0" width="100" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="135" id="hfV-um-PjK"/>
|
||||
<constraint firstAttribute="width" constant="100" id="hfV-um-PjK"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -1893,6 +1894,8 @@
|
||||
<outlet property="autoTapSlider" destination="ksz-Dq-bUf" id="nbG-ye-X7e"/>
|
||||
<outlet property="autoTapStack" destination="e4b-0v-36Z" id="PZd-KE-lPb"/>
|
||||
<outlet property="borderWidthAlphaStack" destination="9ZS-28-txW" id="qxV-2E-g8m"/>
|
||||
<outlet property="buttonModeSelector" destination="cKS-fy-Zpg" id="uIq-60-t7E"/>
|
||||
<outlet property="buttonModeStack" destination="LGW-oJ-5Zf" id="Jn6-Mu-XYp"/>
|
||||
<outlet property="chevronImageView" destination="iVZ-Fd-dY7" id="403-Nj-wRx"/>
|
||||
<outlet property="chevronView" destination="hKp-ds-86I" id="Og9-aT-zNH"/>
|
||||
<outlet property="currentProfileLabel" destination="iel-xl-xd9" id="PJ0-FX-z6w"/>
|
||||
@@ -1920,8 +1923,6 @@
|
||||
<outlet property="toolbarStackView" destination="YLK-IE-ajS" id="j4o-Kp-7Z8"/>
|
||||
<outlet property="toolbarTopConstraintiPad" destination="Ppb-J1-lXg" id="laN-Zh-lHm"/>
|
||||
<outlet property="trashCanButton" destination="qJs-n8-eV7" id="ubK-qm-Cox"/>
|
||||
<outlet property="triggerModeSelector" destination="cKS-fy-Zpg" id="uIq-60-t7E"/>
|
||||
<outlet property="triggerModeStack" destination="LGW-oJ-5Zf" id="Jn6-Mu-XYp"/>
|
||||
<outlet property="undoButton" destination="F7X-l4-cl3" id="vAI-XQ-hZw"/>
|
||||
<outlet property="widgetAlphaLabel" destination="Pwa-YK-6Q2" id="IPF-Es-yTr"/>
|
||||
<outlet property="widgetAlphaSlider" destination="ii1-Iq-jI4" id="Rev-dn-n3F"/>
|
||||
@@ -2033,7 +2034,7 @@
|
||||
<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="secondarySystemGroupedBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -2042,13 +2043,13 @@
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBrownColor">
|
||||
<color red="0.63529411764705879" green="0.51764705882352946" 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.18823529411764706" green="0.69019607843137254" blue="0.7803921568627451" 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>
|
||||
|
||||
@@ -459,7 +459,7 @@
|
||||
<stackView contentMode="scaleToFill" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="Ejp-Jq-phL" userLabel="stickIndicatorOffsetStack">
|
||||
<rect key="frame" x="0.0" y="415" width="550" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stick Indicator" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcy-zp-LGa" userLabel="stickIndicatorOffsetLabel">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stick indicator" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcy-zp-LGa" userLabel="stickIndicatorOffsetLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="160" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="160" id="2JU-sF-hdJ"/>
|
||||
@@ -548,16 +548,16 @@
|
||||
<rect key="frame" x="0.0" y="523" width="550" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Vibration style" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="u3w-Se-xzH" userLabel="sensitivityFactorLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="160" height="20"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="130" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="160" id="jAW-3B-uPL"/>
|
||||
<constraint firstAttribute="width" constant="130" id="jAW-3B-uPL"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="QRV-je-HEZ">
|
||||
<rect key="frame" x="160" y="0.0" width="390" height="21"/>
|
||||
<rect key="frame" x="130" y="0.0" width="420" height="21"/>
|
||||
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<segments>
|
||||
<segment title="light"/>
|
||||
@@ -579,16 +579,16 @@
|
||||
<rect key="frame" x="0.0" y="550" width="550" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Button down" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ozU-7C-qKE" userLabel="uilabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="160" height="20"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="120" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="160" id="Sii-hk-yFR"/>
|
||||
<constraint firstAttribute="width" constant="120" id="Sii-hk-yFR"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="tE5-Bm-4OB">
|
||||
<rect key="frame" x="160" y="0.0" width="390" height="21"/>
|
||||
<rect key="frame" x="120" y="0.0" width="430" height="21"/>
|
||||
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<segments>
|
||||
<segment title="hovering"/>
|
||||
@@ -608,31 +608,32 @@
|
||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jE7-IB-GTS" userLabel="slidableStack">
|
||||
<rect key="frame" x="0.0" y="577" width="550" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Trigger Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="o9I-Gt-YRd" userLabel="uilabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="160" height="20"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Button mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="o9I-Gt-YRd" userLabel="uilabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="120" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="160" id="W2q-kp-7ce"/>
|
||||
<constraint firstAttribute="width" constant="120" id="W2q-kp-7ce"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="ojp-aS-m8w">
|
||||
<rect key="frame" x="160" y="0.0" width="350" height="21"/>
|
||||
<rect key="frame" x="120" y="0.0" width="420" height="21"/>
|
||||
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<segments>
|
||||
<segment title="slide to toggle"/>
|
||||
<segment title="slide & hold"/>
|
||||
<segment title="regular"/>
|
||||
<segment title="tap to toggle"/>
|
||||
<segment title="movable"/>
|
||||
</segments>
|
||||
<color key="tintColor" systemColor="systemGray2Color"/>
|
||||
<color key="selectedSegmentTintColor" red="0.23136288839999999" green="0.45776400810000001" blue="0.5149240678" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
</segmentedControl>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KZ3-vJ-E8J" userLabel="uilabel">
|
||||
<rect key="frame" x="510" y="0.0" width="40" height="20"/>
|
||||
<rect key="frame" x="540" y="0.0" width="10" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="40" id="BZO-g1-Px9"/>
|
||||
<constraint firstAttribute="width" constant="10" id="BZO-g1-Px9"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -663,6 +664,8 @@
|
||||
<outlet property="autoTapSlider" destination="tsE-QI-xLw" id="WlR-z2-iXM"/>
|
||||
<outlet property="autoTapStack" destination="tDJ-cy-BYU" id="moT-DJ-jwF"/>
|
||||
<outlet property="borderWidthAlphaStack" destination="50p-sC-ObU" id="2Xq-p7-31z"/>
|
||||
<outlet property="buttonModeSelector" destination="ojp-aS-m8w" id="ACc-Sd-Edq"/>
|
||||
<outlet property="buttonModeStack" destination="jE7-IB-GTS" id="hKb-gp-eem"/>
|
||||
<outlet property="chevronImageView" destination="76O-3K-aa8" id="yGv-fM-Ohf"/>
|
||||
<outlet property="chevronView" destination="Zll-IP-PFt" id="MGR-C0-h57"/>
|
||||
<outlet property="currentProfileLabel" destination="f46-Pt-0Pv" id="BjA-jF-t86"/>
|
||||
@@ -686,8 +689,6 @@
|
||||
<outlet property="toolbarStackView" destination="4he-pe-kYB" id="eVm-jM-dcv"/>
|
||||
<outlet property="toolbarTopConstraintiPhone" destination="wNr-7F-S0i" id="iXe-d3-R7i"/>
|
||||
<outlet property="trashCanButton" destination="dhG-Fo-0GK" id="dv6-TN-2Bj"/>
|
||||
<outlet property="triggerModeSelector" destination="ojp-aS-m8w" id="ACc-Sd-Edq"/>
|
||||
<outlet property="triggerModeStack" destination="jE7-IB-GTS" id="hKb-gp-eem"/>
|
||||
<outlet property="undoButton" destination="Pdg-l4-BvF" id="emD-GI-v2v"/>
|
||||
<outlet property="vibrationStyleSelector" destination="QRV-je-HEZ" id="cEF-HG-B4h"/>
|
||||
<outlet property="vibrationStyleStack" destination="gsG-6n-BAb" id="etn-BF-xqj"/>
|
||||
@@ -1067,7 +1068,7 @@
|
||||
<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" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="150" minValue="0.0" maxValue="3600" translatesAutoresizingMaskIntoConstraints="NO" id="Qqh-Oh-dOV" userLabel="touchMoveEventIntervalSlider">
|
||||
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="2667" minValue="0.0" maxValue="20000" translatesAutoresizingMaskIntoConstraints="NO" id="Qqh-Oh-dOV" userLabel="touchMoveEventIntervalSlider">
|
||||
<rect key="frame" x="-2" y="25.5" width="384" height="32"/>
|
||||
<color key="tintColor" systemColor="linkColor"/>
|
||||
</slider>
|
||||
@@ -2060,19 +2061,19 @@
|
||||
<image name="trash.fill" catalog="system" width="117" height="128"/>
|
||||
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
|
||||
<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="secondarySystemGroupedBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBrownColor">
|
||||
<color red="0.63529411764705879" green="0.51764705882352946" 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="systemGray2Color">
|
||||
<color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.68235294120000001" green="0.68235294120000001" blue="0.69803921570000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemTealColor">
|
||||
<color red="0.18823529411764706" green="0.69019607843137254" blue="0.7803921568627451" 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>
|
||||
|
||||
@@ -119,7 +119,7 @@ import UIKit
|
||||
@objc public static let directionPads: [String] = ["DPAD", "WASDPAD", "ARROWPAD"]
|
||||
@objc public static let stickTouchPads: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD"]
|
||||
@objc public static let nonVectorStickPads: [String] = ["LSPAD", "RSPAD"]
|
||||
@objc public static let specialOverlayButtonCmds: [String] = ["SETTINGS","GYRO"]
|
||||
@objc public static let functionalButtonCmds: [String] = ["SETTINGS","GYRO","ACCEL","MOTION"]
|
||||
|
||||
// @objc public static let specialGameWidgets: [String] = ["YSRSV", "YSLT", "YSRT", "YSRB", "YSB", "YSRT2", "YSRB2", "YSB2", "YSEM", "YSML", "YSMR", "YSWASD"]
|
||||
|
||||
@@ -463,7 +463,7 @@ import UIKit
|
||||
let combinedStrings = [CommandManager.keyboardButtonMappings.keys.map { $0 as String },
|
||||
CommandManager.oscButtonMappings.keys.map { $0 as String },
|
||||
CommandManager.mouseButtonMappings.keys.map { $0 as String },
|
||||
CommandManager.specialOverlayButtonCmds.map { $0 as String },
|
||||
CommandManager.functionalButtonCmds.map { $0 as String },
|
||||
CommandManager.touchPadCmds.map { $0 as String }
|
||||
]
|
||||
.lazy
|
||||
|
||||
@@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface OSCProfile : NSObject <NSCoding, NSSecureCoding>
|
||||
|
||||
@property NSString *name;
|
||||
@property NSMutableArray <OnScreenButtonState *> *buttonStates;
|
||||
@property NSMutableArray <NSData *> *buttonStatesEncoded;
|
||||
@property BOOL isSelected;
|
||||
|
||||
- (id) initWithName:(NSString*)name buttonStates:(NSMutableArray*)buttonStates isSelected:(BOOL)isSelected;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
- (id) initWithName:(NSString*)name buttonStates:(NSMutableArray*)buttonStates isSelected:(BOOL)isSelected {
|
||||
if ((self = [self init])) {
|
||||
self.name = name;
|
||||
self.buttonStates = buttonStates;
|
||||
self.buttonStatesEncoded = buttonStates;
|
||||
self.isSelected = isSelected;
|
||||
}
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
- (void) encodeWithCoder:(NSCoder*)encoder {
|
||||
[encoder encodeObject:self.name forKey:@"name"];
|
||||
[encoder encodeObject:self.buttonStates forKey:@"buttonStates"];
|
||||
[encoder encodeObject:self.buttonStatesEncoded forKey:@"buttonStates"];
|
||||
[encoder encodeBool:self.isSelected forKey:@"isSelected"];
|
||||
}
|
||||
|
||||
- (id) initWithCoder:(NSCoder*)decoder {
|
||||
if (self = [super init]) {
|
||||
self.name = [decoder decodeObjectForKey:@"name"];
|
||||
self.buttonStates = [decoder decodeObjectForKey:@"buttonStates"];
|
||||
self.buttonStatesEncoded = [decoder decodeObjectForKey:@"buttonStates"];
|
||||
self.isSelected = [decoder decodeBoolForKey:@"isSelected"];
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* Returns the index of the 'selected' profile within the array it's in
|
||||
*/
|
||||
- (NSInteger) getIndexOfSelectedProfile;
|
||||
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile;
|
||||
|
||||
- (NSMutableArray *) getEncodedProfiles;
|
||||
- (NSData* )getSelectedEncodedProfile;
|
||||
- (void) importEncodedProfiles:(NSMutableArray* )profilesEncoded;
|
||||
- (OnScreenButtonState *)unarchiveButtonStateEncoded:(NSData *)data;
|
||||
- (void) importDefaultTemplates;
|
||||
@@ -74,6 +76,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (BOOL) profileNameAlreadyExist:(NSString*)name;
|
||||
- (OSCProfile *) findProfileByName:(NSString*) name inProfileArray:(NSMutableArray*)profiles;
|
||||
- (CGPoint)normalizeWidgetPosition:(CGPoint)position;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -105,6 +105,21 @@ static CGRect layoutViewBounds;
|
||||
|
||||
}
|
||||
|
||||
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile{
|
||||
NSInteger index = 0;
|
||||
NSMutableArray *profiles = [self getAllProfiles];
|
||||
for (OSCProfile *profile in profiles) {
|
||||
if (profile.isSelected == YES) {
|
||||
index = [profiles indexOfObject:profile];
|
||||
}
|
||||
}
|
||||
if(index>0) profiles[index] = newProfile;
|
||||
|
||||
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles];
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces one 'OSCProfile' object for another in the 'OSCProfile' objects array stored in persistent storage
|
||||
@@ -139,8 +154,11 @@ static CGRect layoutViewBounds;
|
||||
}
|
||||
|
||||
- (NSMutableArray *) getEncodedProfiles {
|
||||
NSMutableArray *profiles = [self getAllProfiles];
|
||||
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles]; // encode each 'profile' object in the array and add them to a new array
|
||||
NSData *profilesArrayEncoded = [[NSUserDefaults standardUserDefaults] objectForKey: @"OSCProfiles"]; // Get the encoded array of encoded OSC profiles from persistent storage
|
||||
NSSet *classes = [NSSet setWithObjects:[NSString class], [NSMutableData class], [NSMutableArray class], [OSCProfile class], [OnScreenButtonState class], nil];
|
||||
|
||||
NSMutableArray *profilesEncoded = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:profilesArrayEncoded error:nil]; // Decode the encoded array itself, NOT the objects contained in the array
|
||||
|
||||
return profilesEncoded;
|
||||
}
|
||||
|
||||
@@ -260,12 +278,7 @@ static CGRect layoutViewBounds;
|
||||
buttonStates:buttonStatesEncoded isSelected:YES]; // create a new 'OSCProfile'. Set the array of encoded button states created above to the 'buttonStates' property of the new profile, along with a 'name'. Set 'isSelected' argument to YES which will set this saved profile as the one that will show up in the game stream view
|
||||
|
||||
|
||||
/* set all saved OSCProfiles 'isSelected' property to NO since the new profile you're adding will be set as the selected profile */
|
||||
NSMutableArray *profiles = [self getAllProfiles];
|
||||
for (OSCProfile *profile in profiles) {
|
||||
profile.isSelected = NO;
|
||||
}
|
||||
[self replaceProfile:[self getSelectedProfile] withProfile:newProfile];
|
||||
[self replaceSelectedProfileWith:newProfile];
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -373,11 +386,8 @@ static CGRect layoutViewBounds;
|
||||
CGPoint normalizedPosition = [self normalizeWidgetPosition:widgetView.center];
|
||||
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:widgetView.cmdString buttonType:CustomOnScreenWidget andPosition:normalizedPosition];
|
||||
buttonState.alias = widgetView.widgetLabel;
|
||||
// buttonState.sizeReference = layoutViewBounds.size.width > layoutViewBounds.size.height ? longSide : shortSide;
|
||||
//widgetView.sizeReference = buttonState.sizeReference; // testttttttttt ???
|
||||
NSLog(@"sizeRef: %d", buttonState.sizeReference);
|
||||
buttonState.identifier = widgetView.identifier;
|
||||
buttonState.widthFactor = [self normalizeSizeWidthFactorWith:widgetView and:buttonState];
|
||||
// NSLog(@"logging widthFactor %f", buttonState.widthFactor);
|
||||
buttonState.heightFactor = [self normalizeSizeHeightFactor:widgetView and:buttonState];
|
||||
buttonState.backgroundAlpha = widgetView.backgroundAlpha;
|
||||
buttonState.borderWidth = widgetView.borderWidth;
|
||||
@@ -390,7 +400,7 @@ static CGRect layoutViewBounds;
|
||||
buttonState.stickIndicatorOffset = widgetView.stickIndicatorOffset;
|
||||
buttonState.widgetShape = widgetView.shape;
|
||||
buttonState.minStickOffset = widgetView.minStickOffset;
|
||||
buttonState.buttonTriggerMode = widgetView.buttonTriggerMode;
|
||||
buttonState.buttonMode = widgetView.buttonMode;
|
||||
|
||||
NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
|
||||
[buttonStatesEncoded addObject: buttonStateEncoded];
|
||||
|
||||
@@ -24,10 +24,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property NSString *name;
|
||||
@property NSString *alias;
|
||||
@property NSString *identifier;
|
||||
@property CGPoint position;
|
||||
@property (nonatomic, assign) BOOL isHidden;
|
||||
@property (nonatomic, assign) uint8_t buttonTriggerMode;
|
||||
@property (nonatomic, assign) uint8_t buttonType;
|
||||
@property (nonatomic, assign) uint8_t buttonMode;
|
||||
@property (nonatomic, assign) uint8_t widgetType;
|
||||
@property (nonatomic, assign) uint8_t sizeReference;
|
||||
@property (nonatomic, assign) CGFloat widthFactor; // for OnScreenWidgetView
|
||||
@property (nonatomic, assign) CGFloat heightFactor; // for OnScreenWidgetView
|
||||
@@ -66,11 +67,12 @@ typedef NS_ENUM(NSInteger, MouseButtonAction) {
|
||||
noClick
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, ButtonTriggerMode) {
|
||||
typedef NS_ENUM(NSInteger, ButtonMode) {
|
||||
slideToToggle,
|
||||
slideAndHold,
|
||||
regular,
|
||||
tapToToggle
|
||||
tapToToggle,
|
||||
movable
|
||||
};
|
||||
|
||||
- (id) initWithButtonName:(NSString*)name buttonType:(uint8_t)buttonType andPosition:(CGPoint)position;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
//
|
||||
|
||||
#import "OnScreenButtonState.h"
|
||||
#import "VoidLink-Swift.h"
|
||||
|
||||
@implementation OnScreenButtonState
|
||||
|
||||
@@ -19,7 +20,7 @@
|
||||
self.name = name;
|
||||
//self.isHidden = isHidden;
|
||||
self.position = position;
|
||||
self.buttonType = buttonType;
|
||||
self.widgetType = buttonType;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -31,12 +32,13 @@
|
||||
|
||||
- (void) encodeWithCoder:(NSCoder*)encoder {
|
||||
[encoder encodeObject:self.name forKey:@"name"];
|
||||
[encoder encodeObject:([self.identifier isEqualToString:@""]||!self.identifier) ? [UUIDHelper newUUID] : self.identifier forKey:@"identifier"];
|
||||
[encoder encodeObject:self.alias forKey:@"alias"];
|
||||
[encoder encodeInt:self.buttonType forKey:@"buttonType"];
|
||||
[encoder encodeInt:self.widgetType forKey:@"buttonType"]; // keep original key
|
||||
[encoder encodeInt:self.sizeReference forKey:@"sizeReference"];
|
||||
[encoder encodeInt:self.vibrationStyle forKey:@"vibrationStyle"];
|
||||
[encoder encodeInt:self.mouseButtonAction forKey:@"mouseButtonAction"];
|
||||
[encoder encodeInt:self.buttonTriggerMode forKey:@"slideMode"]; // triggerMode: previously slideMode
|
||||
[encoder encodeInt:self.buttonMode forKey:@"slideMode"]; // buttonMode: previously slideMode, keep it for consistency
|
||||
[encoder encodeInt:self.autoTapInterval forKey:@"autoTapInterval"];
|
||||
[encoder encodeCGPoint:self.position forKey:@"position"];
|
||||
[encoder encodeBool:self.isHidden forKey:@"isHidden"];
|
||||
@@ -56,12 +58,13 @@
|
||||
- (id) initWithCoder:(NSCoder*)decoder {
|
||||
if (self = [super init]) {
|
||||
self.name = [decoder decodeObjectForKey:@"name"];
|
||||
self.identifier = [decoder containsValueForKey:@"identifier"] ? [decoder decodeObjectForKey:@"identifier"] : [UUIDHelper newUUID];
|
||||
self.alias = [decoder decodeObjectForKey:@"alias"];
|
||||
self.buttonType = [decoder decodeIntForKey:@"buttonType"];
|
||||
self.widgetType = [decoder decodeIntForKey:@"buttonType"];
|
||||
self.sizeReference = [decoder containsValueForKey:@"sizeReference"] ? [decoder decodeIntForKey:@"sizeReference"] : longSide;
|
||||
self.vibrationStyle = [decoder decodeIntForKey:@"vibrationStyle"];
|
||||
self.mouseButtonAction = [decoder decodeIntForKey:@"mouseButtonAction"];
|
||||
self.buttonTriggerMode = [decoder containsValueForKey:@"slideMode"] ? [decoder decodeIntForKey:@"slideMode"] : 0;
|
||||
self.buttonMode = [decoder containsValueForKey:@"slideMode"] ? [decoder decodeIntForKey:@"slideMode"] : 0;
|
||||
self.autoTapInterval = [decoder containsValueForKey:@"autoTapInterval"] ? [decoder decodeIntForKey:@"autoTapInterval"] : 45;
|
||||
self.position = [decoder decodeCGPointForKey:@"position"];
|
||||
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// MicHandler.swift
|
||||
// VoidLink
|
||||
//
|
||||
// Created by True砖家 on 2025/9/28.
|
||||
// Copyright © 2025 True砖家 on Bilibili. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreMotion
|
||||
|
||||
@objc public protocol OnScreenWidgetStickMixedInputDelegate: AnyObject {
|
||||
func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat)
|
||||
func mixLeftStickAndGyroInput(x: CGFloat, y: CGFloat)
|
||||
func gyroMixInputStarted() -> Bool
|
||||
}
|
||||
|
||||
@objc class MotionHandler: NSObject, OscInstanceProviderDelegate, OnScreenWidgetStickMixedInputDelegate{
|
||||
func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
||||
rightStickTouchInputX = x
|
||||
rightStickTouchInputY = y
|
||||
}
|
||||
|
||||
func mixLeftStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
||||
leftStickTouchInputX = x
|
||||
leftStickTouchInputY = y
|
||||
}
|
||||
|
||||
func gyroMixInputStarted() -> Bool {
|
||||
return gyroControlStarted
|
||||
}
|
||||
|
||||
func getOnScreenControlsInstance(_ sender: Any!) {
|
||||
if let controls = sender as? OnScreenControls {
|
||||
self.onScreenControls = controls
|
||||
print("ClassA received OnScreenControls instance: \(controls)")
|
||||
} else {
|
||||
print("ClassA received an unknown sender")
|
||||
}
|
||||
}
|
||||
|
||||
static let shared = MotionHandler()
|
||||
|
||||
@objc class func sharedInstance() -> MotionHandler {
|
||||
return MotionHandler.shared
|
||||
}
|
||||
|
||||
private var gyroControlStarted: Bool = false
|
||||
private var accelControlStarted: Bool = false
|
||||
private let motionManager = CMMotionManager()
|
||||
public var sensitvityYaw:CGFloat = 1.0
|
||||
public var sensitvityPitch:CGFloat = 1.0
|
||||
public var sensitvityRoll:CGFloat = 1.0
|
||||
private var windowScene: Any?
|
||||
|
||||
private var onScreenControls: OnScreenControls = OnScreenControls.init()
|
||||
public let stickMaxOffset: CGFloat = 0x7FFE
|
||||
private var stickInputScale: CGFloat = 35
|
||||
|
||||
var yaw:Double = 0
|
||||
var pitch:Double = 0
|
||||
var roll:Double = 0
|
||||
|
||||
var rightStickTouchInputX:Double = 0
|
||||
var rightStickTouchInputY:Double = 0
|
||||
|
||||
var leftStickTouchInputX:Double = 0
|
||||
var leftStickTouchInputY:Double = 0
|
||||
|
||||
var updateInterval: TimeInterval = 1.0 / 120.0 {
|
||||
didSet {
|
||||
motionManager.accelerometerUpdateInterval = updateInterval
|
||||
motionManager.gyroUpdateInterval = updateInterval
|
||||
}
|
||||
}
|
||||
|
||||
@objc public override init() {
|
||||
super.init()
|
||||
if #available(iOS 13.0, *) {
|
||||
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
|
||||
self.windowScene = scene
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
motionManager.accelerometerUpdateInterval = updateInterval
|
||||
motionManager.gyroUpdateInterval = updateInterval
|
||||
}
|
||||
|
||||
/// 启动传感器数据更新
|
||||
@objc public func startGyroUpdate() {
|
||||
gyroControlStarted = true
|
||||
if motionManager.isGyroAvailable {
|
||||
motionManager.startGyroUpdates(to: .main) { [weak self] gyroData, _ in
|
||||
guard let self = self, let data = gyroData else { return }
|
||||
self.handleGyroData(x: data.rotationRate.x,
|
||||
y: data.rotationRate.y,
|
||||
z: data.rotationRate.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func startAccelUpdate() {
|
||||
gyroControlStarted = true
|
||||
if motionManager.isAccelerometerAvailable {
|
||||
motionManager.startAccelerometerUpdates(to: .main) { [weak self] accelData, _ in
|
||||
guard let self = self, let data = accelData else { return }
|
||||
self.handleAccelerometerData(x: data.acceleration.x,
|
||||
y: data.acceleration.y,
|
||||
z: data.acceleration.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止更新
|
||||
@objc public func stopGyroUpdate() {
|
||||
gyroControlStarted = false
|
||||
if motionManager.isAccelerometerActive {
|
||||
motionManager.stopAccelerometerUpdates()
|
||||
}
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
}
|
||||
|
||||
@objc public func stopAccelUpdate() {
|
||||
accelControlStarted = false
|
||||
if motionManager.isAccelerometerActive {
|
||||
motionManager.stopAccelerometerUpdates()
|
||||
}
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
}
|
||||
|
||||
// MARK: - 私有方法处理数据
|
||||
private func handleAccelerometerData(x: Double, y: Double, z: Double) {
|
||||
// 在这里处理加速度数据,比如计算方向、存储或驱动逻辑
|
||||
//print("加速度计: x=\(x), y=\(y), z=\(z)")
|
||||
}
|
||||
|
||||
private func handleGyroData(x: Double, y: Double, z: Double) {
|
||||
|
||||
var yawSource:Double = 0
|
||||
var pitchSource:Double = 0
|
||||
var rollSource:Double = 0
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
let orientation = (windowScene as! UIWindowScene).interfaceOrientation
|
||||
print("界面方向变化: \(orientation)")
|
||||
switch orientation {
|
||||
case .landscapeLeft:
|
||||
yawSource = x
|
||||
pitchSource = -y
|
||||
rollSource = -z
|
||||
case .landscapeRight:
|
||||
yawSource = -x
|
||||
pitchSource = y
|
||||
rollSource = -z
|
||||
case .portrait:
|
||||
yawSource = -y
|
||||
pitchSource = -x
|
||||
rollSource = -z
|
||||
case .portraitUpsideDown:
|
||||
yawSource = y
|
||||
pitchSource = x
|
||||
rollSource = -z
|
||||
default:
|
||||
yawSource = x
|
||||
pitchSource = -y
|
||||
rollSource = -z
|
||||
}
|
||||
} else {
|
||||
yawSource = x
|
||||
pitchSource = -y
|
||||
rollSource = z
|
||||
}
|
||||
|
||||
if !gyroControlStarted {
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
print("Gyro: stopped")
|
||||
return
|
||||
}
|
||||
|
||||
if true {
|
||||
yaw = rightStickTouchInputX + gyroInputToStickInput(input:yawSource*sensitvityYaw*10)
|
||||
yaw = self.clampStickInput(input: yaw)
|
||||
pitch = rightStickTouchInputY - gyroInputToStickInput(input:pitchSource*sensitvityPitch*10)
|
||||
pitch = self.clampStickInput(input: pitch)
|
||||
self.onScreenControls.sendRightStickTouchPadEvent(yaw, pitch)
|
||||
}
|
||||
|
||||
if true {
|
||||
roll = leftStickTouchInputX + gyroInputToStickInput(input:rollSource*sensitvityRoll*10)
|
||||
roll = self.clampStickInput(input: roll)
|
||||
self.onScreenControls.sendLeftStickTouchPadEvent(roll, leftStickTouchInputY)
|
||||
}
|
||||
|
||||
if false {
|
||||
yaw = yawSource*sensitvityYaw*30
|
||||
pitch = pitchSource*sensitvityPitch*30
|
||||
LiSendMouseMoveEvent(Int16(yaw),Int16(pitch))
|
||||
}
|
||||
|
||||
if false {
|
||||
roll = rollSource*sensitvityPitch*100
|
||||
LiSendMouseMoveEvent(Int16(roll),0)
|
||||
}
|
||||
}
|
||||
|
||||
private func clampStickInput(input: CGFloat) -> CGFloat{
|
||||
return fmax(fmin(input, stickMaxOffset),-stickMaxOffset)
|
||||
}
|
||||
|
||||
private func gyroInputToStickInput(input: CGFloat) -> CGFloat{
|
||||
return self.clampStickInput(input: stickMaxOffset * input / 8)
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,11 @@
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
|
||||
@implementation NativeTouchHandler {
|
||||
StreamView* streamView;
|
||||
TemporarySettings* currentSettings;
|
||||
bool activateCoordSelector;
|
||||
CGFloat pointerVelocityDividerLocationByPoints;
|
||||
uint16_t touchMoveEventIntervalUs;
|
||||
|
||||
bool asyncNativeTouch;
|
||||
unsigned int touchDownQos;
|
||||
@@ -43,6 +41,8 @@
|
||||
CGFloat EDGE_TOLERANCE;
|
||||
|
||||
CGRect streamViewBounds;
|
||||
|
||||
int64_t moveEventIntervalNSec;
|
||||
}
|
||||
|
||||
- (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings{
|
||||
@@ -50,7 +50,7 @@
|
||||
self->streamView = view;
|
||||
self->currentSettings = settings;
|
||||
self->activateCoordSelector = currentSettings.pointerVelocityModeDivider.floatValue != 1.0;
|
||||
self->touchMoveEventIntervalUs = currentSettings.touchMoveEventInterval.intValue;
|
||||
self->moveEventIntervalNSec = (int64_t)(currentSettings.touchMoveEventInterval.intValue * 1000);;
|
||||
self->streamViewBounds = view.bounds;
|
||||
|
||||
self->pointerIdDict = [NSMutableDictionary dictionary];
|
||||
@@ -213,7 +213,6 @@
|
||||
if (asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchDownQos, 0), ^{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
|
||||
[self handleTouchDown:touch]; //generate & populate pointerId
|
||||
if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN];
|
||||
@@ -222,7 +221,6 @@
|
||||
else{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
|
||||
[self handleTouchDown:touch]; //generate & populate pointerId
|
||||
if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN];
|
||||
@@ -231,50 +229,26 @@
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
if (asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchMoveQos, 0), ^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, moveEventIntervalNSec), dispatch_get_main_queue(), ^{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
|
||||
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
|
||||
[[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
|
||||
usleep(self->touchMoveEventIntervalUs);
|
||||
[[self getPointerObjFromDict:touch] doesNeedResetCoords];
|
||||
}
|
||||
});
|
||||
else {
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
|
||||
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
|
||||
[[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
|
||||
// usleep(self->touchMoveEventIntervalUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
if(asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchEndQos, 0), ^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, moveEventIntervalNSec), dispatch_get_main_queue(), ^{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
|
||||
[self removePointerId:touch]; //then remove pointerId
|
||||
if(self->activateCoordSelector) [self removePointerObjFromDict:touch];
|
||||
[self->blacklistedTouches removeObject:@((uintptr_t)touch)];
|
||||
}
|
||||
//if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count])
|
||||
});
|
||||
else{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
|
||||
[self removePointerId:touch]; //then remove pointerId
|
||||
if(self->activateCoordSelector) [self removePointerObjFromDict:touch];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
@class StreamConfiguration;
|
||||
|
||||
// sending self as an instance to OnScreenWidgetView or other related classes
|
||||
@protocol InstanceProviderDelegate <NSObject>
|
||||
@protocol OscInstanceProviderDelegate <NSObject>
|
||||
- (void)getOnScreenControlsInstance:(id)sender; // Renamed delegate method
|
||||
@end
|
||||
|
||||
@@ -54,7 +54,7 @@ typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
|
||||
|
||||
|
||||
|
||||
@property (nonatomic, weak) id<InstanceProviderDelegate> delegate; // Delegate property
|
||||
@property (nonatomic, weak) id<OscInstanceProviderDelegate> instanceReceiverDelegate; // Delegate property
|
||||
- (void)sendInstance;
|
||||
|
||||
// @property (nonatomic, assign) CustomTapGestureRecognizer* mouseRightClickTapRecognizer; // this object will be passed to onscreencontrols class for areVirtualControllerTaps flag setting
|
||||
|
||||
@@ -197,8 +197,8 @@ static float L3_Y;
|
||||
// sending self as an instance to OnScreenWidgetView
|
||||
- (void)sendInstance{
|
||||
NSLog(@"OnScreenControls is sending its instance...");
|
||||
if ([self.delegate respondsToSelector:@selector(getOnScreenControlsInstance:)]) {
|
||||
[self.delegate getOnScreenControlsInstance:self];
|
||||
if ([self.instanceReceiverDelegate respondsToSelector:@selector(getOnScreenControlsInstance:)]) {
|
||||
[self.instanceReceiverDelegate getOnScreenControlsInstance:self];
|
||||
} else {
|
||||
NSLog(@"Delegate not set or does not respond to getOnScreenControlsInstance:");
|
||||
}
|
||||
@@ -373,11 +373,11 @@ static float L3_Y;
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
[_activeCustomOscButtonPositionDict removeAllObjects]; //reset the Dict.
|
||||
// NSLog(@"_activeCustomOscButtonPositionDict update: STARTOVER");
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
[OnScreenControls.layerVibrationStyleDic setObject:@(buttonState.vibrationStyle) forKey:buttonState.name];
|
||||
if(!buttonState.isHidden && [validPositionButtonNames containsObject:buttonState.name] && (buttonState.buttonType == LegacyOnScreenControls || [profilesManager getIndexOfSelectedProfile] == 0 ) ){
|
||||
if(!buttonState.isHidden && [validPositionButtonNames containsObject:buttonState.name] && (buttonState.widgetType == LegacyOnScreenControls || [profilesManager getIndexOfSelectedProfile] == 0 ) ){
|
||||
[_activeCustomOscButtonPositionDict setObject:[NSValue valueWithCGPoint:buttonState.position] forKey:buttonState.name]; // we got a buttonname -> position dict here
|
||||
// NSLog(@"_activeCustomOscButtonPositionDict update, button name:%@, position: %f, %f", buttonState.name, buttonState.position.x, buttonState.position.y);
|
||||
}
|
||||
@@ -730,7 +730,7 @@ static float L3_Y;
|
||||
- (void) setDPadCenter {
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if ([buttonState.name isEqualToString:@"dPad"]) {
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
@@ -746,7 +746,7 @@ static float L3_Y;
|
||||
- (void) setAnalogStickPositions {
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; // returns the currently selected OSCProfile
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
if ([buttonState.name isEqualToString:@"leftStickBackground"]) {
|
||||
@@ -770,7 +770,7 @@ static float L3_Y;
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile];
|
||||
bool defaultProfileSelected = [profilesManager getIndexOfSelectedProfile] == 0;
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
|
||||
OnScreenButtonState *buttonStateDecoded = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
|
||||
@@ -832,7 +832,7 @@ static float L3_Y;
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile];
|
||||
// bool defaultProfileSelected = [profilesManager getIndexOfSelectedProfile] == 0;
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonStateDecoded = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayers) { // iterate through each button layer on screen
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc class OnScreenWidgetView: UIView, InstanceProviderDelegate {
|
||||
@objc class OnScreenWidgetView: UIView, OscInstanceProviderDelegate {
|
||||
// receiving the OnScreenControls instance from delegate
|
||||
@objc func getOnScreenControlsInstance(_ sender: Any) {
|
||||
if let controls = sender as? OnScreenControls {
|
||||
@@ -20,11 +20,31 @@ import UIKit
|
||||
}
|
||||
|
||||
@objc public weak var guidelineDelegate: OnScreenWidgetGuidelineUpdateDelegate?
|
||||
|
||||
@objc protocol OnScreenWidgetGuidelineUpdateDelegate: AnyObject {
|
||||
func updateGuidelinesForOnScreenWidget(_ sender: Any)
|
||||
}
|
||||
|
||||
@objc public weak var mixInputDelegate: OnScreenWidgetStickMixedInputDelegate?
|
||||
@objc protocol OnScreenWidgetStickMixedInputDelegate: AnyObject {
|
||||
func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat)
|
||||
func mixLeftStickAndGyroInput(x: CGFloat, y: CGFloat)
|
||||
func gyroMixInputStarted()->Bool
|
||||
}
|
||||
|
||||
@objc public weak var functionalButtonDelegate: OnScreenFunctionalButtonDelegate?
|
||||
@objc protocol OnScreenFunctionalButtonDelegate: AnyObject {
|
||||
func startGyroUpdate()
|
||||
func startAccelUpdate()
|
||||
func stopGyroUpdate()
|
||||
func stopAccelUpdate()
|
||||
func openWidgetLayoutTool()
|
||||
func switchWidgetProfile()
|
||||
func bringUpSoftKeyboard()
|
||||
func enterPip()
|
||||
func toggleStatsOverlay()
|
||||
func disconnectAndQuitApp()
|
||||
}
|
||||
|
||||
@objc enum WidgetTypeEnum: UInt8 {
|
||||
case uninitialized
|
||||
case button
|
||||
@@ -37,17 +57,19 @@ import UIKit
|
||||
@objc static public var buttonVisualFeedbackEnabled: Bool = true
|
||||
@objc public var widgetLabel: String
|
||||
@objc public var cmdString: String
|
||||
@objc public var identifier: String = ""
|
||||
private var buttonString: String = ""
|
||||
private var functionalButtonString: String = ""
|
||||
private var touchPadString: String = ""
|
||||
// super combo key string set
|
||||
private var comboButtonStrings: [String] = []
|
||||
@objc public var comboButtonStrings: [String] = []
|
||||
private var comboKeyTimeIntervalMs: UInt32 = 0
|
||||
|
||||
@objc public var pressed: Bool
|
||||
@objc public var widthFactor: CGFloat = 1.0
|
||||
@objc public var heightFactor: CGFloat = 1.0
|
||||
|
||||
@objc public var buttonTriggerMode: Int = 0
|
||||
@objc public var buttonMode: Int = 0
|
||||
@objc private var tapToToggleFlag: Bool = true
|
||||
|
||||
@objc public var sizeReference: Int = WidgetSizeReference.longSide.rawValue
|
||||
@@ -74,6 +96,9 @@ import UIKit
|
||||
private var vibrationGenerator = UIImpactFeedbackGenerator(style: .light)
|
||||
private var vibrationOn: Bool = false
|
||||
|
||||
// for movable buttons during streaming
|
||||
@objc public var relocatedDuringStreaming: Bool = false
|
||||
|
||||
// for all touchPad or buttons hybrid with touchPads
|
||||
@objc public var hasStickIndicator: Bool = false
|
||||
@objc public var hasSensitivityTweak: Bool = false
|
||||
@@ -198,7 +223,8 @@ import UIKit
|
||||
self.comboButtonStrings = comboStrings.filter{$0 != touchPadString}
|
||||
self.touchPadString = touchPadString
|
||||
self.buttonString = self.comboButtonStrings.first ?? ""
|
||||
|
||||
self.functionalButtonString = Set(self.comboButtonStrings).intersection(Set(CommandManager.functionalButtonCmds)).first ?? ""
|
||||
|
||||
// let stickAndMouseTouchpads = ["LSPAD", "RSPAD", "LSVPAD", "RSVPAD", "MOUSEPAD"]
|
||||
let nonVectorStickPads = ["LSPAD", "RSPAD"]
|
||||
// if CommandManager.touchPadCmds.contains(self.touchPadString) {self.hasSensitivityTweak = true}
|
||||
@@ -278,20 +304,26 @@ import UIKit
|
||||
if self.widgetType == WidgetTypeEnum.button {
|
||||
if !self.touchPadString.isEmpty {
|
||||
self.mouseButtonAction = MouseButtonAction.noClick
|
||||
self.buttonTriggerMode = ButtonTriggerMode.regular.rawValue
|
||||
self.buttonMode = ButtonMode.regular.rawValue
|
||||
}
|
||||
if !Set(CommandManager.specialOverlayButtonCmds).isDisjoint(with: Set(self.comboButtonStrings)) {
|
||||
self.buttonTriggerMode = ButtonTriggerMode.regular.rawValue
|
||||
if !self.functionalButtonString.isEmpty {
|
||||
self.buttonMode = ButtonMode.movable.rawValue
|
||||
if (self.functionalButtonString == "GYRO" ||
|
||||
self.functionalButtonString == "ACCEL" ||
|
||||
self.functionalButtonString == "MOTION")
|
||||
{self.buttonMode = ButtonMode.tapToToggle.rawValue}
|
||||
}
|
||||
}
|
||||
|
||||
setupView()
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
// ======================================================================================================
|
||||
@objc public func setupAutoTapTimer() {
|
||||
if self.widgetType == WidgetTypeEnum.button {
|
||||
@@ -488,11 +520,15 @@ import UIKit
|
||||
label.centerYAnchor.constraint(equalTo: centerYAnchor),])
|
||||
|
||||
if self.shape != "round"{
|
||||
let shortSideLen = min(self.layer.bounds.size.width, self.layer.bounds.size.height)
|
||||
self.layer.cornerRadius = shortSideLen/2 < 16 ? shortSideLen/3.2 : 16
|
||||
self.setSquareWidgetCornerRadius()
|
||||
}
|
||||
}
|
||||
|
||||
private func setSquareWidgetCornerRadius(){
|
||||
let shortSideLen = min(self.layer.bounds.size.width, self.layer.bounds.size.height)
|
||||
self.layer.cornerRadius = shortSideLen/2 < 16 ? shortSideLen/3.2 : 16
|
||||
}
|
||||
|
||||
private func setupView() {
|
||||
label.text = self.widgetLabel
|
||||
label.font = UIFont.boldSystemFont(ofSize: 19)
|
||||
@@ -508,7 +544,7 @@ import UIKit
|
||||
|
||||
self.translatesAutoresizingMaskIntoConstraints = true // this is mandatory to prevent unexpected key view location change
|
||||
|
||||
self.layer.cornerRadius = 16
|
||||
self.setSquareWidgetCornerRadius()
|
||||
self.layer.borderWidth = self.borderWidth
|
||||
|
||||
self.tweakAlpha()
|
||||
@@ -534,8 +570,8 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
if !Set(CommandManager.specialOverlayButtonCmds).isDisjoint(with: Set(self.comboButtonStrings)){
|
||||
self.layer.borderWidth = 0
|
||||
if !self.functionalButtonString.isEmpty{
|
||||
self.layer.borderWidth = self.borderWidth
|
||||
}
|
||||
|
||||
if self.shape == "round" {
|
||||
@@ -1054,6 +1090,11 @@ import UIKit
|
||||
|
||||
private func handleButtonDown() {
|
||||
if !OnScreenWidgetView.editMode {self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)}
|
||||
|
||||
if !OnScreenWidgetView.editMode, !self.functionalButtonString.isEmpty {self.handleFunctionalButtonDown()}
|
||||
|
||||
// sadflkasdfl;
|
||||
|
||||
if OnScreenWidgetView.buttonVisualFeedbackEnabled {
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
@@ -1072,6 +1113,11 @@ import UIKit
|
||||
|
||||
private func handleButtonUp() {
|
||||
if !OnScreenWidgetView.editMode {self.sendComboButtonsUpEvent(comboStrings: self.comboButtonStrings)}
|
||||
|
||||
if !OnScreenWidgetView.editMode && !self.functionalButtonString.isEmpty{
|
||||
self.handleFunctionalButtonUp()
|
||||
}
|
||||
|
||||
if OnScreenWidgetView.buttonVisualFeedbackEnabled {
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
@@ -1123,10 +1169,8 @@ import UIKit
|
||||
|
||||
//=========================================send on screen controller stick events
|
||||
private func touchInputToStickInput(input: CGFloat) -> CGFloat{
|
||||
var target = stickMaxOffset * input / stickInputScale
|
||||
if target > stickMaxOffset {target = stickMaxOffset}
|
||||
if target < -stickMaxOffset {target = -stickMaxOffset}
|
||||
return target
|
||||
let target = stickMaxOffset * input / stickInputScale
|
||||
return fmax(fmin(target, stickMaxOffset),-stickMaxOffset)
|
||||
}
|
||||
|
||||
private func touchInputToStickBallCoord(input: CGFloat) -> CGFloat {
|
||||
@@ -1145,7 +1189,12 @@ import UIKit
|
||||
// vertical input must be inverted
|
||||
targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
|
||||
targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
|
||||
self.onScreenControls.sendRightStickTouchPadEvent(targetX, targetY)
|
||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||
self.onScreenControls.sendRightStickTouchPadEvent(targetX, targetY)
|
||||
}
|
||||
else{
|
||||
self.mixInputDelegate?.mixRightStickAndGyroInput(x: targetX, y: targetY)
|
||||
}
|
||||
}
|
||||
|
||||
private func sendLeftStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){
|
||||
@@ -1153,7 +1202,12 @@ import UIKit
|
||||
var targetY = -self.touchInputToStickInput(input: inputY)
|
||||
targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
|
||||
targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
|
||||
self.onScreenControls.sendLeftStickTouchPadEvent(targetX, targetY)
|
||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||
self.onScreenControls.sendLeftStickTouchPadEvent(targetX, targetY)
|
||||
}
|
||||
else{
|
||||
self.mixInputDelegate?.mixLeftStickAndGyroInput(x: targetX, y: targetY)
|
||||
}
|
||||
}
|
||||
//==========================================================================================================
|
||||
|
||||
@@ -1228,6 +1282,7 @@ import UIKit
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
|
||||
self.touchBegan = true
|
||||
self.firstTouchMoved = false
|
||||
super.touchesBegan(touches, with: event)
|
||||
@@ -1244,8 +1299,13 @@ import UIKit
|
||||
quickDoubleTapDetected = touchTapTimeInterval < QUICK_TAP_TIME_INTERVAL
|
||||
|
||||
let touch = touches.first
|
||||
if OnScreenWidgetView.editMode || !Set(CommandManager.specialOverlayButtonCmds).isDisjoint(with: Set(self.comboButtonStrings)) {self.touchBeganLocation = touch!.location(in: superview)}
|
||||
else {self.touchBeganLocation = touch!.location(in: self)}
|
||||
// get touchBeganLocation
|
||||
|
||||
if OnScreenWidgetView.editMode {self.touchBeganLocation = touch!.location(in: superview)}
|
||||
else {
|
||||
if self.buttonMode == ButtonMode.movable.rawValue {self.touchBeganLocation = touch!.location(in: superview)}
|
||||
else {self.touchBeganLocation = touch!.location(in: self)}
|
||||
}
|
||||
self.latestTouchLocation = touchBeganLocation
|
||||
}
|
||||
|
||||
@@ -1317,7 +1377,7 @@ import UIKit
|
||||
|
||||
// this will also deal with button events
|
||||
if self.widgetType == WidgetTypeEnum.button && !self.comboButtonStrings.isEmpty {
|
||||
if self.buttonTriggerMode != ButtonTriggerMode.tapToToggle.rawValue {
|
||||
if self.buttonMode != ButtonMode.tapToToggle.rawValue {
|
||||
self.handleTapDownOrSlidein()
|
||||
setLock.lock()
|
||||
self.capturedTouches.union(touches)
|
||||
@@ -1348,8 +1408,11 @@ import UIKit
|
||||
|
||||
private func moveByTouch(touch: UITouch){
|
||||
let currentLocation: CGPoint
|
||||
if OnScreenWidgetView.editMode || !Set(CommandManager.specialOverlayButtonCmds).isDisjoint(with: Set(self.comboButtonStrings)) {currentLocation = touch.location(in: superview)}
|
||||
else {currentLocation = touch.location(in: self)}
|
||||
if OnScreenWidgetView.editMode {currentLocation = touch.location(in: superview)}
|
||||
else {
|
||||
if self.buttonMode == ButtonMode.movable.rawValue {currentLocation = touch.location(in: superview)}
|
||||
else {return}
|
||||
}
|
||||
|
||||
if !firstTouchMoved {
|
||||
// First move event
|
||||
@@ -1366,6 +1429,8 @@ import UIKit
|
||||
center = CGPoint(x: outOfBoundsX ? center.x : center.x+offsetX, y: outOfBoundsY ? center.y : center.y+offsetY)
|
||||
|
||||
latestTouchLocation = currentLocation
|
||||
|
||||
relocatedDuringStreaming = true
|
||||
// center = currentLocation;
|
||||
//NSLog("x coord: %f, y coord: %f", self.frame.origin.x, self.frame.origin.y)
|
||||
if OnScreenWidgetView.editMode {
|
||||
@@ -1415,7 +1480,7 @@ import UIKit
|
||||
setLock.lock()
|
||||
let captured = widget.capturedTouches.contains(touch)
|
||||
setLock.unlock()
|
||||
if !captured || widget.buttonTriggerMode == ButtonTriggerMode.regular.rawValue {continue}
|
||||
if !captured || widget.buttonMode == ButtonMode.regular.rawValue {continue}
|
||||
widget.handleFingerUpOrSlideout()
|
||||
setLock.lock()
|
||||
widget.capturedTouches.remove(touch)
|
||||
@@ -1431,7 +1496,7 @@ import UIKit
|
||||
for subview in self.superview?.subviews ?? [] {
|
||||
if let widget = subview as? OnScreenWidgetView{
|
||||
if widget.widgetType != WidgetTypeEnum.button {continue}
|
||||
let isSlidableButton = widget.buttonTriggerMode == ButtonTriggerMode.slideToToggle.rawValue || widget.buttonTriggerMode == ButtonTriggerMode.slideAndHold.rawValue
|
||||
let isSlidableButton = widget.buttonMode == ButtonMode.slideToToggle.rawValue || widget.buttonMode == ButtonMode.slideAndHold.rawValue
|
||||
let pointInSubview = widget.convert(locationInSuperView, from: self.superview)
|
||||
if widget.bounds.contains(pointInSubview){
|
||||
setLock.lock()
|
||||
@@ -1451,13 +1516,13 @@ import UIKit
|
||||
setLock.unlock()
|
||||
if !captured || !isSlidableButton {continue}
|
||||
// print("UIButton: \(widget.buttonLabel) out test, \(widget.touchPadString), \(CACurrentMediaTime())")
|
||||
if(widget.buttonTriggerMode == ButtonTriggerMode.slideToToggle.rawValue){
|
||||
if(widget.buttonMode == ButtonMode.slideToToggle.rawValue){
|
||||
widget.handleFingerUpOrSlideout()
|
||||
setLock.lock()
|
||||
widget.capturedTouches.remove(touch)
|
||||
setLock.unlock()
|
||||
}
|
||||
if(widget.buttonTriggerMode == ButtonTriggerMode.slideAndHold.rawValue){
|
||||
if(widget.buttonMode == ButtonMode.slideAndHold.rawValue){
|
||||
// do nothing here
|
||||
}
|
||||
}
|
||||
@@ -1468,6 +1533,7 @@ import UIKit
|
||||
|
||||
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
super.touchesMoved(touches, with: event)
|
||||
|
||||
if !OnScreenWidgetView.editMode {
|
||||
|
||||
if !self.touchPadString.isEmpty{
|
||||
@@ -1475,15 +1541,14 @@ import UIKit
|
||||
}
|
||||
|
||||
if self.widgetType == WidgetTypeEnum.button {
|
||||
if self.buttonTriggerMode == ButtonTriggerMode.slideToToggle.rawValue || self.buttonTriggerMode == ButtonTriggerMode.slideAndHold.rawValue {self.handleButtonSliding(touches: touches)}
|
||||
if self.buttonMode == ButtonMode.slideToToggle.rawValue || self.buttonMode == ButtonMode.slideAndHold.rawValue {self.handleButtonSliding(touches: touches)}
|
||||
}
|
||||
|
||||
if !Set(CommandManager.specialOverlayButtonCmds).isDisjoint(with: Set(self.comboButtonStrings)){
|
||||
if self.buttonMode == ButtonMode.movable.rawValue{
|
||||
if let touch = touches.first {
|
||||
NSLog("touchTapTimeStamp %f", self.touchTapTimeStamp)
|
||||
if CACurrentMediaTime() - self.touchTapTimeStamp > 0.3 { // temporarily relocate special buttons
|
||||
self.moveByTouch(touch: touch)
|
||||
self.handleButtonUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1570,6 +1635,56 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
private func handleFunctionalButtonDown(){
|
||||
switch self.functionalButtonString {
|
||||
case "GYRO":
|
||||
self.functionalButtonDelegate?.startGyroUpdate()
|
||||
case "ACCEL":
|
||||
self.functionalButtonDelegate?.startAccelUpdate()
|
||||
case "MOTION":
|
||||
self.functionalButtonDelegate?.startGyroUpdate()
|
||||
self.functionalButtonDelegate?.startAccelUpdate()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func handleFunctionalButtonUp(){
|
||||
let longPressed = CACurrentMediaTime() - self.touchTapTimeStamp > 0.3
|
||||
switch self.functionalButtonString {
|
||||
case "SETTINGS":
|
||||
if longPressed, buttonMode == ButtonMode.movable.rawValue {break}
|
||||
NotificationCenter.default.post(name: Notification.Name("SettingsOverlayButtonPressedNotification"), object:nil) // inform layout tool controller to fetch button size factors. self will be passed as the object of the notification
|
||||
case "GYRO":
|
||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
||||
case "ACCEL":
|
||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
||||
case "MOTION":
|
||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func clearRightStickTouchPadFlag(){
|
||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
}
|
||||
else{
|
||||
self.mixInputDelegate?.mixRightStickAndGyroInput(x: 0, y: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private func clearLeftStickTouchPadFlag(){
|
||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
}
|
||||
else{
|
||||
self.mixInputDelegate?.mixRightStickAndGyroInput(x: 0, y: 0)
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
touchesEnded(touches, with: event)
|
||||
}
|
||||
@@ -1633,15 +1748,15 @@ import UIKit
|
||||
if !OnScreenWidgetView.editMode && !self.touchPadString.isEmpty {
|
||||
switch self.touchPadString{
|
||||
case "LSPAD":
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.clearLeftStickTouchPadFlag()
|
||||
if widgetType == WidgetTypeEnum.touchPad {self.resetStickBallPositionAndHideIndicator()}
|
||||
case "RSPAD":
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
self.clearRightStickTouchPadFlag()
|
||||
if widgetType == WidgetTypeEnum.touchPad {self.resetStickBallPositionAndHideIndicator()}
|
||||
case "LSVPAD":
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.clearLeftStickTouchPadFlag()
|
||||
case "RSVPAD":
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
self.clearRightStickTouchPadFlag()
|
||||
case "WASDPAD":
|
||||
LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["W"]!,Int8(KEY_ACTION_UP), 0)
|
||||
LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["A"]!,Int8(KEY_ACTION_UP), 0)
|
||||
@@ -1677,7 +1792,7 @@ import UIKit
|
||||
}
|
||||
|
||||
if !OnScreenWidgetView.editMode && !self.cmdString.contains("+") && !self.comboButtonStrings.isEmpty { // if the command(keystring contains "+", it's a legacy multi-key command
|
||||
if self.buttonTriggerMode == ButtonTriggerMode.slideToToggle.rawValue || self.buttonTriggerMode == ButtonTriggerMode.slideAndHold.rawValue {
|
||||
if self.buttonMode == ButtonMode.slideToToggle.rawValue || self.buttonMode == ButtonMode.slideAndHold.rawValue {
|
||||
self.handleFingerUpAfterSliding(touches: touches)
|
||||
setLock.lock()
|
||||
self.capturedTouches.minus(touches)
|
||||
@@ -1685,17 +1800,6 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
if !OnScreenWidgetView.editMode && !Set(CommandManager.specialOverlayButtonCmds).isDisjoint(with: Set(self.comboButtonStrings)){
|
||||
if CACurrentMediaTime() - self.touchTapTimeStamp < 0.3 {
|
||||
switch self.cmdString {
|
||||
case "SETTINGS":
|
||||
NotificationCenter.default.post(name: Notification.Name("SettingsOverlayButtonPressedNotification"), object:nil) // inform layout tool controller to fetch button size factors. self will be passed as the object of the notification
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CATransaction.commit()
|
||||
|
||||
if OnScreenWidgetView.editMode {
|
||||
@@ -1734,13 +1838,20 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
if self.buttonTriggerMode != ButtonTriggerMode.tapToToggle.rawValue {self.handleFingerUpOrSlideout()}
|
||||
if self.buttonMode != ButtonMode.tapToToggle.rawValue {self.handleFingerUpOrSlideout()}
|
||||
}
|
||||
|
||||
override func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
if superview == nil {
|
||||
self.handleFingerUpOrSlideout()
|
||||
if self.functionalButtonString == "GYRO" {self.functionalButtonDelegate?.stopGyroUpdate()}
|
||||
if self.functionalButtonString == "ACCEL" {self.functionalButtonDelegate?.stopAccelUpdate()}
|
||||
if self.functionalButtonString == "MOTION" {
|
||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
||||
}
|
||||
buttonDownVisualEffectLayer.borderWidth = 0
|
||||
buttonDownVisualEffectLayer.borderColor = defaultBorderColor
|
||||
crossMarkLayer.removeFromSuperlayer()
|
||||
stickBallLayer.removeFromSuperlayer()
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
TemporarySettings* currentSettings;
|
||||
bool activateCoordSelector;
|
||||
CGFloat pointerVelocityDividerLocationByPoints;
|
||||
uint16_t touchMoveEventIntervalUs;
|
||||
|
||||
bool asyncNativeTouch;
|
||||
unsigned int touchDownQos;
|
||||
@@ -42,6 +41,8 @@
|
||||
CGFloat EDGE_TOLERANCE;
|
||||
|
||||
CGRect streamViewBounds;
|
||||
|
||||
int64_t moveEventIntervalNSec;
|
||||
}
|
||||
|
||||
- (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings{
|
||||
@@ -49,7 +50,7 @@
|
||||
self->streamView = view;
|
||||
self->currentSettings = settings;
|
||||
self->activateCoordSelector = currentSettings.pointerVelocityModeDivider.floatValue != 1.0;
|
||||
self->touchMoveEventIntervalUs = currentSettings.touchMoveEventInterval.intValue;
|
||||
self->moveEventIntervalNSec = (int64_t)(currentSettings.touchMoveEventInterval.intValue * 1000);;
|
||||
self->streamViewBounds = view.bounds;
|
||||
|
||||
self->pointerIdDict = [NSMutableDictionary dictionary];
|
||||
@@ -228,46 +229,24 @@
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
if (asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchMoveQos, 0), ^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, moveEventIntervalNSec), dispatch_get_main_queue(), ^{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
|
||||
[[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
|
||||
usleep(self->touchMoveEventIntervalUs);
|
||||
[[self getPointerObjFromDict:touch] doesNeedResetCoords];
|
||||
}
|
||||
});
|
||||
else {
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
|
||||
[[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
|
||||
// usleep(self->touchMoveEventIntervalUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
if(asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchEndQos, 0), ^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, moveEventIntervalNSec), dispatch_get_main_queue(), ^{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
|
||||
[self removePointerId:touch]; //then remove pointerId
|
||||
if(self->activateCoordSelector) [self removePointerObjFromDict:touch];
|
||||
[self->blacklistedTouches removeObject:@((uintptr_t)touch)];
|
||||
}
|
||||
//if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count])
|
||||
});
|
||||
else{
|
||||
for (UITouch* touch in touches){
|
||||
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
|
||||
[self removePointerId:touch]; //then remove pointerId
|
||||
if(self->activateCoordSelector) [self removePointerObjFromDict:touch];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
@property (assign, nonatomic) CGFloat streamAspectRatio;
|
||||
@property (assign, nonatomic) CGRect originalFrame;
|
||||
@property (assign, nonatomic) bool widgetToolOpened;
|
||||
|
||||
@property (strong, nonatomic) OnScreenControls* onScreenControls;
|
||||
|
||||
- (void) setupStreamView:(ControllerSupport*)controllerSupport
|
||||
interactionDelegate:(id<UserInteractionDelegate>)interactionDelegate
|
||||
@@ -53,6 +53,7 @@
|
||||
andConfig:(StreamConfiguration*)streamConfig;
|
||||
- (void) clearOnScreenWidgets;
|
||||
- (void) reloadOnScreenWidgetViews;
|
||||
- (void) saveRelocatedWidgetViews;
|
||||
|
||||
- (CGSize) getVideoAreaSize;
|
||||
- (CGPoint) adjustCoordinatesForVideoArea:(CGPoint)point;
|
||||
|
||||
+75
-28
@@ -35,16 +35,14 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
*/
|
||||
@implementation StreamView {
|
||||
UIView* streamFrameTopLayerView;
|
||||
UIViewController* streamFrameVC;
|
||||
|
||||
TemporarySettings* settings;
|
||||
|
||||
OnScreenControls* onScreenControls;
|
||||
|
||||
|
||||
KeyboardInputField* keyInputField;
|
||||
BOOL isInputingText;
|
||||
NSMutableSet* keysDown;
|
||||
float streamAspectRatio;
|
||||
|
||||
|
||||
|
||||
// iOS 13.4 mouse support
|
||||
NSInteger lastMouseButtonMask;
|
||||
@@ -78,6 +76,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
UIKeyModifierFlags comboKeyModifierFlags;
|
||||
|
||||
WidgetSizeTransition _widgetSizeTransition;
|
||||
OSCProfilesManager* profilesManager;
|
||||
}
|
||||
|
||||
- (void) setupStreamView:(ControllerSupport*)controllerSupport
|
||||
@@ -156,7 +155,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
|
||||
// we'll render on-screen controls on the toplayer too:
|
||||
onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig]; // don't delete, this is mandatory
|
||||
_onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig]; // don't delete, this is mandatory
|
||||
/*
|
||||
// here we pass the tap recognizer to the onscreencontrols obj
|
||||
if (settings.touchMode.intValue == RelativeTouch){
|
||||
@@ -167,7 +166,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
OnScreenControlsLevel level = (OnScreenControlsLevel)[settings.onscreenControls integerValue];
|
||||
if (settings.touchMode.intValue != RelativeTouch && settings.touchMode.intValue != NativeTouch ) {
|
||||
Log(LOG_I, @"On-screen controls disabled in non-relative touch mode");
|
||||
[onScreenControls setLevel:OnScreenControlsLevelOff];
|
||||
[_onScreenControls setLevel:OnScreenControlsLevelOff];
|
||||
|
||||
//pass touchesCaptureByOnScreenButtons Set to the native touchhandler, this NSSet is init witihin onscreencontrols class, don't do it again in native touch handler class
|
||||
[OnScreenControls.touchAddrsCapturedByOnScreenControls removeAllObjects]; // reset the attribute to nil
|
||||
@@ -182,11 +181,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
//else if (level == OnScreenControlsLevelAuto) {
|
||||
else if (false) { // level auto cancelled in settings
|
||||
[controllerSupport initAutoOnScreenControlMode:onScreenControls];
|
||||
[controllerSupport initAutoOnScreenControlMode:_onScreenControls];
|
||||
}
|
||||
else {
|
||||
Log(LOG_I, @"Setting manual on-screen controls level: %d", (int)level);
|
||||
[onScreenControls setLevel:level];
|
||||
[_onScreenControls setLevel:level];
|
||||
}
|
||||
// It would be nice to just use GCMouse on iOS 14+ and the older API on iOS 13
|
||||
// but unfortunately that isn't possible today. GCMouse doesn't recognize many
|
||||
@@ -263,6 +262,17 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
NSLog(@"keyboard will show %f", CACurrentMediaTime());
|
||||
}
|
||||
|
||||
- (UIViewController *)parentViewController {
|
||||
UIResponder *responder = self;
|
||||
while (responder) {
|
||||
if ([responder isKindOfClass:[UIViewController class]]) {
|
||||
return (UIViewController *)responder;
|
||||
}
|
||||
responder = responder.nextResponder;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// this method also deals with recovering streamview when local keyboard is turned off
|
||||
- (void)keyboardWillHide{
|
||||
// NSLog(@"keyboard will hide markmark %f", CACurrentMediaTime());
|
||||
@@ -401,7 +411,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
- (void) showOnScreenControls {
|
||||
#if !TARGET_OS_TV
|
||||
if(!_widgetToolOpened) [onScreenControls show];
|
||||
if(!_widgetToolOpened) [_onScreenControls show];
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -411,7 +421,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
- (void) disableOnScreenControls {
|
||||
#if !TARGET_OS_TV
|
||||
[onScreenControls setLevel:OnScreenControlsLevelOff];
|
||||
[_onScreenControls setLevel:OnScreenControlsLevelOff];
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -438,7 +448,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
andConfig:(StreamConfiguration*)streamConfig {
|
||||
|
||||
// we'll render on-screen controllers on the toplayer too.
|
||||
onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig];
|
||||
_onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig];
|
||||
/*
|
||||
// pass mouseRightClickTapRecognizer to onScreenControls obj here:
|
||||
if([self isOscEnabled]){
|
||||
@@ -446,7 +456,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
RelativeTouchHandler* relativeTouchHandler = (RelativeTouchHandler *)touchHandler;
|
||||
onScreenControls.mouseRightClickTapRecognizer = relativeTouchHandler.mouseRightClickTapRecognizer;
|
||||
} */
|
||||
if([self isOscEnabled]) [onScreenControls setLevel:(OnScreenControlsLevel)settings.onscreenControls.intValue];
|
||||
if([self isOscEnabled]) [_onScreenControls setLevel:(OnScreenControlsLevel)settings.onscreenControls.intValue];
|
||||
}
|
||||
|
||||
|
||||
@@ -477,6 +487,35 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
else return longSide;
|
||||
}
|
||||
|
||||
- (void)saveRelocatedWidgetViews{
|
||||
NSMutableDictionary* relocatedWidgetDict = [NSMutableDictionary dictionary];
|
||||
|
||||
for(UIView* subview in self->streamFrameTopLayerView.subviews){
|
||||
if([subview isKindOfClass:[OnScreenWidgetView class]]){
|
||||
OnScreenWidgetView* widget = (OnScreenWidgetView* )subview;
|
||||
if(widget.relocatedDuringStreaming) [relocatedWidgetDict setObject:widget forKey:widget.identifier];
|
||||
}
|
||||
}
|
||||
|
||||
if(relocatedWidgetDict.allKeys.count == 0) return;
|
||||
|
||||
profilesManager = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||
OSCProfile *newProfile = [profilesManager getSelectedProfile];
|
||||
|
||||
for (NSInteger i = 0; i < newProfile.buttonStatesEncoded.count; i++) {
|
||||
NSData* buttonStateEncoded = newProfile.buttonStatesEncoded[i];
|
||||
OnScreenButtonState *newButtonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if([relocatedWidgetDict.allKeys containsObject:newButtonState.identifier]){
|
||||
OnScreenWidgetView* widget = [relocatedWidgetDict objectForKey:newButtonState.identifier];
|
||||
newButtonState.position = [profilesManager normalizeWidgetPosition:widget.center];
|
||||
NSData *newButtonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:newButtonState requiringSecureCoding:YES error:nil];
|
||||
newProfile.buttonStatesEncoded[i] = newButtonStateEncoded;
|
||||
}
|
||||
}
|
||||
|
||||
[profilesManager replaceSelectedProfileWith:newProfile];
|
||||
}
|
||||
|
||||
- (void) reloadOnScreenWidgetViews{
|
||||
|
||||
// NSLog(@"reload on screen keyboard buttons here");
|
||||
@@ -484,25 +523,33 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
// remove all keyboard widget views first
|
||||
[self clearOnScreenWidgets];
|
||||
|
||||
// get streamFrameVC
|
||||
if(!streamFrameVC) streamFrameVC = [self parentViewController];
|
||||
|
||||
// bool customOscEnabled = [self isOscEnabled] && settings.onscreenControls.intValue == OnScreenControlsLevelCustom;
|
||||
|
||||
if(![self isOnScreenWidgetEnabled]) return;
|
||||
|
||||
OnScreenWidgetView.buttonVisualFeedbackEnabled = settings.buttonVisualFeedback;
|
||||
|
||||
OSCProfilesManager* profilesManager = [OSCProfilesManager sharedManager: self.bounds];
|
||||
if(!profilesManager) profilesManager = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
|
||||
if(!OnScreenWidgetView.editMode){ // in edit mode, keyboard widget view will be updated within layoutool view controller.
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if(buttonState.buttonType == CustomOnScreenWidget){
|
||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape]; //reconstruct widgetView
|
||||
//--------------------------------------------------
|
||||
onScreenControls.delegate = widgetView; // connecting onScreenControls to OnScreenWidgetView, sending the active instance for touchPad stick control
|
||||
[onScreenControls sendInstance];
|
||||
_onScreenControls.instanceReceiverDelegate = widgetView; // connecting onScreenControls to OnScreenWidgetView, sending the active instance for touchPad stick control
|
||||
[_onScreenControls sendInstance];
|
||||
//--------------------------------------------------
|
||||
|
||||
widgetView.functionalButtonDelegate = (id<OnScreenFunctionalButtonDelegate>)streamFrameVC;
|
||||
widgetView.mixInputDelegate = (id<OnScreenWidgetStickMixedInputDelegate>) [MotionHandler sharedInstance];
|
||||
|
||||
widgetView.identifier = buttonState.identifier;
|
||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
widgetView.widthFactor = buttonState.widthFactor;
|
||||
widgetView.heightFactor = buttonState.heightFactor;
|
||||
@@ -515,7 +562,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
widgetView.trackballDecelerationRate = buttonState.decelerationRate;
|
||||
widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset;
|
||||
widgetView.minStickOffset = buttonState.minStickOffset;
|
||||
widgetView.buttonTriggerMode = buttonState.buttonTriggerMode;
|
||||
widgetView.buttonMode = buttonState.buttonMode;
|
||||
// Add the widgetView to the view controller's view
|
||||
[self->streamFrameTopLayerView addSubview:widgetView]; // add keyboard button to the stream frame view. must add it to the target view before setting location.
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
@@ -533,11 +580,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
|
||||
- (OnScreenControlsLevel) getCurrentOscState {
|
||||
if (onScreenControls == nil) {
|
||||
if (_onScreenControls == nil) {
|
||||
return OnScreenControlsLevelOff;
|
||||
}
|
||||
else {
|
||||
return [onScreenControls getLevel];
|
||||
return [_onScreenControls getLevel];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,10 +761,10 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
[self startInteractionTimer];
|
||||
|
||||
if(settings.touchMode.intValue == NativeTouch || settings.touchMode.intValue == RelativeTouch){
|
||||
[self->onScreenControls handleTouchDownEvent:touches];
|
||||
[self->_onScreenControls handleTouchDownEvent:touches];
|
||||
[self->touchHandler touchesBegan:touches withEvent:event];
|
||||
}
|
||||
else if(![onScreenControls handleTouchDownEvent:touches]) [touchHandler touchesBegan:touches withEvent:event];
|
||||
else if(![_onScreenControls handleTouchDownEvent:touches]) [touchHandler touchesBegan:touches withEvent:event];
|
||||
}
|
||||
|
||||
- (UIBarButtonItem *)createButtonWithImageNamed:(NSString *)imageName backgroundColor:(UIColor *)backgroundColor target:(id)target action:(SEL)action keyCode:(NSInteger)keyCode isToggleable:(BOOL)isToggleable {
|
||||
@@ -880,9 +927,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
if(self->settings.touchMode.intValue == NativeTouch || self->settings.touchMode.intValue == RelativeTouch){
|
||||
[self->touchHandler touchesMoved:touches withEvent:event];
|
||||
[self->onScreenControls handleTouchMovedEvent:touches];
|
||||
[self->_onScreenControls handleTouchMovedEvent:touches];
|
||||
}
|
||||
else if(![self->onScreenControls handleTouchMovedEvent:touches]) [self->touchHandler touchesMoved:touches withEvent:event];
|
||||
else if(![self->_onScreenControls handleTouchMovedEvent:touches]) [self->touchHandler touchesMoved:touches withEvent:event];
|
||||
}
|
||||
|
||||
- (void) handleKeyCombos:(UIPress*) press{
|
||||
@@ -976,9 +1023,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
if(settings.touchMode.intValue == NativeTouch || settings.touchMode.intValue == RelativeTouch){
|
||||
[self->touchHandler touchesEnded:touches withEvent:event]; // when touches ended, must call the native touchhandler before onScreenControls, since the NSSet of touches captured by on screen button shall be updated later
|
||||
[self->onScreenControls handleTouchUpEvent:touches];
|
||||
[self->_onScreenControls handleTouchUpEvent:touches];
|
||||
}
|
||||
else if(![onScreenControls handleTouchUpEvent:touches]) [touchHandler touchesEnded:touches withEvent:event];
|
||||
else if(![_onScreenControls handleTouchUpEvent:touches]) [touchHandler touchesEnded:touches withEvent:event];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// UUIDHelper.swift
|
||||
// VoidLink
|
||||
//
|
||||
// Created by True砖家 on 2025/9/30.
|
||||
// Copyright © 2025 True砖家@Bilibili. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc public class UUIDHelper: NSObject {
|
||||
|
||||
@objc public static func newUUID() -> String {
|
||||
return UUID().uuidString
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -94,8 +94,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *mouseDownButtonStack;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *mouseButtonDownSelector;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *triggerModeStack;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *triggerModeSelector;
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *buttonModeStack;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *buttonModeSelector;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *autoTapStack;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *autoTapLabel;
|
||||
|
||||
+25
-14
@@ -122,11 +122,13 @@
|
||||
|
||||
// _activeCustomOscButtonPositionDict will be updated every time when the osc profile is reloaded
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
// OnScreenButtonState* buttonState = [NSKeyedUnarchiver unarchivedObjectOfClass:[OnScreenButtonState class] fromData:buttonStateEncoded error:nil];
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if(buttonState.buttonType == CustomOnScreenWidget){
|
||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape]; //reconstruct widgetView
|
||||
widgetView.identifier = buttonState.identifier;
|
||||
NSLog(@"cmd: %@, id: %@", buttonState.name, widgetView.identifier);
|
||||
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
|
||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
widgetView.widthFactor = buttonState.widthFactor;
|
||||
@@ -140,7 +142,7 @@
|
||||
widgetView.trackballDecelerationRate = buttonState.decelerationRate;
|
||||
widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset;
|
||||
widgetView.minStickOffset = buttonState.minStickOffset;
|
||||
widgetView.buttonTriggerMode = buttonState.buttonTriggerMode;
|
||||
widgetView.buttonMode = buttonState.buttonMode;
|
||||
// Add the widgetView to the view controller's view
|
||||
[self.view insertSubview:widgetView belowSubview:self.widgetPanelStack];
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
@@ -609,8 +611,8 @@
|
||||
bool noValidMouseButtonString = ![CommandManager.mouseButtonMappings.allKeys containsObject:cmdString];
|
||||
bool noValidTouchPadString = ![CommandManager.touchPadCmds containsObject:cmdString];
|
||||
bool noValidOscButtonString = ![CommandManager.oscButtonMappings.allKeys containsObject:cmdString];
|
||||
bool noValidSpecialButtonString = ![CommandManager.specialOverlayButtonCmds containsObject:cmdString];
|
||||
bool paramInvalid = noValidKeyboardString && noValidMouseButtonString && noValidTouchPadString && noValidOscButtonString && noValidSpecialButtonString && noValidSuperComboButtonString;
|
||||
bool noValidFunctionalButtonString = ![CommandManager.functionalButtonCmds containsObject:cmdString];
|
||||
bool paramInvalid = noValidKeyboardString && noValidMouseButtonString && noValidTouchPadString && noValidOscButtonString && noValidFunctionalButtonString && noValidSuperComboButtonString;
|
||||
|
||||
if([buttonLabel isEqualToString:@""]) widgetInitParams[@"buttonLabel"] = [[cmdString lowercaseString] capitalizedString];
|
||||
|
||||
@@ -634,6 +636,7 @@
|
||||
- (void) updateWidget:(OnScreenWidgetView* )widget byParams:(NSMutableDictionary* )widgetInitParams createNew:(bool)createNew{
|
||||
if(![self isWidgetParamsValid:widgetInitParams]) return;
|
||||
OnScreenWidgetView* newWidget = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"]]; //reconstruct widgetView
|
||||
newWidget.identifier = createNew ? [UUIDHelper newUUID] : widget.identifier;
|
||||
newWidget.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
|
||||
newWidget.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
newWidget.widthFactor = widget.widthFactor;
|
||||
@@ -647,7 +650,7 @@
|
||||
newWidget.minStickOffset = [widgetInitParams[@"minStickOffsetString"] floatValue];
|
||||
[newWidget setVibrationWithStyle:widget.vibrationStyle];
|
||||
newWidget.mouseButtonAction = widget.mouseButtonAction;
|
||||
newWidget.buttonTriggerMode = widget.buttonTriggerMode;
|
||||
newWidget.buttonMode = widget.buttonMode;
|
||||
[self.view insertSubview:newWidget belowSubview:self.widgetPanelStack];
|
||||
|
||||
if(createNew) [newWidget setLocationWithPosition:CGPointMake(90, 130)];
|
||||
@@ -669,6 +672,7 @@
|
||||
if(![self isWidgetParamsValid:widgetInitParams]) return;
|
||||
//saving & present the keyboard button:
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"]];
|
||||
widgetView.identifier = [UUIDHelper newUUID];
|
||||
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
|
||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
widgetView.minStickOffset = [widgetInitParams[@"minStickOffsetString"] floatValue];
|
||||
@@ -768,7 +772,7 @@
|
||||
|
||||
// hide irrelevant stacks
|
||||
self.autoTapStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton;
|
||||
self.triggerModeStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton;
|
||||
self.buttonModeStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton;
|
||||
|
||||
bool showSensitivityFactorStack = selectedWidgetView.hasSensitivityTweak;
|
||||
bool showStickIndicatorOffsetStack = selectedWidgetView.hasStickIndicator;
|
||||
@@ -790,8 +794,6 @@
|
||||
self.mouseDownButtonStack.hidden = !([selectedWidgetView.cmdString containsString:@"MOUSEPAD"] && selectedWidgetView.widgetType == WidgetTypeEnumTouchPad);
|
||||
self.decelerationRateStack.hidden = !([selectedWidgetView.cmdString containsString:@"TRACKBALL"] && selectedWidgetView.widgetType == WidgetTypeEnumTouchPad);
|
||||
|
||||
[self autoFitStack:self.widgetPanelStack];
|
||||
|
||||
// setup values
|
||||
|
||||
|
||||
@@ -840,8 +842,17 @@
|
||||
[self decelerationRateSliderMoved:self.decelerationRateSlider];
|
||||
|
||||
self.mouseButtonDownSelector.selectedSegmentIndex = selectedWidgetView.mouseButtonAction;
|
||||
[self.triggerModeSelector setSelectedSegmentIndex:selectedWidgetView.buttonTriggerMode];
|
||||
|
||||
NSSet *specialCmds = [NSSet setWithArray:CommandManager.functionalButtonCmds];
|
||||
NSSet *comboButtonStrings = [NSSet setWithArray:widgetView.comboButtonStrings];
|
||||
bool isFuncationalButton = [specialCmds intersectsSet:comboButtonStrings];
|
||||
[self.buttonModeSelector setEnabled:!isFuncationalButton forSegmentAtIndex:0];
|
||||
[self.buttonModeSelector setEnabled:!isFuncationalButton forSegmentAtIndex:1];
|
||||
if(isFuncationalButton) [self.autoTapStack setHidden:YES];
|
||||
[self.buttonModeSelector setSelectedSegmentIndex:selectedWidgetView.buttonMode];
|
||||
|
||||
[self autoFitStack:self.widgetPanelStack];
|
||||
|
||||
if([self isIPhone]){
|
||||
self.vibrationStyleStack.hidden =
|
||||
[widgetView.cmdString containsString:@"MOUSEPAD"] ||
|
||||
@@ -959,7 +970,7 @@
|
||||
|
||||
- (void)slideModeChanged:(UISegmentedControl* )sender{
|
||||
if(self->selectedWidgetView != nil && self->widgetViewSelected){
|
||||
selectedWidgetView.buttonTriggerMode = _triggerModeSelector.selectedSegmentIndex;
|
||||
selectedWidgetView.buttonMode = _buttonModeSelector.selectedSegmentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1139,9 +1150,9 @@
|
||||
[self.mouseButtonDownSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal];
|
||||
self.mouseDownButtonStack.hidden = YES;
|
||||
|
||||
[self.triggerModeSelector addTarget:self action:@selector(slideModeChanged:) forControlEvents:(UIControlEventValueChanged)];
|
||||
[self.triggerModeSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal];
|
||||
self.triggerModeStack.hidden = YES;
|
||||
[self.buttonModeSelector addTarget:self action:@selector(slideModeChanged:) forControlEvents:(UIControlEventValueChanged)];
|
||||
[self.buttonModeSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal];
|
||||
self.buttonModeStack.hidden = YES;
|
||||
|
||||
|
||||
if([self isIPhone]){
|
||||
|
||||
@@ -381,6 +381,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(![self manuallyChangedFPS]) [self framerateChanged];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated{
|
||||
@@ -1603,7 +1605,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self.resolutionSelector addTarget:self action:@selector(newResolutionChosen) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[self.framerateSelector setSelectedSegmentIndex:framerate];
|
||||
[self.framerateSelector addTarget:self action:@selector(updateBitrate) forControlEvents:UIControlEventValueChanged];
|
||||
[self.framerateSelector addTarget:self action:@selector(framerateChanged) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[self.bitrateSlider setMinimumValue:0];
|
||||
[self.bitrateSlider setMaximumValue:(sizeof(bitrateTable) / sizeof(*bitrateTable)) - 1];
|
||||
[self.bitrateSlider setValue:[self getSliderValueForBitrate:_bitrate] animated:YES];
|
||||
@@ -2126,6 +2129,23 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self handleOswGestureChange];
|
||||
}
|
||||
|
||||
- (BOOL)manuallyChangedFPS {
|
||||
NSString *key = @"manuallyChangedFPS";
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
return [defaults objectForKey:key] ? [defaults boolForKey:key] : NO;
|
||||
}
|
||||
|
||||
- (void)framerateChanged{
|
||||
NSString *key = @"manuallyChangedFPS";
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
NSInteger fps = [self getChosenFrameRate];
|
||||
[self.touchMoveEventIntervalSlider setValue: fps>60 ? 1/((CGFloat)fps)*0.5*1000000-1500 : 3530];
|
||||
[self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
|
||||
[self updateBitrate];
|
||||
}
|
||||
|
||||
- (void) updateBitrate {
|
||||
NSInteger fps = [self getChosenFrameRate];
|
||||
NSInteger width = [self getChosenStreamWidth];
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
@interface StreamFrameViewController : GCEventViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, AVPictureInPictureControllerDelegate>
|
||||
#else
|
||||
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, ToolboxSpecialEntryDelegate, AVPictureInPictureControllerDelegate>
|
||||
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, ToolboxSpecialEntryDelegate, AVPictureInPictureControllerDelegate, OnScreenFunctionalButtonDelegate>
|
||||
|
||||
#endif
|
||||
@property (nonatomic) StreamManager* streamMan;
|
||||
@@ -40,8 +40,8 @@
|
||||
@property (nonatomic, strong) MetalViewController *metalViewController;
|
||||
@property (nonatomic, strong) ImGuiRenderer *imguiView;
|
||||
|
||||
-(void)updatePreferredDisplayMode:(BOOL)streamActive;
|
||||
-(void)reConfigStreamViewRealtime:(BOOL)reloadSettings;
|
||||
|
||||
- (void)updatePreferredDisplayMode:(BOOL)streamActive;
|
||||
- (void)setUserInteractionEnabledForStreamView:(bool)enabled;
|
||||
- (bool)shallDisableGyroHotSwitch;
|
||||
|
||||
|
||||
@@ -45,8 +45,6 @@
|
||||
- (id)initWithRefreshRate:(float)arg1 videoDynamicRange:(int)arg2;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation StreamFrameViewController {
|
||||
ControllerSupport *_controllerSupport;
|
||||
StreamManager *_streamMan;
|
||||
@@ -91,6 +89,7 @@
|
||||
LayoutOnScreenControlsViewController *_layoutOnScreenControlsVC;
|
||||
ToolboxViewController* toolBoxViewController;
|
||||
MicHandler* micHandler;
|
||||
MotionHandler *_motionHandler;
|
||||
|
||||
#else
|
||||
UITapGestureRecognizer *_menuTapGestureRecognizer;
|
||||
@@ -332,7 +331,6 @@
|
||||
// Insert at index 0 to ensure it doesn't cover OSC controls (CALayers)
|
||||
if([_streamView.superview isKindOfClass:[UIScrollView class]]){
|
||||
[_streamView removeFromSuperview];
|
||||
NSLog(@"removeFromSuperview %f", CACurrentMediaTime());
|
||||
}
|
||||
|
||||
[self.view insertSubview:_streamView atIndex:0];
|
||||
@@ -435,6 +433,10 @@
|
||||
[self startDisplayLink];
|
||||
}
|
||||
|
||||
_motionHandler = [MotionHandler sharedInstance];
|
||||
_streamView.onScreenControls.instanceReceiverDelegate = _motionHandler;
|
||||
[_streamView.onScreenControls sendInstance];
|
||||
|
||||
NSLog(@"frameview gestures: %d", (uint32_t)[self.view.gestureRecognizers count]);
|
||||
NSLog(@"streamview gestures: %d", (uint32_t)[_streamView.gestureRecognizers count]);
|
||||
}
|
||||
@@ -775,6 +777,7 @@
|
||||
}
|
||||
|
||||
- (void)openWidgetLayoutTool{
|
||||
[_streamView saveRelocatedWidgetViews];
|
||||
_streamView.widgetToolOpened = true;
|
||||
[self->_streamView disableOnScreenControls];
|
||||
[self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode
|
||||
@@ -785,6 +788,7 @@
|
||||
}
|
||||
|
||||
- (void)switchWidgetProfile{
|
||||
[_streamView saveRelocatedWidgetViews];
|
||||
_streamView.widgetToolOpened = true;
|
||||
[self->_streamView disableOnScreenControls];
|
||||
[self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode
|
||||
@@ -1063,6 +1067,7 @@
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
//[self.pipController startPictureInPicture];
|
||||
//sleep(1);
|
||||
[_streamView saveRelocatedWidgetViews];
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
#endif
|
||||
@@ -1143,6 +1148,7 @@
|
||||
|
||||
- (void)expandSettingsView{
|
||||
self.mainFrameViewcontroller.settingsExpandedInStreamView = true; //notify mainFrameViewContorller that this is a setting expansion in stream view, some settings shall be disabled.
|
||||
[_streamView saveRelocatedWidgetViews];
|
||||
[self.mainFrameViewcontroller expandSettingsView];
|
||||
}
|
||||
|
||||
@@ -1556,6 +1562,22 @@
|
||||
[self reConfigStreamViewRealtime];
|
||||
}
|
||||
|
||||
- (void)startGyroUpdate{
|
||||
[_motionHandler startGyroUpdate];
|
||||
}
|
||||
|
||||
- (void)startAccelUpdate{
|
||||
[_motionHandler startAccelUpdate];
|
||||
}
|
||||
|
||||
- (void)stopGyroUpdate{
|
||||
[_motionHandler stopGyroUpdate];
|
||||
}
|
||||
|
||||
- (void)stopAccelUpdate{
|
||||
[_motionHandler stopAccelUpdate];
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
// Require a confirmation when streaming to activate a system gesture
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
|
||||
|
||||
@@ -1453,6 +1453,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"cKS-fy-Zpg.segmentTitles[4]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[4] = \"movable\"; ObjectID = \"cKS-fy-Zpg\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "movable"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "可移动"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "可移動"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "可移動"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CpY-Sb-BDc.title" : {
|
||||
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"CpY-Sb-BDc\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -3272,31 +3302,31 @@
|
||||
}
|
||||
},
|
||||
"qNQ-fB-jhY.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Trigger mode\"; ObjectID = \"qNQ-fB-jhY\";",
|
||||
"comment" : "Class = \"UILabel\"; text = \"Button mode\"; ObjectID = \"qNQ-fB-jhY\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Trigger mode"
|
||||
"value" : "Button mode"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "触发模式"
|
||||
"value" : "按键模式"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "觸發模式"
|
||||
"value" : "按鍵模式"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "觸發模式"
|
||||
"value" : "按鍵模式"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1580,13 +1580,13 @@
|
||||
}
|
||||
},
|
||||
"fcy-zp-LGa.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Stick Indicator\"; ObjectID = \"fcy-zp-LGa\";",
|
||||
"comment" : "Class = \"UILabel\"; text = \"Stick indicator\"; ObjectID = \"fcy-zp-LGa\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Stick Indicator"
|
||||
"value" : "Stick indicator"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2312,31 +2312,31 @@
|
||||
}
|
||||
},
|
||||
"o9I-Gt-YRd.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Trigger Mode\"; ObjectID = \"o9I-Gt-YRd\";",
|
||||
"comment" : "Class = \"UILabel\"; text = \"Button mode\"; ObjectID = \"o9I-Gt-YRd\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Trigger Mode"
|
||||
"value" : "Button mode"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "滑动触发"
|
||||
"state" : "translated",
|
||||
"value" : "按键模式"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "滑動觸發"
|
||||
"state" : "translated",
|
||||
"value" : "按鍵模式"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "滑動觸發"
|
||||
"state" : "translated",
|
||||
"value" : "按鍵模式"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2521,6 +2521,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ojp-aS-m8w.segmentTitles[4]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[4] = \"movable\"; ObjectID = \"ojp-aS-m8w\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "movable"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "可移动"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "可移動"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "可移動"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ozU-7C-qKE.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Button down\"; ObjectID = \"ozU-7C-qKE\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
|
||||
Reference in New Issue
Block a user