A game about forced loneliness, made by TACStudios
at master 31 kB view raw
1#if UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_WSA || PACKAGE_DOCS_GENERATION 2using System; 3using System.Runtime.CompilerServices; 4using System.Runtime.InteropServices; 5using UnityEngine.InputSystem.Controls; 6using UnityEngine.InputSystem.Layouts; 7using UnityEngine.InputSystem.LowLevel; 8using UnityEngine.InputSystem.Switch.LowLevel; 9using UnityEngine.InputSystem.Utilities; 10 11////REVIEW: The Switch controller can be used to point at things; can we somehow help leverage that? 12 13namespace UnityEngine.InputSystem.Switch.LowLevel 14{ 15#if UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_WSA 16 /// <summary> 17 /// Structure of HID input reports for Switch Pro controllers. 18 /// </summary> 19 [StructLayout(LayoutKind.Explicit, Size = 7)] 20 internal struct SwitchProControllerHIDInputState : IInputStateTypeInfo 21 { 22 public static FourCC Format = new FourCC('S', 'P', 'V', 'S'); // Switch Pro Virtual State 23 24 public FourCC format => Format; 25 26 [InputControl(name = "leftStick", layout = "Stick", format = "VC2B")] 27 [InputControl(name = "leftStick/x", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5")] 28 [InputControl(name = "leftStick/left", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0.15,clampMax=0.5,invert")] 29 [InputControl(name = "leftStick/right", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=0.85")] 30 [InputControl(name = "leftStick/y", offset = 1, format = "BYTE", parameters = "invert,normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5")] 31 [InputControl(name = "leftStick/up", offset = 1, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0.15,clampMax=0.5,invert")] 32 [InputControl(name = "leftStick/down", offset = 1, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=0.85,invert=false")] 33 [FieldOffset(0)] public byte leftStickX; 34 [FieldOffset(1)] public byte leftStickY; 35 36 [InputControl(name = "rightStick", layout = "Stick", format = "VC2B")] 37 [InputControl(name = "rightStick/x", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5")] 38 [InputControl(name = "rightStick/left", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0,clampMax=0.5,invert")] 39 [InputControl(name = "rightStick/right", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=1")] 40 [InputControl(name = "rightStick/y", offset = 1, format = "BYTE", parameters = "invert,normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5")] 41 [InputControl(name = "rightStick/up", offset = 1, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0.15,clampMax=0.5,invert")] 42 [InputControl(name = "rightStick/down", offset = 1, format = "BYTE", parameters = "normalize,normalizeMin=0.15,normalizeMax=0.85,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=0.85,invert=false")] 43 [FieldOffset(2)] public byte rightStickX; 44 [FieldOffset(3)] public byte rightStickY; 45 46 [InputControl(name = "dpad", format = "BIT", bit = 0, sizeInBits = 4)] 47 [InputControl(name = "dpad/up", bit = (int)Button.Up)] 48 [InputControl(name = "dpad/right", bit = (int)Button.Right)] 49 [InputControl(name = "dpad/down", bit = (int)Button.Down)] 50 [InputControl(name = "dpad/left", bit = (int)Button.Left)] 51 [InputControl(name = "buttonWest", displayName = "Y", shortDisplayName = "Y", bit = (int)Button.Y, usage = "SecondaryAction")] 52 [InputControl(name = "buttonNorth", displayName = "X", shortDisplayName = "X", bit = (int)Button.X)] 53 [InputControl(name = "buttonSouth", displayName = "B", shortDisplayName = "B", bit = (int)Button.B, usages = new[] { "Back", "Cancel" })] 54 [InputControl(name = "buttonEast", displayName = "A", shortDisplayName = "A", bit = (int)Button.A, usages = new[] { "PrimaryAction", "Submit" })] 55 [InputControl(name = "leftShoulder", displayName = "L", shortDisplayName = "L", bit = (uint)Button.L)] 56 [InputControl(name = "rightShoulder", displayName = "R", shortDisplayName = "R", bit = (uint)Button.R)] 57 [InputControl(name = "leftStickPress", displayName = "Left Stick", bit = (uint)Button.StickL)] 58 [InputControl(name = "rightStickPress", displayName = "Right Stick", bit = (uint)Button.StickR)] 59 [InputControl(name = "leftTrigger", displayName = "ZL", shortDisplayName = "ZL", format = "BIT", bit = (uint)Button.ZL)] 60 [InputControl(name = "rightTrigger", displayName = "ZR", shortDisplayName = "ZR", format = "BIT", bit = (uint)Button.ZR)] 61 [InputControl(name = "start", displayName = "Plus", bit = (uint)Button.Plus, usage = "Menu")] 62 [InputControl(name = "select", displayName = "Minus", bit = (uint)Button.Minus)] 63 [FieldOffset(4)] public ushort buttons1; 64 65 [InputControl(name = "capture", layout = "Button", displayName = "Capture", bit = (uint)Button.Capture - 16)] 66 [InputControl(name = "home", layout = "Button", displayName = "Home", bit = (uint)Button.Home - 16)] 67 [FieldOffset(6)] public byte buttons2; 68 69 public enum Button 70 { 71 Up = 0, 72 Right = 1, 73 Down = 2, 74 Left = 3, 75 76 West = 4, 77 North = 5, 78 South = 6, 79 East = 7, 80 81 L = 8, 82 R = 9, 83 StickL = 10, 84 StickR = 11, 85 86 ZL = 12, 87 ZR = 13, 88 Plus = 14, 89 Minus = 15, 90 Capture = 16, 91 Home = 17, 92 93 X = North, 94 B = South, 95 Y = West, 96 A = East, 97 } 98 99 [MethodImpl(MethodImplOptions.AggressiveInlining)] 100 public SwitchProControllerHIDInputState WithButton(Button button, bool value = true) 101 { 102 Set(button, value); 103 return this; 104 } 105 106 [MethodImpl(MethodImplOptions.AggressiveInlining)] 107 public void Set(Button button, bool state) 108 { 109 Debug.Assert((int)button < 18, $"Expected button < 18"); 110 if ((int)button < 16) 111 { 112 var bit = (ushort)(1U << (int)button); 113 if (state) 114 buttons1 = (ushort)(buttons1 | bit); 115 else 116 buttons1 &= (ushort)~bit; 117 } 118 else if ((int)button < 18) 119 { 120 var bit = (byte)(1U << ((int)button - 16)); 121 if (state) 122 buttons2 = (byte)(buttons2 | bit); 123 else 124 buttons2 &= (byte)~bit; 125 } 126 } 127 128 [MethodImpl(MethodImplOptions.AggressiveInlining)] 129 public void Press(Button button) 130 { 131 Set(button, true); 132 } 133 134 [MethodImpl(MethodImplOptions.AggressiveInlining)] 135 public void Release(Button button) 136 { 137 Set(button, false); 138 } 139 } 140#endif 141} 142 143namespace UnityEngine.InputSystem.Switch 144{ 145#if UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_WSA || PACKAGE_DOCS_GENERATION 146 /// <summary> 147 /// A Nintendo Switch Pro controller connected to a desktop mac/windows PC using the HID interface. 148 /// </summary> 149 [InputControlLayout(stateType = typeof(SwitchProControllerHIDInputState), displayName = "Switch Pro Controller")] 150 public class SwitchProControllerHID : Gamepad, IInputStateCallbackReceiver, IEventPreProcessor 151 { 152 [InputControl(name = "capture", displayName = "Capture")] 153 public ButtonControl captureButton { get; protected set; } 154 155 [InputControl(name = "home", displayName = "Home")] 156 public ButtonControl homeButton { get; protected set; } 157 158 protected override void OnAdded() 159 { 160 base.OnAdded(); 161 162 captureButton = GetChildControl<ButtonControl>("capture"); 163 homeButton = GetChildControl<ButtonControl>("home"); 164 165 HandshakeRestart(); 166 } 167 168 private static readonly SwitchMagicOutputReport.CommandIdType[] s_HandshakeSequence = new[] 169 { 170 SwitchMagicOutputReport.CommandIdType.Status, 171 SwitchMagicOutputReport.CommandIdType.Handshake, 172 SwitchMagicOutputReport.CommandIdType.Highspeed, 173 SwitchMagicOutputReport.CommandIdType.Handshake, 174 SwitchMagicOutputReport.CommandIdType.ForceUSB 175 ////TODO: Should we add a step to revert back to simple interface? 176 //// Because currently full reports don't work in old input system. 177 }; 178 179 private int m_HandshakeStepIndex; 180 private double m_HandshakeTimer; 181 182 private void HandshakeRestart() 183 { 184 // Delay first command issue until some time into the future 185 m_HandshakeStepIndex = -1; 186 m_HandshakeTimer = InputRuntime.s_Instance.currentTime; 187 } 188 189 private void HandshakeTick() 190 { 191 const double handshakeRestartTimeout = 2.0; 192 const double handshakeNextStepTimeout = 0.1; 193 194 var currentTime = InputRuntime.s_Instance.currentTime; 195 196 // There were no events for last few seconds, restart handshake 197 if (currentTime >= m_LastUpdateTimeInternal + handshakeRestartTimeout && 198 currentTime >= m_HandshakeTimer + handshakeRestartTimeout) 199 m_HandshakeStepIndex = 0; 200 // If handshake is complete, ignore the tick. 201 else if (m_HandshakeStepIndex + 1 >= s_HandshakeSequence.Length) 202 return; 203 // If we timeout, proceed to next step after some time is elapsed. 204 else if (currentTime > m_HandshakeTimer + handshakeNextStepTimeout) 205 m_HandshakeStepIndex++; 206 // If we haven't timed out on handshake step, skip the tick. 207 else 208 return; 209 210 m_HandshakeTimer = currentTime; 211 212 var command = s_HandshakeSequence[m_HandshakeStepIndex]; 213 214 // Native backend rejects one of the commands based on size of descriptor. 215 // So just report both at a same time. 216 ////TODO: fix this. 217 var commandBt = SwitchMagicOutputHIDBluetooth.Create(command); 218 if (ExecuteCommand(ref commandBt) > 0) 219 return; 220 221 var commandUsb = SwitchMagicOutputHIDUSB.Create(command); 222 ExecuteCommand(ref commandUsb); 223 } 224 225 public void OnNextUpdate() 226 { 227 HandshakeTick(); 228 } 229 230 // filter out three lower bits as jitter noise 231 internal const byte JitterMaskLow = 0b01111000; 232 internal const byte JitterMaskHigh = 0b10000111; 233 234 public unsafe void OnStateEvent(InputEventPtr eventPtr) 235 { 236 if (eventPtr.type == StateEvent.Type && eventPtr.stateFormat == SwitchProControllerHIDInputState.Format) 237 { 238 var currentState = (SwitchProControllerHIDInputState*)((byte*)currentStatePtr + m_StateBlock.byteOffset); 239 var newState = (SwitchProControllerHIDInputState*)StateEvent.FromUnchecked(eventPtr)->state; 240 241 var actuated = 242 // we need to make device current if axes are outside of deadzone specifying hardware jitter of sticks around zero point 243 newState->leftStickX<JitterMaskLow 244 || newState->leftStickX> JitterMaskHigh 245 || newState->leftStickY<JitterMaskLow 246 || newState->leftStickY> JitterMaskHigh 247 || newState->rightStickX<JitterMaskLow 248 || newState->rightStickX> JitterMaskHigh 249 || newState->rightStickY<JitterMaskLow 250 || newState->rightStickY> JitterMaskHigh 251 // we need to make device current if buttons state change 252 || newState->buttons1 != currentState->buttons1 253 || newState->buttons2 != currentState->buttons2; 254 255 if (!actuated) 256 InputSystem.s_Manager.DontMakeCurrentlyUpdatingDeviceCurrent(); 257 } 258 259 InputState.Change(this, eventPtr); 260 } 261 262 public bool GetStateOffsetForEvent(InputControl control, InputEventPtr eventPtr, ref uint offset) 263 { 264 return false; 265 } 266 267 public unsafe bool PreProcessEvent(InputEventPtr eventPtr) 268 { 269 if (eventPtr.type == DeltaStateEvent.Type) 270 // if someone queued delta state SPVS directly, just use as-is 271 // otherwise skip all delta state events 272 return DeltaStateEvent.FromUnchecked(eventPtr)->stateFormat == SwitchProControllerHIDInputState.Format; 273 274 // use all other non-state/non-delta-state events 275 if (eventPtr.type != StateEvent.Type) 276 return true; 277 278 var stateEvent = StateEvent.FromUnchecked(eventPtr); 279 var size = stateEvent->stateSizeInBytes; 280 281 if (stateEvent->stateFormat == SwitchProControllerHIDInputState.Format) 282 return true; // if someone queued SPVS directly, just use as-is 283 284 if (stateEvent->stateFormat != SwitchHIDGenericInputReport.Format || size < sizeof(SwitchHIDGenericInputReport)) 285 return false; // skip unrecognized state events otherwise they will corrupt control states 286 287 var genericReport = (SwitchHIDGenericInputReport*)stateEvent->state; 288 if (genericReport->reportId == SwitchSimpleInputReport.ExpectedReportId && size >= SwitchSimpleInputReport.kSize) 289 { 290 var data = ((SwitchSimpleInputReport*)stateEvent->state)->ToHIDInputReport(); 291 *((SwitchProControllerHIDInputState*)stateEvent->state) = data; 292 stateEvent->stateFormat = SwitchProControllerHIDInputState.Format; 293 return true; 294 } 295 else if (genericReport->reportId == SwitchFullInputReport.ExpectedReportId && size >= SwitchFullInputReport.kSize) 296 { 297 var data = ((SwitchFullInputReport*)stateEvent->state)->ToHIDInputReport(); 298 *((SwitchProControllerHIDInputState*)stateEvent->state) = data; 299 stateEvent->stateFormat = SwitchProControllerHIDInputState.Format; 300 return true; 301 } 302 else if (size == 8 || size == 9) // official accessories send 8 byte reports 303 { 304 // On Windows HID stack we somehow get 1 byte extra prepended, so if we get 9 bytes, subtract one, see ISX-993 305 // This is written in such way that if we fix it in backend, we wont break the package (Unity will report 8 bytes instead of 9 bytes). 306 var bugOffset = size == 9 ? 1 : 0; 307 var data = ((SwitchInputOnlyReport*)((byte*)stateEvent->state + bugOffset))->ToHIDInputReport(); 308 *((SwitchProControllerHIDInputState*)stateEvent->state) = data; 309 stateEvent->stateFormat = SwitchProControllerHIDInputState.Format; 310 return true; 311 } 312 else 313 return false; // skip unrecognized reportId 314 } 315 316 [StructLayout(LayoutKind.Explicit, Size = kSize)] 317 private struct SwitchInputOnlyReport 318 { 319 public const int kSize = 7; 320 321 [FieldOffset(0)] public byte buttons0; 322 [FieldOffset(1)] public byte buttons1; 323 [FieldOffset(2)] public byte hat; 324 [FieldOffset(3)] public byte leftX; 325 [FieldOffset(4)] public byte leftY; 326 [FieldOffset(5)] public byte rightX; 327 [FieldOffset(6)] public byte rightY; 328 329 [MethodImpl(MethodImplOptions.AggressiveInlining)] 330 public SwitchProControllerHIDInputState ToHIDInputReport() 331 { 332 var state = new SwitchProControllerHIDInputState 333 { 334 leftStickX = leftX, 335 leftStickY = leftY, 336 rightStickX = rightX, 337 rightStickY = rightY 338 }; 339 340 state.Set(SwitchProControllerHIDInputState.Button.Y, (buttons0 & 0x01) != 0); 341 state.Set(SwitchProControllerHIDInputState.Button.B, (buttons0 & 0x02) != 0); 342 state.Set(SwitchProControllerHIDInputState.Button.A, (buttons0 & 0x04) != 0); 343 state.Set(SwitchProControllerHIDInputState.Button.X, (buttons0 & 0x08) != 0); 344 state.Set(SwitchProControllerHIDInputState.Button.L, (buttons0 & 0x10) != 0); 345 state.Set(SwitchProControllerHIDInputState.Button.R, (buttons0 & 0x20) != 0); 346 state.Set(SwitchProControllerHIDInputState.Button.ZL, (buttons0 & 0x40) != 0); 347 state.Set(SwitchProControllerHIDInputState.Button.ZR, (buttons0 & 0x80) != 0); 348 state.Set(SwitchProControllerHIDInputState.Button.Minus, (buttons1 & 0x01) != 0); 349 state.Set(SwitchProControllerHIDInputState.Button.Plus, (buttons1 & 0x02) != 0); 350 state.Set(SwitchProControllerHIDInputState.Button.StickL, (buttons1 & 0x04) != 0); 351 state.Set(SwitchProControllerHIDInputState.Button.StickR, (buttons1 & 0x08) != 0); 352 state.Set(SwitchProControllerHIDInputState.Button.Home, (buttons1 & 0x10) != 0); 353 state.Set(SwitchProControllerHIDInputState.Button.Capture, (buttons1 & 0x20) != 0); 354 355 var left = false; 356 var up = false; 357 var right = false; 358 var down = false; 359 360 switch (hat) 361 { 362 case 0: 363 up = true; 364 break; 365 case 1: 366 up = true; 367 right = true; 368 break; 369 case 2: 370 right = true; 371 break; 372 case 3: 373 down = true; 374 right = true; 375 break; 376 case 4: 377 down = true; 378 break; 379 case 5: 380 down = true; 381 left = true; 382 break; 383 case 6: 384 left = true; 385 break; 386 case 7: 387 up = true; 388 left = true; 389 break; 390 } 391 392 state.Set(SwitchProControllerHIDInputState.Button.Left, left); 393 state.Set(SwitchProControllerHIDInputState.Button.Up, up); 394 state.Set(SwitchProControllerHIDInputState.Button.Right, right); 395 state.Set(SwitchProControllerHIDInputState.Button.Down, down); 396 return state; 397 } 398 } 399 400 [StructLayout(LayoutKind.Explicit, Size = kSize)] 401 private struct SwitchSimpleInputReport 402 { 403 public const int kSize = 12; 404 public const byte ExpectedReportId = 0x3f; 405 406 [FieldOffset(0)] public byte reportId; 407 [FieldOffset(1)] public byte buttons0; 408 [FieldOffset(2)] public byte buttons1; 409 [FieldOffset(3)] public byte hat; 410 [FieldOffset(4)] public ushort leftX; 411 [FieldOffset(6)] public ushort leftY; 412 [FieldOffset(8)] public ushort rightX; 413 [FieldOffset(10)] public ushort rightY; 414 415 [MethodImpl(MethodImplOptions.AggressiveInlining)] 416 public SwitchProControllerHIDInputState ToHIDInputReport() 417 { 418 var leftXByte = (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(leftX, 16, 8); 419 var leftYByte = (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(leftY, 16, 8); 420 var rightXByte = (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(rightX, 16, 8); 421 var rightYByte = (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(rightY, 16, 8); 422 423 var state = new SwitchProControllerHIDInputState 424 { 425 leftStickX = leftXByte, 426 leftStickY = leftYByte, 427 rightStickX = rightXByte, 428 rightStickY = rightYByte 429 }; 430 431 state.Set(SwitchProControllerHIDInputState.Button.B, (buttons0 & 0x01) != 0); 432 state.Set(SwitchProControllerHIDInputState.Button.A, (buttons0 & 0x02) != 0); 433 state.Set(SwitchProControllerHIDInputState.Button.Y, (buttons0 & 0x04) != 0); 434 state.Set(SwitchProControllerHIDInputState.Button.X, (buttons0 & 0x08) != 0); 435 state.Set(SwitchProControllerHIDInputState.Button.L, (buttons0 & 0x10) != 0); 436 state.Set(SwitchProControllerHIDInputState.Button.R, (buttons0 & 0x20) != 0); 437 state.Set(SwitchProControllerHIDInputState.Button.ZL, (buttons0 & 0x40) != 0); 438 state.Set(SwitchProControllerHIDInputState.Button.ZR, (buttons0 & 0x80) != 0); 439 state.Set(SwitchProControllerHIDInputState.Button.Minus, (buttons1 & 0x01) != 0); 440 state.Set(SwitchProControllerHIDInputState.Button.Plus, (buttons1 & 0x02) != 0); 441 state.Set(SwitchProControllerHIDInputState.Button.StickL, (buttons1 & 0x04) != 0); 442 state.Set(SwitchProControllerHIDInputState.Button.StickR, (buttons1 & 0x08) != 0); 443 state.Set(SwitchProControllerHIDInputState.Button.Home, (buttons1 & 0x10) != 0); 444 state.Set(SwitchProControllerHIDInputState.Button.Capture, (buttons1 & 0x20) != 0); 445 446 var left = false; 447 var up = false; 448 var right = false; 449 var down = false; 450 451 switch (hat) 452 { 453 case 0: 454 up = true; 455 break; 456 case 1: 457 up = true; 458 right = true; 459 break; 460 case 2: 461 right = true; 462 break; 463 case 3: 464 down = true; 465 right = true; 466 break; 467 case 4: 468 down = true; 469 break; 470 case 5: 471 down = true; 472 left = true; 473 break; 474 case 6: 475 left = true; 476 break; 477 case 7: 478 up = true; 479 left = true; 480 break; 481 } 482 483 state.Set(SwitchProControllerHIDInputState.Button.Left, left); 484 state.Set(SwitchProControllerHIDInputState.Button.Up, up); 485 state.Set(SwitchProControllerHIDInputState.Button.Right, right); 486 state.Set(SwitchProControllerHIDInputState.Button.Down, down); 487 488 return state; 489 } 490 } 491 492 [StructLayout(LayoutKind.Explicit, Size = kSize)] 493 private struct SwitchFullInputReport 494 { 495 public const int kSize = 25; 496 public const byte ExpectedReportId = 0x30; 497 498 [FieldOffset(0)] public byte reportId; 499 [FieldOffset(3)] public byte buttons0; 500 [FieldOffset(4)] public byte buttons1; 501 [FieldOffset(5)] public byte buttons2; 502 [FieldOffset(6)] public byte left0; 503 [FieldOffset(7)] public byte left1; 504 [FieldOffset(8)] public byte left2; 505 [FieldOffset(9)] public byte right0; 506 [FieldOffset(10)] public byte right1; 507 [FieldOffset(11)] public byte right2; 508 509 [MethodImpl(MethodImplOptions.AggressiveInlining)] 510 public SwitchProControllerHIDInputState ToHIDInputReport() 511 { 512 ////TODO: calibration curve 513 514 var leftXRaw = (uint)(left0 | ((left1 & 0x0F) << 8)); 515 var leftYRaw = (uint)(((left1 & 0xF0) >> 4) | (left2 << 4)); 516 var rightXRaw = (uint)(right0 | ((right1 & 0x0F) << 8)); 517 var rightYRaw = (uint)(((right1 & 0xF0) >> 4) | (right2 << 4)); 518 519 var leftXByte = (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(leftXRaw, 12, 8); 520 var leftYByte = (byte)(0xff - (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(leftYRaw, 12, 8)); 521 var rightXByte = (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(rightXRaw, 12, 8); 522 var rightYByte = (byte)(0xff - (byte)NumberHelpers.RemapUIntBitsToNormalizeFloatToUIntBits(rightYRaw, 12, 8)); 523 524 var state = new SwitchProControllerHIDInputState 525 { 526 leftStickX = leftXByte, 527 leftStickY = leftYByte, 528 rightStickX = rightXByte, 529 rightStickY = rightYByte 530 }; 531 532 state.Set(SwitchProControllerHIDInputState.Button.Y, (buttons0 & 0x01) != 0); 533 state.Set(SwitchProControllerHIDInputState.Button.X, (buttons0 & 0x02) != 0); 534 state.Set(SwitchProControllerHIDInputState.Button.B, (buttons0 & 0x04) != 0); 535 state.Set(SwitchProControllerHIDInputState.Button.A, (buttons0 & 0x08) != 0); 536 state.Set(SwitchProControllerHIDInputState.Button.R, (buttons0 & 0x40) != 0); 537 state.Set(SwitchProControllerHIDInputState.Button.ZR, (buttons0 & 0x80) != 0); 538 state.Set(SwitchProControllerHIDInputState.Button.Minus, (buttons1 & 0x01) != 0); 539 state.Set(SwitchProControllerHIDInputState.Button.Plus, (buttons1 & 0x02) != 0); 540 state.Set(SwitchProControllerHIDInputState.Button.StickR, (buttons1 & 0x04) != 0); 541 state.Set(SwitchProControllerHIDInputState.Button.StickL, (buttons1 & 0x08) != 0); 542 state.Set(SwitchProControllerHIDInputState.Button.Home, (buttons1 & 0x10) != 0); 543 state.Set(SwitchProControllerHIDInputState.Button.Capture, (buttons1 & 0x20) != 0); 544 state.Set(SwitchProControllerHIDInputState.Button.Down, (buttons2 & 0x01) != 0); 545 state.Set(SwitchProControllerHIDInputState.Button.Up, (buttons2 & 0x02) != 0); 546 state.Set(SwitchProControllerHIDInputState.Button.Right, (buttons2 & 0x04) != 0); 547 state.Set(SwitchProControllerHIDInputState.Button.Left, (buttons2 & 0x08) != 0); 548 state.Set(SwitchProControllerHIDInputState.Button.L, (buttons2 & 0x40) != 0); 549 state.Set(SwitchProControllerHIDInputState.Button.ZL, (buttons2 & 0x80) != 0); 550 551 return state; 552 } 553 } 554 555 [StructLayout(LayoutKind.Explicit)] 556 private struct SwitchHIDGenericInputReport 557 { 558 public static FourCC Format => new FourCC('H', 'I', 'D'); 559 560 [FieldOffset(0)] public byte reportId; 561 } 562 563 [StructLayout(LayoutKind.Explicit, Size = kSize)] 564 internal struct SwitchMagicOutputReport 565 { 566 public const int kSize = 49; 567 568 public const byte ExpectedReplyInputReportId = 0x81; 569 570 [FieldOffset(0)] public byte reportType; 571 [FieldOffset(1)] public byte commandId; 572 573 internal enum ReportType 574 { 575 Magic = 0x80 576 } 577 578 public enum CommandIdType 579 { 580 Status = 0x01, 581 Handshake = 0x02, 582 Highspeed = 0x03, 583 ForceUSB = 0x04, 584 } 585 } 586 587 [StructLayout(LayoutKind.Explicit, Size = kSize)] 588 internal struct SwitchMagicOutputHIDBluetooth : IInputDeviceCommandInfo 589 { 590 public static FourCC Type => new FourCC('H', 'I', 'D', 'O'); 591 public FourCC typeStatic => Type; 592 593 public const int kSize = InputDeviceCommand.kBaseCommandSize + 49; 594 595 [FieldOffset(0)] public InputDeviceCommand baseCommand; 596 [FieldOffset(InputDeviceCommand.kBaseCommandSize + 0)] public SwitchMagicOutputReport report; 597 598 public static SwitchMagicOutputHIDBluetooth Create(SwitchMagicOutputReport.CommandIdType type) 599 { 600 return new SwitchMagicOutputHIDBluetooth 601 { 602 baseCommand = new InputDeviceCommand(Type, kSize), 603 report = new SwitchMagicOutputReport 604 { 605 reportType = (byte)SwitchMagicOutputReport.ReportType.Magic, 606 commandId = (byte)type 607 } 608 }; 609 } 610 } 611 612 [StructLayout(LayoutKind.Explicit, Size = kSize)] 613 internal struct SwitchMagicOutputHIDUSB : IInputDeviceCommandInfo 614 { 615 public static FourCC Type => new FourCC('H', 'I', 'D', 'O'); 616 public FourCC typeStatic => Type; 617 618 public const int kSize = InputDeviceCommand.kBaseCommandSize + 64; 619 620 [FieldOffset(0)] public InputDeviceCommand baseCommand; 621 [FieldOffset(InputDeviceCommand.kBaseCommandSize + 0)] public SwitchMagicOutputReport report; 622 623 public static SwitchMagicOutputHIDUSB Create(SwitchMagicOutputReport.CommandIdType type) 624 { 625 return new SwitchMagicOutputHIDUSB 626 { 627 baseCommand = new InputDeviceCommand(Type, kSize), 628 report = new SwitchMagicOutputReport 629 { 630 reportType = (byte)SwitchMagicOutputReport.ReportType.Magic, 631 commandId = (byte)type 632 } 633 }; 634 } 635 } 636 } 637#endif 638} 639#endif // UNITY_EDITOR || UNITY_SWITCH