ControllerApplet.cs
1 using Ryujinx.Common.Logging; 2 using Ryujinx.Common.Memory; 3 using Ryujinx.HLE.HOS.Services.Am.AppletAE; 4 using Ryujinx.HLE.HOS.Services.Hid; 5 using Ryujinx.HLE.HOS.Services.Hid.Types; 6 using System; 7 using System.IO; 8 using System.Runtime.CompilerServices; 9 using System.Runtime.InteropServices; 10 using static Ryujinx.HLE.HOS.Services.Hid.HidServer.HidUtils; 11 12 namespace Ryujinx.HLE.HOS.Applets 13 { 14 internal class ControllerApplet : IApplet 15 { 16 private readonly Horizon _system; 17 18 private AppletSession _normalSession; 19 20 public event EventHandler AppletStateChanged; 21 22 public ControllerApplet(Horizon system) 23 { 24 _system = system; 25 } 26 27 public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) 28 { 29 _normalSession = normalSession; 30 31 byte[] launchParams = _normalSession.Pop(); 32 byte[] controllerSupportArgPrivate = _normalSession.Pop(); 33 ControllerSupportArgPrivate privateArg = IApplet.ReadStruct<ControllerSupportArgPrivate>(controllerSupportArgPrivate); 34 35 Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ArgPriv {privateArg.PrivateSize} {privateArg.ArgSize} {privateArg.Mode} " + 36 $"HoldType:{(NpadJoyHoldType)privateArg.NpadJoyHoldType} StyleSets:{(ControllerType)privateArg.NpadStyleSet}"); 37 38 if (privateArg.Mode != ControllerSupportMode.ShowControllerSupport) 39 { 40 _normalSession.Push(BuildResponse()); // Dummy response for other modes 41 AppletStateChanged?.Invoke(this, null); 42 43 return ResultCode.Success; 44 } 45 46 byte[] controllerSupportArg = _normalSession.Pop(); 47 48 ControllerSupportArgHeader argHeader; 49 50 if (privateArg.ArgSize == Marshal.SizeOf<ControllerSupportArgV7>()) 51 { 52 ControllerSupportArgV7 arg = IApplet.ReadStruct<ControllerSupportArgV7>(controllerSupportArg); 53 argHeader = arg.Header; 54 55 Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version 7 EnableExplainText={arg.EnableExplainText != 0}"); 56 // Read enable text here? 57 } 58 else if (privateArg.ArgSize == Marshal.SizeOf<ControllerSupportArgVPre7>()) 59 { 60 ControllerSupportArgVPre7 arg = IApplet.ReadStruct<ControllerSupportArgVPre7>(controllerSupportArg); 61 argHeader = arg.Header; 62 63 Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version Pre-7 EnableExplainText={arg.EnableExplainText != 0}"); 64 // Read enable text here? 65 } 66 else 67 { 68 Logger.Stub?.PrintStub(LogClass.ServiceHid, "ControllerSupportArg Version Unknown"); 69 70 argHeader = IApplet.ReadStruct<ControllerSupportArgHeader>(controllerSupportArg); // Read just the header 71 } 72 73 int playerMin = argHeader.PlayerCountMin; 74 int playerMax = argHeader.PlayerCountMax; 75 bool singleMode = argHeader.EnableSingleMode != 0; 76 77 Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet Arg {playerMin} {playerMax} {argHeader.EnableTakeOverConnection} {argHeader.EnableSingleMode}"); 78 79 if (singleMode) 80 { 81 // Applications can set an arbitrary player range even with SingleMode, so clamp it 82 playerMin = playerMax = 1; 83 } 84 85 int configuredCount; 86 PlayerIndex primaryIndex; 87 while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex)) 88 { 89 ControllerAppletUIArgs uiArgs = new() 90 { 91 PlayerCountMin = playerMin, 92 PlayerCountMax = playerMax, 93 SupportedStyles = (ControllerType)privateArg.NpadStyleSet, 94 SupportedPlayers = _system.Device.Hid.Npads.GetSupportedPlayers(), 95 IsDocked = _system.State.DockedMode, 96 }; 97 98 if (!_system.Device.UIHandler.DisplayMessageDialog(uiArgs)) 99 { 100 break; 101 } 102 } 103 104 ControllerSupportResultInfo result = new() 105 { 106 PlayerCount = (sbyte)configuredCount, 107 SelectedId = (uint)GetNpadIdTypeFromIndex(primaryIndex), 108 }; 109 110 Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ReturnResult {result.PlayerCount} {result.SelectedId}"); 111 112 _normalSession.Push(BuildResponse(result)); 113 AppletStateChanged?.Invoke(this, null); 114 115 _system.ReturnFocus(); 116 117 return ResultCode.Success; 118 } 119 120 public ResultCode GetResult() 121 { 122 return ResultCode.Success; 123 } 124 125 private static byte[] BuildResponse(ControllerSupportResultInfo result) 126 { 127 using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); 128 using BinaryWriter writer = new(stream); 129 130 writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>()))); 131 132 return stream.ToArray(); 133 } 134 135 private static byte[] BuildResponse() 136 { 137 using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); 138 using BinaryWriter writer = new(stream); 139 140 writer.Write((ulong)ResultCode.Success); 141 142 return stream.ToArray(); 143 } 144 } 145 }