11using System ;
2- using AquaMai . Config . Attributes ;
32using LibUsbDotNet . Main ;
43using LibUsbDotNet ;
54using MelonLoader ;
65using UnityEngine ;
76using AquaMai . Core . Helpers ;
87using System . Threading ;
8+ using JetBrains . Annotations ;
99
1010namespace AquaMai . Mods . GameSystem . ExclusiveTouch ;
1111
12- [ ConfigCollapseNamespace ]
13- [ ConfigSection ( exampleHidden : true ) ]
14- public class ExclusiveTouch
12+ public abstract class ExclusiveTouchBase ( int playerNo , int vid , int pid , [ CanBeNull ] string serialNumber , byte configuration , int interfaceNumber , ReadEndpointID endpoint , int packetSize , int minX , int minY , int maxX , int maxY , bool flip , int radius )
1513{
16- [ ConfigEntry ]
17- public static readonly bool enable1p ;
18-
19- [ ConfigEntry ]
20- public static readonly int vid1p ;
21- [ ConfigEntry ]
22- public static readonly int pid1p ;
23- [ ConfigEntry ]
24- public static readonly string serialNumber1p = "" ;
25- [ ConfigEntry ]
26- public static readonly byte configuration1p = 1 ;
27- [ ConfigEntry ]
28- public static readonly int interfaceNumber1p = 0 ;
29- [ ConfigEntry ]
30- public static readonly int reportId1p ;
31- [ ConfigEntry ]
32- public static readonly ReadEndpointID endpoint1p = ReadEndpointID . Ep01 ;
33- [ ConfigEntry ]
34- public static readonly int packetSize1p = 64 ;
35- [ ConfigEntry ]
36- public static readonly int minX1p ;
37- [ ConfigEntry ]
38- public static readonly int minY1p ;
39- [ ConfigEntry ]
40- public static readonly int maxX1p ;
41- [ ConfigEntry ]
42- public static readonly int maxY1p ;
43- [ ConfigEntry ]
44- public static readonly bool flip1p ;
45-
46- [ ConfigEntry ( "触摸体积半径" , zh : "基准是 1440x1440" ) ]
47- public static readonly int radius1p ;
48-
49- private static UsbDevice [ ] devices = new UsbDevice [ 2 ] ;
50- private static TouchSensorMapper [ ] touchSensorMappers = new TouchSensorMapper [ 2 ] ;
14+ private UsbDevice device ;
15+ private TouchSensorMapper touchSensorMapper ;
5116
5217 private class TouchPoint
5318 {
@@ -56,65 +21,60 @@ private class TouchPoint
5621 public bool IsActive ;
5722 }
5823
59- // [玩家][ 手指ID]
60- private static readonly TouchPoint [ ] [ ] allFingerPoints = new TouchPoint [ 2 ] [ ] ;
24+ // [手指ID]
25+ private readonly TouchPoint [ ] allFingerPoints = new TouchPoint [ 256 ] ;
6126
6227 // 防吃键
63- private static readonly ulong [ ] frameAccumulators = new ulong [ 2 ] ;
64- private static readonly object [ ] touchLocks = [ new object ( ) , new object ( ) ] ;
28+ private ulong frameAccumulators ;
29+ private readonly object touchLock = new ( ) ;
6530
6631 private const int TouchTimeoutMs = 20 ;
6732
68- public static void OnBeforePatch ( )
33+ public void Start ( )
6934 {
70- if ( enable1p )
35+ // 方便组 2P
36+ var finder = new UsbDeviceFinder ( vid , pid , string . IsNullOrWhiteSpace ( serialNumber ) ? null : serialNumber ) ;
37+ device = UsbDevice . OpenUsbDevice ( finder ) ;
38+ if ( device == null )
7139 {
72- // 方便组 2P
73- var serialNumber = string . IsNullOrWhiteSpace ( serialNumber1p ) ? null : serialNumber1p ;
74- var finder = new UsbDeviceFinder ( vid1p , pid1p , serialNumber ) ;
75- var device = UsbDevice . OpenUsbDevice ( finder ) ;
76- if ( device == null )
40+ MelonLogger . Msg ( "[ExclusiveTouch] Cannot connect 1P" ) ;
41+ }
42+ else
43+ {
44+ IUsbDevice wholeDevice = device as IUsbDevice ;
45+ if ( wholeDevice != null )
7746 {
78- MelonLogger . Msg ( "[ExclusiveTouch] Cannot connect 1P" ) ;
47+ wholeDevice . SetConfiguration ( configuration ) ;
48+ wholeDevice . ClaimInterface ( interfaceNumber ) ;
7949 }
80- else
50+ touchSensorMapper = new TouchSensorMapper ( minX , minY , maxX , maxY , radius , flip ) ;
51+ Application . quitting += ( ) =>
8152 {
82- IUsbDevice wholeDevice = device as IUsbDevice ;
53+ var tmpDevice = device ;
54+ device = null ;
8355 if ( wholeDevice != null )
8456 {
85- wholeDevice . SetConfiguration ( configuration1p ) ;
86- wholeDevice . ClaimInterface ( interfaceNumber1p ) ;
87- }
88- touchSensorMappers [ 0 ] = new TouchSensorMapper ( minX1p , minY1p , maxX1p , maxY1p , radius1p , flip1p ) ;
89- Application . quitting += ( ) =>
90- {
91- devices [ 0 ] = null ;
92- if ( wholeDevice != null )
93- {
94- wholeDevice . ReleaseInterface ( interfaceNumber1p ) ;
95- }
96- device . Close ( ) ;
97- } ;
98-
99- allFingerPoints [ 0 ] = new TouchPoint [ 256 ] ;
100- for ( int i = 0 ; i < 256 ; i ++ )
101- {
102- allFingerPoints [ 0 ] [ i ] = new TouchPoint ( ) ;
57+ wholeDevice . ReleaseInterface ( interfaceNumber ) ;
10358 }
59+ tmpDevice . Close ( ) ;
60+ } ;
10461
105- devices [ 0 ] = device ;
106- Thread readThread = new Thread ( ( ) => ReadThread ( 0 ) ) ;
107- readThread . Start ( ) ;
108- TouchStatusProvider . RegisterTouchStatusProvider ( 0 , GetTouchState ) ;
62+ for ( int i = 0 ; i < 256 ; i ++ )
63+ {
64+ allFingerPoints [ i ] = new TouchPoint ( ) ;
10965 }
66+
67+ Thread readThread = new ( ReadThread ) ;
68+ readThread . Start ( ) ;
69+ TouchStatusProvider . RegisterTouchStatusProvider ( playerNo , GetTouchState ) ;
11070 }
11171 }
11272
113- private static void ReadThread ( int playerNo )
73+ private void ReadThread ( )
11474 {
115- byte [ ] buffer = new byte [ packetSize1p ] ;
116- var reader = devices [ playerNo ] . OpenEndpointReader ( endpoint1p ) ;
117- while ( devices [ playerNo ] != null )
75+ byte [ ] buffer = new byte [ packetSize ] ;
76+ var reader = device . OpenEndpointReader ( endpoint ) ;
77+ while ( device != null )
11878 {
11979 int bytesRead ;
12080 ErrorCode ec = reader . Read ( buffer , 100 , out bytesRead ) ; // 100ms 超时
@@ -128,120 +88,72 @@ private static void ReadThread(int playerNo)
12888
12989 if ( bytesRead > 0 )
13090 {
131- OnTouchData ( playerNo , buffer ) ;
91+ OnTouchData ( buffer ) ;
13292 }
13393 }
13494 }
13595
136- private static void OnTouchData ( int playerNo , byte [ ] data )
137- {
138- byte reportId = data [ 0 ] ;
139- if ( reportId != reportId1p ) return ;
96+ protected abstract void OnTouchData ( byte [ ] data ) ;
14097
141- #if true // PDX
142- for ( int i = 0 ; i < 10 ; i ++ )
143- {
144- var index = i * 6 + 1 ;
145- if ( data [ index ] == 0 ) continue ;
146- bool isPressed = ( data [ index ] & 0x01 ) == 1 ;
147- var fingerId = data [ index + 1 ] ;
148- ushort x = BitConverter . ToUInt16 ( data , index + 2 ) ;
149- ushort y = BitConverter . ToUInt16 ( data , index + 4 ) ;
150- HandleFinger ( x , y , fingerId , isPressed , playerNo ) ;
151- }
152- #else // 凌莞的便携屏
153- // 解析第一根手指
154- if ( data . Length >= 7 )
155- {
156- byte status1 = data [ 1 ] ;
157- int fingerId1 = ( status1 >> 4 ) & 0x0F ; // 高4位:手指ID
158- bool isPressed1 = ( status1 & 0x01 ) == 1 ; // 低位:按下状态
159- ushort x1 = BitConverter . ToUInt16 ( data , 2 ) ;
160- ushort y1 = BitConverter . ToUInt16 ( data , 4 ) ;
161-
162- HandleFinger ( x1 , y1 , fingerId1 , isPressed1 , playerNo ) ;
163- }
164-
165- // 解析第二根手指
166- if ( data . Length >= 14 )
167- {
168- byte status2 = data [ 6 ] ;
169- int fingerId2 = ( status2 >> 4 ) & 0x0F ;
170- bool isPressed2 = ( status2 & 0x01 ) == 1 ;
171- ushort x2 = BitConverter . ToUInt16 ( data , 7 ) ;
172- ushort y2 = BitConverter . ToUInt16 ( data , 9 ) ;
173-
174- // 只有坐标非零才处理第二根手指
175- if ( x2 != 0 || y2 != 0 )
176- {
177- HandleFinger ( x2 , y2 , fingerId2 , isPressed2 , playerNo ) ;
178- }
179- }
180- #endif
181- }
182-
183- private static void HandleFinger ( ushort x , ushort y , int fingerId , bool isPressed , int playerNo )
98+ protected void HandleFinger ( ushort x , ushort y , int fingerId , bool isPressed )
18499 {
185100 // 安全检查,防止越界
186101 if ( fingerId < 0 || fingerId >= 256 ) return ;
187102
188- lock ( touchLocks [ playerNo ] )
103+ lock ( touchLock )
189104 {
190- var point = allFingerPoints [ playerNo ] [ fingerId ] ;
105+ var point = allFingerPoints [ fingerId ] ;
191106
192107 if ( isPressed )
193108 {
194- ulong touchMask = touchSensorMappers [ playerNo ] . ParseTouchPoint ( x , y ) ;
109+ ulong touchMask = touchSensorMapper . ParseTouchPoint ( x , y ) ;
195110
196111 if ( ! point . IsActive )
197112 {
198113 point . IsActive = true ;
199- MelonLogger . Msg ( $ "[ExclusiveTouch] { playerNo + 1 } P: 手指{ fingerId } 按下 at ({ x } , { y } ) -> 0x{ touchMask : X} ") ;
200114 }
201115
202116 point . Mask = touchMask ;
203117 point . LastUpdate = DateTime . Now ;
204-
205- frameAccumulators [ playerNo ] |= touchMask ;
118+
119+ frameAccumulators |= touchMask ;
206120 }
207121 else
208122 {
209123 if ( point . IsActive )
210124 {
211125 point . IsActive = false ;
212- MelonLogger . Msg ( $ "[ExclusiveTouch] { playerNo + 1 } P: 手指{ fingerId } 松开") ;
213126 }
214127 }
215128 }
216129 }
217130
218- public static ulong GetTouchState ( int playerNo )
131+ private ulong GetTouchState ( int player )
219132 {
220- lock ( touchLocks [ playerNo ] )
133+ if ( player != playerNo ) return 0 ;
134+ lock ( touchLock )
221135 {
222136 ulong currentTouchData = 0 ;
223137 var now = DateTime . Now ;
224- var points = allFingerPoints [ playerNo ] ;
225138
226- for ( int i = 0 ; i < points . Length ; i ++ )
139+ for ( int i = 0 ; i < allFingerPoints . Length ; i ++ )
227140 {
228- var point = points [ i ] ;
141+ var point = allFingerPoints [ i ] ;
229142 if ( point . IsActive )
230143 {
231144 if ( ( now - point . LastUpdate ) . TotalMilliseconds > TouchTimeoutMs )
232145 {
233146 point . IsActive = false ;
234- MelonLogger . Msg ( $ "[ExclusiveTouch] { playerNo + 1 } P: 手指{ i } 超时自动释放") ;
235147 }
236148 else
237149 {
238150 currentTouchData |= point . Mask ;
239151 }
240152 }
241153 }
242-
243- ulong finalResult = currentTouchData | frameAccumulators [ playerNo ] ;
244- frameAccumulators [ playerNo ] = 0 ;
154+
155+ ulong finalResult = currentTouchData | frameAccumulators ;
156+ frameAccumulators = 0 ;
245157
246158 return finalResult ;
247159 }
0 commit comments